/**
 * File:	include/users/widgets.ycp
 * Package:	Configuration of users and groups
 * Summary:	Widgets definitions and helper functions
 * Authors:	Jiri Suchomel <jsuchome@suse.cz>
 *
 * $Id: widgets.ycp 20592 2004-12-17 09:35:25Z jsuchome $
 */
{

import "Autologin";
import "Label";
import "Ldap";
import "Message";
import "Mode";
import "Popup";
import "Report";
import "Stage";
import "Users";
import "UsersCache";
import "UsersLDAP";
import "Wizard";

include "users/complex.ycp";

textdomain "users";

// values to MenuItem in Summary dialog
map userset_to_string = $[
    // the type of user set
    "system":    _("&System Users"),
    // the type of user set
    "local":     _("&Local Users"),
    // the type of user set
    "nis":       _("&NIS Users"),
    // the type of user set
    "ldap":      _("L&DAP Users"),
    // the type of user set
    "samba":     _("Sam&ba Users"),
    // the type of user set
    "custom":    _("&Custom")
];

// values to MenuItem in Summary dialog
map groupset_to_string = $[
    // the type of group set
    "system":    _("&System Groups"),
    // the type of group set
    "local":     _("&Local Groups"),
    // the type of group set
    "nis":       _("&NIS Groups"),
    // the type of group set
    "ldap":      _("L&DAP Groups"),
    // the type of group set
    "samba":     _("Sam&ba Groups"),
    // the type of group set
    "custom":    _("&Custom")
];

/**
 * Popup for choosing the password encryption method.
 **/
define string EncryptionPopup() ``{

      string method = Users::EncryptionMethod ();

      // Help text for password expert dialog 1/5
      string help_text = _("<p>
<b>This is for experts only.</b>
</p>");

      // Help text for password expert dialog 2/5
      help_text = help_text + _("<p>
Choose a password encryption method for local and system users.
<b>DES</b>, the Linux default method, works in all network environments, but it
restricts passwords to eight characters or less.
</p>
");

      // Help text for password expert dialog 3/5
      help_text = help_text + _("<p>
<b>MD5</b> allows longer passwords, thus provides more security, but some
network protocols don't support this, and you may have problems with NIS.
</p>");

      // Help text for password expert dialog 4/5
      help_text = help_text + _("<p>
<b>Blowfish</b> is similar to MD5, but uses a different algorithm
to encrypt passwords.
</P>");
      // Help text for password expert dialog 5/5
      help_text = help_text + _("<p>
As a general rule of thumb, use DES if you are unsure.
</p>");

      UI::OpenDialog(
	`VBox(
	  // Label
	  `Heading( _("Set Password Encryption") ),
	  `VSpacing( 0.7 ),
	  `HBox(
		`HSpacing( 2 ),
		`RadioButtonGroup(
		// frame label
		`Frame( _("Encryption Type"),
		  `VBox(
		  `VSpacing( 0.5 ),
		  // Radio buttons for password encryption: DES-crypt
		  `Left(`RadioButton(`id(`des), _("&DES (Linux default)"),
			method == "des" ) ),
		  // Radio buttons for password encryption: MD5-crypt
		  `Left(`RadioButton(`id(`md5), _("&MD5"),
			method == "md5" ) ),
		  // Radio buttons for password encryption: blowfish-crypt
		  `Left(`RadioButton(`id(`blowfish), _("&Blowfish"),
			method == "blowfish" ) )
		  )
		)),
		`HSpacing( 2 )
	  ),
	  `VSpacing( 0.5 ),
	  `HBox(
		`HStretch(),
		`HWeight(1, `PushButton(`id(`ok), `opt(`default, `key_F10),
		    Label::OKButton() )),
		`HStretch(),
		`HWeight(1, `PushButton(`id(`cancel), `opt (`key_F9),
		    Label::CancelButton())),
		`HStretch(),
		`HWeight(1, `PushButton(`id(`help), `opt (`key_F2),
		    Label::HelpButton() ) ),
		`HStretch()
	  )
	));

      any button = nil;

      do
      {
	  button = UI::UserInput();

	  if ( button == `help )
	  {
	      Wizard::ShowHelp( help_text );
	  }
	  else if ( button == `ok )
	  {
	      if      ( (boolean) UI::QueryWidget( `id(`des), `Value ) )
		  method = "des";
	      else if ( (boolean) UI::QueryWidget( `id(`md5), `Value ) )
		  method = "md5";
	      else if ( (boolean) UI::QueryWidget( `id(`blowfish), `Value ) )
		  method = "blowfish";
	      y2milestone( "Changing encryption method to %1", method );
	  }
      } while ( button != `ok && button != `cancel );

      UI::CloseDialog();
      return method;
};

/**
 * NIS server enabled together with non-DES encryption of passwords
 * In these popup, ask user what to do.
 */
define symbol AskForNISServerEncryptionPopup (string encr) {

    symbol ret = `ok;

    // help text 1/3
    string text = _("<p>
You have changed the default encryption for user passwords.</p>") +

    // help text 2/3
    _("<p>It seems that you are running a NIS server. In some network enviroments,
you could be unable to login to a NIS client when a user password is
crypted with a different method than DES.
</p>
") +

    // help text 3/3
    _("<p>Really use the selected method?</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::SetAskNISServerNotDES((boolean)UI::QueryWidget(`id(`ch),`Value));
    }
    UI::CloseDialog();
    return ret;
}

/**
 * Popup for configuration user/group filter for making the LDAP search
 * @return modified?
 */
define boolean LDAPSearchFilterPopup () ``{

    string default_user_f	= UsersLDAP::GetDefaultUserFilter ();
    string default_group_f	= UsersLDAP::GetDefaultGroupFilter ();

    string user_f		= UsersLDAP::GetCurrentUserFilter ();
    string group_f		= UsersLDAP::GetCurrentGroupFilter ();

    if (user_f == "")
	user_f = default_user_f;
    if (group_f == "")
	group_f = default_group_f;

    boolean ret = false;

    // attributes are listed here, because during filter editing, the connection
    // to LDAP server doesn't have to be run yet
    list user_attributes	= [
	"objectclass", "loginshell", "gecos", "description", "cn", "uid",
	"uidnumber", "gidnumber", "homedirectory", "shadowlastchange",
	"shadowmin", "shadowmax", "shadowwarning", "shadowinactive",
	"shadowexpire", "shadowflag", "audio", "businesscategory", "carlicense",
	"departmentnumber", "displayname", "employeenumber", "employeetype",
	"givenname", "homephone", "homepostaladdress", "initials", "jpegphoto",
	"labeleduri", "mail", "manager", "mobile", "o", "pager", "photo",
	"roomnumber", "secretary", "usercertificate", "x500uniqueidentifier",
	"preferredlanguage", "usersmimecertificate", "userpkcs12", "title",
	"x121address", "registeredaddress", "destinationindicator",
	"preferreddeliverymethod", "telexnumber", "teletexterminalidentifier",
	"telephonenumber", "internationalisdnnumber","facsimiletelephonenumber",
	"street", "postofficebox", "postalcode", "postaladdress",
	"physicaldeliveryofficename", "ou", "st", "l", "seealso", "sn"
    ];
    list group_attributes	= [
	"objectclass", "memberuid", "description", "gidnumber",
	"businesscategory", "seealso", "owner", "ou", "o", "member", "cn"
    ];

    list connectives	= [
	// combo box item
	`item (`id("and"), _("AND")),
	// combo box item
	`item (`id("or"), _("OR"))
    ];
    list equality	= [ "=", "~=", "<=", ">=" ];
    symbol curr_shown	= UsersCache::GetCurrentSummary () == "users" ?
	`users : `groups;

    string help_text =
// helptext 1/4 - caption
_("<p><b>LDAP Search Filter Changes</b></p>") +

// helptext 2/4
_("<p>Here, extend the search filters for users and groups beyond the default search filters.</p>") +

// helptext 3/4
_("<p>With <b>Default</b>, load the default filter from the user and group
configuration modules, saved on the LDAP server (values of 'susesearchfilter' attributes).
If you are not connected yet, you are prompted for the password.</p>
") +

// helptext 4/4 (do not translate the value (written as <tt> font))
_("<p><b>Example:</b>
<br>With user filter
<br>
<tt>(&(objectclass=posixaccount)(uid=u*))</tt>
<br>
only obtain users with a user name beginning with 'u'.</p>
");


    term contents = `HBox (`HSpacing(1.5), `VBox(
	`HSpacing(70), // max 65 with help on left side...
        `VSpacing(0.5),
	`Left (`RadioButtonGroup (`VBox(
	    `Left (`RadioButton (`id (`users), `opt(`notify),
		// radiobutton label
		_("Search Filter for &Users"), curr_shown == `users)),
	    `Left (`RadioButton (`id (`groups), `opt(`notify),
		// radiobutton label
		_("Search Filter for &Groups"),curr_shown == `groups))
	))),
	`TextEntry (`id(`currf), "", curr_shown == `users ? user_f : group_f),
	`VSpacing(0.5),
	// frame label
	`Frame (_("Add New Condition to Current Filter"), `HBox (
	    `HSpacing (0.5), `VBox (
		`Left (`ComboBox (`id(`andor), "", connectives)),
		`HBox (
		    `ReplacePoint (`id(`rpa),
			// combobox label
			`ComboBox (`id(`atrs), `opt(`editable), _("&Attribute"),
			    curr_shown == `users ?
			    user_attributes : group_attributes)
		    ),
		    `HSpacing (),
		    `ComboBox (`id(`eq), "", equality),
		    `HSpacing (),
		    // textentry label
		    `TextEntry (`id(`val), _("&Value"), "")
		),
		// pushbuttton label
		`Right (`PushButton (`id(`addu), _("A&dd to Filter")))
	    ),
	    `HSpacing (0.5)
	)),
	`VSpacing(),
	`HBox (
	    `PushButton(`id(`ok),`opt(`default,`key_F10),Label::OKButton()),
	    `PushButton(`id(`cancel), `opt(`key_F9), Label::CancelButton()),
	    `PushButton(`id(`help), `opt(`key_F2), Label::HelpButton()),
	    // Pushbutton label
	    `PushButton(`id(`read), `opt(`key_F3), _("De&fault"))
	),
        `VSpacing (0.5)
    ), `HSpacing (1.5));

    UI::OpenDialog (`opt(`decorated), contents);
    symbol button	= `notnext;
    do
    {
	button = (symbol) UI::UserInput();

	if ( button == `help )
        {
	    Wizard::ShowHelp (help_text);
	    continue;
	}
	if ( button == `read )
	{
	    if (Ldap::bind_pass == nil)
		Ldap::SetBindPassword (Ldap::GetLDAPPassword (true));
	    if (Ldap::bind_pass != nil && UsersLDAP::ReadFilters() == "")
	    {
		UI::ChangeWidget (`id (`currf), `Value, curr_shown == `users ?
		    UsersLDAP::GetDefaultUserFilter () :
		    UsersLDAP::GetDefaultGroupFilter ());
	    }
	    continue;
	}
	string curr_f =	(string) UI::QueryWidget(`id (`currf), `Value);
	if (button == `addu)
	{
	    if ((string) UI::QueryWidget(`id (`val),`Value) == "")
	    {
		// error popup
		Popup::Error (_("Enter the value for attribute."));
		UI::SetFocus (`id(`val));
		continue;
	    }
	    string new_value	= sformat ("%1%2%3",
		(string) UI::QueryWidget(`id (`atrs),`Value),
		(string) UI::QueryWidget(`id (`eq),`Value),
		(string) UI::QueryWidget(`id (`val),`Value));

	    string conn	= (string) UI::QueryWidget(`id (`andor),`Value);
	    UI::ChangeWidget(`id (`currf), `Value,
		UsersLDAP::AddToFilter (curr_f, new_value, conn));
	}
	if (button == `ok || button == `users || button == `groups)
	{
	    if ((button == `groups || (button == `ok && curr_shown == `users))
		&& user_f != curr_f)
	    {
		string curr_user_f = curr_f;
		if (curr_user_f == "")
		{
		    // error popup
		    Popup::Error (_("Enter the value of user filter."));
		    UI::SetFocus (`id(`currf));
		    button = `notnext;
		    continue;
		}
		if (!issubstring (tolower (curr_user_f),tolower(default_user_f))
		    // yes/no popup question
		    && !Popup::YesNo (_("The new user filter does not contain the default user filter.
Really use it?
")))
		{
		    UI::SetFocus (`id(`currf));
		    button = `notnext;
		    continue;
		}
		user_f = curr_user_f;
	    }
	    if ((button == `users || (button == `ok && curr_shown == `groups))
		&& group_f != curr_f)
	    {
		string curr_group_f = curr_f;
		if (curr_group_f == "")
		{
		    // error popup
		    Popup::Error (_("Enter the value of group filter."));
		    UI::SetFocus (`id(`currf));
		    button = `notnext;
		    continue;
		}
		if(!issubstring(tolower(curr_group_f),tolower(default_group_f)))
		{
		    // yes/no popup question
		    if (!Popup::YesNo (_("The new group filter does not contain the default group filter.
Really use it?
")))
		    {
			UI::SetFocus (`id(`currf));
			button = `notnext;
			continue;
		    }
		}
		group_f = curr_group_f;
	    }
	    if (button == `ok)
	    {
		// checks are OK, let's update the values now
		if (user_f != UsersLDAP::GetCurrentUserFilter ())
		{
		    UsersLDAP::SetCurrentUserFilter (user_f);
		    ret = true;
		}
		if (group_f != UsersLDAP::GetCurrentGroupFilter())
		{
		    UsersLDAP::SetCurrentGroupFilter (group_f);
		    ret = true;
		}
	    }
	    else
	    {
		UI::ChangeWidget (`id (curr_shown), `Value, false);
		curr_shown = (curr_shown == `users) ? `groups : `users;
		UI::ChangeWidget (`id (curr_shown), `Value, true);
		UI::ChangeWidget (`id (`currf), `Value, curr_shown == `users ?
		    user_f : group_f);
		UI::ReplaceWidget (`id(`rpa),
		    // combobox label
		    `ComboBox (`id(`atrs), `opt(`editable), _("&Attribute"),
			curr_shown == `users ?
			user_attributes : group_attributes)
		);
	    }
	}
    } while ( button != `ok && button != `cancel );

    UI::CloseDialog();
    return ret;
}

/**
 * Popup for Login settings (Auotolgin feature, login without passwords)
 * @return modified?
 **/
define boolean AutologinPopup() ``{

    string help_text =
    // helptext 0/3 - caption
    _("<p><b>Login Settings</b></p>") +

     // helptext 1/3 - general info
    _("<p>
The features described below are only available if you are using KDM as a login manager.
</p>
") +

// helptext 2/3
_("<p><b>Auto Login</b><br>
The <b>login</b> procedure could be skipped if <b>Auto Login</b> is set. In such a case, the user choosen from the list is logged in automatically.</p>
") +

// helptext 3/3
_("<p><b>Passwordless Logins</b><br>
If this option is checked, all users are allowed to login without entering
the password. Otherwise, you are asked for the password even if you set a user to log in automatically.</p>
");


    boolean ret = false;

    string user		= Autologin::user;
    boolean pw_less	= Autologin::pw_less;
    boolean auto_used	= (user != "");

    // TODO check if nis/ldap users were read?

    list usernames	= UsersCache::GetUsernames ("local");

    UI::OpenDialog(`opt(`decorated), `HBox(`HSpacing(1.5),
        `VBox(
            `HSpacing(40),
            `VSpacing(0.5),
		// frame label
	        `Frame(_("KDM Login Settings"), `HBox(
		  `HSpacing (0.5),
		  `VBox(
		    `VSpacing(0.5),
		    `Left(`CheckBox (`id(`auto),`opt (`notify),
			// checkbox label
			_("&Auto Login"), auto_used)),
		    `VSpacing(0.2),
		    `HBox (
			`HSpacing (4), // move text under the checkbox
			`Left(`ComboBox (`id(`autouser),
			    // textentry label
			    _("&User to Log in"), usernames))
		    ),
		    `VSpacing(0.5),
		    `Left(`CheckBox (`id(`pw_less),
			// checkbox label
			_("Password&less Logins"), pw_less)),
		    `VSpacing(0.5)
		  ),
		  `HSpacing(0.5)
		)),
            `VSpacing(0.5),
	    `HBox(
		`PushButton(`id(`ok),`opt(`default,`key_F10),Label::OKButton()),
	        `PushButton(`id(`cancel), `opt(`key_F9), Label::CancelButton()),
	        `PushButton(`id(`help), `opt(`key_F2), Label::HelpButton())
	    ),
            `VSpacing(0.5)),
        `HSpacing(1.5)
    ));

    if (user != "")
	UI::ChangeWidget (`id (`autouser), `Value, user);
    UI::ChangeWidget (`id (`autouser), `Enabled, auto_used);

    symbol button = nil;
    do
    {
	button = (symbol) UI::UserInput();

	if ( button == `help )
	{
	    Wizard::ShowHelp( help_text );
	}
	if ( button == `auto )
	{
	    auto_used = (boolean) UI::QueryWidget (`id(`auto), `Value);
	    UI::ChangeWidget (`id (`autouser), `Enabled, auto_used);
	}
    } while ( !contains ([`ok, `cancel, `abort], button));

    if (button == `ok)
    {
	user = (auto_used) ?(string)UI::QueryWidget (`id(`autouser), `Value):"";
	pw_less = (boolean) UI::QueryWidget (`id(`pw_less), `Value);

	if (user != Autologin::user || pw_less != Autologin::pw_less)
	{
	    ret = true;
	    Autologin::used	= auto_used;
	    Autologin::user	= user;
	    Autologin::pw_less	= pw_less;
	    Autologin::modified = true;
	}
    }
    UI::CloseDialog();
    return ret;
};


/**
 * Popup for deleting user
 * @return symbol for sequencer
 */
define symbol DeleteUserPopup() ``{

    boolean delete      = true;
    boolean delete_home = false;

    string type		= UsersCache::GetUserType ();
    map user		= Users::GetCurrentUser ();
    string username     = user["uid"]:"";
    string home		= user["org_user","homedirectory"]:user["org_homedirectory"]:user["homedirectory"]:"";
    integer uid		= -1;
    any uid_a		= user["org_user","uidnumber"]:user["org_uidnumber"]:user["uidnumber"]:"-1";
    if (is (uid_a, string))
	uid = tointeger (uid_a);
    else uid = (integer) uid_a;

    if (type == "nis")
    {
        // error popup
        Report::Message (sformat (_("Cannot delete the user %1. It must be done on the NIS server."), username));
        return nil;
    }

    // if the user has log on system
    map out	= (map) SCR::Execute (.target.bash_output,
	sformat ("ps --no-headers -u %1", uid));
    string proc = out["stdout"]:"";
    if (size (proc) != 0 && !Mode::config ())
    {
        // error popup
        Report::Error(_("You can not delete this user, because the user is present.
Please log off the user first."));
        delete = false;
        return nil;
    }

    boolean no_home	= false;
    // check if dir exists with this owner
    map stat		= (map)SCR::Read (.target.stat, home);
    if ((type == "ldap" && !Ldap::file_server) || (stat["uid"]:-1 != uid))
    {
        no_home = true;
    }

    // if the user want to delete a system user
    if (type == "system")
    {
        // yes-no popup headline
        if(! Popup::YesNoHeadline(_("The Selected User Is a System User"),
	    // yes-no popup contents
	    _("Do you really want to delete this user?")))
        {
	    delete = false;
        }
    }
    else
    {
        if (home != "" && !no_home)
        {
	    term contents = `HBox(
		`HSpacing(3),
		`VBox (`VSpacing(1),
		    `Left (
			// question popup. %1 is username
			`Heading(sformat(_("Delete the user %1?"), username ))),
		    `VSpacing(0.5),
		    `Left (`CheckBox(`id(`delete_home),
			// checkbox label
			sformat(_("Delete &Home Directory
%1
"), home )
		    )),
		    `VSpacing(1),
		    `HBox (
			`Bottom(`PushButton(`id(`ok),`opt(`key_F10),
			    Label::YesButton()  )),
			`Bottom(`PushButton(`id(`cancel), `opt(`key_F9),
			    Label::NoButton()))
		    )
		),
		`HSpacing(3)
	    );

            UI::OpenDialog(`opt( `decorated ), contents );
            any ret  = UI::UserInput();
	    if (ret != `ok)
		delete = false;
            delete_home = (boolean) UI::QueryWidget (`id(`delete_home), `Value);

            UI::CloseDialog();
        }
        else
        {
            // yes-no popup. %1 is username
            if( ! Popup::YesNo(sformat(_("
Do you really want to delete the user %1?
"), username )))
	    {
		delete = false;
	    }
        }
    }
    if (delete)
    {
	Users::DeleteUser (delete_home);
        return `delete;
    }
    return nil;
}

/**
 * Popup for deleting group
 * @return symbol for sequencer
 */
define symbol DeleteGroupPopup() ``{

    boolean delete		= true;
    string type			= UsersCache::GetGroupType ();
    map group			= Users::GetCurrentGroup ();
    string member_attribute	= UsersLDAP::GetMemberAttribute ();

    // if no user is in this group
    if (group["userlist"]:$[] == $[] &&
        group["more_users"]:$[] == $[] &&
        group[member_attribute]:$[] == $[])
    {
        //if the group is a system group ask the user ..
        if (type == "system")
        {
            // yes-no popup headline
            if(!Popup::YesNoHeadline(_("The marked group is a system group."),
		/// yes-no popup contents
		_("Really delete this group?")))
	    {
                return nil;
	    }
        }
        else
        {
            // yes-no popup, %1 si group name
            if (! Popup::YesNo(sformat(_("
Do you really want to delete the group %1?
"), group["cn"]:"")))
	    {
                return nil;
	    }
        }
    }
    else
    {
        // warning popup
        Popup::Warning(_("You cannot delete this group because
there are users in the group.
Remove these users from the group first.
"));
        return nil;
    }

    Users::DeleteGroup ();
    return `delete;
}

/**
 * Dialog for definition of customized view
 * @param what "users" or "groups"
 * @return true if customs were odified
 */
define boolean CustomizePopup (string what) ``{

    term view = `VBox();
    string label = "";
    list<string> sets = [];
    list custom_sets = [];
    map set_to_string = $[];

    if (what == "users")
    {
        sets = filter (string set, Users::GetAvailableUserSets (),
	    ``(set != "custom"));
        custom_sets = Users::GetUserCustomSets ();
        set_to_string = userset_to_string;
	// Frame label
        label = _("Customize the View of the User List");
    }
    else
    {
        sets = filter (string set, Users::GetAvailableGroupSets (),
	    ``(set != "custom"));
        custom_sets = Users::GetGroupCustomSets ();
        set_to_string = groupset_to_string;
	// Frame label
        label = _("Customize the View of the Group List");
    }

    foreach (string set, sets, ``{
        view = add (view, `VSpacing(0.5));
        if ( contains (custom_sets, set) )
	    view = add (view,
		`Left(`CheckBox(`id(set), set_to_string [set]:"", true)));
        else
	    view = add (view,
		`Left(`CheckBox(`id(set), set_to_string [set]:"", false)));
    });
    view = add (view, `VSpacing(0.5));

    UI::OpenDialog(`opt(`decorated), `HBox(`HSpacing(1.5),
        `VBox(
            `HSpacing(40),
            `VSpacing(0.5),
	    `Frame(label, view),
            `VSpacing(0.5),
	    `HBox(
	        `PushButton(`id(`ok),`opt(`default,`key_F10),Label::OKButton()),
		`PushButton(`id(`cancel), `opt(`key_F9), Label::CancelButton())
	    ),
            `VSpacing(0.5)),
        `HSpacing(1.5)
    ));
    any ret = UI::UserInput();
    boolean modified = false;
    list<string> new_customs = [];
    if (ret == `ok)
    {
        foreach (string set, sets, ``{
            if ((boolean)UI::QueryWidget(`id(set), `Value))
            {
                new_customs = add (new_customs, set);
                if (! contains (custom_sets, set)) modified = true;
            }
            else
            {
                if (contains (custom_sets, set)) modified = true;
            }
        });;
    }
    UI::CloseDialog();
    if (modified)
    {
	if (contains (new_customs, "ldap") && Ldap::bind_pass == nil)
	{
	    Ldap::SetBindPassword (Ldap::GetLDAPPassword (true));
	    if (Ldap::bind_pass == nil || UsersLDAP::ReadSettings () != "")
	    {
		new_customs = filter (string set, new_customs,``(set !="ldap"));
	    }
	}
        modified = Users::ChangeCustoms (what, new_customs);
    }
    return modified;
}

/**
 * When there are more users/groups shown, choose which type should be created
 * after `add click
 */
define string ChooseTypePopup (list<string> sets, string what) ``{

    map set_to_string = $[
	// type of user/group
	// (item of list with the headline 'Choose the type of user to add')
	"local"	: _("Local"),
	// type of user/group
	// (item of list with the headline 'Choose the type of user to add')
	"ldap"	: _("LDAP"),
	// type of user/group
	// (item of list with the headline 'Choose the type of user to add')
	"system": _("System")
    ];

    sets		= filter (string set, sets, ``(set != "nis"));
    string ret		= sets[0]:"local";
    string label	= (what == "user") ?
	// label
	_("Choose the type of user to add") :
	// label
	_("Choose the type of group to add");

    if (size (sets) < 2) return ret;

    term rbs = `VBox ();
    foreach (string set, sets, ``{
	rbs = add (rbs, `Left (
	    `RadioButton (`id (set), set_to_string [set]:set, set == ret)));
    });

    UI::OpenDialog(`opt(`decorated), `HBox(`HSpacing(1.5),
        `VBox(
            `HSpacing(40),
            `VSpacing(0.5),
	    `Left (`Label (label)),
            `VSpacing(0.5),
	    `Left (`RadioButtonGroup (rbs)),
            `VSpacing(0.5),
	    `HBox(
	        `PushButton(`id(`ok),`opt(`default,`key_F10),Label::OKButton()),
		`PushButton(`id(`cancel), `opt(`key_F9), Label::CancelButton())
	    ),
            `VSpacing(0.5)),
        `HSpacing(1.5)
    ));

    any r = UI::UserInput();

    foreach (string set, sets, ``{
	if ( (boolean) UI::QueryWidget( `id(set), `Value ) )
	    ret = set;
    });

    UI::CloseDialog();

    if (r == `cancel) return "";

    return ret;
}


//================================================================

define list GetSetsItems (string set) ``{

    list items = [];
    if (set == "users")
    {
	foreach (string set, Users::GetAvailableUserSets (), ``{
	    items = add (items, `item (`id(set), userset_to_string [set]:""));
	});
	// menubutton item
	items = add (items, `item (`id(`customize), _("Customi&ze Filter...")));
    }
    else
    {
	foreach (string set, Users::GetAvailableGroupSets (), ``{
	    items = add (items, `item (`id(set), groupset_to_string [set]:""));
	});
	// menubutton item
	items = add (items, `item (`id(`customize), _("Customi&ze Filter...")));
    }
    return items;
}

/**
 * return the list of menu items of "Expert Options" menubutton
 */
define list GetExpertList () ``{

    // TODO do not generate always -> expert_list_changed
    list expert_list = [
	// menubutton label
	`item(`id(`defaults), _("De&faults for New Users")),
    ];
    if (!Mode::config ())
    {
	expert_list = merge (expert_list, [
	    // menubutton label
	    `item(`id(`enc), _("Password &Encryption")),
	    // menubutton label
	    `item(`id(`auth), _("&Authentication and User Sources"))
	]);
	if (Autologin::available)
	{
	    expert_list = add (expert_list,
	        // menubutton label
		`item(`id(`autologinconf), _("&Login Settings")));
	}
	if (!Stage::cont ())
	{
	    expert_list = add (expert_list,
		// menubutton label
		`item(`id(`save), _("&Write Changes Now")));
	}
    }
    return expert_list;
}

define list GetLDAPExpertList () ``{

    // TODO do not generate always -> expert_list_changed
    list expert_list = [ ];
    if (!Mode::config () && Users::LDAPAvailable () && !Users::LDAPModified ())
    {
	expert_list = add (expert_list,
	    // menubutton label
	    `item(`id(`ldapfilter), _("LDAP &Search Filter")));
	expert_list = add (expert_list,
	    // menubutton label
	    `item(`id(`ldapconf),_("L&DAP Users and Groups Configuraton")));
    }
    return expert_list;
}

//================================================================
//----------------- some help texts ------------------------------

/**
 * First part of the help text.
 * @return string help text
 */
define string help_main_start()``{
    // help text 1/1
    return _("<p>
Linux is a <b>multiuser system</b>. Several different users can be logged in the
system at the same time.  To avoid confusion, each user must have
a unique identity if they want to use Linux. Furthermore, every user at
least belongs to one group.
</p>
");
}


/**
 * Last part of the help text.
 * @return string help text
 */
define string help_main_end()``{

    // button label (without shortcut sign)
    string button = Stage::cont () ? _("Next") : _("Finish");

    // help text 1/3
    return _("<p>
Users and groups are arranged in various sets. Change the set currently shown in the table with <b>Set Filter</b>.
Customize your view filter with <b>Customize Filter</b>.</p>
") +

    // help text 2/3
_("<p>
Click <b>Expert Options</b> to edit various expert settings:
password encryption type, user authentication method, default values for new
users, or login settings. With <b>Write Changes Now</b>, save
all changes made so far without exiting the configuration module.</p>
") +

    // help text 3/3, %1 is translated button label
    sformat(_("<p>
To save the modified user and group setting to your system, press
<b>%1</b>.
</p>
"), button);
}

/**
 * Help for UsersDialog.
 * @return string help text
 */
define string UsersDialogHelp() ``{
    // help text 1/4
    return help_main_start() + _("
<p>
In this dialog, get information about existing <b>Users</b>.
</p>
") +

    // help text 2/4
_("<p>
To shift to the group dialog, select <b>Groups</b>.
</p>
") +

    // help text 3/4
_("
<p>
To create a new user, push the button <b>Add</b>.
</p>
") +

    // help text 4/4
_("<p>
To edit or delete an existing user, select one user from the list and
click <b>Edit</b> or <b>Delete</b>.
</p>
") +

    help_main_end();
}

/**
 * Help for usersGroups.
 * @return string help text
 */
define string GroupsDialogHelp()``{
    return help_main_start() +
    // help text 1/4
_("
<p>
In this dialog, get information about existing <b>Groups</b>.
</p>
") +
    // help text 2/4
_("<p>
To shift to the user dialog, select <b>Users</b>.
</p>
") +

    // help text 3/4
_("
<p>
To create a new group, push the button <b>Add</b>.
</p>
") +
    // help text 4/4
_("<p>
To edit or delete an existing group, select one group from the list and
push the button <b>Edit</b> or <b>Delete</b>.
</p>
")     + help_main_end();
}


//================================================================

/**
 * Initialize the contents of Summary Table widget
 */
define void SummaryTableInit (string widget_id) ``{

    list items			= [];
    string current_summary	= UsersCache::GetCurrentSummary ();
    if (current_summary == "users")
    {
	items = UsersCache::GetUserItems ();
	UI::ReplaceWidget (`id(`rptable),
	    `Table (`id("table"), `opt(`notify), `header(
		// table header
		_("Login"),
		// table header
		_("Name"),
		// table header
		_("UID"),
		// table header
		_("Groups")), [] )
	);
    }
    else
    {
	items = UsersCache::GetGroupItems ();
	UI::ReplaceWidget (`id(`rptable),
	    `Table (`id("table"), `opt(`notify), `header(
		// table header
		_("Group name"),
		// table header
		_("Group ID"),
		// table header
		_("Group members")), [] )
	);
    }
    UI::ChangeWidget (`id(widget_id), `Items, items);
    if (size (items) > 0)
    {
	UI::SetFocus (`id(widget_id));
	integer focusline = UsersCache::GetCurrentFocus ();
	if (focusline != nil)
	    UI::ChangeWidget (`id(widget_id), `CurrentItem, focusline);
    }
}

define symbol SummaryTableHandler (string widget_id, map event) {

    any ev_id = event["ID"]:nil;

    if (ev_id == "table")
    {
        ev_id = `edit;
    }
    if (ev_id == `new)
    {
	string error	= "";
	if (UsersCache::GetCurrentSummary () == "users")
	{
	    list<string> current_users	= Users::GetCurrentUsers();
	    if (size (current_users) > 1)
	    {
		string set = ChooseTypePopup (current_users, "user");
		if (set == "") return nil;
		current_users = filter (string u, current_users, ``(u != set));
		current_users = prepend (current_users, set);
		Users::SetCurrentUsers (current_users);
	    }
	    error = Users::AddUser ($[]);
	}
	else
	{
	    list<string> current_groups	= Users::GetCurrentGroups();
	    if (size (current_groups) > 1)
	    {
		string set = ChooseTypePopup (current_groups, "group");
		if (set == "") return nil;
		current_groups = filter (string u, current_groups,``(u != set));
		current_groups = prepend (current_groups, set);
		Users::SetCurrentGroups (current_groups);
	    }
	    error = Users::AddGroup ($[]);
	}
	if (error != "")
	{
	    Popup::Error (error);
	    return nil;
	}
    }
    if (ev_id == `edit || ev_id == `delete)
    {
        integer selected = (integer)UI::QueryWidget (`id("table"),`CurrentItem);
        if (selected != nil)
        {
	    string error	= "";
	    UsersCache::SetCurrentFocus (selected);
	    if (UsersCache::GetCurrentSummary () == "users")
	    {
		Users::SelectUser (selected);
		if (ev_id == `delete)
		    ev_id = DeleteUserPopup ();
		else
		{
		    error = Users::EditUser ($[]);
		}
	    }
	    else
            {
                Users::SelectGroup (selected);
                if (UsersCache::GetGroupType () == "nis")
                {
                    // error popup
                    Report::Message (_("To edit or delete a NIS group,
you must do it on server.
"));
                    ev_id = nil;
                }

                if (ev_id == `delete)
                    ev_id = DeleteGroupPopup();
		else
		{
		    error = Users::EditGroup ($[]);
		}
            }
	    if (error != "")
	    {
		Report::Error (error);
		return nil;
	    }
        }
        else
        {
	    // error popup
            Report::Message(_("Select an entry from the table."));
            return nil;
        }
    }
    if (! is (ev_id, symbol))
    {
	y2error ("strange ev_id value: %1", ev_id);
	return nil;
    }
    return (symbol) ev_id;
}

define void RadioButtonsUsersGroupsInit (string widget_id) ``{

    string current_summary	= UsersCache::GetCurrentSummary ();
    string curr = "";
    if (current_summary == "users")
    {
	list current_users = Users::GetCurrentUsers ();
        if (UsersCache::CustomizedUsersView ())
            curr = userset_to_string["custom"]:"";
	else
            curr = userset_to_string[current_users[0]:"custom"]:"";
    }
    else
    {
	list current_groups = Users::GetCurrentGroups ();
        if (UsersCache::CustomizedGroupsView ())
            curr = groupset_to_string["custom"]:"";
	else
            curr = groupset_to_string[current_groups[0]:"custom"]:"";
    }
    // label, e.g. 'Filter: Local Users', 'Filter: NIS Groups', 'Filter: Custom'
    curr = sformat (_("Filter: %1"), deletechars (curr, "&"));

    UI::ReplaceWidget (`id(`rprb), `HBox (
	    `Left (`RadioButtonGroup (`HBox(
		// radiobutton
		`RadioButton (`id ("users"), `opt(`notify), _("&Users")),
		// radiobutton
		`RadioButton (`id ("groups"), `opt(`notify), _("&Groups"))
	    ))),
	    `Right (`Label (`id(`current_filter), curr))
    ));

    UI::ChangeWidget (`id (current_summary), `Value, true);
    Wizard::RestoreHelp (
	current_summary == "users" ? UsersDialogHelp () : GroupsDialogHelp ()
    );
}

define symbol RadioButtonsUsersGroupsHandler (string widget_id, map event) {

    if (!(boolean)
	UI::QueryWidget (`id (UsersCache::GetCurrentSummary ()),`Value))
    {
	UsersCache::ChangeCurrentSummary ();
	SummaryTableInit ("table");

	UI::ReplaceWidget (`id(`rpfilter), `HBox(
            // MenuButton label
            `MenuButton (`id(`sets), `opt(`key_F2), _("&Set Filter"),
                GetSetsItems (UsersCache::GetCurrentSummary ()))
	));
    }
    RadioButtonsUsersGroupsInit (widget_id);
}

define void AdvancedButtonsInit (string widget_id) ``{

    UI::ReplaceWidget (`id(`rpfilter), `HBox(
	// MenuButton label
        `MenuButton (`id(`sets), `opt(`key_F2), _("&Set Filter"),
	    GetSetsItems (UsersCache::GetCurrentSummary ()))
    ));
    UI::ReplaceWidget (`id (`rpexpert),
	// Menu Buton label
	`MenuButton (`id(`expertlist), _("E&xpert Options..."), GetExpertList())
    );
    if (Users::LDAPAvailable ())
    {
	UI::ReplaceWidget (`id (`rpldap),
	    // Menu Buton label
	    `MenuButton (`id(`ldaplist), _("&LDAP Options..."), GetLDAPExpertList())
	);
	if (Users::LDAPModified ())
	    UI::ChangeWidget (`id(`ldaplist), `Enabled, false);
    }
}

/**
 * Handler for events generated by Expert Settings menubutton
 */
define symbol AdvancedButtonsHandler (string widget_id, map event) {

    any ev_id			= event["ID"]:nil;
    string current_summary	= UsersCache::GetCurrentSummary ();

    if (current_summary == "users" && is (ev_id,string) &&
	contains (Users::GetAvailableUserSets (), (string) ev_id))
    {
	if (ev_id == "ldap" && Ldap::bind_pass == nil)
	{
	    Ldap::SetBindPassword (Ldap::GetLDAPPassword (true));
	    if (Ldap::bind_pass == nil)
		return nil;
	}
	boolean popup = false;
	if ((ev_id == "ldap" && Users::LDAPNotRead ()) ||
	    (ev_id == "nis" && Users::NISNotRead ()))
	{
	    UI::OpenDialog (`opt(`decorated ),
	        // wait popup
		`Label(_("Reading sets of users and groups. Please wait...")));
	    popup = true;
	}
        if (Users::ChangeCurrentUsers ((string) ev_id))
        {
	    if (popup)
	    {
		UI::CloseDialog ();
		popup = false;
	    }
            UsersCache::SetCustomizedUsersView (ev_id == "custom");
	    SummaryTableInit ("table");
	    RadioButtonsUsersGroupsInit ("rb_users_groups");
        }
	if (popup) UI::CloseDialog ();
	return nil;
    }
    if (current_summary == "groups" && is (ev_id,string) &&
	contains (Users::GetAvailableGroupSets (), (string) ev_id))
    {
	if (ev_id == "ldap" && Ldap::bind_pass == nil)
	{
	    Ldap::SetBindPassword (Ldap::GetLDAPPassword (true));
	}
	boolean popup = false;
	if ((ev_id == "ldap" && Users::LDAPNotRead ()) ||
	    (ev_id == "nis" && Users::NISNotRead ()))
	{
	    UI::OpenDialog (`opt(`decorated ),
	        // wait popup
		`Label(_("Reading sets of users and groups. Please wait...")));
	    popup = true;
	}
        if (Users::ChangeCurrentGroups ((string) ev_id))
        {
	    if (popup)
	    {
		UI::CloseDialog ();
		popup = false;
	    }
            UsersCache::SetCustomizedGroupsView (ev_id == "custom");
	    SummaryTableInit ("table");
	    RadioButtonsUsersGroupsInit ("rb_users_groups");
        }
	if (popup) UI::CloseDialog ();
	return nil;
    }
    if (ev_id == `customize)
    {
        if (CustomizePopup (current_summary) &&
	    UsersCache::CustomizedUsersView ())
	{
	    SummaryTableInit ("table");
	}
	RadioButtonsUsersGroupsInit ("rb_users_groups");
	return nil;
    }
    if (ev_id == `enc)
    {
	string enc = EncryptionPopup ();
	if (enc != Users::EncryptionMethod ())
	{
	    if (enc != "des" &&
		Users::NISMaster () && !Users::NotAskNISServerNotDES ())
	    {
		if (AskForNISServerEncryptionPopup (enc) != `ok)
		    return nil;
	    }
	    Users::SetEncryptionMethod (enc);
	}
	return nil;
    }
    if (ev_id == `autologinconf)
    {
	AutologinPopup();
	return nil;
    }
    if (ev_id == `ldapfilter)
    {
	// change of search filter (only when LDAP was not modified yet)
	if (LDAPSearchFilterPopup() && !Users::LDAPModified())
	{
	    Users::SetLDAPNotRead (true);
	    list current = (current_summary == "users") ?
		Users::GetCurrentUsers () : Users::GetCurrentGroups ();
	    if (contains (current, "ldap"))
	    {
		// simulate the action "show LDAP users"
		AdvancedButtonsHandler (widget_id, $["ID":"ldap"]);
	    }
	    // now update the other list (not current_summary)
	    current = (current_summary == "users") ?
		Users::GetCurrentGroups () : Users::GetCurrentUsers ();
	    if (contains (current, "ldap"))
	    {
		// customize view is lost... TODO
		if (current_summary == "users")
		    Users::ChangeCurrentGroups ("ldap");
		else
		    Users::ChangeCurrentUsers ("ldap");
	    }
	}
	return nil;
    }
    if (ev_id == `ldapconf)
    {
	any ret = WFM::CallFunction ("ldap_config", ["late_dialog"]);
	if (ret == `next && Ldap::ldap_modified)
	{
	    if (!Users::LDAPNotRead () &&
		// yes/no popup (data were changed)
		Popup::YesNo (_("Reread all data from LDAP server?")))
            {
		// read all LDAP configuration again!
		Users::SetLDAPNotRead (true);
		UsersLDAP::SetFiltersRead (false);
		UsersLDAP::SetInitialized (false);
		list current = (current_summary == "users") ?
		    Users::GetCurrentUsers () : Users::GetCurrentGroups ();
		if (contains (current, "ldap"))
		{
		    // simulate the action "show LDAP users"
		    AdvancedButtonsHandler (widget_id, $["ID":"ldap"]);
		}
		// now update the other list (not current_summary)
		current = (current_summary == "users") ?
		    Users::GetCurrentGroups () : Users::GetCurrentUsers ();
		if (contains (current, "ldap"))
		{
		    // customize view is lost... TODO
		    if (current_summary == "users")
			Users::ChangeCurrentGroups ("ldap");
		    else
			Users::ChangeCurrentUsers ("ldap");
		}
	    }
	    Ldap::ldap_modified	= false;
	}
	return nil;
    }
    if (ev_id == `save)
    {
	if (!Users::Modified ())
	{
	    //popup message (user wants to save but there is no modification)
	    Popup::Message (_("Nothing was modified yet."));
	    return nil;
	}
	Wizard::CreateDialog ();
        Wizard::SetDesktopIcon("users");
	any ret = WriteDialog (true);
	Wizard::CloseDialog ();
	if (ret == `next)
	{
	    y2debug ("write ret: %1", ret);
	}
	else
	{
	    y2debug ("write ret: %1", ret);
	    // TODO if write of LDAP fails, should it be read again?
	}
	// LDAP expert options could be available again
	AdvancedButtonsInit ("advanced_buttons");
	return nil;
    }
    if (! is (ev_id, symbol))
    {
	y2error ("strange ev_id value: %1", ev_id);
	return nil;
    }
    return (symbol) ev_id;
}

map <string, map <string, any> > widgets = $[
    "rb_users_groups" : $[
        "widget"		: `custom,
	"custom_widget"		: `ReplacePoint (`id(`rprb), `HBox (
	    `Left (`RadioButtonGroup (`HBox(
		// radiobutton
		`RadioButton (`id ("users"), `opt(`notify), _("&Users")),
		// radiobutton
		`RadioButton (`id ("groups"), `opt(`notify), _("&Groups"))
	    ))),
	    `Right (`Label (`id(`current_filter), "")))
	),
        "init"			: RadioButtonsUsersGroupsInit,
        "handle"		: RadioButtonsUsersGroupsHandler,
	"handle_events"		: [ "users", "groups" ],
	"no_help"		: true
    ],
    "table" : $[
        "widget"		: `custom,
	"custom_widget"		: `VBox (
	    `ReplacePoint (`id(`rptable), `Table (`id("table"), `header(""))),
	    `HBox (
		`PushButton(`id(`new),`opt(`key_F3), Label::AddButton ()),
		`PushButton(`id(`edit), `opt(`key_F4), Label::EditButton ()),
		`PushButton(`id(`delete), `opt(`key_F5),Label::DeleteButton()),
		`HStretch(),
		`ReplacePoint (`id (`rpfilter),
		    // menubutton label
		    `MenuButton (`id(`sets), `opt(`key_F2, `disabled), _("&Set Filter"), [])
		)
	    )
	),
	"init"			: SummaryTableInit,
	"handle"		: SummaryTableHandler,
	"handle_events"		: ["table", `new, `edit, `delete, `next],
	"no_help"		: true
    ],
    "advanced_buttons" : $[
        "widget"		: `custom,
	"custom_widget"		: `HBox (
	    `ReplacePoint (`id (`rpexpert),
		// Menu Buton label
		`MenuButton (`id(`expertlist), _("E&xpert Options..."),[])
	    ),
	    `ReplacePoint (`id (`rpldap), `Empty ())
	),
	"init"			: AdvancedButtonsInit,
	"handle"		: AdvancedButtonsHandler,
	"handle_events"		: [
	    // these are for user/group types
	    "local", "system", "ldap", "nis", "custom",
	    `defaults, `customize, `enc, `auth, `autologinconf, `ldapfilter,
	    `ldapconf, `save
	],
	"no_help"		: true
    ],
];

}//EOF
