Got .Field method for MVC working and have macro's rendering inside of RTE in MVC.

Added internal setting for global settings to return an MVC area string based on the umbraco path.
Added a ton of extension methods from v5 that are used in much of the MVC engines.
Added UmbracoHelper methods for MVC rendering including Field so that we can render the correct RTE
field markup when the RTE contains a macro, will add extension methods for the @CurrentPage dynamic object to
do the same to make it consistent.
This commit is contained in:
Shannon Deminick
2012-09-05 09:35:24 +07:00
parent bba5e6a59a
commit 56b7f8d98b
13 changed files with 856 additions and 8 deletions

View File

@@ -150,6 +150,29 @@ namespace Umbraco.Core.Configuration
}
}
/// <summary>
/// This returns the string of the MVC Area route.
/// </summary>
/// <remarks>
/// THIS IS TEMPORARY AND SHOULD BE REMOVED WHEN WE MIGRATE/UPDATE THE CONFIG SETTINGS TO BE A REAL CONFIG SECTION
/// AND SHOULD PROBABLY BE HANDLED IN A MORE ROBUST WAY.
///
/// This will return the MVC area that we will route all custom routes through like surface controllers, etc...
/// We will use the 'Path' (default ~/umbraco) to create it but since it cannot contain '/' and people may specify a path of ~/asdf/asdf/admin
/// we will convert the '/' to '-' and use that as the path. its a bit lame but will work.
/// </remarks>
internal static string MvcArea
{
get
{
if (Path.IsNullOrWhiteSpace())
{
throw new InvalidOperationException("Cannot create an MVC Area path without the umbracoPath specified");
}
return Path.TrimStart('~').TrimStart('/').Replace('/', '-').Trim();
}
}
/// <summary>
/// Gets the path to umbraco's client directory (/umbraco_client by default).
/// This is a relative path to the Umbraco Path as it always must exist beside the 'umbraco'

View File

@@ -0,0 +1,191 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web.Mvc;
namespace Umbraco.Core
{
///<summary>
/// Extension methods for dictionary
///</summary>
internal static class DictionaryExtensions
{
/// <summary>
/// Converts a dictionary to another type by only using direct casting
/// </summary>
/// <typeparam name="TKeyOut"></typeparam>
/// <typeparam name="TValOut"></typeparam>
/// <param name="d"></param>
/// <returns></returns>
public static IDictionary<TKeyOut, TValOut> ConvertTo<TKeyOut, TValOut>(this IDictionary d)
{
var result = new Dictionary<TKeyOut, TValOut>();
foreach (DictionaryEntry v in d)
{
result.Add((TKeyOut)v.Key, (TValOut)v.Value);
}
return result;
}
/// <summary>
/// Converts a dictionary to another type using the specified converters
/// </summary>
/// <typeparam name="TKeyOut"></typeparam>
/// <typeparam name="TValOut"></typeparam>
/// <param name="d"></param>
/// <param name="keyConverter"></param>
/// <param name="valConverter"></param>
/// <returns></returns>
public static IDictionary<TKeyOut, TValOut> ConvertTo<TKeyOut, TValOut>(this IDictionary d, Func<object, TKeyOut> keyConverter, Func<object, TValOut> valConverter)
{
var result = new Dictionary<TKeyOut, TValOut>();
foreach (DictionaryEntry v in d)
{
result.Add(keyConverter(v.Key), valConverter(v.Value));
}
return result;
}
/// <summary>
/// Converts a dictionary to a NameValueCollection
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
public static NameValueCollection ToNameValueCollection(this IDictionary<string, string> d)
{
var n = new NameValueCollection();
foreach (var i in d)
{
n.Add(i.Key, i.Value);
}
return n;
}
/// <summary>
/// Converts a dictionary to a FormCollection
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
public static FormCollection ToFormCollection(this IDictionary<string, object> d)
{
var n = new FormCollection();
foreach (var i in d)
{
n.Add(i.Key, Convert.ToString(i.Value));
}
return n;
}
/// <summary>
/// Returns a new dictionary of this ... others merged leftward.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TK"></typeparam>
/// <typeparam name="TV"></typeparam>
/// <param name="me"></param>
/// <param name="others"></param>
/// <returns></returns>
/// <remarks>
/// Reference: http://stackoverflow.com/questions/294138/merging-dictionaries-in-c
/// </remarks>
public static T MergeLeft<T, TK, TV>(this T me, params IDictionary<TK, TV>[] others)
where T : IDictionary<TK, TV>, new()
{
var newMap = new T();
foreach (var p in (new List<IDictionary<TK, TV>> { me }).Concat(others).SelectMany(src => src))
{
newMap[p.Key] = p.Value;
}
return newMap;
}
/// <summary>
/// Returns the value of the key value based on the key, if the key is not found, a null value is returned
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TVal">The type of the val.</typeparam>
/// <param name="d">The d.</param>
/// <param name="key">The key.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns></returns>
public static TVal GetValue<TKey, TVal>(this IDictionary<TKey, TVal> d, TKey key, TVal defaultValue = default(TVal))
{
if (d.ContainsKey(key))
{
return d[key];
}
return defaultValue;
}
/// <summary>
/// Returns the value of the key value based on the key as it's string value, if the key is not found, then an empty string is returned
/// </summary>
/// <param name="d"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string GetValueAsString<TKey, TVal>(this IDictionary<TKey, TVal> d, TKey key)
{
if (d.ContainsKey(key))
{
return d[key].ToString();
}
return String.Empty;
}
/// <summary>contains key ignore case.</summary>
/// <param name="dictionary">The dictionary.</param>
/// <param name="key">The key.</param>
/// <typeparam name="TValue">Value Type</typeparam>
/// <returns>The contains key ignore case.</returns>
public static bool ContainsKeyIgnoreCase<TValue>(this IDictionary<string, TValue> dictionary, string key)
{
return dictionary.Keys.Any(i => i.Equals(key, StringComparison.CurrentCultureIgnoreCase));
}
/// <summary>
/// Converts a dictionary object to a query string representation such as:
/// firstname=shannon&lastname=deminick
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
public static string ToQueryString(this IDictionary<string, object> d)
{
if (!d.Any()) return "";
var builder = new StringBuilder();
foreach (var i in d)
{
builder.Append(String.Format("{0}={1}&", i.Key, i.Value));
}
return builder.ToString().TrimEnd('&');
}
/// <summary>The get entry ignore case.</summary>
/// <param name="dictionary">The dictionary.</param>
/// <param name="key">The key.</param>
/// <typeparam name="TValue">The type</typeparam>
/// <returns>The entry</returns>
public static TValue GetEntryIgnoreCase<TValue>(this IDictionary<string, TValue> dictionary, string key)
{
return dictionary.GetEntryIgnoreCase(key, default(TValue));
}
/// <summary>The get entry ignore case.</summary>
/// <param name="dictionary">The dictionary.</param>
/// <param name="key">The key.</param>
/// <param name="defaultValue">The default value.</param>
/// <typeparam name="TValue">The type</typeparam>
/// <returns>The entry</returns>
public static TValue GetEntryIgnoreCase<TValue>(this IDictionary<string, TValue> dictionary, string key, TValue defaultValue)
{
key = dictionary.Keys.Where(i => i.Equals(key, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
return !key.IsNullOrWhiteSpace()
? dictionary[key]
: defaultValue;
}
}
}

View File

@@ -10,7 +10,6 @@ using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using System.Text;
using System.Xml;
namespace Umbraco.Core

View File

@@ -38,6 +38,7 @@
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@@ -55,6 +56,7 @@
<Compile Include="Configuration\FileSystemProviderElementCollection.cs" />
<Compile Include="Configuration\FileSystemProvidersSection.cs" />
<Compile Include="CoreBootManager.cs" />
<Compile Include="DictionaryExtensions.cs" />
<Compile Include="Dictionary\CultureDictionaryFactoryResolver.cs" />
<Compile Include="Dictionary\ICultureDictionaryFactory.cs" />
<Compile Include="DocumentPropertyExtensions.cs" />

View File

@@ -0,0 +1,312 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using Umbraco.Core;
using umbraco;
namespace Umbraco.Web
{
/// <summary>
/// HtmlHelper extensions for use in templates
/// </summary>
public static class HtmlHelperRenderExtensions
{
/// <summary>
/// Used for rendering out the Form for BeginUmbracoForm
/// </summary>
internal class UmbracoForm : MvcForm
{
public UmbracoForm(
ViewContext viewContext,
string surfaceController,
string surfaceAction,
string area,
Guid? surfaceId,
object additionalRouteVals = null)
: base(viewContext)
{
//need to create a params string as Base64 to put into our hidden field to use during the routes
string surfaceRouteParams;
if (surfaceId.HasValue)
{
surfaceRouteParams = string.Format("c={0}&a={1}&i={2}&ar={3}",
viewContext.HttpContext.Server.UrlEncode(surfaceController),
viewContext.HttpContext.Server.UrlEncode(surfaceAction),
viewContext.HttpContext.Server.UrlEncode(surfaceId.Value.ToString("N")),
area);
}
else
{
surfaceRouteParams = string.Format("c={0}&a={1}&ar={2}",
viewContext.HttpContext.Server.UrlEncode(surfaceController),
viewContext.HttpContext.Server.UrlEncode(surfaceAction),
area);
}
var additionalRouteValsAsQuery = additionalRouteVals.ToDictionary<object>().ToQueryString();
if (!additionalRouteValsAsQuery.IsNullOrWhiteSpace())
surfaceRouteParams = "&" + additionalRouteValsAsQuery;
_base64String = Convert.ToBase64String(Encoding.UTF8.GetBytes(surfaceRouteParams));
_textWriter = viewContext.Writer;
}
private bool _disposed;
private readonly string _base64String;
private readonly TextWriter _textWriter;
protected override void Dispose(bool disposing)
{
if (this._disposed)
return;
this._disposed = true;
//write out the hidden surface form routes
_textWriter.Write("<input name='uformpostroutevals' type='hidden' value='" + _base64String + "' />");
base.Dispose(disposing);
}
}
public static MvcHtmlString EditorFor<T>(this HtmlHelper htmlHelper, string templateName = "", string htmlFieldName = "", object additionalViewData = null)
where T : new()
{
var model = new T();
var typedHelper = new HtmlHelper<T>(
htmlHelper.ViewContext.CopyWithModel(model),
htmlHelper.ViewDataContainer.CopyWithModel(model));
return typedHelper.EditorFor(x => model, templateName, htmlFieldName, additionalViewData);
}
/// <summary>
/// A validation summary that lets you pass in a prefix so that the summary only displays for elements
/// containing the prefix. This allows you to have more than on validation summary on a page.
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="prefix"></param>
/// <param name="excludePropertyErrors"></param>
/// <param name="message"></param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper,
string prefix = "",
bool excludePropertyErrors = false,
string message = "",
IDictionary<string, object> htmlAttributes = null)
{
if (prefix.IsNullOrWhiteSpace())
{
return htmlHelper.ValidationSummary(excludePropertyErrors, message, htmlAttributes);
}
//if there's a prefix applied, we need to create a new html helper with a filtered ModelState collection so that it only looks for
//specific model state with the prefix.
var filteredHtmlHelper = new HtmlHelper(htmlHelper.ViewContext, htmlHelper.ViewDataContainer.FilterContainer(prefix));
return filteredHtmlHelper.ValidationSummary(excludePropertyErrors, message, htmlAttributes);
}
/// <summary>
/// Helper method to create a new form to execute in the Umbraco request pipeline against a locally declared controller
/// </summary>
/// <param name="html"></param>
/// <param name="action"></param>
/// <param name="controllerName"></param>
/// <returns></returns>
public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName)
{
return html.BeginUmbracoForm(action, controllerName, null, new Dictionary<string, object>());
}
/// <summary>
/// Helper method to create a new form to execute in the Umbraco request pipeline against a locally declared controller
/// </summary>
/// <param name="html"></param>
/// <param name="action"></param>
/// <param name="controllerName"></param>
/// <param name="additionalRouteVals"></param>
/// <returns></returns>
public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName,
object additionalRouteVals)
{
return html.BeginUmbracoForm(action, controllerName, additionalRouteVals, new Dictionary<string, object>());
}
/// <summary>
/// Helper method to create a new form to execute in the Umbraco request pipeline against a locally declared controller
/// </summary>
/// <param name="html"></param>
/// <param name="action"></param>
/// <param name="controllerName"></param>
/// <param name="additionalRouteVals"></param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName,
object additionalRouteVals,
object htmlAttributes)
{
return html.BeginUmbracoForm(action, controllerName, additionalRouteVals, htmlAttributes.ToDictionary<object>());
}
/// <summary>
/// Helper method to create a new form to execute in the Umbraco request pipeline against a locally declared controller
/// </summary>
/// <param name="html"></param>
/// <param name="action"></param>
/// <param name="controllerName"></param>
/// <param name="additionalRouteVals"></param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName,
object additionalRouteVals,
IDictionary<string, object> htmlAttributes)
{
var settings = DependencyResolver.Current.GetService<UmbracoSettings>();
var area = Umbraco.Core.Configuration.GlobalSettings.MvcArea;
var formAction = html.ViewContext.HttpContext.Request.Url.AbsolutePath;
return html.RenderForm(formAction, FormMethod.Post, htmlAttributes, controllerName, action, area, null);
}
/// <summary>
/// Helper method to create a new form to execute in the Umbraco request pipeline to a surface controller plugin
/// </summary>
/// <param name="html"></param>
/// <param name="action"></param>
/// <param name="surfaceId">The surface controller to route to</param>
/// <returns></returns>
public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Guid surfaceId)
{
return html.BeginUmbracoForm(action, surfaceId, null, new Dictionary<string, object>());
}
/// <summary>
/// Helper method to create a new form to execute in the Umbraco request pipeline to a surface controller plugin
/// </summary>
/// <param name="html"></param>
/// <param name="action"></param>
/// <param name="surfaceId">The surface controller to route to</param>
/// <param name="additionalRouteVals"></param>
/// <returns></returns>
public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Guid surfaceId,
object additionalRouteVals)
{
return html.BeginUmbracoForm(action, surfaceId, additionalRouteVals, new Dictionary<string, object>());
}
/// <summary>
/// Helper method to create a new form to execute in the Umbraco request pipeline to a surface controller plugin
/// </summary>
/// <param name="html"></param>
/// <param name="action"></param>
/// <param name="surfaceId">The surface controller to route to</param>
/// <param name="additionalRouteVals"></param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Guid surfaceId,
object additionalRouteVals,
object htmlAttributes)
{
return html.BeginUmbracoForm(action, surfaceId, additionalRouteVals, htmlAttributes.ToDictionary<object>());
}
/// <summary>
/// Helper method to create a new form to execute in the Umbraco request pipeline to a surface controller plugin
/// </summary>
/// <param name="html"></param>
/// <param name="action"></param>
/// <param name="surfaceId">The surface controller to route to</param>
/// <param name="additionalRouteVals"></param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Guid surfaceId,
object additionalRouteVals,
IDictionary<string, object> htmlAttributes)
{
//TODO: Make me work :)
throw new NotImplementedException();
//var settings = DependencyResolver.Current.GetService<UmbracoSettings>();
//var area = Umbraco.Core.Configuration.GlobalSettings.MvcArea;
//var formAction = html.ViewContext.HttpContext.Request.Url.AbsolutePath;
//var surfaceMetadata = DependencyResolver.Current.GetService<ComponentRegistrations>()
// .SurfaceControllers
// .Where(x => x.Metadata.Id == surfaceId)
// .SingleOrDefault();
//if (surfaceMetadata == null)
// throw new InvalidOperationException("Could not find the surface controller with id " + surfaceId);
////now, need to figure out what area this surface controller belongs too...
//var pluginDefition = surfaceMetadata.Metadata.PluginDefinition;
//if (pluginDefition.HasRoutablePackageArea())
//{
// //a plugin def CAN be null, if the plugin is actually in our Web DLL folder or if someone drops their
// //dll into the bin... though if they do that it still wont work since they wont get an area registered.
// //area = PluginManager.GetPackageFolderFromPluginDll(pluginDefition.OriginalAssemblyFile).Name;
// area = pluginDefition.PackageName;
//}
////render the form
//return html.RenderForm(formAction, FormMethod.Post, htmlAttributes, surfaceMetadata.Metadata.ControllerName, action, area, surfaceId);
}
/// <summary>
/// This renders out the form for us
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="formAction"></param>
/// <param name="method"></param>
/// <param name="htmlAttributes"></param>
/// <param name="surfaceController"></param>
/// <param name="surfaceAction"></param>
/// <param name="area"></param>
/// <param name="surfaceId"></param>
/// <param name="additionalRouteVals"></param>
/// <returns></returns>
/// <remarks>
/// This code is pretty much the same as the underlying MVC code that writes out the form
/// </remarks>
private static MvcForm RenderForm(this HtmlHelper htmlHelper,
string formAction,
FormMethod method,
IDictionary<string, object> htmlAttributes,
string surfaceController,
string surfaceAction,
string area,
Guid? surfaceId,
object additionalRouteVals = null)
{
var tagBuilder = new TagBuilder("form");
tagBuilder.MergeAttributes(htmlAttributes);
// action is implicitly generated, so htmlAttributes take precedence.
tagBuilder.MergeAttribute("action", formAction);
// method is an explicit parameter, so it takes precedence over the htmlAttributes.
tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
var traditionalJavascriptEnabled = htmlHelper.ViewContext.ClientValidationEnabled && !htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled;
if (traditionalJavascriptEnabled)
{
// forms must have an ID for client validation
tagBuilder.GenerateId("form" + Guid.NewGuid().ToString("N"));
}
htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
//new UmbracoForm:
var theForm = new UmbracoForm(htmlHelper.ViewContext, surfaceController, surfaceAction, area, surfaceId, additionalRouteVals);
if (traditionalJavascriptEnabled)
{
htmlHelper.ViewContext.FormContext.FormId = tagBuilder.Attributes["id"];
}
return theForm;
}
}
}

View File

@@ -0,0 +1,76 @@
using System.Linq;
using System.Web.Mvc;
namespace Umbraco.Web
{
internal static class ModelStateExtensions
{
/// <summary>
/// Merges ModelState that has names matching the prefix
/// </summary>
/// <param name="state"></param>
/// <param name="dictionary"></param>
/// <param name="prefix"></param>
public static void Merge(this ModelStateDictionary state, ModelStateDictionary dictionary, string prefix)
{
if (dictionary == null)
return;
foreach (var keyValuePair in dictionary.Where(keyValuePair => keyValuePair.Key.StartsWith(prefix + ".")))
{
state[keyValuePair.Key] = keyValuePair.Value;
}
}
/// <summary>
/// Checks if there are any model errors on any fields containing the prefix
/// </summary>
/// <param name="state"></param>
/// <param name="prefix"></param>
/// <returns></returns>
public static bool IsValid(this ModelStateDictionary state, string prefix)
{
return state.Where(v => v.Key.StartsWith(prefix + ".")).All(v => !v.Value.Errors.Any());
}
//NOTE: we used this alot in v5 when we had editors in MVC, this was really handy for knockout editors using JS
///// <summary>
///// Adds an error to the model state that has to do with data validation, this is generally used for JSON responses
///// </summary>
///// <param name="state"></param>
///// <param name="errorMessage"></param>
//public static void AddDataValidationError(this ModelStateDictionary state, string errorMessage)
//{
// state.AddModelError("DataValidation", errorMessage);
//}
/// <summary>
/// Serializes the ModelState to JSON for JavaScript to interrogate the errors
/// </summary>
/// <param name="state"></param>
/// <returns></returns>
public static JsonResult ToJsonErrors(this ModelStateDictionary state)
{
return new JsonResult
{
Data = new
{
success = state.IsValid.ToString().ToLower(),
failureType = "ValidationError",
validationErrors = from e in state
where e.Value.Errors.Count > 0
select new
{
name = e.Key,
errors = e.Value.Errors.Select(x => x.ErrorMessage)
.Concat(
e.Value.Errors.Where(x => x.Exception != null).Select(x => x.Exception.Message))
}
}
};
}
}
}

View File

@@ -0,0 +1,13 @@
namespace Umbraco.Web
{
/// <summary>
/// Used in the .Field method when rendering an Umbraco field to specify what case type it should be
/// </summary>
public enum RenderFieldCaseType
{
Upper,
Lower,
Title,
Unchanged
}
}

View File

@@ -0,0 +1,13 @@
namespace Umbraco.Web
{
/// <summary>
/// Used in the .Field method to render an Umbraco field to specify what encoding to use
/// </summary>
public enum RenderFieldEncodingType
{
Url,
Html,
Unchanged
}
}

View File

@@ -246,6 +246,7 @@
<Compile Include="FormlessPage.cs">
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="HtmlHelperRenderExtensions.cs" />
<Compile Include="IApplicationEventHandler.cs" />
<Compile Include="Media\EmbedProviders\AbstractOEmbedProvider.cs" />
<Compile Include="Media\EmbedProviders\AbstractProvider.cs" />
@@ -255,6 +256,9 @@
<Compile Include="Media\EmbedProviders\Settings\Dictionary.cs" />
<Compile Include="Media\EmbedProviders\Settings\String.cs" />
<Compile Include="Media\EmbedProviders\Twitgoo.cs" />
<Compile Include="ModelStateExtensions.cs" />
<Compile Include="RenderFieldCaseType.cs" />
<Compile Include="RenderFieldEncodingType.cs" />
<Compile Include="Routing\LookupByPageIdQuery.cs" />
<Compile Include="umbraco.presentation\Default.aspx.cs">
<SubType>ASPXCodeBehind</SubType>
@@ -262,6 +266,8 @@
<Compile Include="umbraco.presentation\LibraryCacheRefresher.cs" />
<Compile Include="umbraco.presentation\umbraco\nodeFactory\UmbracoSiteMapProviderAccessUpdate.cs" />
<Compile Include="UmbracoHelper.cs" />
<Compile Include="ViewContextExtensions.cs" />
<Compile Include="ViewDataContainerExtensions.cs" />
<Compile Include="XmlPublishedContentStore.cs" />
<Compile Include="ContentStoreResolver.cs" />
<Compile Include="DocumentNotFoundHttpHandler.cs" />

View File

@@ -2,8 +2,13 @@
using System.Collections;
using System.IO;
using System.Web;
using System.Web.Configuration;
using System.Web.UI;
using Umbraco.Core;
using Umbraco.Core.Models;
using umbraco;
using System.Collections.Generic;
using umbraco.presentation.templateControls;
namespace Umbraco.Web
{
@@ -13,10 +18,12 @@ namespace Umbraco.Web
public class UmbracoHelper
{
private readonly UmbracoContext _umbracoContext;
private readonly IDocument _currentPage;
internal UmbracoHelper(UmbracoContext umbracoContext)
{
_umbracoContext = umbracoContext;
_currentPage = _umbracoContext.DocumentRequest.Document;
}
@@ -62,11 +69,133 @@ namespace Umbraco.Web
UmbracoContext.Current.DocumentRequest.UmbracoPage.Elements,
_umbracoContext.PageId.Value);
containerPage.Controls.Add(macroControl);
var output = new StringWriter();
_umbracoContext.HttpContext.Server.Execute(containerPage, output, false);
return new HtmlString(output.ToString());
using (var output = new StringWriter())
{
_umbracoContext.HttpContext.Server.Execute(containerPage, output, false);
return new HtmlString(output.ToString());
}
}
#endregion
#region Field
/// <summary>
/// Renders an field to the template
/// </summary>
/// <param name="fieldAlias"></param>
/// <param name="valueAlias"></param>
/// <param name="altFieldAlias"></param>
/// <param name="altValueAlias"></param>
/// <param name="altText"></param>
/// <param name="insertBefore"></param>
/// <param name="insertAfter"></param>
/// <param name="recursive"></param>
/// <param name="convertLineBreaks"></param>
/// <param name="removeParagraphTags"></param>
/// <param name="casing"></param>
/// <param name="encoding"></param>
/// <param name="formatString"></param>
/// <returns></returns>
public IHtmlString Field(string fieldAlias, string valueAlias = "",
string altFieldAlias = "", string altValueAlias = "", string altText = "", string insertBefore = "", string insertAfter = "",
bool recursive = false, bool convertLineBreaks = false, bool removeParagraphTags = false,
RenderFieldCaseType casing = RenderFieldCaseType.Unchanged,
RenderFieldEncodingType encoding = RenderFieldEncodingType.Unchanged,
string formatString = "")
{
return Field(_currentPage, fieldAlias, valueAlias, altFieldAlias, altValueAlias,
altText, insertBefore, insertAfter, recursive, convertLineBreaks, removeParagraphTags,
casing, encoding, formatString);
}
/// <summary>
/// Renders an field to the template
/// </summary>
/// <param name="currentPage"></param>
/// <param name="fieldAlias"></param>
/// <param name="valueAlias"></param>
/// <param name="altFieldAlias"></param>
/// <param name="altValueAlias"></param>
/// <param name="altText">TODO: This currently doesn't do anything!! we should implement it... it is static text that is displayed if all else fails</param>
/// <param name="insertBefore"></param>
/// <param name="insertAfter"></param>
/// <param name="recursive"></param>
/// <param name="convertLineBreaks"></param>
/// <param name="removeParagraphTags"></param>
/// <param name="casing"></param>
/// <param name="encoding"></param>
/// <param name="formatString"></param>
/// <returns></returns>
public IHtmlString Field(IDocument currentPage, string fieldAlias, string valueAlias = "",
string altFieldAlias = "", string altValueAlias = "", string altText = "", string insertBefore = "", string insertAfter = "",
bool recursive = false, bool convertLineBreaks = false, bool removeParagraphTags = false,
RenderFieldCaseType casing = RenderFieldCaseType.Unchanged,
RenderFieldEncodingType encoding = RenderFieldEncodingType.Unchanged,
string formatString = "")
{
//TODO: This is real nasty and we should re-write the 'item' and 'ItemRenderer' class but si fine for now
var attributes = new Dictionary<string, string>
{
{"field", fieldAlias},
{"recursive", recursive.ToString().ToLowerInvariant()},
{"useIfEmpty", altFieldAlias},
{"textIfEmpty", altText},
{"stripParagraph", removeParagraphTags.ToString().ToLowerInvariant()},
{
"case", casing == RenderFieldCaseType.Lower ? "lower"
: casing == RenderFieldCaseType.Upper ? "upper"
: casing == RenderFieldCaseType.Title ? "title"
: string.Empty
},
{"insertTextBefore", insertBefore},
{"insertTextAfter", insertAfter},
{"convertLineBreaks", convertLineBreaks.ToString().ToLowerInvariant()}
};
switch (encoding)
{
case RenderFieldEncodingType.Url:
attributes.Add("urlEncode", "true");
break;
case RenderFieldEncodingType.Html:
attributes.Add("htmlEncode", "true");
break;
case RenderFieldEncodingType.Unchanged:
default:
break;
}
//need to convert our dictionary over to this weird dictionary type
var attributesForItem = new AttributeCollectionAdapter(
new AttributeCollection(
new StateBag()));
foreach(var i in attributes)
{
attributesForItem.Add(i.Key, i.Value);
}
var item = new Item()
{
Field = fieldAlias,
TextIfEmpty = altText,
LegacyAttributes = attributesForItem
};
var containerPage = new FormlessPage();
containerPage.Controls.Add(item);
using (var output = new StringWriter())
using (var htmlWriter = new HtmlTextWriter(output))
{
ItemRenderer.Instance.Init(item);
ItemRenderer.Instance.Load(item);
ItemRenderer.Instance.Render(item, htmlWriter);
_umbracoContext.HttpContext.Server.Execute(containerPage, output, false);
return new HtmlString(output.ToString());
}
}
#endregion
}
}

View File

@@ -0,0 +1,34 @@
using System.Web.Mvc;
namespace Umbraco.Web
{
internal static class ViewContextExtensions
{
/// <summary>
/// Creates a new ViewContext from an existing one but specifies a new Model for the ViewData
/// </summary>
/// <param name="vc"></param>
/// <param name="model"></param>
/// <returns></returns>
public static ViewContext CopyWithModel(this ViewContext vc, object model)
{
return new ViewContext
{
Controller = vc.Controller,
HttpContext = vc.HttpContext,
RequestContext = vc.RequestContext,
RouteData = vc.RouteData,
TempData = vc.TempData,
View = vc.View,
ViewData = new ViewDataDictionary(vc)
{
Model = model
},
FormContext = vc.FormContext,
ClientValidationEnabled = vc.ClientValidationEnabled,
UnobtrusiveJavaScriptEnabled = vc.UnobtrusiveJavaScriptEnabled,
Writer = vc.Writer
};
}
}
}

View File

@@ -0,0 +1,47 @@
using System.Web.Mvc;
namespace Umbraco.Web
{
internal static class ViewDataContainerExtensions
{
private class ViewDataContainer : IViewDataContainer
{
public ViewDataContainer()
{
ViewData = new ViewDataDictionary();
}
public ViewDataDictionary ViewData { get; set; }
}
/// <summary>
/// Creates a new IViewDataContainer but with a filtered ModelState
/// </summary>
/// <param name="container"></param>
/// <param name="prefix"></param>
/// <returns></returns>
public static IViewDataContainer FilterContainer(this IViewDataContainer container, string prefix)
{
var newContainer = new ViewDataContainer();
newContainer.ViewData.ModelState.Merge(container.ViewData.ModelState, prefix);
return newContainer;
}
/// <summary>
/// Returns a new IViewContainer based on the current one but supplies a different model to the ViewData
/// </summary>
/// <param name="container"></param>
/// <param name="model"></param>
/// <returns></returns>
public static IViewDataContainer CopyWithModel(this IViewDataContainer container, object model)
{
return new ViewDataContainer
{
ViewData = new ViewDataDictionary(container.ViewData)
{
Model = model
}
};
}
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Web;
using System.Xml;
using Umbraco.Core;
namespace umbraco
{
@@ -121,10 +122,12 @@ namespace umbraco
}
// CASING
if (helper.FindAttribute(attributes, "case") == "lower")
_fieldContent = _fieldContent.ToLower();
else if (helper.FindAttribute(attributes, "case") == "upper")
_fieldContent = _fieldContent.ToUpper();
if (helper.FindAttribute(attributes, "case") == "lower")
_fieldContent = _fieldContent.ToLower();
else if (helper.FindAttribute(attributes, "case") == "upper")
_fieldContent = _fieldContent.ToUpper();
else if (helper.FindAttribute(attributes, "case") == "title")
_fieldContent = _fieldContent.ConvertCase(StringAliasCaseType.PascalCase);
// OTHER FORMATTING FUNCTIONS
// If we use masterpages, this is moved to the ItemRenderer to add support for before/after in inline XSLT