/**
 * File:
 *   include/nis/ui.ycp
 *
 * Package:
 *   Configuration of NIS
 *
 * Summary:
 *   User interface functions.
 *
 * Authors:
 *   Martin Vidner <mvidner@suse.cz>
 *
 * $Id: ui.ycp 22192 2005-03-03 11:20:00Z jsuchome $
 *
 * All user interface functions.
 *
 */

{
    textdomain "nis";

    import "Autologin";
    import "CWMFirewallInterfaces";
    import "Label";
    import "Message";
    import "Mode";
    import "Nis";
    import "Package";
    import "Popup";
    import "Report";
    import "Sequencer";
    import "Stage";
    import "Wizard";

    /**
     * A Wizard Sequencer helper
     * @return	`next
     */
    define symbol JustNext () ``{
	return `next;
    }

    /*const*/ string broadcast_help =
	// Translators: network broadcast address
	_("<p>The <b>Broadcast</b> option enables searching
in the local network to find a server after the specified servers
fail to respond. It is a security risk.</p>
");

    /*const*/ string dhcp_help =
	// Translators: help text
	// DHCP = Dynamic host configuration protocol
	_("<p>If you are using <b>DHCP</b> and the server provides the NIS domain name or servers, you can enable their use here. DHCP itself can be set up in the network module.</p>");

    /*const*/ string expert_help =
	// Translators: short for Expert settings
	_("<p><b>Expert</b> gives access to some
less frequently used settings.</p>
");

    /**
     * The dialog that appears when the [Abort] button is pressed.
     * @param touched data modified?
     * @return `abort if user really wants to abort, `back otherwise
     */
    define boolean ReallyAbort (boolean touched) ``{

	return
	(!Stage::cont () || contains (WFM::Args (), "from_users"))
	    ? Popup::ReallyAbort (touched)
	    : Popup::ConfirmAbort (`incomplete);
    }

    /**
     * Pop up a warning if dhcp does not provide nis configuration
     */
    define void WarnIfDhcpNotServed () ``{
	if (!Nis::dhcp_possible)
	{
	    // Translators: warning popup
	    Report::Warning (_("NIS configuration is not currently provided by DHCP.
Change the DHCP server configuration to provide it.
"));
	}
    }

    /**
     * Let the user choose some of a list of items
     * @param title	selectionbox title
     * @param items	a list of items
     * @return		items or nil on cancel
     */
    define list<string> ChooseItems (string title, list<string> items) ``{
	list<term> msb_items = maplist (string entry, items,
					 ``( `item (`id (entry), entry)));
	UI::OpenDialog (
	    `VBox (
		`HSpacing (40),
		`HBox (
		    `MultiSelectionBox (`id (`items), title, msb_items),
		    `VSpacing(10)
		    ),
		`HBox (
		    // pushbutton label
		    // Select all items (in this case NIS servers) of a list
		    `PushButton (`id (`all), _("Select &All")),
		    // pushbutton label
		    // Deselect all items (in this case NIS servers) of a list
		    `PushButton (`id (`none), _("Select &None"))
		    ),
		`HBox (
		    `PushButton (`id (`ok), `opt (`default, `key_F10),
				 Label::OKButton ()),
		    `PushButton (`id (`cancel), `opt (`key_F9),
				 Label::CancelButton())
		    )
		));
	UI::SetFocus (`id (`items));
	any ret = nil;
	do
	{
	    ret = UI::UserInput();
	    if (ret == `all)
	    {
		UI::ChangeWidget (`id (`items), `SelectedItems, items);
	    }
	    else if (ret == `none)
	    {
		UI::ChangeWidget (`id (`items), `SelectedItems, []);
	    }
	}
	while (ret != `cancel && ret != `ok);

	if (ret == `ok)
	{
	    items = (list<string>) UI::QueryWidget (`id (`items), `SelectedItems);
	}
	else
	{
	    items = nil;
	}
	UI::CloseDialog();

	return items;
    }

    /**
     * A cache of NIS servers found on the LAN for each domain.
     * @see SelectNisServers
     */
    map found_servers = $[]; // map <string, list <string> >

    /**
     * Scan the network for NIS servers and let the user select among them.
     * @param domain which domain
     * @return space separated list of servers or ""
     */
    define string SelectNisServers (string domain) ``{
	Wizard::SetScreenShotName ("nis-client-2a1-servers");
	if (Mode::screen_shot())
	{
	    found_servers[domain] = ["nis.example.com", "10.42.1.1", "10.42.1.2"];
	}

	if (!haskey (found_servers, domain))
	{
	    // popup window
	    // LAN: local area network
	    UI::OpenDialog (`Label (sformat (_("Scanning for NIS servers in domain %1 on this LAN..."), domain)));
	    found_servers[domain] = SCR::Read (add (.net.ypserv.find, domain));
	    UI::CloseDialog ();
	    if (found_servers[domain]:nil == nil)
	    {
		found_servers[domain] = ["internal-error"];
	    }
	}

	list <string> selected_servers =
	    // selection box label
	    ChooseItems (sformat (_("&NIS Servers in Domain %1"), domain),
			 found_servers[domain]:[]);
	y2milestone ("selected_servers: %1", selected_servers);
	if (selected_servers == nil)
	{
	    selected_servers = [];
	}
	Wizard::RestoreScreenShotName ();
	return mergestring (selected_servers, " ");
    }

    /**
     * The simple dialog
     * @return	`back, `abort, `next, `multiple or `expert
     */
    define symbol MainDialog () ``{

	Wizard::SetScreenShotName ("nis-client-2a-single");

	// help text
	string help_text = _("<p>Enter your NIS domain, such as example.com,
 and the NIS server's address, such as nis.example.com or 10.20.1.1.</p>
");

	// help text
	help_text = help_text + _("<p>Specify multiple servers
by separating their addresses with spaces.</p>
");

	help_text = help_text + broadcast_help;

	help_text = help_text + dhcp_help;

	// help text
	help_text = help_text + _("<p>Automounter is a daemon that mounts directories automatically,
such as users' home directories.
It is assumed that its configuration files (auto.*) already exist,
either locally or over NIS.</p>");



	boolean dhcp_wanted = Nis::dhcp_wanted;
	string domain  = Nis::GetDomain ();
	string servers = Nis::GetServers ();

	// In this simple case, let's discard the distinction.
	boolean default_broadcast =
	    Nis::default_broadcast || Nis::global_broadcast;

	// the default is the current status
	// or true in case we were called during the installation
	boolean yp_client = Nis::start;
	if (Stage::cont () && !contains (WFM::Args (), "from_users"))
	    yp_client	= true;
	boolean autofs = Nis::_start_autofs;
	map<string,list> all_servers =
	    mapmap (string d, list s, Nis::multidomain_servers, ``(
		$[
		    d: [
			s,
			Nis::multidomain_broadcast[d]:false,
			Nis::slp_domain[d]:false
		    ]
		]
	));

	all_servers = add (all_servers, "",
	    [Nis::servers, Nis::default_broadcast]);

	list<string> additional_domains = [];

	foreach (string d, list s, all_servers, {
	    if (d != nil && d != "")
		additional_domains = add (additional_domains, d);
	});

	y2debug("all_servers: %1", all_servers);
	y2debug("additional_domains: %1", additional_domains);

	term con = nil;
	term nis_frame =

	    // frame label
	    `Frame (_("NIS client"),
		    `VBox (`VSpacing (0.2),

			   `RadioButtonGroup (
			       `id (`dhcpg),
			       `VBox (
				   // radio button label
				   `Left (`RadioButton (`id (`dhcpyes), `opt (`notify), _("Au&tomatic Setup (via DHCP)"), dhcp_wanted)),
				   // radio button label
				   `Left (`RadioButton (`id (`dhcpno), `opt (`notify), _("&Static Setup"), !dhcp_wanted))
				   )),


			   `VSpacing (0.2),
			   // text entry label
			   `TextEntry (`id (`domain), _("N&IS Domain"), domain),
			   `VSpacing (0.2),
			   // text entry label
			   `TextEntry (`id (`servers), _("&Addresses of NIS servers"), servers),
			   `HBox (
			       // check box label
			       `Left (`CheckBox (`id (`broadcast), `opt (`notify), _("Br&oadcast"), default_broadcast)),
			       // pushbutton label, find nis servers
			       // Shortcut must not conflict with Finish and Next (#29960)
			       `Right (`PushButton (`id (`find), _("Fin&d")))
			       ),
			    `HBox(
				`VBox(
				    `Left(`Label(_("Additional NIS Domains"))),
				    `HBox(
					`HSpacing(1),
					`Label(`id (`adddomains), `opt(`outputField, `hstretch), mergestring(additional_domains, ", "))
				    )
				),
				`HSpacing(1.2),
				`VBox(
				    `Label(""),
				    `PushButton (`id (`edit), _("&Edit"))
				)
			    )
			)
		);

	con =
	    `VBox (`VSpacing (0.5),
		`RadioButtonGroup (
		    `id (`rd),
		    `Left (
			`HVSquash (
			    `VBox (
				// radio button label
				`Left (`RadioButton (`id (`nisno), `opt (`notify), _("Do &not use NIS"), !yp_client)),
				// radio button label
				`Left (`RadioButton (`id (`nisyes), `opt (`notify), _("&Use NIS"), yp_client))
				)))),
		`VSpacing (1.2),
		nis_frame,
		`VSpacing (0.5),
		`CheckBox (`id(`autofs), _("Start Auto&mounter"), autofs),
		`VSpacing (0.5),
		`PushButton (`id (`expert), `opt (`key_F7),
		    // button label
		    // (short for Expert settings)
		    _("E&xpert...")
		),
		`VSpacing (0.5)
	    );

	Wizard::SetContentsButtons (
	    // dialog title
	    _("Configuration of NIS client"), con, help_text,
	    Label::BackButton (), Stage::cont ()? Label::NextButton():Label::FinishButton ());
	Wizard::RestoreAbortButton ();

	// during installation, only enabling makes sence
	if (Stage::cont () && !contains (WFM::Args (), "from_users"))
	{
	    UI::ChangeWidget (`id (`nisno), `Enabled, false);
	}


	UI::ChangeWidget (`id (`autofs), `Enabled, Nis::_autofs_allowed);

	symbol result = nil;
	repeat
	{
	    yp_client = ((symbol) UI::QueryWidget (`id (`rd), `CurrentButton) != `nisno);
	    UI::ChangeWidget (`id (`expert), `Enabled, yp_client);
	    UI::ChangeWidget (`id (`dhcpno), `Enabled, yp_client);
	    UI::ChangeWidget (`id (`dhcpyes), `Enabled, yp_client);
	    UI::ChangeWidget (`id (`autofs), `Enabled, yp_client);

	    dhcp_wanted = ((symbol) UI::QueryWidget (`id (`dhcpg), `CurrentButton) == `dhcpyes);
	    UI::ChangeWidget (`id (`domain), `Enabled, !dhcp_wanted && yp_client);
	    UI::ChangeWidget (`id (`servers), `Enabled, !dhcp_wanted && yp_client);
	    UI::ChangeWidget (`id (`broadcast), `Enabled, !dhcp_wanted && yp_client);
	    UI::ChangeWidget (`id (`find), `Enabled, !dhcp_wanted && yp_client);
	    UI::ChangeWidget (`id (`edit), `Enabled, !dhcp_wanted && yp_client);
	    UI::ChangeWidget (`id (`adddomains), `Enabled, !dhcp_wanted && yp_client);

	    result = (symbol) UI::UserInput ();
	    if (result == `cancel)
	    {
		result = `abort;
	    }

	    if (result == `dhcpyes)
	    {
		WarnIfDhcpNotServed ();
	    }
	    else if (result == `find)
	    {
		domain =  (string) UI::QueryWidget (`id (`domain), `Value);
		if (domain == "")
		{
		    // Message popup. The user wants to Find servers
		    // but the domain is unknown.
		    Popup::Message (_("Finding servers works only when the domain is known."));
		    UI::SetFocus (`id (`domain));
		}
		else
		{
		    string servers = SelectNisServers (domain);
		    if (servers != "")
		    {
			UI::ChangeWidget (`id (`servers), `Value, servers);
		    }
		}
	    }
	    else if (contains ([`next, `edit, `expert], result))
	    {
		yp_client = ((symbol) UI::QueryWidget (`id (`rd), `CurrentButton) != `nisno);

		// Using NIS and LDAP simultaneously is not supported (#36981).
		if (result == `next &&
		    yp_client && !Nis::start && Nis::UsersByLdap ())
		{
		    // yes-no popup
		    if (!Popup::YesNo (_("When you configure your machine as a NIS client,
you cannot retrieve the user data from LDAP.
Are you sure?")))
		    {
			result = nil;
			continue;
		    }
		}

		domain = (string) UI::QueryWidget (`id (`domain), `Value);
		servers = (string) UI::QueryWidget (`id (`servers), `Value);
		default_broadcast = (boolean) UI::QueryWidget (`id (`broadcast), `Value);

		if (yp_client && !dhcp_wanted &&
		    !Nis::check_nisdomainname (domain))
		{
		    UI::SetFocus (`id (`domain));
		    Popup::Error (Nis::valid_nisdomainname ());
		    result = nil;
		    continue;
		}

		list<string> temp_ad = filter (
		    string a, splitstring (servers, " "), ``(a != ""));
		if (yp_client && !dhcp_wanted && !default_broadcast &&
		    (size (temp_ad) == 0 ||
		     find (string a, temp_ad,
			   ``(!Nis::check_address_nis (a))) != nil))
		{
		    UI::SetFocus (`id (`servers));
		    Popup::Message (Nis::valid_address_nis ());
		    result = nil;
		    continue;
		}
	    }
	}
	until (result == `back || result == `next || result == `expert ||
	       result == `edit ||
	       (result == `abort && ReallyAbort (Nis::touched)));

	if (contains ([`next, `expert, `edit], result))
	{
	    Nis::Touch (Nis::start != yp_client);
	    Nis::start = yp_client;

	    Nis::dhcp_restart = Nis::dhcp_restart ||
		(Nis::dhcp_wanted != dhcp_wanted);
	    Nis::Touch (Nis::dhcp_restart);
	    Nis::dhcp_wanted = dhcp_wanted;

	    Nis::SetDomain (domain);
	    Nis::SetServers (servers);
	    Nis::Touch (Nis::default_broadcast != default_broadcast);
	    Nis::default_broadcast = default_broadcast;
	    Nis::Touch (Nis::global_broadcast != false);
	    Nis::global_broadcast = false;

	    boolean newautofs = Nis::_autofs_allowed && (boolean) UI::QueryWidget (`id (`autofs), `Value);
	    Nis::Touch (Nis::_start_autofs != newautofs);
	    Nis::_start_autofs = newautofs;
	}

	Wizard::RestoreScreenShotName ();
	return (symbol) result;
    }


    /**
     * The expert dialog
     * @return	`back, `abort or `next
     */
    define symbol ExpertDialog () {
	Wizard::SetScreenShotName ("nis-client-2c-expert");

	map<string,any> firewall_widget =
	    CWMFirewallInterfaces::CreateOpenFirewallWidget ($[
	    "services"	: [ "nis-client" ],// = portmap + ypbind
	    "display_details" : true,
	    // firewall openning help
	    "help" : _("<p><b>Firewall Settings</b><br>
To open the firewall to allow accessing the 'ypbind' service
from remote computers, set <b>Open Port in Firewall</b>.
To select interfaces on which to open the port, click <b>Firewall Details</b>.
This option is only available if the firewall is enabled.</p>
"),
	]);
	term firewall_layout = firewall_widget["custom_widget"]:`VBox ();

	// help text 1/4
	string help_text = _("<p>Normally, it is possible for any host to query which server a client is using. Disabling <b>Answer Remote Hosts</b> restricts this only to the local host.</p>");

	// help text 2/4
	// Check, ie. turn on a check box
	help_text = help_text + _("<p>Check <b>Broken server</b> if answers from servers running on an unprivileged port should be accepted. It is a security risk and it is better to replace such a server.</p>");

	// help text 3/4
	help_text = help_text + _("<p>See <b>man ypbind</b> for details on other options.</p>");

	help_text = help_text + firewall_widget["help"]:"";

	boolean local_only = Nis::local_only;
	boolean broken_server = Nis::broken_server;
	string options = Nis::options;

	term contents =
	    `HSquash (`VBox (
		`Frame (
		    // frame label
		    _("Expert settings"),
		    `VBox (
			`VSpacing (0.2),
			// check box label
			`Left (`CheckBox (`id (`remote), _("Ans&wer Remote Hosts"), !local_only)),
			// check box label
			`Left (`CheckBox (`id (`broken_server), _("Br&oken server"), broken_server)),
			`VSpacing (0.2),
			// text entry label
			// ypbind is the daemon name
			`TextEntry (`id (`options), _("Other &ypbind options"), options),
			`VSpacing (0.2)
			)
		    ),
		`VSpacing (),
		firewall_layout
		));

	Wizard::SetContentsButtons (
	    // dialog title
	    _("Expert settings"), contents, help_text,
	    Label::CancelButton (), Label::AcceptButton ());
	Wizard::HideAbortButton ();

	CWMFirewallInterfaces::OpenFirewallInit (firewall_widget, "");

	map event	= $[];
	any result = nil;
	repeat
	{
	    event	= UI::WaitForEvent ();
	    result	= event["ID"]:nil;
	    CWMFirewallInterfaces::OpenFirewallHandle(firewall_widget,"",event);

	    if (result == `cancel)
	    {
		result = `abort;
	    }

	    if (result == `next)
	    {
		local_only = !(boolean) UI::QueryWidget (`id (`remote), `Value);
		broken_server = (boolean) UI::QueryWidget (`id (`broken_server), `Value);
		// TODO: disallow " in options
		options = (string) UI::QueryWidget (`id (`options), `Value);
		CWMFirewallInterfaces::OpenFirewallStore (
		    firewall_widget,"",event);
	    }
	}
	until (result == `back || result == `next ||
	       (result == `abort && ReallyAbort (Nis::touched)));

	if (result == `next)
	{
	    Nis::Touch (Nis::local_only != local_only);
	    Nis::local_only = local_only;
	    Nis::Touch (Nis::broken_server != broken_server);
	    Nis::broken_server = broken_server;
	    Nis::Touch (Nis::options != options);
	    Nis::options = options;
	}

	Wizard::RestoreScreenShotName ();
	return (symbol) result;
    }

    string check_g = nil;
    /**
     * Constructs items for the domain table
     * @param	default_d	the default domain
     * @param	all_servers	map of @ref server_sp
     * @return a list of items
     * @see Nis#multidomain_servers TODO
     */
    define list DomainTableItems (string default_d, map<string,list> all_servers) ``{
	if (check_g == nil)
	{
	    check_g = UI::Glyph (`CheckMark);
	}

	y2debug("all_servers: %1", all_servers);
	// maps are sorted, so the default domain, "", comes first
	return maplist (string d, list server_sp, all_servers, ``(
			    `item (
				`id (d),
				d,
				// this would be a priority example x ]:
				(server_sp[1]:false)? check_g: "",
				(server_sp[2]:false)? check_g: "",
				mergestring (server_sp[0]:[], ", ")
				)
			    ));
    }

    /**
     * @param	default_d	the default domain
     * @param	all_servers show these items
     * @param	d	the selected item
     */
    define void UpdateDomainTable (string default_d, map<string,list> all_servers,
					  string d) ``{
	UI::ChangeWidget (`id (`domains), `Items,
			  DomainTableItems (default_d, all_servers));
	UI::ChangeWidget (`id (`domains), `CurrentItem, d);
    }

    /**
     * @param	m	a map
     * @return		keys of the map
     */
    define list mapkeys (map m) ``{
	return maplist (any k, any v, m, ``( k ));
    }

    /**
     * @tuple server_sp
     * 0 list(string)	server list
     * 1 boolean	broadcast
     */

    /**
     * Add/Edit a domain, including its name and servers
     * @param	init		currently selected domain: nil=add, ""=default
     * @param	default_d	the default domain
     * @param	server_sp	@ref server_sp
     * @param	existing	existing domains
     * @return [name, [ [server1, server2], broadcast? ]]
     */
    define list DomainPopup (string init, string default_d,
				    list server_sp, list<string> existing) ``{
	Wizard::SetScreenShotName ("nis-client-2b1-domain");

	string domain = (init == nil)? "": ((init == "") ? default_d: init);
	list<string> servers = server_sp[0]:[];
	string servers_s = mergestring (servers, "\n");
	boolean broadcast = server_sp[1]:false;
	boolean slp = server_sp[2]:false;

	term t_servers =
	    `VBox (
		`MultiLineEdit(`id (`servers),
			       // Translators: multilineedit label
			       // comma: ","
			       _("&Servers (separated by spaces or commas)"),
			       servers_s),
		`HBox (
		    `CheckBox (`id (`local_broadcast),
			       // checkbox label
			       _("&Broadcast"), broadcast),
		    `CheckBox (`id (`slp),
			       // checkbox label
			       _("&SLP"), slp),
		    // pushbutton label, find nis servers
		    // Shortcut must not conflict with Finish and Next (#29960)
		    `PushButton (`id (`find), _("Fin&d"))
		    ),
		`Empty ()
		);

	term contents =
	    `HBox(
		`HSpacing(1),
		`VBox(
		    `VSpacing(0.2),
		    // Translators: popup dialog heading
		    `Heading (_("Domain Settings")),
		    // Add a domain, Adding a domain? Edit...
		    // Translators: text entry label
		    `Left (`TextEntry (`id (`domain), _("&Domain name"), domain)),
		    `VSpacing(0.5),
		    t_servers,
		    `VSpacing(0.2),
		    `HBox (`PushButton (`id (`ok), `opt (`default, `key_F10),
					Label::OKButton ()),
			   `PushButton (`id (`cancel), `opt (`key_F9),
					Label::CancelButton ())),
		    `VSpacing(0.2)
		    ),
		`HSpacing(1)
		);

	UI::OpenDialog (`opt(`decorated), contents);
	UI::SetFocus (`id (`domain));

	any ui = nil;
	while (true)
	{
	    ui = UI::UserInput ();
	    if (ui == `cancel)
	    {
		break;
	    }
	    else if (ui == `find)
	    {
		domain = (string) UI::QueryWidget (`id (`domain), `Value);
		if (domain == "")
		{
		    // Message popup. The user wants to Find servers
		    // but the domain is unknown.
		    Popup::Message (_("Finding servers works only when the domain is known."));
		    UI::SetFocus (`id (`domain));
		}
		else
		{
		    string servers = SelectNisServers (domain);
		    if (servers != "")
		    {
			UI::ChangeWidget (`id (`servers), `Value, servers);
		    }
		}
	    }
	    else if (ui == `ok)
	    {
		// Input validation
		// all querywidgets done now for consistency
		domain = (string) UI::QueryWidget (`id (`domain), `Value);
		servers_s = (string) UI::QueryWidget (`id (`servers), `Value);
		broadcast = (boolean) UI::QueryWidget (`id (`local_broadcast), `Value);
		slp = (boolean) UI::QueryWidget (`id (`slp), `Value);

		servers = splitstring (servers_s, ", \n");
		servers = filter (string s, servers, ``( s != "" ));
		string bad_server = (string) find (string s, servers, ``(
					      !Nis::check_address_nis (s)
					      ));

		if (!Nis::check_nisdomainname (domain)) //also disallows ""
		{
		    UI::SetFocus (`id (`domain));
		    Popup::Error (Nis::valid_nisdomainname ());
		}
		else if (init != "" && domain != init &&
			 contains (existing, domain))
		{
		    UI::SetFocus (`id (`domain));
		    // Translators: error message
		    Popup::Error (_("This domain is already defined."));
		}
		else if (bad_server != nil)
		{
		    UI::SetFocus (`id (`servers));
		    string msg = sformat (
			// Translators: error message
			_("The format of server address '%1' is not correct."), bad_server);
		    Popup::Error (msg + "\n\n" + Nis::valid_address_nis ());
		}
		// check options (local broadcast and slp)
		else if (broadcast && slp)
		{
		    UI::SetFocus (`id (`local_broadcast));
		    // error message, 'Broadcast' and 'SLP' are checkboxes
		    Popup::Error(_("Enabling both Broadcast and SLP options\ndoes not make any sense. Select just one option."));
		}
		else
		{
		    // all checks OK, break the input loop
		    break;
		}
	    }
	}

	UI::CloseDialog ();
	Wizard::RestoreScreenShotName ();
	return (ui == `ok)? [domain, [servers, broadcast, slp]] : nil;
    }

    /**
     * The servers dialog
     * @return	`back, `abort or `next
     */
    define symbol AdditionalDialog () {
	Wizard::SetScreenShotName ("nis-client-2b-multiple");

	// variable naming: _d means _domain, _s means _server
	map<string,list> all_servers = mapmap (string d, list s, Nis::multidomain_servers,
				  ``( $[d: [s, Nis::multidomain_broadcast[d]:false, Nis::slp_domain[d]:false]] ));

	// help text
	string help_text = _("<p>Specify the servers for additional domains.</p>");

	// help text
	help_text = help_text + broadcast_help;

	// help text
	help_text = help_text + _("<p>The Service Location Protocol (<b>SLP</b>) can be used to find NIS server.</p>");

	// "" means the default domain
	string current_d = "";

	// build the dialog contents
	term multiple = `VBox (
	    `VSpacing (0.8),
	    // dialog label
	    `Left (`Label(_("Additional Domains"))),
	    `Table (
		`id (`domains),
		`opt (`notify, `immediate),
		`header (
		    // table header
		    _("Domain"),
		    // table header
		    `Center (_("Broadcast")),
		    // table header - Service Location Protocol
		    `Center (_("SLP")),
		    // table header
		    _("Servers")),
		    DomainTableItems (nil, all_servers)
	    ),
	    `HBox (
		// button label
		`PushButton (`id (`add_d), `opt (`key_F3), _("A&dd")),
		`PushButton (`id (`edit_d),`opt(`key_F4), Label::EditButton ()),
		`PushButton (`id (`del_d), `opt(`key_F5), Label::DeleteButton())
	    ),
	    `VSpacing (1)
	);

	term nis_vbox =
	    `VBox (`VSpacing (0.2),
		   multiple,
		   `VSpacing (0.2)
		);

	term nis_frame =
	    // frame label
	    `Frame (_("NIS client"), nis_vbox);

	term contents = nis_vbox;

	Wizard::SetContentsButtons (
	    sformat ("%1 - %2",
		     // dialog title
		     _("Configuration of NIS client"),
		     // dialog subtitle
		     _("Additional Domains")), contents, help_text,
	    Label::CancelButton (), Label::AcceptButton ());
	Wizard::HideAbortButton ();

	any result = nil;

	while (true)
	{
	    current_d = (string) UI::QueryWidget (`id (`domains), `CurrentItem);
	    boolean any_d = current_d != nil;

	    UI::ChangeWidget (`id (`edit_d), `Enabled, any_d);
	    // deleting the defalut domain
	    // actually deletes only the server list
	    UI::ChangeWidget (`id (`del_d), `Enabled, any_d
			      /*&& current_d !=""*/);

	    // Kludge, because a `Table still does not have a shortcut.
	    // exclude textentry-notify
	    if (result != `domain)
	    {
		UI::SetFocus (`id (`domains));
	    }

	    result = UI::UserInput ();
	    if (result == `cancel)
	    {
		result = `abort;
	    }

	    // switch
	    if (result == `add_d)
	    {
		list name_servers = DomainPopup (nil, nil, [[], false], (list<string>)mapkeys (all_servers));
		if (name_servers != nil)
		{
		    string d = (string) name_servers[0]:nil;
		    list s_sp = name_servers[1]:[];
		    all_servers = add (all_servers, d, s_sp);
		    // show these items, d selected
		    UpdateDomainTable (nil, all_servers, d);
		}
	    }
	    else if (result == `edit_d)
	    {
		string d0 = (string) UI::QueryWidget (`id (`domains), `CurrentItem);

		if (d0 != nil)
		{
		    // editing the default domain is a special case
		    list name_servers = DomainPopup (d0, nil,
						     all_servers[d0]:[],
						     (list<string>) mapkeys (all_servers));
		    if (name_servers != nil)
		    {
			string d = (string) name_servers[0]:nil;
			list s_sp = name_servers[1]:[];
			string newkey = (d0=="")? "": d;
			all_servers = mapmap (string k, list v, all_servers,``(
						  (k == d0) ?
						  $[ newkey: s_sp ] :
						  $[ k: v]
						  ));
			// show these items, d selected
			// TODO: if it flickers,
			//  only replace the old line by a new one
			UpdateDomainTable (nil, all_servers, newkey);
		    }
		}
	    }
	    else if (result == `del_d)
	    {
		string d0 = (string) UI::QueryWidget (`id (`domains), `CurrentItem);
		if (d0 != nil)
		{
		    Wizard::SetScreenShotName ("nis-client-2b-del-dom");
		    // Translators: a yes-no popup
		    if (Popup::YesNo (_("Really delete this domain?")))
		    {
			all_servers = filter (string k, list v,
					      all_servers, ``(k != d0));
		    }
		    Wizard::RestoreScreenShotName ();
		    // show these items, the default domain selected
		    UpdateDomainTable (nil, all_servers, "");
		}
	    }
	    else if (result == `back || result == `next || (result == `abort &&
					 ReallyAbort (Nis::touched)))
	    {
		break;
	    }
	}

	if (result == `next)
	{
	    // add default server - it isn't displayed in the table
	    all_servers = add (all_servers, "",
			   [Nis::servers, Nis::default_broadcast]);

	    map<string,list<string> > only_servers = mapmap (string d, list v, all_servers, ``(
					   $[d: v[0]:[]]
					   ));
	    list<string> servers = only_servers[""]:[];
	    boolean default_broadcast = nil;
	    map<string,list<string> > multidomain_servers = nil;
	    map<string,boolean> multidomain_broadcast = nil;

	    map<string,boolean> only_broadcast = mapmap (string d, list v, all_servers, ``(
					     $[d: v[1]:false]
					     ));
	    default_broadcast = only_broadcast[""]:false;
	    multidomain_servers = filter (string d, list<string> v,
					  only_servers, ``( d != "") );
	    multidomain_broadcast = filter (string d, boolean v,
					    only_broadcast, ``( d != "") );

	    map slpdomain = mapmap (string d, list v, all_servers, ``(
					     $[d: v[2]:false]
					     ));

	    Nis::Touch (Nis::servers != servers);
	    Nis::servers = servers;
	    Nis::Touch (Nis::default_broadcast != default_broadcast);
	    Nis::default_broadcast = default_broadcast;
	    Nis::Touch (Nis::slp_domain != slpdomain);
	    Nis::slp_domain = slpdomain;
	    Nis::Touch (false); //TODO need to know earlier for abort?
	    Nis::multidomain_servers = multidomain_servers;
	    Nis::Touch (false); //TODO need to know earlier for abort?
	    Nis::multidomain_broadcast = multidomain_broadcast;
	}

	Wizard::RestoreScreenShotName ();
	return (symbol) result;
    }

    /**
     * Confirmation dialog
     * Also probes for packages that need to be installed (autofs)
     * #23050 don't display the dialog
     * @return `back or `next
     */
    define symbol SaveDialog () ``{
	Wizard::SetScreenShotName ("nis-client-3-save");

	string message = Nis::ProbePackages ();

	if (message != "")
	{
	    Popup::Message (message);
	}

	Wizard::RestoreScreenShotName ();
	return `next;
    }

    map Dialogs = $[
	"main"		: ``(MainDialog ()),
	"additional"	: ``(AdditionalDialog ()),
	"expert"	: ``(ExpertDialog ()),
	"common-next"	: [ ``( JustNext () ), true ],
	"end"		: ``(SaveDialog ()),
	];

    map Sequence = $[
	"ws_start"  : "main",
	"main"	       : $[
	    `next   : "common-next",
	    `expert : "expert",
	    `edit   : "additional",
	    `abort  : `abort
	    ],
	"additional"   : $[
	    `next   : "main",
	    `abort  : `abort
	    ],
	"expert"       : $[
	    `next   : "main",
	    `abort  : `abort
	    ],
	// This is will make AutoSequence finish without
	// confirmation. NormalSequence overrides it.
	"common-next"  : $[
	    `next   : `next,
	    ],
	"end"          : $[
	    `next   : `next
	    ]
	];

    /**
     * Dhcpcd writes yp.conf in the multidomain form.
     * Let's try rewriting it so that it fits into the simple dialog.
     * It is done only after read.
     */
    define void FitIntoSingle () ``{
	if (Nis::dhcp_wanted && Nis::dhcpcd_running)
	{
	    string d = Nis::GetDomain ();
	    if (size (Nis::multidomain_servers) == 1)
	    {
		list<string> s = Nis::multidomain_servers[d]:[];
		// if there's only one entry, for the correct domain
		if (size (s) > 0)
		{
		    y2milestone ("Fitting into the simple dialog");
		    Nis::servers = s;
		    Nis::multidomain_servers = $[];
		    Nis::default_broadcast = Nis::multidomain_broadcast[d]:false;
		    Nis::multidomain_broadcast = $[];
		}
	    }
	}
    }

    /**
     * The normal workflow
     * @return `back, `abort or `next
     */
    define symbol NormalSequence() ``{
	map normal_override = $[
	    "common-next": $[
		`next: "end",
		],
	    ];

	Wizard::CreateDialog ();
	Wizard::SetDesktopIcon("nis");

	if (Mode::screen_shot())
	{
	    Nis::Fake ();
	}
	else
	{
	    Nis::Read ();
	}

	FitIntoSingle ();

	// the second map must override the first!
	symbol result = Sequencer::Run(Dialogs,
					 union (Sequence, normal_override));

	if (result == `next)
	{
	    if (Nis::start)
		// popup text FIXME better...
		Autologin::AskForDisabling(_("NIS is now enabled."));

	    // Install packages if needed.
	    // Cannot do it in Write, autoinstall does it differently.
	    if (size (Nis::install_packages) > 0)
	    {
		if (!Package::DoInstallAndRemove (Nis::install_packages, []))
		{
		    Popup::Error (Message::FailedToInstallPackages ());
		}
	    }

	    if (Nis::Write ())
	    {
		if (Nis::start && Nis::DomainChanged ())
		{
		    Popup::Warning (Message::DomainHasChangedMustReboot());
		}
	    }
	}
	UI::CloseDialog();
	return result;
    }

    /**
     * The autoinstallation workflow
     * @return `back, `abort or `next
     */
    define symbol AutoSequence() ``{
	Wizard::CreateDialog ();
	Wizard::SetDesktopIcon("nis");
	symbol ret = Sequencer::Run(Dialogs, Sequence);
	UI::CloseDialog ();
	return ret;
    }

}
