setting all protected methods on WizardStepPage as virtual
[castle.git] / MonoRail / Castle.MonoRail.Framework / WizardStepPage.cs
bloba018f4821d2db3dbfb374b72d0122365c2130f5b
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.Framework
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Reflection;
21 using Castle.Components.Binder;
22 using Castle.MonoRail.Framework.Helpers;
23 using Castle.MonoRail.Framework.Internal;
24 using Services;
26 /// <summary>
27 /// Represents a wizard step. In essence it is a controller, but with some subtle differences.
28 /// See the remarks for more information.
29 /// </summary>
30 ///
31 /// <seealso xref="WizardActionProvider"/>
32 /// <seealso xref="IWizardController"/>
33 ///
34 /// <remarks>
35 /// Implementors can optionally override <see cref="WizardStepPage.ActionName"/>
36 /// to customize the accessible action name and
37 /// <see cref="WizardStepPage.RenderWizardView"/> in order to define which view
38 /// should be used (defaults to the step name)
39 ///
40 /// <para>
41 /// Please note that an step might have actions as well, but it follows a different
42 /// convention to be accessed. You must use the wizard controller name, slash, the
43 /// step name, hifen, the action name. For example <c>/MyWizard/AddressInformation-GetCountries.rails</c>
44 /// Which would access the following action
45 /// </para>
46 ///
47 /// <code>
48 /// public class AddressInformation : WizardStepPage
49 /// {
50 /// public void GetCountries()
51 /// {
52 /// ...
53 /// }
54 /// }
55 /// </code>
56 /// <para>
57 /// Note that the RedirectToAction will always send to an internal action, so you should
58 /// omit the controller name for that.
59 /// </para>
60 ///
61 /// <para>
62 /// You can use a family of redirect methods to go back and forward on the wizard's
63 /// steps.
64 /// </para>
65 /// </remarks>
66 public abstract class WizardStepPage : SmartDispatcherController, IWizardStepPage
68 #region Fields
70 private IWizardController wizardParentController;
71 private IControllerContext wizardcontrollerContext;
73 #endregion
75 #region Constructors
77 /// <summary>
78 /// Initializes a new instance of the <see cref="WizardStepPage"/> class.
79 /// </summary>
80 public WizardStepPage()
84 /// <summary>
85 /// Initializes a new instance of the <see cref="WizardStepPage"/> class.
86 /// </summary>
87 /// <param name="binder">The binder.</param>
88 public WizardStepPage(IDataBinder binder) : base(binder)
92 #endregion
94 #region Useful Properties
96 /// <summary>
97 /// Gets the wizard controller.
98 /// </summary>
99 /// <value>The wizard controller.</value>
100 public IWizardController WizardController
102 get { return wizardParentController; }
103 set { wizardParentController = value; }
106 /// <summary>
107 /// Gets the controller context.
108 /// </summary>
109 /// <value>The controller context.</value>
110 public IControllerContext WizardControllerContext
112 get { return wizardcontrollerContext; }
113 set { wizardcontrollerContext = value; }
116 #endregion
118 #region Core Lifecycle methods
120 /// <summary>
121 /// Invoked when the wizard is being access from the start
122 /// action. Implementors should perform session clean up (if
123 /// they actually use the session) to avoid stale data on forms.
124 /// </summary>
125 public virtual void Reset()
129 /// <summary>
130 /// Returns the action name that will be used
131 /// to represent this step.
132 /// </summary>
133 public virtual String ActionName
137 Type thisType = GetType();
139 // Hack fix for "dynamic proxied" controllers
140 if (thisType.Assembly.FullName.StartsWith("DynamicAssemblyProxyGen") ||
141 thisType.Assembly.FullName.StartsWith("DynamicProxyGenAssembly2"))
143 return thisType.BaseType.Name;
146 return GetType().Name;
150 /// <summary>
151 /// Used to decide on which view to render.
152 /// </summary>
153 protected internal virtual void RenderWizardView()
155 RenderView(ActionName);
158 /// <summary>
159 /// Allow the step to assert some condition
160 /// before being accessed. Returning <c>false</c>
161 /// prevents the step from being processed but
162 /// before doing that you must send a redirect.
163 /// </summary>
164 /// <returns></returns>
165 public virtual bool IsPreConditionSatisfied(IEngineContext context)
167 return true;
170 /// <summary>
171 /// Uses a simple heuristic to select the best method -- especially in the
172 /// case of method overloads.
173 /// </summary>
174 /// <param name="action">The action name</param>
175 /// <param name="actions">The avaliable actions</param>
176 /// <param name="request">The request instance</param>
177 /// <param name="actionArgs">The custom arguments for the action</param>
178 /// <param name="actionType">Type of the action.</param>
179 /// <returns></returns>
180 protected override MethodInfo SelectMethod(string action, IDictionary actions, IRequest request, IDictionary<string, object> actionArgs, ActionType actionType)
182 if (action == "RenderWizardView")
184 return typeof(WizardStepPage).GetMethod("RenderWizardView", BindingFlags.Instance | BindingFlags.NonPublic);
186 else
188 return base.SelectMethod(action, actions, request, actionArgs, actionType);
192 #endregion
194 /// <summary>
195 /// Override takes care of selecting the wizard parent layout as default
196 /// layout if no layout is attached to the step
197 /// </summary>
198 protected override void ResolveLayout()
200 base.ResolveLayout();
202 if (LayoutName == null)
204 LayoutNames = wizardcontrollerContext.LayoutNames;
208 #region DoNavigate and Redirects
210 /// <summary>
211 /// Navigates within the wizard steps using optionally a form parameter
212 /// to dictate to where it should go.
213 /// </summary>
214 /// <remarks>
215 /// By default this will invoke <see cref="RedirectToNextStep(IDictionary)"/>
216 /// however you can send a field form <c>navigate.to</c> to customize this.
217 /// The possible values for <c>navigate.to</c> are:
218 /// <list type="bullet">
219 /// <item><term>previous</term>
220 /// <description>Invokes <see cref="RedirectToPreviousStep()"/></description></item>
221 /// <item><term>first</term>
222 /// <description>Invokes <see cref="RedirectToFirstStep()"/></description></item>
223 /// <item><term>step name</term>
224 /// <description>A custom step name to navigate</description></item>
225 /// </list>
226 /// </remarks>
227 protected virtual void DoNavigate()
229 DoNavigate((IDictionary) null);
232 /// <summary>
233 /// Navigates within the wizard steps using optionally a form parameter
234 /// to dictate to where it should go.
235 /// </summary>
236 /// <remarks>
237 /// By default this will invoke <see cref="RedirectToNextStep(IDictionary)"/>
238 /// however you can send a field form <c>navigate.to</c> to customize this.
239 /// The possible values for <c>navigate.to</c> are:
240 /// <list type="bullet">
241 /// <item><term>previous</term>
242 /// <description>Invokes <see cref="RedirectToPreviousStep()"/></description></item>
243 /// <item><term>first</term>
244 /// <description>Invokes <see cref="RedirectToFirstStep()"/></description></item>
245 /// <item><term>step name</term>
246 /// <description>A custom step name to navigate</description></item>
247 /// </list>
248 /// </remarks>
249 /// <param name="queryStringParameters">Query string parameters to be on the URL</param>
250 protected virtual void DoNavigate(params String[] queryStringParameters)
252 DoNavigate(DictHelper.Create(queryStringParameters));
255 /// <summary>
256 /// Navigates within the wizard steps using optionally a form parameter
257 /// to dictate to where it should go.
258 /// </summary>
259 /// <remarks>
260 /// By default this will invoke <see cref="RedirectToNextStep(IDictionary)"/>
261 /// however you can send a field form <c>navigate.to</c> to customize this.
262 /// The possible values for <c>navigate.to</c> are:
263 /// <list type="bullet">
264 /// <item><term>previous</term>
265 /// <description>Invokes <see cref="RedirectToPreviousStep()"/></description></item>
266 /// <item><term>first</term>
267 /// <description>Invokes <see cref="RedirectToFirstStep()"/></description></item>
268 /// <item><term>step name</term>
269 /// <description>A custom step name to navigate</description></item>
270 /// </list>
271 /// </remarks>
272 /// <param name="queryStringParameters">Query string parameters to be on the URL</param>
273 protected virtual void DoNavigate(IDictionary queryStringParameters)
275 string uriPrefix = "uri:";
277 String navigateTo = Params["navigate.to"];
279 if (navigateTo == "previous")
281 RedirectToPreviousStep(queryStringParameters);
283 else if (navigateTo == null || navigateTo == String.Empty || navigateTo == "next")
285 RedirectToNextStep(queryStringParameters);
287 else if (navigateTo.StartsWith(uriPrefix))
289 RedirectToUrl(navigateTo.Substring(uriPrefix.Length), queryStringParameters);
291 else if (navigateTo == "first")
293 RedirectToFirstStep(queryStringParameters);
295 else
297 RedirectToStep(navigateTo, queryStringParameters);
301 /// <summary>
302 /// Sends a redirect to the next wizard step (if it exists)
303 /// </summary>
304 /// <exception cref="MonoRailException">if no further step exists</exception>
305 protected virtual void RedirectToNextStep()
307 RedirectToNextStep((IDictionary) null);
310 /// <summary>
311 /// Sends a redirect to the next wizard step (if it exists)
312 /// </summary>
313 /// <exception cref="MonoRailException">if no further step exists</exception>
314 protected virtual void RedirectToNextStep(params String[] queryStringParameters)
316 RedirectToNextStep(DictHelper.Create(queryStringParameters));
319 /// <summary>
320 /// Sends a redirect to the next wizard step (if it exists)
321 /// </summary>
322 /// <exception cref="MonoRailException">if no further step exists</exception>
323 protected virtual void RedirectToNextStep(IDictionary queryStringParameters)
325 String wizardName = WizardUtils.ConstructWizardNamespace(ControllerContext);
327 int currentIndex = (int) Context.Session[wizardName + "currentstepindex"];
329 IList stepList = (IList) Context.Items["wizard.step.list"];
331 if ((currentIndex + 1) < stepList.Count)
333 int nextStepIndex = currentIndex + 1;
335 String nextStep = (String) stepList[nextStepIndex];
337 WizardUtils.RegisterCurrentStepInfo(Context, wizardParentController, ControllerContext, nextStepIndex, nextStep);
339 InternalRedirectToStep(Context, nextStepIndex, nextStep, queryStringParameters);
341 else
343 throw new MonoRailException("There is no next step available");
347 /// <summary>
348 /// Sends a redirect to the previous wizard step
349 /// </summary>
350 /// <exception cref="MonoRailException">
351 /// if no previous step exists (ie. already in the first one)</exception>
352 protected virtual void RedirectToPreviousStep()
354 RedirectToPreviousStep((IDictionary) null);
357 /// <summary>
358 /// Sends a redirect to the previous wizard step
359 /// </summary>
360 /// <exception cref="MonoRailException">
361 /// if no previous step exists (ie. already in the first one)</exception>
362 protected virtual void RedirectToPreviousStep(params String[] queryStringParameters)
364 RedirectToPreviousStep(DictHelper.Create(queryStringParameters));
367 /// <summary>
368 /// Sends a redirect to the previous wizard step
369 /// </summary>
370 /// <exception cref="MonoRailException">
371 /// if no previous step exists (ie. already in the first one)</exception>
372 protected virtual void RedirectToPreviousStep(IDictionary queryStringParameters)
374 String wizardName = WizardUtils.ConstructWizardNamespace(wizardcontrollerContext);
376 int currentIndex = (int) Context.Session[wizardName + "currentstepindex"];
378 IList stepList = (IList) Context.Items["wizard.step.list"];
380 if ((currentIndex - 1) >= 0)
382 int prevStepIndex = currentIndex - 1;
384 String prevStep = (String) stepList[prevStepIndex];
386 InternalRedirectToStep(Context, prevStepIndex, prevStep, queryStringParameters);
388 else
390 throw new MonoRailException("There is no previous step available");
394 /// <summary>
395 /// Sends a redirect to the first wizard step
396 /// </summary>
397 protected virtual void RedirectToFirstStep()
399 RedirectToFirstStep((IDictionary) null);
402 /// <summary>
403 /// Sends a redirect to the first wizard step
404 /// </summary>
405 protected virtual void RedirectToFirstStep(params String[] queryStringParameters)
407 RedirectToFirstStep(DictHelper.Create(queryStringParameters));
410 /// <summary>
411 /// Sends a redirect to the first wizard step
412 /// </summary>
413 protected virtual void RedirectToFirstStep(IDictionary queryStringParameters)
415 IList stepList = (IList) Context.Items["wizard.step.list"];
417 String firstStep = (String) stepList[0];
419 InternalRedirectToStep(Context, 0, firstStep, queryStringParameters);
422 /// <summary>
423 /// Sends a redirect to a custom step (that must exists)
424 /// </summary>
425 protected virtual bool RedirectToStep(String stepName)
427 return RedirectToStep(stepName, (IDictionary) null);
430 /// <summary>
431 /// Sends a redirect to a custom step (that must exists)
432 /// </summary>
433 protected virtual bool RedirectToStep(String stepName, params String[] queryStringParameters)
435 return RedirectToStep(stepName, DictHelper.Create(queryStringParameters));
438 /// <summary>
439 /// Sends a redirect to a custom step (that must exists)
440 /// </summary>
441 protected virtual bool RedirectToStep(String stepName, IDictionary queryStringParameters)
443 IList stepList = (IList) Context.Items["wizard.step.list"];
445 for(int index = 0; index < stepList.Count; index++)
447 String curStep = (String) stepList[index];
449 if (curStep == stepName)
451 InternalRedirectToStep(Context, index, stepName, queryStringParameters);
452 return true;
456 return false;
459 /// <summary>
460 /// For a wizard step, an internal action will always be named
461 /// with the controller name as a prefix , plus an hifen and finally
462 /// the action name. This implementation does exactly that.
463 /// </summary>
464 /// <param name="action">Raw action name</param>
465 /// <returns>Properly formatted action name</returns>
466 protected override String TransformActionName(String action)
468 return base.TransformActionName(ActionName + "-" + action);
471 private void InternalRedirectToStep(IEngineContext engineContext, int stepIndex, String step,
472 IDictionary queryStringParameters)
474 WizardUtils.RegisterCurrentStepInfo(engineContext, wizardParentController, wizardcontrollerContext, stepIndex, step);
476 // Does this support areas?
478 if (queryStringParameters != null && queryStringParameters.Count != 0)
480 Redirect(WizardControllerContext.AreaName, wizardcontrollerContext.Name, step, queryStringParameters);
482 else if (Context.Request.QueryString.HasKeys())
484 // We need to preserve any attribute from the QueryString
485 // for example in case the url has an Id
487 Redirect(WizardControllerContext.AreaName, wizardcontrollerContext.Name, step, Query);
489 else
491 Redirect(WizardControllerContext.AreaName, wizardcontrollerContext.Name, step);
495 #endregion