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);