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
.ActiveRecordScaffold
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
.ActiveRecordScaffold
.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="controller"></param>
104 public void Execute(Controller controller
)
106 // We make sure the code is always surrounded by a SessionScope.
107 // If none is found, we create one
109 SessionScope scope
= null;
111 if (SessionScope
.Current
== null)
113 scope
= new SessionScope(FlushAction
.Never
);
118 model
= GetARModel();
120 PerformActionProcess(controller
);
122 String templateName
= ComputeTemplateName(controller
);
124 if (controller
.HasTemplate(templateName
))
126 controller
.RenderSharedView(templateName
);
130 RenderStandardHtml(controller
);
143 /// Implementors should return the template name
144 /// for the current action.
146 /// <param name="controller"></param>
147 /// <returns></returns>
148 protected abstract string ComputeTemplateName(Controller controller
);
151 /// Only invoked if the programmer havent provided
152 /// a custom template for the current action. Implementors
153 /// should create a basic html to present.
155 /// <param name="controller"></param>
156 protected abstract void RenderStandardHtml(Controller controller
);
159 /// Implementors should perform the action for the
160 /// scaffolding, like new or create.
162 /// <param name="controller"></param>
163 protected virtual void PerformActionProcess(Controller controller
)
165 controller
.PropertyBag
["useModelName"] = useModelName
;
166 controller
.PropertyBag
["model"] = Model
;
167 controller
.PropertyBag
["keyprop"] = ObtainPKProperty();
171 /// Gets the current <see cref="ActiveRecordModel"/>
173 protected ActiveRecordModel Model
175 get { return model; }
178 private ActiveRecordModel
GetARModel()
180 ActiveRecordModel foundModel
= ActiveRecordModel
.GetModel(modelType
);
182 if (foundModel
== null)
184 throw new ScaffoldException("Specified type is not an ActiveRecord type or the ActiveRecord " +
185 "framework was not started properly. Did you forget to invoke ActiveRecordStarter.Initialize() ?");
192 /// Gets the property that represents the Primary key
193 /// for the current <see cref="ActiveRecordModel"/>
195 /// <returns></returns>
196 protected PropertyInfo
ObtainPKProperty()
198 PrimaryKeyModel keyModel
= ARCommonUtils
.ObtainPKProperty(model
);
200 if (keyModel
!= null)
202 return keyModel
.Property
;
208 protected void RenderFromTemplate(String templateName
, Controller controller
)
210 StringWriter writer
= new StringWriter();
212 IDictionary context
= new Hashtable();
214 context
.Add("flash", controller
.Context
.Flash
);
216 foreach(DictionaryEntry entry
in controller
.PropertyBag
)
218 context
.Add(entry
.Key
, entry
.Value
);
221 #if USE_LOCAL_TEMPLATES
222 templateEngine
.Process( context
, templateName
, writer
);
224 templateEngine
.Process( context
, "Castle.MonoRail.ActiveRecordScaffold/Templates/" + templateName
, writer
);
227 if (useDefaultLayout
)
229 StringWriter layoutwriter
= new StringWriter();
231 context
.Add("childContent", writer
.GetStringBuilder().ToString());
233 #if USE_LOCAL_TEMPLATES
234 templateEngine
.Process(context
, "layout.vm", layoutwriter
);
236 templateEngine
.Process(context
, "Castle.MonoRail.ActiveRecordScaffold/Templates/layout.vm", layoutwriter
);
239 writer
= layoutwriter
;
241 controller
.CancelView();
242 controller
.Response
.Write(writer
.GetStringBuilder().ToString());
246 controller
.DirectRender(writer
.GetStringBuilder().ToString());
250 protected static void SetUpHelpers(Controller controller
)
252 ARFormHelper formHelper
= new ARFormHelper();
253 formHelper
.SetController(controller
);
255 ValidationHelper validationHelper
= new ValidationHelper();
256 validationHelper
.SetController(controller
);
258 PresentationHelper presentationHelper
= new PresentationHelper();
259 presentationHelper
.SetController(controller
);
261 PaginationHelper paginationHelper
= new PaginationHelper();
262 paginationHelper
.SetController(controller
);
264 ScriptaculousHelper scriptaculous
= new ScriptaculousHelper();
265 scriptaculous
.SetController(controller
);
267 AjaxHelper ajaxHelper
= new AjaxHelper();
268 ajaxHelper
.SetController(controller
);
270 controller
.PropertyBag
["Scriptaculous"] = scriptaculous
;
271 controller
.PropertyBag
["Ajax"] = ajaxHelper
;
272 controller
.PropertyBag
["Form"] = formHelper
;
273 controller
.PropertyBag
["ValidationHelper"] = validationHelper
;
274 controller
.PropertyBag
["PresentationHelper"] = presentationHelper
;
275 controller
.PropertyBag
["PaginationHelper"] = paginationHelper
;
278 protected static void AssertIsPost(Controller controller
)
280 String method
= controller
.Context
.UnderlyingContext
.Request
.HttpMethod
;
282 if (method
!= "POST")
284 throw new Exception("This action cannot be accessed using the verb " + method
);