Removed untyped contructor from ComponentRegistration and add a protected setter.
[castle.git] / MonoRail / Castle.MonoRail.Framework / WizardActionProvider.cs
blob191b2b11bf3907bd0c4d3484702fbe16039f466e
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 Castle.MonoRail.Framework.Helpers;
21 using Castle.MonoRail.Framework.Internal;
22 using Castle.MonoRail.Framework.Descriptors;
24 /// <summary>
25 /// Provide easy to use Wizard-like support.
26 /// </summary>
27 /// <seealso cref="IWizardController"/>
28 /// <remarks>
29 /// MonoRail uses the DynamicAction infrastructure to provide
30 /// wizard support so we dont force
31 /// the programmer to inherit from a specific Controller
32 /// which can be quite undesirable in real world projects.
33 /// <para>
34 /// Nevertheless we do require that the programmer
35 /// implements <see cref="IWizardController"/> on the wizard controller.
36 /// </para>
37 /// </remarks>
38 public class WizardActionProvider : IDynamicActionProvider, IDynamicAction
40 private IWizardStepPage[] steps;
41 private IWizardStepPage currentStepInstance;
42 private String rawAction;
43 private String innerAction;
44 private String requestedAction;
45 private UrlInfo urlInfo;
47 /// <summary>
48 /// Implementation of IDynamicActionProvider.
49 /// <para>
50 /// Grab all steps related to the wizard
51 /// and register them as dynamic actions.
52 /// </para>
53 /// </summary>
54 /// <param name="engineContext">The engine context.</param>
55 /// <param name="controller">Wizard controller (must implement <see cref="IWizardController"/></param>
56 /// <param name="controllerContext">The controller context.</param>
57 public void IncludeActions(IEngineContext engineContext, IController controller, IControllerContext controllerContext)
59 // Primordial assert
61 IWizardController wizardController = controller as IWizardController;
63 if (wizardController == null)
65 throw new MonoRailException("The controller {0} must implement the interface " +
66 "IWizardController to be used as a wizard", controllerContext.Name);
69 // Grab all Wizard Steps
71 steps = wizardController.GetSteps(engineContext);
73 if (steps == null || steps.Length == 0)
75 throw new MonoRailException("The controller {0} returned no WizardStepPage", controllerContext.Name);
78 List<string> stepList = new List<string>();
80 // Include the "start" dynamic action, which resets the wizard state
82 controllerContext.DynamicActions["start"] = this;
84 // Find out the action request (and possible inner action)
85 // Each action will be a step name, or maybe the step name + action (ie Page1-Save)
87 urlInfo = engineContext.UrlInfo;
89 rawAction = urlInfo.Action;
91 requestedAction = ObtainRequestedAction(rawAction, out innerAction);
93 // If no inner action was found, fallback to 'RenderWizardView'
95 if (string.IsNullOrEmpty(innerAction))
97 innerAction = "RenderWizardView";
100 engineContext.Items["wizard.step.list"] = stepList;
102 SetUpWizardHelper(engineContext, controller, controllerContext);
104 // Initialize all steps and while we are at it,
105 // discover the current step
107 foreach(IWizardStepPage step in steps)
109 string actionName = step.ActionName;
111 step.WizardController = wizardController;
112 step.WizardControllerContext = controllerContext;
114 if (string.Compare(requestedAction, actionName, true) == 0)
116 currentStepInstance = step;
118 if (innerAction != null)
120 // If there's an inner action, we invoke it as a step too
121 controllerContext.DynamicActions[rawAction] = new DelegateDynamicAction(OnStepActionRequested);
124 engineContext.CurrentController = step;
126 else
128 controllerContext.DynamicActions[actionName] = new DelegateDynamicAction(OnStepActionRequested);
131 stepList.Add(actionName);
134 SetUpWizardHelper(engineContext, controller, controllerContext);
137 /// <summary>
138 /// Invoked as "start" action
139 /// </summary>
140 /// <returns></returns>
141 public object Execute(IEngineContext engineContext, IController controller, IControllerContext controllerContext)
143 StartWizard(engineContext, controller, controllerContext, true);
144 return null;
147 /// <summary>
148 /// Invoked when a step is accessed on the url,
149 /// i.e. http://host/mywizard/firststep.rails and
150 /// when an inner action is invoked like http://host/mywizard/firststep-save.rails
151 /// </summary>
152 /// <param name="engineContext">The engine context.</param>
153 /// <param name="controller">The controller.</param>
154 /// <param name="controllerContext">The controller context.</param>
155 private object OnStepActionRequested(IEngineContext engineContext, IController controller, IControllerContext controllerContext)
157 if (currentStepInstance != null && !HasRequiredSessionData(engineContext, controller, controllerContext))
159 StartWizard(engineContext, controller, controllerContext, false);
162 controllerContext.SelectedViewName = null;
164 IWizardController wizController = (IWizardController) controller;
166 String wizardName = WizardUtils.ConstructWizardNamespace(controllerContext);
167 String currentStep = (String) engineContext.Session[wizardName + "currentstep"];
169 // If OnBeforeStep returns false we stop
170 if (!wizController.OnBeforeStep(wizardName, currentStep, currentStepInstance))
172 return null;
175 ControllerMetaDescriptor stepMetaDescriptor =
176 engineContext.Services.ControllerDescriptorProvider.BuildDescriptor(currentStepInstance);
178 // Record the step we're working with
179 WizardUtils.RegisterCurrentStepInfo(engineContext, controller, controllerContext, currentStepInstance.ActionName);
183 IControllerContext stepContext =
184 engineContext.Services.ControllerContextFactory.Create(
185 controllerContext.AreaName, controllerContext.Name, innerAction,
186 stepMetaDescriptor, controllerContext.RouteMatch);
187 stepContext.PropertyBag = controllerContext.PropertyBag;
189 SetUpWizardHelper(engineContext, currentStepInstance, stepContext);
191 // IsPreConditionSatisfied might need the controller's context
192 if (currentStepInstance is Controller)
194 ((Controller)currentStepInstance).Contextualize(engineContext, stepContext);
197 // The step cannot be accessed in the current state of matters
198 if (!currentStepInstance.IsPreConditionSatisfied(engineContext))
200 return null;
203 currentStepInstance.Process(engineContext, stepContext);
205 return null;
207 finally
209 wizController.OnAfterStep(wizardName, currentStep, currentStepInstance);
213 /// <summary>
214 /// Represents an empty (no-op) action.
215 /// </summary>
216 /// <param name="controller">The controller.</param>
217 protected void EmptyAction(Controller controller)
219 controller.CancelView();
222 /// <summary>
223 /// Determines whether all wizard specific information is on the user session.
224 /// </summary>
225 /// <param name="engineContext">The engine context.</param>
226 /// <param name="controller">The controller.</param>
227 /// <param name="controllerContext">The controller context.</param>
228 /// <returns>
229 /// <c>true</c> if has session data; otherwise, <c>false</c>.
230 /// </returns>
231 protected bool HasRequiredSessionData(IEngineContext engineContext, IController controller, IControllerContext controllerContext)
233 String wizardName = WizardUtils.ConstructWizardNamespace(controllerContext);
235 return (engineContext.Session.Contains(wizardName + "currentstepindex") &&
236 engineContext.Session.Contains(wizardName + "currentstep"));
239 /// <summary>
240 /// Starts the wizard by adding the required information to the
241 /// session and invoking <see cref="IWizardController.OnWizardStart"/>
242 /// and detecting the first step.
243 /// </summary>
244 /// <param name="engineContext">The engine context.</param>
245 /// <param name="controller">The controller.</param>
246 /// <param name="controllerContext">The controller context.</param>
247 /// <param name="redirect">if set to <c>true</c>, a redirect
248 /// will be issued to the first step.</param>
249 protected void StartWizard(IEngineContext engineContext, IController controller, IControllerContext controllerContext, bool redirect)
251 ResetSteps(engineContext, controller);
253 IWizardController wizardController = (IWizardController) controller;
255 IList stepList = (IList) engineContext.Items["wizard.step.list"];
257 String firstStep = (String) stepList[0];
259 String wizardName = WizardUtils.ConstructWizardNamespace(controllerContext);
261 engineContext.Session[wizardName + "currentstepindex"] = 0;
262 engineContext.Session[wizardName + "currentstep"] = firstStep;
264 wizardController.OnWizardStart();
266 if (redirect)
268 engineContext.Response.Redirect(controllerContext.AreaName, controllerContext.Name, firstStep);
272 /// <summary>
273 /// Resets the steps by invoking <see cref="IWizardStepPage.Reset"/>
274 /// on all steps instances.
275 /// </summary>
276 /// <param name="engineContext">The engine context.</param>
277 /// <param name="controller">The controller.</param>
278 protected void ResetSteps(IEngineContext engineContext, IController controller)
280 IWizardController wizardController = (IWizardController) controller;
282 IWizardStepPage[] steps = wizardController.GetSteps(engineContext);
284 foreach(IWizardStepPage step in steps)
286 step.Reset();
290 private String ObtainRequestedAction(String action, out String innerAction)
292 innerAction = null;
294 int index = action.IndexOf('-');
296 if (index != -1)
298 innerAction = action.Substring(index + 1);
300 return action.Substring(0, index);
303 return action;
306 private void SetUpWizardHelper(IEngineContext engineContext, IController controller, IControllerContext controllerContext)
308 if (controller == null) return;
310 WizardHelper helper = new WizardHelper();
312 helper.SetContext(engineContext);
313 helper.SetController(controller, controllerContext);
315 controllerContext.Helpers.Add(helper);