001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.extensions.wizard; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Set; 024 025import org.apache.wicket.Component; 026import org.apache.wicket.extensions.wizard.dynamic.DynamicWizardModel; 027import org.apache.wicket.markup.html.basic.Label; 028import org.apache.wicket.markup.html.form.Form; 029import org.apache.wicket.markup.html.form.FormComponent; 030import org.apache.wicket.markup.html.form.validation.IFormValidator; 031import org.apache.wicket.markup.html.panel.Panel; 032import org.apache.wicket.model.CompoundPropertyModel; 033import org.apache.wicket.model.IModel; 034import org.apache.wicket.model.Model; 035 036 037/** 038 * default implementation of {@link IWizardStep}. It is also a panel, which is used as the view 039 * component. 040 * 041 * <p> 042 * And example of a custom step with a panel follows. 043 * 044 * Java (defined e.g. in class x.NewUserWizard): 045 * 046 * <pre> 047 * private final class UserNameStep extends WizardStep 048 * { 049 * public UserNameStep() 050 * { 051 * super(new ResourceModel("username.title"), new ResourceModel("username.summary")); 052 * add(new RequiredTextField("user.userName")); 053 * add(new RequiredTextField("user.email").add(EmailAddressValidator.getInstance())); 054 * } 055 * } 056 * </pre> 057 * 058 * HTML (defined in e.g. file x/NewUserWizard$UserNameStep.html): 059 * 060 * <pre> 061 * <wicket:panel> 062 * <table> 063 * <tr> 064 * <td><wicket:message key="username">Username</wicket:message></td> 065 * <td><input type="text" wicket:id="user.userName" /></td> 066 * </tr> 067 * <tr> 068 * <td><wicket:message key="email">Email Address</wicket:message></td> 069 * <td><input type="text" wicket:id="user.email" /></td> 070 * </tr> 071 * </table> 072 * </wicket:panel> 073 * </pre> 074 * 075 * </p> 076 * 077 * @author Eelco Hillenius 078 */ 079public class WizardStep extends Panel implements IWizardStep 080{ 081 /** 082 * Wraps form validators for this step such that they are only executed when this step is 083 * active. 084 */ 085 private final class FormValidatorWrapper implements IFormValidator 086 { 087 088 private static final long serialVersionUID = 1L; 089 090 private final List<IFormValidator> validators = new ArrayList<>(); 091 092 /** 093 * Adds a form validator. 094 * 095 * @param validator 096 * The validator to add 097 */ 098 public final void add(final IFormValidator validator) 099 { 100 validators.add(validator); 101 } 102 103 /** 104 * @see org.apache.wicket.markup.html.form.validation.IFormValidator#getDependentFormComponents() 105 */ 106 @Override 107 public FormComponent<?>[] getDependentFormComponents() 108 { 109 if (isActiveStep()) 110 { 111 Set<Component> components = new HashSet<>(); 112 for (IFormValidator v : validators) 113 { 114 FormComponent<?>[] dependentComponents = v.getDependentFormComponents(); 115 if (dependentComponents != null) 116 { 117 int len = dependentComponents.length; 118 components.addAll(Arrays.asList(dependentComponents).subList(0, len)); 119 } 120 } 121 return components.toArray(new FormComponent[components.size()]); 122 } 123 return null; 124 } 125 126 /** 127 * @see org.apache.wicket.markup.html.form.validation.IFormValidator#validate(org.apache.wicket.markup.html.form.Form) 128 */ 129 @Override 130 public void validate(final Form<?> form) 131 { 132 if (isActiveStep()) 133 { 134 for (IFormValidator v : validators) 135 { 136 v.validate(form); 137 } 138 } 139 } 140 141 /** 142 * @return whether the step this wrapper is part of is the current step 143 */ 144 private boolean isActiveStep() 145 { 146 return (wizardModel.getActiveStep().equals(WizardStep.this)); 147 } 148 } 149 150 /** 151 * Default header for wizards. 152 */ 153 private final class Header extends Panel 154 { 155 private static final long serialVersionUID = 1L; 156 157 /** 158 * Construct. 159 * 160 * @param id 161 * The component id 162 * @param wizard 163 * The containing wizard 164 */ 165 public Header(final String id, final IWizard wizard) 166 { 167 super(id); 168 setDefaultModel(new CompoundPropertyModel<>(wizard)); 169 add(new Label("title", WizardStep.this::getTitle)); 170 add(new Label("summary", WizardStep.this::getSummary)); 171 } 172 } 173 174 private static final long serialVersionUID = 1L; 175 176 /** 177 * Marks this step as being fully configured. Only when this is <tt>true</tt> can the wizard 178 * progress. True by default as that works best with normal forms. Clients can set this to false 179 * if some intermediate step, like a file upload, needs to be completed before the wizard may 180 * progress. 181 */ 182 private boolean complete = true; 183 184 /** 185 * A summary of this step, or some usage advice. 186 */ 187 private IModel<String> summary; 188 189 /** 190 * The title of this step. 191 */ 192 private IModel<String> title; 193 194 /** 195 * The wizard model. 196 */ 197 private IWizardModel wizardModel; 198 199 /** 200 * The wrapper of {@link IFormValidator}s for this step. 201 */ 202 private FormValidatorWrapper formValidatorWrapper = new FormValidatorWrapper(); 203 204 /** 205 * Construct without a title and a summary. Useful for when you provide a custom header by 206 * overiding {@link #getHeader(String, Component, IWizard)}. 207 */ 208 public WizardStep() 209 { 210 super(Wizard.VIEW_ID); 211 } 212 213 /** 214 * Creates a new step with the specified title and summary. The title and summary are displayed 215 * in the wizard title block while this step is active. 216 * 217 * @param title 218 * the title of this step. 219 * @param summary 220 * a brief summary of this step or some usage guidelines. 221 */ 222 public WizardStep(final IModel<String> title, final IModel<String> summary) 223 { 224 this(title, summary, null); 225 } 226 227 /** 228 * Creates a new step with the specified title and summary. The title and summary are displayed 229 * in the wizard title block while this step is active. 230 * 231 * @param title 232 * the title of this step. 233 * @param summary 234 * a brief summary of this step or some usage guidelines. 235 * @param model 236 * Any model which is to be used for this step 237 */ 238 public WizardStep(final IModel<String> title, final IModel<String> summary, 239 final IModel<?> model) 240 { 241 super(Wizard.VIEW_ID, model); 242 243 this.title = wrap(title); 244 this.summary = wrap(summary); 245 } 246 247 /** 248 * Creates a new step with the specified title and summary. The title and summary are displayed 249 * in the wizard title block while this step is active. 250 * 251 * @param title 252 * the title of this step. 253 * @param summary 254 * a brief summary of this step or some usage guidelines. 255 */ 256 public WizardStep(final String title, final String summary) 257 { 258 this(title, summary, null); 259 } 260 261 /** 262 * Creates a new step with the specified title and summary. The title and summary are displayed 263 * in the wizard title block while this step is active. 264 * 265 * @param title 266 * the title of this step. 267 * @param summary 268 * a brief summary of this step or some usage guidelines. 269 * @param model 270 * Any model which is to be used for this step 271 */ 272 public WizardStep(final String title, final String summary, final IModel<?> model) 273 { 274 this(new Model<String>(title), new Model<>(summary), model); 275 } 276 277 /** 278 * Adds a form validator. 279 * 280 * @param validator 281 */ 282 public final void add(final IFormValidator validator) 283 { 284 formValidatorWrapper.add(validator); 285 } 286 287 /** 288 * @see org.apache.wicket.extensions.wizard.IWizardStep#applyState() 289 */ 290 @Override 291 public void applyState() 292 { 293 } 294 295 /** 296 * @see org.apache.wicket.extensions.wizard.IWizardStep#getHeader(java.lang.String, 297 * org.apache.wicket.Component, org.apache.wicket.extensions.wizard.IWizard) 298 */ 299 @Override 300 public Component getHeader(final String id, final Component parent, final IWizard wizard) 301 { 302 return new Header(id, wizard); 303 } 304 305 /** 306 * Gets the summary of this step. This will be displayed in the title of the wizard while this 307 * step is active. The summary is typically an overview of the step or some usage guidelines for 308 * the user. 309 * 310 * @return the summary of this step. 311 */ 312 public String getSummary() 313 { 314 return (summary != null) ? summary.getObject() : null; 315 } 316 317 /** 318 * Gets the title of this step. 319 * 320 * @return the title of this step. 321 */ 322 public String getTitle() 323 { 324 return (title != null) ? title.getObject() : null; 325 } 326 327 /** 328 * @see org.apache.wicket.extensions.wizard.IWizardStep#getView(java.lang.String, 329 * org.apache.wicket.Component, org.apache.wicket.extensions.wizard.IWizard) 330 */ 331 @Override 332 public Component getView(final String id, final Component parent, final IWizard wizard) 333 { 334 return this; 335 } 336 337 /** 338 * Called to initialize the step. When this method is called depends on the kind of wizard model 339 * that is used. 340 * 341 * The {@link WizardModel static wizard model} knows all the steps upfront and initializes themm 342 * when starting up. This method will be called when the wizard is {@link #init(IWizardModel) 343 * initializing}. 344 * 345 * The {@link DynamicWizardModel dynamic wizard model} initializes steps every time they are 346 * encountered. 347 * 348 * This method sets the wizard model and then calls template method 349 * {@link #onInit(IWizardModel)} 350 * 351 * @param wizardModel 352 * the model to which the step belongs. 353 */ 354 @Override 355 public final void init(final IWizardModel wizardModel) 356 { 357 this.wizardModel = wizardModel; 358 onInit(wizardModel); 359 } 360 361 /** 362 * Checks if this step is compete. This method should return true if the wizard can proceed to 363 * the next step. This property is bound and changes can be made at anytime by calling 364 * {@link #setComplete(boolean)} . 365 * 366 * @return <tt>true</tt> if the wizard can proceed from this step, <tt>false</tt> otherwise. 367 * @see #setComplete 368 */ 369 @Override 370 public boolean isComplete() 371 { 372 return complete; 373 } 374 375 /** 376 * Marks this step as compete. The wizard will not be able to proceed from this step until this 377 * property is configured to <tt>true</tt>. 378 * 379 * @param complete 380 * <tt>true</tt> to allow the wizard to proceed, <tt>false</tt> otherwise. 381 * @see #isComplete 382 */ 383 public void setComplete(final boolean complete) 384 { 385 this.complete = complete; 386 } 387 388 /** 389 * Sets summary. 390 * 391 * @param summary 392 * summary 393 */ 394 public void setSummaryModel(final IModel<String> summary) 395 { 396 this.summary = wrap(summary); 397 } 398 399 /** 400 * Sets title. 401 * 402 * @param title 403 * title 404 */ 405 public void setTitleModel(final IModel<String> title) 406 { 407 this.title = wrap(title); 408 } 409 410 /** 411 * @see org.apache.wicket.Component#detachModel() 412 */ 413 @Override 414 protected void detachModel() 415 { 416 super.detachModel(); 417 if (title != null) 418 { 419 title.detach(); 420 } 421 if (summary != null) 422 { 423 summary.detach(); 424 } 425 } 426 427 @Override 428 protected void onInitialize() 429 { 430 super.onInitialize(); 431 432 Form<?> form = findParent(Form.class); 433 form.add(formValidatorWrapper); 434 } 435 436 /** 437 * Template method that is called when the step is being initialized. 438 * 439 * @param wizardModel 440 * @see #init(IWizardModel) 441 */ 442 protected void onInit(final IWizardModel wizardModel) 443 { 444 } 445 446 /** 447 * @return wizard model 448 */ 449 public IWizardModel getWizardModel() 450 { 451 return wizardModel; 452 } 453}