package org.appfuse.webapp.pages.components;
import com.google.common.collect.Lists;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapButton;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapLink;
import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.image.GlyphIconType;
import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.Collapsible;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.*;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.*;
import org.appfuse.model.Address;
import org.appfuse.model.Role;
import org.appfuse.model.User;
import org.appfuse.webapp.pages.components.country.SimpleCountryDropDownChoice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Set;
import static java.lang.String.format;
/**
* Reusable form for editing users.
*
* Available abstract methods can be used to define specific behavior for different pages.
*
* TODO: MZA: Where to put (possible) different validators configuration?
*
* @author Marcin ZajÄ…czkowski, 2011-03-14
*/
public abstract class UserEditPanel extends Panel {
protected final Logger log = LoggerFactory.getLogger(getClass());
private final List<Role> allAvailableRoles;
//TODO: wrap allAvailableRoles into detachable model
public UserEditPanel(String id, IModel<User> userModel, List<Role> allAvailableRoles) {
super(id, userModel);
this.allAvailableRoles = allAvailableRoles;
}
@Override
protected void onInitialize() {
super.onInitialize();
add(new Label("usernameLabel", new ResourceModel("user.username")));
add(new RequiredTextField<String>("username").add(new AutofocusBehavior()).add(new RequiredBehavior()));
add(createPasswordGroup());
add(new Label("passwordHintLabel", getString("user.passwordHint")));
add(new RequiredTextField("passwordHint").add(new RequiredBehavior()));
add(new Label("firstNameLabel", getString("user.firstName")));
add(new RequiredTextField("firstName").add(new RequiredBehavior()));
add(new Label("lastNameLabel", getString("user.lastName")));
add(new RequiredTextField("lastName").add(new RequiredBehavior()));
add(new Label("emailLabel", getString("user.email")));
add(new RequiredTextField("email").add(new RequiredBehavior()));
add(new Label("phoneNumberLabel", getString("user.phoneNumber")));
add(new TextField("phoneNumber"));
add(new Label("websiteLabel", getString("user.website")));
add(new RequiredTextField("website").add(new RequiredBehavior()));
add(createCollapsibleAddress());
PropertyModel<Set<Role>> rolesModel = new PropertyModel<Set<Role>>(getDefaultModel(), "roles");
add(createAccountSettingsGroup(rolesModel));
add(createDisplayRolesGroup(rolesModel));
add(createGroupWithTopButtons());
}
private WebMarkupContainer createPasswordGroup() {
final WebMarkupContainer passwordGroup = new WebMarkupContainer("passwordGroup");
passwordGroup.add(new Label("passwordLabel", getString("user.password")));
//TODO: setResetPassword() disabled temporarily to allow user edition without entering password each time
// See APF-1370
passwordGroup.add(new PasswordTextField("password").setResetPassword(false).add(new RequiredBehavior()));
passwordGroup.add(new Label("confirmPasswordLabel", getString("user.confirmPassword")));
passwordGroup.add(new PasswordTextField("confirmPassword").setResetPassword(false).add(new RequiredBehavior()));
return passwordGroup;
}
private Collapsible createCollapsibleAddress() {
final PropertyModel<Address> addressModel = new PropertyModel<Address>(getDefaultModel(), "address");
AbstractTab addressTab = new AbstractTab(new ResourceModel("user.address.address")) {
@Override
public WebMarkupContainer getPanel(String panelId) {
return new AddressFragment(panelId, "address", new CompoundPropertyModel<Address>(addressModel));
}
};
return new Collapsible("collapsibleAddress", Lists.<ITab>newArrayList(addressTab), shouldAddressTabBeCollapsed());
}
private Model<Integer> shouldAddressTabBeCollapsed() {
return isCollapsedAddressTab() ? Model.of(-1) : Model.of(0);
}
private WebMarkupContainer createAccountSettingsGroup(IModel<Set<Role>> rolesModel) {
final WebMarkupContainer accountSettingsGroup = new WebMarkupContainer("accountSettingsGroup");
accountSettingsGroup.setVisible(getAccountSettingsGroupVisibility());
accountSettingsGroup.add(new CheckBox("enabled"));
accountSettingsGroup.add(new CheckBox("accountExpired"));
accountSettingsGroup.add(new CheckBox("accountLocked"));
accountSettingsGroup.add(new CheckBox("credentialsExpired"));
accountSettingsGroup.add(createRolesCheckGroup(rolesModel));
return accountSettingsGroup;
}
private WebMarkupContainer createDisplayRolesGroup(IModel<Set<Role>> rolesModel) {
WebMarkupContainer displayRolesGroup = new WebMarkupContainer("displayRolesGroup");
displayRolesGroup.setVisible(getDisplayRolesGroupVisibility());
displayRolesGroup.add(createRolesRepeater(rolesModel));
return displayRolesGroup;
}
private CheckGroup<Role> createRolesCheckGroup(IModel<Set<Role>> rolesModel) {
CheckGroup<Role> rolesCheckGroup = new CheckGroup<Role>("rolesGroup", rolesModel);
ListView<Role> roles = new ListView<Role>("roles", allAvailableRoles) {
@Override
protected void populateItem(ListItem<Role> roleListItem) {
roleListItem.add(new Check<Role>("value", roleListItem.getModel()));
roleListItem.add(new Label("label", roleListItem.getModel()));
}
}.setReuseItems(true);
rolesCheckGroup.add(roles);
return rolesCheckGroup;
}
private RepeatingView createRolesRepeater(IModel<Set<Role>> rolesModel) {
RepeatingView rolesRepeater = new RepeatingView("rolesRepeater");
Set<Role> roles = rolesModel.getObject();
for (Role role : roles) {
WebMarkupContainer roleItem = new WebMarkupContainer(rolesRepeater.newChildId());
rolesRepeater.add(roleItem);
roleItem.add(new Label("roleName", "[" + role.toString() + "]"));
// //MZA: WebMarkupContainer could be removed when ugly hack with " " was used
// rolesRepeater.add(new Label(rolesRepeater.newChildId(), role + " "));
}
return rolesRepeater;
}
private WebMarkupContainer createGroupWithTopButtons() {
WebMarkupContainer buttonsGroup = createButtonsGroup("buttonsGroup");
buttonsGroup.add(new SaveButton("saveButton"));
//TODO: MZA: Find a better way to control visibility on the page
//TODO: MZA: DeleteButton visible only when from list and not new user
buttonsGroup.add(new DeleteButton("deleteButton", generateDeleteConfirmMessage()));
buttonsGroup.add(createCancelButton("cancelButton"));
return buttonsGroup;
}
private WebMarkupContainer createButtonsGroup(String groupId) {
return new WebMarkupContainer(groupId);
}
private String generateDeleteConfirmMessage() {
return new StringResourceModel(
"delete.confirm", this, null, new Object[]{getString("userList.user")}).getString();
}
private BootstrapLink<Panel> createCancelButton(String buttonId) {
return new BootstrapLink<Panel>(buttonId, Model.<Panel>of(this), Buttons.Type.Default) {
@Override
public void onClick() {
onCancelButtonSubmit();
}
}.setIconType(GlyphIconType.remove).setLabel(new ResourceModel("button.cancel"));
}
public class AddressFragment extends Fragment {
public AddressFragment(String id, String markupId, IModel<Address> model) {
super(id, markupId, UserEditPanel.this, model);
}
@Override
protected void onInitialize() {
super.onInitialize();
//moved to onInitilize to prevent:
// "Make sure you are not calling Component#getString() inside your Component's constructor."
add(new TextField("address"));
add(new RequiredTextField("city").add(new RequiredBehavior()));
add(new RequiredTextField("province").add(new RequiredBehavior()));
add(new RequiredTextField("postalCode").add(new RequiredBehavior()));
SimpleCountryDropDownChoice countries = new SimpleCountryDropDownChoice("country",
new PropertyModel<String>(getDefaultModel(), "country"));
add(countries.setRequired(true).add(new RequiredBehavior()));
}
}
private final /*static*/ class SaveButton extends BootstrapButton {
private SaveButton(String buttonId) {
super(buttonId, new ResourceModel("button.save"), Buttons.Type.Primary);
setIconType(GlyphIconType.ok);
}
@Override
public void onSubmit() {
onSaveButtonSubmit();
}
}
private class DeleteButton extends BootstrapButton {
public DeleteButton(String buttonId, String confirmMessage) {
super(buttonId, new ResourceModel("button.delete"), Buttons.Type.Danger);
setIconType(GlyphIconType.trash);
setDefaultFormProcessing(false);
setVisible(getDeleteButtonVisibility());
add(new AttributeAppender("onclick", format("return confirmMessage('%s')", confirmMessage)));
}
@Override
public void onSubmit() {
onDeleteButtonSubmit();
}
}
protected abstract void onSaveButtonSubmit();
protected abstract void onDeleteButtonSubmit();
protected abstract void onCancelButtonSubmit();
protected abstract boolean getAccountSettingsGroupVisibility();
protected abstract boolean getDisplayRolesGroupVisibility();
protected abstract boolean getDeleteButtonVisibility();
protected abstract boolean isCollapsedAddressTab();
}