diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..212566614 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Services/ContentFieldDisplay.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Services/ContentFieldDisplay.cs index b1bb4212c..a360722ac 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Services/ContentFieldDisplay.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Services/ContentFieldDisplay.cs @@ -33,7 +33,7 @@ namespace Orchard.Layouts.Services { var drivers = GetFieldDrivers(field.FieldDefinition.Name); drivers.Invoke(driver => { - var result = driver.BuildDisplayShape(context); + var result = Filter(driver.BuildDisplayShape(context), field); if (result != null) result.Apply(context); }, Logger); @@ -67,6 +67,21 @@ namespace Orchard.Layouts.Services { return context.Shape; } + private DriverResult Filter(DriverResult driverResult, ContentField field) { + DriverResult result = null; + var combinedResult = driverResult as CombinedResult; + var contentShapeResult = driverResult as ContentShapeResult; + + if (combinedResult != null) { + result = combinedResult.GetResults().SingleOrDefault(x => x.ContentField != null && x.ContentField.Name == field.Name); + } + else if (contentShapeResult != null) { + result = contentShapeResult.ContentField != null && contentShapeResult.ContentField.Name == field.Name ? contentShapeResult : driverResult; + } + + return result; + } + private IEnumerable GetFieldDrivers(string fieldName) { return _contentFieldDrivers.Where(x => x.GetType().BaseType.GenericTypeArguments[0].Name == fieldName); } diff --git a/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs b/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs index c3aa49228..f6b7e3f08 100644 --- a/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs +++ b/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs @@ -1,475 +1,475 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Web.Mvc; -using System.Web.Mvc.Html; -using System.Web.Routing; -using Orchard.Localization; -using Orchard.Utility; -using Orchard.Utility.Extensions; -using System.Web; - -namespace Orchard.Mvc.Html { - public static class HtmlHelperExtensions { - - public static string NameOf(this HtmlHelper html, Expression> expression) { - return Reflect.NameOf(html.ViewData.Model, expression); - } - - public static string NameOf(this HtmlHelper html, Expression> expression) { - return Reflect.NameOf(html.ViewData.Model, expression); - } - - public static string FieldNameFor(this HtmlHelper html, Expression> expression) { - return html.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)); - } - - public static string FieldIdFor(this HtmlHelper html, Expression> expression) { - var id = html.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression)); - // because "[" and "]" aren't replaced with "_" in GetFullHtmlFieldId - return id.Replace('[', '_').Replace(']', '_'); - } - - public static IHtmlString LabelFor(this HtmlHelper html, Expression> expression, LocalizedString labelText) { - return LabelFor(html, expression, labelText.ToString()); - } - - public static IHtmlString LabelFor(this HtmlHelper html, Expression> expression, string labelText) { - if (String.IsNullOrEmpty(labelText)) { - return MvcHtmlString.Empty; - } - var htmlFieldName = ExpressionHelper.GetExpressionText(expression); - var tag = new TagBuilder("label"); - tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName)); - tag.SetInnerText(labelText); - return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal)); - } - - public static MvcHtmlString SelectOption(this HtmlHelper html, T currentValue, T optionValue, string text) { - return SelectOption(html, optionValue, object.Equals(optionValue, currentValue), text); - } - - public static MvcHtmlString SelectOption(this HtmlHelper html, T currentValue, T optionValue, string text, object htmlAttributes) { - return SelectOption(html, optionValue, object.Equals(optionValue, currentValue), text, new RouteValueDictionary(htmlAttributes)); - } - - public static MvcHtmlString SelectOption(this HtmlHelper html, T currentValue, T optionValue, string text, RouteValueDictionary htmlAttributes) { - return SelectOption(html, optionValue, object.Equals(optionValue, currentValue), text, htmlAttributes); - } - - public static MvcHtmlString SelectOption(this HtmlHelper html, object optionValue, bool selected, string text) { - return SelectOption(html, optionValue, selected, text, null); - } - - public static MvcHtmlString SelectOption(this HtmlHelper html, object optionValue, bool selected, string text, object htmlAttributes) { - return SelectOption(html, optionValue, selected, text, new RouteValueDictionary(htmlAttributes)); - } - - public static MvcHtmlString SelectOption(this HtmlHelper html, object optionValue, bool selected, string text, RouteValueDictionary htmlAttributes) { - var builder = new TagBuilder("option"); - - if (optionValue != null) - builder.MergeAttribute("value", optionValue.ToString()); - - if (selected) - builder.MergeAttribute("selected", "selected"); - - builder.SetInnerText(text); - - if (htmlAttributes != null) { - builder.MergeAttributes(htmlAttributes); - } - - return MvcHtmlString.Create(builder.ToString(TagRenderMode.Normal)); - } - - public static WorkContext GetWorkContext(this HtmlHelper html) { - var workContext = html.ViewContext.RequestContext.GetWorkContext(); - - if (workContext == null) - throw new ApplicationException("The WorkContext cannot be found for the request"); - - return workContext; - } - - #region UnorderedList - - public static IHtmlString UnorderedList(this HtmlHelper htmlHelper, IEnumerable items, Func generateContent, string cssClass) { - return htmlHelper.UnorderedList(items, generateContent, cssClass, null, null); - } - - public static IHtmlString UnorderedList(this HtmlHelper htmlHelper, IEnumerable items, Func generateContent, string cssClass, string itemCssClass, string alternatingItemCssClass) { - return UnorderedList(items, (t, i) => generateContent(t, i) as IHtmlString, cssClass, t => itemCssClass, t => alternatingItemCssClass); - } - - public static IHtmlString UnorderedList(this HtmlHelper htmlHelper, IEnumerable items, Func generateContent, string cssClass) { - return htmlHelper.UnorderedList(items, generateContent, cssClass, null, null); - } - - public static IHtmlString UnorderedList(this HtmlHelper htmlHelper, IEnumerable items, Func generateContent, string cssClass, string itemCssClass, string alternatingItemCssClass) { - return UnorderedList(items, generateContent, cssClass, t => itemCssClass, t => alternatingItemCssClass); - } - - private static IHtmlString UnorderedList(IEnumerable items, Func generateContent, string cssClass, Func generateItemCssClass, Func generateAlternatingItemCssClass) { - if(items == null) { - return new HtmlString(string.Empty); - } - - // prevent multiple evaluations of the enumeration - items = items.ToArray(); - - if (!items.Any()) { - return new HtmlString(string.Empty); - } - - var sb = new StringBuilder(250); - int counter = 0, count = items.Count() - 1; - - if(string.IsNullOrEmpty(cssClass)) { - sb.Append("
    "); - } - else { - sb.Append("
      "); - } - - foreach (var item in items) { - var sbClass = new StringBuilder(50); - - if (counter == 0) { - sbClass.Append("first "); - } - - if (counter == count) { - sbClass.Append("last "); - } - - if (generateItemCssClass != null) { - sbClass.Append(generateItemCssClass(item)).Append(" "); - } - - if (counter % 2 != 0 && generateAlternatingItemCssClass != null) { - sbClass.Append(generateAlternatingItemCssClass(item)).Append(" "); - } - - var clss = sbClass.ToString().TrimEnd(); - - if(String.IsNullOrWhiteSpace(clss)) { - sb.Append("
    • ") - .Append(generateContent(item, counter)) - .Append("
    • "); - } - else { - sb.Append("
    • ") - .Append(generateContent(item, counter)) - .Append("
    • "); - } - - counter++; - } - - sb.Append("
    "); - - return new HtmlString(sb.ToString()); - } - - #endregion - - #region Ellipsize - - public static IHtmlString Ellipsize(this HtmlHelper htmlHelper, string text, int characterCount) { - return new HtmlString(htmlHelper.Encode(text.Ellipsize(characterCount))); - } - - public static IHtmlString Ellipsize(this HtmlHelper htmlHelper, string text, int characterCount, string ellipsis) { - return new HtmlString(htmlHelper.Encode(text.Ellipsize(characterCount, ellipsis))); - } - - #endregion - - #region Excerpt - - public static MvcHtmlString Excerpt(this HtmlHelper html, string markup, int length) { - return MvcHtmlString.Create(html.Encode(HttpUtility.HtmlDecode(markup.RemoveTags()).Ellipsize(length))); - } - - #endregion - - #region Image - - public static MvcHtmlString Image(this HtmlHelper htmlHelper, string src, string alt, object htmlAttributes) { - return htmlHelper.Image(src, alt, new RouteValueDictionary(htmlAttributes)); - } - - public static MvcHtmlString Image(this HtmlHelper htmlHelper, string src, string alt, IDictionary htmlAttributes) { - UrlHelper url = new UrlHelper(htmlHelper.ViewContext.RequestContext); - string imageUrl = url.Content(src); - TagBuilder imageTag = new TagBuilder("img"); - - if (!string.IsNullOrEmpty(imageUrl)) - imageTag.MergeAttribute("src", imageUrl); - - if (!string.IsNullOrEmpty(alt)) - imageTag.MergeAttribute("alt", alt); - - imageTag.MergeAttributes(htmlAttributes, true); - - if (imageTag.Attributes.ContainsKey("alt") && !imageTag.Attributes.ContainsKey("title")) - imageTag.MergeAttribute("title", imageTag.Attributes["alt"] ?? ""); - - return MvcHtmlString.Create(imageTag.ToString(TagRenderMode.SelfClosing)); - } - - #endregion - - #region Link - - public static IHtmlString Link(this HtmlHelper htmlHelper, string linkContents, string href) { - return htmlHelper.Link(linkContents, href, null); - } - - public static IHtmlString Link(this HtmlHelper htmlHelper, IHtmlString linkContents, string href) { - return htmlHelper.Link(linkContents, href, null); - } - - public static IHtmlString Link(this HtmlHelper htmlHelper, string linkContents, string href, object htmlAttributes) { - return htmlHelper.Link(linkContents, href, new RouteValueDictionary(htmlAttributes)); - } - - public static IHtmlString Link(this HtmlHelper htmlHelper, string linkContents, string href, IDictionary htmlAttributes) { - var tagBuilder = new TagBuilder("a") { InnerHtml = htmlHelper.Encode(linkContents) }; - tagBuilder.MergeAttributes(htmlAttributes); - tagBuilder.MergeAttribute("href", href); - return new HtmlString(tagBuilder.ToString(TagRenderMode.Normal)); - } - - public static IHtmlString Link(this HtmlHelper htmlHelper, IHtmlString linkContents, string href, IDictionary htmlAttributes) { - var tagBuilder = new TagBuilder("a") { InnerHtml = linkContents.ToHtmlString() }; - tagBuilder.MergeAttributes(htmlAttributes); - tagBuilder.MergeAttribute("href", href); - return new HtmlString(tagBuilder.ToString(TagRenderMode.Normal)); - } - - #endregion - - #region LinkOrDefault - - public static IHtmlString LinkOrDefault(this HtmlHelper htmlHelper, string linkContents, string href) { - return htmlHelper.LinkOrDefault(linkContents, href, null); - } - - public static IHtmlString LinkOrDefault(this HtmlHelper htmlHelper, string linkContents, string href, object htmlAttributes) { - return htmlHelper.LinkOrDefault(linkContents, href, new RouteValueDictionary(htmlAttributes)); - } - - public static IHtmlString LinkOrDefault(this HtmlHelper htmlHelper, string linkContents, string href, IDictionary htmlAttributes) { - string linkText = htmlHelper.Encode(linkContents); - - if (string.IsNullOrEmpty(href)) { - return new HtmlString(linkText); - } - - TagBuilder tagBuilder = new TagBuilder("a") { - InnerHtml = linkText - }; - tagBuilder.MergeAttributes(htmlAttributes); - tagBuilder.MergeAttribute("href", href); - return new HtmlString(tagBuilder.ToString(TagRenderMode.Normal)); - } - - #endregion - - #region Hint - public static IHtmlString Hint(this HtmlHelper htmlHelper, LocalizedString text) { - return Hint(htmlHelper, text, default(object)); - } - - public static IHtmlString Hint(this HtmlHelper htmlHelper, LocalizedString text, object htmlAttributes) { - return Hint(htmlHelper, text, htmlAttributes != null ? new RouteValueDictionary(htmlAttributes) : null); - } - - public static IHtmlString Hint(this HtmlHelper htmlHelper, LocalizedString text, IDictionary htmlAttributes) { - var tagBuilder = new TagBuilder("span") { InnerHtml = text.Text }; - - if (htmlAttributes != null) { - tagBuilder.MergeAttributes(htmlAttributes); - } - - tagBuilder.AddCssClass("hint"); - return new HtmlString(tagBuilder.ToString(TagRenderMode.Normal)); - } - #endregion - - - #region BeginFormAntiForgeryPost - - public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper) { - return htmlHelper.BeginFormAntiForgeryPost(htmlHelper.ViewContext.HttpContext.Request.Url.PathAndQuery, FormMethod.Post, new RouteValueDictionary()); - } - - public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction) { - return htmlHelper.BeginFormAntiForgeryPost(formAction, FormMethod.Post, new RouteValueDictionary()); - } - - public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction, FormMethod formMethod) { - return htmlHelper.BeginFormAntiForgeryPost(formAction, formMethod, new RouteValueDictionary()); - } - - public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction, FormMethod formMethod, object htmlAttributes) { - return htmlHelper.BeginFormAntiForgeryPost(formAction, formMethod, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); - } - - public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction, FormMethod formMethod, IDictionary htmlAttributes) { - // Force the browser not to cache protected forms, and to reload them if needed. - var response = htmlHelper.ViewContext.HttpContext.Response; - response.Cache.SetExpires(System.DateTime.UtcNow.AddDays(-1)); - response.Cache.SetValidUntilExpires(false); - response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); - response.Cache.SetCacheability(HttpCacheability.NoCache); - response.Cache.SetNoStore(); - - var tagBuilder = new TagBuilder("form"); - - tagBuilder.MergeAttributes(htmlAttributes); - tagBuilder.MergeAttribute("action", formAction); - tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(formMethod), true); - - htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag)); - - return new MvcFormAntiForgeryPost(htmlHelper); - } - #endregion - - #region AntiForgeryTokenOrchard - - public static MvcHtmlString AntiForgeryTokenOrchard(this HtmlHelper htmlHelper) { - - try { - return htmlHelper.AntiForgeryToken(); - } - catch (HttpAntiForgeryException) { - // Work-around an issue in MVC 2: If the browser sends a cookie that is not - // coming from this server (this can happen if the user didn't close their browser - // while the application server configuration changed), clear it up - // so that a new one is generated and sent to the browser. This is harmless - // from a security point of view, since we are _issuing_ an anti-forgery token, - // not validating input. - - // Remove the token so that MVC will create a new one. - var antiForgeryTokenName = htmlHelper.GetAntiForgeryTokenName(); - htmlHelper.ViewContext.HttpContext.Request.Cookies.Remove(antiForgeryTokenName); - - // Try again - return htmlHelper.AntiForgeryToken(); - } - } - - private static string GetAntiForgeryTokenName(this HtmlHelper htmlHelper) { - // Generate the same cookie name as MVC - var appPath = htmlHelper.ViewContext.HttpContext.Request.ApplicationPath; - const string antiForgeryTokenName = "__RequestVerificationToken"; - if (string.IsNullOrEmpty(appPath)) { - return antiForgeryTokenName; - } - return antiForgeryTokenName + '_' + Base64EncodeForCookieName(appPath); - } - - private static string Base64EncodeForCookieName(string s) { - return Convert.ToBase64String(Encoding.UTF8.GetBytes(s)).Replace('+', '.').Replace('/', '-').Replace('=', '_'); - } - - #endregion - - #region AntiForgeryTokenValueOrchardLink - - public static IHtmlString AntiForgeryTokenValueOrchardLink(this HtmlHelper htmlHelper, string linkContents, string href) { - return htmlHelper.AntiForgeryTokenValueOrchardLink(linkContents, href, (object)null); - } - - public static IHtmlString AntiForgeryTokenValueOrchardLink(this HtmlHelper htmlHelper, string linkContents, string href, object htmlAttributes) { - return htmlHelper.AntiForgeryTokenValueOrchardLink(linkContents, href, new RouteValueDictionary(htmlAttributes)); - } - - public static IHtmlString AntiForgeryTokenValueOrchardLink(this HtmlHelper htmlHelper, string linkContents, string href, IDictionary htmlAttributes) { - return htmlHelper.Link(linkContents, htmlHelper.AntiForgeryTokenGetUrl(href).ToString(), htmlAttributes); - } - - #endregion - - #region AntiForgeryTokenGetUrl - - public static IHtmlString AntiForgeryTokenGetUrl(this HtmlHelper htmlHelper, string baseUrl) { - return new HtmlString(string.Format("{0}{1}__RequestVerificationToken={2}", baseUrl, baseUrl.IndexOf('?') > -1 ? "&" : "?", htmlHelper.ViewContext.HttpContext.Server.UrlEncode(htmlHelper.AntiForgeryTokenValueOrchard().ToString()))); - } - - #endregion - - #region AntiForgeryTokenValueOrchard - - public static IHtmlString AntiForgeryTokenValueOrchard(this HtmlHelper htmlHelper) { - //HAACK: (erikpo) Since MVC doesn't expose any of its methods for generating the antiforgery token and setting the cookie, we'll just let it do its thing and parse out what we need - var field = htmlHelper.AntiForgeryTokenOrchard().ToHtmlString(); - var beginIndex = field.IndexOf("value=\"") + 7; - var endIndex = field.IndexOf("\"", beginIndex); - - return new HtmlString(field.Substring(beginIndex, endIndex - beginIndex)); - } - - #endregion - - #region HtmlHelperFor - // Credit: Max Toro http://maxtoroq.github.io/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html - public static HtmlHelper HtmlHelperFor(this HtmlHelper htmlHelper) { - return HtmlHelperFor(htmlHelper, default(TModel)); - } - - public static HtmlHelper HtmlHelperFor(this HtmlHelper htmlHelper, TModel model) { - return HtmlHelperFor(htmlHelper, model, null); - } - - public static HtmlHelper HtmlHelperFor(this HtmlHelper htmlHelper, TModel model, string htmlFieldPrefix) { - - var viewDataContainer = CreateViewDataContainer(htmlHelper.ViewData, model); - - var templateInfo = viewDataContainer.ViewData.TemplateInfo; - - if (!String.IsNullOrEmpty(htmlFieldPrefix)) - templateInfo.HtmlFieldPrefix = templateInfo.GetFullHtmlFieldName(htmlFieldPrefix); - - var viewContext = htmlHelper.ViewContext; - var newViewContext = new ViewContext( - viewContext.Controller.ControllerContext, - viewContext.View, - viewDataContainer.ViewData, - viewContext.TempData, - viewContext.Writer); - - return new HtmlHelper(newViewContext, viewDataContainer, htmlHelper.RouteCollection); - } - - private static IViewDataContainer CreateViewDataContainer(ViewDataDictionary viewData, object model) { - - var newViewData = new ViewDataDictionary(viewData) { - Model = model - }; - - newViewData.TemplateInfo = new TemplateInfo { - HtmlFieldPrefix = newViewData.TemplateInfo.HtmlFieldPrefix - }; - - return new ViewDataContainer { - ViewData = newViewData - }; - } - - private class ViewDataContainer : IViewDataContainer { - public ViewDataDictionary ViewData { get; set; } - } - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Web.Mvc; +using System.Web.Mvc.Html; +using System.Web.Routing; +using Orchard.Localization; +using Orchard.Utility; +using Orchard.Utility.Extensions; +using System.Web; + +namespace Orchard.Mvc.Html { + public static class HtmlHelperExtensions { + + public static string NameOf(this HtmlHelper html, Expression> expression) { + return Reflect.NameOf(html.ViewData.Model, expression); + } + + public static string NameOf(this HtmlHelper html, Expression> expression) { + return Reflect.NameOf(html.ViewData.Model, expression); + } + + public static string FieldNameFor(this HtmlHelper html, Expression> expression) { + return html.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)); + } + + public static string FieldIdFor(this HtmlHelper html, Expression> expression) { + var id = html.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression)); + // because "[" and "]" aren't replaced with "_" in GetFullHtmlFieldId + return id.Replace('[', '_').Replace(']', '_'); + } + + public static IHtmlString LabelFor(this HtmlHelper html, Expression> expression, LocalizedString labelText) { + return LabelFor(html, expression, labelText.ToString()); + } + + public static IHtmlString LabelFor(this HtmlHelper html, Expression> expression, string labelText) { + if (String.IsNullOrEmpty(labelText)) { + return MvcHtmlString.Empty; + } + var htmlFieldName = ExpressionHelper.GetExpressionText(expression); + var tag = new TagBuilder("label"); + tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName)); + tag.SetInnerText(labelText); + return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal)); + } + + public static MvcHtmlString SelectOption(this HtmlHelper html, T currentValue, T optionValue, string text) { + return SelectOption(html, optionValue, object.Equals(optionValue, currentValue), text); + } + + public static MvcHtmlString SelectOption(this HtmlHelper html, T currentValue, T optionValue, string text, object htmlAttributes) { + return SelectOption(html, optionValue, object.Equals(optionValue, currentValue), text, new RouteValueDictionary(htmlAttributes)); + } + + public static MvcHtmlString SelectOption(this HtmlHelper html, T currentValue, T optionValue, string text, RouteValueDictionary htmlAttributes) { + return SelectOption(html, optionValue, object.Equals(optionValue, currentValue), text, htmlAttributes); + } + + public static MvcHtmlString SelectOption(this HtmlHelper html, object optionValue, bool selected, string text) { + return SelectOption(html, optionValue, selected, text, null); + } + + public static MvcHtmlString SelectOption(this HtmlHelper html, object optionValue, bool selected, string text, object htmlAttributes) { + return SelectOption(html, optionValue, selected, text, new RouteValueDictionary(htmlAttributes)); + } + + public static MvcHtmlString SelectOption(this HtmlHelper html, object optionValue, bool selected, string text, RouteValueDictionary htmlAttributes) { + var builder = new TagBuilder("option"); + + if (optionValue != null) + builder.MergeAttribute("value", optionValue.ToString()); + + if (selected) + builder.MergeAttribute("selected", "selected"); + + builder.SetInnerText(text); + + if (htmlAttributes != null) { + builder.MergeAttributes(htmlAttributes); + } + + return MvcHtmlString.Create(builder.ToString(TagRenderMode.Normal)); + } + + public static WorkContext GetWorkContext(this HtmlHelper html) { + var workContext = html.ViewContext.RequestContext.GetWorkContext(); + + if (workContext == null) + throw new ApplicationException("The WorkContext cannot be found for the request"); + + return workContext; + } + + #region UnorderedList + + public static IHtmlString UnorderedList(this HtmlHelper htmlHelper, IEnumerable items, Func generateContent, string cssClass) { + return htmlHelper.UnorderedList(items, generateContent, cssClass, null, null); + } + + public static IHtmlString UnorderedList(this HtmlHelper htmlHelper, IEnumerable items, Func generateContent, string cssClass, string itemCssClass, string alternatingItemCssClass) { + return UnorderedList(items, (t, i) => generateContent(t, i) as IHtmlString, cssClass, t => itemCssClass, t => alternatingItemCssClass); + } + + public static IHtmlString UnorderedList(this HtmlHelper htmlHelper, IEnumerable items, Func generateContent, string cssClass) { + return htmlHelper.UnorderedList(items, generateContent, cssClass, null, null); + } + + public static IHtmlString UnorderedList(this HtmlHelper htmlHelper, IEnumerable items, Func generateContent, string cssClass, string itemCssClass, string alternatingItemCssClass) { + return UnorderedList(items, generateContent, cssClass, t => itemCssClass, t => alternatingItemCssClass); + } + + private static IHtmlString UnorderedList(IEnumerable items, Func generateContent, string cssClass, Func generateItemCssClass, Func generateAlternatingItemCssClass) { + if(items == null) { + return new HtmlString(string.Empty); + } + + // prevent multiple evaluations of the enumeration + items = items.ToArray(); + + if (!items.Any()) { + return new HtmlString(string.Empty); + } + + var sb = new StringBuilder(250); + int counter = 0, count = items.Count() - 1; + + if(string.IsNullOrEmpty(cssClass)) { + sb.Append("
      "); + } + else { + sb.Append("
        "); + } + + foreach (var item in items) { + var sbClass = new StringBuilder(50); + + if (counter == 0) { + sbClass.Append("first "); + } + + if (counter == count) { + sbClass.Append("last "); + } + + if (generateItemCssClass != null) { + sbClass.Append(generateItemCssClass(item)).Append(" "); + } + + if (counter % 2 != 0 && generateAlternatingItemCssClass != null) { + sbClass.Append(generateAlternatingItemCssClass(item)).Append(" "); + } + + var clss = sbClass.ToString().TrimEnd(); + + if(String.IsNullOrWhiteSpace(clss)) { + sb.Append("
      • ") + .Append(generateContent(item, counter)) + .Append("
      • "); + } + else { + sb.Append("
      • ") + .Append(generateContent(item, counter)) + .Append("
      • "); + } + + counter++; + } + + sb.Append("
      "); + + return new HtmlString(sb.ToString()); + } + + #endregion + + #region Ellipsize + + public static IHtmlString Ellipsize(this HtmlHelper htmlHelper, string text, int characterCount) { + return new HtmlString(htmlHelper.Encode(text.Ellipsize(characterCount))); + } + + public static IHtmlString Ellipsize(this HtmlHelper htmlHelper, string text, int characterCount, string ellipsis) { + return new HtmlString(htmlHelper.Encode(text.Ellipsize(characterCount, ellipsis))); + } + + #endregion + + #region Excerpt + + public static MvcHtmlString Excerpt(this HtmlHelper html, string markup, int length) { + return MvcHtmlString.Create(html.Encode(HttpUtility.HtmlDecode(markup.RemoveTags()).Ellipsize(length))); + } + + #endregion + + #region Image + + public static MvcHtmlString Image(this HtmlHelper htmlHelper, string src, string alt, object htmlAttributes) { + return htmlHelper.Image(src, alt, new RouteValueDictionary(htmlAttributes)); + } + + public static MvcHtmlString Image(this HtmlHelper htmlHelper, string src, string alt, IDictionary htmlAttributes) { + UrlHelper url = new UrlHelper(htmlHelper.ViewContext.RequestContext); + string imageUrl = url.Content(src); + TagBuilder imageTag = new TagBuilder("img"); + + if (!string.IsNullOrEmpty(imageUrl)) + imageTag.MergeAttribute("src", imageUrl); + + if (!string.IsNullOrEmpty(alt)) + imageTag.MergeAttribute("alt", alt); + + imageTag.MergeAttributes(htmlAttributes, true); + + if (imageTag.Attributes.ContainsKey("alt") && !imageTag.Attributes.ContainsKey("title")) + imageTag.MergeAttribute("title", imageTag.Attributes["alt"] ?? ""); + + return MvcHtmlString.Create(imageTag.ToString(TagRenderMode.SelfClosing)); + } + + #endregion + + #region Link + + public static IHtmlString Link(this HtmlHelper htmlHelper, string linkContents, string href) { + return htmlHelper.Link(linkContents, href, null); + } + + public static IHtmlString Link(this HtmlHelper htmlHelper, IHtmlString linkContents, string href) { + return htmlHelper.Link(linkContents, href, null); + } + + public static IHtmlString Link(this HtmlHelper htmlHelper, string linkContents, string href, object htmlAttributes) { + return htmlHelper.Link(linkContents, href, new RouteValueDictionary(htmlAttributes)); + } + + public static IHtmlString Link(this HtmlHelper htmlHelper, string linkContents, string href, IDictionary htmlAttributes) { + var tagBuilder = new TagBuilder("a") { InnerHtml = htmlHelper.Encode(linkContents) }; + tagBuilder.MergeAttributes(htmlAttributes); + tagBuilder.MergeAttribute("href", href); + return new HtmlString(tagBuilder.ToString(TagRenderMode.Normal)); + } + + public static IHtmlString Link(this HtmlHelper htmlHelper, IHtmlString linkContents, string href, IDictionary htmlAttributes) { + var tagBuilder = new TagBuilder("a") { InnerHtml = linkContents.ToHtmlString() }; + tagBuilder.MergeAttributes(htmlAttributes); + tagBuilder.MergeAttribute("href", href); + return new HtmlString(tagBuilder.ToString(TagRenderMode.Normal)); + } + + #endregion + + #region LinkOrDefault + + public static IHtmlString LinkOrDefault(this HtmlHelper htmlHelper, string linkContents, string href) { + return htmlHelper.LinkOrDefault(linkContents, href, null); + } + + public static IHtmlString LinkOrDefault(this HtmlHelper htmlHelper, string linkContents, string href, object htmlAttributes) { + return htmlHelper.LinkOrDefault(linkContents, href, new RouteValueDictionary(htmlAttributes)); + } + + public static IHtmlString LinkOrDefault(this HtmlHelper htmlHelper, string linkContents, string href, IDictionary htmlAttributes) { + string linkText = htmlHelper.Encode(linkContents); + + if (string.IsNullOrEmpty(href)) { + return new HtmlString(linkText); + } + + TagBuilder tagBuilder = new TagBuilder("a") { + InnerHtml = linkText + }; + tagBuilder.MergeAttributes(htmlAttributes); + tagBuilder.MergeAttribute("href", href); + return new HtmlString(tagBuilder.ToString(TagRenderMode.Normal)); + } + + #endregion + + #region Hint + public static IHtmlString Hint(this HtmlHelper htmlHelper, LocalizedString text) { + return Hint(htmlHelper, text, default(object)); + } + + public static IHtmlString Hint(this HtmlHelper htmlHelper, LocalizedString text, object htmlAttributes) { + return Hint(htmlHelper, text, htmlAttributes != null ? new RouteValueDictionary(htmlAttributes) : null); + } + + public static IHtmlString Hint(this HtmlHelper htmlHelper, LocalizedString text, IDictionary htmlAttributes) { + var tagBuilder = new TagBuilder("span") { InnerHtml = text.Text }; + + if (htmlAttributes != null) { + tagBuilder.MergeAttributes(htmlAttributes); + } + + tagBuilder.AddCssClass("hint"); + return new HtmlString(tagBuilder.ToString(TagRenderMode.Normal)); + } + #endregion + + + #region BeginFormAntiForgeryPost + + public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper) { + return htmlHelper.BeginFormAntiForgeryPost(htmlHelper.ViewContext.HttpContext.Request.Url.PathAndQuery, FormMethod.Post, new RouteValueDictionary()); + } + + public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction) { + return htmlHelper.BeginFormAntiForgeryPost(formAction, FormMethod.Post, new RouteValueDictionary()); + } + + public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction, FormMethod formMethod) { + return htmlHelper.BeginFormAntiForgeryPost(formAction, formMethod, new RouteValueDictionary()); + } + + public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction, FormMethod formMethod, object htmlAttributes) { + return htmlHelper.BeginFormAntiForgeryPost(formAction, formMethod, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); + } + + public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction, FormMethod formMethod, IDictionary htmlAttributes) { + // Force the browser not to cache protected forms, and to reload them if needed. + var response = htmlHelper.ViewContext.HttpContext.Response; + response.Cache.SetExpires(System.DateTime.UtcNow.AddDays(-1)); + response.Cache.SetValidUntilExpires(false); + response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); + response.Cache.SetCacheability(HttpCacheability.NoCache); + response.Cache.SetNoStore(); + + var tagBuilder = new TagBuilder("form"); + + tagBuilder.MergeAttributes(htmlAttributes); + tagBuilder.MergeAttribute("action", formAction); + tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(formMethod), true); + + htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag)); + + return new MvcFormAntiForgeryPost(htmlHelper); + } + #endregion + + #region AntiForgeryTokenOrchard + + public static MvcHtmlString AntiForgeryTokenOrchard(this HtmlHelper htmlHelper) { + + try { + return htmlHelper.AntiForgeryToken(); + } + catch (HttpAntiForgeryException) { + // Work-around an issue in MVC 2: If the browser sends a cookie that is not + // coming from this server (this can happen if the user didn't close their browser + // while the application server configuration changed), clear it up + // so that a new one is generated and sent to the browser. This is harmless + // from a security point of view, since we are _issuing_ an anti-forgery token, + // not validating input. + + // Remove the token so that MVC will create a new one. + var antiForgeryTokenName = htmlHelper.GetAntiForgeryTokenName(); + htmlHelper.ViewContext.HttpContext.Request.Cookies.Remove(antiForgeryTokenName); + + // Try again + return htmlHelper.AntiForgeryToken(); + } + } + + private static string GetAntiForgeryTokenName(this HtmlHelper htmlHelper) { + // Generate the same cookie name as MVC + var appPath = htmlHelper.ViewContext.HttpContext.Request.ApplicationPath; + const string antiForgeryTokenName = "__RequestVerificationToken"; + if (string.IsNullOrEmpty(appPath)) { + return antiForgeryTokenName; + } + return antiForgeryTokenName + '_' + Base64EncodeForCookieName(appPath); + } + + private static string Base64EncodeForCookieName(string s) { + return Convert.ToBase64String(Encoding.UTF8.GetBytes(s)).Replace('+', '.').Replace('/', '-').Replace('=', '_'); + } + + #endregion + + #region AntiForgeryTokenValueOrchardLink + + public static IHtmlString AntiForgeryTokenValueOrchardLink(this HtmlHelper htmlHelper, string linkContents, string href) { + return htmlHelper.AntiForgeryTokenValueOrchardLink(linkContents, href, (object)null); + } + + public static IHtmlString AntiForgeryTokenValueOrchardLink(this HtmlHelper htmlHelper, string linkContents, string href, object htmlAttributes) { + return htmlHelper.AntiForgeryTokenValueOrchardLink(linkContents, href, new RouteValueDictionary(htmlAttributes)); + } + + public static IHtmlString AntiForgeryTokenValueOrchardLink(this HtmlHelper htmlHelper, string linkContents, string href, IDictionary htmlAttributes) { + return htmlHelper.Link(linkContents, htmlHelper.AntiForgeryTokenGetUrl(href).ToString(), htmlAttributes); + } + + #endregion + + #region AntiForgeryTokenGetUrl + + public static IHtmlString AntiForgeryTokenGetUrl(this HtmlHelper htmlHelper, string baseUrl) { + return new HtmlString(string.Format("{0}{1}__RequestVerificationToken={2}", baseUrl, baseUrl.IndexOf('?') > -1 ? "&" : "?", htmlHelper.ViewContext.HttpContext.Server.UrlEncode(htmlHelper.AntiForgeryTokenValueOrchard().ToString()))); + } + + #endregion + + #region AntiForgeryTokenValueOrchard + + public static IHtmlString AntiForgeryTokenValueOrchard(this HtmlHelper htmlHelper) { + //HAACK: (erikpo) Since MVC doesn't expose any of its methods for generating the antiforgery token and setting the cookie, we'll just let it do its thing and parse out what we need + var field = htmlHelper.AntiForgeryTokenOrchard().ToHtmlString(); + var beginIndex = field.IndexOf("value=\"") + 7; + var endIndex = field.IndexOf("\"", beginIndex); + + return new HtmlString(field.Substring(beginIndex, endIndex - beginIndex)); + } + + #endregion + + #region HtmlHelperFor + // Credit: Max Toro http://maxtoroq.github.io/2012/07/patterns-for-aspnet-mvc-plugins-viewmodels.html + public static HtmlHelper HtmlHelperFor(this HtmlHelper htmlHelper) { + return HtmlHelperFor(htmlHelper, default(TModel)); + } + + public static HtmlHelper HtmlHelperFor(this HtmlHelper htmlHelper, TModel model) { + return HtmlHelperFor(htmlHelper, model, null); + } + + public static HtmlHelper HtmlHelperFor(this HtmlHelper htmlHelper, TModel model, string htmlFieldPrefix) { + + var viewDataContainer = CreateViewDataContainer(htmlHelper.ViewData, model); + + var templateInfo = viewDataContainer.ViewData.TemplateInfo; + + if (!String.IsNullOrEmpty(htmlFieldPrefix)) + templateInfo.HtmlFieldPrefix = templateInfo.GetFullHtmlFieldName(htmlFieldPrefix); + + var viewContext = htmlHelper.ViewContext; + var newViewContext = new ViewContext( + viewContext.Controller.ControllerContext, + viewContext.View, + viewDataContainer.ViewData, + viewContext.TempData, + viewContext.Writer); + + return new HtmlHelper(newViewContext, viewDataContainer, htmlHelper.RouteCollection); + } + + private static IViewDataContainer CreateViewDataContainer(ViewDataDictionary viewData, object model) { + + var newViewData = new ViewDataDictionary(viewData) { + Model = model + }; + + newViewData.TemplateInfo = new TemplateInfo { + HtmlFieldPrefix = newViewData.TemplateInfo.HtmlFieldPrefix + }; + + return new ViewDataContainer { + ViewData = newViewData + }; + } + + private class ViewDataContainer : IViewDataContainer { + public ViewDataDictionary ViewData { get; set; } + } + #endregion + } +} diff --git a/src/Orchard/Mvc/MvcModule.cs b/src/Orchard/Mvc/MvcModule.cs index 61c7a74f2..d238e5fb8 100644 --- a/src/Orchard/Mvc/MvcModule.cs +++ b/src/Orchard/Mvc/MvcModule.cs @@ -1,282 +1,282 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Globalization; -using System.Web; -using System.Web.Caching; -using System.Web.Instrumentation; -using System.Web.Mvc; -using System.Web.Routing; -using Autofac; -using Orchard.Mvc.Routes; -using Orchard.Settings; -using Orchard.Exceptions; - -namespace Orchard.Mvc { - public class MvcModule : Module { - - protected override void Load(ContainerBuilder moduleBuilder) { - moduleBuilder.RegisterType().InstancePerDependency(); - - moduleBuilder.Register(HttpContextBaseFactory).As().InstancePerDependency(); - moduleBuilder.Register(RequestContextFactory).As().InstancePerDependency(); - moduleBuilder.Register(UrlHelperFactory).As().InstancePerDependency(); - } - - private static bool IsRequestValid() { - if (HttpContext.Current == null) - return false; - - try { - // The "Request" property throws at application startup on IIS integrated pipeline mode. - var req = HttpContext.Current.Request; - } - catch (Exception ex) { - if (ex.IsFatal()) { - throw; - } - - return false; - } - - return true; - } - - static HttpContextBase HttpContextBaseFactory(IComponentContext context) { - if (IsRequestValid()) { - return new HttpContextWrapper(HttpContext.Current); - } - - var siteService = context.Resolve(); - - // Wrapping the code accessing the SiteSettings in a function that will be executed later (in HttpContextPlaceholder), - // so that the RequestContext will have been established when the time comes to actually load the site settings, - // which requires activating the Site content item, which in turn requires a UrlHelper, which in turn requires a RequestContext, - // thus preventing a StackOverflowException. - var baseUrl = new Func(() => siteService.GetSiteSettings().BaseUrl); - var httpContextBase = new HttpContextPlaceholder(baseUrl); - - context.Resolve().CreateWorkContextScope(httpContextBase); - return httpContextBase; - } - - static RequestContext RequestContextFactory(IComponentContext context) { - var httpContextAccessor = context.Resolve(); - var httpContext = httpContextAccessor.Current(); - if (httpContext != null) { - - var mvcHandler = httpContext.Handler as MvcHandler; - if (mvcHandler != null) { - return mvcHandler.RequestContext; - } - - var hasRequestContext = httpContext.Handler as IHasRequestContext; - if (hasRequestContext != null) { - if (hasRequestContext.RequestContext != null) - return hasRequestContext.RequestContext; - } - } - else { - httpContext = HttpContextBaseFactory(context); - } - - return new RequestContext(httpContext, new RouteData()); - } - - static UrlHelper UrlHelperFactory(IComponentContext context) { - return new UrlHelper(context.Resolve(), context.Resolve()); - } - - /// - /// Standin context for background tasks. - /// - public class HttpContextPlaceholder : HttpContextBase { - private readonly Lazy _baseUrl; - private readonly IDictionary _items = new Dictionary(); - - public HttpContextPlaceholder(Func baseUrl) { - _baseUrl = new Lazy(baseUrl); - } - - public override HttpRequestBase Request { - get { return new HttpRequestPlaceholder(new Uri(_baseUrl.Value)); } - } - - public override IHttpHandler Handler { get; set; } - - public override HttpResponseBase Response { - get { return new HttpResponsePlaceholder(); } - } - - public override IDictionary Items { - get { return _items; } - } - - public override PageInstrumentationService PageInstrumentation { - get { return new PageInstrumentationService(); } - } - - public override Cache Cache { - get { return HttpRuntime.Cache; } - } - - public override HttpServerUtilityBase Server { - get { return new HttpServerUtilityPlaceholder(); } - } - - public override object GetService(Type serviceType) { - return null; - } - } - - public class HttpResponsePlaceholder : HttpResponseBase { - public override string ApplyAppPathModifier(string virtualPath) { - return virtualPath; - } - - public override HttpCookieCollection Cookies { - get { - return new HttpCookieCollection(); - } - } - } - - /// - /// standin context for background tasks. - /// - public class HttpRequestPlaceholder : HttpRequestBase { - private readonly Uri _uri; - - public HttpRequestPlaceholder(Uri uri) { - _uri = uri; - } - - /// - /// anonymous identity provided for background task. - /// - public override bool IsAuthenticated { - get { return false; } - } - - /// - /// Create an anonymous ID the same way as ASP.NET would. - /// Some users of an HttpRequestPlaceHolder object could expect this, - /// say CookieCultureSelector from module Orchard.CulturePicker. - /// - public override string AnonymousID { - get { - return Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture); - } - } - - // empty collection provided for background operation - public override NameValueCollection Form { - get { - return new NameValueCollection(); - } - } - - public override Uri Url { - get { - return _uri; - } - } - - public override NameValueCollection Headers { - get { - return new NameValueCollection { { "Host", _uri.Authority } }; - } - } - - public override string HttpMethod { - get { - return ""; - } - } - - public override NameValueCollection Params { - get { - return new NameValueCollection(); - } - } - - public override string AppRelativeCurrentExecutionFilePath { - get { - return "~/"; - } - } - - public override string ApplicationPath { - get { - return _uri.LocalPath; - } - } - - public override NameValueCollection ServerVariables { - get { - return new NameValueCollection { - { "SERVER_PORT", _uri.Port.ToString(CultureInfo.InvariantCulture) }, - { "HTTP_HOST", _uri.Authority.ToString(CultureInfo.InvariantCulture) }, - - }; - } - } - - public override HttpCookieCollection Cookies { - get { - return new HttpCookieCollection(); - } - } - - public override bool IsLocal { - get { return true; } - } - - public override string Path { - get { return "/"; } - } - - public override string UserAgent { - get { - return "Placeholder"; - } - } - - public override string UserHostAddress { - get { - return "127.0.0.1"; - } - } - - public override string[] UserLanguages { - get { - return new string[0]; - } - } - - public override HttpBrowserCapabilitiesBase Browser { - get { - return new HttpBrowserCapabilitiesPlaceholder(); - } - } - } - - public class HttpBrowserCapabilitiesPlaceholder : HttpBrowserCapabilitiesBase { - public override string this[string key] { - get { - return ""; - } - } - - public override bool IsMobileDevice { get { return false; } } - public override string Browser { get { return "Placeholder"; } } - public override bool Cookies { get { return true; } } - public override ArrayList Browsers { get { return new ArrayList(); } } - } - - public class HttpServerUtilityPlaceholder : HttpServerUtilityBase { - public override int ScriptTimeout { get; set; } - } - } +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Web; +using System.Web.Caching; +using System.Web.Instrumentation; +using System.Web.Mvc; +using System.Web.Routing; +using Autofac; +using Orchard.Mvc.Routes; +using Orchard.Settings; +using Orchard.Exceptions; + +namespace Orchard.Mvc { + public class MvcModule : Module { + + protected override void Load(ContainerBuilder moduleBuilder) { + moduleBuilder.RegisterType().InstancePerDependency(); + + moduleBuilder.Register(HttpContextBaseFactory).As().InstancePerDependency(); + moduleBuilder.Register(RequestContextFactory).As().InstancePerDependency(); + moduleBuilder.Register(UrlHelperFactory).As().InstancePerDependency(); + } + + private static bool IsRequestValid() { + if (HttpContext.Current == null) + return false; + + try { + // The "Request" property throws at application startup on IIS integrated pipeline mode. + var req = HttpContext.Current.Request; + } + catch (Exception ex) { + if (ex.IsFatal()) { + throw; + } + + return false; + } + + return true; + } + + static HttpContextBase HttpContextBaseFactory(IComponentContext context) { + if (IsRequestValid()) { + return new HttpContextWrapper(HttpContext.Current); + } + + var siteService = context.Resolve(); + + // Wrapping the code accessing the SiteSettings in a function that will be executed later (in HttpContextPlaceholder), + // so that the RequestContext will have been established when the time comes to actually load the site settings, + // which requires activating the Site content item, which in turn requires a UrlHelper, which in turn requires a RequestContext, + // thus preventing a StackOverflowException. + var baseUrl = new Func(() => siteService.GetSiteSettings().BaseUrl); + var httpContextBase = new HttpContextPlaceholder(baseUrl); + + context.Resolve().CreateWorkContextScope(httpContextBase); + return httpContextBase; + } + + static RequestContext RequestContextFactory(IComponentContext context) { + var httpContextAccessor = context.Resolve(); + var httpContext = httpContextAccessor.Current(); + if (httpContext != null) { + + var mvcHandler = httpContext.Handler as MvcHandler; + if (mvcHandler != null) { + return mvcHandler.RequestContext; + } + + var hasRequestContext = httpContext.Handler as IHasRequestContext; + if (hasRequestContext != null) { + if (hasRequestContext.RequestContext != null) + return hasRequestContext.RequestContext; + } + } + else { + httpContext = HttpContextBaseFactory(context); + } + + return new RequestContext(httpContext, new RouteData()); + } + + static UrlHelper UrlHelperFactory(IComponentContext context) { + return new UrlHelper(context.Resolve(), context.Resolve()); + } + + /// + /// Standin context for background tasks. + /// + public class HttpContextPlaceholder : HttpContextBase { + private readonly Lazy _baseUrl; + private readonly IDictionary _items = new Dictionary(); + + public HttpContextPlaceholder(Func baseUrl) { + _baseUrl = new Lazy(baseUrl); + } + + public override HttpRequestBase Request { + get { return new HttpRequestPlaceholder(new Uri(_baseUrl.Value)); } + } + + public override IHttpHandler Handler { get; set; } + + public override HttpResponseBase Response { + get { return new HttpResponsePlaceholder(); } + } + + public override IDictionary Items { + get { return _items; } + } + + public override PageInstrumentationService PageInstrumentation { + get { return new PageInstrumentationService(); } + } + + public override Cache Cache { + get { return HttpRuntime.Cache; } + } + + public override HttpServerUtilityBase Server { + get { return new HttpServerUtilityPlaceholder(); } + } + + public override object GetService(Type serviceType) { + return null; + } + } + + public class HttpResponsePlaceholder : HttpResponseBase { + public override string ApplyAppPathModifier(string virtualPath) { + return virtualPath; + } + + public override HttpCookieCollection Cookies { + get { + return new HttpCookieCollection(); + } + } + } + + /// + /// standin context for background tasks. + /// + public class HttpRequestPlaceholder : HttpRequestBase { + private readonly Uri _uri; + + public HttpRequestPlaceholder(Uri uri) { + _uri = uri; + } + + /// + /// anonymous identity provided for background task. + /// + public override bool IsAuthenticated { + get { return false; } + } + + /// + /// Create an anonymous ID the same way as ASP.NET would. + /// Some users of an HttpRequestPlaceHolder object could expect this, + /// say CookieCultureSelector from module Orchard.CulturePicker. + /// + public override string AnonymousID { + get { + return Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture); + } + } + + // empty collection provided for background operation + public override NameValueCollection Form { + get { + return new NameValueCollection(); + } + } + + public override Uri Url { + get { + return _uri; + } + } + + public override NameValueCollection Headers { + get { + return new NameValueCollection { { "Host", _uri.Authority } }; + } + } + + public override string HttpMethod { + get { + return ""; + } + } + + public override NameValueCollection Params { + get { + return new NameValueCollection(); + } + } + + public override string AppRelativeCurrentExecutionFilePath { + get { + return "~/"; + } + } + + public override string ApplicationPath { + get { + return _uri.LocalPath; + } + } + + public override NameValueCollection ServerVariables { + get { + return new NameValueCollection { + { "SERVER_PORT", _uri.Port.ToString(CultureInfo.InvariantCulture) }, + { "HTTP_HOST", _uri.Authority.ToString(CultureInfo.InvariantCulture) }, + + }; + } + } + + public override HttpCookieCollection Cookies { + get { + return new HttpCookieCollection(); + } + } + + public override bool IsLocal { + get { return true; } + } + + public override string Path { + get { return "/"; } + } + + public override string UserAgent { + get { + return "Placeholder"; + } + } + + public override string UserHostAddress { + get { + return "127.0.0.1"; + } + } + + public override string[] UserLanguages { + get { + return new string[0]; + } + } + + public override HttpBrowserCapabilitiesBase Browser { + get { + return new HttpBrowserCapabilitiesPlaceholder(); + } + } + } + + public class HttpBrowserCapabilitiesPlaceholder : HttpBrowserCapabilitiesBase { + public override string this[string key] { + get { + return ""; + } + } + + public override bool IsMobileDevice { get { return false; } } + public override string Browser { get { return "Placeholder"; } } + public override bool Cookies { get { return true; } } + public override ArrayList Browsers { get { return new ArrayList(); } } + } + + public class HttpServerUtilityPlaceholder : HttpServerUtilityBase { + public override int ScriptTimeout { get; set; } + } + } } \ No newline at end of file diff --git a/src/Orchard/Mvc/ViewEngines/IViewEngineProvider.cs b/src/Orchard/Mvc/ViewEngines/IViewEngineProvider.cs index 293331a5e..853db7405 100644 --- a/src/Orchard/Mvc/ViewEngines/IViewEngineProvider.cs +++ b/src/Orchard/Mvc/ViewEngines/IViewEngineProvider.cs @@ -1,23 +1,23 @@ -using System.Collections.Generic; -using System.Web.Mvc; - -namespace Orchard.Mvc.ViewEngines { - public class CreateThemeViewEngineParams { - public string VirtualPath { get; set; } - } - - public class CreateModulesViewEngineParams { - public IEnumerable VirtualPaths { get; set; } - public IEnumerable ExtensionLocations { get; set; } - } - - public interface IViewEngineProvider : ISingletonDependency { - IViewEngine CreateThemeViewEngine(CreateThemeViewEngineParams parameters); - IViewEngine CreateModulesViewEngine(CreateModulesViewEngineParams parameters); - - /// - /// Produce a view engine configured to resolve only fully qualified {viewName} parameters - /// - IViewEngine CreateBareViewEngine(); - } -} +using System.Collections.Generic; +using System.Web.Mvc; + +namespace Orchard.Mvc.ViewEngines { + public class CreateThemeViewEngineParams { + public string VirtualPath { get; set; } + } + + public class CreateModulesViewEngineParams { + public IEnumerable VirtualPaths { get; set; } + public IEnumerable ExtensionLocations { get; set; } + } + + public interface IViewEngineProvider : ISingletonDependency { + IViewEngine CreateThemeViewEngine(CreateThemeViewEngineParams parameters); + IViewEngine CreateModulesViewEngine(CreateModulesViewEngineParams parameters); + + /// + /// Produce a view engine configured to resolve only fully qualified {viewName} parameters + /// + IViewEngine CreateBareViewEngine(); + } +} diff --git a/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs b/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs index 14775ec3c..8910a10ac 100644 --- a/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs +++ b/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs @@ -1,102 +1,102 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Mvc; -using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy; -using Orchard.Logging; -using Orchard.Mvc.ViewEngines.ThemeAwareness; - -namespace Orchard.Mvc.ViewEngines.Razor { - public class RazorViewEngineProvider : IViewEngineProvider, IShapeTemplateViewEngine { - public RazorViewEngineProvider() { - Logger = NullLogger.Instance; - RazorCompilationEventsShim.EnsureInitialized(); - } - static readonly string[] DisabledFormats = new[] { "~/Disabled" }; - - public ILogger Logger { get; set; } - - public IViewEngine CreateThemeViewEngine(CreateThemeViewEngineParams parameters) { - // Area: if "area" in RouteData. Url hit for module... - // Area-Layout Paths - no-op because LayoutViewEngine uses multi-pass instead of layout paths - // Area-View Paths - no-op because LayoutViewEngine relies entirely on Partial view resolution - // Area-Partial Paths - enable theming views associated with a module based on the route - - // Layout Paths - no-op because LayoutViewEngine uses multi-pass instead of layout paths - // View Paths - no-op because LayoutViewEngine relies entirely on Partial view resolution - // Partial Paths - - // {area}/{controller}/ - - // for "routed" request views... - // enable /Views/{area}/{controller}/{viewName} - - // enable /Views/{partialName} - // enable /Views/"DisplayTemplates/"+{templateName} - // enable /Views/"EditorTemplates/+{templateName} - var partialViewLocationFormats = new[] { - parameters.VirtualPath + "/Views/{0}.cshtml", - }; - - //Logger.Debug("PartialViewLocationFormats (theme): \r\n\t-{0}", string.Join("\r\n\t-", partialViewLocationFormats)); - - var areaPartialViewLocationFormats = new[] { - parameters.VirtualPath + "/Views/{2}/{1}/{0}.cshtml", - }; - - //Logger.Debug("AreaPartialViewLocationFormats (theme): \r\n\t-{0}", string.Join("\r\n\t-", areaPartialViewLocationFormats)); - - var viewEngine = new RazorViewEngine { - MasterLocationFormats = DisabledFormats, - ViewLocationFormats = DisabledFormats, - PartialViewLocationFormats = partialViewLocationFormats, - AreaMasterLocationFormats = DisabledFormats, - AreaViewLocationFormats = DisabledFormats, - AreaPartialViewLocationFormats = areaPartialViewLocationFormats, - ViewLocationCache = new ThemeViewLocationCache(parameters.VirtualPath), - }; - - return viewEngine; - } - - public IViewEngine CreateModulesViewEngine(CreateModulesViewEngineParams parameters) { - //TBD: It would probably be better to determined the area deterministically from the module of the controller, not by trial and error. - var areaFormats = parameters.ExtensionLocations.Select(location => location + "/{2}/Views/{1}/{0}.cshtml").ToArray(); - - //Logger.Debug("AreaFormats (module): \r\n\t-{0}", string.Join("\r\n\t-", areaFormats)); - - var universalFormats = parameters.VirtualPaths - .SelectMany(x => new[] { - x + "/Views/{0}.cshtml", - }) - .ToArray(); - - //Logger.Debug("UniversalFormats (module): \r\n\t-{0}", string.Join("\r\n\t-", universalFormats)); - - var viewEngine = new RazorViewEngine { - MasterLocationFormats = DisabledFormats, - ViewLocationFormats = universalFormats, - PartialViewLocationFormats = universalFormats, - AreaMasterLocationFormats = DisabledFormats, - AreaViewLocationFormats = areaFormats, - AreaPartialViewLocationFormats = areaFormats, - }; - - return viewEngine; - } - - public IViewEngine CreateBareViewEngine() { - return new RazorViewEngine { - MasterLocationFormats = DisabledFormats, - ViewLocationFormats = DisabledFormats, - PartialViewLocationFormats = DisabledFormats, - AreaMasterLocationFormats = DisabledFormats, - AreaViewLocationFormats = DisabledFormats, - AreaPartialViewLocationFormats = DisabledFormats, - }; - } - - public IEnumerable DetectTemplateFileNames(IEnumerable fileNames) { - return fileNames.Where(fileName => fileName.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase)); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy; +using Orchard.Logging; +using Orchard.Mvc.ViewEngines.ThemeAwareness; + +namespace Orchard.Mvc.ViewEngines.Razor { + public class RazorViewEngineProvider : IViewEngineProvider, IShapeTemplateViewEngine { + public RazorViewEngineProvider() { + Logger = NullLogger.Instance; + RazorCompilationEventsShim.EnsureInitialized(); + } + static readonly string[] DisabledFormats = new[] { "~/Disabled" }; + + public ILogger Logger { get; set; } + + public IViewEngine CreateThemeViewEngine(CreateThemeViewEngineParams parameters) { + // Area: if "area" in RouteData. Url hit for module... + // Area-Layout Paths - no-op because LayoutViewEngine uses multi-pass instead of layout paths + // Area-View Paths - no-op because LayoutViewEngine relies entirely on Partial view resolution + // Area-Partial Paths - enable theming views associated with a module based on the route + + // Layout Paths - no-op because LayoutViewEngine uses multi-pass instead of layout paths + // View Paths - no-op because LayoutViewEngine relies entirely on Partial view resolution + // Partial Paths - + // {area}/{controller}/ + + // for "routed" request views... + // enable /Views/{area}/{controller}/{viewName} + + // enable /Views/{partialName} + // enable /Views/"DisplayTemplates/"+{templateName} + // enable /Views/"EditorTemplates/+{templateName} + var partialViewLocationFormats = new[] { + parameters.VirtualPath + "/Views/{0}.cshtml", + }; + + //Logger.Debug("PartialViewLocationFormats (theme): \r\n\t-{0}", string.Join("\r\n\t-", partialViewLocationFormats)); + + var areaPartialViewLocationFormats = new[] { + parameters.VirtualPath + "/Views/{2}/{1}/{0}.cshtml", + }; + + //Logger.Debug("AreaPartialViewLocationFormats (theme): \r\n\t-{0}", string.Join("\r\n\t-", areaPartialViewLocationFormats)); + + var viewEngine = new RazorViewEngine { + MasterLocationFormats = DisabledFormats, + ViewLocationFormats = DisabledFormats, + PartialViewLocationFormats = partialViewLocationFormats, + AreaMasterLocationFormats = DisabledFormats, + AreaViewLocationFormats = DisabledFormats, + AreaPartialViewLocationFormats = areaPartialViewLocationFormats, + ViewLocationCache = new ThemeViewLocationCache(parameters.VirtualPath), + }; + + return viewEngine; + } + + public IViewEngine CreateModulesViewEngine(CreateModulesViewEngineParams parameters) { + //TBD: It would probably be better to determined the area deterministically from the module of the controller, not by trial and error. + var areaFormats = parameters.ExtensionLocations.Select(location => location + "/{2}/Views/{1}/{0}.cshtml").ToArray(); + + //Logger.Debug("AreaFormats (module): \r\n\t-{0}", string.Join("\r\n\t-", areaFormats)); + + var universalFormats = parameters.VirtualPaths + .SelectMany(x => new[] { + x + "/Views/{0}.cshtml", + }) + .ToArray(); + + //Logger.Debug("UniversalFormats (module): \r\n\t-{0}", string.Join("\r\n\t-", universalFormats)); + + var viewEngine = new RazorViewEngine { + MasterLocationFormats = DisabledFormats, + ViewLocationFormats = universalFormats, + PartialViewLocationFormats = universalFormats, + AreaMasterLocationFormats = DisabledFormats, + AreaViewLocationFormats = areaFormats, + AreaPartialViewLocationFormats = areaFormats, + }; + + return viewEngine; + } + + public IViewEngine CreateBareViewEngine() { + return new RazorViewEngine { + MasterLocationFormats = DisabledFormats, + ViewLocationFormats = DisabledFormats, + PartialViewLocationFormats = DisabledFormats, + AreaMasterLocationFormats = DisabledFormats, + AreaViewLocationFormats = DisabledFormats, + AreaPartialViewLocationFormats = DisabledFormats, + }; + } + + public IEnumerable DetectTemplateFileNames(IEnumerable fileNames) { + return fileNames.Where(fileName => fileName.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase)); + } + } +} diff --git a/src/Orchard/Mvc/ViewEngines/Razor/WebViewPage.cs b/src/Orchard/Mvc/ViewEngines/Razor/WebViewPage.cs index 66dcb1c38..516e17fe3 100644 --- a/src/Orchard/Mvc/ViewEngines/Razor/WebViewPage.cs +++ b/src/Orchard/Mvc/ViewEngines/Razor/WebViewPage.cs @@ -1,247 +1,247 @@ -using System; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Web.WebPages; -using Orchard.ContentManagement; -using Orchard.DisplayManagement; -using Orchard.DisplayManagement.Shapes; -using Orchard.Environment.Configuration; -using Orchard.Localization; -using Orchard.Mvc.Html; -using Orchard.Mvc.Spooling; -using Orchard.Security; -using Orchard.Security.Permissions; -using Orchard.UI.Resources; - -namespace Orchard.Mvc.ViewEngines.Razor { - - public abstract class WebViewPage : System.Web.Mvc.WebViewPage, IOrchardViewPage { - private ScriptRegister _scriptRegister; - private ResourceRegister _stylesheetRegister; - private Localizer _localizer = NullLocalizer.Instance; - private object _display; - private object _layout; - - public Localizer T { - get { - // first time used, create it - if(_localizer == NullLocalizer.Instance) { - - // if the Model is a shape, get localization scopes from binding sources - // e.g., Logon.cshtml in a theme, overriging Users/Logon.cshtml, needs T to - // fallback to the one in Users - var shape = Model as IShape; - if(shape != null && shape.Metadata.BindingSources.Count > 1) { - var localizers = shape.Metadata.BindingSources.Reverse().Select(scope => LocalizationUtilities.Resolve(ViewContext, scope)).ToList(); - _localizer = (text, args) => { - foreach(var localizer in localizers) { - var hint = localizer(text, args); - // if not localized using this scope, use next scope - if(hint.Text != text) { - return hint; - } - } - - // no localization found, return default value - return new LocalizedString(text, VirtualPath, text, args); - }; - } - else { - // not a shape, use the VirtualPath as scope - _localizer = LocalizationUtilities.Resolve(ViewContext, VirtualPath); - } - } - - return _localizer; - } - } - - public dynamic Display { get { return _display; } } - // review: (heskew) is it going to be a problem? - public new dynamic Layout { get { return _layout; } } - public WorkContext WorkContext { get; set; } - - public dynamic New { get { return ShapeFactory; } } - - private IDisplayHelperFactory _displayHelperFactory; - public IDisplayHelperFactory DisplayHelperFactory { - get { - return _displayHelperFactory ?? (_displayHelperFactory = WorkContext.Resolve()); - } - } - - private IShapeFactory _shapeFactory; - public IShapeFactory ShapeFactory { - get { - return _shapeFactory ?? (_shapeFactory = WorkContext.Resolve()); - } - } - - private IAuthorizer _authorizer; - public IAuthorizer Authorizer { - get { - return _authorizer ?? (_authorizer = WorkContext.Resolve()); - } - } - - private IContentManager _contentManager; - public dynamic BuildDisplay(IContent content, string displayType = "", string groupId = "") { - if (_contentManager == null) { - _contentManager = WorkContext.Resolve(); - } - - return _contentManager.BuildDisplay(content, displayType, groupId); - } - - public ScriptRegister Script { - get { - return _scriptRegister ?? - (_scriptRegister = new WebViewScriptRegister(this, Html.ViewDataContainer, ResourceManager)); - } - } - - private IResourceManager _resourceManager; - public IResourceManager ResourceManager { - get { return _resourceManager ?? (_resourceManager = WorkContext.Resolve()); } - } - - public ResourceRegister Style { - get { - return _stylesheetRegister ?? - (_stylesheetRegister = new ResourceRegister(Html.ViewDataContainer, ResourceManager, "stylesheet")); - } - } - - private string[] _commonLocations; - public string[] CommonLocations { get { return _commonLocations ?? (_commonLocations = WorkContext.Resolve().CommonLocations); } } - - public void RegisterImageSet(string imageSet, string style = "", int size = 16) { - // hack to fake the style "alternate" for now so we don't have to change stylesheet names when this is hooked up - // todo: (heskew) deal in shapes so we have real alternates - var imageSetStylesheet = !string.IsNullOrWhiteSpace(style) - ? string.Format("{0}-{1}.css", imageSet, style) - : string.Format("{0}.css", imageSet); - Style.Include(imageSetStylesheet); - } - - public virtual void RegisterLink(LinkEntry link) { - ResourceManager.RegisterLink(link); - } - - public void SetMeta(string name = null, string content = null, string httpEquiv = null, string charset = null) { - var metaEntry = new MetaEntry(name, content, httpEquiv, charset); - SetMeta(metaEntry); - } - - public virtual void SetMeta(MetaEntry meta) { - ResourceManager.SetMeta(meta); - } - - public void AppendMeta(string name, string content, string contentSeparator) { - AppendMeta(new MetaEntry { Name = name, Content = content }, contentSeparator); - } - - public virtual void AppendMeta(MetaEntry meta, string contentSeparator) { - ResourceManager.AppendMeta(meta, contentSeparator); - } - - public override void InitHelpers() { - base.InitHelpers(); - - WorkContext = ViewContext.GetWorkContext(); - - _display = DisplayHelperFactory.CreateHelper(ViewContext, this); - _layout = WorkContext.Layout; - } - - public bool AuthorizedFor(Permission permission) { - return Authorizer.Authorize(permission); - } - - public bool AuthorizedFor(Permission permission, IContent content) { - return Authorizer.Authorize(permission, content); - } - - public bool HasText(object thing) { - return !string.IsNullOrWhiteSpace(Convert.ToString(thing)); - } - - public OrchardTagBuilder Tag(dynamic shape, string tagName) { - return Html.GetWorkContext().Resolve().Create(shape, tagName); - } - - public IHtmlString DisplayChildren(dynamic shape) { - var writer = new HtmlStringWriter(); - foreach (var item in shape) { - writer.Write(Display(item)); - } - return writer; - } - - private string _tenantPrefix; - public override string Href(string path, params object[] pathParts) { - if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) - || path.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { - return path; - } - - if (_tenantPrefix == null) { - _tenantPrefix = WorkContext.Resolve().RequestUrlPrefix ?? ""; - } - - if (!String.IsNullOrEmpty(_tenantPrefix) - && path.StartsWith("~/") - && !CommonLocations.Any(gpp=>path.StartsWith(gpp, StringComparison.OrdinalIgnoreCase)) - ) { - return base.Href("~/" + _tenantPrefix + path.Substring(2), pathParts); - } - - return base.Href(path, pathParts); - } - - public IDisposable Capture(Action callback) { - return new CaptureScope(this, callback); - } - - public IDisposable Capture(dynamic zone, string position = null) { - return new CaptureScope(this, html => zone.Add(html, position)); - } - - class CaptureScope : IDisposable { - readonly WebPageBase _viewPage; - readonly Action _callback; - - public CaptureScope(WebPageBase viewPage, Action callback) { - _viewPage = viewPage; - _callback = callback; - _viewPage.OutputStack.Push(new HtmlStringWriter()); - } - - void IDisposable.Dispose() { - var writer = (HtmlStringWriter)_viewPage.OutputStack.Pop(); - _callback(writer); - } - } - - class WebViewScriptRegister : ScriptRegister { - private readonly WebPageBase _viewPage; - - public WebViewScriptRegister(WebPageBase viewPage, IViewDataContainer container, IResourceManager resourceManager) - : base(container, resourceManager) { - _viewPage = viewPage; - } - - public override IDisposable Head() { - return new CaptureScope(_viewPage, s => ResourceManager.RegisterHeadScript(s.ToString())); - } - - public override IDisposable Foot() { - return new CaptureScope(_viewPage, s => ResourceManager.RegisterFootScript(s.ToString())); - } - } - } - - public abstract class WebViewPage : WebViewPage { - } -} +using System; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.Web.WebPages; +using Orchard.ContentManagement; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Shapes; +using Orchard.Environment.Configuration; +using Orchard.Localization; +using Orchard.Mvc.Html; +using Orchard.Mvc.Spooling; +using Orchard.Security; +using Orchard.Security.Permissions; +using Orchard.UI.Resources; + +namespace Orchard.Mvc.ViewEngines.Razor { + + public abstract class WebViewPage : System.Web.Mvc.WebViewPage, IOrchardViewPage { + private ScriptRegister _scriptRegister; + private ResourceRegister _stylesheetRegister; + private Localizer _localizer = NullLocalizer.Instance; + private object _display; + private object _layout; + + public Localizer T { + get { + // first time used, create it + if(_localizer == NullLocalizer.Instance) { + + // if the Model is a shape, get localization scopes from binding sources + // e.g., Logon.cshtml in a theme, overriging Users/Logon.cshtml, needs T to + // fallback to the one in Users + var shape = Model as IShape; + if(shape != null && shape.Metadata.BindingSources.Count > 1) { + var localizers = shape.Metadata.BindingSources.Reverse().Select(scope => LocalizationUtilities.Resolve(ViewContext, scope)).ToList(); + _localizer = (text, args) => { + foreach(var localizer in localizers) { + var hint = localizer(text, args); + // if not localized using this scope, use next scope + if(hint.Text != text) { + return hint; + } + } + + // no localization found, return default value + return new LocalizedString(text, VirtualPath, text, args); + }; + } + else { + // not a shape, use the VirtualPath as scope + _localizer = LocalizationUtilities.Resolve(ViewContext, VirtualPath); + } + } + + return _localizer; + } + } + + public dynamic Display { get { return _display; } } + // review: (heskew) is it going to be a problem? + public new dynamic Layout { get { return _layout; } } + public WorkContext WorkContext { get; set; } + + public dynamic New { get { return ShapeFactory; } } + + private IDisplayHelperFactory _displayHelperFactory; + public IDisplayHelperFactory DisplayHelperFactory { + get { + return _displayHelperFactory ?? (_displayHelperFactory = WorkContext.Resolve()); + } + } + + private IShapeFactory _shapeFactory; + public IShapeFactory ShapeFactory { + get { + return _shapeFactory ?? (_shapeFactory = WorkContext.Resolve()); + } + } + + private IAuthorizer _authorizer; + public IAuthorizer Authorizer { + get { + return _authorizer ?? (_authorizer = WorkContext.Resolve()); + } + } + + private IContentManager _contentManager; + public dynamic BuildDisplay(IContent content, string displayType = "", string groupId = "") { + if (_contentManager == null) { + _contentManager = WorkContext.Resolve(); + } + + return _contentManager.BuildDisplay(content, displayType, groupId); + } + + public ScriptRegister Script { + get { + return _scriptRegister ?? + (_scriptRegister = new WebViewScriptRegister(this, Html.ViewDataContainer, ResourceManager)); + } + } + + private IResourceManager _resourceManager; + public IResourceManager ResourceManager { + get { return _resourceManager ?? (_resourceManager = WorkContext.Resolve()); } + } + + public ResourceRegister Style { + get { + return _stylesheetRegister ?? + (_stylesheetRegister = new ResourceRegister(Html.ViewDataContainer, ResourceManager, "stylesheet")); + } + } + + private string[] _commonLocations; + public string[] CommonLocations { get { return _commonLocations ?? (_commonLocations = WorkContext.Resolve().CommonLocations); } } + + public void RegisterImageSet(string imageSet, string style = "", int size = 16) { + // hack to fake the style "alternate" for now so we don't have to change stylesheet names when this is hooked up + // todo: (heskew) deal in shapes so we have real alternates + var imageSetStylesheet = !string.IsNullOrWhiteSpace(style) + ? string.Format("{0}-{1}.css", imageSet, style) + : string.Format("{0}.css", imageSet); + Style.Include(imageSetStylesheet); + } + + public virtual void RegisterLink(LinkEntry link) { + ResourceManager.RegisterLink(link); + } + + public void SetMeta(string name = null, string content = null, string httpEquiv = null, string charset = null) { + var metaEntry = new MetaEntry(name, content, httpEquiv, charset); + SetMeta(metaEntry); + } + + public virtual void SetMeta(MetaEntry meta) { + ResourceManager.SetMeta(meta); + } + + public void AppendMeta(string name, string content, string contentSeparator) { + AppendMeta(new MetaEntry { Name = name, Content = content }, contentSeparator); + } + + public virtual void AppendMeta(MetaEntry meta, string contentSeparator) { + ResourceManager.AppendMeta(meta, contentSeparator); + } + + public override void InitHelpers() { + base.InitHelpers(); + + WorkContext = ViewContext.GetWorkContext(); + + _display = DisplayHelperFactory.CreateHelper(ViewContext, this); + _layout = WorkContext.Layout; + } + + public bool AuthorizedFor(Permission permission) { + return Authorizer.Authorize(permission); + } + + public bool AuthorizedFor(Permission permission, IContent content) { + return Authorizer.Authorize(permission, content); + } + + public bool HasText(object thing) { + return !string.IsNullOrWhiteSpace(Convert.ToString(thing)); + } + + public OrchardTagBuilder Tag(dynamic shape, string tagName) { + return Html.GetWorkContext().Resolve().Create(shape, tagName); + } + + public IHtmlString DisplayChildren(dynamic shape) { + var writer = new HtmlStringWriter(); + foreach (var item in shape) { + writer.Write(Display(item)); + } + return writer; + } + + private string _tenantPrefix; + public override string Href(string path, params object[] pathParts) { + if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) + || path.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { + return path; + } + + if (_tenantPrefix == null) { + _tenantPrefix = WorkContext.Resolve().RequestUrlPrefix ?? ""; + } + + if (!String.IsNullOrEmpty(_tenantPrefix) + && path.StartsWith("~/") + && !CommonLocations.Any(gpp=>path.StartsWith(gpp, StringComparison.OrdinalIgnoreCase)) + ) { + return base.Href("~/" + _tenantPrefix + path.Substring(2), pathParts); + } + + return base.Href(path, pathParts); + } + + public IDisposable Capture(Action callback) { + return new CaptureScope(this, callback); + } + + public IDisposable Capture(dynamic zone, string position = null) { + return new CaptureScope(this, html => zone.Add(html, position)); + } + + class CaptureScope : IDisposable { + readonly WebPageBase _viewPage; + readonly Action _callback; + + public CaptureScope(WebPageBase viewPage, Action callback) { + _viewPage = viewPage; + _callback = callback; + _viewPage.OutputStack.Push(new HtmlStringWriter()); + } + + void IDisposable.Dispose() { + var writer = (HtmlStringWriter)_viewPage.OutputStack.Pop(); + _callback(writer); + } + } + + class WebViewScriptRegister : ScriptRegister { + private readonly WebPageBase _viewPage; + + public WebViewScriptRegister(WebPageBase viewPage, IViewDataContainer container, IResourceManager resourceManager) + : base(container, resourceManager) { + _viewPage = viewPage; + } + + public override IDisposable Head() { + return new CaptureScope(_viewPage, s => ResourceManager.RegisterHeadScript(s.ToString())); + } + + public override IDisposable Foot() { + return new CaptureScope(_viewPage, s => ResourceManager.RegisterFootScript(s.ToString())); + } + } + } + + public abstract class WebViewPage : WebViewPage { + } +} diff --git a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs index 54ba27b23..a518278c7 100644 --- a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs +++ b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs @@ -1,167 +1,167 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Mvc; -using Orchard.Environment.Configuration; -using Orchard.Environment.Descriptor.Models; -using Orchard.Environment.Extensions; -using Orchard.Environment.Extensions.Helpers; -using Orchard.Environment.Extensions.Models; -using Orchard.Logging; - -namespace Orchard.Mvc.ViewEngines.ThemeAwareness { - public interface IThemeAwareViewEngine : IDependency { - ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache, bool useDeepPaths); - ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache, bool useDeepPaths); - } - - public class ThemeAwareViewEngine : IThemeAwareViewEngine { - private readonly WorkContext _workContext; - private readonly IEnumerable _viewEngineProviders; - private readonly IConfiguredEnginesCache _configuredEnginesCache; - private readonly IExtensionManager _extensionManager; - private readonly ShellDescriptor _shellDescriptor; - private readonly IViewEngine _nullEngines = new ViewEngineCollectionWrapper(Enumerable.Empty()); - - public ThemeAwareViewEngine( - WorkContext workContext, - IEnumerable viewEngineProviders, - IConfiguredEnginesCache configuredEnginesCache, - IExtensionManager extensionManager, - ShellDescriptor shellDescriptor) { - _workContext = workContext; - _viewEngineProviders = viewEngineProviders; - _configuredEnginesCache = configuredEnginesCache; - _extensionManager = extensionManager; - _shellDescriptor = shellDescriptor; - - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache, bool useDeepPaths) { - var engines = _nullEngines; - - if (partialViewName.StartsWith("/") || partialViewName.StartsWith("~")) { - engines = BareEngines(); - } - else if (_workContext.CurrentTheme != null) { - engines = useDeepPaths ? DeepEngines(_workContext.CurrentTheme) : ShallowEngines(_workContext.CurrentTheme); - } - - return engines.FindPartialView(controllerContext, partialViewName, useCache); - } - - public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache, bool useDeepPaths) { - var engines = _nullEngines; - - if (viewName.StartsWith("/") || viewName.StartsWith("~")) { - engines = BareEngines(); - } - else if (_workContext.CurrentTheme != null) { - engines = useDeepPaths ? DeepEngines(_workContext.CurrentTheme) : ShallowEngines(_workContext.CurrentTheme); - } - - return engines.FindView(controllerContext, viewName, masterName, useCache); - } - - - private IViewEngine BareEngines() { - return _configuredEnginesCache.BindBareEngines(() => new ViewEngineCollectionWrapper(_viewEngineProviders.Select(vep => vep.CreateBareViewEngine()))); - } - - private IViewEngine ShallowEngines(ExtensionDescriptor theme) { - //return _configuredEnginesCache.BindShallowEngines(theme.ThemeName, () => new ViewEngineCollectionWrapper(_viewEngineProviders.Select(vep => vep.CreateBareViewEngine()))); - return DeepEngines(theme); - } - - private IViewEngine DeepEngines(ExtensionDescriptor theme) { - return _configuredEnginesCache.BindDeepEngines(theme.Id, () => { - // The order for searching for views is: - // 1. Current "theme" - // 2. Base themes of the current theme (in "base" order) - // 3. Active features from modules in dependency order - - var engines = Enumerable.Empty(); - // 1. current theme - engines = engines.Concat(CreateThemeViewEngines(theme)); - - // 2. Base themes of the current theme (in "base" order) - engines = GetBaseThemes(theme).Aggregate(engines, (current, baseTheme) => current.Concat(CreateThemeViewEngines(baseTheme))); - - // 3. Active features from modules in dependency order - var enabledModules = _extensionManager.EnabledFeatures(_shellDescriptor) - .Reverse() // reverse from (C <= B <= A) to (A => B => C) - .Where(fd => DefaultExtensionTypes.IsModule(fd.Extension.ExtensionType)); - - var moduleVirtualPaths = enabledModules - .Select(fd => fd.Extension.VirtualPath) - .Distinct(StringComparer.OrdinalIgnoreCase) // is Distinct guaranty to keep order? - .ToList(); - - var moduleLocations = enabledModules - .Select(fd => fd.Extension.Location) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - var moduleParams = new CreateModulesViewEngineParams { VirtualPaths = moduleVirtualPaths, ExtensionLocations = moduleLocations }; - engines = engines.Concat(_viewEngineProviders.Select(vep => vep.CreateModulesViewEngine(moduleParams))); - - return new ViewEngineCollectionWrapper(engines); - }); - } - - private IEnumerable CreateThemeViewEngines(ExtensionDescriptor theme) { - var themeLocation = theme.Location + "/" + theme.Id; - var themeParams = new CreateThemeViewEngineParams {VirtualPath = themeLocation}; - return _viewEngineProviders.Select(vep => vep.CreateThemeViewEngine(themeParams)); - } - - private IEnumerable GetBaseThemes(ExtensionDescriptor themeExtension) { - if (themeExtension.Id.Equals("TheAdmin", StringComparison.OrdinalIgnoreCase)) { - // Special case: conceptually, the base themes of "TheAdmin" is the list of all - // enabled themes. This is so that any enabled theme can have controller/action/views - // in the Admin of the site. - return _extensionManager - .EnabledFeatures(_shellDescriptor) - .Reverse() // reverse from (C <= B <= A) to (A => B => C) - .Select(fd => fd.Extension) - .Where(fd => DefaultExtensionTypes.IsTheme(fd.ExtensionType)); - } - else { - var availableFeatures = _extensionManager.AvailableFeatures(); - var list = new List(); - while(true) { - if (themeExtension == null) - break; - - if (String.IsNullOrEmpty(themeExtension.BaseTheme)) - break; - - var baseFeature = availableFeatures.FirstOrDefault(fd => fd.Id == themeExtension.BaseTheme); - if (baseFeature == null) { - Logger.Error("Base theme '{0}' of theme '{1}' not found in list of features", themeExtension.BaseTheme, themeExtension.Id); - break; - } - - // Protect against potential infinite loop - if (list.Contains(baseFeature.Extension)) { - Logger.Error("Base theme '{0}' of theme '{1}' ignored, as it seems there is recursion in base themes", themeExtension.BaseTheme, themeExtension.Id); - break; - } - - list.Add(baseFeature.Extension); - - themeExtension = baseFeature.Extension; - } - return list; - } - } - - public void ReleaseView(ControllerContext controllerContext, IView view) { - throw new NotImplementedException(); - } - } - -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using Orchard.Environment.Configuration; +using Orchard.Environment.Descriptor.Models; +using Orchard.Environment.Extensions; +using Orchard.Environment.Extensions.Helpers; +using Orchard.Environment.Extensions.Models; +using Orchard.Logging; + +namespace Orchard.Mvc.ViewEngines.ThemeAwareness { + public interface IThemeAwareViewEngine : IDependency { + ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache, bool useDeepPaths); + ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache, bool useDeepPaths); + } + + public class ThemeAwareViewEngine : IThemeAwareViewEngine { + private readonly WorkContext _workContext; + private readonly IEnumerable _viewEngineProviders; + private readonly IConfiguredEnginesCache _configuredEnginesCache; + private readonly IExtensionManager _extensionManager; + private readonly ShellDescriptor _shellDescriptor; + private readonly IViewEngine _nullEngines = new ViewEngineCollectionWrapper(Enumerable.Empty()); + + public ThemeAwareViewEngine( + WorkContext workContext, + IEnumerable viewEngineProviders, + IConfiguredEnginesCache configuredEnginesCache, + IExtensionManager extensionManager, + ShellDescriptor shellDescriptor) { + _workContext = workContext; + _viewEngineProviders = viewEngineProviders; + _configuredEnginesCache = configuredEnginesCache; + _extensionManager = extensionManager; + _shellDescriptor = shellDescriptor; + + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache, bool useDeepPaths) { + var engines = _nullEngines; + + if (partialViewName.StartsWith("/") || partialViewName.StartsWith("~")) { + engines = BareEngines(); + } + else if (_workContext.CurrentTheme != null) { + engines = useDeepPaths ? DeepEngines(_workContext.CurrentTheme) : ShallowEngines(_workContext.CurrentTheme); + } + + return engines.FindPartialView(controllerContext, partialViewName, useCache); + } + + public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache, bool useDeepPaths) { + var engines = _nullEngines; + + if (viewName.StartsWith("/") || viewName.StartsWith("~")) { + engines = BareEngines(); + } + else if (_workContext.CurrentTheme != null) { + engines = useDeepPaths ? DeepEngines(_workContext.CurrentTheme) : ShallowEngines(_workContext.CurrentTheme); + } + + return engines.FindView(controllerContext, viewName, masterName, useCache); + } + + + private IViewEngine BareEngines() { + return _configuredEnginesCache.BindBareEngines(() => new ViewEngineCollectionWrapper(_viewEngineProviders.Select(vep => vep.CreateBareViewEngine()))); + } + + private IViewEngine ShallowEngines(ExtensionDescriptor theme) { + //return _configuredEnginesCache.BindShallowEngines(theme.ThemeName, () => new ViewEngineCollectionWrapper(_viewEngineProviders.Select(vep => vep.CreateBareViewEngine()))); + return DeepEngines(theme); + } + + private IViewEngine DeepEngines(ExtensionDescriptor theme) { + return _configuredEnginesCache.BindDeepEngines(theme.Id, () => { + // The order for searching for views is: + // 1. Current "theme" + // 2. Base themes of the current theme (in "base" order) + // 3. Active features from modules in dependency order + + var engines = Enumerable.Empty(); + // 1. current theme + engines = engines.Concat(CreateThemeViewEngines(theme)); + + // 2. Base themes of the current theme (in "base" order) + engines = GetBaseThemes(theme).Aggregate(engines, (current, baseTheme) => current.Concat(CreateThemeViewEngines(baseTheme))); + + // 3. Active features from modules in dependency order + var enabledModules = _extensionManager.EnabledFeatures(_shellDescriptor) + .Reverse() // reverse from (C <= B <= A) to (A => B => C) + .Where(fd => DefaultExtensionTypes.IsModule(fd.Extension.ExtensionType)); + + var moduleVirtualPaths = enabledModules + .Select(fd => fd.Extension.VirtualPath) + .Distinct(StringComparer.OrdinalIgnoreCase) // is Distinct guaranty to keep order? + .ToList(); + + var moduleLocations = enabledModules + .Select(fd => fd.Extension.Location) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + var moduleParams = new CreateModulesViewEngineParams { VirtualPaths = moduleVirtualPaths, ExtensionLocations = moduleLocations }; + engines = engines.Concat(_viewEngineProviders.Select(vep => vep.CreateModulesViewEngine(moduleParams))); + + return new ViewEngineCollectionWrapper(engines); + }); + } + + private IEnumerable CreateThemeViewEngines(ExtensionDescriptor theme) { + var themeLocation = theme.Location + "/" + theme.Id; + var themeParams = new CreateThemeViewEngineParams {VirtualPath = themeLocation}; + return _viewEngineProviders.Select(vep => vep.CreateThemeViewEngine(themeParams)); + } + + private IEnumerable GetBaseThemes(ExtensionDescriptor themeExtension) { + if (themeExtension.Id.Equals("TheAdmin", StringComparison.OrdinalIgnoreCase)) { + // Special case: conceptually, the base themes of "TheAdmin" is the list of all + // enabled themes. This is so that any enabled theme can have controller/action/views + // in the Admin of the site. + return _extensionManager + .EnabledFeatures(_shellDescriptor) + .Reverse() // reverse from (C <= B <= A) to (A => B => C) + .Select(fd => fd.Extension) + .Where(fd => DefaultExtensionTypes.IsTheme(fd.ExtensionType)); + } + else { + var availableFeatures = _extensionManager.AvailableFeatures(); + var list = new List(); + while(true) { + if (themeExtension == null) + break; + + if (String.IsNullOrEmpty(themeExtension.BaseTheme)) + break; + + var baseFeature = availableFeatures.FirstOrDefault(fd => fd.Id == themeExtension.BaseTheme); + if (baseFeature == null) { + Logger.Error("Base theme '{0}' of theme '{1}' not found in list of features", themeExtension.BaseTheme, themeExtension.Id); + break; + } + + // Protect against potential infinite loop + if (list.Contains(baseFeature.Extension)) { + Logger.Error("Base theme '{0}' of theme '{1}' ignored, as it seems there is recursion in base themes", themeExtension.BaseTheme, themeExtension.Id); + break; + } + + list.Add(baseFeature.Extension); + + themeExtension = baseFeature.Extension; + } + return list; + } + } + + public void ReleaseView(ControllerContext controllerContext, IView view) { + throw new NotImplementedException(); + } + } + +} diff --git a/src/Orchard/Mvc/ViewPage.cs b/src/Orchard/Mvc/ViewPage.cs index 5889ef0f6..799c4837e 100644 --- a/src/Orchard/Mvc/ViewPage.cs +++ b/src/Orchard/Mvc/ViewPage.cs @@ -1,168 +1,168 @@ -using System; -using System.IO; -using System.Web; -using System.Web.Mvc; -using System.Web.UI; -using Autofac; -using Orchard.DisplayManagement; -using Orchard.DisplayManagement.Shapes; -using Orchard.Localization; -using Orchard.Mvc.Html; -using Orchard.Mvc.Spooling; -using Orchard.Security; -using Orchard.Security.Permissions; -using Orchard.UI.Resources; - -namespace Orchard.Mvc { - public class ViewPage : System.Web.Mvc.ViewPage, IOrchardViewPage { - private ScriptRegister _scriptRegister; - private ResourceRegister _stylesheetRegister; - - private object _display; - private Localizer _localizer = NullLocalizer.Instance; - private object _layout; - private WorkContext _workContext; - - public Localizer T { get { return _localizer; } } - public dynamic Display { get { return _display; } } - public ScriptRegister Script { - get { - return _scriptRegister ?? - (_scriptRegister = new ViewPageScriptRegister(Writer, Html.ViewDataContainer, Html.GetWorkContext().Resolve())); - } - } - - public dynamic Layout { get { return _layout; } } - public WorkContext WorkContext { get { return _workContext; } } - - private IDisplayHelperFactory _displayHelperFactory; - public IDisplayHelperFactory DisplayHelperFactory { - get { - return _displayHelperFactory ?? (_displayHelperFactory = _workContext.Resolve()); - } - } - - private IShapeFactory _shapeFactory; - public IShapeFactory ShapeFactory { - get { - return _shapeFactory ?? (_shapeFactory = _workContext.Resolve()); - } - } - - private IAuthorizer _authorizer; - public IAuthorizer Authorizer { - get { - return _authorizer ?? (_authorizer = _workContext.Resolve()); - } - } - - public ResourceRegister Style { - get { - return _stylesheetRegister ?? - (_stylesheetRegister = new ResourceRegister(Html.ViewDataContainer, Html.GetWorkContext().Resolve(), "stylesheet")); - } - } - - public override void InitHelpers() { - base.InitHelpers(); - - _workContext = ViewContext.GetWorkContext(); - - _localizer = LocalizationUtilities.Resolve(ViewContext, AppRelativeVirtualPath); - _display = DisplayHelperFactory.CreateHelper(ViewContext, this); - _layout = _workContext.Layout; - } - - public virtual void RegisterLink(LinkEntry link) { - Html.GetWorkContext().Resolve().RegisterLink(link); - } - - public void SetMeta(string name = null, string content = null, string httpEquiv = null, string charset = null) { - var metaEntry = new MetaEntry(name, content, httpEquiv, charset); - SetMeta(metaEntry); - } - - public virtual void SetMeta(MetaEntry meta) { - Html.GetWorkContext().Resolve().SetMeta(meta); - } - - public void AppendMeta(string name, string content, string contentSeparator) { - AppendMeta(new MetaEntry { Name = name, Content = content }, contentSeparator); - } - - public virtual void AppendMeta(MetaEntry meta, string contentSeparator) { - Html.GetWorkContext().Resolve().AppendMeta(meta, contentSeparator); - } - - public MvcHtmlString H(string value) { - return MvcHtmlString.Create(Html.Encode(value)); - } - - public bool AuthorizedFor(Permission permission) { - return Authorizer.Authorize(permission); - } - - public bool HasText(object thing) { - return !string.IsNullOrWhiteSpace(Convert.ToString(thing)); - } - - public OrchardTagBuilder Tag(dynamic shape, string tagName) { - return Html.GetWorkContext().Resolve().Create(shape, tagName); - } - - public IHtmlString DisplayChildren(dynamic shape) { - var writer = new HtmlStringWriter(); - foreach (var item in shape) { - writer.Write(Display(item)); - } - return writer; - } - - public IDisposable Capture(Action callback) { - return new CaptureScope(Writer, callback); - } - - public IDisposable Capture(dynamic zone, string position = null) { - return new CaptureScope(Writer, html => zone.Add(html, position)); - } - - public class CaptureScope : IDisposable { - private readonly HtmlTextWriter _context; - private readonly Action _callback; - private readonly TextWriter _oldWriter; - private readonly HtmlStringWriter _writer; - - public CaptureScope(HtmlTextWriter context, Action callback) { - _context = context; - _oldWriter = _context.InnerWriter; - _callback = callback; - _context.InnerWriter = _writer = new HtmlStringWriter(); - } - - public void Dispose() { - _callback(_writer); - _context.InnerWriter = _oldWriter; - } - } - - internal class ViewPageScriptRegister : ScriptRegister { - private readonly HtmlTextWriter _context; - - public ViewPageScriptRegister(HtmlTextWriter context, IViewDataContainer container, IResourceManager resourceManager) - : base(container, resourceManager) { - _context = context; - } - - public override IDisposable Head() { - return new CaptureScope(_context, s => ResourceManager.RegisterHeadScript(s.ToString())); - } - - public override IDisposable Foot() { - return new CaptureScope(_context, s => ResourceManager.RegisterFootScript(s.ToString())); - } - } - } - - public class ViewPage : ViewPage { - } -} +using System; +using System.IO; +using System.Web; +using System.Web.Mvc; +using System.Web.UI; +using Autofac; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Shapes; +using Orchard.Localization; +using Orchard.Mvc.Html; +using Orchard.Mvc.Spooling; +using Orchard.Security; +using Orchard.Security.Permissions; +using Orchard.UI.Resources; + +namespace Orchard.Mvc { + public class ViewPage : System.Web.Mvc.ViewPage, IOrchardViewPage { + private ScriptRegister _scriptRegister; + private ResourceRegister _stylesheetRegister; + + private object _display; + private Localizer _localizer = NullLocalizer.Instance; + private object _layout; + private WorkContext _workContext; + + public Localizer T { get { return _localizer; } } + public dynamic Display { get { return _display; } } + public ScriptRegister Script { + get { + return _scriptRegister ?? + (_scriptRegister = new ViewPageScriptRegister(Writer, Html.ViewDataContainer, Html.GetWorkContext().Resolve())); + } + } + + public dynamic Layout { get { return _layout; } } + public WorkContext WorkContext { get { return _workContext; } } + + private IDisplayHelperFactory _displayHelperFactory; + public IDisplayHelperFactory DisplayHelperFactory { + get { + return _displayHelperFactory ?? (_displayHelperFactory = _workContext.Resolve()); + } + } + + private IShapeFactory _shapeFactory; + public IShapeFactory ShapeFactory { + get { + return _shapeFactory ?? (_shapeFactory = _workContext.Resolve()); + } + } + + private IAuthorizer _authorizer; + public IAuthorizer Authorizer { + get { + return _authorizer ?? (_authorizer = _workContext.Resolve()); + } + } + + public ResourceRegister Style { + get { + return _stylesheetRegister ?? + (_stylesheetRegister = new ResourceRegister(Html.ViewDataContainer, Html.GetWorkContext().Resolve(), "stylesheet")); + } + } + + public override void InitHelpers() { + base.InitHelpers(); + + _workContext = ViewContext.GetWorkContext(); + + _localizer = LocalizationUtilities.Resolve(ViewContext, AppRelativeVirtualPath); + _display = DisplayHelperFactory.CreateHelper(ViewContext, this); + _layout = _workContext.Layout; + } + + public virtual void RegisterLink(LinkEntry link) { + Html.GetWorkContext().Resolve().RegisterLink(link); + } + + public void SetMeta(string name = null, string content = null, string httpEquiv = null, string charset = null) { + var metaEntry = new MetaEntry(name, content, httpEquiv, charset); + SetMeta(metaEntry); + } + + public virtual void SetMeta(MetaEntry meta) { + Html.GetWorkContext().Resolve().SetMeta(meta); + } + + public void AppendMeta(string name, string content, string contentSeparator) { + AppendMeta(new MetaEntry { Name = name, Content = content }, contentSeparator); + } + + public virtual void AppendMeta(MetaEntry meta, string contentSeparator) { + Html.GetWorkContext().Resolve().AppendMeta(meta, contentSeparator); + } + + public MvcHtmlString H(string value) { + return MvcHtmlString.Create(Html.Encode(value)); + } + + public bool AuthorizedFor(Permission permission) { + return Authorizer.Authorize(permission); + } + + public bool HasText(object thing) { + return !string.IsNullOrWhiteSpace(Convert.ToString(thing)); + } + + public OrchardTagBuilder Tag(dynamic shape, string tagName) { + return Html.GetWorkContext().Resolve().Create(shape, tagName); + } + + public IHtmlString DisplayChildren(dynamic shape) { + var writer = new HtmlStringWriter(); + foreach (var item in shape) { + writer.Write(Display(item)); + } + return writer; + } + + public IDisposable Capture(Action callback) { + return new CaptureScope(Writer, callback); + } + + public IDisposable Capture(dynamic zone, string position = null) { + return new CaptureScope(Writer, html => zone.Add(html, position)); + } + + public class CaptureScope : IDisposable { + private readonly HtmlTextWriter _context; + private readonly Action _callback; + private readonly TextWriter _oldWriter; + private readonly HtmlStringWriter _writer; + + public CaptureScope(HtmlTextWriter context, Action callback) { + _context = context; + _oldWriter = _context.InnerWriter; + _callback = callback; + _context.InnerWriter = _writer = new HtmlStringWriter(); + } + + public void Dispose() { + _callback(_writer); + _context.InnerWriter = _oldWriter; + } + } + + internal class ViewPageScriptRegister : ScriptRegister { + private readonly HtmlTextWriter _context; + + public ViewPageScriptRegister(HtmlTextWriter context, IViewDataContainer container, IResourceManager resourceManager) + : base(container, resourceManager) { + _context = context; + } + + public override IDisposable Head() { + return new CaptureScope(_context, s => ResourceManager.RegisterHeadScript(s.ToString())); + } + + public override IDisposable Foot() { + return new CaptureScope(_context, s => ResourceManager.RegisterFootScript(s.ToString())); + } + } + } + + public class ViewPage : ViewPage { + } +} diff --git a/src/Orchard/Mvc/ViewUserControl.cs b/src/Orchard/Mvc/ViewUserControl.cs index 8653a1e08..ead2f2ad9 100644 --- a/src/Orchard/Mvc/ViewUserControl.cs +++ b/src/Orchard/Mvc/ViewUserControl.cs @@ -1,128 +1,128 @@ -using System; -using System.Web; -using System.Web.Mvc; -using Autofac; -using Orchard.DisplayManagement; -using Orchard.DisplayManagement.Shapes; -using Orchard.Localization; -using Orchard.Mvc.Html; -using Orchard.Mvc.Spooling; -using Orchard.Security; -using Orchard.Security.Permissions; -using Orchard.UI.Resources; - -namespace Orchard.Mvc { - public class ViewUserControl : System.Web.Mvc.ViewUserControl,IOrchardViewPage { - private ScriptRegister _scriptRegister; - private ResourceRegister _stylesheetRegister; - - private object _display; - private Localizer _localizer = NullLocalizer.Instance; - private object _layout; - private WorkContext _workContext; - - public Localizer T { get { return _localizer; } } - public dynamic Display { get { return _display; } } - public dynamic New { get { return ShapeFactory; } } - public dynamic Layout { get { return _layout; } } - public WorkContext WorkContext { get { return _workContext; } } - - private IDisplayHelperFactory _displayHelperFactory; - public IDisplayHelperFactory DisplayHelperFactory { - get { - return _displayHelperFactory ?? (_displayHelperFactory = _workContext.Resolve()); - } - } - - private IShapeFactory _shapeFactory; - public IShapeFactory ShapeFactory { - get { - return _shapeFactory ?? (_shapeFactory = _workContext.Resolve()); - } - } - - private IAuthorizer _authorizer; - public IAuthorizer Authorizer { - get { - return _authorizer ?? (_authorizer = _workContext.Resolve()); - } - } - - public ScriptRegister Script { - get { - return _scriptRegister ?? - (_scriptRegister = new ViewPage.ViewPageScriptRegister(Writer, Html.ViewDataContainer, Html.GetWorkContext().Resolve())); - } - } - - public ResourceRegister Style { - get { - return _stylesheetRegister ?? - (_stylesheetRegister = new ResourceRegister(Html.ViewDataContainer, Html.GetWorkContext().Resolve(), "stylesheet")); - } - } - - public virtual void RegisterLink(LinkEntry link) { - Html.GetWorkContext().Resolve().RegisterLink(link); - } - - public void SetMeta(string name = null, string content = null, string httpEquiv = null, string charset = null) { - var metaEntry = new MetaEntry(name, content, httpEquiv, charset); - SetMeta(metaEntry); - } - - public virtual void SetMeta(MetaEntry meta) { - Html.GetWorkContext().Resolve().SetMeta(meta); - } - - public void AppendMeta(string name, string content, string contentSeparator) { - AppendMeta(new MetaEntry { Name = name, Content = content }, contentSeparator); - } - - public virtual void AppendMeta(MetaEntry meta, string contentSeparator) { - Html.GetWorkContext().Resolve().AppendMeta(meta, contentSeparator); - } - - public override void RenderView(ViewContext viewContext) { - _workContext = viewContext.GetWorkContext(); - - _localizer = LocalizationUtilities.Resolve(viewContext, AppRelativeVirtualPath); - _display = DisplayHelperFactory.CreateHelper(viewContext, this); - _layout = _workContext.Layout; - - base.RenderView(viewContext); - } - - public MvcHtmlString H(string value) { - return MvcHtmlString.Create(Html.Encode(value)); - } - - public bool AuthorizedFor(Permission permission) { - return Authorizer.Authorize(permission); - } - - public bool HasText(object thing) { - return !string.IsNullOrWhiteSpace(Convert.ToString(thing)); - } - - public OrchardTagBuilder Tag(dynamic shape, string tagName) { - return Html.GetWorkContext().Resolve().Create(shape, tagName); - } - - public IHtmlString DisplayChildren(dynamic shape) { - var writer = new HtmlStringWriter(); - foreach (var item in shape) { - writer.Write(Display(item)); - } - return writer; - } - - public IDisposable Capture(Action callback) { - return new ViewPage.CaptureScope(Writer, callback); - } - - } - - public class ViewUserControl : ViewUserControl { - } -} +using System; +using System.Web; +using System.Web.Mvc; +using Autofac; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Shapes; +using Orchard.Localization; +using Orchard.Mvc.Html; +using Orchard.Mvc.Spooling; +using Orchard.Security; +using Orchard.Security.Permissions; +using Orchard.UI.Resources; + +namespace Orchard.Mvc { + public class ViewUserControl : System.Web.Mvc.ViewUserControl,IOrchardViewPage { + private ScriptRegister _scriptRegister; + private ResourceRegister _stylesheetRegister; + + private object _display; + private Localizer _localizer = NullLocalizer.Instance; + private object _layout; + private WorkContext _workContext; + + public Localizer T { get { return _localizer; } } + public dynamic Display { get { return _display; } } + public dynamic New { get { return ShapeFactory; } } + public dynamic Layout { get { return _layout; } } + public WorkContext WorkContext { get { return _workContext; } } + + private IDisplayHelperFactory _displayHelperFactory; + public IDisplayHelperFactory DisplayHelperFactory { + get { + return _displayHelperFactory ?? (_displayHelperFactory = _workContext.Resolve()); + } + } + + private IShapeFactory _shapeFactory; + public IShapeFactory ShapeFactory { + get { + return _shapeFactory ?? (_shapeFactory = _workContext.Resolve()); + } + } + + private IAuthorizer _authorizer; + public IAuthorizer Authorizer { + get { + return _authorizer ?? (_authorizer = _workContext.Resolve()); + } + } + + public ScriptRegister Script { + get { + return _scriptRegister ?? + (_scriptRegister = new ViewPage.ViewPageScriptRegister(Writer, Html.ViewDataContainer, Html.GetWorkContext().Resolve())); + } + } + + public ResourceRegister Style { + get { + return _stylesheetRegister ?? + (_stylesheetRegister = new ResourceRegister(Html.ViewDataContainer, Html.GetWorkContext().Resolve(), "stylesheet")); + } + } + + public virtual void RegisterLink(LinkEntry link) { + Html.GetWorkContext().Resolve().RegisterLink(link); + } + + public void SetMeta(string name = null, string content = null, string httpEquiv = null, string charset = null) { + var metaEntry = new MetaEntry(name, content, httpEquiv, charset); + SetMeta(metaEntry); + } + + public virtual void SetMeta(MetaEntry meta) { + Html.GetWorkContext().Resolve().SetMeta(meta); + } + + public void AppendMeta(string name, string content, string contentSeparator) { + AppendMeta(new MetaEntry { Name = name, Content = content }, contentSeparator); + } + + public virtual void AppendMeta(MetaEntry meta, string contentSeparator) { + Html.GetWorkContext().Resolve().AppendMeta(meta, contentSeparator); + } + + public override void RenderView(ViewContext viewContext) { + _workContext = viewContext.GetWorkContext(); + + _localizer = LocalizationUtilities.Resolve(viewContext, AppRelativeVirtualPath); + _display = DisplayHelperFactory.CreateHelper(viewContext, this); + _layout = _workContext.Layout; + + base.RenderView(viewContext); + } + + public MvcHtmlString H(string value) { + return MvcHtmlString.Create(Html.Encode(value)); + } + + public bool AuthorizedFor(Permission permission) { + return Authorizer.Authorize(permission); + } + + public bool HasText(object thing) { + return !string.IsNullOrWhiteSpace(Convert.ToString(thing)); + } + + public OrchardTagBuilder Tag(dynamic shape, string tagName) { + return Html.GetWorkContext().Resolve().Create(shape, tagName); + } + + public IHtmlString DisplayChildren(dynamic shape) { + var writer = new HtmlStringWriter(); + foreach (var item in shape) { + writer.Write(Display(item)); + } + return writer; + } + + public IDisposable Capture(Action callback) { + return new ViewPage.CaptureScope(Writer, callback); + } + + } + + public class ViewUserControl : ViewUserControl { + } +} diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 93d44dbdb..b9baf0915 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -1,1067 +1,1067 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} - Library - Properties - Orchard - Orchard.Framework - v4.5.1 - 512 - - - 3.5 - - false - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - ..\OrchardBasicCorrectness.ruleset - false - false - - - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false - 0436 - - - - False - ..\..\lib\autofac\Autofac.dll - True - - - False - ..\..\lib\autofac\Autofac.Configuration.dll - True - - - False - ..\..\lib\castle\net45\Castle.Core.dll - - - False - ..\..\lib\nhibernate\FluentNHibernate.dll - - - False - ..\..\lib\nhibernate\Iesi.Collections.dll - - - ..\..\lib\log4net\log4net.dll - - - - ..\..\lib\owin\Microsoft.Owin.dll - - - False - ..\..\lib\newtonsoft.json\Newtonsoft.Json.dll - - - ..\..\lib\nhibernate\NHibernate.dll - False - True - - - False - ..\..\lib\nhibernate.linq\NHibernate.Linq.dll - - - ..\..\lib\owin\Owin.dll - - - - 3.5 - - - - 3.5 - - - - True - - - 3.0 - - - - - - - - ..\..\lib\aspnetwebapi\System.Web.Http.dll - - - False - ..\..\lib\aspnetwebapi\System.Web.Http.WebHost.dll - - - False - ..\..\lib\aspnetmvc\System.Web.Mvc.dll - - - False - ..\..\lib\aspnetmvc\System.Web.Razor.dll - - - False - ..\..\lib\aspnetmvc\System.Web.WebPages.dll - - - ..\..\lib\aspnetmvc\System.Web.WebPages.Razor.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - - Code - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - Code - - - Code - - - Code - - - - Code - - - - - - - - - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - Code - - - Code - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - - - Code - - - - - - - - - - - - Code - - - Code - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - Code - - - Code - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ASPXCodeBehind - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ASPXCodeBehind - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Library + Properties + Orchard + Orchard.Framework + v4.5.1 + 512 + + + 3.5 + + false + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + ..\OrchardBasicCorrectness.ruleset + false + false + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + false + 0436 + + + + False + ..\..\lib\autofac\Autofac.dll + True + + + False + ..\..\lib\autofac\Autofac.Configuration.dll + True + + + False + ..\..\lib\castle\net45\Castle.Core.dll + + + False + ..\..\lib\nhibernate\FluentNHibernate.dll + + + False + ..\..\lib\nhibernate\Iesi.Collections.dll + + + ..\..\lib\log4net\log4net.dll + + + + ..\..\lib\owin\Microsoft.Owin.dll + + + False + ..\..\lib\newtonsoft.json\Newtonsoft.Json.dll + + + ..\..\lib\nhibernate\NHibernate.dll + False + True + + + False + ..\..\lib\nhibernate.linq\NHibernate.Linq.dll + + + ..\..\lib\owin\Owin.dll + + + + 3.5 + + + + 3.5 + + + + True + + + 3.0 + + + + + + + + ..\..\lib\aspnetwebapi\System.Web.Http.dll + + + False + ..\..\lib\aspnetwebapi\System.Web.Http.WebHost.dll + + + False + ..\..\lib\aspnetmvc\System.Web.Mvc.dll + + + False + ..\..\lib\aspnetmvc\System.Web.Razor.dll + + + False + ..\..\lib\aspnetmvc\System.Web.WebPages.dll + + + ..\..\lib\aspnetmvc\System.Web.WebPages.Razor.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + Code + + + Code + + + Code + + + + Code + + + + + + + + + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + Code + + + + + + + + + + + + Code + + + Code + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ASPXCodeBehind + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ASPXCodeBehind + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/src/Orchard/Recipes/Models/Recipe.cs b/src/Orchard/Recipes/Models/Recipe.cs index 889ca3f0f..ac57c37b3 100644 --- a/src/Orchard/Recipes/Models/Recipe.cs +++ b/src/Orchard/Recipes/Models/Recipe.cs @@ -1,17 +1,17 @@ -using System; -using System.Collections.Generic; - -namespace Orchard.Recipes.Models { - public class Recipe { - public string Name { get; set; } - public string Description { get; set; } - public string Author { get; set; } - public string WebSite { get; set; } - public string Version { get; set; } - public bool IsSetupRecipe { get; set; } - public DateTime? ExportUtc { get; set; } - public string Category { get; set; } - public string Tags { get; set; } - public IEnumerable RecipeSteps { get; set; } - } -} +using System; +using System.Collections.Generic; + +namespace Orchard.Recipes.Models { + public class Recipe { + public string Name { get; set; } + public string Description { get; set; } + public string Author { get; set; } + public string WebSite { get; set; } + public string Version { get; set; } + public bool IsSetupRecipe { get; set; } + public DateTime? ExportUtc { get; set; } + public string Category { get; set; } + public string Tags { get; set; } + public IEnumerable RecipeSteps { get; set; } + } +} diff --git a/src/Orchard/Recipes/Models/RecipeStep.cs b/src/Orchard/Recipes/Models/RecipeStep.cs index 28994a300..952a8787d 100644 --- a/src/Orchard/Recipes/Models/RecipeStep.cs +++ b/src/Orchard/Recipes/Models/RecipeStep.cs @@ -1,17 +1,17 @@ -using System.Xml.Linq; - -namespace Orchard.Recipes.Models { - public class RecipeStep { - public RecipeStep(string id, string recipeName, string name, XElement step) { - Id = id; - RecipeName = recipeName; - Name = name; - Step = step; - } - - public string Id { get; set; } - public string RecipeName { get; private set; } - public string Name { get; private set; } - public XElement Step { get; private set; } - } +using System.Xml.Linq; + +namespace Orchard.Recipes.Models { + public class RecipeStep { + public RecipeStep(string id, string recipeName, string name, XElement step) { + Id = id; + RecipeName = recipeName; + Name = name; + Step = step; + } + + public string Id { get; set; } + public string RecipeName { get; private set; } + public string Name { get; private set; } + public XElement Step { get; private set; } + } } \ No newline at end of file diff --git a/src/Orchard/Recipes/Services/IRecipeHarvester.cs b/src/Orchard/Recipes/Services/IRecipeHarvester.cs index 3cea30e98..4a61a0b4a 100644 --- a/src/Orchard/Recipes/Services/IRecipeHarvester.cs +++ b/src/Orchard/Recipes/Services/IRecipeHarvester.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Orchard.Recipes.Models; - -namespace Orchard.Recipes.Services { - public interface IRecipeHarvester : IDependency { - /// - /// Returns a collection of all recipes. - /// - IEnumerable HarvestRecipes(); - - /// - /// Returns a collection of all recipes found in the specified extension. - /// - IEnumerable HarvestRecipes(string extensionId); - } - - public static class RecipeHarvesterExtensions { - public static Recipe GetRecipeByName(this IEnumerable recipes, string recipeName) { - return recipes.FirstOrDefault(r => r.Name.Equals(recipeName, StringComparison.OrdinalIgnoreCase)); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.Recipes.Models; + +namespace Orchard.Recipes.Services { + public interface IRecipeHarvester : IDependency { + /// + /// Returns a collection of all recipes. + /// + IEnumerable HarvestRecipes(); + + /// + /// Returns a collection of all recipes found in the specified extension. + /// + IEnumerable HarvestRecipes(string extensionId); + } + + public static class RecipeHarvesterExtensions { + public static Recipe GetRecipeByName(this IEnumerable recipes, string recipeName) { + return recipes.FirstOrDefault(r => r.Name.Equals(recipeName, StringComparison.OrdinalIgnoreCase)); + } + } +} diff --git a/src/Orchard/Recipes/Services/IRecipeParser.cs b/src/Orchard/Recipes/Services/IRecipeParser.cs index b804d73d0..22372c0b9 100644 --- a/src/Orchard/Recipes/Services/IRecipeParser.cs +++ b/src/Orchard/Recipes/Services/IRecipeParser.cs @@ -1,15 +1,15 @@ -using System.Xml.Linq; -using Orchard.Recipes.Models; - -namespace Orchard.Recipes.Services { - public interface IRecipeParser : IDependency { - Recipe ParseRecipe(XDocument recipeDocument); - } - - public static class RecipeParserExtensions { - public static Recipe ParseRecipe(this IRecipeParser recipeParser, string recipeText) { - var recipeDocument = XDocument.Parse(recipeText, LoadOptions.PreserveWhitespace); - return recipeParser.ParseRecipe(recipeDocument); - } - } -} +using System.Xml.Linq; +using Orchard.Recipes.Models; + +namespace Orchard.Recipes.Services { + public interface IRecipeParser : IDependency { + Recipe ParseRecipe(XDocument recipeDocument); + } + + public static class RecipeParserExtensions { + public static Recipe ParseRecipe(this IRecipeParser recipeParser, string recipeText) { + var recipeDocument = XDocument.Parse(recipeText, LoadOptions.PreserveWhitespace); + return recipeParser.ParseRecipe(recipeDocument); + } + } +} diff --git a/src/Orchard/Security/IMembershipService.cs b/src/Orchard/Security/IMembershipService.cs index b4a642665..c1de8042d 100644 --- a/src/Orchard/Security/IMembershipService.cs +++ b/src/Orchard/Security/IMembershipService.cs @@ -1,10 +1,10 @@ -namespace Orchard.Security { - public interface IMembershipService : IDependency { - MembershipSettings GetSettings(); - - IUser CreateUser(CreateUserParams createUserParams); - IUser GetUser(string username); - IUser ValidateUser(string userNameOrEmail, string password); - void SetPassword(IUser user, string password); - } -} +namespace Orchard.Security { + public interface IMembershipService : IDependency { + MembershipSettings GetSettings(); + + IUser CreateUser(CreateUserParams createUserParams); + IUser GetUser(string username); + IUser ValidateUser(string userNameOrEmail, string password); + void SetPassword(IUser user, string password); + } +} diff --git a/src/Orchard/Security/Providers/FormsAuthenticationService.cs b/src/Orchard/Security/Providers/FormsAuthenticationService.cs index 136b068b0..7a3d50164 100644 --- a/src/Orchard/Security/Providers/FormsAuthenticationService.cs +++ b/src/Orchard/Security/Providers/FormsAuthenticationService.cs @@ -1,180 +1,180 @@ -using System; -using System.Web; -using System.Web.Security; -using Orchard.Environment.Configuration; -using Orchard.Logging; -using Orchard.Mvc; -using Orchard.Mvc.Extensions; -using Orchard.Services; -using Orchard.Utility.Extensions; - -namespace Orchard.Security.Providers { - public class FormsAuthenticationService : IAuthenticationService { - private const int _cookieVersion = 3; - - private readonly ShellSettings _settings; - private readonly IClock _clock; - private readonly IMembershipService _membershipService; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ISslSettingsProvider _sslSettingsProvider; - private readonly IMembershipValidationService _membershipValidationService; - - private IUser _signedInUser; - private bool _isAuthenticated; - - // This fixes a performance issue when the forms authentication cookie is set to a - // user name not mapped to an actual Orchard user content item. If the request is - // authenticated but a null user is returned, multiple calls to GetAuthenticatedUser - // will cause multiple DB invocations, slowing down the request. We therefore - // remember if the current user is a non-Orchard user between invocations. - private bool _isNonOrchardUser; - - public FormsAuthenticationService( - ShellSettings settings, - IClock clock, - IMembershipService membershipService, - IHttpContextAccessor httpContextAccessor, - ISslSettingsProvider sslSettingsProvider, - IMembershipValidationService membershipValidationService) { - _settings = settings; - _clock = clock; - _membershipService = membershipService; - _httpContextAccessor = httpContextAccessor; - _sslSettingsProvider = sslSettingsProvider; - _membershipValidationService = membershipValidationService; - - Logger = NullLogger.Instance; - - ExpirationTimeSpan = TimeSpan.FromDays(30); - } - - public ILogger Logger { get; set; } - - public TimeSpan ExpirationTimeSpan { get; set; } - - public void SignIn(IUser user, bool createPersistentCookie) { - var now = _clock.UtcNow.ToLocalTime(); - - // The cookie user data is "{userName.Base64};{tenant}". - // The username is encoded to Base64 to prevent collisions with the ';' seprarator. - var userData = String.Concat(user.UserName.ToBase64(), ";", _settings.Name); - - var ticket = new FormsAuthenticationTicket( - _cookieVersion, - user.UserName, - now, - now.Add(ExpirationTimeSpan), - createPersistentCookie, - userData, - FormsAuthentication.FormsCookiePath); - - var encryptedTicket = FormsAuthentication.Encrypt(ticket); - - var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket) { - HttpOnly = true, - Secure = _sslSettingsProvider.GetRequiresSSL(), - Path = FormsAuthentication.FormsCookiePath - }; - - var httpContext = _httpContextAccessor.Current(); - - if (!String.IsNullOrEmpty(_settings.RequestUrlPrefix)) { - cookie.Path = GetCookiePath(httpContext); - } - - if (FormsAuthentication.CookieDomain != null) { - cookie.Domain = FormsAuthentication.CookieDomain; - } - - if (createPersistentCookie) { - cookie.Expires = ticket.Expiration; - } - - httpContext.Response.Cookies.Add(cookie); - - _isAuthenticated = true; - _signedInUser = user; - } - - public void SignOut() { - _signedInUser = null; - _isAuthenticated = false; - FormsAuthentication.SignOut(); - - // overwritting the authentication cookie for the given tenant - var httpContext = _httpContextAccessor.Current(); - var rFormsCookie = new HttpCookie(FormsAuthentication.FormsCookieName, "") { - Expires = DateTime.Now.AddYears(-1), - }; - - if (!String.IsNullOrEmpty(_settings.RequestUrlPrefix)) { - rFormsCookie.Path = GetCookiePath(httpContext); - } - - httpContext.Response.Cookies.Add(rFormsCookie); - } - - public void SetAuthenticatedUserForRequest(IUser user) { - _signedInUser = user; - _isAuthenticated = true; - } - - public IUser GetAuthenticatedUser() { - - if (_isNonOrchardUser) - return null; - - if (_signedInUser != null || _isAuthenticated) - return _signedInUser; - - var httpContext = _httpContextAccessor.Current(); - if (httpContext.IsBackgroundContext() || !httpContext.Request.IsAuthenticated || !(httpContext.User.Identity is FormsIdentity)) { - return null; - } - - var formsIdentity = (FormsIdentity)httpContext.User.Identity; - var userData = formsIdentity.Ticket.UserData ?? ""; - - // The cookie user data is {userName.Base64};{tenant}. - var userDataSegments = userData.Split(';'); - - if (userDataSegments.Length < 2) { - return null; - } - - var userDataName = userDataSegments[0]; - var userDataTenant = userDataSegments[1]; - - try { - userDataName = userDataName.FromBase64(); - } - catch { - return null; - } - - if (!String.Equals(userDataTenant, _settings.Name, StringComparison.Ordinal)) { - return null; - } - - _signedInUser = _membershipService.GetUser(userDataName); - if (_signedInUser == null || !_membershipValidationService.CanAuthenticateWithCookie(_signedInUser)) { - _isNonOrchardUser = true; - return null; - } - - _isAuthenticated = true; - return _signedInUser; - } - - private string GetCookiePath(HttpContextBase httpContext) { - var cookiePath = httpContext.Request.ApplicationPath; - if (cookiePath != null && cookiePath.Length > 1) { - cookiePath += '/'; - } - - cookiePath += _settings.RequestUrlPrefix; - - return cookiePath; - } - } -} +using System; +using System.Web; +using System.Web.Security; +using Orchard.Environment.Configuration; +using Orchard.Logging; +using Orchard.Mvc; +using Orchard.Mvc.Extensions; +using Orchard.Services; +using Orchard.Utility.Extensions; + +namespace Orchard.Security.Providers { + public class FormsAuthenticationService : IAuthenticationService { + private const int _cookieVersion = 3; + + private readonly ShellSettings _settings; + private readonly IClock _clock; + private readonly IMembershipService _membershipService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ISslSettingsProvider _sslSettingsProvider; + private readonly IMembershipValidationService _membershipValidationService; + + private IUser _signedInUser; + private bool _isAuthenticated; + + // This fixes a performance issue when the forms authentication cookie is set to a + // user name not mapped to an actual Orchard user content item. If the request is + // authenticated but a null user is returned, multiple calls to GetAuthenticatedUser + // will cause multiple DB invocations, slowing down the request. We therefore + // remember if the current user is a non-Orchard user between invocations. + private bool _isNonOrchardUser; + + public FormsAuthenticationService( + ShellSettings settings, + IClock clock, + IMembershipService membershipService, + IHttpContextAccessor httpContextAccessor, + ISslSettingsProvider sslSettingsProvider, + IMembershipValidationService membershipValidationService) { + _settings = settings; + _clock = clock; + _membershipService = membershipService; + _httpContextAccessor = httpContextAccessor; + _sslSettingsProvider = sslSettingsProvider; + _membershipValidationService = membershipValidationService; + + Logger = NullLogger.Instance; + + ExpirationTimeSpan = TimeSpan.FromDays(30); + } + + public ILogger Logger { get; set; } + + public TimeSpan ExpirationTimeSpan { get; set; } + + public void SignIn(IUser user, bool createPersistentCookie) { + var now = _clock.UtcNow.ToLocalTime(); + + // The cookie user data is "{userName.Base64};{tenant}". + // The username is encoded to Base64 to prevent collisions with the ';' seprarator. + var userData = String.Concat(user.UserName.ToBase64(), ";", _settings.Name); + + var ticket = new FormsAuthenticationTicket( + _cookieVersion, + user.UserName, + now, + now.Add(ExpirationTimeSpan), + createPersistentCookie, + userData, + FormsAuthentication.FormsCookiePath); + + var encryptedTicket = FormsAuthentication.Encrypt(ticket); + + var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket) { + HttpOnly = true, + Secure = _sslSettingsProvider.GetRequiresSSL(), + Path = FormsAuthentication.FormsCookiePath + }; + + var httpContext = _httpContextAccessor.Current(); + + if (!String.IsNullOrEmpty(_settings.RequestUrlPrefix)) { + cookie.Path = GetCookiePath(httpContext); + } + + if (FormsAuthentication.CookieDomain != null) { + cookie.Domain = FormsAuthentication.CookieDomain; + } + + if (createPersistentCookie) { + cookie.Expires = ticket.Expiration; + } + + httpContext.Response.Cookies.Add(cookie); + + _isAuthenticated = true; + _signedInUser = user; + } + + public void SignOut() { + _signedInUser = null; + _isAuthenticated = false; + FormsAuthentication.SignOut(); + + // overwritting the authentication cookie for the given tenant + var httpContext = _httpContextAccessor.Current(); + var rFormsCookie = new HttpCookie(FormsAuthentication.FormsCookieName, "") { + Expires = DateTime.Now.AddYears(-1), + }; + + if (!String.IsNullOrEmpty(_settings.RequestUrlPrefix)) { + rFormsCookie.Path = GetCookiePath(httpContext); + } + + httpContext.Response.Cookies.Add(rFormsCookie); + } + + public void SetAuthenticatedUserForRequest(IUser user) { + _signedInUser = user; + _isAuthenticated = true; + } + + public IUser GetAuthenticatedUser() { + + if (_isNonOrchardUser) + return null; + + if (_signedInUser != null || _isAuthenticated) + return _signedInUser; + + var httpContext = _httpContextAccessor.Current(); + if (httpContext.IsBackgroundContext() || !httpContext.Request.IsAuthenticated || !(httpContext.User.Identity is FormsIdentity)) { + return null; + } + + var formsIdentity = (FormsIdentity)httpContext.User.Identity; + var userData = formsIdentity.Ticket.UserData ?? ""; + + // The cookie user data is {userName.Base64};{tenant}. + var userDataSegments = userData.Split(';'); + + if (userDataSegments.Length < 2) { + return null; + } + + var userDataName = userDataSegments[0]; + var userDataTenant = userDataSegments[1]; + + try { + userDataName = userDataName.FromBase64(); + } + catch { + return null; + } + + if (!String.Equals(userDataTenant, _settings.Name, StringComparison.Ordinal)) { + return null; + } + + _signedInUser = _membershipService.GetUser(userDataName); + if (_signedInUser == null || !_membershipValidationService.CanAuthenticateWithCookie(_signedInUser)) { + _isNonOrchardUser = true; + return null; + } + + _isAuthenticated = true; + return _signedInUser; + } + + private string GetCookiePath(HttpContextBase httpContext) { + var cookiePath = httpContext.Request.ApplicationPath; + if (cookiePath != null && cookiePath.Length > 1) { + cookiePath += '/'; + } + + cookiePath += _settings.RequestUrlPrefix; + + return cookiePath; + } + } +} diff --git a/src/Orchard/Security/SecurityFilter.cs b/src/Orchard/Security/SecurityFilter.cs index fae4cad0c..c0ade6b65 100644 --- a/src/Orchard/Security/SecurityFilter.cs +++ b/src/Orchard/Security/SecurityFilter.cs @@ -1,46 +1,46 @@ -using System.Linq; -using System.Web.Mvc; -using Orchard.Logging; -using Orchard.Mvc.Filters; -using Orchard.UI.Admin; - -namespace Orchard.Security { - public class SecurityFilter : FilterProvider, IExceptionFilter, IAuthorizationFilter { - private readonly IAuthorizer _authorizer; - - public SecurityFilter(IAuthorizer authorizer) { - _authorizer = authorizer; - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - public void OnAuthorization(AuthorizationContext filterContext) { - - var accessFrontEnd = filterContext.ActionDescriptor.GetCustomAttributes(typeof (AlwaysAccessibleAttribute), true).Any(); - - if (!accessFrontEnd && filterContext.ActionDescriptor.ControllerDescriptor.ControllerType.GetCustomAttributes(typeof(AlwaysAccessibleAttribute), true).Any()) { - accessFrontEnd = true; - } - - if (!AdminFilter.IsApplied(filterContext.RequestContext) && !accessFrontEnd && !_authorizer.Authorize(StandardPermissions.AccessFrontEnd)) { - filterContext.Result = new HttpUnauthorizedResult(); - } - } - - public void OnException(ExceptionContext filterContext) { - if (!(filterContext.Exception is OrchardSecurityException)) - return; - - try { - Logger.Information(filterContext.Exception, "Security exception converted to access denied result"); - } - catch { - //a logger exception can't be allowed to interrupt this process - } - - filterContext.Result = new HttpUnauthorizedResult(); - filterContext.ExceptionHandled = true; - } - } -} +using System.Linq; +using System.Web.Mvc; +using Orchard.Logging; +using Orchard.Mvc.Filters; +using Orchard.UI.Admin; + +namespace Orchard.Security { + public class SecurityFilter : FilterProvider, IExceptionFilter, IAuthorizationFilter { + private readonly IAuthorizer _authorizer; + + public SecurityFilter(IAuthorizer authorizer) { + _authorizer = authorizer; + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public void OnAuthorization(AuthorizationContext filterContext) { + + var accessFrontEnd = filterContext.ActionDescriptor.GetCustomAttributes(typeof (AlwaysAccessibleAttribute), true).Any(); + + if (!accessFrontEnd && filterContext.ActionDescriptor.ControllerDescriptor.ControllerType.GetCustomAttributes(typeof(AlwaysAccessibleAttribute), true).Any()) { + accessFrontEnd = true; + } + + if (!AdminFilter.IsApplied(filterContext.RequestContext) && !accessFrontEnd && !_authorizer.Authorize(StandardPermissions.AccessFrontEnd)) { + filterContext.Result = new HttpUnauthorizedResult(); + } + } + + public void OnException(ExceptionContext filterContext) { + if (!(filterContext.Exception is OrchardSecurityException)) + return; + + try { + Logger.Information(filterContext.Exception, "Security exception converted to access denied result"); + } + catch { + //a logger exception can't be allowed to interrupt this process + } + + filterContext.Result = new HttpUnauthorizedResult(); + filterContext.ExceptionHandled = true; + } + } +} diff --git a/src/Orchard/Tasks/BackgroundService.cs b/src/Orchard/Tasks/BackgroundService.cs index 6093f917f..d9d80d804 100644 --- a/src/Orchard/Tasks/BackgroundService.cs +++ b/src/Orchard/Tasks/BackgroundService.cs @@ -1,54 +1,54 @@ -using System; -using System.Collections.Generic; -using Orchard.Data; -using Orchard.Environment.Configuration; -using Orchard.Logging; -using Orchard.Exceptions; - -namespace Orchard.Tasks { - - public interface IBackgroundService : IDependency { - void Sweep(); - } - - public class BackgroundService : IBackgroundService { - private readonly IEnumerable _tasks; - private readonly ITransactionManager _transactionManager; - private readonly string _shellName; - - public BackgroundService( - IEnumerable tasks, - ITransactionManager transactionManager, - ShellSettings shellSettings, - IBackgroundHttpContextFactory backgroundHttpContextFactory) { - - _tasks = tasks; - _transactionManager = transactionManager; - _shellName = shellSettings.Name; - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - public void Sweep() { - foreach (var task in _tasks) { - var taskName = task.GetType().FullName; - - try { - Logger.Information("Start processing background task \"{0}\" on tenant \"{1}\".", taskName, _shellName); - _transactionManager.RequireNew(); - task.Sweep(); - Logger.Information("Finished processing background task \"{0}\" on tenant \"{1}\".", taskName, _shellName); - } - catch (Exception ex) { - if (ex.IsFatal()) { - throw; - } - - _transactionManager.Cancel(); - Logger.Error(ex, "Error while processing background task \"{0}\" on tenant \"{1}\".", taskName, _shellName); - } - } - } - } -} +using System; +using System.Collections.Generic; +using Orchard.Data; +using Orchard.Environment.Configuration; +using Orchard.Logging; +using Orchard.Exceptions; + +namespace Orchard.Tasks { + + public interface IBackgroundService : IDependency { + void Sweep(); + } + + public class BackgroundService : IBackgroundService { + private readonly IEnumerable _tasks; + private readonly ITransactionManager _transactionManager; + private readonly string _shellName; + + public BackgroundService( + IEnumerable tasks, + ITransactionManager transactionManager, + ShellSettings shellSettings, + IBackgroundHttpContextFactory backgroundHttpContextFactory) { + + _tasks = tasks; + _transactionManager = transactionManager; + _shellName = shellSettings.Name; + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public void Sweep() { + foreach (var task in _tasks) { + var taskName = task.GetType().FullName; + + try { + Logger.Information("Start processing background task \"{0}\" on tenant \"{1}\".", taskName, _shellName); + _transactionManager.RequireNew(); + task.Sweep(); + Logger.Information("Finished processing background task \"{0}\" on tenant \"{1}\".", taskName, _shellName); + } + catch (Exception ex) { + if (ex.IsFatal()) { + throw; + } + + _transactionManager.Cancel(); + Logger.Error(ex, "Error while processing background task \"{0}\" on tenant \"{1}\".", taskName, _shellName); + } + } + } + } +} diff --git a/src/Orchard/UI/Notify/NotifyFilter.cs b/src/Orchard/UI/Notify/NotifyFilter.cs index 7961369c7..4c64e59df 100644 --- a/src/Orchard/UI/Notify/NotifyFilter.cs +++ b/src/Orchard/UI/Notify/NotifyFilter.cs @@ -1,116 +1,116 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Mvc; -using Orchard.DisplayManagement; -using Orchard.Localization; -using Orchard.Mvc.Filters; - -namespace Orchard.UI.Notify { - public class NotifyFilter : FilterProvider, IActionFilter, IResultFilter { - public const string TempDataMessages = "Messages"; - private readonly INotifier _notifier; - private readonly IWorkContextAccessor _workContextAccessor; - private readonly dynamic _shapeFactory; - - public NotifyFilter( - INotifier notifier, - IWorkContextAccessor workContextAccessor, - IShapeFactory shapeFactory) { - _notifier = notifier; - _workContextAccessor = workContextAccessor; - _shapeFactory = shapeFactory; - } - - public void OnActionExecuting(ActionExecutingContext filterContext) { - var messages = Convert.ToString(filterContext.Controller.TempData[TempDataMessages]); - if (String.IsNullOrEmpty(messages)) - return; - - var messageEntries = new List(); - foreach (var line in messages.Split(new[] { System.Environment.NewLine + "-" + System.Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) { - var delimiterIndex = line.IndexOf(':'); - if (delimiterIndex != -1) { - var type = (NotifyType)Enum.Parse(typeof(NotifyType), line.Substring(0, delimiterIndex)); - var message = new LocalizedString(line.Substring(delimiterIndex + 1)); - if (!messageEntries.Any(ne => ne.Message.TextHint == message.TextHint)) { - messageEntries.Add(new NotifyEntry { - Type = type, - Message = message - }); - } - } - else { - var message = new LocalizedString(line.Substring(delimiterIndex + 1)); - if (!messageEntries.Any(ne => ne.Message.TextHint == message.TextHint)) { - messageEntries.Add(new NotifyEntry { - Type = NotifyType.Information, - Message = message - }); - } - } - } - - if (!messageEntries.Any()) - return; - - // Make the notifications available for the rest of the current request. - filterContext.HttpContext.Items[TempDataMessages] = messageEntries; - } - - public void OnActionExecuted(ActionExecutedContext filterContext) { - - // Don't touch temp data if there's no work to perform. - if (!_notifier.List().Any()) - return; - - var messageEntries = _notifier.List().ToList(); - - if (filterContext.Result is ViewResultBase) { - // Assign values to the Items collection instead of TempData and - // combine any existing entries added by the previous request with new ones. - var existingEntries = filterContext.HttpContext.Items[TempDataMessages] as IList ?? new List(); - messageEntries = messageEntries.Concat(existingEntries).ToList(); - filterContext.HttpContext.Items[TempDataMessages] = messageEntries; - - return; - } - - var tempData = filterContext.Controller.TempData; - - // Initialize writer with current data. - var sb = new StringBuilder(); - if (tempData.ContainsKey(TempDataMessages)) { - sb.Append(tempData[TempDataMessages]); - } - - // Accumulate messages, one line per message. - foreach (var entry in messageEntries) { - sb.Append(Convert.ToString(entry.Type)) - .Append(':') - .AppendLine(entry.Message.ToString()) - .AppendLine("-"); - } - - // Result is not a view, so assume a redirect and assign values to TemData. - // String data type used instead of complex array to be session-friendly. - tempData[TempDataMessages] = sb.ToString(); - } - - public void OnResultExecuting(ResultExecutingContext filterContext) { - if (!(filterContext.Result is ViewResultBase)) - return; - - var messageEntries = filterContext.HttpContext.Items[TempDataMessages] as IList ?? new List(); - var messagesZone = _workContextAccessor.GetContext(filterContext).Layout.Zones["Messages"]; - foreach (var messageEntry in messageEntries) - messagesZone = messagesZone.Add(_shapeFactory.Message(messageEntry)); - - //todo: (heskew) probably need to keep duplicate messages from being pushed into the zone like the previous behavior - //baseViewModel.Messages = baseViewModel.Messages == null ? messageEntries .Messages.Union(messageEntries).ToList(); - //baseViewModel.Zones.AddRenderPartial("content:before", "Messages", baseViewModel.Messages); - } - public void OnResultExecuted(ResultExecutedContext filterContext) {} - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web.Mvc; +using Orchard.DisplayManagement; +using Orchard.Localization; +using Orchard.Mvc.Filters; + +namespace Orchard.UI.Notify { + public class NotifyFilter : FilterProvider, IActionFilter, IResultFilter { + public const string TempDataMessages = "Messages"; + private readonly INotifier _notifier; + private readonly IWorkContextAccessor _workContextAccessor; + private readonly dynamic _shapeFactory; + + public NotifyFilter( + INotifier notifier, + IWorkContextAccessor workContextAccessor, + IShapeFactory shapeFactory) { + _notifier = notifier; + _workContextAccessor = workContextAccessor; + _shapeFactory = shapeFactory; + } + + public void OnActionExecuting(ActionExecutingContext filterContext) { + var messages = Convert.ToString(filterContext.Controller.TempData[TempDataMessages]); + if (String.IsNullOrEmpty(messages)) + return; + + var messageEntries = new List(); + foreach (var line in messages.Split(new[] { System.Environment.NewLine + "-" + System.Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) { + var delimiterIndex = line.IndexOf(':'); + if (delimiterIndex != -1) { + var type = (NotifyType)Enum.Parse(typeof(NotifyType), line.Substring(0, delimiterIndex)); + var message = new LocalizedString(line.Substring(delimiterIndex + 1)); + if (!messageEntries.Any(ne => ne.Message.TextHint == message.TextHint)) { + messageEntries.Add(new NotifyEntry { + Type = type, + Message = message + }); + } + } + else { + var message = new LocalizedString(line.Substring(delimiterIndex + 1)); + if (!messageEntries.Any(ne => ne.Message.TextHint == message.TextHint)) { + messageEntries.Add(new NotifyEntry { + Type = NotifyType.Information, + Message = message + }); + } + } + } + + if (!messageEntries.Any()) + return; + + // Make the notifications available for the rest of the current request. + filterContext.HttpContext.Items[TempDataMessages] = messageEntries; + } + + public void OnActionExecuted(ActionExecutedContext filterContext) { + + // Don't touch temp data if there's no work to perform. + if (!_notifier.List().Any()) + return; + + var messageEntries = _notifier.List().ToList(); + + if (filterContext.Result is ViewResultBase) { + // Assign values to the Items collection instead of TempData and + // combine any existing entries added by the previous request with new ones. + var existingEntries = filterContext.HttpContext.Items[TempDataMessages] as IList ?? new List(); + messageEntries = messageEntries.Concat(existingEntries).ToList(); + filterContext.HttpContext.Items[TempDataMessages] = messageEntries; + + return; + } + + var tempData = filterContext.Controller.TempData; + + // Initialize writer with current data. + var sb = new StringBuilder(); + if (tempData.ContainsKey(TempDataMessages)) { + sb.Append(tempData[TempDataMessages]); + } + + // Accumulate messages, one line per message. + foreach (var entry in messageEntries) { + sb.Append(Convert.ToString(entry.Type)) + .Append(':') + .AppendLine(entry.Message.ToString()) + .AppendLine("-"); + } + + // Result is not a view, so assume a redirect and assign values to TemData. + // String data type used instead of complex array to be session-friendly. + tempData[TempDataMessages] = sb.ToString(); + } + + public void OnResultExecuting(ResultExecutingContext filterContext) { + if (!(filterContext.Result is ViewResultBase)) + return; + + var messageEntries = filterContext.HttpContext.Items[TempDataMessages] as IList ?? new List(); + var messagesZone = _workContextAccessor.GetContext(filterContext).Layout.Zones["Messages"]; + foreach (var messageEntry in messageEntries) + messagesZone = messagesZone.Add(_shapeFactory.Message(messageEntry)); + + //todo: (heskew) probably need to keep duplicate messages from being pushed into the zone like the previous behavior + //baseViewModel.Messages = baseViewModel.Messages == null ? messageEntries .Messages.Union(messageEntries).ToList(); + //baseViewModel.Zones.AddRenderPartial("content:before", "Messages", baseViewModel.Messages); + } + public void OnResultExecuted(ResultExecutedContext filterContext) {} + } } \ No newline at end of file diff --git a/src/Orchard/UI/Resources/MetaEntry.cs b/src/Orchard/UI/Resources/MetaEntry.cs index 639bb39b2..faa785124 100644 --- a/src/Orchard/UI/Resources/MetaEntry.cs +++ b/src/Orchard/UI/Resources/MetaEntry.cs @@ -1,98 +1,98 @@ -using System; -using System.Collections.Generic; -using System.Web.Mvc; - -namespace Orchard.UI.Resources { - public class MetaEntry { - private readonly TagBuilder _builder = new TagBuilder("meta"); - - public MetaEntry() { - - } - - public MetaEntry(string name = null, string content = null, string httpEquiv = null, string charset = null) { - if (!String.IsNullOrEmpty(name)) { - Name = name; - } - - if (!String.IsNullOrEmpty(content)) { - Content = content; - } - - if (!String.IsNullOrEmpty(httpEquiv)) { - HttpEquiv = httpEquiv; - } - - if (!String.IsNullOrEmpty(charset)) { - Charset = charset; - } - - } - - public static MetaEntry Combine(MetaEntry meta1, MetaEntry meta2, string contentSeparator) { - var newMeta = new MetaEntry(); - Merge(newMeta._builder.Attributes, meta1._builder.Attributes, meta2._builder.Attributes); - if (!String.IsNullOrEmpty(meta1.Content) && !String.IsNullOrEmpty(meta2.Content)) { - newMeta.Content = meta1.Content + contentSeparator + meta2.Content; - } - return newMeta; - } - - private static void Merge(IDictionary d1, params IDictionary[] sources) { - foreach(var d in sources) { - foreach (var pair in d) { - d1[pair.Key] = pair.Value; - } - } - } - - public MetaEntry AddAttribute(string name, string value) { - _builder.MergeAttribute(name, value); - return this; - } - public MetaEntry SetAttribute(string name, string value) { - _builder.MergeAttribute(name, value, true); - return this; - } - - public string Name { - get { - string value; - _builder.Attributes.TryGetValue("name", out value); - return value; - } - set { SetAttribute("name", value); } - } - - public string Content { - get { - string value; - _builder.Attributes.TryGetValue("content", out value); - return value; - } - set { SetAttribute("content", value); } - } - - public string HttpEquiv { - get { - string value; - _builder.Attributes.TryGetValue("http-equiv", out value); - return value; - } - set { SetAttribute("http-equiv", value); } - } - - public string Charset { - get { - string value; - _builder.Attributes.TryGetValue("charset", out value); - return value; - } - set { SetAttribute("charset", value); } - } - - public string GetTag() { - return _builder.ToString(TagRenderMode.SelfClosing); - } - } -} +using System; +using System.Collections.Generic; +using System.Web.Mvc; + +namespace Orchard.UI.Resources { + public class MetaEntry { + private readonly TagBuilder _builder = new TagBuilder("meta"); + + public MetaEntry() { + + } + + public MetaEntry(string name = null, string content = null, string httpEquiv = null, string charset = null) { + if (!String.IsNullOrEmpty(name)) { + Name = name; + } + + if (!String.IsNullOrEmpty(content)) { + Content = content; + } + + if (!String.IsNullOrEmpty(httpEquiv)) { + HttpEquiv = httpEquiv; + } + + if (!String.IsNullOrEmpty(charset)) { + Charset = charset; + } + + } + + public static MetaEntry Combine(MetaEntry meta1, MetaEntry meta2, string contentSeparator) { + var newMeta = new MetaEntry(); + Merge(newMeta._builder.Attributes, meta1._builder.Attributes, meta2._builder.Attributes); + if (!String.IsNullOrEmpty(meta1.Content) && !String.IsNullOrEmpty(meta2.Content)) { + newMeta.Content = meta1.Content + contentSeparator + meta2.Content; + } + return newMeta; + } + + private static void Merge(IDictionary d1, params IDictionary[] sources) { + foreach(var d in sources) { + foreach (var pair in d) { + d1[pair.Key] = pair.Value; + } + } + } + + public MetaEntry AddAttribute(string name, string value) { + _builder.MergeAttribute(name, value); + return this; + } + public MetaEntry SetAttribute(string name, string value) { + _builder.MergeAttribute(name, value, true); + return this; + } + + public string Name { + get { + string value; + _builder.Attributes.TryGetValue("name", out value); + return value; + } + set { SetAttribute("name", value); } + } + + public string Content { + get { + string value; + _builder.Attributes.TryGetValue("content", out value); + return value; + } + set { SetAttribute("content", value); } + } + + public string HttpEquiv { + get { + string value; + _builder.Attributes.TryGetValue("http-equiv", out value); + return value; + } + set { SetAttribute("http-equiv", value); } + } + + public string Charset { + get { + string value; + _builder.Attributes.TryGetValue("charset", out value); + return value; + } + set { SetAttribute("charset", value); } + } + + public string GetTag() { + return _builder.ToString(TagRenderMode.SelfClosing); + } + } +} diff --git a/src/Orchard/Utility/Extensions/StringExtensions.cs b/src/Orchard/Utility/Extensions/StringExtensions.cs index 6e9fef83f..e8bde90b4 100644 --- a/src/Orchard/Utility/Extensions/StringExtensions.cs +++ b/src/Orchard/Utility/Extensions/StringExtensions.cs @@ -1,359 +1,359 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Orchard.Localization; -using System.Web; - -namespace Orchard.Utility.Extensions { - public static class StringExtensions { - public static string CamelFriendly(this string camel) { - if (String.IsNullOrWhiteSpace(camel)) - return ""; - - var sb = new StringBuilder(camel); - - for (int i = camel.Length-1; i>0; i--) { - if(char.IsUpper(sb[i])) { - sb.Insert(i, ' '); - } - } - - return sb.ToString(); - } - - public static string Ellipsize(this string text, int characterCount) { - return text.Ellipsize(characterCount, "\u00A0\u2026"); - } - - public static string Ellipsize(this string text, int characterCount, string ellipsis, bool wordBoundary = false) { - if (String.IsNullOrWhiteSpace(text)) - return ""; - - if (characterCount < 0 || text.Length <= characterCount) - return text; - - // search beginning of word - var backup = characterCount; - while (characterCount > 0 && text[characterCount-1].IsLetter()) { - characterCount--; - } - - // search previous word - while (characterCount > 0 && text[characterCount - 1].IsSpace()) { - characterCount--; - } - - // if it was the last word, recover it, unless boundary is requested - if(characterCount == 0 && !wordBoundary) { - characterCount = backup; - } - - var trimmed = text.Substring(0, characterCount); - return trimmed + ellipsis; - } - - public static string HtmlClassify(this string text) { - if (String.IsNullOrWhiteSpace(text)) - return ""; - - var friendlier = text.CamelFriendly(); - - var result = new char[friendlier.Length]; - - var cursor = 0; - var previousIsNotLetter = false; - for (var i = 0; i < friendlier.Length; i++) { - char current = friendlier[i]; - if (IsLetter(current) || (Char.IsDigit(current) && cursor > 0)) { - if (previousIsNotLetter && i != 0 && cursor > 0) { - result[cursor++] = '-'; - } - - result[cursor++] = Char.ToLowerInvariant(current); - previousIsNotLetter = false; - } - else { - previousIsNotLetter = true; - } - } - - return new string(result, 0, cursor); - } - - public static LocalizedString OrDefault(this string text, LocalizedString defaultValue) { - return String.IsNullOrEmpty(text) - ? defaultValue - : new LocalizedString(text); - } - - public static string RemoveTags(this string html, bool htmlDecode = false) { - if (String.IsNullOrEmpty(html)) { - return String.Empty; - } - - var result = new char[html.Length]; - - var cursor = 0; - var inside = false; - for (var i = 0; i < html.Length; i++) { - char current = html[i]; - - switch(current) { - case '<': - inside = true; - continue; - case '>': - inside = false; - continue; - } - - if (!inside) { - result[cursor++] = current; - } - } - - var stringResult = new string(result, 0, cursor); - - if (htmlDecode) { - stringResult = HttpUtility.HtmlDecode(stringResult); - } - - return stringResult; - } - - // not accounting for only \r (e.g. Apple OS 9 carriage return only new lines) - public static string ReplaceNewLinesWith(this string text, string replacement) { - return String.IsNullOrWhiteSpace(text) - ? String.Empty - : text - .Replace("\r\n", "\r\r") - .Replace("\n", String.Format(replacement, "\r\n")) - .Replace("\r\r", String.Format(replacement, "\r\n")); - } - - public static string ToHexString(this byte[] bytes) { - return BitConverter.ToString(bytes).Replace("-", ""); - } - - public static byte[] ToByteArray(this string hex) { - return Enumerable.Range(0, hex.Length). - Where(x => 0 == x % 2). - Select(x => Convert.ToByte(hex.Substring(x, 2), 16)). - ToArray(); - } - - private static readonly char[] validSegmentChars = "/?#[]@\"^{}|`<>\t\r\n\f ".ToCharArray(); - public static bool IsValidUrlSegment(this string segment) { - // valid isegment from rfc3987 - http://tools.ietf.org/html/rfc3987#page-8 - // the relevant bits: - // isegment = *ipchar - // ipchar = iunreserved / pct-encoded / sub-delims / ":" / "@" - // iunreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" / ucschar - // pct-encoded = "%" HEXDIG HEXDIG - // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" - // ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD / %xD0000-DFFFD / %xE1000-EFFFD - // - // rough blacklist regex == m/^[^/?#[]@"^{}|\s`<>]+$/ (leaving off % to keep the regex simple) - - return !segment.Any(validSegmentChars); - } - - /// - /// Generates a valid technical name. - /// - /// - /// Uses a white list set of chars. - /// - public static string ToSafeName(this string name) { - if (String.IsNullOrWhiteSpace(name)) - return String.Empty; - - name = RemoveDiacritics(name); - name = name.Strip(c => - !c.IsLetter() - && !Char.IsDigit(c) - ); - - name = name.Trim(); - - // don't allow non A-Z chars as first letter, as they are not allowed in prefixes - while (name.Length > 0 && !IsLetter(name[0])) { - name = name.Substring(1); - } - - if (name.Length > 128) - name = name.Substring(0, 128); - - return name; - } - - /// - /// Generates a valid Html name. - /// - /// - /// Uses a white list set of chars. - /// - public static string ToHtmlName(this string name) { - if (String.IsNullOrWhiteSpace(name)) - return String.Empty; - - name = RemoveDiacritics(name); - name = name.Strip(c => - c != '-' - && c != '_' - && !c.IsLetter() - && !Char.IsDigit(c) - ); - - name = name.Trim(); - - // don't allow non A-Z chars as first letter, as they are not allowed in prefixes - while (name.Length > 0 && !IsLetter(name[0])) { - name = name.Substring(1); - } - - return name; - } - - /// - /// Whether the char is a letter between A and Z or not - /// - public static bool IsLetter(this char c) { - return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); - } - - public static bool IsSpace(this char c) { - return (c == '\r' || c == '\n' || c == '\t' || c == '\f' || c == ' '); - } - - public static string RemoveDiacritics(this string name) { - string stFormD = name.Normalize(NormalizationForm.FormD); - var sb = new StringBuilder(); - - foreach (char t in stFormD) { - UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(t); - if (uc != UnicodeCategory.NonSpacingMark) { - sb.Append(t); - } - } - - return (sb.ToString().Normalize(NormalizationForm.FormC)); - } - - public static string Strip(this string subject, params char[] stripped) { - if(stripped == null || stripped.Length == 0 || String.IsNullOrEmpty(subject)) { - return subject; - } - - var result = new char[subject.Length]; - - var cursor = 0; - for (var i = 0; i < subject.Length; i++) { - char current = subject[i]; - if (Array.IndexOf(stripped, current) < 0) { - result[cursor++] = current; - } - } - - return new string(result, 0, cursor); - } - - public static string Strip(this string subject, Func predicate) { - - var result = new char[subject.Length]; - - var cursor = 0; - for (var i = 0; i < subject.Length; i++) { - char current = subject[i]; - if (!predicate(current)) { - result[cursor++] = current; - } - } - - return new string(result, 0, cursor); - } - - public static bool Any(this string subject, params char[] chars) { - if (string.IsNullOrEmpty(subject) || chars == null || chars.Length == 0) { - return false; - } - - for (var i = 0; i < subject.Length; i++) { - char current = subject[i]; - if (Array.IndexOf(chars, current) >= 0) { - return true; - } - } - - return false; - } - - public static bool All(this string subject, params char[] chars) { - if (string.IsNullOrEmpty(subject)) { - return true; - } - - if(chars == null || chars.Length == 0) { - return false; - } - - for (var i = 0; i < subject.Length; i++) { - char current = subject[i]; - if (Array.IndexOf(chars, current) < 0) { - return false; - } - } - - return true; - } - - public static string Translate(this string subject, char[] from, char[] to) { - if (string.IsNullOrEmpty(subject)) { - return subject; - } - - if (from == null || to == null) { - throw new ArgumentNullException(); - } - - if (from.Length != to.Length) { - throw new ArgumentNullException("from", "Parameters must have the same length"); - } - - var map = new Dictionary(from.Length); - for (var i = 0; i < from.Length; i++) { - map[from[i]] = to[i]; - } - - var result = new char[subject.Length]; - - for (var i = 0; i < subject.Length; i++) { - var current = subject[i]; - if (map.ContainsKey(current)) { - result[i] = map[current]; - } - else { - result[i] = current; - } - } - - return new string(result); - } - - public static string ReplaceAll(this string original, IDictionary replacements) { - var pattern = String.Format("{0}", String.Join("|", replacements.Keys)); - return Regex.Replace(original, pattern, match => replacements[match.Value]); - } - - public static string ToBase64(this string value) { - return Convert.ToBase64String(Encoding.UTF8.GetBytes(value)); - } - - public static string FromBase64(this string value) { - return Encoding.UTF8.GetString(Convert.FromBase64String(value)); - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Orchard.Localization; +using System.Web; + +namespace Orchard.Utility.Extensions { + public static class StringExtensions { + public static string CamelFriendly(this string camel) { + if (String.IsNullOrWhiteSpace(camel)) + return ""; + + var sb = new StringBuilder(camel); + + for (int i = camel.Length-1; i>0; i--) { + if(char.IsUpper(sb[i])) { + sb.Insert(i, ' '); + } + } + + return sb.ToString(); + } + + public static string Ellipsize(this string text, int characterCount) { + return text.Ellipsize(characterCount, "\u00A0\u2026"); + } + + public static string Ellipsize(this string text, int characterCount, string ellipsis, bool wordBoundary = false) { + if (String.IsNullOrWhiteSpace(text)) + return ""; + + if (characterCount < 0 || text.Length <= characterCount) + return text; + + // search beginning of word + var backup = characterCount; + while (characterCount > 0 && text[characterCount-1].IsLetter()) { + characterCount--; + } + + // search previous word + while (characterCount > 0 && text[characterCount - 1].IsSpace()) { + characterCount--; + } + + // if it was the last word, recover it, unless boundary is requested + if(characterCount == 0 && !wordBoundary) { + characterCount = backup; + } + + var trimmed = text.Substring(0, characterCount); + return trimmed + ellipsis; + } + + public static string HtmlClassify(this string text) { + if (String.IsNullOrWhiteSpace(text)) + return ""; + + var friendlier = text.CamelFriendly(); + + var result = new char[friendlier.Length]; + + var cursor = 0; + var previousIsNotLetter = false; + for (var i = 0; i < friendlier.Length; i++) { + char current = friendlier[i]; + if (IsLetter(current) || (Char.IsDigit(current) && cursor > 0)) { + if (previousIsNotLetter && i != 0 && cursor > 0) { + result[cursor++] = '-'; + } + + result[cursor++] = Char.ToLowerInvariant(current); + previousIsNotLetter = false; + } + else { + previousIsNotLetter = true; + } + } + + return new string(result, 0, cursor); + } + + public static LocalizedString OrDefault(this string text, LocalizedString defaultValue) { + return String.IsNullOrEmpty(text) + ? defaultValue + : new LocalizedString(text); + } + + public static string RemoveTags(this string html, bool htmlDecode = false) { + if (String.IsNullOrEmpty(html)) { + return String.Empty; + } + + var result = new char[html.Length]; + + var cursor = 0; + var inside = false; + for (var i = 0; i < html.Length; i++) { + char current = html[i]; + + switch(current) { + case '<': + inside = true; + continue; + case '>': + inside = false; + continue; + } + + if (!inside) { + result[cursor++] = current; + } + } + + var stringResult = new string(result, 0, cursor); + + if (htmlDecode) { + stringResult = HttpUtility.HtmlDecode(stringResult); + } + + return stringResult; + } + + // not accounting for only \r (e.g. Apple OS 9 carriage return only new lines) + public static string ReplaceNewLinesWith(this string text, string replacement) { + return String.IsNullOrWhiteSpace(text) + ? String.Empty + : text + .Replace("\r\n", "\r\r") + .Replace("\n", String.Format(replacement, "\r\n")) + .Replace("\r\r", String.Format(replacement, "\r\n")); + } + + public static string ToHexString(this byte[] bytes) { + return BitConverter.ToString(bytes).Replace("-", ""); + } + + public static byte[] ToByteArray(this string hex) { + return Enumerable.Range(0, hex.Length). + Where(x => 0 == x % 2). + Select(x => Convert.ToByte(hex.Substring(x, 2), 16)). + ToArray(); + } + + private static readonly char[] validSegmentChars = "/?#[]@\"^{}|`<>\t\r\n\f ".ToCharArray(); + public static bool IsValidUrlSegment(this string segment) { + // valid isegment from rfc3987 - http://tools.ietf.org/html/rfc3987#page-8 + // the relevant bits: + // isegment = *ipchar + // ipchar = iunreserved / pct-encoded / sub-delims / ":" / "@" + // iunreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" / ucschar + // pct-encoded = "%" HEXDIG HEXDIG + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + // ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD / %xD0000-DFFFD / %xE1000-EFFFD + // + // rough blacklist regex == m/^[^/?#[]@"^{}|\s`<>]+$/ (leaving off % to keep the regex simple) + + return !segment.Any(validSegmentChars); + } + + /// + /// Generates a valid technical name. + /// + /// + /// Uses a white list set of chars. + /// + public static string ToSafeName(this string name) { + if (String.IsNullOrWhiteSpace(name)) + return String.Empty; + + name = RemoveDiacritics(name); + name = name.Strip(c => + !c.IsLetter() + && !Char.IsDigit(c) + ); + + name = name.Trim(); + + // don't allow non A-Z chars as first letter, as they are not allowed in prefixes + while (name.Length > 0 && !IsLetter(name[0])) { + name = name.Substring(1); + } + + if (name.Length > 128) + name = name.Substring(0, 128); + + return name; + } + + /// + /// Generates a valid Html name. + /// + /// + /// Uses a white list set of chars. + /// + public static string ToHtmlName(this string name) { + if (String.IsNullOrWhiteSpace(name)) + return String.Empty; + + name = RemoveDiacritics(name); + name = name.Strip(c => + c != '-' + && c != '_' + && !c.IsLetter() + && !Char.IsDigit(c) + ); + + name = name.Trim(); + + // don't allow non A-Z chars as first letter, as they are not allowed in prefixes + while (name.Length > 0 && !IsLetter(name[0])) { + name = name.Substring(1); + } + + return name; + } + + /// + /// Whether the char is a letter between A and Z or not + /// + public static bool IsLetter(this char c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); + } + + public static bool IsSpace(this char c) { + return (c == '\r' || c == '\n' || c == '\t' || c == '\f' || c == ' '); + } + + public static string RemoveDiacritics(this string name) { + string stFormD = name.Normalize(NormalizationForm.FormD); + var sb = new StringBuilder(); + + foreach (char t in stFormD) { + UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(t); + if (uc != UnicodeCategory.NonSpacingMark) { + sb.Append(t); + } + } + + return (sb.ToString().Normalize(NormalizationForm.FormC)); + } + + public static string Strip(this string subject, params char[] stripped) { + if(stripped == null || stripped.Length == 0 || String.IsNullOrEmpty(subject)) { + return subject; + } + + var result = new char[subject.Length]; + + var cursor = 0; + for (var i = 0; i < subject.Length; i++) { + char current = subject[i]; + if (Array.IndexOf(stripped, current) < 0) { + result[cursor++] = current; + } + } + + return new string(result, 0, cursor); + } + + public static string Strip(this string subject, Func predicate) { + + var result = new char[subject.Length]; + + var cursor = 0; + for (var i = 0; i < subject.Length; i++) { + char current = subject[i]; + if (!predicate(current)) { + result[cursor++] = current; + } + } + + return new string(result, 0, cursor); + } + + public static bool Any(this string subject, params char[] chars) { + if (string.IsNullOrEmpty(subject) || chars == null || chars.Length == 0) { + return false; + } + + for (var i = 0; i < subject.Length; i++) { + char current = subject[i]; + if (Array.IndexOf(chars, current) >= 0) { + return true; + } + } + + return false; + } + + public static bool All(this string subject, params char[] chars) { + if (string.IsNullOrEmpty(subject)) { + return true; + } + + if(chars == null || chars.Length == 0) { + return false; + } + + for (var i = 0; i < subject.Length; i++) { + char current = subject[i]; + if (Array.IndexOf(chars, current) < 0) { + return false; + } + } + + return true; + } + + public static string Translate(this string subject, char[] from, char[] to) { + if (string.IsNullOrEmpty(subject)) { + return subject; + } + + if (from == null || to == null) { + throw new ArgumentNullException(); + } + + if (from.Length != to.Length) { + throw new ArgumentNullException("from", "Parameters must have the same length"); + } + + var map = new Dictionary(from.Length); + for (var i = 0; i < from.Length; i++) { + map[from[i]] = to[i]; + } + + var result = new char[subject.Length]; + + for (var i = 0; i < subject.Length; i++) { + var current = subject[i]; + if (map.ContainsKey(current)) { + result[i] = map[current]; + } + else { + result[i] = current; + } + } + + return new string(result); + } + + public static string ReplaceAll(this string original, IDictionary replacements) { + var pattern = String.Format("{0}", String.Join("|", replacements.Keys)); + return Regex.Replace(original, pattern, match => replacements[match.Value]); + } + + public static string ToBase64(this string value) { + return Convert.ToBase64String(Encoding.UTF8.GetBytes(value)); + } + + public static string FromBase64(this string value) { + return Encoding.UTF8.GetString(Convert.FromBase64String(value)); + } + } +} diff --git a/src/Orchard/Validation/Argument.cs b/src/Orchard/Validation/Argument.cs index 7ddce7923..3672b53b7 100644 --- a/src/Orchard/Validation/Argument.cs +++ b/src/Orchard/Validation/Argument.cs @@ -1,41 +1,41 @@ -using System; - -namespace Orchard.Validation { - public class Argument { - public static void Validate(bool condition, string name) { - if (!condition) { - throw new ArgumentException("Invalid argument", name); - } - } - - public static void Validate(bool condition, string name, string message) { - if (!condition) { - throw new ArgumentException(message, name); - } - } - - public static void ThrowIfNull(T value, string name) where T : class { - if (value == null) { - throw new ArgumentNullException(name); - } - } - - public static void ThrowIfNull(T value, string name, string message) where T : class { - if (value == null) { - throw new ArgumentNullException(name, message); - } - } - - public static void ThrowIfNullOrEmpty(string value, string name) { - if (string.IsNullOrEmpty(value)) { - throw new ArgumentException("Argument must be a non empty string", name); - } - } - - public static void ThrowIfNullOrEmpty(string value, string name, string message) { - if (string.IsNullOrEmpty(value)) { - throw new ArgumentException(message, name); - } - } - } -} +using System; + +namespace Orchard.Validation { + public class Argument { + public static void Validate(bool condition, string name) { + if (!condition) { + throw new ArgumentException("Invalid argument", name); + } + } + + public static void Validate(bool condition, string name, string message) { + if (!condition) { + throw new ArgumentException(message, name); + } + } + + public static void ThrowIfNull(T value, string name) where T : class { + if (value == null) { + throw new ArgumentNullException(name); + } + } + + public static void ThrowIfNull(T value, string name, string message) where T : class { + if (value == null) { + throw new ArgumentNullException(name, message); + } + } + + public static void ThrowIfNullOrEmpty(string value, string name) { + if (string.IsNullOrEmpty(value)) { + throw new ArgumentException("Argument must be a non empty string", name); + } + } + + public static void ThrowIfNullOrEmpty(string value, string name, string message) { + if (string.IsNullOrEmpty(value)) { + throw new ArgumentException(message, name); + } + } + } +} diff --git a/src/Orchard/WebApi/DefaultOrchardWebApiHttpHttpControllerActivator.cs b/src/Orchard/WebApi/DefaultOrchardWebApiHttpHttpControllerActivator.cs index a2bbd0e15..88a09899c 100644 --- a/src/Orchard/WebApi/DefaultOrchardWebApiHttpHttpControllerActivator.cs +++ b/src/Orchard/WebApi/DefaultOrchardWebApiHttpHttpControllerActivator.cs @@ -1,69 +1,69 @@ -using System; -using System.Net.Http; -using System.Web.Http; -using System.Web.Http.Controllers; -using System.Web.Http.Dispatcher; -using Autofac; -using Autofac.Core; -using Autofac.Features.Metadata; -using Orchard.WebApi.Extensions; - -namespace Orchard.WebApi { - public class DefaultOrchardWebApiHttpControllerActivator : IHttpControllerActivator { - private readonly HttpConfiguration _configuration; - - public DefaultOrchardWebApiHttpControllerActivator(HttpConfiguration configuration) - : base() { - _configuration = configuration; - } - - /// - /// Tries to resolve an instance for the controller associated with a given service key for the work context scope. - /// - /// The type of the controller. - /// The work context. - /// The service key for the controller. - /// The controller instance. - /// True if the controller was resolved; false otherwise. - protected bool TryResolve(WorkContext workContext, object serviceKey, out T instance) { - if (workContext != null && serviceKey != null) { - var key = new KeyedService(serviceKey, typeof(T)); - object value; - if (workContext.Resolve().TryResolveService(key, out value)) { - instance = (T)value; - return true; - } - } - - instance = default(T); - return false; - } - public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { - var routeData = request.GetRouteData(); - - HttpControllerContext controllerContext = new HttpControllerContext(_configuration, routeData, request); - - // Determine the area name for the request, and fall back to stock orchard controllers - var areaName = routeData.GetAreaName(); - - // Service name pattern matches the identification strategy - var serviceKey = (areaName + "/" + controllerDescriptor.ControllerName).ToLowerInvariant(); - - // Now that the request container is known - try to resolve the controller information - Meta> info; - var workContext = controllerContext.GetWorkContext(); - if (TryResolve(workContext, serviceKey, out info)) { - controllerContext.ControllerDescriptor = - new HttpControllerDescriptor(_configuration, controllerDescriptor.ControllerName, controllerType); - - var controller = info.Value.Value; - - controllerContext.Controller = controller; - - return controller; - } - - return null; - } - } +using System; +using System.Net.Http; +using System.Web.Http; +using System.Web.Http.Controllers; +using System.Web.Http.Dispatcher; +using Autofac; +using Autofac.Core; +using Autofac.Features.Metadata; +using Orchard.WebApi.Extensions; + +namespace Orchard.WebApi { + public class DefaultOrchardWebApiHttpControllerActivator : IHttpControllerActivator { + private readonly HttpConfiguration _configuration; + + public DefaultOrchardWebApiHttpControllerActivator(HttpConfiguration configuration) + : base() { + _configuration = configuration; + } + + /// + /// Tries to resolve an instance for the controller associated with a given service key for the work context scope. + /// + /// The type of the controller. + /// The work context. + /// The service key for the controller. + /// The controller instance. + /// True if the controller was resolved; false otherwise. + protected bool TryResolve(WorkContext workContext, object serviceKey, out T instance) { + if (workContext != null && serviceKey != null) { + var key = new KeyedService(serviceKey, typeof(T)); + object value; + if (workContext.Resolve().TryResolveService(key, out value)) { + instance = (T)value; + return true; + } + } + + instance = default(T); + return false; + } + public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { + var routeData = request.GetRouteData(); + + HttpControllerContext controllerContext = new HttpControllerContext(_configuration, routeData, request); + + // Determine the area name for the request, and fall back to stock orchard controllers + var areaName = routeData.GetAreaName(); + + // Service name pattern matches the identification strategy + var serviceKey = (areaName + "/" + controllerDescriptor.ControllerName).ToLowerInvariant(); + + // Now that the request container is known - try to resolve the controller information + Meta> info; + var workContext = controllerContext.GetWorkContext(); + if (TryResolve(workContext, serviceKey, out info)) { + controllerContext.ControllerDescriptor = + new HttpControllerDescriptor(_configuration, controllerDescriptor.ControllerName, controllerType); + + var controller = info.Value.Value; + + controllerContext.Controller = controller; + + return controller; + } + + return null; + } + } } \ No newline at end of file diff --git a/src/Orchard/WorkContext.cs b/src/Orchard/WorkContext.cs index 767719f3d..492c10dad 100644 --- a/src/Orchard/WorkContext.cs +++ b/src/Orchard/WorkContext.cs @@ -1,109 +1,109 @@ -using System; -using System.Web; -using Orchard.Environment.Extensions.Models; -using Orchard.Security; -using Orchard.Settings; - -namespace Orchard { - /// - /// A work context for work context scope - /// - public abstract class WorkContext { - /// - /// Resolves a registered dependency type. - /// - /// The type of the dependency. - /// An instance of the dependency if it could be resolved. - public abstract T Resolve(); - - /// - /// Resolves a registered dependency type. - /// - /// The type of the dependency. - /// An instance of the dependency if it could be resolved. - public abstract object Resolve(Type serviceType); - - /// - /// Tries to resolve a registered dependency type. - /// - /// The type of the dependency. - /// An instance of the dependency if it could be resolved. - /// True if the dependency could be resolved, false otherwise. - public abstract bool TryResolve(out T service); - - /// - /// Tries to resolve a registered dependency type. - /// - /// The type of the dependency. - /// An instance of the dependency if it could be resolved. - /// True if the dependency could be resolved, false otherwise. - public abstract bool TryResolve(Type serviceType, out object service); - - public abstract T GetState(string name); - public abstract void SetState(string name, T value); - - /// - /// The http context corresponding to the work context - /// - public HttpContextBase HttpContext { - get { return GetState("HttpContext"); } - set { SetState("HttpContext", value); } - } - - /// - /// The Layout shape corresponding to the work context - /// - public dynamic Layout { - get { return GetState("Layout"); } - set { SetState("Layout", value); } - } - - /// - /// Settings of the site corresponding to the work context - /// - public ISite CurrentSite { - get { return GetState("CurrentSite"); } - set { SetState("CurrentSite", value); } - } - - /// - /// The user, if there is any corresponding to the work context - /// - public IUser CurrentUser { - get { return GetState("CurrentUser"); } - set { SetState("CurrentUser", value); } - } - - /// - /// The theme used in the work context - /// - public ExtensionDescriptor CurrentTheme { - get { return GetState("CurrentTheme"); } - set { SetState("CurrentTheme", value); } - } - - /// - /// Active culture of the work context - /// - public string CurrentCulture { - get { return GetState("CurrentCulture"); } - set { SetState("CurrentCulture", value); } - } - - /// - /// Active calendar of the work context - /// - public string CurrentCalendar { - get { return GetState("CurrentCalendar"); } - set { SetState("CurrentCalendar", value); } - } - - /// - /// Time zone of the work context - /// - public TimeZoneInfo CurrentTimeZone { - get { return GetState("CurrentTimeZone"); } - set { SetState("CurrentTimeZone", value); } - } - } -} +using System; +using System.Web; +using Orchard.Environment.Extensions.Models; +using Orchard.Security; +using Orchard.Settings; + +namespace Orchard { + /// + /// A work context for work context scope + /// + public abstract class WorkContext { + /// + /// Resolves a registered dependency type. + /// + /// The type of the dependency. + /// An instance of the dependency if it could be resolved. + public abstract T Resolve(); + + /// + /// Resolves a registered dependency type. + /// + /// The type of the dependency. + /// An instance of the dependency if it could be resolved. + public abstract object Resolve(Type serviceType); + + /// + /// Tries to resolve a registered dependency type. + /// + /// The type of the dependency. + /// An instance of the dependency if it could be resolved. + /// True if the dependency could be resolved, false otherwise. + public abstract bool TryResolve(out T service); + + /// + /// Tries to resolve a registered dependency type. + /// + /// The type of the dependency. + /// An instance of the dependency if it could be resolved. + /// True if the dependency could be resolved, false otherwise. + public abstract bool TryResolve(Type serviceType, out object service); + + public abstract T GetState(string name); + public abstract void SetState(string name, T value); + + /// + /// The http context corresponding to the work context + /// + public HttpContextBase HttpContext { + get { return GetState("HttpContext"); } + set { SetState("HttpContext", value); } + } + + /// + /// The Layout shape corresponding to the work context + /// + public dynamic Layout { + get { return GetState("Layout"); } + set { SetState("Layout", value); } + } + + /// + /// Settings of the site corresponding to the work context + /// + public ISite CurrentSite { + get { return GetState("CurrentSite"); } + set { SetState("CurrentSite", value); } + } + + /// + /// The user, if there is any corresponding to the work context + /// + public IUser CurrentUser { + get { return GetState("CurrentUser"); } + set { SetState("CurrentUser", value); } + } + + /// + /// The theme used in the work context + /// + public ExtensionDescriptor CurrentTheme { + get { return GetState("CurrentTheme"); } + set { SetState("CurrentTheme", value); } + } + + /// + /// Active culture of the work context + /// + public string CurrentCulture { + get { return GetState("CurrentCulture"); } + set { SetState("CurrentCulture", value); } + } + + /// + /// Active calendar of the work context + /// + public string CurrentCalendar { + get { return GetState("CurrentCalendar"); } + set { SetState("CurrentCalendar", value); } + } + + /// + /// Time zone of the work context + /// + public TimeZoneInfo CurrentTimeZone { + get { return GetState("CurrentTimeZone"); } + set { SetState("CurrentTimeZone", value); } + } + } +} diff --git a/src/Tools/Orchard/HostContext/CommandHostContextProvider.cs b/src/Tools/Orchard/HostContext/CommandHostContextProvider.cs index f53fd2af1..81689f071 100644 --- a/src/Tools/Orchard/HostContext/CommandHostContextProvider.cs +++ b/src/Tools/Orchard/HostContext/CommandHostContextProvider.cs @@ -1,126 +1,126 @@ -using System; -using System.IO; -using System.Linq; -using System.Security; -using System.Web; -using System.Web.Compilation; -using System.Web.Hosting; -using Orchard.Host; -using Orchard.Parameters; - -namespace Orchard.HostContext { - public class CommandHostContextProvider : ICommandHostContextProvider { - private readonly string[] _args; - private TextWriter _output; - private TextReader _input; - - public CommandHostContextProvider(string[] args) { - _input = Console.In; - _output = Console.Out; - _args = args; - } - - [SecurityCritical] - public CommandHostContext CreateContext() { - var context = new CommandHostContext { RetryResult = CommandReturnCodes.Retry }; - Initialize(context); - return context; - } - - [SecurityCritical] - public void Shutdown(CommandHostContext context) { - try { - if (context.CommandHost != null) { - LogInfo(context, "Shutting down Orchard session..."); - context.CommandHost.StopSession(_input, _output); - } - } - catch (AppDomainUnloadedException) { - LogInfo(context, " (AppDomain already unloaded)"); - } - - if (context.CommandHost != null) { - LogInfo(context, "Shutting down ASP.NET AppDomain..."); - ApplicationManager.GetApplicationManager().ShutdownAll(); - } - } - - private void Initialize(CommandHostContext context) { - context.Arguments = new OrchardParametersParser().Parse(new CommandParametersParser().Parse(_args)); - context.Logger = new Logger(context.Arguments.Verbose, _output); - - // Perform some argument validation and display usage if something is incorrect - context.DisplayUsageHelp = context.Arguments.Switches.ContainsKey("?"); - if (context.DisplayUsageHelp) - return; - - context.DisplayUsageHelp = (context.Arguments.Arguments.Any() && context.Arguments.ResponseFiles.Any()); - if (context.DisplayUsageHelp) { - _output.WriteLine("Incorrect syntax: Response files cannot be used in conjunction with commands"); - return; - } - - if (string.IsNullOrEmpty(context.Arguments.VirtualPath)) - context.Arguments.VirtualPath = "/"; - LogInfo(context, "Virtual path: \"{0}\"", context.Arguments.VirtualPath); - - if (string.IsNullOrEmpty(context.Arguments.WorkingDirectory)) - context.Arguments.WorkingDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); - - LogInfo(context, "Working directory: \"{0}\"", context.Arguments.WorkingDirectory); - - LogInfo(context, "Detecting orchard installation root directory..."); - context.OrchardDirectory = GetOrchardDirectory(context.Arguments.WorkingDirectory); - LogInfo(context, "Orchard root directory: \"{0}\"", context.OrchardDirectory.FullName); - - LogInfo(context, "Creating ASP.NET AppDomain for command agent..."); - context.CommandHost = CreateWorkerAppDomainWithHost(context.Arguments.VirtualPath, context.OrchardDirectory.FullName, typeof(CommandHost)); - - LogInfo(context, "Starting Orchard session"); - context.StartSessionResult = context.CommandHost.StartSession(_input, _output); - } - - private void LogInfo(CommandHostContext context, string format, params object[] args) { - if (context.Logger != null) - context.Logger.LogInfo(format, args); - } - - private DirectoryInfo GetOrchardDirectory(string directory) { - for (var directoryInfo = new DirectoryInfo(directory); directoryInfo != null; directoryInfo = directoryInfo.Parent) { - if (!directoryInfo.Exists) { - throw new ApplicationException(string.Format("Directory \"{0}\" does not exist", directoryInfo.FullName)); - } - - // We look for - // 1) .\web.config - // 2) .\bin\Orchard.Framework.dll - var webConfigFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, "web.config")); - if (!webConfigFileInfo.Exists) - continue; - - var binDirectoryInfo = new DirectoryInfo(Path.Combine(directoryInfo.FullName, "bin")); - if (!binDirectoryInfo.Exists) - continue; - - var orchardFrameworkFileInfo = new FileInfo(Path.Combine(binDirectoryInfo.FullName, "Orchard.Framework.dll")); - if (!orchardFrameworkFileInfo.Exists) - continue; - - return directoryInfo; - } - - throw new ApplicationException( - string.Format("Directory \"{0}\" doesn't seem to contain an Orchard installation", new DirectoryInfo(directory).FullName)); - } - - private static CommandHost CreateWorkerAppDomainWithHost(string virtualPath, string physicalPath, Type hostType) { - var clientBuildManager = new ClientBuildManager(virtualPath, physicalPath); - // Fix for https://github.com/OrchardCMS/Orchard/issues/1749 - // By forcing the CBM to build App_Code, etc, we ensure that the ASP.NET BuildManager - // is in a state where it can safely (i.e. in a multi-threaded safe way) process - // multiple concurrent calls to "GetCompiledAssembly". - clientBuildManager.CompileApplicationDependencies(); - return (CommandHost)clientBuildManager.CreateObject(hostType, false); - } - } -} +using System; +using System.IO; +using System.Linq; +using System.Security; +using System.Web; +using System.Web.Compilation; +using System.Web.Hosting; +using Orchard.Host; +using Orchard.Parameters; + +namespace Orchard.HostContext { + public class CommandHostContextProvider : ICommandHostContextProvider { + private readonly string[] _args; + private TextWriter _output; + private TextReader _input; + + public CommandHostContextProvider(string[] args) { + _input = Console.In; + _output = Console.Out; + _args = args; + } + + [SecurityCritical] + public CommandHostContext CreateContext() { + var context = new CommandHostContext { RetryResult = CommandReturnCodes.Retry }; + Initialize(context); + return context; + } + + [SecurityCritical] + public void Shutdown(CommandHostContext context) { + try { + if (context.CommandHost != null) { + LogInfo(context, "Shutting down Orchard session..."); + context.CommandHost.StopSession(_input, _output); + } + } + catch (AppDomainUnloadedException) { + LogInfo(context, " (AppDomain already unloaded)"); + } + + if (context.CommandHost != null) { + LogInfo(context, "Shutting down ASP.NET AppDomain..."); + ApplicationManager.GetApplicationManager().ShutdownAll(); + } + } + + private void Initialize(CommandHostContext context) { + context.Arguments = new OrchardParametersParser().Parse(new CommandParametersParser().Parse(_args)); + context.Logger = new Logger(context.Arguments.Verbose, _output); + + // Perform some argument validation and display usage if something is incorrect + context.DisplayUsageHelp = context.Arguments.Switches.ContainsKey("?"); + if (context.DisplayUsageHelp) + return; + + context.DisplayUsageHelp = (context.Arguments.Arguments.Any() && context.Arguments.ResponseFiles.Any()); + if (context.DisplayUsageHelp) { + _output.WriteLine("Incorrect syntax: Response files cannot be used in conjunction with commands"); + return; + } + + if (string.IsNullOrEmpty(context.Arguments.VirtualPath)) + context.Arguments.VirtualPath = "/"; + LogInfo(context, "Virtual path: \"{0}\"", context.Arguments.VirtualPath); + + if (string.IsNullOrEmpty(context.Arguments.WorkingDirectory)) + context.Arguments.WorkingDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); + + LogInfo(context, "Working directory: \"{0}\"", context.Arguments.WorkingDirectory); + + LogInfo(context, "Detecting orchard installation root directory..."); + context.OrchardDirectory = GetOrchardDirectory(context.Arguments.WorkingDirectory); + LogInfo(context, "Orchard root directory: \"{0}\"", context.OrchardDirectory.FullName); + + LogInfo(context, "Creating ASP.NET AppDomain for command agent..."); + context.CommandHost = CreateWorkerAppDomainWithHost(context.Arguments.VirtualPath, context.OrchardDirectory.FullName, typeof(CommandHost)); + + LogInfo(context, "Starting Orchard session"); + context.StartSessionResult = context.CommandHost.StartSession(_input, _output); + } + + private void LogInfo(CommandHostContext context, string format, params object[] args) { + if (context.Logger != null) + context.Logger.LogInfo(format, args); + } + + private DirectoryInfo GetOrchardDirectory(string directory) { + for (var directoryInfo = new DirectoryInfo(directory); directoryInfo != null; directoryInfo = directoryInfo.Parent) { + if (!directoryInfo.Exists) { + throw new ApplicationException(string.Format("Directory \"{0}\" does not exist", directoryInfo.FullName)); + } + + // We look for + // 1) .\web.config + // 2) .\bin\Orchard.Framework.dll + var webConfigFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, "web.config")); + if (!webConfigFileInfo.Exists) + continue; + + var binDirectoryInfo = new DirectoryInfo(Path.Combine(directoryInfo.FullName, "bin")); + if (!binDirectoryInfo.Exists) + continue; + + var orchardFrameworkFileInfo = new FileInfo(Path.Combine(binDirectoryInfo.FullName, "Orchard.Framework.dll")); + if (!orchardFrameworkFileInfo.Exists) + continue; + + return directoryInfo; + } + + throw new ApplicationException( + string.Format("Directory \"{0}\" doesn't seem to contain an Orchard installation", new DirectoryInfo(directory).FullName)); + } + + private static CommandHost CreateWorkerAppDomainWithHost(string virtualPath, string physicalPath, Type hostType) { + var clientBuildManager = new ClientBuildManager(virtualPath, physicalPath); + // Fix for https://github.com/OrchardCMS/Orchard/issues/1749 + // By forcing the CBM to build App_Code, etc, we ensure that the ASP.NET BuildManager + // is in a state where it can safely (i.e. in a multi-threaded safe way) process + // multiple concurrent calls to "GetCompiledAssembly". + clientBuildManager.CompileApplicationDependencies(); + return (CommandHost)clientBuildManager.CreateObject(hostType, false); + } + } +}