diff --git a/Disco.Services/Authorization/DiscoAuthorizeAllAttribute.cs b/Disco.Services/Authorization/DiscoAuthorizeAllAttribute.cs index 557409ed..15cc425d 100644 --- a/Disco.Services/Authorization/DiscoAuthorizeAllAttribute.cs +++ b/Disco.Services/Authorization/DiscoAuthorizeAllAttribute.cs @@ -8,7 +8,7 @@ using System.Web.Mvc; namespace Disco.Services.Authorization { - public class DiscoAuthorizeAllAttribute : AuthorizeAttribute + public class DiscoAuthorizeAllAttribute : DiscoAuthorizeBaseAttribute { string[] authorizedClaims; @@ -20,22 +20,17 @@ namespace Disco.Services.Authorization this.authorizedClaims = AuthorisedClaims; } - protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) + public override bool IsAuthorized(System.Web.HttpContextBase httpContext) { - if (httpContext == null) - throw new ArgumentNullException("httpContext"); - - var authToken = UserService.CurrentAuthorization; - - if (authToken == null) + if (Token == null) return false; // No Current User - return authToken.HasAll(authorizedClaims); + return Token.HasAll(authorizedClaims); } - protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) + public override string HandleUnauthorizedMessage() { - filterContext.Result = new HttpUnauthorizedResult(AuthorizationToken.BuildRequireAllMessage(authorizedClaims)); + return AuthorizationToken.BuildRequireAllMessage(authorizedClaims); } } } diff --git a/Disco.Services/Authorization/DiscoAuthorizeAnyAttribute.cs b/Disco.Services/Authorization/DiscoAuthorizeAnyAttribute.cs index d04f9e9e..d7e9f634 100644 --- a/Disco.Services/Authorization/DiscoAuthorizeAnyAttribute.cs +++ b/Disco.Services/Authorization/DiscoAuthorizeAnyAttribute.cs @@ -8,7 +8,7 @@ using System.Web.Mvc; namespace Disco.Services.Authorization { - public class DiscoAuthorizeAnyAttribute : AuthorizeAttribute + public class DiscoAuthorizeAnyAttribute : DiscoAuthorizeBaseAttribute { string[] authorizedClaims; @@ -20,22 +20,17 @@ namespace Disco.Services.Authorization this.authorizedClaims = AuthorisedClaims; } - protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) + public override bool IsAuthorized(System.Web.HttpContextBase httpContext) { - if (httpContext == null) - throw new ArgumentNullException("httpContext"); - - var authToken = UserService.CurrentAuthorization; - - if (authToken == null) + if (Token == null) return false; // No Current User - return authToken.HasAny(authorizedClaims); + return Token.HasAny(authorizedClaims); } - protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) + public override string HandleUnauthorizedMessage() { - filterContext.Result = new HttpUnauthorizedResult(AuthorizationToken.BuildRequireAnyMessage(authorizedClaims)); + return AuthorizationToken.BuildRequireAnyMessage(authorizedClaims); } } } diff --git a/Disco.Services/Authorization/DiscoAuthorizeAttribute.cs b/Disco.Services/Authorization/DiscoAuthorizeAttribute.cs index 9d042417..51f5f4b0 100644 --- a/Disco.Services/Authorization/DiscoAuthorizeAttribute.cs +++ b/Disco.Services/Authorization/DiscoAuthorizeAttribute.cs @@ -8,7 +8,7 @@ using System.Web.Mvc; namespace Disco.Services.Authorization { - public class DiscoAuthorizeAttribute : AuthorizeAttribute + public class DiscoAuthorizeAttribute : DiscoAuthorizeBaseAttribute { string authorizedClaim; @@ -19,23 +19,18 @@ namespace Disco.Services.Authorization this.authorizedClaim = AuthorisedClaim; } - protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) + public override bool IsAuthorized(System.Web.HttpContextBase httpContext) { - if (httpContext == null) - throw new ArgumentNullException("httpContext"); - - var authToken = UserService.CurrentAuthorization; - - if (authToken == null) + if (Token == null) return false; // No Current User if (authorizedClaim == null) - return authToken.RoleTokens.Count > 0; // Just Authenticate - no Authorization (but require at least 1 role) + return Token.RoleTokens.Count > 0; // Just Authenticate - no Authorization (but require at least 1 role) else - return authToken.Has(authorizedClaim); + return Token.Has(authorizedClaim); } - protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) + public override string HandleUnauthorizedMessage() { string resultMessage; @@ -47,7 +42,7 @@ namespace Disco.Services.Authorization else resultMessage = AuthorizationToken.BuildRequireMessage(authorizedClaim); - filterContext.Result = new HttpUnauthorizedResult(resultMessage); + return resultMessage; } } } diff --git a/Disco.Services/Authorization/DiscoAuthorizeBaseAttribute.cs b/Disco.Services/Authorization/DiscoAuthorizeBaseAttribute.cs new file mode 100644 index 00000000..ccb1c27e --- /dev/null +++ b/Disco.Services/Authorization/DiscoAuthorizeBaseAttribute.cs @@ -0,0 +1,39 @@ +using Disco.Services.Users; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Mvc; + +namespace Disco.Services.Authorization +{ + public abstract class DiscoAuthorizeBaseAttribute : AuthorizeAttribute + { + protected AuthorizationToken Token + { + get + { + return UserService.CurrentAuthorization; + } + } + + public abstract bool IsAuthorized(System.Web.HttpContextBase httpContext); + public abstract string HandleUnauthorizedMessage(); + + protected sealed override bool AuthorizeCore(System.Web.HttpContextBase httpContext) + { + if (httpContext == null) + throw new ArgumentNullException("httpContext"); + + return IsAuthorized(httpContext); + } + + protected sealed override void HandleUnauthorizedRequest(AuthorizationContext filterContext) + { + string resultMessage = HandleUnauthorizedMessage(); + + filterContext.Result = new HttpUnauthorizedResult(resultMessage); + } + } +} diff --git a/Disco.Services/Disco.Services.csproj b/Disco.Services/Disco.Services.csproj index 21817985..92b6ec3d 100644 --- a/Disco.Services/Disco.Services.csproj +++ b/Disco.Services/Disco.Services.csproj @@ -92,6 +92,7 @@ + @@ -174,6 +175,8 @@ + + @@ -220,7 +223,7 @@ - + diff --git a/Disco.Services/Plugins/Features/UIExtension/Results/MultipleResult.cs b/Disco.Services/Plugins/Features/UIExtension/Results/MultipleResult.cs index 87f90bdd..1663f5d3 100644 --- a/Disco.Services/Plugins/Features/UIExtension/Results/MultipleResult.cs +++ b/Disco.Services/Plugins/Features/UIExtension/Results/MultipleResult.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Web.Mvc; namespace Disco.Services.Plugins.Features.UIExtension.Results { @@ -18,7 +19,7 @@ namespace Disco.Services.Plugins.Features.UIExtension.Results this.results = Results; } - public override void ExecuteResult(System.Web.Mvc.WebViewPage page) + public override void ExecuteResult(WebViewPage page) { foreach (var result in this.results) { diff --git a/Disco.Services/Plugins/Features/UIExtension/Results/PluginResourceCssResult.cs b/Disco.Services/Plugins/Features/UIExtension/Results/PluginResourceCssResult.cs index 875ffa75..d343677d 100644 --- a/Disco.Services/Plugins/Features/UIExtension/Results/PluginResourceCssResult.cs +++ b/Disco.Services/Plugins/Features/UIExtension/Results/PluginResourceCssResult.cs @@ -1,9 +1,12 @@ -using System; +using Disco.Services.Web.Bundles; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; +using System.Web.Mvc; +using System.Web.Routing; namespace Disco.Services.Plugins.Features.UIExtension.Results { @@ -16,13 +19,13 @@ namespace Disco.Services.Plugins.Features.UIExtension.Results : base(Source) { this._resource = Resource; - this._resourceUrl = HttpContext.Current.Request.RequestContext.DiscoPluginResourceUrl(Resource, false, Source.PluginManifest); + this._resourceUrl = new HtmlString(Source.PluginManifest.WebResourceUrl(Resource)); - var deferredBundles = HttpContext.Current.Items["Bundles.UIExtensionCss"] as List; + var deferredBundles = HttpContext.Current.Items[Bundle.UIExtensionCssKey] as List; if (deferredBundles == null) { deferredBundles = new List(); - HttpContext.Current.Items["Bundles.UIExtensionCss"] = deferredBundles; + HttpContext.Current.Items[Bundle.UIExtensionCssKey] = deferredBundles; } if (!deferredBundles.Contains(this._resourceUrl)) deferredBundles.Add(this._resourceUrl); diff --git a/Disco.Services/Plugins/Features/UIExtension/Results/PluginResourceScriptResult.cs b/Disco.Services/Plugins/Features/UIExtension/Results/PluginResourceScriptResult.cs index 14a67c52..497ec59f 100644 --- a/Disco.Services/Plugins/Features/UIExtension/Results/PluginResourceScriptResult.cs +++ b/Disco.Services/Plugins/Features/UIExtension/Results/PluginResourceScriptResult.cs @@ -1,4 +1,5 @@ -using System; +using Disco.Services.Web.Bundles; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -17,16 +18,16 @@ namespace Disco.Services.Plugins.Features.UIExtension.Results : base(Source) { this._resource = Resource; - this._resourceUrl = HttpContext.Current.Request.RequestContext.DiscoPluginResourceUrl(Resource, false, Source.PluginManifest); + this._resourceUrl = new HtmlString(Source.PluginManifest.WebResourceUrl(Resource)); this._placeInPageHead = PlaceInPageHead; if (this._placeInPageHead) { - var deferredBundles = HttpContext.Current.Items["Bundles.UIExtensionScripts"] as List; + var deferredBundles = HttpContext.Current.Items[Bundle.UIExtensionScriptsKey] as List; if (deferredBundles == null) { deferredBundles = new List(); - HttpContext.Current.Items["Bundles.UIExtensionScripts"] = deferredBundles; + HttpContext.Current.Items[Bundle.UIExtensionScriptsKey] = deferredBundles; } if (!deferredBundles.Contains(this._resourceUrl)) deferredBundles.Add(this._resourceUrl); diff --git a/Disco.Services/Plugins/PluginExtensions.cs b/Disco.Services/Plugins/PluginExtensions.cs index 258f659d..70ead538 100644 --- a/Disco.Services/Plugins/PluginExtensions.cs +++ b/Disco.Services/Plugins/PluginExtensions.cs @@ -10,6 +10,7 @@ using System.Web.Routing; using System.Web; using System.Web.Mvc.Html; using System.Globalization; +using Disco.Services.Web.Bundles; namespace Disco.Services.Plugins { @@ -93,34 +94,13 @@ namespace Disco.Services.Plugins #endregion #region Virtual Directories - //public static string WebHandlerResource(this PluginManifest pluginManifest, string resourcePath, RequestContext requestContext) - //{ - // var rootPath = WebHandlerRootUrl(pluginManifest, requestContext); - // return string.Concat(rootPath, resourcePath); - //} - //public static string WebHandlerRootUrl(this PluginManifest pluginManifest, RequestContext requestContext) - //{ - // var tempPath = pluginManifest.WebHandlerActionUrl(requestContext, "_"); - // return tempPath.Substring(0, tempPath.LastIndexOf(@"/") + 1); - //} - //public static string WebHandlerActionUrl(this PluginManifest pluginManifest, RequestContext requestContext, string PluginAction) - //{ - // var routeValues = new RouteValueDictionary(new { PluginId = pluginManifest.Id, PluginAction = PluginAction }); - // return UrlHelper.GenerateUrl("Plugin", "PluginWebHandler", "Index", routeValues, RouteTable.Routes, requestContext, true); - //} - //public static string WebHandlerResourceUrl(this PluginManifest pluginManifest, RequestContext requestContext, string PluginAction) - //{ - // var routeValues = new RouteValueDictionary(new { PluginId = pluginManifest.Id, PluginAction = PluginAction }); - - - - // return UrlHelper.GenerateUrl("Plugin", "PluginWebHandler", "Index", routeValues, RouteTable.Routes, requestContext, true); - //} + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static HtmlString DiscoPluginResourceUrl(this WebViewPage ViewPage, string Resource) { return ViewPage.DiscoPluginResourceUrl(Resource, false); } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static HtmlString DiscoPluginResourceUrl(this WebViewPage ViewPage, string Resource, bool Download) { if (string.IsNullOrEmpty(Resource)) @@ -133,10 +113,12 @@ namespace Disco.Services.Plugins return ViewPage.DiscoPluginResourceUrl(Resource, false, manifest); } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static HtmlString DiscoPluginResourceUrl(this WebViewPage ViewPage, string Resource, bool Download, PluginManifest manifest) { return ViewPage.ViewContext.RequestContext.DiscoPluginResourceUrl(Resource, Download, manifest); } + [Obsolete] public static HtmlString DiscoPluginResourceUrl(this RequestContext RequestContext, string Resource, bool Download, PluginManifest manifest) { var resourcePath = manifest.WebResourcePath(Resource); @@ -151,6 +133,7 @@ namespace Disco.Services.Plugins return new HtmlString(pluginActionUrl); } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static HtmlString DiscoPluginActionUrl(this WebViewPage ViewPage, string PluginAction) { if (string.IsNullOrEmpty(PluginAction)) @@ -163,16 +146,19 @@ namespace Disco.Services.Plugins return ViewPage.DiscoPluginActionUrl(PluginAction, manifest); } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static HtmlString DiscoPluginActionUrl(this WebViewPage ViewPage, string PluginAction, PluginManifest manifest) { return ViewPage.ViewContext.RequestContext.DiscoPluginActionUrl(PluginAction, manifest); } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static HtmlString DiscoPluginActionUrl(this RequestContext RequestContext, string PluginAction, PluginManifest manifest) { var routeValues = new RouteValueDictionary(new { PluginId = manifest.Id, PluginAction = PluginAction }); string pluginActionUrl = UrlHelper.GenerateUrl("Plugin", null, null, routeValues, RouteTable.Routes, RequestContext, false); return new HtmlString(pluginActionUrl); } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static HtmlString DiscoPluginConfigureUrl(this WebViewPage ViewPage) { // Find Plugin @@ -182,16 +168,19 @@ namespace Disco.Services.Plugins return ViewPage.DiscoPluginConfigureUrl(manifest); } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static HtmlString DiscoPluginConfigureUrl(this WebViewPage ViewPage, PluginManifest manifest) { return new HtmlString(ViewPage.ViewContext.RequestContext.DiscoPluginConfigureUrl(manifest)); } + [Obsolete] public static string DiscoPluginConfigureUrl(this RequestContext RequestContext, PluginManifest manifest) { var routeValues = new RouteValueDictionary(new { PluginId = manifest.Id }); string pluginActionUrl = UrlHelper.GenerateUrl("Config_Plugins_Configure", null, null, routeValues, RouteTable.Routes, RequestContext, false); return pluginActionUrl; } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static MvcForm DiscoPluginActionBeginForm(this WebViewPage ViewPage, string PluginAction, FormMethod method, IDictionary htmlAttributes) { if (string.IsNullOrEmpty(PluginAction)) @@ -207,19 +196,29 @@ namespace Disco.Services.Plugins return ViewPage.FormHelper(pluginActionUrl, method, htmlAttributes); } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static MvcForm DiscoPluginActionBeginForm(this WebViewPage ViewPage, string PluginAction, FormMethod method) { +#pragma warning disable 618 return ViewPage.DiscoPluginActionBeginForm(PluginAction, method, null); +#pragma warning restore 618 } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static MvcForm DiscoPluginActionBeginForm(this WebViewPage ViewPage, string PluginAction, IDictionary htmlAttributes) { +#pragma warning disable 618 return ViewPage.DiscoPluginActionBeginForm(PluginAction, FormMethod.Post, htmlAttributes); +#pragma warning restore 618 } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static MvcForm DiscoPluginActionBeginForm(this WebViewPage ViewPage, string PluginAction) { +#pragma warning disable 618 return ViewPage.DiscoPluginActionBeginForm(PluginAction, FormMethod.Post, null); +#pragma warning restore 618 } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] private static MvcForm FormHelper(this WebViewPage ViewPage, string formAction, FormMethod method, IDictionary htmlAttributes) { TagBuilder builder = new TagBuilder("form"); @@ -244,7 +243,7 @@ namespace Disco.Services.Plugins return form; } - + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static void DiscoPluginRegisterStylesheet(this WebViewPage ViewPage, string Resource) { if (string.IsNullOrEmpty(Resource)) @@ -257,10 +256,12 @@ namespace Disco.Services.Plugins ViewPage.DiscoPluginRegisterStylesheet(Resource, manifest); } + [Obsolete("Inherit ViewPages from 'Disco.Services.Plugins.WebViewPage' instead.")] public static void DiscoPluginRegisterStylesheet(this WebViewPage ViewPage, string Resource, PluginManifest manifest) { ViewPage.ViewContext.RequestContext.DiscoPluginRegisterStylesheet(Resource, manifest); } + [Obsolete] public static void DiscoPluginRegisterStylesheet(this RequestContext RequestContext, string Resource, PluginManifest manifest) { var resourcePath = manifest.WebResourcePath(Resource); @@ -272,11 +273,11 @@ namespace Disco.Services.Plugins HtmlString pluginResourceUrlHtml = new HtmlString(pluginResourceUrl); - var deferredBundles = RequestContext.HttpContext.Items["Bundles.UIExtensionCss"] as List; + var deferredBundles = RequestContext.HttpContext.Items[Bundle.UIExtensionCssKey] as List; if (deferredBundles == null) { deferredBundles = new List(); - HttpContext.Current.Items["Bundles.UIExtensionCss"] = deferredBundles; + HttpContext.Current.Items[Bundle.UIExtensionCssKey] = deferredBundles; } if (!deferredBundles.Contains(pluginResourceUrlHtml)) deferredBundles.Add(pluginResourceUrlHtml); @@ -285,6 +286,7 @@ namespace Disco.Services.Plugins #endregion #region Request Caching + [Obsolete] public static void SetCacheability(this PluginWebHandler Handler, TimeSpan CacheDuration) { var cache = Handler.HostController.Response.Cache; @@ -293,6 +295,7 @@ namespace Disco.Services.Plugins cache.SetValidUntilExpires(true); cache.SetCacheability(HttpCacheability.Private); } + [Obsolete] public static void SetCacheabilityOff(this PluginWebHandler Handler) { var cache = Handler.HostController.Response.Cache; @@ -302,6 +305,7 @@ namespace Disco.Services.Plugins #endregion #region Render Partial Compiled + [Obsolete] private static void RenderPartialCompiledInternal(this HtmlHelper htmlHelper, Type viewType, object model, TextWriter writer) { if (writer == null) @@ -315,15 +319,19 @@ namespace Disco.Services.Plugins HttpContextBase httpContext = htmlHelper.ViewContext.HttpContext; page.ExecutePageHierarchy(new WebPageContext(httpContext, null, model), writer, null); } + [Obsolete] public static MvcHtmlString PartialCompiled(this HtmlHelper htmlHelper, Type viewType) { return PartialCompiled(htmlHelper, viewType, null); } + [Obsolete] public static MvcHtmlString PartialCompiled(this HtmlHelper htmlHelper, Type viewType, object model) { using (StringWriter writer = new StringWriter(CultureInfo.CurrentCulture)) { +#pragma warning disable 618 htmlHelper.RenderPartialCompiledInternal(viewType, model, writer); +#pragma warning restore 618 return MvcHtmlString.Create(writer.ToString()); } } diff --git a/Disco.Services/Plugins/PluginManifest.cs b/Disco.Services/Plugins/PluginManifest.cs index 8488a1ee..517ad7be 100644 --- a/Disco.Services/Plugins/PluginManifest.cs +++ b/Disco.Services/Plugins/PluginManifest.cs @@ -12,6 +12,8 @@ using Disco.Data.Repository; using Disco.Services.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using Disco.Services.Authorization; +using System.Web.Routing; namespace Disco.Services.Plugins { @@ -54,6 +56,8 @@ namespace Disco.Services.Plugins private Type ConfigurationHandlerType { get; set; } [JsonIgnore] private Type WebHandlerType { get; set; } + [JsonIgnore] + private DiscoAuthorizeBaseAttribute[] WebHandlerAuthorizers { get; set; } [JsonIgnore] public string PluginLocation { get; private set; } @@ -426,6 +430,18 @@ namespace Disco.Services.Plugins if (!typeof(PluginWebHandler).IsAssignableFrom(this.WebHandlerType)) throw new ArgumentException("The Plugin WebHandlerType must inherit Disco.Services.Plugins.PluginWebHandler", "WebHandlerType"); + // Determine WebHandler Authorize Attributes + if (this.WebHandlerAuthorizers == null) + { + this.WebHandlerAuthorizers = this.WebHandlerType.GetCustomAttributes(true).ToArray(); + } + if (this.WebHandlerAuthorizers.Length > 0) + { + var attributeDenied = this.WebHandlerAuthorizers.FirstOrDefault(a => !a.IsAuthorized(HostController.HttpContext)); + if (attributeDenied != null) + throw new AccessDeniedException(attributeDenied.HandleUnauthorizedMessage()); + } + var handler = (PluginWebHandler)Activator.CreateInstance(this.WebHandlerType); handler.Manifest = this; @@ -480,6 +496,18 @@ namespace Disco.Services.Plugins return new Tuple(resourcePath, resourceHash.Item1); } + public string WebResourceUrl(string Resource) + { + var resourcePath = this.WebResourcePath(Resource); + + var url = UrlHelper.GenerateUrl("Plugin_Resources", null, null, + new RouteValueDictionary(new Dictionary() { { "PluginId", this.Id }, { "res", Resource } }), + RouteTable.Routes, HttpContext.Current.Request.RequestContext, false); + + url += string.Format("?v={0}", resourcePath.Item2); + + return url; + } public void LogException(Exception PluginException) { diff --git a/Disco.Services/Plugins/PluginWebHandler.cs b/Disco.Services/Plugins/PluginWebHandler.cs index 9973e4ca..28ff5dfb 100644 --- a/Disco.Services/Plugins/PluginWebHandler.cs +++ b/Disco.Services/Plugins/PluginWebHandler.cs @@ -1,12 +1,14 @@ -using System; -using System.Collections.Generic; +using Disco.Data.Repository; +using Disco.Models.Repository; +using Disco.Services.Authorization; +using Disco.Services.Users; +using RazorGenerator.Mvc; +using System; using System.IO; -using System.Linq; using System.Text; -using System.Threading.Tasks; +using System.Web; using System.Web.Mvc; using System.Web.Routing; -using RazorGenerator.Mvc; namespace Disco.Services.Plugins { @@ -14,18 +16,103 @@ namespace Disco.Services.Plugins { public PluginManifest Manifest { get; set; } public Controller HostController { get; set; } + protected DiscoDataContext Database; + + public void OnActionExecuting() + { + this.Database = new DiscoDataContext(); + this.Database.Configuration.LazyLoadingEnabled = false; + } public abstract ActionResult ExecuteAction(string ActionName); public virtual void Dispose() { - // Nothing in Base Class + if (this.Database != null) + { + this.Database.Dispose(); + this.Database = null; + } } + public AuthorizationToken Authorization + { + get + { + return UserService.CurrentAuthorization; + } + } + + public User CurrentUser + { + get + { + return UserService.CurrentUser; + } + } + + #region Caching + + public void SetCacheability(TimeSpan CacheDuration) + { + var cache = this.HostController.Response.Cache; + cache.SetOmitVaryStar(true); + cache.SetExpires(DateTime.Now.Add(CacheDuration)); + cache.SetValidUntilExpires(true); + cache.SetCacheability(HttpCacheability.Private); + } + public void SetCacheabilityOff() + { + var cache = this.HostController.Response.Cache; + cache.SetExpires(DateTime.Now.AddDays(-1)); + cache.SetCacheability(HttpCacheability.NoCache); + } + + #endregion + #region Action Results #region Compiled View private static string[] _viewFileNames = new string[] { "cshtml" }; + + public ActionResult CompiledView(object Model, bool UseDiscoLayout) where ViewType : WebViewPage + { + string layoutPath = UseDiscoLayout ? "~/Views/Shared/_Layout.cshtml" : null; + + IView v = new PrecompiledMvcView(this.HostController.Request.Path, layoutPath, typeof(ViewType), false, _viewFileNames); + + if (Model != null) + this.HostController.ViewData.Model = Model; + + return new ViewResult { View = v, ViewData = this.HostController.ViewData, TempData = this.HostController.TempData }; + } + public ActionResult CompiledView(bool UseDiscoLayout) where ViewType : WebViewPage + { + return this.CompiledView(null, UseDiscoLayout); + } + public ActionResult CompiledView(object Model) where ViewType : WebViewPage + { + return this.CompiledView(Model, true); + } + public ActionResult CompiledView() where ViewType : WebViewPage + { + return this.CompiledView(false, true); + } + public ActionResult CompiledPartialView(object Model) where ViewType : WebViewPage + { + IView v = new PrecompiledMvcView(this.HostController.Request.Path, typeof(ViewType), false, _viewFileNames); + + if (Model != null) + this.HostController.ViewData.Model = Model; + + return new PartialViewResult { View = v, ViewData = this.HostController.ViewData, TempData = this.HostController.TempData }; + } + public ActionResult CompiledPartialView() where ViewType : WebViewPage + { + return this.CompiledView(); + } + + [Obsolete("Use Generic Methods")] public ActionResult CompiledView(Type CompiledViewType, object Model, bool UseDiscoLayout) { string layoutPath = UseDiscoLayout ? "~/Views/Shared/_Layout.cshtml" : null; @@ -37,19 +124,23 @@ namespace Disco.Services.Plugins return new ViewResult { View = v, ViewData = this.HostController.ViewData, TempData = this.HostController.TempData }; } + [Obsolete("Use Generic Methods")] public ActionResult CompiledView(Type CompiledViewType, bool UseDiscoLayout) { return this.CompiledView(CompiledViewType, null, UseDiscoLayout); } + [Obsolete("Use Generic Methods")] public ActionResult CompiledView(Type CompiledViewType, object Model) { return this.CompiledView(CompiledViewType, Model, true); } - public ActionResult CompiledView(Type CompiledViewType) + [Obsolete("Use Generic Methods")] + public ActionResult CompiledView(Type CompiledViewType) { return this.CompiledView(CompiledViewType, false, true); } - public ActionResult CompiledPartialView(Type PartialCompiledViewType, object Model) + [Obsolete("Use Generic Methods")] + public ActionResult CompiledPartialView(Type PartialCompiledViewType, object Model) { IView v = new PrecompiledMvcView(this.HostController.Request.Path, PartialCompiledViewType, false, _viewFileNames); @@ -58,7 +149,8 @@ namespace Disco.Services.Plugins return new PartialViewResult { View = v, ViewData = this.HostController.ViewData, TempData = this.HostController.TempData }; } - public ActionResult CompiledPartialView(Type PartialCompiledViewType) + [Obsolete("Use Generic Methods")] + public ActionResult CompiledPartialView(Type PartialCompiledViewType) { return this.CompiledView(PartialCompiledViewType, null); } diff --git a/Disco.Services/Plugins/PluginWebHandlerController.cs b/Disco.Services/Plugins/PluginWebHandlerController.cs index 3e822c57..15ba6695 100644 --- a/Disco.Services/Plugins/PluginWebHandlerController.cs +++ b/Disco.Services/Plugins/PluginWebHandlerController.cs @@ -1,4 +1,5 @@ -using System; +using Disco.Services.Authorization; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -19,6 +20,14 @@ namespace Disco.Services.Plugins if (methodDescriptor == null) return this.HttpNotFound("Unknown Plugin Method"); + // Authorize Method + if (methodDescriptor.Authorizers.Length > 0) + { + var attributeDenied = methodDescriptor.Authorizers.FirstOrDefault(a => !a.IsAuthorized(HostController.HttpContext)); + if (attributeDenied != null) + return new HttpUnauthorizedResult(attributeDenied.HandleUnauthorizedMessage()); + } + var methodParams = BuildMethodParameters(handlerType, methodDescriptor.MethodInfo, ActionName, this.HostController); return (ActionResult)methodDescriptor.MethodInfo.Invoke(this, methodParams); @@ -63,24 +72,6 @@ namespace Disco.Services.Plugins parameterValue = methodParam.DefaultValue; result[i] = parameterValue; - - //var paramInstance = Activator.CreateInstance(methodParam.ParameterType); - - //IModelBinder binder = ModelBinders.Binders.GetBinder(methodParam.ParameterType); - //ModelBindingContext bindingContext = new ModelBindingContext - //{ - // ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => paramInstance, methodParam.ParameterType), - // ModelName = methodParam.Name, - // ModelState = HostController.ModelState, - // PropertyFilter = (p) => true, - // ValueProvider = HostController.ValueProvider - //}; - //binder.BindModel(HostController.ControllerContext, bindingContext); - - //if (methodParam.HasDefaultValue && paramInstance == null) - // paramInstance = methodParam.DefaultValue; - - //result[i] = paramInstance; } return result; @@ -102,7 +93,8 @@ namespace Disco.Services.Plugins var item = new WebHandlerCachedItem() { Method = method.Name, - MethodInfo = method + MethodInfo = method, + Authorizers = method.GetCustomAttributes().ToArray() }; result.Add(item.Method.ToLower(), item); } @@ -115,6 +107,7 @@ namespace Disco.Services.Plugins { public string Method { get; set; } public MethodInfo MethodInfo { get; set; } + public DiscoAuthorizeBaseAttribute[] Authorizers { get; set; } } #endregion } diff --git a/Disco.Services/Plugins/PluginWebViewPage.cs b/Disco.Services/Plugins/PluginWebViewPage.cs new file mode 100644 index 00000000..de11c634 --- /dev/null +++ b/Disco.Services/Plugins/PluginWebViewPage.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.Services.Plugins +{ + public abstract class PluginWebViewPage : Disco.Services.Web.WebViewPage + { + public WebHelper Plugin { get; private set; } + + public PluginWebViewPage() + { + var self = this.GetType(); + var manifest = Plugins.GetPlugin(self.Assembly); + + this.Plugin = new WebHelper(this, manifest); + } + } +} diff --git a/Disco.Services/Plugins/WebHelper.cs b/Disco.Services/Plugins/WebHelper.cs new file mode 100644 index 00000000..8834bb91 --- /dev/null +++ b/Disco.Services/Plugins/WebHelper.cs @@ -0,0 +1,184 @@ +using Disco.Services.Web.Bundles; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using System.Web.Mvc.Html; +using System.Web.Routing; +using System.Web.WebPages; + +namespace Disco.Services.Plugins +{ + public class WebHelper + { + private WebViewPage ViewPage { get; set; } + public PluginManifest Manifest { get; private set; } + + public WebHelper(WebViewPage ViewPage, PluginManifest manifest) + { + this.ViewPage = ViewPage; + this.Manifest = manifest; + } + + #region Html Helpers + + #region Form Helpers + public MvcForm BeginForm(string Action, FormMethod Method, bool MultipartEncoding, IDictionary HtmlAttributes) + { + if (string.IsNullOrEmpty(Action)) + throw new ArgumentNullException("PluginAction"); + + var url = ActionUrl(Action); + + return BeginForm_Helper(url.ToString(), Method, MultipartEncoding, HtmlAttributes); + } + public MvcForm BeginForm(string Action, FormMethod Method, bool MultipartEncoding) + { + return BeginForm(Action, Method, MultipartEncoding, null); + } + public MvcForm BeginForm(string Action, FormMethod Method, IDictionary HtmlAttributes) + { + return BeginForm(Action, Method, false, HtmlAttributes); + } + public MvcForm BeginForm(string Action, FormMethod Method) + { + return BeginForm(Action, Method, false, null); + } + public MvcForm BeginForm(string Action) + { + return BeginForm(Action, FormMethod.Get, false, null); + } + private MvcForm BeginForm_Helper(string FormAction, FormMethod Method, bool MultipartEncoding, IDictionary HtmlAttributes) + { + TagBuilder builder = new TagBuilder("form"); + builder.MergeAttributes(HtmlAttributes); + if (MultipartEncoding) + builder.MergeAttribute("enctype", "multipart/form-data"); + builder.MergeAttribute("action", FormAction); + builder.MergeAttribute("method", HtmlHelper.GetFormMethodString(Method), true); + + bool useClientValidation = ViewPage.ViewContext.ClientValidationEnabled && !ViewPage.ViewContext.UnobtrusiveJavaScriptEnabled; + if (useClientValidation) + { + object lastFormNumber = ViewPage.ViewContext.HttpContext.Items["DiscoPluginLastFormNum"]; + int num = (lastFormNumber != null) ? (((int)lastFormNumber) + 1) : 1000; + ViewPage.ViewContext.HttpContext.Items["DiscoPluginLastFormNum"] = num; + + builder.GenerateId(string.Format(CultureInfo.InvariantCulture, "form{0}", new object[] { num })); + } + ViewPage.ViewContext.Writer.Write(builder.ToString(TagRenderMode.StartTag)); + MvcForm form = new MvcForm(ViewPage.ViewContext); + if (useClientValidation) + { + ViewPage.ViewContext.FormContext.FormId = builder.Attributes["id"]; + } + return form; + } + #endregion + + public void IncludeStyleSheet(string Resource) + { + var url = ResourceUrl(Resource); + + var deferredBundles = ViewPage.Context.Items[Bundle.UIExtensionCssKey] as List; + if (deferredBundles == null) + { + deferredBundles = new List(); + ViewPage.Context.Items[Bundle.UIExtensionCssKey] = deferredBundles; + } + if (!deferredBundles.Contains(url)) + deferredBundles.Add(url); + } + public void IncludeJavaScript(string Resource) + { + var url = ResourceUrl(Resource); + + var deferredBundles = ViewPage.Context.Items[Bundle.UIExtensionScriptsKey] as List; + if (deferredBundles == null) + { + deferredBundles = new List(); + ViewPage.Context.Items[Bundle.UIExtensionScriptsKey] = deferredBundles; + } + if (!deferredBundles.Contains(url)) + deferredBundles.Add(url); + } + + public HtmlString PartialCompiled(object Model) where ViewType : WebViewPage + { + using (System.IO.StringWriter writer = new StringWriter(CultureInfo.CurrentCulture)) + { + RenderPartialCompiled(writer, Model); + + return new HtmlString(writer.ToString()); + } + } + public HtmlString PartialCompiled() where ViewType : WebViewPage + { + return PartialCompiled(null); + } + private void RenderPartialCompiled(TextWriter Writer, object Model) + { + if (Writer == null) + throw new ArgumentNullException("Writer"); + WebViewPage page = Activator.CreateInstance(typeof(ViewType)) as WebViewPage; + if (page == null) + throw new InvalidOperationException("Invalid View Type"); + page.ViewContext = ViewPage.ViewContext; + page.ViewData = new ViewDataDictionary(Model); + page.InitHelpers(); + HttpContextBase httpContext = ViewPage.ViewContext.HttpContext; + page.ExecutePageHierarchy(new WebPageContext(httpContext, null, Model), Writer, null); + } + + #endregion + + #region Urls + + public HtmlString ConfigurationUrl() + { + var url = GenerateUrl("Config_Plugins_Configure", new Dictionary() { { "PluginId", Manifest.Id } }); + + return new HtmlString(url); + } + + public HtmlString ActionUrl(string Action) + { + var url = GenerateUrl("Plugin", new Dictionary() { { "PluginId", Manifest.Id }, { "PluginAction", Action } }); + + return new HtmlString(url); + } + + public HtmlString ResourceUrl(string Resource) + { + return ResourceUrl(Resource, false); + } + + public HtmlString ResourceUrl(string Resource, bool Download) + { + var url = Manifest.WebResourceUrl(Resource); + + if (Download) + url += "&Download=true"; + + return new HtmlString(url); + } + + #endregion + + #region Helpers + private string GenerateUrl(string RouteName, RouteValueDictionary RouteValues) + { + return UrlHelper.GenerateUrl(RouteName, null, null, RouteValues, RouteTable.Routes, ViewPage.Request.RequestContext, false); + } + private string GenerateUrl(string RouteName, IDictionary RouteValues) + { + return GenerateUrl(RouteName, new RouteValueDictionary(RouteValues)); + } + #endregion + } +} diff --git a/Disco.Services/Web/Bundles/Bundle.cs b/Disco.Services/Web/Bundles/Bundle.cs index f9f1738a..9afb511b 100644 --- a/Disco.Services/Web/Bundles/Bundle.cs +++ b/Disco.Services/Web/Bundles/Bundle.cs @@ -11,6 +11,10 @@ namespace Disco.Services.Web.Bundles { public class Bundle { + public const string DeferredKey = "Bundles.Deferred"; + public const string UIExtensionScriptsKey = "Bundles.UIExtensionScripts"; + public const string UIExtensionCssKey = "Bundles.UIExtensionCss"; + private DateTime? _FileLastModified { get; set; } private string _FileHash { get; set; } private string _VersionUrl { get; set; } diff --git a/Disco.Services/Web/Bundles/BundleExtensions.cs b/Disco.Services/Web/Bundles/BundleExtensions.cs index 8f16d940..9174039a 100644 --- a/Disco.Services/Web/Bundles/BundleExtensions.cs +++ b/Disco.Services/Web/Bundles/BundleExtensions.cs @@ -16,21 +16,21 @@ namespace Disco.Services.Web // Ensure 'App-Relative' Url: BundleUrl = BundleUrl.StartsWith("~/") ? BundleUrl : (BundleUrl.StartsWith("/") ? string.Concat("~", BundleUrl) : string.Concat("~/", BundleUrl)); - var deferredBundles = htmlHelper.ViewContext.HttpContext.Items["Bundles.Deferred"] as List; + var deferredBundles = htmlHelper.ViewContext.HttpContext.Items[Bundle.DeferredKey] as List; if (deferredBundles == null) { deferredBundles = new List(); - htmlHelper.ViewContext.HttpContext.Items["Bundles.Deferred"] = deferredBundles; + htmlHelper.ViewContext.HttpContext.Items[Bundle.DeferredKey] = deferredBundles; } if (!deferredBundles.Contains(BundleUrl)) deferredBundles.Add(BundleUrl); } public static HtmlString BundleRenderDeferred(this HtmlHelper htmlHelper) { - var deferredBundles = htmlHelper.ViewContext.HttpContext.Items["Bundles.Deferred"] as List; + var deferredBundles = htmlHelper.ViewContext.HttpContext.Items[Bundle.DeferredKey] as List; - var uiExtensionScripts = htmlHelper.ViewContext.HttpContext.Items["Bundles.UIExtensionScripts"] as List; - var uiExtensionCss = htmlHelper.ViewContext.HttpContext.Items["Bundles.UIExtensionCss"] as List; + var uiExtensionScripts = htmlHelper.ViewContext.HttpContext.Items[Bundle.UIExtensionScriptsKey] as List; + var uiExtensionCss = htmlHelper.ViewContext.HttpContext.Items[Bundle.UIExtensionCssKey] as List; if (deferredBundles != null || uiExtensionScripts != null || uiExtensionCss != null) { diff --git a/Disco.Web.Extensions/MvcExtensions/PartialCompiled/PartialCompiledHtmlExtensions.cs b/Disco.Web.Extensions/MvcExtensions/PartialCompiled/PartialCompiledHtmlExtensions.cs index eec594e5..9393a436 100644 --- a/Disco.Web.Extensions/MvcExtensions/PartialCompiled/PartialCompiledHtmlExtensions.cs +++ b/Disco.Web.Extensions/MvcExtensions/PartialCompiled/PartialCompiledHtmlExtensions.cs @@ -34,16 +34,16 @@ namespace Disco.Web.Extensions { htmlHelper.RenderPartialCompiledInternal(viewType, model, htmlHelper.ViewContext.Writer); } - public static MvcHtmlString PartialCompiled(this HtmlHelper htmlHelper, Type viewType) + public static HtmlString PartialCompiled(this HtmlHelper htmlHelper, Type viewType) { return PartialCompiled(htmlHelper, viewType, null); } - public static MvcHtmlString PartialCompiled(this HtmlHelper htmlHelper, Type viewType, object model) + public static HtmlString PartialCompiled(this HtmlHelper htmlHelper, Type viewType, object model) { using (StringWriter writer = new StringWriter(CultureInfo.CurrentCulture)) { htmlHelper.RenderPartialCompiledInternal(viewType, model, writer); - return MvcHtmlString.Create(writer.ToString()); + return new HtmlString(writer.ToString()); } } #endregion diff --git a/Disco.Web/ClientSource/Style/User.css b/Disco.Web/ClientSource/Style/User.css index bf8ce5d6..962be4e6 100644 --- a/Disco.Web/ClientSource/Style/User.css +++ b/Disco.Web/ClientSource/Style/User.css @@ -213,6 +213,12 @@ #UserDetailTab-Authorization .fancytree-container { border: none; } +#UserDetailTab-Authorization span.fancytree-ico-c > span.fancytree-icon { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACgklEQVQ4y5XSXUhTYRgH8OkItViSri676CIIupC6kIjIrQTNtLbhMEOLmjGsi1CiJoULZmEpW4l5RDehkR/owJNF+3DLliMd6GDWcrFNyNEceAarZZuc7d8xSNQzgx54Lt6v3/vxvBwAnI3Z0NDAJQiifWJiImgwGOIkScasVuuMWq2u3Dp3LTc15HJ5Zk9PzzhFURh79fpTr7avv4voHns/ORkJh8NQqVTX/wmIxWLZW5sNBNGt3tgvrareYzQavVqt9nt5eXnetkCTosk4OkrGWh485G7dqfXR4yvMlSCVSs9sC3z2frFOTU0vp7ur2TJe4XA4oNFoKrcFZl0us9lsodIBk44PpSaTCXa7vSwtoNPpAsxqOJ1OBAIBOhKJ0PF4nE6lUjQzTq+srKR8Ph/m5uaSS0tLLSygsbFx1ev1IhgMwuPxIBQKIRaLIZlM4m+4XC4olUp0dna+YwEKhSLh9/uxuLgIt9v9B4pGo6Bpeh1YOx1TSuj1+nEWcPfe/cSsy4fgtzB8vgX4F34wADbF/Pw81JoOGEgbG3jeVppY9jVD2XwbN+ol+BURwdBfC1ldPaL+ywi5ZTgvuYibshJQ5nNsQFxWmHCTRzHWvR88XjaeKrmgPnJw8AAPFcKdzANkQCrIQn4+H3fEWWyg4MixuOD4IdDTeWiTZyB3Nw+Wrkx4BjOxj5+LW1XZwDAHJw/vAjdnr5kNFBSsSipr0asqwcKbQlyqLkH91bOgZk6DaKlgfuAFzOpOwN5xCnw+37kOFBcX7ygqKrpWU1P7kylPqrXtGdqf6DAwMIjhkZcYGrHAZLbBYrGhT0/ixdBoSiQSfRUKhRKBQJDDSffr/id/A3fSz48XbZuBAAAAAElFTkSuQmCC') /*Images/Actions/locked.png*/; +} +#UserDetailTab-Authorization span.fancytree-ico-c.fancytree-selected > span.fancytree-icon { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACWUlEQVQ4y6XRXWiSURgHcJsXa4WNNuuyiy6CoAupixERoXXhmljuxaJiFrVA1i72cVFCOSMt8rNt2YfGO5g5Z1NstWW+c4ZBq4QpqMkEbZDSCObAMprjdf90sIjxsgUdODd/zvmd5zwPCwDrf/aGB7q6utgmk8ngdruzVqt10eVyTWu1Wuk/AXK5vMpoNPpjsRgGbU8/9fbdH/J4PAuRSARKpfLKhoBYLG595nTCaDSZVjPp6TPbHQ5H0mAwfBeJRHXrAp0dna9JcqCguX2H/Xd+S625aLFYQBDE8XWBd+8/TI6Njc+vzcfGX4nLX4FOp5OuC0wGAlS53NzaPPAm2Gi32+H3+5tYJEl+pigKoVAIPp+PnpqaosPhMF1uHB2Px2mv14vya6VgMKhhHGN3d/dSMplENptFIpHA3NwcCoUCSqUSKqvScZVKBbPZHGQEFApFMZ1OI5PJIBqNrkD5fB40Ta8AlcrUajVsNpufEbh+42YxHEkh+/UbUqlZpGd/lAH8WTMzMzDd64d7NMAMDOobi/OpHqh6rqK9jcCvBQncQzK0Xm5DPn0BJ4lz6GgVIkedYAaamxqK0dEDePl4FziczehTsZGLs7BnNwdiwRac4lejvp6La83VzABv/8FF/qG9oD/WQS/fhNptHEw8rEJiuAo7ubXACAtH9m0Fu2YHxQzweEuEVIYnaiFmvQ04f1aItksi5KaP4ZFGjDB5GG/7j4LL5YYYgZYW2c/yiJbv6h/A0EvC4RjGiOsFnK4J+KgABmyjsDufL0skki8CgYCoXOLz+TWrwG+kXMkgQ6yv+QAAAABJRU5ErkJggg==') /*Images/Actions/unlocked.png*/; +} #UserDetailTab-Authorization span.fancytree-node.fancytree-selected { font-style: normal; } diff --git a/Disco.Web/ClientSource/Style/User.less b/Disco.Web/ClientSource/Style/User.less index 4e0676fe..ad2d3802 100644 --- a/Disco.Web/ClientSource/Style/User.less +++ b/Disco.Web/ClientSource/Style/User.less @@ -182,6 +182,14 @@ border: none; } + span.fancytree-ico-c > span.fancytree-icon { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACgklEQVQ4y5XSXUhTYRgH8OkItViSri676CIIupC6kIjIrQTNtLbhMEOLmjGsi1CiJoULZmEpW4l5RDehkR/owJNF+3DLliMd6GDWcrFNyNEceAarZZuc7d8xSNQzgx54Lt6v3/vxvBwAnI3Z0NDAJQiifWJiImgwGOIkScasVuuMWq2u3Dp3LTc15HJ5Zk9PzzhFURh79fpTr7avv4voHns/ORkJh8NQqVTX/wmIxWLZW5sNBNGt3tgvrareYzQavVqt9nt5eXnetkCTosk4OkrGWh485G7dqfXR4yvMlSCVSs9sC3z2frFOTU0vp7ur2TJe4XA4oNFoKrcFZl0us9lsodIBk44PpSaTCXa7vSwtoNPpAsxqOJ1OBAIBOhKJ0PF4nE6lUjQzTq+srKR8Ph/m5uaSS0tLLSygsbFx1ev1IhgMwuPxIBQKIRaLIZlM4m+4XC4olUp0dna+YwEKhSLh9/uxuLgIt9v9B4pGo6Bpeh1YOx1TSuj1+nEWcPfe/cSsy4fgtzB8vgX4F34wADbF/Pw81JoOGEgbG3jeVppY9jVD2XwbN+ol+BURwdBfC1ldPaL+ywi5ZTgvuYibshJQ5nNsQFxWmHCTRzHWvR88XjaeKrmgPnJw8AAPFcKdzANkQCrIQn4+H3fEWWyg4MixuOD4IdDTeWiTZyB3Nw+Wrkx4BjOxj5+LW1XZwDAHJw/vAjdnr5kNFBSsSipr0asqwcKbQlyqLkH91bOgZk6DaKlgfuAFzOpOwN5xCnw+37kOFBcX7ygqKrpWU1P7kylPqrXtGdqf6DAwMIjhkZcYGrHAZLbBYrGhT0/ixdBoSiQSfRUKhRKBQJDDSffr/id/A3fSz48XbZuBAAAAAElFTkSuQmCC') /*Images/Actions/locked.png*/; + } + + span.fancytree-ico-c.fancytree-selected > span.fancytree-icon { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACWUlEQVQ4y6XRXWiSURgHcJsXa4WNNuuyiy6CoAupixERoXXhmljuxaJiFrVA1i72cVFCOSMt8rNt2YfGO5g5Z1NstWW+c4ZBq4QpqMkEbZDSCObAMprjdf90sIjxsgUdODd/zvmd5zwPCwDrf/aGB7q6utgmk8ngdruzVqt10eVyTWu1Wuk/AXK5vMpoNPpjsRgGbU8/9fbdH/J4PAuRSARKpfLKhoBYLG595nTCaDSZVjPp6TPbHQ5H0mAwfBeJRHXrAp0dna9JcqCguX2H/Xd+S625aLFYQBDE8XWBd+8/TI6Njc+vzcfGX4nLX4FOp5OuC0wGAlS53NzaPPAm2Gi32+H3+5tYJEl+pigKoVAIPp+PnpqaosPhMF1uHB2Px2mv14vya6VgMKhhHGN3d/dSMplENptFIpHA3NwcCoUCSqUSKqvScZVKBbPZHGQEFApFMZ1OI5PJIBqNrkD5fB40Ta8AlcrUajVsNpufEbh+42YxHEkh+/UbUqlZpGd/lAH8WTMzMzDd64d7NMAMDOobi/OpHqh6rqK9jcCvBQncQzK0Xm5DPn0BJ4lz6GgVIkedYAaamxqK0dEDePl4FziczehTsZGLs7BnNwdiwRac4lejvp6La83VzABv/8FF/qG9oD/WQS/fhNptHEw8rEJiuAo7ubXACAtH9m0Fu2YHxQzweEuEVIYnaiFmvQ04f1aItksi5KaP4ZFGjDB5GG/7j4LL5YYYgZYW2c/yiJbv6h/A0EvC4RjGiOsFnK4J+KgABmyjsDufL0skki8CgYCoXOLz+TWrwG+kXMkgQ6yv+QAAAABJRU5ErkJggg==') /*Images/Actions/unlocked.png*/; + } + span.fancytree-node.fancytree-selected { font-style: normal; } diff --git a/Disco.Web/ClientSource/Style/User.min.css b/Disco.Web/ClientSource/Style/User.min.css index 2130f5b3..b3c4240b 100644 --- a/Disco.Web/ClientSource/Style/User.min.css +++ b/Disco.Web/ClientSource/Style/User.min.css @@ -1 +1 @@ -.tableData{border:solid 1px #e8eef4;border-collapse:collapse}.tableData>tbody>tr>td{border:solid 1px #e8eef4;background-color:#fff}.tableData>tbody>tr:nth-child(odd)>td{background-color:#fcfcfd}.tableData>thead>tr>th,.tableData>tbody>tr>th{background-color:#e8eef4;border:solid 1px #e8eef4}.tableData>tbody>tr:hover>td{background-color:#e8eef4}.tableData>tfoot>tr>th,.tableData>tfoot>tr>td{background-color:#e8eef4}.tableDataDark{border:solid 1px #8db2d8;border-collapse:collapse}.tableDataDark td{border:solid 1px #8db2d8;background-color:#fff}.tableDataDark th{background-color:#8db2d8;border:solid 1px #8db2d8}.tableDataContainer{background-color:#fff}.tableDataVertical{border:solid 1px #e8eef4;border-collapse:collapse}.tableDataVertical>tbody>tr:nth-child(odd){background-color:#e8eef4;margin:0;padding:0}.tableDataVertical>tbody>tr>th.name{width:170px;text-align:right}.tableDataVertical table.sub>tbody>tr:not(:first-child)>th,.tableDataVertical table.sub>tbody>tr:not(:first-child)>td{border-top:1px dashed #aaa}.tableDataVertical table.sub>tbody>tr>th{font-weight:normal;text-align:right}.tableDataVertical table.sub>tbody>tr>th.name{text-align:right}.icon16{display:inline-block;height:16px;width:16px;margin-left:2px;cursor:pointer}.subtleUntilHover{-moz-opacity:.3;opacity:.3}.subtleUntilHover:hover{-moz-opacity:1;opacity:1}#User_Show #User_Show_Subjects{table-layout:fixed}#User_Show #User_Show_Subjects>tbody>tr>td{padding-top:0;height:100%}#User_Show #User_Show_Subjects>tbody>tr>td>div{position:relative}#User_Show #User_Show_Subjects>tbody>tr>td>div div.status{margin-top:2px;padding-top:2px;border-top:1px dashed #ddd}#User_Show #User_Show_Subjects>tbody>tr>td>div input.discreet{margin-left:-2px}#User_Show #User_Show_Subjects>tbody>tr>td:not(:last-child){border-right:1px dashed #aaa}#User_Show #User_Show_Subjects .dialog{display:none}#User_Show #User_Show_Subjects #User_Show_Details{width:330px}#User_Show #User_Show_Subjects #User_Show_Details table.verticalHeadings>tbody>tr>td:first-child{width:104px;font-weight:bold}#User_Show #User_Show_Subjects #User_Show_Details #User_Show_Details_Identity_Id{font-weight:bold}#User_Show #User_Show_Subjects #User_Show_Details #User_Show_GenerateDocument_Container{padding-top:4px}#User_Show #User_Show_Subjects #User_Show_Details #User_Show_GenerateDocument_Container #User_Show_GenerateDocument{padding:0}#User_Show #User_Show_Subjects #User_Show_Details #User_Show_Details_Actions{margin-top:4px}#User_Show #User_Show_Subjects #User_Show_AssignedDevices .User_Show_AssignedDevices_CurrentAssignment{border-bottom:1px dashed #ddd;padding:4px}#User_Show #User_Show_Subjects #User_Show_AssignedDevices .User_Show_AssignedDevices_CurrentAssignment td:first-child{width:90px;font-weight:bold}#User_Show #User_Show_Subjects #User_Show_AssignedDevices .User_Show_AssignedDevices_CurrentAssignment img.User_Show_AssignedDevices_CurrentAssignment_Image{float:left;width:64px;height:64px;margin-right:6px}#User_Show #User_Show_Subjects #User_Show_AssignedDevices .User_Show_AssignedDevices_CurrentAssignment div.User_Show_AssignedDevices_CurrentAssignment_Details{float:left}#User_Show #User_Show_Subjects #User_Show_Subjects_Actions>td{padding-top:4px}#UserDetailTabs{margin-top:10px;border-radius:0;background-image:none;background-color:#fff;border:none;padding:0}#UserDetailTabs #UserDetailTabItems{border-radius:0;border-top:1px solid #ddd;border-right:1px solid #ddd;border-left:1px solid #ddd;border-bottom:none;padding:2px 0 0 4px;background-image:none;background-color:#eee;display:table}#UserDetailTabs #UserDetailTabItems>li{top:0;border-radius:0;margin:0 5px 0 0;padding:0;line-height:normal;margin-right:4px}#UserDetailTabs #UserDetailTabItems>li>a{padding:5px 8px}#UserDetailTabs div.ui-tabs-panel{border-radius:0;padding:4px;border-right:1px solid #ddd;border-bottom:1px solid #ddd;border-left:1px solid #ddd;border-top:none;background-color:#eee}#UserDetailTab-JobsContainer div.jobTable{margin:-1px;border:1px solid #ddd}#UserDetailTab-JobsContainer .dataTables_wrapper .dataTables_filter{margin-top:-24px;-moz-opacity:1;opacity:1}#UserDetailTab-JobsContainer .dataTables_wrapper .dataTables_length{margin-top:-24px;-moz-opacity:1;opacity:1}#UserDetailTab-JobsContainer .dataTables_wrapper .dataTables_showStatusClosed{right:220px;margin-top:-24px}#UserDetailTab-Authorization #UserDetailTab-AuthorizationContainer{background-color:#fff;border:1px solid #ccc}#UserDetailTab-Authorization #UserDetailTab-Authorization_ClaimsTree_Container{width:50%;float:left;padding:6px 10px 6px 4px}#UserDetailTab-Authorization #UserDetailTab-Authorization_ClaimsTree_Container>span.smallMessage:last-child{display:block;text-align:right}#UserDetailTab-Authorization .fancytree-container{border:none}#UserDetailTab-Authorization span.fancytree-node.fancytree-selected{font-style:normal}#UserDetailTab-Authorization span.fancytree-checkbox{margin-left:3px;background-position:-96px -32px}#UserDetailTab-Authorization span.fancytree-checkbox:hover{background-position:-96px -32px}#UserDetailTab-Authorization .fancytree-partsel span.fancytree-checkbox{background-position:-128px -32px}#UserDetailTab-Authorization .fancytree-partsel span.fancytree-checkbox:hover{background-position:-128px -32px}#UserDetailTab-Authorization .fancytree-selected span.fancytree-checkbox{background-position:-112px -32px}#UserDetailTab-Authorization .fancytree-selected span.fancytree-checkbox:hover{background-position:-112px -32px}#UserDetailTab-Authorization #UserDetailTab-Authorization_Membership{width:40%;float:right;padding:6px 10px;border-left:1px dashed #ccc;border-bottom:1px dashed #ccc}#UserDetailTab-Authorization #UserDetailTab-Authorization_Membership #UserDetailTab-Authorization_Membership_Roles{margin-bottom:10px}#UserDetailTab-Authorization #UserDetailTab-Authorization_Membership #UserDetailTab-Authorization_Membership_Groups_Container>span.smallMessage:last-child{display:block;text-align:right}#UserDetailTab-Authorization #UserDetailTab-Authorization_NoAccess{width:50%;float:left;padding:6px 10px}#UserDetailTab-Authorization #UserDetailTab-Authorization_NoAccess h3{margin-bottom:10px}#userShowResources #Attachments{padding:0;border:1px solid #ccc;background-color:#fff}#userShowResources #Attachments div.attachmentOutput{height:115px;overflow:auto;font-size:.95em}#userShowResources #Attachments div.attachmentOutput>a{display:block;float:left;height:48px;width:221px;padding:2px;margin:2px;font-size:.9em;border:1px solid #fff;color:#000;text-decoration:none}#userShowResources #Attachments div.attachmentOutput>a span.comments,#userShowResources #Attachments div.attachmentOutput>a span.author,#userShowResources #Attachments div.attachmentOutput>a span.timestamp{display:block;float:left;width:168px;overflow:hidden;height:16px}#userShowResources #Attachments div.attachmentOutput>a span.author{color:#888;width:150px}#userShowResources #Attachments div.attachmentOutput>a span.timestamp{color:#888;font-style:italic}#userShowResources #Attachments div.attachmentOutput>a span.icon{display:block;float:left;height:48px;width:48px;margin-right:2px}#userShowResources #Attachments div.attachmentOutput>a span.icon img{height:48px;width:48px}#userShowResources #Attachments div.attachmentOutput>a:hover{background-color:#ededed;border:1px solid #ccc;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}#userShowResources #Attachments div.attachmentOutput>a:hover span.remove{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADHklEQVQ4T22SeUgUcRTHZ/aY2dnZLdfWI4KuP+zQsja7KMraXaMsOrUghe6DMjNXN7LlSyt2EHZAqbgdZFRQ/xR0QGVqZmi6h2mnVHYYBboVbgvS8evtSkHWF74w897nvTfv9xuO+48uRAyMrpqSnNSYssD6wJxqrUxISjonG6P7cv/otNZgfDR/cXrPwcO72dXrYK5TYKXlYMdLEUShw2uZu9wl6KP61oV1NiJmaFfGKhvLtYNtziJvA9u0FWzj1t73bTvAbHZ0LF2R55INw/8qPiboI9/PnpPH5i1C98RpaCP3TJ6On8kpYMlWfJthwSvLfAQnTQebtxBtU2fkH1JqjX8aVA0alvY13oQXsUPgWbkar5o8eEjgZ0MsvpAfWlPRTjFf5hq8jRmMwKhEXIkatCJcfICXjI8NsbsbVRJq4xIQCATAGIP/wwe4x09GU4IJne86wrFAMIgHI8fCp9KgQR/pcPKaaK5YKU24IehwjFPijCCiBXvwk+CQuzreo/Ptu/DzD/ITZxEuiRqcIPayQoMiXpzIOVWSuYSm2zkVijkFXBwHT64N/s4ufO7uDtvv98OXZ0cF5UqJ2UtsGS+gkBetnF0lWQrVEhZzPLZTsoigihHxeNbcjJevX+Nlezuet7bifMI4HKVcEU3fTJyDVyGfF1O4HKXGVCZosY4aZBNQPHoM6quq4aWi1qdPw/a2tKLhbi1KEidgFzFrid2nUCMntMIGTjCe1OgcRyhRNmQ4am7eRmVdHercHrizsuHZkoX7Hg9u1d6Fr9GLK4mTsJ/YEqXo2MgJvX/nLrWUVklrXJB1qN/jRLXbDU92DqoJrCF7qVENNWncfwAX+/XDNZpeoBB7rzGkTE4wuCS9rVkp4o5CgC/ZDB/t2UZ+QW6hT26ZZUYVXV8THWC5WsqnmgF/GoSUwQuDK7T9c98IMjpoalCtBYuMATMORI9Gj48Ua1dKqBBkWyYvDPur+LfSOXUkRN2yexFRBf64eHyflYLvMy34NGosGgwxBU5BTkvvO/l/WkIHu14lm3bqjGa7bDBvUMumpRTry4X0C+L3YvcBfxOhAAAAAElFTkSuQmCC)}#userShowResources #Attachments div.attachmentOutput>a span.remove{display:block;float:left;height:16px;width:16px;margin-left:2px}#userShowResources #Attachments div.attachmentOutput>a span.remove:hover{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADG0lEQVQ4y3WTCUhUURSG35vlvnnzZsoJQyMo2nTMKdsXEtOcjKxogwhKaDFNysysJooQRzTLbIFScVrIaMEiElqgMjUzsnEWl1apbNG0dCqcJiarv+NEgWEPfnj3nO/+5567cAC4f3XOb5ChfFrkemvMgn0PoufllhkmJZ2R/Mf0xfYanFTr9A/nL77ozT3oxZVrgOUEUFAEHC2AJz3zm8M495KFaUf3aXDaLyCqc+WqdqSZgKRk0iZg/UYgcePv8aYtwFYTWpYu/2CRdMZeBkeYdmTrrDnvEbsIXZNnoInknRqOn5ExQORsdEcY8dI4H54p4UDsQjRNj+g8IFfr/xqUDx5W8iV0Ap4HDoV9xWq8rLWjnsBPukB8JtXPnodmijnj1uBNwBC4Q8JQOnBwqc9gLy/qH+kCvVaFiKogA9xuN3o+V1sbbOOnotYwAR1vW3wxt8eDB/qxcCpUqNEO6DbzKgOXJxcTrjMNjnBynGICGtIz8JPgHnW2tKLjzVvf/w/SY3MWLggqHCP2skyFLF7YwJkV4p58qm7iFMjjZLBQV/a0rXB1dOJTV5dPLpcLzm0mFFOugJhsYgt5hkxeyOVMCjEnUyliMcdjMyWzCCoODsXTujq8ePUKL5qb8ayxEWcN43CYcllUPYm43bwC23lhP5cqV60rZGrEk0EKAXmjx+B+eQUcNKnxyROfHA2NqLlThfywidhJzFpi98iUSO1pIYFj+uMqzbdDlCgcOhyVN26hrLoa1TY7bMkpsG9Ixj27HTer7sBpdaA0bApyiM2XC92JHDP4jnGnUiwpozbOSRrczzCjwmaDPSUVFQRWkhxkVEkm1py9KOnXD1ep+i6ZcPnvPYjj2AiLqG2vkwu4LWNwRkbDSX02kZ6TGmjJDVHRKKfjq6UNLFKKHTQnqNdVXsmz8GJ1/3evmYQWCnuUamBAAOA/CF6VFu0Ua5aLKGZSexzPZvX5mJZxylHpgub8Xb+BX11BofgeFYPvM434GDIWNbqAr2YmlSzjWPB/X+MfLaGNXaeQ4ndo/LNNki47QSnFL+VYSF/sL7crTEWWnWvrAAAAAElFTkSuQmCC)}#userShowResources #Attachments.cannotAddAttachments div.attachmentOutput{height:162px}#userShowResources #Attachments div.attachmentInput{border-top:1px solid #ccc;height:40px;background-color:#fff;padding:3px}#userShowResources #Attachments div.attachmentInput span.action{display:block;margin:2px 4px 0 0;height:32px;width:32px;cursor:pointer;float:right;border:1px solid #fff;padding:3px;background-repeat:no-repeat;background-position:2px 3px}#userShowResources #Attachments div.attachmentInput span.action:hover{background-color:#ededed;border:1px solid #ccc;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}#userShowResources #Attachments div.attachmentInput span.upload{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAFPUlEQVRYw7VXayzkVxRfy4yKTj0aj6JWJISuR9EiWj5IFZt4pB+kLYmdbEJEJpp2daeIJkRNMx7xqFcl0iXxaKwVj8ishhG+eARr10wY6hVMx3gM1mNxeo7YRDfzNr3Jyfzn/u/939899/x+59xbAHDLEIbNxNHRMSQ5OTmvuLi4pbS0tDU1NbXA3d09FN8ZqZxniMVZLJZvSUnJ0NHREVA7PDwEhUIBb1pNTU2PtbW1+/8CwMvLiz07O/uKFqqrq9sNDAzstLCw4Jubm/Pd3NyeZGdnb56dncH09LQUQfgaFIC9vX30+vr6uUwmg5CQkD509Rdo75HL0W6jWaJF+/n5CRcXF0EgEEiMjIw+MAgAbM7d3d1/y6QywAU68b+NmrFeYWFhwtPTU8jIyKi/MQDaXU5OjoDcHhsb+4x2fdX/jqqAwxbf0dFxNj4+vofPtjcCgO7+gRbn8/lSS0vLjPT09KrOp09F/f39S9XV1QOhoaFsXITxFoD3cfcv8MgA2RKuNwAzM7MgsVh8MDAwAAkJCRI8230CIxKJYHh4GLa2ti4jv6CggI7F7BoABlJ0cHNzEzA4v9ELADar1tZWkVQqBS6Xe4G/F/Pz8xAdHT2KwZWN7zkODg7NVVVVr87PzyErK6vp2lxTnDO2vLwMOCZeLwBsNvu3K9fDxMQEjI2NgZ2dXSl+3OnaQtZo3Pr6+qNt+TZ4enreu+q/09nZuUdeQrA+OgNwdXX9en9/H5qbm+Hx48dAO/fw8PiddqbEUyyMg66VlRVg379fQ32+vr4Z5Lnc3Nzn/zkabRY3MTHxmJmZ2UExgfz8fKBAiomJ+Usd9fCcc+fm5gDluB3HOSLwtcnJSXB2dv5JJxoS5crLy58dHx9DXl4eLCwsAMquFPs/VjcvPDy8dHV1FeLi4v7E6O+V4e7j4+MHiQ06AYiIiPiRzh2TC/T19YFQKASUWo4G0GZlZWUvpqamyAOLBATnb2N/sE5S7OTk9KVcLn/d09MDlZWVl+fu4+PTSIKjbh7GRuqCZIGSEAwNDV2gPgDmgYc6J6PMzMxW0nDk9MXa2hqkpaW9vB7xyozBYNzt7e3dHRkZuQxYiURCUv2HKtBqzx75yk1MTFwmFzY1Nb1G+sRrcL1RFpcr2NzYgNraWlheWgIOhyOmvKFXPYDtM3S/ggLP1tb2F3WFBZm/v/93BLaxsRFGR0ehoaHhBEF/pXYNdRUOj8cTUgCi0hHlWGqLEgvW5+j2EwrS9vb2SwB29vY/a2SZqhcoJFxaHClH0fuppqjHceO0exKpJXQ96kQ/9r+rFwAmkxmMCeeIdoGUe6gFVX8lnpPrUbCAV8iT4eKfaJVflHUidxtp9ygiAk2Uo927uLhUFBUVnVDkd3V1nZuamj7QOsEpy3iYNJZINq2srBK0zJKeKDT/EGVR8yk/3L4JgLuDg4PHLS0tB/j8oTYfSUpKqtuWyyElJeWltnPUAfDHKD5FEaHgs9T0gYCAAA4lJ6yEdnF8hM7lnRIAd1D5VijfI/eDNVRH/pjfD0jzsS54pFd9qQQAMygoqJ3Eh1dYSKU2U0WKdseiQ0QlVmRkJI0zNwiAKxAxWD5t7ezsAGa1IaTiR9dLK6xyvu3q7pYeHhwAFqQktZ56l/cqAJgaGxt/z+cXHSr2FEA3n8qKihEsRnoxOCUbqPVkmOdXcGzoje6U6vhNRWZUVNTztra2szmxGIhmVAei6u17e3s/wfchN77UarrxknvRHuAFtMjGxqacwWQ8omjXlBsMAuAtMMYEyFDX+Tf2LzGXbu1DZYkMAAAAAElFTkSuQmCC)}#userShowResources #Attachments div.attachmentInput span.photo{background-image:url(data:image/png;base64,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)}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments{margin-top:6px;background-color:#fff;line-height:1.3em;border:1px solid #ddd}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment{display:block;padding:4px;cursor:pointer}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment:not(:last-child){border-bottom:1px dashed #ddd}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment:hover{background-color:#e8eef4}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment td:first-child{width:90px;font-weight:bold}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment img.CreateJob_Assignment_Image{float:left;width:64px;height:64px;margin-right:6px}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment div.CreateJob_Assignment_Details{float:left} \ No newline at end of file +.tableData{border:solid 1px #e8eef4;border-collapse:collapse}.tableData>tbody>tr>td{border:solid 1px #e8eef4;background-color:#fff}.tableData>tbody>tr:nth-child(odd)>td{background-color:#fcfcfd}.tableData>thead>tr>th,.tableData>tbody>tr>th{background-color:#e8eef4;border:solid 1px #e8eef4}.tableData>tbody>tr:hover>td{background-color:#e8eef4}.tableData>tfoot>tr>th,.tableData>tfoot>tr>td{background-color:#e8eef4}.tableDataDark{border:solid 1px #8db2d8;border-collapse:collapse}.tableDataDark td{border:solid 1px #8db2d8;background-color:#fff}.tableDataDark th{background-color:#8db2d8;border:solid 1px #8db2d8}.tableDataContainer{background-color:#fff}.tableDataVertical{border:solid 1px #e8eef4;border-collapse:collapse}.tableDataVertical>tbody>tr:nth-child(odd){background-color:#e8eef4;margin:0;padding:0}.tableDataVertical>tbody>tr>th.name{width:170px;text-align:right}.tableDataVertical table.sub>tbody>tr:not(:first-child)>th,.tableDataVertical table.sub>tbody>tr:not(:first-child)>td{border-top:1px dashed #aaa}.tableDataVertical table.sub>tbody>tr>th{font-weight:normal;text-align:right}.tableDataVertical table.sub>tbody>tr>th.name{text-align:right}.icon16{display:inline-block;height:16px;width:16px;margin-left:2px;cursor:pointer}.subtleUntilHover{-moz-opacity:.3;opacity:.3}.subtleUntilHover:hover{-moz-opacity:1;opacity:1}#User_Show #User_Show_Subjects{table-layout:fixed}#User_Show #User_Show_Subjects>tbody>tr>td{padding-top:0;height:100%}#User_Show #User_Show_Subjects>tbody>tr>td>div{position:relative}#User_Show #User_Show_Subjects>tbody>tr>td>div div.status{margin-top:2px;padding-top:2px;border-top:1px dashed #ddd}#User_Show #User_Show_Subjects>tbody>tr>td>div input.discreet{margin-left:-2px}#User_Show #User_Show_Subjects>tbody>tr>td:not(:last-child){border-right:1px dashed #aaa}#User_Show #User_Show_Subjects .dialog{display:none}#User_Show #User_Show_Subjects #User_Show_Details{width:330px}#User_Show #User_Show_Subjects #User_Show_Details table.verticalHeadings>tbody>tr>td:first-child{width:104px;font-weight:bold}#User_Show #User_Show_Subjects #User_Show_Details #User_Show_Details_Identity_Id{font-weight:bold}#User_Show #User_Show_Subjects #User_Show_Details #User_Show_GenerateDocument_Container{padding-top:4px}#User_Show #User_Show_Subjects #User_Show_Details #User_Show_GenerateDocument_Container #User_Show_GenerateDocument{padding:0}#User_Show #User_Show_Subjects #User_Show_Details #User_Show_Details_Actions{margin-top:4px}#User_Show #User_Show_Subjects #User_Show_AssignedDevices .User_Show_AssignedDevices_CurrentAssignment{border-bottom:1px dashed #ddd;padding:4px}#User_Show #User_Show_Subjects #User_Show_AssignedDevices .User_Show_AssignedDevices_CurrentAssignment td:first-child{width:90px;font-weight:bold}#User_Show #User_Show_Subjects #User_Show_AssignedDevices .User_Show_AssignedDevices_CurrentAssignment img.User_Show_AssignedDevices_CurrentAssignment_Image{float:left;width:64px;height:64px;margin-right:6px}#User_Show #User_Show_Subjects #User_Show_AssignedDevices .User_Show_AssignedDevices_CurrentAssignment div.User_Show_AssignedDevices_CurrentAssignment_Details{float:left}#User_Show #User_Show_Subjects #User_Show_Subjects_Actions>td{padding-top:4px}#UserDetailTabs{margin-top:10px;border-radius:0;background-image:none;background-color:#fff;border:none;padding:0}#UserDetailTabs #UserDetailTabItems{border-radius:0;border-top:1px solid #ddd;border-right:1px solid #ddd;border-left:1px solid #ddd;border-bottom:none;padding:2px 0 0 4px;background-image:none;background-color:#eee;display:table}#UserDetailTabs #UserDetailTabItems>li{top:0;border-radius:0;margin:0 5px 0 0;padding:0;line-height:normal;margin-right:4px}#UserDetailTabs #UserDetailTabItems>li>a{padding:5px 8px}#UserDetailTabs div.ui-tabs-panel{border-radius:0;padding:4px;border-right:1px solid #ddd;border-bottom:1px solid #ddd;border-left:1px solid #ddd;border-top:none;background-color:#eee}#UserDetailTab-JobsContainer div.jobTable{margin:-1px;border:1px solid #ddd}#UserDetailTab-JobsContainer .dataTables_wrapper .dataTables_filter{margin-top:-24px;-moz-opacity:1;opacity:1}#UserDetailTab-JobsContainer .dataTables_wrapper .dataTables_length{margin-top:-24px;-moz-opacity:1;opacity:1}#UserDetailTab-JobsContainer .dataTables_wrapper .dataTables_showStatusClosed{right:220px;margin-top:-24px}#UserDetailTab-Authorization #UserDetailTab-AuthorizationContainer{background-color:#fff;border:1px solid #ccc}#UserDetailTab-Authorization #UserDetailTab-Authorization_ClaimsTree_Container{width:50%;float:left;padding:6px 10px 6px 4px}#UserDetailTab-Authorization #UserDetailTab-Authorization_ClaimsTree_Container>span.smallMessage:last-child{display:block;text-align:right}#UserDetailTab-Authorization .fancytree-container{border:none}#UserDetailTab-Authorization span.fancytree-ico-c>span.fancytree-icon{background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACgklEQVQ4y5XSXUhTYRgH8OkItViSri676CIIupC6kIjIrQTNtLbhMEOLmjGsi1CiJoULZmEpW4l5RDehkR/owJNF+3DLliMd6GDWcrFNyNEceAarZZuc7d8xSNQzgx54Lt6v3/vxvBwAnI3Z0NDAJQiifWJiImgwGOIkScasVuuMWq2u3Dp3LTc15HJ5Zk9PzzhFURh79fpTr7avv4voHns/ORkJh8NQqVTX/wmIxWLZW5sNBNGt3tgvrareYzQavVqt9nt5eXnetkCTosk4OkrGWh485G7dqfXR4yvMlSCVSs9sC3z2frFOTU0vp7ur2TJe4XA4oNFoKrcFZl0us9lsodIBk44PpSaTCXa7vSwtoNPpAsxqOJ1OBAIBOhKJ0PF4nE6lUjQzTq+srKR8Ph/m5uaSS0tLLSygsbFx1ev1IhgMwuPxIBQKIRaLIZlM4m+4XC4olUp0dna+YwEKhSLh9/uxuLgIt9v9B4pGo6Bpeh1YOx1TSuj1+nEWcPfe/cSsy4fgtzB8vgX4F34wADbF/Pw81JoOGEgbG3jeVppY9jVD2XwbN+ol+BURwdBfC1ldPaL+ywi5ZTgvuYibshJQ5nNsQFxWmHCTRzHWvR88XjaeKrmgPnJw8AAPFcKdzANkQCrIQn4+H3fEWWyg4MixuOD4IdDTeWiTZyB3Nw+Wrkx4BjOxj5+LW1XZwDAHJw/vAjdnr5kNFBSsSipr0asqwcKbQlyqLkH91bOgZk6DaKlgfuAFzOpOwN5xCnw+37kOFBcX7ygqKrpWU1P7kylPqrXtGdqf6DAwMIjhkZcYGrHAZLbBYrGhT0/ixdBoSiQSfRUKhRKBQJDDSffr/id/A3fSz48XbZuBAAAAAElFTkSuQmCC')}#UserDetailTab-Authorization span.fancytree-ico-c.fancytree-selected>span.fancytree-icon{background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACWUlEQVQ4y6XRXWiSURgHcJsXa4WNNuuyiy6CoAupixERoXXhmljuxaJiFrVA1i72cVFCOSMt8rNt2YfGO5g5Z1NstWW+c4ZBq4QpqMkEbZDSCObAMprjdf90sIjxsgUdODd/zvmd5zwPCwDrf/aGB7q6utgmk8ngdruzVqt10eVyTWu1Wuk/AXK5vMpoNPpjsRgGbU8/9fbdH/J4PAuRSARKpfLKhoBYLG595nTCaDSZVjPp6TPbHQ5H0mAwfBeJRHXrAp0dna9JcqCguX2H/Xd+S625aLFYQBDE8XWBd+8/TI6Njc+vzcfGX4nLX4FOp5OuC0wGAlS53NzaPPAm2Gi32+H3+5tYJEl+pigKoVAIPp+PnpqaosPhMF1uHB2Px2mv14vya6VgMKhhHGN3d/dSMplENptFIpHA3NwcCoUCSqUSKqvScZVKBbPZHGQEFApFMZ1OI5PJIBqNrkD5fB40Ta8AlcrUajVsNpufEbh+42YxHEkh+/UbUqlZpGd/lAH8WTMzMzDd64d7NMAMDOobi/OpHqh6rqK9jcCvBQncQzK0Xm5DPn0BJ4lz6GgVIkedYAaamxqK0dEDePl4FziczehTsZGLs7BnNwdiwRac4lejvp6La83VzABv/8FF/qG9oD/WQS/fhNptHEw8rEJiuAo7ubXACAtH9m0Fu2YHxQzweEuEVIYnaiFmvQ04f1aItksi5KaP4ZFGjDB5GG/7j4LL5YYYgZYW2c/yiJbv6h/A0EvC4RjGiOsFnK4J+KgABmyjsDufL0skki8CgYCoXOLz+TWrwG+kXMkgQ6yv+QAAAABJRU5ErkJggg==')}#UserDetailTab-Authorization span.fancytree-node.fancytree-selected{font-style:normal}#UserDetailTab-Authorization span.fancytree-checkbox{margin-left:3px;background-position:-96px -32px}#UserDetailTab-Authorization span.fancytree-checkbox:hover{background-position:-96px -32px}#UserDetailTab-Authorization .fancytree-partsel span.fancytree-checkbox{background-position:-128px -32px}#UserDetailTab-Authorization .fancytree-partsel span.fancytree-checkbox:hover{background-position:-128px -32px}#UserDetailTab-Authorization .fancytree-selected span.fancytree-checkbox{background-position:-112px -32px}#UserDetailTab-Authorization .fancytree-selected span.fancytree-checkbox:hover{background-position:-112px -32px}#UserDetailTab-Authorization #UserDetailTab-Authorization_Membership{width:40%;float:right;padding:6px 10px;border-left:1px dashed #ccc;border-bottom:1px dashed #ccc}#UserDetailTab-Authorization #UserDetailTab-Authorization_Membership #UserDetailTab-Authorization_Membership_Roles{margin-bottom:10px}#UserDetailTab-Authorization #UserDetailTab-Authorization_Membership #UserDetailTab-Authorization_Membership_Groups_Container>span.smallMessage:last-child{display:block;text-align:right}#UserDetailTab-Authorization #UserDetailTab-Authorization_NoAccess{width:50%;float:left;padding:6px 10px}#UserDetailTab-Authorization #UserDetailTab-Authorization_NoAccess h3{margin-bottom:10px}#userShowResources #Attachments{padding:0;border:1px solid #ccc;background-color:#fff}#userShowResources #Attachments div.attachmentOutput{height:115px;overflow:auto;font-size:.95em}#userShowResources #Attachments div.attachmentOutput>a{display:block;float:left;height:48px;width:221px;padding:2px;margin:2px;font-size:.9em;border:1px solid #fff;color:#000;text-decoration:none}#userShowResources #Attachments div.attachmentOutput>a span.comments,#userShowResources #Attachments div.attachmentOutput>a span.author,#userShowResources #Attachments div.attachmentOutput>a span.timestamp{display:block;float:left;width:168px;overflow:hidden;height:16px}#userShowResources #Attachments div.attachmentOutput>a span.author{color:#888;width:150px}#userShowResources #Attachments div.attachmentOutput>a span.timestamp{color:#888;font-style:italic}#userShowResources #Attachments div.attachmentOutput>a span.icon{display:block;float:left;height:48px;width:48px;margin-right:2px}#userShowResources #Attachments div.attachmentOutput>a span.icon img{height:48px;width:48px}#userShowResources #Attachments div.attachmentOutput>a:hover{background-color:#ededed;border:1px solid #ccc;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}#userShowResources #Attachments div.attachmentOutput>a:hover span.remove{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADHklEQVQ4T22SeUgUcRTHZ/aY2dnZLdfWI4KuP+zQsja7KMraXaMsOrUghe6DMjNXN7LlSyt2EHZAqbgdZFRQ/xR0QGVqZmi6h2mnVHYYBboVbgvS8evtSkHWF74w897nvTfv9xuO+48uRAyMrpqSnNSYssD6wJxqrUxISjonG6P7cv/otNZgfDR/cXrPwcO72dXrYK5TYKXlYMdLEUShw2uZu9wl6KP61oV1NiJmaFfGKhvLtYNtziJvA9u0FWzj1t73bTvAbHZ0LF2R55INw/8qPiboI9/PnpPH5i1C98RpaCP3TJ6On8kpYMlWfJthwSvLfAQnTQebtxBtU2fkH1JqjX8aVA0alvY13oQXsUPgWbkar5o8eEjgZ0MsvpAfWlPRTjFf5hq8jRmMwKhEXIkatCJcfICXjI8NsbsbVRJq4xIQCATAGIP/wwe4x09GU4IJne86wrFAMIgHI8fCp9KgQR/pcPKaaK5YKU24IehwjFPijCCiBXvwk+CQuzreo/Ptu/DzD/ITZxEuiRqcIPayQoMiXpzIOVWSuYSm2zkVijkFXBwHT64N/s4ufO7uDtvv98OXZ0cF5UqJ2UtsGS+gkBetnF0lWQrVEhZzPLZTsoigihHxeNbcjJevX+Nlezuet7bifMI4HKVcEU3fTJyDVyGfF1O4HKXGVCZosY4aZBNQPHoM6quq4aWi1qdPw/a2tKLhbi1KEidgFzFrid2nUCMntMIGTjCe1OgcRyhRNmQ4am7eRmVdHercHrizsuHZkoX7Hg9u1d6Fr9GLK4mTsJ/YEqXo2MgJvX/nLrWUVklrXJB1qN/jRLXbDU92DqoJrCF7qVENNWncfwAX+/XDNZpeoBB7rzGkTE4wuCS9rVkp4o5CgC/ZDB/t2UZ+QW6hT26ZZUYVXV8THWC5WsqnmgF/GoSUwQuDK7T9c98IMjpoalCtBYuMATMORI9Gj48Ua1dKqBBkWyYvDPur+LfSOXUkRN2yexFRBf64eHyflYLvMy34NGosGgwxBU5BTkvvO/l/WkIHu14lm3bqjGa7bDBvUMumpRTry4X0C+L3YvcBfxOhAAAAAElFTkSuQmCC)}#userShowResources #Attachments div.attachmentOutput>a span.remove{display:block;float:left;height:16px;width:16px;margin-left:2px}#userShowResources #Attachments div.attachmentOutput>a span.remove:hover{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADG0lEQVQ4y3WTCUhUURSG35vlvnnzZsoJQyMo2nTMKdsXEtOcjKxogwhKaDFNysysJooQRzTLbIFScVrIaMEiElqgMjUzsnEWl1apbNG0dCqcJiarv+NEgWEPfnj3nO/+5567cAC4f3XOb5ChfFrkemvMgn0PoufllhkmJZ2R/Mf0xfYanFTr9A/nL77ozT3oxZVrgOUEUFAEHC2AJz3zm8M495KFaUf3aXDaLyCqc+WqdqSZgKRk0iZg/UYgcePv8aYtwFYTWpYu/2CRdMZeBkeYdmTrrDnvEbsIXZNnoInknRqOn5ExQORsdEcY8dI4H54p4UDsQjRNj+g8IFfr/xqUDx5W8iV0Ap4HDoV9xWq8rLWjnsBPukB8JtXPnodmijnj1uBNwBC4Q8JQOnBwqc9gLy/qH+kCvVaFiKogA9xuN3o+V1sbbOOnotYwAR1vW3wxt8eDB/qxcCpUqNEO6DbzKgOXJxcTrjMNjnBynGICGtIz8JPgHnW2tKLjzVvf/w/SY3MWLggqHCP2skyFLF7YwJkV4p58qm7iFMjjZLBQV/a0rXB1dOJTV5dPLpcLzm0mFFOugJhsYgt5hkxeyOVMCjEnUyliMcdjMyWzCCoODsXTujq8ePUKL5qb8ayxEWcN43CYcllUPYm43bwC23lhP5cqV60rZGrEk0EKAXmjx+B+eQUcNKnxyROfHA2NqLlThfywidhJzFpi98iUSO1pIYFj+uMqzbdDlCgcOhyVN26hrLoa1TY7bMkpsG9Ixj27HTer7sBpdaA0bApyiM2XC92JHDP4jnGnUiwpozbOSRrczzCjwmaDPSUVFQRWkhxkVEkm1py9KOnXD1ep+i6ZcPnvPYjj2AiLqG2vkwu4LWNwRkbDSX02kZ6TGmjJDVHRKKfjq6UNLFKKHTQnqNdVXsmz8GJ1/3evmYQWCnuUamBAAOA/CF6VFu0Ua5aLKGZSexzPZvX5mJZxylHpgub8Xb+BX11BofgeFYPvM434GDIWNbqAr2YmlSzjWPB/X+MfLaGNXaeQ4ndo/LNNki47QSnFL+VYSF/sL7crTEWWnWvrAAAAAElFTkSuQmCC)}#userShowResources #Attachments.cannotAddAttachments div.attachmentOutput{height:162px}#userShowResources #Attachments div.attachmentInput{border-top:1px solid #ccc;height:40px;background-color:#fff;padding:3px}#userShowResources #Attachments div.attachmentInput span.action{display:block;margin:2px 4px 0 0;height:32px;width:32px;cursor:pointer;float:right;border:1px solid #fff;padding:3px;background-repeat:no-repeat;background-position:2px 3px}#userShowResources #Attachments div.attachmentInput span.action:hover{background-color:#ededed;border:1px solid #ccc;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}#userShowResources #Attachments div.attachmentInput span.upload{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAFPUlEQVRYw7VXayzkVxRfy4yKTj0aj6JWJISuR9EiWj5IFZt4pB+kLYmdbEJEJpp2daeIJkRNMx7xqFcl0iXxaKwVj8ishhG+eARr10wY6hVMx3gM1mNxeo7YRDfzNr3Jyfzn/u/939899/x+59xbAHDLEIbNxNHRMSQ5OTmvuLi4pbS0tDU1NbXA3d09FN8ZqZxniMVZLJZvSUnJ0NHREVA7PDwEhUIBb1pNTU2PtbW1+/8CwMvLiz07O/uKFqqrq9sNDAzstLCw4Jubm/Pd3NyeZGdnb56dncH09LQUQfgaFIC9vX30+vr6uUwmg5CQkD509Rdo75HL0W6jWaJF+/n5CRcXF0EgEEiMjIw+MAgAbM7d3d1/y6QywAU68b+NmrFeYWFhwtPTU8jIyKi/MQDaXU5OjoDcHhsb+4x2fdX/jqqAwxbf0dFxNj4+vofPtjcCgO7+gRbn8/lSS0vLjPT09KrOp09F/f39S9XV1QOhoaFsXITxFoD3cfcv8MgA2RKuNwAzM7MgsVh8MDAwAAkJCRI8230CIxKJYHh4GLa2ti4jv6CggI7F7BoABlJ0cHNzEzA4v9ELADar1tZWkVQqBS6Xe4G/F/Pz8xAdHT2KwZWN7zkODg7NVVVVr87PzyErK6vp2lxTnDO2vLwMOCZeLwBsNvu3K9fDxMQEjI2NgZ2dXSl+3OnaQtZo3Pr6+qNt+TZ4enreu+q/09nZuUdeQrA+OgNwdXX9en9/H5qbm+Hx48dAO/fw8PiddqbEUyyMg66VlRVg379fQ32+vr4Z5Lnc3Nzn/zkabRY3MTHxmJmZ2UExgfz8fKBAiomJ+Usd9fCcc+fm5gDluB3HOSLwtcnJSXB2dv5JJxoS5crLy58dHx9DXl4eLCwsAMquFPs/VjcvPDy8dHV1FeLi4v7E6O+V4e7j4+MHiQ06AYiIiPiRzh2TC/T19YFQKASUWo4G0GZlZWUvpqamyAOLBATnb2N/sE5S7OTk9KVcLn/d09MDlZWVl+fu4+PTSIKjbh7GRuqCZIGSEAwNDV2gPgDmgYc6J6PMzMxW0nDk9MXa2hqkpaW9vB7xyozBYNzt7e3dHRkZuQxYiURCUv2HKtBqzx75yk1MTFwmFzY1Nb1G+sRrcL1RFpcr2NzYgNraWlheWgIOhyOmvKFXPYDtM3S/ggLP1tb2F3WFBZm/v/93BLaxsRFGR0ehoaHhBEF/pXYNdRUOj8cTUgCi0hHlWGqLEgvW5+j2EwrS9vb2SwB29vY/a2SZqhcoJFxaHClH0fuppqjHceO0exKpJXQ96kQ/9r+rFwAmkxmMCeeIdoGUe6gFVX8lnpPrUbCAV8iT4eKfaJVflHUidxtp9ygiAk2Uo927uLhUFBUVnVDkd3V1nZuamj7QOsEpy3iYNJZINq2srBK0zJKeKDT/EGVR8yk/3L4JgLuDg4PHLS0tB/j8oTYfSUpKqtuWyyElJeWltnPUAfDHKD5FEaHgs9T0gYCAAA4lJ6yEdnF8hM7lnRIAd1D5VijfI/eDNVRH/pjfD0jzsS54pFd9qQQAMygoqJ3Eh1dYSKU2U0WKdseiQ0QlVmRkJI0zNwiAKxAxWD5t7ezsAGa1IaTiR9dLK6xyvu3q7pYeHhwAFqQktZ56l/cqAJgaGxt/z+cXHSr2FEA3n8qKihEsRnoxOCUbqPVkmOdXcGzoje6U6vhNRWZUVNTztra2szmxGIhmVAei6u17e3s/wfchN77UarrxknvRHuAFtMjGxqacwWQ8omjXlBsMAuAtMMYEyFDX+Tf2LzGXbu1DZYkMAAAAAElFTkSuQmCC)}#userShowResources #Attachments div.attachmentInput span.photo{background-image:url(data:image/png;base64,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)}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments{margin-top:6px;background-color:#fff;line-height:1.3em;border:1px solid #ddd}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment{display:block;padding:4px;cursor:pointer}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment:not(:last-child){border-bottom:1px dashed #ddd}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment:hover{background-color:#e8eef4}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment td:first-child{width:90px;font-weight:bold}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment img.CreateJob_Assignment_Image{float:left;width:64px;height:64px;margin-right:6px}#User_Show_Details_Actions_CreateJob_Dialog #CreateJob_Assignments li.CreateJob_Assignment div.CreateJob_Assignment_Details{float:left} \ No newline at end of file diff --git a/Disco.Web/Controllers/PluginWebHandlerController.cs b/Disco.Web/Controllers/PluginWebHandlerController.cs index 7f4052a6..df1d98ef 100644 --- a/Disco.Web/Controllers/PluginWebHandlerController.cs +++ b/Disco.Web/Controllers/PluginWebHandlerController.cs @@ -11,7 +11,6 @@ namespace Disco.Web.Controllers { public partial class PluginWebHandlerController : Controller { - [DiscoAuthorize(Claims.DiscoAdminAccount)] [OutputCache(Duration = 0, Location = System.Web.UI.OutputCacheLocation.None)] public virtual ActionResult Index(string PluginId, string PluginAction) { @@ -19,11 +18,20 @@ namespace Disco.Web.Controllers if (manifest.HasWebHandler) { - using (var pluginWebHandler = manifest.CreateWebHandler(this)) + try { - return pluginWebHandler.ExecuteAction(PluginAction); + using (var pluginWebHandler = manifest.CreateWebHandler(this)) + { + pluginWebHandler.OnActionExecuting(); + return pluginWebHandler.ExecuteAction(PluginAction); + } + } + catch (AccessDeniedException accessDeniedException) + { + return new HttpUnauthorizedResult(accessDeniedException.Message); } } + return HttpNotFound("Plugin has no Web Handler"); } @@ -42,7 +50,7 @@ namespace Disco.Web.Controllers { return HttpNotFound("Plugin Resource Not Found"); } - + var pluginResourcePath = pluginResource.Item1; var mimeType = Disco.BI.Interop.MimeTypes.ResolveMimeType(pluginResourcePath);