View Javadoc

1   package org.appfuse.webapp.pages.components;
2   
3   import com.google.common.collect.Lists;
4   import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapButton;
5   import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapLink;
6   import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
7   import de.agilecoders.wicket.core.markup.html.bootstrap.image.GlyphIconType;
8   import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.Collapsible;
9   import org.apache.wicket.behavior.AttributeAppender;
10  import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
11  import org.apache.wicket.extensions.markup.html.tabs.ITab;
12  import org.apache.wicket.markup.html.WebMarkupContainer;
13  import org.apache.wicket.markup.html.basic.Label;
14  import org.apache.wicket.markup.html.form.*;
15  import org.apache.wicket.markup.html.list.ListItem;
16  import org.apache.wicket.markup.html.list.ListView;
17  import org.apache.wicket.markup.html.panel.Fragment;
18  import org.apache.wicket.markup.html.panel.Panel;
19  import org.apache.wicket.markup.repeater.RepeatingView;
20  import org.apache.wicket.model.*;
21  import org.appfuse.model.Address;
22  import org.appfuse.model.Role;
23  import org.appfuse.model.User;
24  import org.appfuse.webapp.pages.components.country.SimpleCountryDropDownChoice;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  
28  import java.util.List;
29  import java.util.Set;
30  
31  import static java.lang.String.format;
32  
33  /**
34   * Reusable form for editing users.
35   *
36   * Available abstract methods can be used to define specific behavior for different pages.
37   *
38   * TODO: MZA: Where to put (possible) different validators configuration?
39   *
40   * @author Marcin ZajÄ…czkowski, 2011-03-14
41   */
42  public abstract class UserEditPanel extends Panel {
43  
44      protected final Logger log = LoggerFactory.getLogger(getClass());
45  
46      private final List<Role> allAvailableRoles;
47  
48      //TODO: wrap allAvailableRoles into detachable model
49      public UserEditPanel(String id, IModel<User> userModel, List<Role> allAvailableRoles) {
50          super(id, userModel);
51          this.allAvailableRoles = allAvailableRoles;
52      }
53  
54      @Override
55      protected void onInitialize() {
56          super.onInitialize();
57  
58          add(new Label("usernameLabel", new ResourceModel("user.username")));
59          add(new RequiredTextField<String>("username").add(new AutofocusBehavior()).add(new RequiredBehavior()));
60  
61          add(createPasswordGroup());
62  
63          add(new Label("passwordHintLabel", getString("user.passwordHint")));
64          add(new RequiredTextField("passwordHint").add(new RequiredBehavior()));
65  
66          add(new Label("firstNameLabel", getString("user.firstName")));
67          add(new RequiredTextField("firstName").add(new RequiredBehavior()));
68  
69          add(new Label("lastNameLabel", getString("user.lastName")));
70          add(new RequiredTextField("lastName").add(new RequiredBehavior()));
71  
72          add(new Label("emailLabel", getString("user.email")));
73          add(new RequiredTextField("email").add(new RequiredBehavior()));
74  
75          add(new Label("phoneNumberLabel", getString("user.phoneNumber")));
76          add(new TextField("phoneNumber"));
77  
78          add(new Label("websiteLabel", getString("user.website")));
79          add(new RequiredTextField("website").add(new RequiredBehavior()));
80  
81          add(createCollapsibleAddress());
82  
83          PropertyModel<Set<Role>> rolesModel = new PropertyModel<Set<Role>>(getDefaultModel(), "roles");
84          add(createAccountSettingsGroup(rolesModel));
85          add(createDisplayRolesGroup(rolesModel));
86          add(createGroupWithTopButtons());
87      }
88  
89      private WebMarkupContainer createPasswordGroup() {
90          final WebMarkupContainer passwordGroup = new WebMarkupContainer("passwordGroup");
91          passwordGroup.add(new Label("passwordLabel", getString("user.password")));
92          //TODO: setResetPassword() disabled temporarily to allow user edition without entering password each time
93          // See APF-1370
94          passwordGroup.add(new PasswordTextField("password").setResetPassword(false).add(new RequiredBehavior()));
95          passwordGroup.add(new Label("confirmPasswordLabel", getString("user.confirmPassword")));
96          passwordGroup.add(new PasswordTextField("confirmPassword").setResetPassword(false).add(new RequiredBehavior()));
97          return passwordGroup;
98      }
99  
100     private Collapsible createCollapsibleAddress() {
101         final PropertyModel<Address> addressModel = new PropertyModel<Address>(getDefaultModel(), "address");
102         AbstractTab addressTab = new AbstractTab(new ResourceModel("user.address.address")) {
103             @Override
104             public WebMarkupContainer getPanel(String panelId) {
105                 return new AddressFragment(panelId, "address", new CompoundPropertyModel<Address>(addressModel));
106             }
107         };
108         return new Collapsible("collapsibleAddress", Lists.<ITab>newArrayList(addressTab), shouldAddressTabBeCollapsed());
109     }
110 
111     private Model<Integer> shouldAddressTabBeCollapsed() {
112         return isCollapsedAddressTab() ? Model.of(-1) : Model.of(0);
113     }
114 
115     private WebMarkupContainer createAccountSettingsGroup(IModel<Set<Role>> rolesModel) {
116         final WebMarkupContainer accountSettingsGroup = new WebMarkupContainer("accountSettingsGroup");
117         accountSettingsGroup.setVisible(getAccountSettingsGroupVisibility());
118 
119         accountSettingsGroup.add(new CheckBox("enabled"));
120         accountSettingsGroup.add(new CheckBox("accountExpired"));
121         accountSettingsGroup.add(new CheckBox("accountLocked"));
122         accountSettingsGroup.add(new CheckBox("credentialsExpired"));
123         accountSettingsGroup.add(createRolesCheckGroup(rolesModel));
124         return accountSettingsGroup;
125     }
126 
127     private WebMarkupContainer createDisplayRolesGroup(IModel<Set<Role>> rolesModel) {
128         WebMarkupContainer displayRolesGroup = new WebMarkupContainer("displayRolesGroup");
129         displayRolesGroup.setVisible(getDisplayRolesGroupVisibility());
130         displayRolesGroup.add(createRolesRepeater(rolesModel));
131         return displayRolesGroup;
132     }
133 
134     private CheckGroup<Role> createRolesCheckGroup(IModel<Set<Role>> rolesModel) {
135         CheckGroup<Role> rolesCheckGroup = new CheckGroup<Role>("rolesGroup", rolesModel);
136 
137         ListView<Role> roles = new ListView<Role>("roles", allAvailableRoles) {
138             @Override
139             protected void populateItem(ListItem<Role> roleListItem) {
140                 roleListItem.add(new Check<Role>("value", roleListItem.getModel()));
141                 roleListItem.add(new Label("label", roleListItem.getModel()));
142             }
143         }.setReuseItems(true);
144         rolesCheckGroup.add(roles);
145         return rolesCheckGroup;
146     }
147 
148     private RepeatingView createRolesRepeater(IModel<Set<Role>> rolesModel) {
149         RepeatingView rolesRepeater = new RepeatingView("rolesRepeater");
150         Set<Role> roles = rolesModel.getObject();
151         for (Role role : roles) {
152             WebMarkupContainer roleItem = new WebMarkupContainer(rolesRepeater.newChildId());
153             rolesRepeater.add(roleItem);
154             roleItem.add(new Label("roleName", "[" + role.toString() + "]"));
155 //            //MZA: WebMarkupContainer could be removed when ugly hack with " " was used
156 //            rolesRepeater.add(new Label(rolesRepeater.newChildId(), role + " "));
157         }
158         return rolesRepeater;
159     }
160 
161     private WebMarkupContainer createGroupWithTopButtons() {
162         WebMarkupContainer buttonsGroup = createButtonsGroup("buttonsGroup");
163 
164         buttonsGroup.add(new SaveButton("saveButton"));
165         //TODO: MZA: Find a better way to control visibility on the page
166         //TODO: MZA: DeleteButton visible only when from list and not new user
167         buttonsGroup.add(new DeleteButton("deleteButton", generateDeleteConfirmMessage()));
168         buttonsGroup.add(createCancelButton("cancelButton"));
169         return buttonsGroup;
170     }
171 
172     private WebMarkupContainer createButtonsGroup(String groupId) {
173         return new WebMarkupContainer(groupId);
174     }
175 
176     private String generateDeleteConfirmMessage() {
177         return new StringResourceModel(
178                     "delete.confirm", this, null, new Object[]{getString("userList.user")}).getString();
179     }
180 
181     private BootstrapLink<Panel> createCancelButton(String buttonId) {
182         return new BootstrapLink<Panel>(buttonId, Model.<Panel>of(this), Buttons.Type.Default) {
183             @Override
184             public void onClick() {
185                 onCancelButtonSubmit();
186             }
187         }.setIconType(GlyphIconType.remove).setLabel(new ResourceModel("button.cancel"));
188     }
189 
190     public class AddressFragment extends Fragment {
191 
192         public AddressFragment(String id, String markupId, IModel<Address> model) {
193             super(id, markupId, UserEditPanel.this, model);
194         }
195 
196         @Override
197         protected void onInitialize() {
198             super.onInitialize();
199             //moved to onInitilize to prevent:
200             // "Make sure you are not calling Component#getString() inside your Component's constructor."
201             add(new TextField("address"));
202             add(new RequiredTextField("city").add(new RequiredBehavior()));
203             add(new RequiredTextField("province").add(new RequiredBehavior()));
204             add(new RequiredTextField("postalCode").add(new RequiredBehavior()));
205             SimpleCountryDropDownChoice countries = new SimpleCountryDropDownChoice("country",
206                     new PropertyModel<String>(getDefaultModel(), "country"));
207             add(countries.setRequired(true).add(new RequiredBehavior()));
208         }
209     }
210 
211     private final /*static*/ class SaveButton extends BootstrapButton {
212 
213         private SaveButton(String buttonId) {
214             super(buttonId, new ResourceModel("button.save"), Buttons.Type.Primary);
215             setIconType(GlyphIconType.ok);
216         }
217 
218         @Override
219         public void onSubmit() {
220             onSaveButtonSubmit();
221         }
222     }
223 
224     private class DeleteButton extends BootstrapButton {
225         public DeleteButton(String buttonId, String confirmMessage) {
226             super(buttonId, new ResourceModel("button.delete"), Buttons.Type.Danger);
227             setIconType(GlyphIconType.trash);
228             setDefaultFormProcessing(false);
229             setVisible(getDeleteButtonVisibility());
230 
231             add(new AttributeAppender("onclick", format("return confirmMessage('%s')", confirmMessage)));
232         }
233 
234         @Override
235         public void onSubmit() {
236             onDeleteButtonSubmit();
237         }
238     }
239 
240     protected abstract void onSaveButtonSubmit();
241 
242     protected abstract void onDeleteButtonSubmit();
243 
244     protected abstract void onCancelButtonSubmit();
245 
246     protected abstract boolean getAccountSettingsGroupVisibility();
247 
248     protected abstract boolean getDisplayRolesGroupVisibility();
249 
250     protected abstract boolean getDeleteButtonVisibility();
251 
252     protected abstract boolean isCollapsedAddressTab();
253 }