1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
18 using System
.Collections
;
20 using Castle
.MonoRail
.Framework
.Helpers
;
21 using Castle
.MonoRail
.Framework
.Internal
;
24 /// Provide easy to use Wizard-like support.
27 /// We use the DynamicAction infrastructure to provide
28 /// wizard support as we dont force
29 /// the programmer to inherit from a specific Controller
30 /// which can be quite undesirable in common business projects
33 /// Nevertheless we do require that the programmer
34 /// implements <see cref="IWizardController"/> on the wizard controller.
37 public class WizardActionProvider
: IDynamicActionProvider
, IDynamicAction
39 private WizardStepPage
[] steps
;
40 private WizardStepPage currentStepInstance
;
41 private IControllerLifecycleExecutor currentStepExecutor
;
42 private String rawAction
;
43 private String innerAction
;
44 private String requestedAction
;
45 private UrlInfo urlInfo
;
48 /// Initializes a new instance of the <see cref="WizardActionProvider"/> class.
50 public WizardActionProvider()
55 /// Implementation of IDynamicActionProvider.
57 /// Grab all steps related to the wizard
58 /// and register them as dynamic actions.
61 /// <param name="controller">Wizard controller (must implement <see cref="IWizardController"/></param>
62 public void IncludeActions(Controller controller
)
64 IRailsEngineContext context
= controller
.Context
;
68 IWizardController wizardController
= controller
as IWizardController
;
70 if (wizardController
== null)
72 throw new RailsException("The controller {0} must implement the interface " +
73 "IWizardController to be used as a wizard", controller
.Name
);
76 // Grab all Wizard Steps
78 steps
= wizardController
.GetSteps(controller
.Context
);
80 if (steps
== null || steps
.Length
== 0)
82 throw new RailsException("The controller {0} returned no WizardStepPage", controller
.Name
);
85 IList stepList
= new ArrayList();
87 // Include the "start" dynamic action, which resets the wizard state
89 controller
.DynamicActions
["start"] = this;
91 // Find out the action request (and possible inner action)
92 // Each action will be a step name, or maybe the step name + action (ie Page1-Save)
94 urlInfo
= controller
.Context
.UrlInfo
;
96 rawAction
= urlInfo
.Action
;
98 requestedAction
= ObtainRequestedAction(rawAction
, out innerAction
);
100 // If no inner action was found, fallback to 'RenderWizardView'
102 if (innerAction
== null || innerAction
== String
.Empty
)
104 innerAction
= "RenderWizardView";
107 IControllerLifecycleExecutorFactory execFactory
=
108 (IControllerLifecycleExecutorFactory
) context
.GetService(typeof(IControllerLifecycleExecutorFactory
));
110 // Initialize all steps and while we are at it,
111 // discover the current step
113 foreach(WizardStepPage step
in steps
)
115 String actionName
= step
.ActionName
;
117 if (String
.Compare(requestedAction
, actionName
, true) == 0)
119 currentStepInstance
= step
;
121 if (innerAction
!= null)
123 // If there's an inner action, we invoke it as a step too
124 controller
.DynamicActions
[rawAction
] =
125 new DelegateDynamicAction(new ActionDelegate(OnStepActionRequested
));
128 context
.CurrentController
= step
;
131 controller
.DynamicActions
[actionName
] =
132 new DelegateDynamicAction(new ActionDelegate(OnStepActionRequested
));
134 stepList
.Add(actionName
);
136 IControllerLifecycleExecutor stepExec
= execFactory
.CreateExecutor(step
, context
);
137 stepExec
.InitializeController(controller
.AreaName
, controller
.Name
, innerAction
);
138 step
.Initialize(controller
);
140 if (currentStepInstance
== step
)
142 currentStepExecutor
= stepExec
;
144 if (!stepExec
.SelectAction(innerAction
, controller
.Name
))
146 stepExec
.PerformErrorHandling();
151 if (!stepExec
.RunStartRequestFilters())
153 context
.UnderlyingContext
.Response
.End();
158 context
.Items
["wizard.step.list"] = stepList
;
160 SetUpWizardHelper(controller
);
161 SetUpWizardHelper(currentStepInstance
);
165 /// Invoked as "start" action
167 /// <param name="controller"></param>
168 public void Execute(Controller controller
)
170 StartWizard(controller
, true);
174 /// Invoked when a step is accessed on the url,
175 /// i.e. http://host/mywizard/firststep.rails and
176 /// when an inner action is invoked like http://host/mywizard/firststep-save.rails
178 /// <param name="controller"></param>
179 private void OnStepActionRequested(Controller controller
)
181 if (currentStepInstance
!= null && !HasRequiredSessionData(controller
))
183 StartWizard(controller
, false);
186 controller
.CancelView();
188 IRailsEngineContext context
= controller
.Context
;
190 IWizardController wizController
= (IWizardController
) controller
;
192 String wizardName
= WizardUtils
.ConstructWizardNamespace(controller
);
194 String currentStep
= (String
) context
.Session
[wizardName
+ "currentstep"];
196 // The step will inherit the controller property bag,
197 // this way filters can pass values to the step property without having to know it
198 currentStepInstance
.PropertyBag
= controller
.PropertyBag
;
200 // If OnBeforeStep returns false we stop
201 if (!wizController
.OnBeforeStep(wizardName
, currentStep
, currentStepInstance
))
206 // Initialize step data so instance members can be used
207 // executor.InitializeController(urlInfo.Area, urlInfo.Controller, innerAction);
209 // Record the step we're working with
210 WizardUtils
.RegisterCurrentStepInfo(controller
, currentStepInstance
.ActionName
);
212 // The step cannot be accessed in the current state of matters
213 if (!currentStepInstance
.IsPreConditionSatisfied(controller
.Context
))
221 // TODO: Invoke Whole step here
222 // currentStepInstance.Process(controller.Context,
223 // urlInfo.Area, urlInfo.Controller, innerAction);
224 currentStepExecutor
.ProcessSelectedAction();
228 wizController
.OnAfterStep(wizardName
, currentStep
, currentStepInstance
);
230 currentStepExecutor
.Dispose();
234 protected void EmptyAction(Controller controller
)
236 controller
.CancelView();
239 protected bool HasRequiredSessionData(Controller controller
)
241 String wizardName
= WizardUtils
.ConstructWizardNamespace(controller
);
243 IRailsEngineContext context
= controller
.Context
;
245 return (context
.Session
.Contains(wizardName
+ "currentstepindex") &&
246 context
.Session
.Contains(wizardName
+ "currentstep"));
249 protected void StartWizard(Controller controller
, bool redirect
)
251 ResetSteps(controller
);
253 IWizardController wizardController
= controller
as IWizardController
;
255 IRailsEngineContext context
= controller
.Context
;
257 IList stepList
= (IList
) context
.Items
["wizard.step.list"];
259 String firstStep
= (String
) stepList
[0];
261 String wizardName
= WizardUtils
.ConstructWizardNamespace(controller
);
263 context
.Session
[wizardName
+ "currentstepindex"] = 0;
264 context
.Session
[wizardName
+ "currentstep"] = firstStep
;
266 wizardController
.OnWizardStart();
270 context
.Response
.Redirect(controller
.AreaName
, controller
.Name
, firstStep
);
274 protected void ResetSteps(Controller controller
)
276 IWizardController wizardController
= controller
as IWizardController
;
278 WizardStepPage
[] steps
= wizardController
.GetSteps(controller
.Context
);
280 foreach(WizardStepPage step
in steps
)
282 step
.InitializeFieldsFromServiceProvider(controller
.Context
);
287 private String
ObtainRequestedAction(String action
, out String innerAction
)
291 int index
= action
.IndexOf('-');
295 innerAction
= action
.Substring(index
+ 1);
297 return action
.Substring(0, index
);
303 private void SetUpWizardHelper(Controller controller
)
305 if (controller
== null) return;
307 WizardHelper helper
= new WizardHelper();
309 helper
.SetController(controller
);
311 controller
.Helpers
["wizardhelper"] = helper
;