diff --git a/src/Plunder.Setting/Core/Extensions/DictionaryExtensions.cs b/src/Plunder.Setting/Core/Extensions/DictionaryExtensions.cs new file mode 100644 index 0000000..426b6ca --- /dev/null +++ b/src/Plunder.Setting/Core/Extensions/DictionaryExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace Plunder.Setting.Core.Extensions +{ + public static class DictionaryExtensions + { + public static T GetValue(this IDictionary dic, string key) + { + if (dic == null) + throw new ArgumentNullException(nameof(dic)); + object valueObj = null; + if (dic.TryGetValue(key, out valueObj)) + { + if (valueObj is T) + return (T)valueObj; + } + return default(T); + } + } +} diff --git a/src/Plunder.Setting/Core/Http/HtmlActionResult.cs b/src/Plunder.Setting/Core/Http/HtmlActionResult.cs new file mode 100644 index 0000000..c53423a --- /dev/null +++ b/src/Plunder.Setting/Core/Http/HtmlActionResult.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web.Http; + +namespace Plunder.Setting.Core.Http +{ + public class HtmlActionResult : IHttpActionResult + { + private const string ViewDirectory = @"E:devConsoleApplication8ConsoleApplication8"; + private readonly string _view; + private readonly dynamic _model; + + private readonly HttpRequestMessage _request; + private readonly string _location; + + public HtmlActionResult(string viewName, object model) + { + _view = LoadView(viewName); + _model = model; + } + + + public Task ExecuteAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + private static string LoadView(string name) + { + var view = File.ReadAllText(Path.Combine(ViewDirectory, name + ".cshtml")); + return view; + } + } +} diff --git a/src/Plunder.Setting/Core/Http/HttpRouteCollectionExtensions.cs b/src/Plunder.Setting/Core/Http/~HttpRouteCollectionExtensions.cs similarity index 100% rename from src/Plunder.Setting/Core/Http/HttpRouteCollectionExtensions.cs rename to src/Plunder.Setting/Core/Http/~HttpRouteCollectionExtensions.cs diff --git a/src/Plunder.Setting/Core/Http/NamespaceHttpControllerSelector.cs b/src/Plunder.Setting/Core/Http/~NamespaceHttpControllerSelector.cs similarity index 100% rename from src/Plunder.Setting/Core/Http/NamespaceHttpControllerSelector.cs rename to src/Plunder.Setting/Core/Http/~NamespaceHttpControllerSelector.cs diff --git a/src/Plunder.Setting/Core/Http/OldNamespaceHttpControllerSelector.cs b/src/Plunder.Setting/Core/Http/~OldNamespaceHttpControllerSelector.cs similarity index 100% rename from src/Plunder.Setting/Core/Http/OldNamespaceHttpControllerSelector.cs rename to src/Plunder.Setting/Core/Http/~OldNamespaceHttpControllerSelector.cs diff --git a/src/Plunder.Setting/Core/Mvc/Controller.cs b/src/Plunder.Setting/Core/Mvc/Controller.cs new file mode 100644 index 0000000..fe7dfdf --- /dev/null +++ b/src/Plunder.Setting/Core/Mvc/Controller.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; + +namespace Plunder.Setting.Core.Mvc +{ + public class Controller : ApiController + { + private ExpandoObject _viewDataDictionary; + public dynamic ViewData + { + get + { + if (_viewDataDictionary == null) + { + _viewDataDictionary = new ExpandoObject(); + } + return _viewDataDictionary; + } + set { _viewDataDictionary = value; } + } + + protected internal ViewResult View() + { + return View(viewName: null, masterName: null, model: null); + } + + protected internal ViewResult View(object model) + { + return View(null /* viewName */, null /* masterName */, model); + } + + protected internal ViewResult View(string viewName) + { + return View(viewName, masterName: null, model: null); + } + + protected internal ViewResult View(string viewName, string masterName) + { + return View(viewName, masterName, null /* model */); + } + + protected internal ViewResult View(string viewName, object model) + { + return View(viewName, null /* masterName */, model); + } + + protected internal virtual ViewResult View(string viewName, string masterName, object model) + { + if (model != null) + { + ViewData.Model = model; + } + + return new ViewResult + { + ViewName = viewName, + MasterName = masterName, + ViewData = ViewData, + TempData = TempData, + ViewEngineCollection = ViewEngineCollection + }; + } + } +} diff --git a/src/Plunder.Setting/Core/Mvc/IView.cs b/src/Plunder.Setting/Core/Mvc/IView.cs new file mode 100644 index 0000000..bdb8db4 --- /dev/null +++ b/src/Plunder.Setting/Core/Mvc/IView.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http.Controllers; + +namespace Plunder.Setting.Core.Mvc +{ + public interface IView + { + void Render(HttpControllerContext controllerContext, TextWriter writer); + } +} diff --git a/src/Plunder.Setting/Core/Mvc/IViewEngine.cs b/src/Plunder.Setting/Core/Mvc/IViewEngine.cs new file mode 100644 index 0000000..465cea1 --- /dev/null +++ b/src/Plunder.Setting/Core/Mvc/IViewEngine.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http.Controllers; + +namespace Plunder.Setting.Core.Mvc +{ + public interface IViewEngine + { + ViewEngineResult FindPartialView(HttpControllerContext controllerContext, string partialViewName, bool useCache); + ViewEngineResult FindView(HttpControllerContext controllerContext, string viewName, string masterName, bool useCache); + void ReleaseView(HttpControllerContext controllerContext, IView view); + } +} diff --git a/src/Plunder.Setting/Core/Mvc/RazorViewEngine.cs b/src/Plunder.Setting/Core/Mvc/RazorViewEngine.cs new file mode 100644 index 0000000..80cdd63 --- /dev/null +++ b/src/Plunder.Setting/Core/Mvc/RazorViewEngine.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http.Controllers; +using Plunder.Setting.Core.Extensions; +using System.Globalization; + +namespace Plunder.Setting.Core.Mvc +{ + public class RazorViewEngine : IViewEngine + { + internal static readonly string ViewStartFileName = "_ViewStart"; + + private const string CacheKeyFormat = ":ViewCacheEntry:{0}:{1}:{2}:{3}:"; //{4}: + private const string CacheKeyPrefixMaster = "Master"; + private const string CacheKeyPrefixPartial = "Partial"; + private const string CacheKeyPrefixView = "View"; + private static readonly string[] _emptyLocations = new string[0]; + + public string[] ViewLocationFormats { get; set; } + + public string[] MasterLocationFormats { get; set; } + + public string[] PartialViewLocationFormats { get; set; } + + public string[] FileExtensions { get; set; } + + + public ViewEngineResult FindPartialView(HttpControllerContext controllerContext, string partialViewName, bool useCache) + { + throw new NotImplementedException(); + } + + public ViewEngineResult FindView(HttpControllerContext controllerContext, string viewName, string masterName, bool useCache) + { + if (controllerContext == null) + { + throw new ArgumentNullException("controllerContext"); + } + if (String.IsNullOrEmpty(viewName)) + { + throw new ArgumentNullException("viewName"); + } + + string[] viewLocationsSearched; + string[] masterLocationsSearched; + + string controllerName = controllerContext.RouteData.Values.GetValue("controller"); + string viewPath = GetPath(controllerContext, ViewLocationFormats, /*AreaViewLocationFormats,*/ "ViewLocationFormats", viewName, controllerName, CacheKeyPrefixView, out viewLocationsSearched); + string masterPath = GetPath(controllerContext, MasterLocationFormats,/* AreaMasterLocationFormats,*/ "MasterLocationFormats", masterName, controllerName, CacheKeyPrefixMaster, out masterLocationsSearched); + + if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName))) + { + return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched)); + } + + return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this); + } + + private string GetPath(HttpControllerContext controllerContext, string[] locations, /*string[] areaLocations,*/ string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, out string[] searchedLocations) + { + searchedLocations = _emptyLocations; + + if (String.IsNullOrEmpty(name)) + { + return String.Empty; + } + + //string areaName = AreaHelpers.GetAreaName(controllerContext.RouteData); + //bool usingAreas = !String.IsNullOrEmpty(areaName); + List viewLocations = GetViewLocations(locations/*, (usingAreas) ? areaLocations : null*/); + + if (viewLocations.Count == 0) + throw new InvalidOperationException(nameof(locations)); + + bool nameRepresentsPath = IsSpecificPath(name); + string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName/*, areaName*/); + + return nameRepresentsPath + ? GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) + : GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName/*, areaName*/, cacheKey, ref searchedLocations); + } + + private string GetPathFromGeneralName(HttpControllerContext controllerContext, List locations, string name, string controllerName, /*string areaName,*/ string cacheKey, ref string[] searchedLocations) + { + string result = String.Empty; + searchedLocations = new string[locations.Count]; + + for (int i = 0; i < locations.Count; i++) + { + ViewLocation location = locations[i]; + string virtualPath = location.Format(name, controllerName); + DisplayInfo virtualPathDisplayInfo = DisplayModeProvider.GetDisplayInfoForVirtualPath(virtualPath, controllerContext.HttpContext, path => FileExists(controllerContext, path), controllerContext.DisplayMode); + + if (virtualPathDisplayInfo != null) + { + string resolvedVirtualPath = virtualPathDisplayInfo.FilePath; + + searchedLocations = _emptyLocations; + result = resolvedVirtualPath; + ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, virtualPathDisplayInfo.DisplayMode.DisplayModeId), result); + + if (controllerContext.DisplayMode == null) + { + controllerContext.DisplayMode = virtualPathDisplayInfo.DisplayMode; + } + + // Populate the cache for all other display modes. We want to cache both file system hits and misses so that we can distinguish + // in future requests whether a file's status was evicted from the cache (null value) or if the file doesn't exist (empty string). + IEnumerable allDisplayModes = DisplayModeProvider.Modes; + foreach (IDisplayMode displayMode in allDisplayModes) + { + if (displayMode.DisplayModeId != virtualPathDisplayInfo.DisplayMode.DisplayModeId) + { + DisplayInfo displayInfoToCache = displayMode.GetDisplayInfo(controllerContext.HttpContext, virtualPath, virtualPathExists: path => FileExists(controllerContext, path)); + + string cacheValue = String.Empty; + if (displayInfoToCache != null && displayInfoToCache.FilePath != null) + { + cacheValue = displayInfoToCache.FilePath; + } + ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayMode.DisplayModeId), cacheValue); + } + } + break; + } + + searchedLocations[i] = virtualPath; + } + + return result; + } + + private string GetPathFromSpecificName(HttpControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations) + { + string result = name; + + if (!(FilePathIsSupported(name) && FileExists(controllerContext, name))) + { + result = String.Empty; + searchedLocations = new[] { name }; + } + + ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result); + return result; + } + + private bool FilePathIsSupported(string virtualPath) + { + if (FileExtensions == null) + { + // legacy behavior for custom ViewEngine that might not set the FileExtensions property + return true; + } + else + { + // get rid of the '.' because the FileExtensions property expects extensions withouth a dot. + string extension = GetExtensionThunk(virtualPath).TrimStart('.'); + return FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); + } + } + + private static List GetViewLocations(string[] viewLocationFormats/*, string[] areaViewLocationFormats*/) + { + List allLocations = new List(); + + //if (areaViewLocationFormats != null) + //{ + // foreach (string areaViewLocationFormat in areaViewLocationFormats) + // { + // allLocations.Add(new AreaAwareViewLocation(areaViewLocationFormat)); + // } + //} + + if (viewLocationFormats != null) + { + foreach (string viewLocationFormat in viewLocationFormats) + { + allLocations.Add(new ViewLocation(viewLocationFormat)); + } + } + + return allLocations; + } + + + internal virtual string CreateCacheKey(string prefix, string name, string controllerName/*, string areaName*/) + { + return String.Format(CultureInfo.InvariantCulture, CacheKeyFormat, + GetType().AssemblyQualifiedName, prefix, name, controllerName/*, areaName*/); + } + + private static bool IsSpecificPath(string name) + { + char c = name[0]; + return (c == '~' || c == '/'); + } + + public void ReleaseView(HttpControllerContext controllerContext, IView view) + { + throw new NotImplementedException(); + } + + + public RazorViewEngine() + { + ViewLocationFormats = new[] + { + "~/Views/{1}/{0}.cshtml", + "~/Views/Shared/{0}.cshtml" + }; + MasterLocationFormats = new[] + { + "~/Views/{1}/{0}.cshtml", + "~/Views/Shared/{0}.cshtml" + }; + PartialViewLocationFormats = new[] + { + "~/Views/{1}/{0}.cshtml", + "~/Views/Shared/{0}.cshtml" + }; + + FileExtensions = new[] + { + "cshtml" + }; + } + } +} diff --git a/src/Plunder.Setting/Core/Mvc/ViewEngineResult.cs b/src/Plunder.Setting/Core/Mvc/ViewEngineResult.cs new file mode 100644 index 0000000..46c98a2 --- /dev/null +++ b/src/Plunder.Setting/Core/Mvc/ViewEngineResult.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +namespace Plunder.Setting.Core.Mvc +{ + public class ViewEngineResult + { + public ViewEngineResult(IEnumerable searchedLocations) + { + if (searchedLocations == null) + { + throw new ArgumentNullException("searchedLocations"); + } + + SearchedLocations = searchedLocations; + } + + public ViewEngineResult(IView view, IViewEngine viewEngine) + { + if (view == null) + { + throw new ArgumentNullException("view"); + } + if (viewEngine == null) + { + throw new ArgumentNullException("viewEngine"); + } + + View = view; + ViewEngine = viewEngine; + } + + public IEnumerable SearchedLocations { get; private set; } + + public IView View { get; private set; } + + public IViewEngine ViewEngine { get; private set; } + } +} diff --git a/src/Plunder.Setting/Core/Mvc/ViewLocation.cs b/src/Plunder.Setting/Core/Mvc/ViewLocation.cs new file mode 100644 index 0000000..24eebb0 --- /dev/null +++ b/src/Plunder.Setting/Core/Mvc/ViewLocation.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Plunder.Setting.Core.Mvc +{ + internal class ViewLocation + { + protected string _virtualPathFormatString; + + public ViewLocation(string virtualPathFormatString) + { + _virtualPathFormatString = virtualPathFormatString; + } + + public virtual string Format(string viewName, string controllerName) + { + return String.Format(CultureInfo.InvariantCulture, _virtualPathFormatString, viewName, controllerName); + } + } +} diff --git a/src/Plunder.Setting/Core/Mvc/ViewResult.cs b/src/Plunder.Setting/Core/Mvc/ViewResult.cs new file mode 100644 index 0000000..4805a05 --- /dev/null +++ b/src/Plunder.Setting/Core/Mvc/ViewResult.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web.Http; + +namespace Plunder.Setting.Core.Mvc +{ + public class ViewResult : IHttpActionResult + { + public Task ExecuteAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Plunder.Setting/Plunder.Setting.csproj b/src/Plunder.Setting/Plunder.Setting.csproj index fc80534..cb5e0c1 100644 --- a/src/Plunder.Setting/Plunder.Setting.csproj +++ b/src/Plunder.Setting/Plunder.Setting.csproj @@ -101,11 +101,20 @@ + + + + + + + + + - - - + + + @@ -124,6 +133,9 @@ + + + diff --git a/src/Plunder.Setting/UIControllers/HomeUIController.cs b/src/Plunder.Setting/UIControllers/HomeUIController.cs index e816a5b..5ed2764 100644 --- a/src/Plunder.Setting/UIControllers/HomeUIController.cs +++ b/src/Plunder.Setting/UIControllers/HomeUIController.cs @@ -8,7 +8,7 @@ using System.Net.Http.Headers; namespace Plunder.Setting.UIControllers { /// - /// + /// /// [RoutePrefix("ui/Home")] public class HomeUIController : ApiController @@ -18,7 +18,35 @@ namespace Plunder.Setting.UIControllers [Route("~/ui")] [Route("~/")] [HttpGet] - public HttpResponseMessage Index() + public IHttpActionResult Index() + { + var templateKey = "domainui.index"; + string template = "Hello @Model ! This is UI! Path:" + Request.RequestUri.AbsolutePath; + //string result = RazEngine.Razor.Compile(template, new { Name = "World" }); + string result = null; + if (RazEngine.Razor.IsTemplateCached(templateKey, typeof(string))) + result = RazEngine.Razor.Run(templateKey, model: "Man"); + else + result = RazEngine.Razor.RunCompile(template, "templateKey", null, "Man"); + + + var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK); + var viewPath = this.RequestContext. + + httpResponseMessage.Content = new StringContent(result); + + + MediaTypeHeaderValue mediaTypeHeaderValue = new MediaTypeHeaderValue("text/html"); + + mediaTypeHeaderValue.CharSet = System.Text.Encoding.UTF8.WebName; + + httpResponseMessage.Content.Headers.ContentType = mediaTypeHeaderValue; + + return httpResponseMessage; + } + + + public HttpResponseMessage Index2() { var templateKey = "domainui.index"; string template = "Hello @Model ! This is UI! Path:" + Request.RequestUri.AbsolutePath; diff --git a/src/Plunder.Setting/Views/HomeUI/Index.cshtml b/src/Plunder.Setting/Views/HomeUI/Index.cshtml new file mode 100644 index 0000000..f6a859e --- /dev/null +++ b/src/Plunder.Setting/Views/HomeUI/Index.cshtml @@ -0,0 +1,15 @@ +@model string +@{ + Layout = null; +} + + + + + + + + + + + \ No newline at end of file diff --git a/src/Plunder.Setting/Views/Web.config b/src/Plunder.Setting/Views/Web.config new file mode 100644 index 0000000..e4f278c --- /dev/null +++ b/src/Plunder.Setting/Views/Web.config @@ -0,0 +1,45 @@ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Plunder.Setting/Views/_ViewStart.cshtml b/src/Plunder.Setting/Views/_ViewStart.cshtml new file mode 100644 index 0000000..2de6241 --- /dev/null +++ b/src/Plunder.Setting/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +}