ACC SHELL

Path : /usr/share/YaST2/include/users/
File Upload :
Current File : //usr/share/YaST2/include/users/dialogs.ycp

/**
 * 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 61042 2010-02-26 13:48:03Z jsuchome $
 */

{

textdomain "users";

import "Autologin";
import "GetInstArgs";
import "FileUtils";
import "Label";
import "Ldap";
import "LdapPopup";
import "Message";
import "Package";
import "Popup";
import "ProductFeatures";
import "Report";
import "Stage";
import "String";
import "Users";
import "UsersCache";
import "UsersLDAP";
import "UsersPlugins";
import "UsersRoutines";
import "UsersSimple";
import "Wizard";

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

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

/**
 * 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 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;
}

/**
 * Ask user for current password, see bugs 242531, 244718
 * @return string or nil when dialog was canceled
 */
string AskForOldPassword () {

    UI::OpenDialog (`opt(`decorated), `HBox (`HSpacing (0.5),
	`VBox (
	    `VSpacing (0.5),
	    // password entry label
	    `Password (`id (`pw1), `opt (`hstretch), _("To access the data required to modify
the encryption settings for this user,
enter the user's current password.")),
	    `Password (`id(`pw2),`opt (`hstretch), Label::ConfirmPassword(),""),
	    `HBox(
		`PushButton (`id(`ok),`opt(`key_F10), Label::OKButton()),
		`PushButton (`id(`cancel),`opt(`key_F9), Label::CancelButton())
	    ),
	    `VSpacing (0.5)
	), `HSpacing (0.5))
    );
    any ret	= `cancel;
    repeat
    {
	ret	= UI::UserInput ();
	if (ret == `ok)
	{
	    if (UI::QueryWidget (`id(`pw1), `Value) != UI::QueryWidget (`id(`pw2), `Value))
	    {
                Report::Error(_("The passwords do not match.
Try again."));
		ret	= `notnext;
		continue;
	    }
	}
    } until (ret == `ok || ret == `cancel);

    string pw	= (string) UI::QueryWidget (`id(`pw1), `Value);
    UI::CloseDialog();
    return (ret == `ok) ? pw : nil;
}

/**
 * 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") && installation () && Users::UseNextTime ())
    {
	Users::RestoreCurrentUser ();
	Users::SetUseNextTime (false);
    }

    map display_info        = UI::GetDisplayInfo ();
    boolean text_mode	= display_info["TextMode"]: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 action = user["modified"]:"";
    if (action == "")
	action = (what == "add_user") ? "added" : "edited";

    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 default_mode	=
	sformat ("%1", 777 - tointeger (String::CutZeros (Users::GetUmask ())));
    string mode		= user["home_mode"]:default_mode;
    integer default_crypted_size	= 100;
    integer crypted_home_size	= GetInt (user["crypted_home_size"]:nil, 0);
    integer org_crypted_home_size	= GetInt (user["org_user","crypted_home_size"]:nil, 0);
    string password	= (string)user["userPassword"]:nil;
    string org_username = user["org_uid"]:username;
    integer uid		= GetInt (user["uidNumber"]:nil, nil);
    integer gid		=
	GetInt (user["gidNumber"]:nil, Users::GetDefaultGID (user_type));
    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"]:"";

    // this user gets root's mail
    boolean root_mail	= haskey (Users::GetRootAliases (), username);
    boolean root_mail_checked	= root_mail;

    // if user's password should be set to root
    boolean root_pw		= false;

    // 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 chown_home		= user["chown_home"]:true;
    boolean no_skel		= user["no_skeleton"]:false;
    boolean do_not_edit		= (user_type == "nis");
    boolean crypted_home_enabled=
	UsersRoutines::CryptedHomesEnabled () &&
	((user_type == "ldap" && Ldap::file_server) ||
	(user_type == "local" || user_type == "system"));

    boolean complex_layout = installation () && 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		= "";
    map client2plugin		= $[];
    // names of plugin GUI clients
    list<string> clients	= [];

    /**
     * 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;
	crypted_home_size	= GetInt (user["crypted_home_size"]:nil, 0);
	mode		= user["home_mode"]:default_mode;
	password	= user["userPassword"]:password;
	org_username	= user["org_uid"]:org_username;
	uid		= GetInt (user["uidNumber"]:nil, uid);
	gid		= GetInt (user["gidNumber"]:nil, gid);
	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);
	}

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

    /**
     * helper function: show a popup if existing home directory should be used
     * and its ownership should be changed
     */
    map ask_chown_home (string dir, boolean chown_default) {

	UI::OpenDialog (`opt(`decorated), `HBox(`HSpacing (1), `VBox (
	    `VSpacing (0.2),
	    // popup label, %1 is path to directory
	    `Label (sformat (_("The home directory (%1) already exists.
Use it anyway?"), dir)),
	    `Left (
		// checkbox label
		`CheckBox (`id(`chown_home), _("&Change directory owner"),
		chown_default)
	    ),
	    `HBox(
		`PushButton (`id(`yes), `opt(`default), Label::YesButton()),
		`PushButton (`id(`no), Label::NoButton())
	    ),
	    `VSpacing (0.2)
	), `HSpacing (1)));
	any ui = UI::UserInput ();
	map retmap	= $[
	    "retval"	: ui == `yes
	];
	if (ui == `yes)
	    retmap["chown_home"] = UI::QueryWidget (`id (`chown_home),`Value);
	UI::CloseDialog ();
	return retmap;
    }


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

	// text entry
	string fullnamelabel	= _("User's &Full Name");
	term name_entries = what == "add_user" ?
	    `InputField (`id (`cn), `opt (`notify), fullnamelabel, cn) :
	    `InputField (`id (`cn), fullnamelabel, cn);

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

	term fields = `VBox(
	    // label text
	    do_not_edit ? `Label(_("For remote users, only additional group memberships can be changed.")) : `VSpacing (0),
	    do_not_edit ? `VSpacing (1) : `VSpacing (0),
	    name_entries,
	    `InputField (`id (`username), (what == "add_user") ?
		`opt (`notify, `hstretch) : `opt (`hstretch),
		// input field for login name
		_("&Username"), username
	    ),
	    `VSpacing (),
	    `Password(`id(`pw1), `opt (`hstretch), Label::Password(), ""),
	    `Password(`id(`pw2), `opt (`hstretch), Label::ConfirmPassword(), "")
	);

	term optionbox = `VBox (
	    // checkbox label
	    `Left (`CheckBox (`id (`root_mail),_("Receive S&ystem Mail"),
		root_mail_checked))
	);
	if (complex_layout)
	{
	    optionbox = add (optionbox,
		// checkbox label
		`Left( `CheckBox (`id (`autologin),_("A&utomatic Login"),
		    Autologin::used) )
	    );
	    any root_pw_feature = ProductFeatures::GetFeature ("globals", "root_password_as_first_user");
	    if (root_pw_feature != "")
		optionbox = add (optionbox,
		    `Left (`CheckBox (`id (`root_pw),
		    // checkbox label
		    _("U&se this password for system administrator"), root_pw_feature == true)
		));
	}
	else if (!do_not_edit && !installation ())
	{
	    optionbox	= add (optionbox,
		// check box label
		`Left(`CheckBox (`id(`ena), _("D&isable User Login"), !enabled))
	    );
	}
	term contents = `VBox (
	`VSpacing(),
	`VBox (
	    `VSpacing( 0.5 ),
	    `HBox (
		`HSpacing (2),
		`VBox (
		    `HSquash ( fields ),
		    `VSpacing( 0.5 ),
		    `HBox (
			`HStretch (),
			`HCenter (`HVSquash (optionbox)),
			`HStretch ()
		    )
		),
		`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(""), _("(Unknown)"), 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 = `Top (`InputField (`id(`addd), `opt(`hstretch),
		// textentry label
		_("Addi&tional User Information"), addit_data)
	    );

	term browse = `VBox(
	    `Label (""),
	    // button label
	    `PushButton( `id(`browse), `opt(`key_F6), _("B&rowse...")),
	    action != "edited" ? `Empty () : `Label ("")
	);

	term home_w = `VBox(
	    // textentry label
	    `InputField (`id(`home),`opt(`hstretch),_("&Home Directory"), home),
	    action != "edited" ? `Empty () :
	    `HBox (`HSpacing (), `Left (`CheckBox (`id (`move_home),
		// check box label
		_("&Move to New Location"), create_home))
	    )
	);
	term new_user_term	= action != "added" ? `VBox () : `VBox (
	    `InputField (`id(`mode), `opt (`hstretch),
		// textentry label
		_("Home Directory &Permission Mode"), mode),
	    // check box label
	    `HBox (`HSpacing (), `Left (
		`CheckBox (`id(`skel), _("E&mpty Home"), no_skel))
	    )
	);
	term crypted_home_term	= crypted_home_enabled ? `HBox (
		`VBox (
		    `Label (""),
		    `HBox (`HSpacing (), `Left (
			`CheckBox (`id(`crypted_home), `opt (`notify),
			// check box label
			_("&Use Encrypted Home Directory"), crypted_home_size > 0))
		    )
		),
		// IntField label
		`IntField (`id (`dirsize), _("&Directory Size in MB"), 10, 2147483647, crypted_home_size) // for max value, see bug 244631 :-)
	) : `HBox ();

	return `HBox (
          `HSpacing(1),
          `VBox(
           // label
           do_not_edit ? `Label(_("For remote users, only additional 
group memberships can be changed.")) : `VSpacing (0),
           `VSpacing(0.5),
           `HBox(
		text_mode ? `Empty () : `HSpacing (1),
		`HWeight (3, `VBox(
		    `VSpacing (0.5),
                    `Top(`InputField( `id(`uid), `opt (`hstretch),
			// textentry label
			 _("User &ID (uid)"), sformat("%1",uid ))),
                    `Top(`VBox(
			`HBox ( home_w, browse),
			new_user_term,
			crypted_home_term
		    )),
                    additional_data,
                    `Top(edit_shell),
                    `Top(edit_defaultgroup),
                    `VStretch()
                )),
                text_mode ? `Empty () : `HSpacing (2),
		`HWeight (2, `VBox (
		    `VSpacing (0.5),
		    `MultiSelectionBox( `id(`grouplist),
			// selection box label
			_("Additional Gr&oups"), additional_groups),
		    (user_type == "ldap")
		    ? `MultiSelectionBox( `id(`ldapgrouplist),
			// selection box label
			_("&LDAP Groups"), additional_ldap_groups)
		    : `Empty()
		)),
		text_mode ? `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 last_change_label= "";
	string expires		= GetString (user["shadowExpire"]:nil, "0");
	if (expires == "")
	    expires = "0";

	integer inact	= GetInt (user["shadowInactive"]:nil, -1);
	integer max	= GetInt (user["shadowMax"]:nil, -1);
	integer min	= GetInt (user["shadowMin"]:nil, -1);
	integer warn	= GetInt (user["shadowWarning"]:nil, -1);

	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_label = out["stdout"]:_("Unknown");
	}
	else
	{
	    // label (date of last password change)
	    last_change_label = _("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(),
		`Left (`Label ("")),
                `HSquash(`VBox(
		    `Left (`Label (sformat (
			// label
			_("Last Password Change: %1"), last_change_label ))),
		    `VSpacing (0.2),
		    `Left (
			// check box label
			`CheckBox (`id (`force_pw), _("Force Password Change"),
			last_change == "0")
		    ),
		    `VSpacing (1),
                    `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),
                    `InputField (`id ("shadowExpire"), `opt (`hstretch),
			// textentry label
			_("Ex&piration Date"), exp_date)
                )),
                `VStretch ()),
            `HSpacing (3)
	);
    }

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

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

	list items = [];
	foreach (string cl, clients, {
	    any summary = WFM::CallFunction (cl, ["Summary", $["what" : "user"]]);
	    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":    _("New Local User"),
            // dialog caption:
            "system":	_("New System User"),
            // dialog caption:
            "ldap":     _("New LDAP User")
        ],
        "edit_user": $[
            // dialog caption:
            "local":    _("Existing Local User"),
            // dialog caption:
            "system":	_("Existing System User"),
            // dialog caption:
            "ldap":	_("Existing LDAP User"),
            // dialog caption:
            "nis":	_("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 && user_type != "ldap")
    {
	// tab label
	tabs = add (tabs,`item(`id(`passwordsettings),_("Pass&word Settings")));
    }

    // Now initialize the list of plugins: we must know now if there is some available.
    // 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 != ""));
    clients	= maplist (string plugin, string client, (map<string,string>) plugin_clients, {
	client2plugin [client]	= plugin;
	return client;
    });
    if (clients != [])
    {
	// 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	= `ReplacePoint(`id(`tabContents), get_edit_term ());
	Wizard::SetContents (
	    dialog_labels [ what, user_type ]:"",
	    dialog_contents,
	    EditUserDialogHelp (complex_layout, user_type, what),
	    GetInstArgs::enable_back(),
	    GetInstArgs::enable_next()
	);
    }
    else{
	Wizard::SetContentsButtons (
	    dialog_labels [ what, user_type ]:"",
	    dialog_contents,
	    EditUserDialogHelp (complex_layout, user_type, what),
	    Label::CancelButton(),
	    Label::OKButton()
	);
	Wizard::HideAbortButton ();
    }

    symbol ret		= `edit;
    symbol current	= nil;
    boolean login_modified	= false;
    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;
    }

    // map with id's of confirmed questions
    map<string,any> ui_map	= $[];

    while (true)
    {
	// map returned from Check*UI functions
	map error_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;
	if (ret == `ok)
	    ret = `next;

	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.
Leave it empty?")))
		{
		    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: TODO bug 238282
		    (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	= UsersSimple::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 the full name
	    // (not for LDAP, there are customized rules...)
	    if (ret == `cn)
	    {
		string uname =
		    (string) UI::QueryWidget (`id (`username), `Value);
		if (login_modified && uname == "")
		    login_modified = false; // reenable suggestion
		if (!login_modified)
		{
		    string full	= (string)UI::QueryWidget (`id (ret), `Value);
		    full	= splitstring (full, " ")[0]:"";
		    full	= UsersSimple::Transliterate (full);
		    UI::ChangeWidget (`id (`username), `Value, tolower (
			filterchars (full, UsersSimple::ValidLognameChars ()))
		    );
		}
	    }
	    if (ret == `username)
	    {
		login_modified	= true;
	    }
	    // 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 passwords do not match.
Try again."));
		focus_tab (current, `pw1);
                continue;
            }
            if ((pw1 != "" || !tab) && pw1 != default_pw)
            {
		error = UsersSimple::CheckPassword (pw1, user_type);
		if (error != "")
		{
		    Report::Error (error);
		    focus_tab (current, `pw1);
		    continue;
		}

		list<string> errors	= UsersSimple::CheckPasswordUI ($[
			"uid"		: username,
			"userPassword"	: pw1,
			"type"		: user_type
		]);
		if (errors != [])
		{
		    string message	= mergestring (errors, "\n\n") +
		    // last part of message popup
		    "\n\n" + _("Really use this password?");
		    if (!Popup::YesNo (message))
		    {
			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 (default_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 (error_map["question_id"]:"" == "chown" &&
			    !haskey (error_map, "owned"))
			{
			    map ret = ask_chown_home (home, chown_home);
			    if (ret["retval"]:false)
			    {
				ui_map["chown"]	= home;
				chown_home	= ret["chown_home"]:chown_home;
			    }
			    else
				failed	= true;
			}
			else
			{
			    if (!Popup::YesNo (error_map ["question"]:""))
				failed = true;
			    else
				ui_map[ error_map["question_id"]:"" ] = home;
			}
		    }
		} while (error_map != $[] && !failed);

		if (failed)
		{
		    ret = `notnext;
		    continue;
		}
	    }
	    user["homeDirectory"]	= home;
	    user["chown_home"]		= chown_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") && installation ())
	    {
		if (Autologin::available)
		{
		    Autologin::user = (boolean)
			UI::QueryWidget(`id (`autologin),`Value) ? username: "";
		    Autologin::used = (boolean)
			UI::QueryWidget(`id (`autologin),`Value);
		    Autologin::modified = true;
		}
	    }
	    else if (UI::WidgetExists (`id (`ena)))
	    {
	    // -------------------------------------- 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;
		    }
		}
	    }
	    root_mail_checked	=
		(boolean) UI::QueryWidget (`id (`root_mail), `Value);
	    root_pw	= UI::WidgetExists (`id (`root_pw)) &&
		(boolean) UI::QueryWidget (`id (`root_pw), `Value);
	    // save the username for possible check if it was changed
	    // and home directory should be re-generated
	    if (org_username == "")
		org_username	= username;
	}

	// indide Details dialog
	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);
	    }
	}
	if (current == `details && ret == `crypted_home)
	{
	    boolean checked	= (boolean) UI::QueryWidget (`id (`crypted_home), `Value);
	    if (!checked && UserLogged (org_username))
	    {
		// error popup
		Report::Error(_("The home directory for this user cannot be decrypted,
because the user is currently logged in.
Log the user out first."));
		UI::ChangeWidget (`id (`crypted_home), `Value, true);
		continue;
	    }
	    if (checked && (integer) UI::QueryWidget (`id (`dirsize), `Value) == 10)
		UI::ChangeWidget (`id (`dirsize), `Value, default_crypted_size);
	    UI::ChangeWidget (`id (`dirsize), `Enabled, checked);
	}

	// 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);
		mode	= (string) UI::QueryWidget(`id(`mode), `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;
		}
		boolean failed                  = false;
		do
		{
		    error_map = Users::CheckUIDUI (new_i_uid, ui_map);
		    if (error_map != $[])
		    {
			if (!Popup::YesNo (error_map ["question"]:""))
			{
			    focus_tab (current, `uid);
			    failed	= true;
			}
			else
			{
			    ui_map[ error_map["question_id"]:"" ] = new_i_uid;
			    if(contains(["local","system"],
				error_map["question_id"]:""))
			    {
				new_type = error_map["question_id"]:"local";
				UsersCache::SetUserType (new_type);
			    }
			}
		    }
		} while (error_map != $[] && !failed);
		if (failed)
		{
		    focus_tab (current, `uid);
		    continue;
		}
            } // end of uid checks

            if (defaultgroup != new_defaultgroup)
            {
		map g	= Users::GetGroupByName (new_defaultgroup, new_type);
		if (g == $[])
		    g   = Users::GetGroupByName (new_defaultgroup, "");
                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 (error_map["question_id"]:"" == "chown" &&
			    !haskey (error_map, "owned"))
			{
			    map ret = ask_chown_home (new_home, chown_home);
			    if (ret["retval"]:false)
			    {
				ui_map["chown"]	= new_home;
				chown_home	= ret["chown_home"]:chown_home;
			    }
			    else
				failed	= true;
			}
			else
			{
			    if (!Popup::YesNo (error_map ["question"]:""))
				failed = true;
			    else
				ui_map[ error_map["question_id"]:""] = new_home;
			}
		    }
		} while (error_map != $[] && !failed);

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

	    if (crypted_home_enabled)
	    {
		integer home_size	= (integer)UI::QueryWidget (`id (`dirsize), `Value);
		if ((boolean)UI::QueryWidget (`id (`crypted_home), `Value))
		{
		    if (home_size == 0)
		    {
			// error popup
			Popup::Error (_("Enter the size for the home directory."));
			focus_tab (current, `dirsize);
			continue;
		    }
		    crypted_home_size	= home_size;
		}
		else crypted_home_size	= 0;
	    }


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

	    // 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;
		chown_home	= false;
	    }
	    if (UI::WidgetExists (`id (`move_home)) &&
		UI::QueryWidget (`id (`move_home), `Value) == false)
		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["chown_home"]		= chown_home;
	    user["addit_data"]		= addit_data;
	    user["no_skeleton"]		= no_skel;
	    user["home_mode"]		= mode;
	    user["crypted_home_size"]	= crypted_home_size;
        }

	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(_("The expiration date must be in the 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);
                    }
                }
            }
	    if (UI::QueryWidget (`id (`force_pw), `Value) == true)
	    {
		// force password change
		user["shadowLastChange"]	= "0";
	    }
	}

	// inside plugins dialog
	if (current == `plugins)
	{
	    plugin_client	= (string) UI::QueryWidget (`id(`table),`CurrentItem);
	    if (plugin_client != nil)
	    {
		plugin		= client2plugin[plugin_client]:plugin_client;
	    }

	    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)
	    {
		boolean plugin_added	= false;
		// first, add the plugin if necessary
		if (!contains (user["plugins"]:[], plugin))
		{
		    error	= Users::AddUserPlugin (plugin);
		    if (error != "")
		    {
			Popup::Error (error);
			ret = `notnext;
			continue;
		    }
		    plugin_added	= true;
		    user	= Users::GetCurrentUser ();
		    reinit_userdata ();
		}
		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 ();
		    UI::ChangeWidget (`id(`table), `Item (plugin_client,0), UI::Glyph (`CheckMark));
		}
		// for `cancel we must remove the plugin if it was added because of `run
		else if (plugin_added)
		{
		    error	= Users::RemoveUserPlugin (plugin);
		    if (error != "")
		    {
			Popup::Error (error);
			ret = `notnext;
			continue;
		    }
		    user	= Users::GetCurrentUser ();
		    reinit_userdata ();
		}
	    }
	}

	// ------------------- now handle switching between the tabs
	if (ret == `edit)
	{
	    Wizard::SetHelpText (
		EditUserDialogHelp (installation (), 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 == "ldap")
		    UI::SetFocus(`id(`givenname));
		else
		    UI::SetFocus(`id(`cn));
	    }
	    if (password != nil || what == "edit_user")
	    {
		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);
	    }

	    // LDAP users can be disabled only with certain plugins (bnc#557714)
	    if (UI::WidgetExists (`id (`ena)) && user_type == "ldap")
	    {
		boolean ena = (
		    contains (user["plugins"]:[], "UsersPluginLDAPShadowAccount") ||
		    contains (user["plugins"]:[], "UsersPluginLDAPPasswordPolicy"));
		UI::ChangeWidget (`id (`ena), `Enabled, ena);
	    }

	    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 (`move_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);
		if (UI::WidgetExists (`id (`move_home)))
		    UI::ChangeWidget (`id (`move_home), `Enabled, false);
	    }
	    if (!FileUtils::Exists (home) && UI::WidgetExists (`id (`move_home)))
	    {
		UI::ChangeWidget (`id (`move_home), `Enabled, false);
	    }
	    if (UI::WidgetExists (`id (`mode)))
	    {
		UI::ChangeWidget (`id (`mode), `ValidChars, "01234567");
		UI::ChangeWidget (`id (`mode), `InputMaxLength, 3);
	    }
	    UI::ChangeWidget(`id(`shell), `Value, shell);

	    if (UI::WidgetExists (`id (`crypted_home)))
		UI::ChangeWidget (`id (`dirsize), `Enabled,
		    (boolean) UI::QueryWidget (`id (`crypted_home), `Value));

	    current	= ret;
	}
	if (ret == `passwordsettings)
	{
	    UI::ReplaceWidget(`tabContents, get_password_term ());
	    if (GetString (user["shadowLastChange"]:nil, "0") == "0")
	    {
		// forcing password change cannot be undone
		UI::ChangeWidget  (`id (`force_pw), `Enabled, false);
	    }
	    Wizard::SetHelpText (EditUserPasswordDialogHelp());
	    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;
	    }
	    if ((crypted_home_enabled && action == "edited" &&
		 user["current_text_userpassword"]:nil == nil) &&
		(
		    crypted_home_size != org_crypted_home_size
			||
		    (crypted_home_size > 0 &&
			(org_username != username || org_home != home ||
			// only password was changed
			user["encrypted"]:false == false)
		    )
		)
	    )
	    {
		// do not ask when enabling for first time and password was already entered
		if ((user["encrypted"]:false == false || user["text_userpassword"]:nil != nil) &&
		    org_crypted_home_size == 0)
		{
		    user["current_text_userpassword"]	= (user["text_userpassword"]:nil != nil) ?
			user["text_userpassword"]:nil : user["userPassword"]:"";
		}
		else
		{
		    string old_pw	= AskForOldPassword ();
		    if (old_pw != nil)
		    {
			user["current_text_userpassword"]	= old_pw;
		    }
		    else
		    {
			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;
	    }
	    // check if autologin is not set for some user
	    if (what == "add_user" && !complex_layout &&
		// ask only when there is still one user (bnc#332729)
		size (UsersCache::GetUsernames ("local")) == 1)
	    {
		Autologin::AskForDisabling (
		    // popup text
		    _("Now you have added a new user."));
	    }
	    if (root_mail_checked)
	    {
		if (username != org_username)
		    Users::RemoveRootAlias (org_username);
		Users::AddRootAlias (username);
	    }
	    else if (root_mail) // not checked now, but checked before
	    {
		if (username != org_username)
		    Users::RemoveRootAlias (org_username);
		else
		    Users::RemoveRootAlias (username);
	    }
	    if (username == "root")
		Users::SaveRootPassword	(false);
	    else if (root_pw)
	    {
		// set root's password
		Users::SetRootPassword (password);
		Users::SaveRootPassword (true);
	    }
	}

	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		= (string) group["userPassword"]:nil;
    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;
    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":	_("New Local Group"),
            // dialog caption:
            "system":	_("New System Group"),
            // dialog caption:
            "ldap":     _("New LDAP Group")
        ],
        "edit_group": $[
            // dialog caption:
            "local":     _("Existing Local Group"),
            // dialog caption:
            "system":	_("Existing System Group"),
            // dialog caption:
            "ldap":     _("Existing LDAP Group")
        ]
    ];

    string plugin_client	= "";
    string plugin		= "";
    map client2plugin		= $[];
    list<string> clients	= [];

    /**
     * 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;
	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 (
        `HWeight (1, `VBox(
            `VSpacing(1),
            `Top (`InputField (`id (`groupname), `opt (`hstretch),
		// textentry label
		_("Group &Name"), groupname)),
            `Top (`InputField (`id (`gid), `opt (`hstretch),
		// textentry label
		_("Group &ID (gid)"), sformat("%1", gid) )),
            `VSpacing(1),
            `Bottom (
		`Password(`id(`pw1), `opt (`hstretch), Label::Password (), "")),
            `Bottom (
		`Password(`id(`pw2), `opt (`hstretch), Label::ConfirmPassword(),
		"")
	    ),
            `VSpacing(1)
        )),
        `HSpacing(2),
        `HWeight (1, `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 () {

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

	list items = [];
	foreach (string cl, clients, {
	    any summary = WFM::CallFunction (cl, ["Summary", $["what" : "group"]]);
	    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 ();

    // Now initialize the list of plugins: we must know now if there is some available.
    // 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 != ""));
    clients	= maplist (string plugin, string client, (map<string,string>) plugin_clients, {
	client2plugin [client]	= plugin;
	return client;
    });
    boolean use_tabs	= (size (clients) > 0);
    boolean has_tabs	= true;

    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))
	{
	    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 ()))
	    );
	}
    }
    else
    {
	dialog_contents		= get_edit_term ();
    }

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

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

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

    repeat
    {
	// map returned from Check*UI functions
	map error_map			= $[];
	// map with id's of confirmed questions
	map<string,any> 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);
		focus_tab (current, `groupname);
		continue;
	    }
	    // --------------------------------- password checks
            if ( pw1 != pw2 )
            {
                // The two group password information do not match
                // error popup
                Report::Error(_("The passwords do not match.
Try again.")) ;

		focus_tab (current, `pw1);
                continue;
            }
            if ( pw1 != "" && pw1 != default_pw )
            {
		error = UsersSimple::CheckPassword (pw1, group_type);
		if (error != "")
		{
		    Report::Error (error);
		    focus_tab (current, `pw1);
		    continue;
		}

		list<string> errors	= UsersSimple::CheckPasswordUI ($[
			"cn"		: new_groupname,
			"userPassword"	: pw1,
			"type"		: group_type
		]);
		if (errors != [])
		{
		    string message	= mergestring (errors, "\n\n") +
		    // last part of message popup
		    "\n\n" + _("Really use this password?");
		    if (!Popup::YesNo (message))
		    {
			focus_tab (current, `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);
		    focus_tab (current, `gid);
		    continue;
		}
		boolean failed	= false;
		do
		{
		    error_map = Users::CheckGIDUI (new_i_gid, ui_map);
		    if (error_map != $[])
		    {
			if (!Popup::YesNo (error_map ["question"]:""))
			{
			    failed	= true;
			}
			else
			{
			    ui_map[ error_map["question_id"]:"" ] = new_i_gid;
			    if(contains(["local","system"],
				error_map["question_id"]:""))
			    {
				new_type = error_map["question_id"]:"local";
				UsersCache::SetGroupType (new_type);
			    }
			}
		    }
		} while (error_map != $[] && !failed);
		if (failed)
                {
		    focus_tab (current, `gid);
		    continue;
		}
	    }

	    // --------------------------------- 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)
	{
	    plugin_client	= (string) UI::QueryWidget (`id(`table),`CurrentItem);
	    if (plugin_client != nil)
	    {
		plugin		= client2plugin[plugin_client]:plugin_client;
	    }
	    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)
	    {
		boolean plugin_added	= false;
		// first, add the plugin if necessary
		if (!contains (group["plugins"]:[], plugin))
		{
		    error	= Users::AddGroupPlugin (plugin);
		    if (error != "")
		    {
			Popup::Error (error);
			ret = `notnext;
			continue;
		    }
		    plugin_added	= true;
		    group	= Users::GetCurrentGroup ();
		    reinit_groupdata ();
		}
		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 ();
		    UI::ChangeWidget (`id(`table), `Item (plugin_client,0), UI::Glyph (`CheckMark));
		}
		else if (plugin_added)
		{
		    error	= Users::RemoveGroupPlugin (plugin);
		    if (error != "")
		    {
			Popup::Error (error);
			ret = `notnext;
			continue;
		    }
		    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 != nil)
		{
		    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;
}

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

    Users::CommitUser ();
    // adding only one user during install
    if (installation () && 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 */
}

ACC SHELL 2018