Fixing an issue with output parameters that are of type IntPtr
[castle.git] / MonoRail / Castle.MonoRail.ActiveRecordSupport / Scaffold / AbstractScaffoldAction.cs
blob495bffd4e7bd09f0e7d2a3d9fbc8595053105579
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.ActiveRecordSupport.Scaffold
17 using System;
18 using System.IO;
19 using System.Reflection;
20 using System.Collections;
21 using Castle.ActiveRecord;
22 using Castle.ActiveRecord.Framework.Internal;
23 using Castle.Components.Binder;
24 using Castle.Components.Common.TemplateEngine;
26 using Castle.MonoRail.ActiveRecordSupport.Scaffold.Helpers;
27 using Castle.MonoRail.ActiveRecordSupport;
28 using Castle.MonoRail.Framework;
29 using Castle.MonoRail.Framework.Helpers;
31 /// <summary>
32 /// Base abstract class for actions that relate to
33 /// Scaffolding support. Provide the basic flow process
34 /// </summary>
35 public abstract class AbstractScaffoldAction : IDynamicAction
37 /// <summary>Holds the AR type</summary>
38 protected readonly Type modelType;
40 /// <summary>Reference to the template engine instance</summary>
41 protected readonly ITemplateEngine templateEngine;
43 /// <summary>A map of PropertyInfo to validation failures</summary>
44 protected IDictionary prop2Validation = new Hashtable();
46 /// <summary>A list of errors that happened during this process</summary>
47 protected ArrayList errors = new ArrayList();
49 /// <summary>Constructs the data source for the binder</summary>
50 protected TreeBuilder builder = new TreeBuilder();
52 /// <summary>Binder that 'knows' ActiveRecord types</summary>
53 protected ARDataBinder binder = new ARDataBinder();
55 /// <summary>The model for the AR type we're dealing with</summary>
56 private ActiveRecordModel model;
58 /// <summary>Used to define if the model name should be present on the action name (urls)</summary>
59 private readonly bool useModelName;
61 /// <summary>Indicates that the controller has no layout, so we use ours</summary>
62 private readonly bool useDefaultLayout;
64 /// <summary>
65 /// Initializes a new instance of the <see cref="AbstractScaffoldAction"/> class.
66 /// </summary>
67 /// <param name="modelType">Type of the model.</param>
68 /// <param name="templateEngine">The template engine.</param>
69 /// <param name="useModelName">Indicates that we should use the model name on urls</param>
70 /// <param name="useDefaultLayout">Whether we should use our layout.</param>
71 public AbstractScaffoldAction(Type modelType, ITemplateEngine templateEngine,
72 bool useModelName, bool useDefaultLayout)
74 this.modelType = modelType;
75 this.templateEngine = templateEngine;
76 this.useModelName = useModelName;
77 this.useDefaultLayout = useDefaultLayout;
79 // Configures the binder
80 binder.AutoLoad = AutoLoadBehavior.OnlyNested;
83 /// <summary>
84 /// Gets a value indicating whether the name of the model should
85 /// be used on the url.
86 /// </summary>
87 /// <value><c>true</c> if yes, otherwise <c>false</c>.</value>
88 public bool UseModelName
90 get { return useModelName; }
93 /// <summary>
94 /// Executes the basic flow which is
95 /// <list type="number">
96 /// <item><description>Resolve the <see cref="ActiveRecordModel"/></description></item>
97 /// <item><description>Resolve the layout (if not is associated with the controller, defaults to "scaffold")</description></item>
98 /// <item><description>Invokes <see cref="PerformActionProcess"/> which should perform the correct process for this action</description></item>
99 /// <item><description>Resolves the template name that the developer might provide by using <see cref="ComputeTemplateName"/></description></item>
100 /// <item><description>If the template exists, renders it. Otherwise invokes <see cref="RenderStandardHtml"/></description></item>
101 /// </list>
102 /// </summary>
103 /// <param name="engineContext">The engine context.</param>
104 /// <param name="controller">The controller.</param>
105 /// <param name="controllerContext">The controller context.</param>
106 public object Execute(IEngineContext engineContext, IController controller, IControllerContext controllerContext)
108 // Cancel any rendering
109 controllerContext.SelectedViewName = null;
111 // We make sure the code is always surrounded by a SessionScope.
112 // If none is found, we create one
114 SessionScope scope = null;
116 if (SessionScope.Current == null)
118 scope = new SessionScope(FlushAction.Never);
123 model = GetARModel();
125 PerformActionProcess(engineContext, controller, controllerContext);
127 String templateName = ComputeTemplateName(controllerContext);
129 if (engineContext.Services.ViewEngineManager.HasTemplate(templateName))
131 controllerContext.SelectedViewName = templateName;
133 else
135 controllerContext.SelectedViewName = null;
136 RenderStandardHtml(engineContext, controller, controllerContext);
139 finally
141 if (scope != null)
143 scope.Dispose();
147 return null;
150 /// <summary>
151 /// Implementors should return the template name
152 /// for the current action.
153 /// </summary>
154 /// <param name="controllerContext">The controller context.</param>
155 /// <returns></returns>
156 protected abstract string ComputeTemplateName(IControllerContext controllerContext);
158 /// <summary>
159 /// Only invoked if the programmer havent provided
160 /// a custom template for the current action. Implementors
161 /// should create a basic html to present.
162 /// </summary>
163 /// <param name="engineContext">The engine context.</param>
164 /// <param name="controller">The controller.</param>
165 /// <param name="controllerContext">The controller context.</param>
166 protected abstract void RenderStandardHtml(IEngineContext engineContext, IController controller, IControllerContext controllerContext);
168 /// <summary>
169 /// Implementors should perform the action for the
170 /// scaffolding, like new or create.
171 /// </summary>
172 /// <param name="engineContext">The engine context.</param>
173 /// <param name="controller">The controller.</param>
174 /// <param name="controllerContext">The controller context.</param>
175 protected virtual void PerformActionProcess(IEngineContext engineContext, IController controller, IControllerContext controllerContext)
177 controllerContext.PropertyBag["useModelName"] = useModelName;
178 controllerContext.PropertyBag["model"] = Model;
179 controllerContext.PropertyBag["keyprop"] = ObtainPKProperty();
182 /// <summary>
183 /// Gets the current <see cref="ActiveRecordModel"/>
184 /// </summary>
185 protected ActiveRecordModel Model
187 get { return model; }
190 private ActiveRecordModel GetARModel()
192 ActiveRecordModel foundModel = ActiveRecordModel.GetModel(modelType);
194 if (foundModel == null)
196 throw new ScaffoldException("Specified type is not an ActiveRecord type or the ActiveRecord " +
197 "framework was not started properly. Did you forget to invoke ActiveRecordStarter.Initialize() ?");
200 return foundModel;
203 /// <summary>
204 /// Gets the property that represents the Primary key
205 /// for the current <see cref="ActiveRecordModel"/>
206 /// </summary>
207 /// <returns></returns>
208 protected PropertyInfo ObtainPKProperty()
210 PrimaryKeyModel keyModel = ARCommonUtils.ObtainPKProperty(model);
212 if (keyModel != null)
214 return keyModel.Property;
217 return null;
220 protected void RenderFromTemplate(String templateName, IEngineContext engineContext, IController controller, IControllerContext controllerContext)
222 StringWriter writer = new StringWriter();
224 IDictionary context = new Hashtable();
226 context.Add("flash", engineContext.Flash);
228 foreach(DictionaryEntry entry in controllerContext.Helpers)
230 context[entry.Key] = entry.Value;
233 foreach(DictionaryEntry entry in controllerContext.PropertyBag)
235 context.Add(entry.Key, entry.Value);
238 #if USE_LOCAL_TEMPLATES
239 templateEngine.Process( context, templateName, writer );
240 #else
241 templateEngine.Process(context, "Castle.MonoRail.ActiveRecordSupport/Scaffold/Templates/" + templateName, writer);
242 #endif
244 if (useDefaultLayout)
246 StringWriter layoutwriter = new StringWriter();
248 context.Add("childContent", writer.GetStringBuilder().ToString());
250 #if USE_LOCAL_TEMPLATES
251 templateEngine.Process(context, "layout.vm", layoutwriter);
252 #else
253 templateEngine.Process(context, "Castle.MonoRail.ActiveRecordSupport/Scaffold/Templates/layout.vm", layoutwriter);
254 #endif
256 writer = layoutwriter;
258 controllerContext.SelectedViewName = null;
259 engineContext.Response.Write(writer.GetStringBuilder().ToString());
261 else
263 engineContext.Services.ViewEngineManager.RenderStaticWithinLayout(writer.GetStringBuilder().ToString(), engineContext, controller, controllerContext);
267 protected static void SetUpHelpers(IEngineContext engineContext, IController controller, IControllerContext controllerContext)
269 ARFormHelper formHelper = new ARFormHelper();
270 formHelper.SetContext(engineContext);
271 formHelper.SetController(controller, controllerContext);
273 ValidationHelper validationHelper = new ValidationHelper();
274 validationHelper.SetContext(engineContext);
275 validationHelper.SetController(controller, controllerContext);
277 PresentationHelper presentationHelper = new PresentationHelper();
278 presentationHelper.SetContext(engineContext);
279 presentationHelper.SetController(controller, controllerContext);
281 PaginationHelper paginationHelper = new PaginationHelper();
282 paginationHelper.SetContext(engineContext);
283 paginationHelper.SetController(controller, controllerContext);
285 ScriptaculousHelper scriptaculous = new ScriptaculousHelper();
286 scriptaculous.SetContext(engineContext);
287 scriptaculous.SetController(controller, controllerContext);
289 AjaxHelper ajaxHelper = new AjaxHelper();
290 ajaxHelper.SetContext(engineContext);
291 ajaxHelper.SetController(controller, controllerContext);
293 controllerContext.Helpers["Scriptaculous"] = scriptaculous;
294 controllerContext.Helpers["Ajax"] = ajaxHelper;
295 controllerContext.Helpers["Form"] = formHelper;
296 controllerContext.Helpers["ValidationHelper"] = validationHelper;
297 controllerContext.Helpers["PresentationHelper"] = presentationHelper;
298 controllerContext.Helpers["PaginationHelper"] = paginationHelper;
301 protected static void AssertIsPost(string method)
303 if (method != "POST")
305 throw new Exception("This action cannot be accessed using the verb " + method);