1 // Copyright 2004-2008 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
.ActiveRecordSupport
.Scaffold
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
;
32 /// Base abstract class for actions that relate to
33 /// Scaffolding support. Provide the basic flow process
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
;
65 /// Initializes a new instance of the <see cref="AbstractScaffoldAction"/> class.
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
;
84 /// Gets a value indicating whether the name of the model should
85 /// be used on the url.
87 /// <value><c>true</c> if yes, otherwise <c>false</c>.</value>
88 public bool UseModelName
90 get { return useModelName; }
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>
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
;
135 controllerContext
.SelectedViewName
= null;
136 RenderStandardHtml(engineContext
, controller
, controllerContext
);
151 /// Implementors should return the template name
152 /// for the current action.
154 /// <param name="controllerContext">The controller context.</param>
155 /// <returns></returns>
156 protected abstract string ComputeTemplateName(IControllerContext controllerContext
);
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.
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
);
169 /// Implementors should perform the action for the
170 /// scaffolding, like new or create.
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();
183 /// Gets the current <see cref="ActiveRecordModel"/>
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() ?");
204 /// Gets the property that represents the Primary key
205 /// for the current <see cref="ActiveRecordModel"/>
207 /// <returns></returns>
208 protected PropertyInfo
ObtainPKProperty()
210 PrimaryKeyModel keyModel
= ARCommonUtils
.ObtainPKProperty(model
);
212 if (keyModel
!= null)
214 return keyModel
.Property
;
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
);
241 templateEngine
.Process(context
, "Castle.MonoRail.ActiveRecordSupport/Scaffold/Templates/" + templateName
, writer
);
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
);
253 templateEngine
.Process(context
, "Castle.MonoRail.ActiveRecordSupport/Scaffold/Templates/layout.vm", layoutwriter
);
256 writer
= layoutwriter
;
258 controllerContext
.SelectedViewName
= null;
259 engineContext
.Response
.Write(writer
.GetStringBuilder().ToString());
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
);