Fix the build.
[castle.git] / MonoRail / Castle.MonoRail.Framework / WizardStepPage.cs
blob90faa6fcf688c4e05eb87ceab9c9dbefe91f035d
1 // Copyright 2004-2007 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.Reflection;
21 using Castle.Components.Binder;
22 using Castle.MonoRail.Framework.Helpers;
23 using Castle.MonoRail.Framework.Internal;
25 /// <summary>
26 /// Represents a wizard step. In essence it is a controller, but with some subtle differences.
27 /// See the remarks for more information.
28 /// </summary>
29 ///
30 /// <seealso cref="WizardActionProvider"/>
31 /// <seealso cref="IWizardController"/>
32 ///
33 /// <remarks>
34 /// Implementors can optionally override <see cref="WizardStepPage.ActionName"/>
35 /// to customize the accessible action name and
36 /// <see cref="WizardStepPage.RenderWizardView"/> in order to define which view
37 /// should be used (defaults to the step name)
38 ///
39 /// <para>
40 /// Please note that an step might have actions as well, but it follows a different
41 /// convention to be accessed. You must use the wizard controller name, slash, the
42 /// step name, hifen, the action name. For example <c>/MyWizard/AddressInformation-GetCountries.rails</c>
43 /// Which would access the following action
44 /// </para>
45 ///
46 /// <code>
47 /// public class AddressInformation : WizardStepPage
48 /// {
49 /// public void GetCountries()
50 /// {
51 /// ...
52 /// }
53 /// }
54 /// </code>
55 /// <para>
56 /// Note that the RedirectToAction will always send to an internal action, so you should
57 /// omit the controller name for that.
58 /// </para>
59 ///
60 /// <para>
61 /// You can use a family of redirect methods to go back and forward on the wizard's
62 /// steps.
63 /// </para>
64 /// </remarks>
65 public abstract class WizardStepPage : SmartDispatcherController
67 #region Fields
69 private Controller _wizardcontroller;
71 #endregion
73 #region Constructors
75 /// <summary>
76 /// Initializes a new instance of the <see cref="WizardStepPage"/> class.
77 /// </summary>
78 public WizardStepPage()
82 /// <summary>
83 /// Initializes a new instance of the <see cref="WizardStepPage"/> class.
84 /// </summary>
85 /// <param name="binder">The binder.</param>
86 public WizardStepPage(IDataBinder binder) : base(binder)
90 #endregion
92 #region Useful Properties
94 /// <summary>
95 /// Gets the wizard controller.
96 /// </summary>
97 /// <value>The wizard controller.</value>
98 public Controller WizardController
100 get { return _wizardcontroller; }
103 #endregion
105 #region Core Lifecycle methods
107 /// <summary>
108 /// Invoked by <see cref="WizardActionProvider"/>.
109 /// </summary>
110 /// <remarks>
111 /// This can be overriden but it's important to invoke the base
112 /// implementation.
113 /// </remarks>
114 /// <param name="wizardController"></param>
115 protected internal virtual void Initialize(Controller wizardController)
117 _wizardcontroller = wizardController;
119 PropertyBag = wizardController.PropertyBag;
122 /// <summary>
123 /// Invoked when the wizard is being access from the start
124 /// action. Implementors should perform session clean up (if
125 /// they actually use the session) to avoid stale data on forms.
126 /// </summary>
127 protected internal virtual void Reset()
131 /// <summary>
132 /// Returns the action name that will be used
133 /// to represent this step.
134 /// </summary>
135 public virtual String ActionName
139 Type thisType = GetType();
141 // Hack fix for "dynamic proxied" controllers
142 if (thisType.Assembly.FullName.StartsWith("DynamicAssemblyProxyGen") ||
143 thisType.Assembly.FullName.StartsWith("DynamicProxyGenAssembly2"))
145 return thisType.BaseType.Name;
148 return GetType().Name;
152 /// <summary>
153 /// Used to decide on which view to render.
154 /// </summary>
155 protected internal virtual void RenderWizardView()
157 RenderView(ActionName);
160 /// <summary>
161 /// Allow the step to assert some condition
162 /// before being accessed. Returning <c>false</c>
163 /// prevents the step from being processed but
164 /// before doing that you must send a redirect.
165 /// </summary>
166 /// <returns></returns>
167 protected internal virtual bool IsPreConditionSatisfied(IRailsEngineContext context)
169 return true;
172 /// <summary>
173 /// Uses a simple heuristic to select the best method -- especially in the
174 /// case of method overloads.
175 /// </summary>
176 /// <param name="action">The action name</param>
177 /// <param name="actions">The avaliable actions</param>
178 /// <param name="request">The request instance</param>
179 /// <param name="actionArgs">The custom arguments for the action</param>
180 /// <returns></returns>
181 protected internal override MethodInfo SelectMethod(String action, IDictionary actions, IRequest request, IDictionary actionArgs)
183 if (action == "RenderWizardView")
185 return typeof(WizardStepPage).GetMethod("RenderWizardView", BindingFlags.Instance|BindingFlags.NonPublic);
187 else
189 return base.SelectMethod(action, actions, request, null);
193 #endregion
195 #region DoNavigate and Redirects
197 /// <summary>
198 /// Navigates within the wizard steps using optionally a form parameter
199 /// to dictate to where it should go.
200 /// </summary>
201 /// <remarks>
202 /// By default this will invoke <see cref="RedirectToNextStep(IDictionary)"/>
203 /// however you can send a field form <c>navigate.to</c> to customize this.
204 /// The possible values for <c>navigate.to</c> are:
205 /// <list type="bullet">
206 /// <item><term>previous</term>
207 /// <description>Invokes <see cref="RedirectToPreviousStep()"/></description></item>
208 /// <item><term>first</term>
209 /// <description>Invokes <see cref="RedirectToFirstStep()"/></description></item>
210 /// <item><term>step name</term>
211 /// <description>A custom step name to navigate</description></item>
212 /// </list>
213 /// </remarks>
214 protected void DoNavigate()
216 DoNavigate((IDictionary) null);
219 /// <summary>
220 /// Navigates within the wizard steps using optionally a form parameter
221 /// to dictate to where it should go.
222 /// </summary>
223 /// <remarks>
224 /// By default this will invoke <see cref="RedirectToNextStep(IDictionary)"/>
225 /// however you can send a field form <c>navigate.to</c> to customize this.
226 /// The possible values for <c>navigate.to</c> are:
227 /// <list type="bullet">
228 /// <item><term>previous</term>
229 /// <description>Invokes <see cref="RedirectToPreviousStep()"/></description></item>
230 /// <item><term>first</term>
231 /// <description>Invokes <see cref="RedirectToFirstStep()"/></description></item>
232 /// <item><term>step name</term>
233 /// <description>A custom step name to navigate</description></item>
234 /// </list>
235 /// </remarks>
236 /// <param name="queryStringParameters">Query string parameters to be on the URL</param>
237 protected void DoNavigate(params String[] queryStringParameters)
239 DoNavigate(DictHelper.Create(queryStringParameters));
242 /// <summary>
243 /// Navigates within the wizard steps using optionally a form parameter
244 /// to dictate to where it should go.
245 /// </summary>
246 /// <remarks>
247 /// By default this will invoke <see cref="RedirectToNextStep(IDictionary)"/>
248 /// however you can send a field form <c>navigate.to</c> to customize this.
249 /// The possible values for <c>navigate.to</c> are:
250 /// <list type="bullet">
251 /// <item><term>previous</term>
252 /// <description>Invokes <see cref="RedirectToPreviousStep()"/></description></item>
253 /// <item><term>first</term>
254 /// <description>Invokes <see cref="RedirectToFirstStep()"/></description></item>
255 /// <item><term>step name</term>
256 /// <description>A custom step name to navigate</description></item>
257 /// </list>
258 /// </remarks>
259 /// <param name="queryStringParameters">Query string parameters to be on the URL</param>
260 protected void DoNavigate(IDictionary queryStringParameters)
262 string uriPrefix = "uri:";
264 String navigateTo = Params["navigate.to"];
266 if (navigateTo == "previous")
268 RedirectToPreviousStep(queryStringParameters);
270 else if (navigateTo == null || navigateTo == String.Empty || navigateTo == "next")
272 RedirectToNextStep(queryStringParameters);
274 else if (navigateTo.StartsWith(uriPrefix))
276 Redirect(navigateTo.Substring(uriPrefix.Length), queryStringParameters);
278 else if (navigateTo == "first")
280 RedirectToFirstStep(queryStringParameters);
282 else
284 RedirectToStep(navigateTo, queryStringParameters);
288 /// <summary>
289 /// Sends a redirect to the next wizard step (if it exists)
290 /// </summary>
291 /// <exception cref="RailsException">if no further step exists</exception>
292 protected void RedirectToNextStep()
294 RedirectToNextStep((IDictionary) null);
297 /// <summary>
298 /// Sends a redirect to the next wizard step (if it exists)
299 /// </summary>
300 /// <exception cref="RailsException">if no further step exists</exception>
301 protected void RedirectToNextStep(params String[] queryStringParameters)
303 RedirectToNextStep(DictHelper.Create(queryStringParameters));
306 /// <summary>
307 /// Sends a redirect to the next wizard step (if it exists)
308 /// </summary>
309 /// <exception cref="RailsException">if no further step exists</exception>
310 protected void RedirectToNextStep(IDictionary queryStringParameters)
312 String wizardName = WizardUtils.ConstructWizardNamespace(_wizardcontroller);
314 int currentIndex = (int) Context.Session[wizardName + "currentstepindex"];
316 IList stepList = (IList) Context.Items["wizard.step.list"];
318 if ((currentIndex + 1) < stepList.Count)
320 int nextStepIndex = currentIndex + 1;
322 String nextStep = (String) stepList[nextStepIndex];
324 WizardUtils.RegisterCurrentStepInfo(_wizardcontroller, nextStepIndex, nextStep);
326 InternalRedirectToStep(nextStepIndex, nextStep, queryStringParameters);
328 else
330 throw new RailsException("There is no next step available");
334 /// <summary>
335 /// Sends a redirect to the previous wizard step
336 /// </summary>
337 /// <exception cref="RailsException">
338 /// if no previous step exists (ie. already in the first one)</exception>
339 protected void RedirectToPreviousStep()
341 RedirectToPreviousStep((IDictionary) null);
344 /// <summary>
345 /// Sends a redirect to the previous wizard step
346 /// </summary>
347 /// <exception cref="RailsException">
348 /// if no previous step exists (ie. already in the first one)</exception>
349 protected void RedirectToPreviousStep(params String[] queryStringParameters)
351 RedirectToPreviousStep(DictHelper.Create(queryStringParameters));
354 /// <summary>
355 /// Sends a redirect to the previous wizard step
356 /// </summary>
357 /// <exception cref="RailsException">
358 /// if no previous step exists (ie. already in the first one)</exception>
359 protected void RedirectToPreviousStep(IDictionary queryStringParameters)
361 String wizardName = WizardUtils.ConstructWizardNamespace(_wizardcontroller);
363 int currentIndex = (int) Context.Session[wizardName + "currentstepindex"];
365 IList stepList = (IList) Context.Items["wizard.step.list"];
367 if ((currentIndex - 1) >= 0)
369 int prevStepIndex = currentIndex - 1;
371 String prevStep = (String) stepList[prevStepIndex];
373 InternalRedirectToStep(prevStepIndex, prevStep, queryStringParameters);
375 else
377 throw new RailsException("There is no previous step available");
381 /// <summary>
382 /// Sends a redirect to the first wizard step
383 /// </summary>
384 protected void RedirectToFirstStep()
386 RedirectToFirstStep((IDictionary) null);
389 /// <summary>
390 /// Sends a redirect to the first wizard step
391 /// </summary>
392 protected void RedirectToFirstStep(params String[] queryStringParameters)
394 RedirectToFirstStep(DictHelper.Create(queryStringParameters));
397 /// <summary>
398 /// Sends a redirect to the first wizard step
399 /// </summary>
400 protected void RedirectToFirstStep(IDictionary queryStringParameters)
402 IList stepList = (IList) Context.Items["wizard.step.list"];
404 String firstStep = (String) stepList[0];
406 InternalRedirectToStep(0, firstStep, queryStringParameters);
409 /// <summary>
410 /// Sends a redirect to a custom step (that must exists)
411 /// </summary>
412 protected bool RedirectToStep(String stepName)
414 return RedirectToStep(stepName, (IDictionary) null);
417 /// <summary>
418 /// Sends a redirect to a custom step (that must exists)
419 /// </summary>
420 protected bool RedirectToStep(String stepName, params String[] queryStringParameters)
422 return RedirectToStep(stepName, DictHelper.Create(queryStringParameters));
425 /// <summary>
426 /// Sends a redirect to a custom step (that must exists)
427 /// </summary>
428 protected bool RedirectToStep(String stepName, IDictionary queryStringParameters)
430 IList stepList = (IList) Context.Items["wizard.step.list"];
432 for(int index = 0; index < stepList.Count; index++)
434 String curStep = (String) stepList[index];
436 if (curStep == stepName)
438 InternalRedirectToStep(index, stepName, queryStringParameters);
439 return true;
443 return false;
446 /// <summary>
447 /// For a wizard step, an internal action will always be named
448 /// with the controller name as a prefix , plus an hifen and finally
449 /// the action name. This implementation does exactly that.
450 /// </summary>
451 /// <param name="action">Raw action name</param>
452 /// <returns>Properly formatted action name</returns>
453 internal override String TransformActionName(String action)
455 return base.TransformActionName(ActionName + "-" + action);
458 private void InternalRedirectToStep(int stepIndex, String step, IDictionary queryStringParameters)
460 WizardUtils.RegisterCurrentStepInfo(_wizardcontroller, stepIndex, step);
462 if (queryStringParameters != null && queryStringParameters.Count != 0)
464 Redirect(_wizardcontroller.Name, step, queryStringParameters);
466 else if (Context.Request.QueryString.HasKeys())
468 // We need to preserve any attribute from the QueryString
469 // for example in case the url has an Id
471 string url = UrlBuilder.BuildUrl(Context.UrlInfo, _wizardcontroller.Name, step) + Context.Request.Uri.Query;
473 Context.Response.Redirect(url);
475 else
477 Context.Response.Redirect(_wizardcontroller.Name, step);
481 #endregion