Minor changes to improve testability of helpers
[castle.git] / MonoRail / Castle.MonoRail.Framework / Helpers / AbstractHelper.cs
bloba752acec0048fe70d9a140790d8983e6e9e27ccf
1 // Copyright 2004-2007 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.Helpers
17 using System;
18 using System.Collections.Specialized;
19 using System.Text;
20 using System.Collections;
21 using Castle.MonoRail.Framework.Internal;
23 /// <summary>
24 /// Optional base class for helpers.
25 /// Extend from this class only if your helpers needs
26 /// a reference to the controller which is using it or
27 /// if you need to use one of the protected utility methods.
28 /// </summary>
29 public abstract class AbstractHelper : IControllerAware
31 private const string MonoRailVersion = "RC3_0006";
33 #region Controller Reference
35 /// <summary>
36 /// Store's <see cref="Controller"/> for the current view.
37 /// </summary>
38 private Controller controller;
40 private UrlHelper urlHelper;
42 private IServerUtility serverUtility;
44 private IRailsEngineContext context;
46 /// <summary>
47 /// Sets the controller.
48 /// </summary>
49 /// <param name="controller">Current view's <see cref="Controller"/>.</param>
50 public virtual void SetController(Controller controller)
52 this.controller = controller;
54 if (controller.Context != null) // It will be null when invoked from test cases
56 context = controller.Context;
57 serverUtility = controller.Context.Server;
61 /// <summary>
62 /// Gets the controller.
63 /// </summary>
64 /// <value>The <see cref="Controller"/> used with the current view.</value>
65 public Controller Controller
67 get { return controller; }
70 #endregion
72 /// <summary>
73 /// Gets or sets the server utility.
74 /// </summary>
75 /// <value>The server utility.</value>
76 public IServerUtility ServerUtility
78 get { return serverUtility; }
79 set { serverUtility = value; }
82 /// <summary>
83 /// Gets the URL helper instance.
84 /// </summary>
85 /// <value>The URL helper.</value>
86 public UrlHelper UrlHelper
88 get { return urlHelper ?? (UrlHelper) controller.Helpers["UrlHelper"]; }
89 set { urlHelper = value; }
92 /// <summary>
93 /// Merges <paramref name="userOptions"/> with <paramref name="defaultOptions"/> placing results in
94 /// <paramref name="userOptions"/>.
95 /// </summary>
96 /// <param name="userOptions">The user options.</param>
97 /// <param name="defaultOptions">The default options.</param>
98 /// <remarks>
99 /// All <see cref="IDictionary.Values"/> and <see cref="IDictionary.Keys"/> in <paramref name="defaultOptions"/>
100 /// are copied to <paramref name="userOptions"/>. Entries with the same <see cref="DictionaryEntry.Key"/> in
101 /// <paramref name="defaultOptions"/> and <paramref name="userOptions"/> are skipped.
102 /// </remarks>
103 protected void MergeOptions(IDictionary userOptions, IDictionary defaultOptions)
105 CommonUtils.MergeOptions(userOptions, defaultOptions);
108 /// <summary>
109 /// Gets the current context.
110 /// </summary>
111 /// <value>The current context.</value>
112 public IRailsEngineContext CurrentContext
114 get { return context; }
115 set { context = value; }
118 #region Helper methods
120 /// <summary>
121 /// Renders the a script block with a <c>src</c> attribute
122 /// pointing to the url. The url must not have an extension.
123 /// <para>
124 /// For example, suppose you invoke it like:
125 /// <code>
126 /// RenderScriptBlockToSource("/my/url/to/my/scripts");
127 /// </code>
128 /// </para>
129 /// <para>
130 /// That will render
131 /// <code><![CDATA[
132 /// <script type="text/javascript" src="/my/url/to/my/scripts.rails?VERSIONID"></script>
133 /// ]]>
134 /// </code>
135 /// As you see the file extension will be inferred
136 /// </para>
137 /// </summary>
138 /// <param name="url">The url for the scripts (should start with a '/')</param>
139 /// <returns>An script block pointing to the given url.</returns>
140 protected string RenderScriptBlockToSource(string url)
142 return string.Format("<script type=\"text/javascript\" src=\"{0}.{1}?" + MonoRailVersion + "\"></script>",
143 context.ApplicationPath + url, context.UrlInfo.Extension);
146 /// <summary>
147 /// Renders the a script block with a <c>src</c> attribute
148 /// pointing to the url sending the querystring as parameter. The url must not have an extension.
149 /// <para>
150 /// For example, suppose you invoke it like:
151 /// <code>
152 /// RenderScriptBlockToSource("/my/url/to/my/scripts", "locale=pt-br");
153 /// </code>
154 /// </para>
155 /// <para>
156 /// That will render
157 /// <code><![CDATA[
158 /// <script type="text/javascript" src="/my/url/to/my/scripts.rails?VERSIONID&locale=pt-br"></script>
159 /// ]]>
160 /// </code>
161 /// As you see the file extension will be inferred
162 /// </para>
163 /// </summary>
164 /// <param name="url">The url for the scripts (should start with a '/')</param>
165 /// <param name="queryString">The query string.</param>
166 /// <returns>An script block pointing to the given url.</returns>
167 protected string RenderScriptBlockToSource(string url, string queryString)
169 if (queryString != null && queryString != string.Empty)
171 queryString = "&" + queryString;
174 return string.Format("<script type=\"text/javascript\" src=\"{0}.{1}?" + MonoRailVersion + "{2}\"></script>",
175 context.ApplicationPath + url, context.UrlInfo.Extension, queryString);
178 /// <summary>
179 /// Generates HTML element attributes string from <paramref name="attributes"/>.
180 /// <code>key1="value1" key2</code>
181 /// </summary>
182 /// <param name="attributes">The attributes for the element.</param>
183 /// <returns><see cref="String"/> to use inside HTML element's tag.</returns>
184 /// <remarks>
185 /// <see cref="string.Empty"/> is returned if <paramref name="attributes"/> is <c>null</c> or empty.
186 /// <para>
187 /// If for some <see cref="DictionaryEntry.Key"/> <see cref="DictionaryEntry.Value"/> is <c>null</c> or
188 /// <see cref="string.Empty"/> only attribute name is appended to the string.
189 /// </para>
190 /// </remarks>
191 protected string GetAttributes(IDictionary attributes)
193 if (attributes == null || attributes.Count == 0) return string.Empty;
195 StringBuilder contents = new StringBuilder();
197 foreach(DictionaryEntry entry in attributes)
199 if (entry.Value == null || entry.Value.ToString() == string.Empty)
201 contents.Append(entry.Key);
203 else
205 contents.AppendFormat("{0}=\"{1}\"", entry.Key, entry.Value);
207 contents.Append(' ');
210 return contents.ToString();
213 /// <summary>
214 /// Builds a query string encoded.
215 /// </summary>
216 /// <remarks>
217 /// Supports multi-value query strings, using any
218 /// <see cref="IEnumerable"/> as a value.
219 /// <example>
220 /// <code>
221 /// IDictionary dict = new Hashtable();
222 /// dict.Add("id", 5);
223 /// dict.Add("selectedItem", new int[] { 2, 4, 99 });
224 /// string querystring = BuildQueryString(dict);
225 /// // should result in: "id=5&amp;selectedItem=2&amp;selectedItem=4&amp;selectedItem=99"
226 /// </code>
227 /// </example>
228 /// </remarks>
229 /// <param name="parameters">The parameters</param>
230 public string BuildQueryString(IDictionary parameters)
232 return CommonUtils.BuildQueryString(serverUtility, parameters, true);
235 /// <summary>
236 /// Builds a query string encoded.
237 /// </summary>
238 /// <remarks>
239 /// Supports multi-value query strings, using any
240 /// <see cref="IEnumerable"/> as a value.
241 /// </remarks>
242 /// <param name="parameters">The parameters</param>
243 public string BuildQueryString(NameValueCollection parameters)
245 return CommonUtils.BuildQueryString(serverUtility, parameters, true);
248 /// <summary>
249 /// Concat two string in a query string format (<c>key=value&amp;key2=value2</c>)
250 /// building a third string with the result
251 /// </summary>
252 /// <param name="leftParams">key values</param>
253 /// <param name="rightParams">key values</param>
254 /// <returns>The concatenation result</returns>
255 protected string ConcatQueryString(string leftParams, string rightParams)
257 if (leftParams == null || leftParams.Length == 0)
259 return rightParams;
261 if (rightParams == null || rightParams.Length == 0)
263 return leftParams;
266 if (leftParams.EndsWith("&") || leftParams.EndsWith("&amp;"))
268 leftParams = leftParams.Substring( 0, leftParams.Length - 1 );
271 return string.Format("{0}&amp;{1}", leftParams, rightParams);
274 /// <summary>
275 /// HTML encodes a string and returns the encoded string.
276 /// </summary>
277 /// <param name="content">The text string to HTML encode.</param>
278 /// <returns>The HTML encoded text.</returns>
279 public virtual string HtmlEncode(string content)
281 return serverUtility.HtmlEncode(content);
284 /// <summary>
285 /// Escapes a content replacing line breaks with html break lines.
286 /// </summary>
287 /// <param name="content">The text to escape.</param>
288 /// <returns>The URL encoded and JavaScript escaped text.</returns>
289 public String LineBreaksToHtml(String content)
291 if (content == null) return string.Empty;
293 // TODO: Replace by a regular expression, which should be much more efficient
295 return content.Replace("\r", "").Replace("\n", "<br/>");
298 /// <summary>
299 /// URL encodes a string and returns the encoded string.
300 /// </summary>
301 /// <param name="content">The text to URL encode.</param>
302 /// <returns>The URL encoded text.</returns>
303 public virtual string UrlEncode(string content)
305 return serverUtility.UrlEncode(content);
308 /// <summary>
309 /// URL encodes the path portion of a URL string and returns the encoded string.
310 /// </summary>
311 /// <param name="content">The text to URL encode.</param>
312 /// <returns>The URL encoded text.</returns>
313 public string UrlPathEncode(string content)
315 return serverUtility.UrlPathEncode(content);
318 /// <summary>
319 /// Escapes JavaScript with Url encoding and returns the encoded string.
320 /// </summary>
321 /// <remarks>
322 /// Converts quotes, single quotes and CR/LFs to their representation as an escape character.
323 /// </remarks>
324 /// <param name="content">The text to URL encode and escape JavaScript within.</param>
325 /// <returns>The URL encoded and JavaScript escaped text.</returns>
326 public string JavaScriptEscape(string content)
328 if (string.IsNullOrEmpty(content)) return content;
330 return serverUtility.JavaScriptEscape(content);
333 /// <summary>
334 /// Builds a JS associative array based on the specified dictionary instance.
335 /// <para>
336 /// For example: <c>{name: value, other: 'another'}</c>
337 /// </para>
338 /// </summary>
339 /// <param name="jsOptions">The js options.</param>
340 /// <returns>An associative array in javascript</returns>
341 public static string JavascriptOptions(IDictionary jsOptions)
343 if (jsOptions == null || jsOptions.Count == 0)
345 return "{}";
348 StringBuilder sb = new StringBuilder(jsOptions.Count * 10);
349 sb.Append("{");
350 bool comma = false;
352 foreach (DictionaryEntry entry in jsOptions)
354 if (!comma) comma = true; else sb.Append(", ");
356 sb.Append(string.Format("{0}:{1}", entry.Key, entry.Value));
359 sb.Append("}");
360 return sb.ToString();
363 /// <summary>
364 /// Generates script block.
365 /// <code>
366 /// &lt;script type=\"text/javascript\"&gt;
367 /// scriptContents
368 /// &lt;/script&gt;
369 /// </code>
370 /// </summary>
371 /// <param name="scriptContents">The script contents.</param>
372 /// <returns><paramref name="scriptContents"/> placed inside <b>script</b> tags.</returns>
373 public static string ScriptBlock(string scriptContents)
375 return "\r\n<script type=\"text/javascript\">\r\n" + scriptContents + "</script>\r\n";
378 /// <summary>
379 /// Quotes the specified string with double quotes
380 /// </summary>
381 /// <param name="content">The content.</param>
382 /// <returns>A quoted string</returns>
383 public static string Quote(object content)
385 return "\"" + content + "\"";
388 /// <summary>
389 /// Quotes the specified string with singdoublele quotes
390 /// </summary>
391 /// <param name="items">Items to quote</param>
392 /// <returns>A quoted string</returns>
393 public static string[] Quote(object[] items)
395 string[] quotedItems = new string[items.Length];
397 int index = 0;
399 foreach(string item in items)
401 quotedItems[index++] = Quote(item);
404 return quotedItems;
407 /// <summary>
408 /// Quotes the specified string with double quotes
409 /// </summary>
410 /// <param name="content">The content.</param>
411 /// <returns>A quoted string</returns>
412 public static string SQuote(object content)
414 return "\'" + content + "\'";
417 #endregion