/**
 * File:	include/users/dialogs.ycp
 * Package:	Configuration of users and groups
 * Summary:	Dialogs definitions
 * Authors:	Johannes Buchhold <jbuch@suse.de>,
 *		Jiri Suchomel <jsuchome@suse.cz>
 *
 * $Id: dialogs.ycp 21965 2005-02-25 15:15:08Z jsuchome $
 */

{

textdomain "users";

import "Autologin";
import "Label";
import "Ldap";
import "LdapPopup";
import "Message";
import "Package";
import "Popup";
import "Progress";
import "Report";
import "Stage";
import "Summary";
import "Users";
import "UsersCache";
import "UsersLDAP";
import "UsersPlugins";
import "Wizard";

include "users/helps.ycp";
include "users/routines.ycp";

string default_pw	= "******";

/**
 * helper function: return the 'any' value as integer
 */
define integer GetInt (any value, integer default_value) {

    if (value == nil)
	return default_value;
    if (is (value, integer))
	return (integer) value;
    if (is (value, string) && value != "")
	return tointeger ((string)value);
    return default_value;
}

/**
 * helper function: return the 'any' value as string
 */
define string GetString (any value, string default_value) {

    if (value == nil)
	return default_value;
    return sformat ("%1", value);
}

/**
 * Upperase letters were used in username! (see bug #26409)
 * In these popup, ask user what to do.
 */
define symbol AskForUppercasePopup (string username) {

    symbol ret = `ok;

    if (username != tolower (username) && !Users::NotAskUppercase () &&
	Package::InstalledAny (["sendmail", "postfix"]))
    {
	// The login name contains uppercase 1/3
	string text = _("<p>
You have used uppercase letters in the user login entry.</p>") +

        // The login name contains uppercase 2/3
_("<p>This could cause problems with delivering the mail
to this user, because mail systems generally do not
support case-sensitive names.<br>
You could solve this problem by editing the alias table.</p>
") +

        // The login name contains uppercase 3/3
_("<p>Really use the entered value?</p>");

	UI::OpenDialog (`opt(`decorated), `HBox(`VSpacing (14),
	    `VBox (
		`HSpacing(50),
		`RichText (`id(`rt), text),
		`CheckBox (`id(`ch), `opt(`notify),
		    Message::DoNotShowMessageAgain()),
		`HBox(
		    `PushButton (`id(`ok),`opt(`key_F10), Label::YesButton()),
		    `PushButton (`id(`no),`opt(`key_F9), Label::NoButton())
		)
	    ))
	);
	do {
	    ret = (symbol) UI::UserInput();
	}
	while (!contains ([`cancel, `ok, `no], ret));

	if (ret != `cancel)
	{
	    Users::SetAskUppercase ((boolean)UI::QueryWidget(`id(`ch),`Value));
	}
	UI::CloseDialog();
    }
    return ret;
}


/**
 * Dialog for adding or editing a user.
 * @param what "add_user" or "edit_user"
 * @return symbol for wizard sequencer
 */
define symbol EditUserDialog (string what) {

    // user has returned to the "add user dialog" during installation workflow:
    if (Users::StartDialog ("user_add") &&
	Stage::cont () && Users::UseNextTime ())
    {
	Users::RestoreCurrentUser ();
	Users::SetUseNextTime (false);
    }

    map<string,any>user	= Users::GetCurrentUser ();
    string error_msg	= "";

    if (user == $[])
    {
	error_msg = Users::AddUser ($[]);
	if (error_msg != "")
	{
	    Popup::Error (error_msg);
	    return `back;
	}
	user = Users::GetCurrentUser ();
    }

    string user_type	= user["type"]:"local";
    string username	= user["uid"]:"";
    string cn		= "";
    // in LDAP, cn is list of strings
    if (is (user["cn"]:nil, list))
	cn	= user["cn",0]:"";
    else
	cn	= user["cn"]:"";
    string tmp_fullname	= cn; // for login proposing
    string default_home = Users::GetDefaultHome (user_type);
    string home		= user["homedirectory"]:default_home;
    string org_home	= user["org_homedirectory"]:home;
    string password	= user["userpassword"]:"x";
    string org_username = user["org_uid"]:username;
    integer uid		= GetInt (user["uidnumber"]:nil, nil);
    integer gid		=
	GetInt (user["gidnumber"]:nil, Users::GetDefaultGID (user_type));
    string dn		= user["dn"]:"";
    boolean enabled	= user["enabled"]:true;
    if (user["disabled"]:false)
	enabled		= false;

    string shell        = user["loginshell"]:"";
    string defaultgroup = user["groupname"]:"";
    // additional parts of GECOS (shown by `finger <username>`) (passwd only)
    string addit_data	= user["addit_data"]:"";

    // only for LDAP users:
    string sn		= "";
    if (haskey (user, "sn"))
    {
	if (is (user["sn"]:nil, list))
	    sn	= user["sn",0]:SplitFullName(`sn, cn);
	else
	    sn	= user["sn"]:SplitFullName(`sn, cn);
    }
    string givenname		= "";
    if (haskey (user, "givenname"))
    {
	if (is (user["givenname"]:nil, list))
	    givenname	= user["givenname",0]:SplitFullName(`givenname, cn);
	else if (is (user["givenname"]:nil, string))
	    givenname	= user["givenname"]:SplitFullName(`givenname, cn);
    }

    boolean create_home		= user["create_home"]:true;
    boolean no_skel		= user["no_skeleton"]:false;
    boolean do_not_edit		= (user_type == "nis");
    boolean complex_layout = Stage::cont () && Users::StartDialog ("user_add");
    map<string,integer> groups	= user["grouplist"]:$[];

    list available_shells	= Users::AllShells ();
    string grouplist		= "";
    string new_type		= user_type;

    map<string,map<string,integer> > all_groupnames	=
	UsersCache::GetAllGroupnames ();

    // backup NIS groups of user (they are not shown in details dialog)
    map<string,integer> nis_groups	= $[];
    foreach (string group, integer val, groups, {
	if (all_groupnames ["nis", group]:0 == 1)
	    nis_groups [group] = 1;
    });
    // of local group list of remote user was modified
    boolean grouplist_modified	= false;

    // date of passwrod expiration
    string exp_date = "";

    string plugin_client	= "";
    string plugin		= "";

    /**
     * initialize local variables with current state of user
     */
    define void reinit_userdata () {

	user_type	= user["type"]:user_type;
	username	= user["uid"]:username;
	if (is (user["cn"]:nil, list))
	    cn	= user["cn",0]:cn;
	else
	    cn	= user["cn"]:cn;
	home		= user["homedirectory"]:home;
	org_home	= user["org_homedirectory"]:org_home;
	password	= user["userpassword"]:password;
	org_username = user["org_uid"]:org_username;
	uid		= GetInt (user["uidnumber"]:nil, uid);
	gid		= GetInt (user["gidnumber"]:nil, gid);
	dn		= user["dn"]:dn;
	enabled		= user["enabled"]:true;
	if (user["disabled"]:false)
	    enabled	= false;

	shell		= user["loginshell"]:shell;
	defaultgroup	= user["groupname"]:defaultgroup;
	addit_data	= user["addit_data"]:addit_data;

	if (haskey (user, "sn"))
	{
	    if (is (user["sn"]:nil, list))
		sn	= user["sn",0]:SplitFullName(`sn, cn);
	    else
		sn	= user["sn"]:SplitFullName(`sn, cn);
	}
	if (haskey (user, "givenname"))
	{
	    if (is (user["givenname"]:nil, list))
		givenname = user["givenname",0]:SplitFullName(`givenname, cn);
	    else if (is (user["givenname"]:nil, string))
		givenname = user["givenname"]:SplitFullName(`givenname, cn);
	}

	create_home	= user["create_home"]:create_home;
	no_skel		= user["no_skeleton"]:no_skel;
	groups		= user["grouplist"]:$[];
	do_not_edit	= (user_type == "nis");
    }

    /**
     * generate contents for User Data Dialog
     */
    define term get_edit_term () {

	term name_entries =
	    // text entry
	    `TextEntry (`id(`cn), _("&Full User Name"), cn);

	if (user_type == "ldap")
	{
	    name_entries = `HBox(
            `HWeight(50,
		// text entry
		`TextEntry(`id(`givenname), _("&First Name"), givenname)
	    ),
	    `HSpacing (0.5),
            `HWeight(50,
		// text entry
		`TextEntry(`id(`sn),  _("&Last Name"),  sn )
	    ));
	}

	term fields = `VBox(
	`VBox (
	    // label text
	    do_not_edit ? `Label(_("Only additional group memberships
can be changed for non-local users.
")) : `VSpacing (0),
	    do_not_edit ? `VSpacing (1) : `VSpacing (0),
	    name_entries
	),
	`VSquash(
	    `HBox(
		// input field for login name
		`Bottom(`TextEntry(`id(`username), _("&User Login"), username)),
		(what != "add_user")
		    ? `HSpacing (0)
		    // push button to generate login from first and last name
		    : `Bottom(`PushButton(`id(`propose), _("&Suggestion"))))
	),
	`Password(`id(`pw1), Label::Password(), ""),
	`Password(`id(`pw2), Label::ConfirmPassword(), "")
	);

	term optionbox = `Empty ();

	if (complex_layout)
	{
	    optionbox = `VBox(
		// checkbox label
		`Left( `CheckBox (`id (`root_mail),_("Receive S&ystem Mail"),
		    Users::GetRootMail () != "") ),
		// checkbox label
		`Left( `CheckBox (`id (`autologin),_("A&uto Login"),
		    Autologin::used) )
	    );
	    optionbox = `HBox(
		`HStretch(),
		`HVCenter( `HVSquash( optionbox ) ),
		`HStretch()
	    );
	}
	else if (!do_not_edit && !Stage::cont ())
	{
	    optionbox = `VBox (
		`VSpacing (1),
		// check box label
		`CheckBox (`id(`ena), _("D&isable User Login"), !enabled),
		`VSpacing (0.5)
	    );
	}

	term contents = `VBox (
	`VSpacing(),
	`VBox (
	    `VSpacing( 0.5 ),
	    `HBox (
		`HSpacing (2),
		`VBox (
		    `HSquash ( fields ),
		    `VSpacing( 0.5 ),
		    `VSquash ( `Top( optionbox ) )
		),
		`HSpacing( 2 )
	    ),
	    `VSpacing (0.5)
	)
	);

	if (complex_layout)
	{
	    contents = add ( contents, `VBox (
	    `HCenter(`PushButton(`id(`additional), `opt(`key_F3),
		// push button
		_("User &Management") ) ),
	    `VSpacing (0.5))
	    );
	}
	return `HVCenter (contents);
    }

    /**
     * generate contents for User Details Dialog
     */
    define term get_details_term () {

	list available_groups		= [];
	list additional_groups		= [];
	list additional_ldap_groups	= [];
	boolean defaultgroup_shown	= false;

	// fill the list available_groups and set the user default group true
	foreach (string grouptype, map<string,integer> groupmap, all_groupnames,
	{
        if (grouptype == "local" || grouptype == "system" ||
            (grouptype == "ldap" && user_type == "ldap"))
	{
          foreach (string group, integer val, groupmap, {

            if (user_type == "ldap")
            {
                if (grouptype == "ldap")
                {
                    if ( group == defaultgroup )
		    {
                        available_groups = add (available_groups,
                            `item( `id(group), group, true));
			defaultgroup_shown	= true;
		    }
                    else
                        available_groups = add (available_groups,
                            `item( `id(group), group));
                    if ( haskey (groups, group))
                        additional_ldap_groups = add (additional_ldap_groups,
                            `item( `id(group), group, true ));
                    else
                        additional_ldap_groups = add (additional_ldap_groups,
                            `item( `id(group), group, false));
                }
                else
                {
		    // if there is a group with same name, use only that
		    // with type "ldap"
		    if ( all_groupnames ["ldap",group]:0 == 1 )
			return;
                    if ( haskey (groups, group))
                        additional_groups = add (additional_groups,
                            `item( `id(group), group, true ));
                    else
                        additional_groups = add (additional_groups,
                            `item( `id(group), group, false));
                }
            }
            else
            {
                if ( group == defaultgroup )
		{
                    available_groups = add (available_groups,
                        `item( `id(group), group, true));
		    defaultgroup_shown	= true;
		}
                else
                    available_groups = add (available_groups,
                        `item( `id(group), group));
                if ( haskey (groups, group))
                    additional_groups = add (additional_groups,
                        `item( `id(group), group, true ));
                else
                    additional_groups = add (additional_groups,
                        `item( `id(group), group, false));
            }
          });
	}
	});
	// show default group, even if the type is 'wrong' (#43433)
	if (!defaultgroup_shown)
	{
	    if (all_groupnames ["local", defaultgroup]:0 == 1 ||
		all_groupnames ["system", defaultgroup]:0 == 1)
	    {
		available_groups = add (available_groups,
		    `item( `id(defaultgroup), defaultgroup, true));
	    }
	}

	if (defaultgroup == "")
	{
	    available_groups = add (available_groups,
		// group name is not known (combobox item):
		`item( `id(""), _("(not known)"), true));
	}

	term edit_defaultgroup = `ComboBox(`id(`defaultgroup), `opt(`hstretch),
	    // combobox label
	    _("De&fault Group"), available_groups );
	term edit_shell = `ComboBox(`id(`shell), `opt(`hstretch, `editable),
	    // combobox label
	    _("Login &shell"), available_shells );

	term additional_data = `Empty();
	if (user_type == "system" || user_type == "local")
	    additional_data =
		// textentry label
		`Top(`TextEntry(`id(`addd), _("Addi&tional User Information:"),
		    addit_data));

	term browse = `VBox(
	    `Label (""),
	    // button label
	    `PushButton( `id(`browse), `opt(`key_F6), _("B&rowse...")));

	term home_w = `VBox(
	    // textentry label
	    `TextEntry( `id(`home), _("&Home Directory"), home )
	);
	return `HBox (
          `HSpacing(1),
          `VBox(
           // label
           do_not_edit ? `Label(_("Only additional group memberships
can be changed for non-local users.")) : `VSpacing (0),
           `VSpacing(0.5),
           `HBox(
		`HSpacing (1),
                `VBox(
		    `VSpacing (0.5),
                    // textentry label
                    `Top(`TextEntry( `id(`uid), _("User &ID (uid)"),
                        sformat("%1",uid ))),
                    `Top(`VBox(
			`HBox ( home_w, browse),
			what != "add_user" ? `VSpacing (0) :
			`Left (
			    `CheckBox (`id(`skel), _("E&mpty Home"), no_skel)
			)
		    )),
                    additional_data,
                    `Top(edit_shell),
                    `Top(edit_defaultgroup),
                    `VStretch()
                ),
                `HSpacing(2),
		`VBox(
		    `VSpacing (0.5),
		    `Top(`MultiSelectionBox( `id(`grouplist),
			// selection box label
			_("Gr&oups"), additional_groups)),
		    (user_type == "ldap")
		    ? `MultiSelectionBox( `id(`ldapgrouplist),
			// selection box label
			_("&LDAP Groups"), additional_ldap_groups)
		    : `Empty()
		    ),
		`HSpacing (1)
	    ),
            `VSpacing(0.5)
           ),
           `HSpacing(1)
	);
    }

    /**
     * generate contents for Password Settings Dialog
     */
    define term get_password_term () {

	string last_change	= GetString (user["shadowlastchange"]:nil, "0");
	string expires		= GetString (user["shadowexpire"]:nil, "0");
	if (expires == "")
	    expires = "0";

	integer inact	= GetInt (user["shadowinactive"]:nil, 0);
	integer max	= GetInt (user["shadowmax"]:nil, 0);
	integer min	= GetInt (user["shadowmin"]:nil, 0);
	integer warn	= GetInt (user["shadowwarning"]:nil, 0);

	if (last_change != "0")
	{
	    map out	= (map)SCR::Execute (.target.bash_output, sformat ("date --date='1970-01-01 00:00:01 %1 days' +\"%%x\"", last_change));
	    // label (date of last password change)
	    last_change = out["stdout"]:_("unknown");
	}
	else
	{
	    // label (date of last password change)
	    last_change = _("never");
	}
	if (expires != "0" && expires != "-1" && expires != "")
	{
	    map out	= (map)SCR::Execute (.target.bash_output, sformat ("date --date='1970-01-01 00:00:01 %1 days' ", expires) + "+\"%Y-%m-%d\"");
	    // remove \n from the end
	    exp_date	= deletechars (out["stdout"]:"", "\n");
	}
	return `HBox (
	    `HSpacing (3),
            `VBox (
                `VStretch(),
                `Top(`Left (`Label (sformat (
		    // label
		    _("Date of the last password change: %1"), last_change )))),
                `VSpacing (1),
                `HSquash(`VBox(
                    `IntField (`id ("shadowwarning"),
			// intfield label
			_("Days &before Password Expiration to Issue Warning"),
			-1, 99999, warn),
                    `VSpacing (0.5),
                    `IntField (`id ("shadowinactive"),
			// intfield label
			_("Days after Password Expires with Usable &Login"),
			-1, 99999, inact),
                    `VSpacing (0.5),
                    `IntField (`id ("shadowmax"),
			// intfield label
			_("Ma&ximum number of days for the same password"),
			-1, 99999, max),
                    `VSpacing (0.5),
                    `IntField (`id ("shadowmin"),
			// intfield label
			_("&Minimum number of days for the same password"),
			-1, 99999, min),
                    `VSpacing (0.5),
                    `TextEntry (`id ("shadowexpire"),
			// textentry label
			_("Ex&piration Date"), exp_date)
                )),
                `VStretch ()),
            `HSpacing (3)
	);
    }

    /**
     * generate contents for Plugins Dialog
     */
    define term get_plugins_term () {

	// UsersPlugins will filter out plugins we cannot use for given type
	map plugin_clients	= UsersPlugins::Apply ("GUIClient",
	    $[ "what" : "user", "type" : user_type ], $[]
	);
	// remove empty clients
	plugin_clients = filter (string plugin, string client,
	    (map<string,string>) plugin_clients, ``(client != ""));

	map client2plugin	= $[];
	list<string> clients	= maplist (string plugin, string client,
	    (map<string,string>) plugin_clients, {

		client2plugin [client]	= plugin;
		return client;
	});

	plugin_client	= clients[0]:"";
	plugin		= client2plugin[plugin_client]:plugin_client;

	list items = [];
	foreach (string cl, clients, {
	    any summary = WFM::CallFunction (cl, ["Summary", $["what" : what]]);
	    string pl	= client2plugin[cl]:cl;
	    if (is (summary, string))
		items = add (items, `item(`id(cl),
		    contains (user["plugins"]:[], pl) ?
			UI::Glyph (`CheckMark) : " ",
		    summary)
		);
	});
	return `HBox (`HSpacing (0.5), `VBox (
	    `Table (`id(`table), `opt(`notify), `header (" ",
		// table header
	        _("Plug-In Description")), items
	    ),
	    `HBox (
		`PushButton (`id(`change), `opt(`key_F3),
		    // pushbutton label
		    _("Add &or Remove Plug-In")),
		// pushbutton label
		`Right (`PushButton(`id(`run), `opt(`key_F6), _("&Launch")))
	    ),
	    `VSpacing (0.5)
	), `HSpacing (0.5));
    }


    map dialog_labels = $[
        "add_user": $[
            // dialog caption:
            "local":    _("Add a New Local User"),
            // dialog caption:
            "system":	_("Add a New System User"),
            // dialog caption:
            "ldap":     _("Add a New LDAP User")
        ],
        "edit_user": $[
            // dialog caption:
            "local":    _("Edit an Existing Local User"),
            // dialog caption:
            "system":	_("Edit an Existing System User"),
            // dialog caption:
            "ldap":	_("Edit an Existing LDAP User"),
            // dialog caption:
            "nis":	_("Edit an Existing NIS User")
        ]
    ];

    list<term> tabs	= [
	// tab label
	`item(`id(`edit), _("Us&er Data"), true),
	// tab label
	`item(`id(`details), _("&Details") ),
    ];

    if (!do_not_edit)
    {
	// tab label
	tabs = add (tabs,`item(`id(`passwordsettings),_("Pass&word Settings")));
    }

    if (user_type == "ldap")
    {
	// tab label
	tabs = add (tabs, `item(`id(`plugins), _("Plu&g-Ins") ));
    }

    term dialog_contents = `VBox (
	`DumbTab (`id(`tabs), tabs,
	    `ReplacePoint(`id(`tabContents ), get_edit_term ()))
    );
    boolean has_tabs	= true;
    if (!UI::HasSpecialWidget (`DumbTab))
    {
	has_tabs	= false;
	term tabbar	= `HBox ();
	foreach (term it, tabs, {
	    string label = it[1]:"";
	    tabbar = add (tabbar,`PushButton (it[0]:`id(label), label));
	});
	dialog_contents = `VBox (`Left(tabbar),
	    `Frame ("", `ReplacePoint(`id(`tabContents), get_edit_term ()))
	);
    }
    if (complex_layout)
	dialog_contents	= get_edit_term ();

    Wizard::SetContentsButtons (
        dialog_labels [ what, user_type ]:"",
        dialog_contents,
        EditUserDialogHelp (complex_layout, user_type, what),
        complex_layout ? Label::BackButton () : Label::CancelButton(),
	complex_layout ? Label::NextButton () : Label::AcceptButton()
    );
    if (!complex_layout)
	Wizard::HideAbortButton ();

    symbol ret		= `edit;
    symbol current	= nil;
    list tabids		= [ `edit, `details, `passwordsettings, `plugins ];
    map ldap_user_defaults = UsersLDAP::GetUserDefaults ();

    // switch focus to specified tab (after error message) and widget inside
    define void focus_tab (symbol tab, any widget) {
	if (has_tabs)
	    UI::ChangeWidget (`id (`tabs), `CurrentItem, tab);
	UI::SetFocus (`id (widget));
	ret = `notnext;
    }

    while (true)
    {
	// map returned from Check*UI functions
	map error_map			= $[];
	// map with id's of confirmed questions
	map<string,integer> ui_map	= $[];
	// error message
	string error			= "";

	if (current != nil)
	{
	    ret = (symbol) UI::UserInput ();
	}

	if ((ret == `abort || ret == `cancel) && ReallyAbort () != `abort)
	{
	    ret = `notnext;
	    continue;
	}
	if (contains ([`abort,`back,`cancel], ret))
	    break;

	boolean tab	= contains (tabids, ret);
	if (tab && ret == current)
	{
	    continue;
	}

	// ------------------- handle actions inside the tabs

	// 1. click inside User Data dialog or moving outside of it
	if (current == `edit)
	{
	    username	= (string) UI::QueryWidget(`id(`username), `Value);

	    // empty username during installation (-> go to next module)
	    if (username == "" && ret ==`next && Users::StartDialog("user_add"))
	    {
		// The user login field is empty, this is allowed if the
		// system is part of a network with (e.g.) NIS user management.
		// yes-no popup headline
		if (Popup::YesNoHeadline(_("Empty user login"),
		    // yes-no popup contents
		    _("Leaving the user name empty only makes sense
in a network environment with an authentication server.
Are you sure?")))
		{
		    ret = `nextmodule;
		    break;
		}
		focus_tab (current, `username);
		continue;
	    }

	    // now gather user data from dialog
	    if (user_type == "ldap")
	    {
		// Form the fullname for LDAP user
		// sn (surname) and cn (fullname) are required attributes,
		// they cannot be empty
		givenname = (string) UI::QueryWidget(`id(`givenname), `Value);
		sn	  = (string) UI::QueryWidget(`id(`sn ), `Value);

		// create default cn/sn if they are not marked for substitution
		if (sn == "" &&
		    (what == "edit_user" ||
		    !haskey (ldap_user_defaults, "sn")))
		{
		    if (givenname == "")
		    {
			sn = username;
		    }
		    else
		    {
			sn = givenname;
			givenname = "";
		    }
		}
		if (cn == "" &&
		    // no substitution when editing:
		    (what == "edit_user" ||
		    // cn should not be substitued:
		    !haskey (ldap_user_defaults, "cn")))
		{
		    // if 'givenname' or 'sn' should be substitued, wait for it
		    // and do not create cn now:
		    if (!haskey (ldap_user_defaults, "sn") &&
			!haskey (ldap_user_defaults, "givenname"))
		    {
			cn = givenname + ((givenname != "") ? " " : "") + sn;
		    }
		}
		UI::ChangeWidget(`id(`givenname), `Value, givenname);
		UI::ChangeWidget(`id(`sn), `Value, sn);
	    }
	    else
	    {
		cn	= (string) UI::QueryWidget(`id(`cn), `Value);
		error	= Users::CheckFullname (cn);
		if (error != "")
		{
		    Report::Error (error);
		    focus_tab (current, `cn);
		    continue;
		}
	    }
	    if (haskey (user, "givenname") && is (user["givenname"]:nil, list))
		user ["givenname",0]	= givenname;
	    else
		user ["givenname"]	= givenname;
	    if (haskey (user, "sn") && is (user["sn"]:nil, list))
		user["sn",0]	= sn;
	    else
		user ["sn"]	= sn;
	    if (haskey (user, "cn") && is (user["cn"]:nil, list))
		user["cn",0]	= cn;
	    else
		user ["cn"]	= cn;

	    // generate a login name from first and last name
	    if (ret == `propose)
	    {
		string to_propose = (issubstring (cn, "%") || cn == "") ?
		    givenname + " " + sn : cn;
		if (tmp_fullname != to_propose)
		{
		    UsersCache::ResetProposing ();
		    tmp_fullname = to_propose;
		}
		username = UsersCache::ProposeUsername (
		    filterchars (to_propose, Users::ValidLognameChars() + " "));
		UI::ChangeWidget(`id(`username), `Value, username);
	    }
	    // in continue mode: move to 'User Management' without adding user
	    if (ret == `additional)
	    {
		if (username == "" &&
		    ((user_type == "ldap" && cn == "" && givenname == "") ||
		     (user_type != "ldap" && cn == ""))
		)
		    ret = `nosave;
	    }
	}
	// now check if currently added user data are correct
	// (going out from User Data tab)
	if (current == `edit && !do_not_edit &&
	    (ret == `next || ret == `additional || tab))
	{
	    // --------------------------------- username checks, part 1/2
	    error = Users::CheckUsername (username);
	    if (error != "")
	    {
                Report::Error (error);
		focus_tab (current, `username);
                continue;
	    }
	    user["uid"]		= username;

	    // --------------------------------- uid check (for nil value)
	    if (!tab && uid == nil)
	    {
		error = Users::CheckUID (uid);
		if (error != "")
		{
		    Report::Error (error);
		    focus_tab (current, `details);
		    continue;
		}
	    }

	    // --------------------------------- password checks
            string pw1   = (string) UI::QueryWidget(`id(`pw1), `Value);
            string pw2   = (string) UI::QueryWidget(`id(`pw2), `Value);

            if (pw1 != pw2)
            {
                // The two user password information do not match
                // error popup
                Report::Error(_("The first and the second version
of the password are different.
Please try again."));
		focus_tab (current, `pw1);
                continue;
            }
            if ((pw1 != "" || !tab) && pw1 != default_pw)
            {
		error = Users::CheckPassword (pw1);
		if (error != "")
		{
		    Report::Error (error);
		    focus_tab (current, `pw1);
		    continue;
		}
		boolean failed			= false;
		do
		{
		    error_map = Users::CheckPasswordUI (username, pw1, ui_map);
		    if (error_map != $[])
		    {
			if (!Popup::YesNo (error_map ["question"]:""))
			{
			    failed = true;
			}
			else
			    ui_map[ error_map["question_id"]:"" ] = 1;
		    }
		} while (error_map != $[] && !failed);

		if (failed)
		{
		    focus_tab (current, `pw1);
		    continue;
		}
		// now saving plain text password
		if (user["encrypted"]:false)
		    user["encrypted"]	= false;
		user ["userpassword"]		= pw1;
		user ["shadowlastchange"]	= Users::LastChangeIsNow();
		password			= pw1;
	    }

            // build default home dir
            if (home == "" || home == default_home || issubstring (home,"%"))
            {
		// LDAP: maybe value of homedirectory should be substituted?
		if (user_type == "ldap" && issubstring (home, "%"))
		{
		    user = UsersLDAP::SubstituteValues ("user", user);
		    home = user["homedirectory"]:default_home;
		}
		if (home == default_home || home == "")
                    home = default_home + username;
            }
	    if (ret != `details && username != org_username)
	    {
		string generated_home	= default_home + username;
		if (user_type == "ldap" && issubstring (home, "%"))
		{
		    map tmp_user = UsersLDAP::SubstituteValues ("user", user);
		    generated_home = tmp_user["homedirectory"]:home;
		}
		if (home != generated_home &&
		    (what == "add_user" || Popup::YesNo (sformat (
// popup question
_("Change home directory to %1?"), generated_home))))
		{
			home	= generated_home;
		}
            }
	    // -------------------------------------- directory checks
	    if (!tab && home != org_home)
	    {
		error = Users::CheckHome (home);
		if (error != "")
		{
		    Report::Error (error);
		    ret = `notnext;
		    continue;
		}
		boolean failed			= false;
		do
		{
		    error_map = Users::CheckHomeUI (uid, home, ui_map);
		    if (error_map != $[])
		    {
			if (!Popup::YesNo (error_map ["question"]:""))
			    failed = true;
			else
			    ui_map[ error_map["question_id"]:"" ] = 1;
		    }
		} while (error_map != $[] && !failed);

		if (failed)
		{
		    ret = `notnext;
		    continue;
		}
	    }
	    user["homedirectory"]	= home;

	    // --------------------------------- username checks, part 2/2
            if (what == "add_user" || username != org_username)
	    {
		if (AskForUppercasePopup (username) != `ok)
		{
		    focus_tab (current, `username);
		    continue;
		}
	    }
	    // --------------------------------- autologin (during installation)
	    if (Users::StartDialog ("user_add") && Stage::cont ())
	    {
		Users::SetRootMail ((boolean)
		    UI::QueryWidget(`id (`root_mail), `Value) ? username : "");
		if (Autologin::available)
		{
		    Autologin::user = (boolean)
			UI::QueryWidget(`id (`autologin),`Value) ? username: "";
		    Autologin::used = (boolean)
			UI::QueryWidget(`id (`autologin),`Value);
		}
	    }
	    else
	    {
	    // -------------------------------------- enable/disable checks

		boolean new_enabled = !(boolean)
		    UI::QueryWidget(`id(`ena), `Value);
		if (enabled	!= new_enabled)
		{
		    enabled	= new_enabled;
		    if (enabled)
		    {
			user["enabled"]		= true;
			if (haskey (user,"disabled"))
			    user ["disabled"]	= false;
		    }
		    else
		    {
			user["disabled"]	= true;
			if (haskey (user,"enabled"))
			    user ["enabled"]	= false;
		    }
		}
	    }
	}

	if (current == `details && ret == `browse)
	{
	    string dir = home;
	    if (SCR::Read(.target.size, home ) == -1)
	    {
		dir = Users::GetDefaultHome (new_type);
	    }
	    dir = UI::AskForExistingDirectory (dir, "");
	    if (dir != nil)
	    {
		if ((findlastof (dir, "/") + 1) == size(dir))
		    dir = substring (dir, 0, size(dir)-1);
		UI::ChangeWidget (`id(`home), `Value, dir);
	    }
	}

	// going from Details dialog
        if (current == `details && (ret == `next || tab))
        {
            string  new_shell	= (string)UI::QueryWidget(`id(`shell), `Value);
            string  new_uid	= (string)UI::QueryWidget(`id(`uid),   `Value);
            string  new_defaultgroup = (string)
		UI::QueryWidget(`id(`defaultgroup),`Value);
            string  new_home	= (string) UI::QueryWidget(`id(`home),  `Value);

	    if (what == "add_user")
	    {
		no_skel	= (boolean) UI::QueryWidget(`id(`skel),`Value);
	    }
	    if ((findlastof (new_home, "/") + 1) == size (new_home))
	    {
		new_home = substring (new_home, 0, size(new_home)-1);
	    }

            if (do_not_edit)
            {
                new_home	= home;
                new_shell	= shell;
                new_uid		= sformat("%1", uid);
                new_defaultgroup = defaultgroup;
            }
            integer new_i_uid    = tointeger (new_uid);

	    // additional data in GECOS field (passwd only)
            if (new_type == "local" || new_type == "system")
            {
                addit_data  = (string) UI::QueryWidget(`id(`addd),  `Value);
		string error = Users::CheckGECOS (addit_data);
		if (error != "")
		{
		    Report::Error (error);
		    focus_tab (current, `addd);
		    ret = `notnext;
		    continue;
		}
            }

            // check the uid
            if (new_i_uid != uid)
            {
		string error = Users::CheckUID (new_i_uid);
		if (error != "")
		{
		    Report::Error (error);
		    focus_tab (current, `uid);
		    continue;
		}
		error_map = Users::CheckUIDUI (new_i_uid, ui_map);
		if (error_map != $[])
		{
		    if (!Popup::YesNo (error_map ["question"]:""))
		    {
			focus_tab (current, `uid);
			continue;
		    }
		    if(contains(["local","system"],error_map["question_id"]:""))
		    {
			new_type = error_map["question_id"]:"local";
			UsersCache::SetUserType (new_type);
		    }
		}
            } // end of uid checks

            if (defaultgroup != new_defaultgroup)
            {
		map g	= Users::GetGroupByName (new_defaultgroup, new_type);
                gid	= GetInt (g["gidnumber"]:nil, gid);
            }

            // check the homedirectory
            if (home != new_home || what == "add_user")
            {
		string error = Users::CheckHome (new_home);
		if (error != "")
		{
		    Report::Error (error);
		    focus_tab (current, `home);
		    continue;
		}
		boolean failed			= false;
		do
		{
		    error_map = Users::CheckHomeUI(new_i_uid, new_home, ui_map);
		    if (error_map != $[])
		    {
			if (!Popup::YesNo (error_map ["question"]:""))
			    failed = true;
			else
			    ui_map[ error_map["question_id"]:"" ] = 1;
		    }
		} while (error_map != $[] && !failed);

		if (failed)
		{
		    focus_tab (current, `home);
		    continue;
		}
	    }

	    error_map = Users::CheckShellUI (new_shell, ui_map);
	    if (error_map != $[])
	    {
		if (!Popup::YesNo (error_map ["question"]:""))
		{
		    focus_tab (current, `shell);
		    continue;
		}
	    }

	    // generate new map of groups (NIS groups were not shown!)
	    map<string,integer> new_groups = listmap (
		string g, (list<string>)
		    UI::QueryWidget(`id(`grouplist), `SelectedItems), ``($[g:1])
	    );
	    if (new_type == "ldap")
	    {
		foreach (string group, (list<string>)
		    UI::QueryWidget (`id(`ldapgrouplist),`SelectedItems), {
			new_groups = add (new_groups, group, 1);
		});
	    }
	    // now add NIS groups again (were not shown in dialog)
	    foreach (string group, integer val, nis_groups, {
		if (!haskey (new_groups, group))
		    new_groups = add (new_groups, group, 1);
	    });
	    // TODO remove from local g. when there is nis g. with same name
	    if (do_not_edit && !grouplist_modified && groups != new_groups)
		grouplist_modified	= true;
	    if (new_home == "/var/lib/nobody")
		create_home	= false;

	    home			= new_home;
	    shell			= new_shell;
	    uid				= new_i_uid;
	    groups			= new_groups;
	    defaultgroup		= new_defaultgroup;
	    user_type			= new_type;
	    user["homedirectory"]	= new_home;
	    user["loginshell"]		= new_shell;
	    user["gidnumber"]		= gid;
	    user["uidnumber"]		= new_i_uid;
	    user["grouplist"]		= new_groups;
	    user["groupname"]		= new_defaultgroup;
	    user["type"]		= new_type;
	    user["create_home"]		= create_home;
	    user["addit_data"]		= addit_data;
	    user["no_skeleton"]		= no_skel;
        }

	if (current == `passwordsettings && (ret == `next || tab))
	{
            string exp = (string)UI::QueryWidget (`id ("shadowexpire"), `Value);
            if (exp != "" &&
                !regexpmatch (exp,"[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]"))
            {
		// popup text: Don't reorder the letters YYYY-MM-DD!!!
		// The date must stay in this format
		Popup::Message(_("Expiration date must be in format YYYY-MM-DD"));
		focus_tab (current, "shadowexpire");
		continue;
            }

            foreach (string shadowsymbol,
		["shadowwarning", "shadowmax", "shadowmin", "shadowinactive"],
	    {
                if (user[shadowsymbol]:nil != UI::QueryWidget (
                        `id(shadowsymbol), `Value))
		{
                    user[shadowsymbol] = sformat ("%1",
                        UI::QueryWidget(`id(shadowsymbol), `Value));
		}
            });

            string new_exp_date = (string)
		UI::QueryWidget(`id("shadowexpire"),`Value);
            if (new_exp_date != exp_date)
            {
		exp_date	= new_exp_date;
                if (exp_date == "")
		{
                    user["shadowexpire"] = (user_type == "ldap") ? "" : "0";
		}
                else
                {
                    map out = (map)SCR::Execute(.target.bash_output,
                        sformat("date --date='%1 UTC' ", exp_date) + "+%s");
		    string seconds_s = deletechars(out["stdout"]:"0","\n");
                    if (seconds_s != "")
                    {
			integer days = (tointeger (seconds_s)) / (60*60*24);
                        user["shadowexpire"] = sformat("%1", days);
                    }
                }
            }
	}

	// inside plugins dialog
	if (current == `plugins)
	{
	    if (ret == `table || ret == `change)
	    {
		ret = contains (user["plugins"]:[], plugin) ? `del : `add;
	    }
	    if (ret == `del)
	    {
		map out	= UsersPlugins::Apply ("PluginRemovable",
		    $[ "what"	: "user",
		       "type"	: user_type,
		       "plugins": [ plugin ],
		    ], $[]);
		// check if plugin _could_ be deleted!
		if (haskey (out, plugin) && ! (out[plugin]:false))
		{
		    // popup message
		    Popup::Message (_("This plug-in cannot be removed."));
		    ret = `not_next;
		    continue;
		}
	    }
	    if (ret == `add || ret == `del || ret == `run)
	    {
		// functions for adding/deleting/launching plugin work on
		// Users::user_in_work, so we must update it before
		if (what == "edit_user")
		{
		    Users::EditUser (user);
		}
		else
		{
		    Users::AddUser (user);
		}
	    }
	    if (ret == `add)
	    {
		error	= Users::AddUserPlugin (plugin);
		if (error != "")
		{
		    Popup::Error (error);
		    ret = `notnext;
		    continue;
		}
		user	= Users::GetCurrentUser ();
		reinit_userdata ();
		UI::ChangeWidget (`id(`table), `Item (plugin_client,0),
		    UI::Glyph (`CheckMark));
	    }
	    if (ret == `del)
	    {
		error	= Users::RemoveUserPlugin (plugin);
		if (error != "")
		{
		    Popup::Error (error);
		    ret = `notnext;
		    continue;
		}
		user	= Users::GetCurrentUser ();
		reinit_userdata ();
		UI::ChangeWidget (`id(`table), `Item (plugin_client,0), " ");
	    }
	    if (ret == `run)
	    {
		// first, add the plugin if necessary
		if (!contains (user["plugins"]:[], plugin))
		{
		    error	= Users::AddUserPlugin (plugin);
		    if (error != "")
		    {
			Popup::Error (error);
			ret = `notnext;
			continue;
		    }
		    user	= Users::GetCurrentUser ();
		    reinit_userdata ();
		    UI::ChangeWidget (`id(`table), `Item (plugin_client,0),
			UI::Glyph (`CheckMark));
		}
		any plugin_ret = WFM::CallFunction (
		    plugin_client, ["Dialog", $[ "what"	: "user" ], user ]);
		if (plugin_ret == `next)
		{
		    // update the map of changed user
		    user	= Users::GetCurrentUser ();
		    reinit_userdata ();
		}
	    }
	}

	// ------------------- now handle switching between the tabs
	if (ret == `edit)
	{
	    Wizard::SetHelpText (
		EditUserDialogHelp (Stage::cont (), user_type, what));
	    UI::ReplaceWidget(`tabContents, get_edit_term ());

	    // update the contets of User Data Dialog
	    if (do_not_edit)
	    {
		UI::ChangeWidget (`id(`cn), `Enabled, false);
		UI::ChangeWidget (`id(`username), `Enabled, false);
		UI::ChangeWidget (`id(`pw1), `Enabled, false);
		UI::ChangeWidget (`id(`pw2), `Enabled, false);
	    }
	    if (what == "add_user")
	    {
		if (user_type == "system")
		    UI::ChangeWidget(`id(`propose), `Enabled, false);
		if (user_type == "ldap")
		    UI::SetFocus(`id(`givenname));
		else
		    UI::SetFocus(`id(`cn));
	    }
	    if (password != "")
	    {
		UI::ChangeWidget(`id(`pw1), `Value, default_pw);
		UI::ChangeWidget(`id(`pw2), `Value, default_pw);
	    }
	    if (complex_layout && !Autologin::available)
	    {
		UI::ChangeWidget(`id(`autologin), `Enabled, false);
		UI::ChangeWidget(`id(`autologin), `Value, false);
	    }

	    current	= ret;
	}
	if (ret == `details)
	{
	    UI::ReplaceWidget(`tabContents, get_details_term ());
	    Wizard::SetHelpText (EditUserDetailsDialogHelp (user_type, what));

	    if (do_not_edit)
	    {
		UI::ChangeWidget (`id (`uid), `Enabled, false);
		UI::ChangeWidget (`id (`home), `Enabled, false);
		UI::ChangeWidget (`id (`shell), `Enabled, false);
		UI::ChangeWidget (`id (`defaultgroup), `Enabled, false);
		UI::ChangeWidget (`id (`browse), `Enabled, false);
	    }
	    if (user_type == "ldap" && !Ldap::file_server)
	    {
		UI::ChangeWidget (`id (`browse), `Enabled, false);
	    }
	    UI::ChangeWidget(`id(`shell), `Value, shell);

	    current	= ret;
	}
	if (ret == `passwordsettings)
	{
	    UI::ReplaceWidget(`tabContents, get_password_term ());
	    Wizard::SetHelpText (EditUserPasswordDialogHelp());
	    if (user_type == "ldap")
	    {
	    list ocs = maplist (
		string oc, user["objectclass"]:[], ``(tolower (oc)));
	    if (!contains (ocs, "shadowaccount"))
	    {
		ocs = add (ocs, "shadowaccount");
		user["objectclass"]	= ocs;
		foreach (string k, any v, Users::GetDefaultShadow ("ldap"), {
		    if (!haskey (user, k))
			user[k]	= v;
		});
	    }
	    }
	    current	= ret;
	}
	if (ret == `plugins)
	{
	    UI::ReplaceWidget(`tabContents, get_plugins_term ());
	    Wizard::SetHelpText (PluginDialogHelp ());
	    UI::ChangeWidget (`id(`table),`CurrentItem, plugin_client);
	    current	= ret;
	}

	if ((ret == `next || ret == `additional) &&
	    // for do_not_edit, there may be a change in groups (Details dialog)
	    (!do_not_edit || grouplist_modified))
	{
	    // --------------------------------- final check
	    error = Users::CheckUser (user);
	    if (error != "")
	    {
		Report::Error (error);
		ret = `notnext;
		continue;
	    }

	    // --------------------------------- save the settings
	    if (haskey (user, "check_error"))
	    {
		user = remove (user, "check_error");
	    }
	    if ( what == "edit_user" )
            {
		error_msg = Users::EditUser (user);
            }
	    else
	    {
		error_msg = Users::AddUser (user);
	    }
	    if (error_msg != "")
	    {
		Report::Error (error_msg);
		ret = `notnext;
		continue;
	    }
	}

	if (contains ([`next,`abort,`back,`cancel,`additional,`nosave], ret))
	    break;
    }

    if (ret == `additional || ret == `nosave)
    {
        // during installation, store the data of first user
        // (to show it when clicking `back from Summary dialog)
	Users::SaveCurrentUser ();
	Users::SetStartDialog ("users");
    }
    return ret;
}

/**
 * Dialog for adding/editing group
 * @param what "add_group" or "edit_group"
 * @return symbol for wizard sequencer
 */
define symbol EditGroupDialog (string what) {

    // create a local copy of current group
    map<string,any> group	= Users::GetCurrentGroup ();
    string  groupname		= group["cn"]:"";
    string  password		= group["userpassword"]:"x";
    integer gid			= GetInt (group["gidnumber"]:nil, -1);
    // these are the users with this group as a default:
    map<string,any> more_users	= group["more_users"]:$[];
    // these are users from /etc/group:
    map<string,any> userlist	= group["userlist"]:$[];
    string group_type		= group["type"]:"";
    string new_type		= group_type;
    string dn			= group["dn"]:"";
    string new_dn		= dn;
    list additional_users	= [];
    string member_attribute	= UsersLDAP::GetMemberAttribute ();

    if (group_type == "ldap")
    {
	userlist		= group[member_attribute]:$[];
    }

    boolean more = size (more_users) > 0;

    map dialog_labels = $[
        "add_group": $[
            // dialog caption:
            "local":	_("Add a New Local Group"),
            // dialog caption:
            "system":	_("Add a New System Group"),
            // dialog caption:
            "ldap":     _("Add a New LDAP Group")
        ],
        "edit_group": $[
            // dialog caption:
            "local":     _("Edit an Existing Local Group"),
            // dialog caption:
            "system":	_("Edit an Existing System Group"),
            // dialog caption:
            "ldap":     _("Edit an Existing LDAP Group")
        ]
    ];

    string plugin_client	= "";
    string plugin		= "";

    /**
     * initialize local variables with current state of group
     */
    define void reinit_groupdata () {

	groupname	= group["cn"]:groupname;
	password	= group["userpassword"]:password;
	gid		= GetInt (group["gidnumber"]:nil, gid);
	more_users	= group["more_users"]:more_users;
	userlist	= group["userlist"]:userlist;
	group_type	= group["type"]:group_type;
	dn		= group["dn"]:dn;
	if (group_type == "ldap")
	{
	    userlist	= group[member_attribute]:$[];
	}
    }

    /**
     * generate contents for Group Data Dialog
     */
    define term get_edit_term () {

	integer i = 0;
	list more_users_items	= [];
	foreach (string u, any val, more_users, {
	    if (i <42)
		more_users_items = add (more_users_items,`item (`id(u),u,true));
	    if (i == 42)
		more_users_items = add (more_users_items,
		    `item (`id("-"), "...", false ));
	    i = i + 1;
	});

	return `HBox (
        `Left(`VBox(
            `VSpacing(1),
            // textentry label
            `Top (`TextEntry (`id (`groupname), _("Group &Name"), groupname)),
            // textentry label
            `Top (`TextEntry (`id (`gid), _("Group &ID (gid)"),
                sformat("%1", gid) )),
            `VSpacing(1),
            `Bottom (`Password(`id(`pw1), Label::Password (), "")),
            `Bottom (`Password(`id(`pw2), Label::ConfirmPassword (),  "")),
            `VSpacing(1)
        )),
        `HSpacing(2),
        `VBox(
            `VSpacing(1),
	    `ReplacePoint (`id(`rpuserlist),
		// selection box label
		`MultiSelectionBox (`id(`userlist), _("Group &Members"), [])
	    ),
            more ? `VSpacing(1) : `VSpacing (0),
            more ? `MultiSelectionBox (`id(`more_users), "",
                more_users_items) : `VSpacing (0),
            `VSpacing(1)
        ));
    }

    /**
     * generate contents for Plugins Dialog
     */
    define term get_plugins_term () {

	// UsersPlugins will filter out plugins we cannot use for given type
	map plugin_clients	= UsersPlugins::Apply ("GUIClient",
	    $[ "what" : "group", "type" : group_type ], $[]
	);
	// remove empty clients
	plugin_clients = filter (string plugin, string client,
	    (map<string,string>) plugin_clients, ``(client != ""));

	map client2plugin	= $[];
	list<string> clients	= maplist (string plugin, string client,
	    (map<string,string>) plugin_clients, {

		client2plugin [client]	= plugin;
		return client;
	});

	plugin_client	= clients[0]:"";
	plugin		= client2plugin[plugin_client]:plugin_client;

	list items = [];
	foreach (string cl, clients, {
	    any summary = WFM::CallFunction (cl, ["Summary", $["what" : what]]);
	    string pl	= client2plugin[cl]:cl;
	    if (is (summary, string))
		items = add (items, `item(`id(cl),
		    contains (group["plugins"]:[], pl) ?
			UI::Glyph (`CheckMark) : " ",
		    summary)
		);
	});
	return `HBox (`HSpacing (0.5), `VBox (
	    `Table (`id(`table), `opt(`notify), `header (" ",
		// table header
	        _("Plug-In Description")), items
	    ),
	    `HBox (
		`PushButton (`id(`change), `opt(`key_F3),
		    // pushbutton label
		    _("Add &or Remove Plug-In")),
		// pushbutton label
		`Right (`PushButton(`id(`run), `opt(`key_F6), _("&Launch")))
	    ),
	    `VSpacing (0.5)
	), `HSpacing (0.5));
    }

    list<term> tabs		= [];
    term dialog_contents	= `Empty ();
    boolean use_tabs		= group_type == "ldap";

    if (use_tabs)
    {
	tabs	= [
	    // tab label
	    `item(`id(`edit), _("Group &Data"), true),
	    // tab label
	    `item(`id(`plugins), _("Plu&g-Ins") )
	];

	dialog_contents = `VBox (
	    `DumbTab (`id(`tabs), tabs,
		`ReplacePoint(`id(`tabContents ), get_edit_term ()))
	);
	if (!UI::HasSpecialWidget (`DumbTab))
	{
	    term tabbar	= `HBox ();
	    foreach (term it, tabs, {
		string label = it[1]:"";
		tabbar = add (tabbar,`PushButton (it[0]:`id(label), label));
	    });
	    dialog_contents = `VBox (`Left(tabbar),
		`Frame ("", `ReplacePoint(`id(`tabContents), get_edit_term ()))
	    );
	}
    }
    else
    {
	dialog_contents		= get_edit_term ();
    }

    Wizard::SetContentsButtons (
        dialog_labels [ what, group_type ]:"",
        dialog_contents,
        EditGroupDialogHelp (more),
        Label::CancelButton(), Label::AcceptButton());
    Wizard::HideAbortButton ();

    symbol ret		= `edit;
    symbol current	= nil;
    list tabids		= [`edit, `plugins ];

    repeat
    {
	// map returned from Check*UI functions
	map error_map			= $[];
	// map with id's of confirmed questions
	map<string,integer> ui_map	= $[];
	// error message
	string error			= "";

	if (current != nil)
	{
	    ret = (symbol) UI::UserInput ();
	}

	if ((ret == `abort || ret == `cancel) && ReallyAbort () != `abort)
	{
	    ret = `notnext;
	    continue;
	}
	if (contains ([`abort,`back,`cancel], ret))
	    break;

	boolean tab	= contains (tabids, ret);
	if (tab && ret == current)
	{
	    continue;
	}

	// 1. click inside Group Data dialog or moving outside of it
        if (current == `edit && (ret == `next || tab))
        {
            string pw1          = (string) UI::QueryWidget(`id(`pw1), `Value);
            string pw2          = (string)UI::QueryWidget(`id(`pw2),`Value);
            string new_gid	= (string)UI::QueryWidget(`id(`gid), `Value);
            integer new_i_gid	= tointeger(new_gid);
            string new_groupname	= (string)
		UI::QueryWidget(`id(`groupname), `Value);

	    // --------------------------------- groupname checks
	    string error = Users::CheckGroupname (new_groupname);
	    if (error != "")
	    {
		Report::Error (error);
                UI::SetFocus (`id(`groupname));
		ret = `notnext;
		continue;
	    }
	    // --------------------------------- password checks
            if ( pw1 != pw2 )
            {
                // The two group password information do not match
                // error popup
                Report::Error(_("The first and the second version
of the password are different.
Please try again.")) ;

		ret	= `notnext;
		UI::SetFocus(`id(`pw1));
                continue;
            }
            if ( pw1 != "" && pw1 != default_pw )
            {
		error = Users::CheckPassword (pw1);
		if (error != "")
		{
		    Report::Error (error);
		    ret	= `notnext;
		    UI::SetFocus(`id(`pw1));
		    continue;
		}
		boolean failed			= false;
		do
		{
		    error_map= Users::CheckPasswordUI(new_groupname,pw1,ui_map);
		    if (error_map != $[])
		    {
			if (!Popup::YesNo (error_map ["question"]:""))
			{
			    failed = true;
			}
			else
			    ui_map[ error_map["question_id"]:"" ] = 1;
		    }
		} while (error_map != $[] && !failed);
		if (failed)
		{
		    ret	= `notnext;
		    UI::SetFocus(`id(`pw1));
		    continue;
		}
		password = pw1;
		if (group["encrypted"]:false)
		    group["encrypted"]	= false;
            }

	    // --------------------------------- gid checks
            if ( new_i_gid != gid)
	    {
		error = Users::CheckGID (new_i_gid);
		if (error != "")
		{
		    Report::Error (error);
		    UI::SetFocus (`id(`gid));
		    ret = `notnext;
		    continue;
		}
		error_map = Users::CheckGIDUI (new_i_gid, ui_map);
		if (error_map != $[])
		{
		    if (!Popup::YesNo (error_map ["question"]:""))
		    {
			ret = `notnext;
			UI::SetFocus(`id(`gid));
			continue;
		    }
		    if(contains(["local","system"],error_map["question_id"]:""))
		    {
			new_type = error_map["question_id"]:"local";
			UsersCache::SetGroupType (new_type);
		    }
		}
	    }

	    // --------------------------------- update userlist
	    map new_userlist = listmap (string user, (list<string>)
		UI::QueryWidget (`id(`userlist), `SelectedItems), ``($[user: 1])
	    );

	    // --------------------------------- now everything should be OK
            group["cn"]			= new_groupname;
            group["userpassword"]	= password;
            group["more_users"]		= more_users;
            group["gidnumber"]		= new_i_gid;
            group["type"]		= new_type;
	    if (group_type == "ldap")
	    {
		group[member_attribute]	= new_userlist;
	    }
	    else
	    {
		group["userlist"]	= new_userlist;
	    }
	    reinit_groupdata ();
	}

	// inside plugins dialog
	if (current == `plugins)
	{
	    if (ret == `table || ret == `change)
	    {
		ret = contains (group["plugins"]:[], plugin) ? `del : `add;
	    }
	    if (ret == `del)
	    {
		map out	= UsersPlugins::Apply ("PluginRemovable",
		    $[ "what"	: "group",
		       "type"	: group_type,
		       "plugins": [ plugin ],
		    ], $[]);
		// check if plugin _could_ be deleted!
		if (haskey (out, plugin) && ! (out[plugin]:false))
		{
		    // popup message
		    Popup::Message (_("This plug-in cannot be removed."));
		    ret = `not_next;
		    continue;
		}
	    }
	    if (ret == `add || ret == `del || ret == `run)
	    {
		// functions for adding/deleting/launching plugin work on
		// Users::group_in_work, so we must update it before
		if (what == "edit_group")
		{
		    Users::EditGroup (group);
		}
		else
		{
		    Users::AddGroup (group);
		}
	    }
	    if (ret == `add)
	    {
		error	= Users::AddGroupPlugin (plugin);
		if (error != "")
		{
		    Popup::Error (error);
		    ret = `notnext;
		    continue;
		}
		group	= Users::GetCurrentGroup ();
		reinit_groupdata ();
		UI::ChangeWidget (`id(`table), `Item (plugin_client,0),
		    UI::Glyph (`CheckMark));
	    }
	    if (ret == `del)
	    {
		error	= Users::RemoveGroupPlugin (plugin);
		if (error != "")
		{
		    Popup::Error (error);
		    ret = `notnext;
		    continue;
		}
		group	= Users::GetCurrentGroup ();
		reinit_groupdata ();
		UI::ChangeWidget (`id(`table), `Item (plugin_client,0), " ");
	    }
	    if (ret == `run)
	    {
		// first, add the plugin if necessary
		if (!contains (group["plugins"]:[], plugin))
		{
		    error	= Users::AddGroupPlugin (plugin);
		    if (error != "")
		    {
			Popup::Error (error);
			ret = `notnext;
			continue;
		    }
		    group	= Users::GetCurrentGroup ();
		    reinit_groupdata ();
		    UI::ChangeWidget (`id(`table), `Item (plugin_client,0),
			UI::Glyph (`CheckMark));
		}
		any plugin_ret = WFM::CallFunction (
		    plugin_client, ["Dialog", $[ "what"	: "group" ], group ]);
		if (plugin_ret == `next)
		{
		    // update the map of changed group
		    group	= Users::GetCurrentGroup ();
		    reinit_groupdata ();
		}
	    }
	}

	// initialize Edit Group tab
	if (ret == `edit)
	{
	    if (use_tabs)
	    {
		Wizard::SetHelpText (EditGroupDialogHelp (more));
		UI::ReplaceWidget (`tabContents, get_edit_term ());
	    }

	    if (what == "add_group") UI::SetFocus(`id(`groupname));
	    if (what == "edit_group")
	    {
		if (password != "" && !contains (["x","*","!"], password))
		{
		    UI::ChangeWidget(`id(`pw1), `Value, default_pw);
		    UI::ChangeWidget(`id(`pw2), `Value, default_pw);
		}
	    }

	    if (more)
	    {
		// set of users having this group as default - cannot be edited!
		UI::ChangeWidget (`id(`more_users), `Enabled, false);
	    }
	    additional_users		= UsersCache::BuildAdditional (group);

	    // add items later (when there is a huge amount of them, it takes
	    // long time to display, so display at least the rest of the dialog)
	    if (size (additional_users) > 0)
	    {
		UI::ReplaceWidget (`id(`rpuserlist),
		    `MultiSelectionBox( `id(`userlist), _("Group &Members"),
			additional_users)
		);
	    }
	    current	= ret;
	}

	if (ret == `plugins)
	{
	    UI::ReplaceWidget (`tabContents, get_plugins_term ());
	    Wizard::SetHelpText (PluginDialogHelp ());
	    UI::ChangeWidget (`id(`table),`CurrentItem, plugin_client);
	    current	= ret;
	}

	// save the changes
	if (ret == `next)
	{
	    error = Users::CheckGroup (group);
	    if (error != "")
	    {
		Report::Error (error);
		ret = `notnext;
		continue;
	    }
	    if (what == "edit_group")
		error = Users::EditGroup (group);
	    else
		error = Users::AddGroup (group);
	    if (error != "")
	    {
		Report::Error (error);
		ret = `notnext;
		continue;
	    }
        }
    } until (contains ([`next, `abort, `back, `cancel], ret));
    return ret;
}

/**
 * The dialogs for setting defaults from /etc/defaults/useradd
 * @return symbol for wizard sequencer
 */
define symbol DefaultsDialog() {

    string home		= Users::GetDefaultHome ("local");
    string shell	= Users::GetDefaultShell ("local");
    integer gid		= Users::GetDefaultGID ("local");
    string defaultgroup	= Users::GetDefaultGroupname ("local");

    map defaults	= Users::GetLoginDefaults ();
    string skel		= defaults["skel"]:"/etc/skel";
    string expire	= defaults["expire"]:"";
    string inactive	= defaults["inactive"]:"";
    integer inact	= tointeger (inactive);

    map<string,integer> groups	= Users::GetDefaultGrouplist ("local");

    string grouplist	= mergestring (
	(list<string>) maplist (string g,integer i, groups, ``(g)), ",");

    string exp_date		= "";
    list available_groups	= [];
    list available_shells	= Users::AllShells ();

    map<string,map<string,integer> > all_groupnames	=
	UsersCache::GetAllGroupnames ();

    foreach (string grouptype, map<string,integer> groupmap, all_groupnames, {
	// only local sets
	if (!contains (["local", "system"], grouptype))
	    return;
        foreach (string group, integer val, groupmap, {
            if ( group == defaultgroup )
                available_groups = add (available_groups,
                    `item( `id(group), group, true));
            else
                available_groups = add (available_groups,
                    `item( `id(group), group));
        });
    });

    if (expire != "0" && expire != "")
    {
	map out	= (map) SCR::Execute (.target.bash_output, sformat (
		"date --date='1970-01-01 00:00:01 %1 days' ", expire) +
		"+\"%Y-%m-%d\"");
        exp_date = deletechars (out["stdout"]:"", "\n");
    }

    term contents = `HBox(`HSpacing(1.5),
        `VBox(
            `VSpacing(0.2),
		// Frame label
	        `Frame(_("Set the Default Values for Adding New User"),`HBox(
		`HSpacing(2), `VBox(
                `ComboBox(`id(`defaultgroup), `opt(`hstretch),
                    // combobox label
                    _("D&efault Group"), available_groups ),
                // text entry
                `TextEntry( `id(`grouplist), _("Se&condary Groups"), grouplist),
                // text entry
                `ComboBox(`id(`shell), `opt(`hstretch, `editable),
                    // combobox label
                    _("Default &Login Shell"), available_shells),
		`HBox(
		    // text entry
		    `TextEntry(`id(`home), _("Path Prefix for &Home Directory"),
			home),
		    `VBox(
			`Label (""),
			`PushButton(`id(`brow_h),`opt(`key_F6), _("Bro&wse..."))
		    )
		),
		`HBox(
		    // text entry
		    `TextEntry(`id(`skel), _("&Skeleton for Home Directory"),
			skel),
		    `VBox(
			`Label (""),
			`PushButton(`id(`brow_s),`opt(`key_F7), _("Bro&wse..."))
		    )
		),
                // text entry
                `TextEntry( `id(`exp), _("Default E&xpiration Date"), exp_date),
                // intfield
                `IntField (`id (`inact), _("Days &after Password Expiration Login Is Usable"), -1, 99999, inact),
		`VSpacing (0.2)
                ),
              `HSpacing(2))),
            `VSpacing(0.2)),
        `HSpacing(1.5));

    Wizard::SetContentsButtons(
            // dialog label
            _("New User Defaults"),
            contents,
            DefaultsDialogHelp(),
            Label::CancelButton(), Label::AcceptButton());

    Wizard::HideAbortButton ();

    UI::ChangeWidget(`id(`shell), `Value, shell);

    boolean modified = false;

    string new_home  = home;
    string new_shell = shell;
    string new_skel = skel;
    string new_defgroup = defaultgroup;
    string new_exp_date = exp_date;
    integer new_inact = inact;
    string new_expire = expire;
    string new_grouplist = grouplist;

    symbol ret = `next;
    while (true)
    {
        modified = false;
        ret = (symbol) UI::UserInput();
	if (ret == `cancel)
	    ret	= `abort;
        if (ret == `back || ret == `abort) break;
	if ( ret == `brow_h)
	{
	    string dir = (string) UI::QueryWidget(`id(`home),  `Value);
	    if (SCR::Read(.target.size, dir ) == -1)
		dir = home;
	    dir = UI::AskForExistingDirectory (dir, "");
	    if (dir != nil)
	    {
		UI::ChangeWidget (`id(`home), `Value, dir);
	    }
	}
	if ( ret == `brow_s)
	{
	    string dir = (string) UI::QueryWidget(`id(`skel),  `Value);
	    if (SCR::Read(.target.size, dir ) == -1)
		dir = skel;
	    dir = UI::AskForExistingDirectory (dir, "");
	    if (dir != nil)
	    {
		UI::ChangeWidget (`id(`skel), `Value, dir);
	    }
	}
        if (ret == `next)
        {
            new_home  = (string)UI::QueryWidget(`id(`home),  `Value);
            new_shell = (string)UI::QueryWidget(`id(`shell), `Value);
            new_skel = (string)UI::QueryWidget(`id(`skel), `Value);
            new_inact = (integer)UI::QueryWidget(`id(`inact), `Value);
            new_defgroup = (string)UI::QueryWidget(`id(`defaultgroup),`Value);
            new_exp_date = (string)UI::QueryWidget(`id(`exp),`Value);
	    new_grouplist = (string)UI::QueryWidget(`id(`grouplist),`Value);
	    // check new grouplist...
	    if (new_grouplist != grouplist)
	    {
		list<string> l_grouplist = [];
		list<string> dont_exist = [];
		foreach (string g, splitstring (new_grouplist, ","), {
		    // check for group existence
		    if (all_groupnames["local", g]:0 == 0 &&
			all_groupnames["system", g]:0 == 0)
			dont_exist = (list<string>) union (dont_exist, [g]);
		    // filter out the duplicates
		    else l_grouplist = (list<string>) union (l_grouplist, [g]);
		});
		if (dont_exist != [])
		{
		    // error message
		    Report::Error(sformat (_("These groups do not exist in your system:
%1
Try again.
"), mergestring (dont_exist, ",")));

		    UI::SetFocus (`id(`grouplist));
		    ret = `notnext;
		    continue;
		}
		new_grouplist = mergestring (l_grouplist, ",");
		UI::ChangeWidget (`id(`grouplist), `Value, new_grouplist);
		modified = true;
	    }
            // check the home existence
            if (home != new_home)
            {
                if (SCR::Read(.target.dir, new_home ) == nil)
                {
		    if (SCR::Read(.target.size, new_home ) != -1)
		    {
			// error message
			Report::Error(_("The entered path prefix for home is not a directory.
Try again.
"));
			UI::SetFocus(`id(`home));
			ret = `notnext;
			continue;
		    }
		    else
		    {
			// yes/no popup
			if (Popup::YesNo (_("The selected directory does not exist.
Create it now?
")))
			{
			    if (!(boolean)SCR::Execute(.target.mkdir, new_home))
			    {
				Report::Error (
				    Message::UnableToCreateDirectory(new_home));
				UI::SetFocus (`id(`home));
				ret = `notnext;
				continue;
			    }
			}
			else
			{
			    UI::SetFocus (`id(`home));
			    ret = `notnext;
			    continue;
			}
		    }
                }
                modified = true;
            }
            // check the shell existence
            if (shell != new_shell)
            {
                if (! contains (available_shells, new_shell))
                {
                    // Yes-No popup
                    if (! Popup::YesNo (_("If you select a nonexistent shell, the user
may be unable to log in. Continue?
")))
		    {
			ret = `notnext;
                        continue;
		    }
                }
                modified = true;
            }
            // check the skeleton existence
            if (skel != new_skel)
            {
                if (SCR::Read(.target.dir, new_skel ) == nil)
                {
                    Report::Error(_("The entered home directory skeleton is not a directory.
Try again.
"));
                    UI::SetFocus(`id(`skel));
		    ret = `notnext;
                    continue;
                }
                modified = true;
            }
            // check the expire date
            if (exp_date != new_exp_date)
            {
                if (new_exp_date != "" && !regexpmatch (new_exp_date,
                    "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]"))
                {
                    // Trannslators: Don't reorder the letters YYYY-MM-DD!!!
                    // The date must stay in this format
                    Popup::Message(_("Expiration date must be in format YYYY-MM-DD"));
                    UI::SetFocus(`id(`exp));
		    ret = `notnext;
                    continue;
                }
                if (new_exp_date == "")
                    new_expire = "";
                else
                {
                    map out = (map) SCR::Execute(.target.bash_output,
                        sformat("date --date='%1 UTC' ", new_exp_date) + "+%s");
                    string seconds_s = deletechars (out["stdout"]:"0", "\n");
                    if (seconds_s != "")
                    {
                        integer days = (tointeger(seconds_s))/ (60*60*24);
                        new_expire = sformat("%1", days);
                    }
                }
                modified = true;
            }
            if (defaultgroup != new_defgroup || inact != new_inact)
                modified = true;
            break;
        }
    }
    if (modified)
    {
	map g	= Users::GetGroupByName (new_defgroup, "local");
        map<string,string> new_defaults = $[
            "home": new_home,
            "shell": new_shell,
            "skel": new_skel,
            "group": sformat ("%1", g["gidnumber"]:gid),
            "inactive": sformat("%1", new_inact),
            "expire": new_expire,
	    "groups": new_grouplist
        ];
        Users::SetLoginDefaults (new_defaults, new_defgroup);
    }

    return ret;
}


/**
 * Choosing the user authentization method
 * @return symbol for sequencer
 */
define symbol AuthentizationDialog() {

    list<string> configurable = [ "nis", "ldap", "kerberos", "samba" ];

    list<string> available = [];
    list<string> installed = [];
    list mb = [];

    map to_string = $[
        // just the name (do not translate)
        "nis"   : _("&NIS"),
        // just the name (do not translate)
        "nisplus"   : _("N&IS+"),
        // just the name (do not translate)
        "ldap"  : _("&LDAP"),
        // just the name (do not translate)
        "kerberos"  : _("&Kerberos"),
        // just the name (do not translate)
        "samba"  : _("&Samba"),
    ];

    // name of module to call
    map call_module = $[
	"samba": "samba-client",
    ];

    /**
     * helper function to get information about authentication from
     * appropriate module
     * @param client
     * @return
     */
    define string get_module_data (string client) {

	string ret = "";
	boolean progress_orig = Progress::set (false);
	if (!contains (installed, client))
	{
	    ret = Summary::NotConfigured();
	}
	else if (client == "ldap")
	{
	    Ldap::Read();
	    ret = Ldap::ShortSummary();
	}
	else if (client == "nis")
	{
	    WFM::CallFunction ("nis_auto", ["Read"]);
	    any a = WFM::CallFunction ("nis_auto", ["ShortSummary"]);
	    if (is (a,string))
		ret = (string) a;
	}
	else if (client == "kerberos")
	{
	    WFM::CallFunction ("kerberos-client_auto", ["Read"]);
	    any a = WFM::CallFunction ("kerberos-client_auto",["ShortSummary"]);
	    if (is (a,string))
		ret = (string) a;
	}
	else if (client == "samba")
	{
	    WFM::CallFunction ("samba-client_auto", ["Read"]);
	    any a = WFM::CallFunction ("samba-client_auto", ["ShortSummary"]);
	    if (is (a,string))
		ret = (string) a;
	}
	Progress::set (progress_orig);
	return ret;
    }

    /**
     * Reloads the configuration and updates the auth-widget contents
     */
    define void reload_config() {

        string summary = "";
	foreach (string client, available, {
	    summary = Summary::AddHeader (summary, sformat (
		"<font color=\"#8BC460\"><a href=\"%1\">%2</a></font>",
		client,
		deletechars (to_string [client]:"", "&")));
	    summary = Summary::AddLine (summary, get_module_data (client));
	});
        UI::ChangeWidget(`id(`summary), `Value, summary);
    }


    foreach (string client, configurable, {
	string package = sformat ("yast2-%1-client", client);
        if (Package::Available (package))
	{
	    available = add (available, client);
	    mb = add (mb, `item(`id(client), to_string[client]:""));
	}
        if (Package::Installed (package))
	    installed = add (installed, client);
    });

    term contents = `HBox(
        `HSpacing(4),
        `VBox(
            `VSpacing(0.5),
            `RichText (`id(`summary), ""),
            `VSpacing( 0.5 ),
            // button label
	    `MenuButton(`opt(`key_F4), _("&Configure..."), mb),
            `VSpacing (0.5)
        ),
        `HSpacing(4)
    );

    // dialog title
    string title = _("Authentication and User Data Sources");

    Wizard::SetContentsButtons (title, contents, AuthentizationDialogHelp(),
        Label::CancelButton(), Label::AcceptButton());
    Wizard::HideAbortButton ();

    reload_config();

    any button		= nil;
    boolean changed	= false;
    list param		= [];

    if (Stage::cont ())
    {
	param		= [ "from_users" ];
    }
    do
    {
        button = UI::UserInput();
        if (is (button,string) && contains (configurable, (string) button))
        {
	    if (!contains (installed, (string) button))
	    {
		string package = sformat ("yast2-%1-client", button);
		if (Package::InstallAllMsg ([package],
		    // popup label (%1 is package to install)
		    sformat (_("Package %1 is not installed.
Install it now?
"), package)))
		{
		    installed = add (installed, (string) button);
		}
		else
		{
		    button = `notnext;
		    continue;
		}
	    }
            if (WFM::CallFunction (
		(string)call_module[button]:button, param) == `next)
	    {
		changed = true;
		reload_config ();
		Wizard::DisableBackButton();
	    }
        }
    } while (! (is (button,symbol) &&
	contains ([`next, `abort, `back, `cancel], (symbol) button) ));

    if (button == `cancel)
	button	= `abort;

    if (changed)
    {
	boolean was_nis_available = Users::NISAvailable ();
	Users::ReadSourcesSettings();
	// enabling NIS could add + lines:
	if (was_nis_available != Users::NISAvailable ())
	{
	    if (Users::NISAvailable ()) // nis enabled now
	    {
		Users::AddPlusPasswd ("+::::::");
		Users::AddPlusGroup ("+:::");
		Users::AddPlusShadow ("+");
	    }
	}
    }
    return (symbol) button;
}


/**
 * Just giving paramaters for commiting user
 * @return symbol for wizard sequencer
 */
define symbol UserSave() {

    Users::CommitUser ();
    // adding only one user during install
    if (Stage::cont () && Users::StartDialog ("user_add") )
        return `save;
    else
	return `next;
}

/**
 * Check the group parameters and commit it if all is OK
 * @return symbol for wizard sequencer
 */
define symbol GroupSave() {

    map <string,any> group = Users::GetCurrentGroup ();
    // do not check group which should be deleted
    if (group["what"]:"" != "delete_group")
    {
	string error = Users::CheckGroup (group);
	if (error != "")
	{
	    Report::Error (error);
	    return `back;
	}
    }
    Users::CommitGroup ();
    return `next;
}

/* EOF */
}
