Added web api project
This commit is contained in:
parent
c83860e9c5
commit
46b4a696cb
27
RMDataManager/App_Start/BundleConfig.cs
Normal file
27
RMDataManager/App_Start/BundleConfig.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System.Web;
|
||||||
|
using System.Web.Optimization;
|
||||||
|
|
||||||
|
namespace RMDataManager
|
||||||
|
{
|
||||||
|
public class BundleConfig
|
||||||
|
{
|
||||||
|
// For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862
|
||||||
|
public static void RegisterBundles(BundleCollection bundles)
|
||||||
|
{
|
||||||
|
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
|
||||||
|
"~/Scripts/jquery-{version}.js"));
|
||||||
|
|
||||||
|
// Use the development version of Modernizr to develop with and learn from. Then, when you're
|
||||||
|
// ready for production, use the build tool at https://modernizr.com to pick only the tests you need.
|
||||||
|
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
|
||||||
|
"~/Scripts/modernizr-*"));
|
||||||
|
|
||||||
|
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
|
||||||
|
"~/Scripts/bootstrap.js"));
|
||||||
|
|
||||||
|
bundles.Add(new StyleBundle("~/Content/css").Include(
|
||||||
|
"~/Content/bootstrap.css",
|
||||||
|
"~/Content/site.css"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
RMDataManager/App_Start/FilterConfig.cs
Normal file
13
RMDataManager/App_Start/FilterConfig.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System.Web;
|
||||||
|
using System.Web.Mvc;
|
||||||
|
|
||||||
|
namespace RMDataManager
|
||||||
|
{
|
||||||
|
public class FilterConfig
|
||||||
|
{
|
||||||
|
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
|
||||||
|
{
|
||||||
|
filters.Add(new HandleErrorAttribute());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
RMDataManager/App_Start/IdentityConfig.cs
Normal file
45
RMDataManager/App_Start/IdentityConfig.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Identity.EntityFramework;
|
||||||
|
using Microsoft.AspNet.Identity.Owin;
|
||||||
|
using Microsoft.Owin;
|
||||||
|
using RMDataManager.Models;
|
||||||
|
|
||||||
|
namespace RMDataManager
|
||||||
|
{
|
||||||
|
// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
|
||||||
|
|
||||||
|
public class ApplicationUserManager : UserManager<ApplicationUser>
|
||||||
|
{
|
||||||
|
public ApplicationUserManager(IUserStore<ApplicationUser> store)
|
||||||
|
: base(store)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
|
||||||
|
{
|
||||||
|
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
|
||||||
|
// Configure validation logic for usernames
|
||||||
|
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
|
||||||
|
{
|
||||||
|
AllowOnlyAlphanumericUserNames = false,
|
||||||
|
RequireUniqueEmail = true
|
||||||
|
};
|
||||||
|
// Configure validation logic for passwords
|
||||||
|
manager.PasswordValidator = new PasswordValidator
|
||||||
|
{
|
||||||
|
RequiredLength = 6,
|
||||||
|
RequireNonLetterOrDigit = true,
|
||||||
|
RequireDigit = true,
|
||||||
|
RequireLowercase = true,
|
||||||
|
RequireUppercase = true,
|
||||||
|
};
|
||||||
|
var dataProtectionProvider = options.DataProtectionProvider;
|
||||||
|
if (dataProtectionProvider != null)
|
||||||
|
{
|
||||||
|
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
|
||||||
|
}
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
RMDataManager/App_Start/RouteConfig.cs
Normal file
23
RMDataManager/App_Start/RouteConfig.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Mvc;
|
||||||
|
using System.Web.Routing;
|
||||||
|
|
||||||
|
namespace RMDataManager
|
||||||
|
{
|
||||||
|
public class RouteConfig
|
||||||
|
{
|
||||||
|
public static void RegisterRoutes(RouteCollection routes)
|
||||||
|
{
|
||||||
|
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
|
||||||
|
|
||||||
|
routes.MapRoute(
|
||||||
|
name: "Default",
|
||||||
|
url: "{controller}/{action}/{id}",
|
||||||
|
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
RMDataManager/App_Start/Startup.Auth.cs
Normal file
69
RMDataManager/App_Start/Startup.Auth.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Identity.EntityFramework;
|
||||||
|
using Microsoft.Owin;
|
||||||
|
using Microsoft.Owin.Security.Cookies;
|
||||||
|
using Microsoft.Owin.Security.Google;
|
||||||
|
using Microsoft.Owin.Security.OAuth;
|
||||||
|
using Owin;
|
||||||
|
using RMDataManager.Providers;
|
||||||
|
using RMDataManager.Models;
|
||||||
|
|
||||||
|
namespace RMDataManager
|
||||||
|
{
|
||||||
|
public partial class Startup
|
||||||
|
{
|
||||||
|
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
|
||||||
|
|
||||||
|
public static string PublicClientId { get; private set; }
|
||||||
|
|
||||||
|
// For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
|
||||||
|
public void ConfigureAuth(IAppBuilder app)
|
||||||
|
{
|
||||||
|
// Configure the db context and user manager to use a single instance per request
|
||||||
|
app.CreatePerOwinContext(ApplicationDbContext.Create);
|
||||||
|
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
|
||||||
|
|
||||||
|
// Enable the application to use a cookie to store information for the signed in user
|
||||||
|
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
|
||||||
|
app.UseCookieAuthentication(new CookieAuthenticationOptions());
|
||||||
|
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
|
||||||
|
|
||||||
|
// Configure the application for OAuth based flow
|
||||||
|
PublicClientId = "self";
|
||||||
|
OAuthOptions = new OAuthAuthorizationServerOptions
|
||||||
|
{
|
||||||
|
TokenEndpointPath = new PathString("/Token"),
|
||||||
|
Provider = new ApplicationOAuthProvider(PublicClientId),
|
||||||
|
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
|
||||||
|
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
|
||||||
|
// In production mode set AllowInsecureHttp = false
|
||||||
|
AllowInsecureHttp = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enable the application to use bearer tokens to authenticate users
|
||||||
|
app.UseOAuthBearerTokens(OAuthOptions);
|
||||||
|
|
||||||
|
// Uncomment the following lines to enable logging in with third party login providers
|
||||||
|
//app.UseMicrosoftAccountAuthentication(
|
||||||
|
// clientId: "",
|
||||||
|
// clientSecret: "");
|
||||||
|
|
||||||
|
//app.UseTwitterAuthentication(
|
||||||
|
// consumerKey: "",
|
||||||
|
// consumerSecret: "");
|
||||||
|
|
||||||
|
//app.UseFacebookAuthentication(
|
||||||
|
// appId: "",
|
||||||
|
// appSecret: "");
|
||||||
|
|
||||||
|
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
|
||||||
|
//{
|
||||||
|
// ClientId = "",
|
||||||
|
// ClientSecret = ""
|
||||||
|
//});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
RMDataManager/App_Start/WebApiConfig.cs
Normal file
30
RMDataManager/App_Start/WebApiConfig.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Web.Http;
|
||||||
|
using Microsoft.Owin.Security.OAuth;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
namespace RMDataManager
|
||||||
|
{
|
||||||
|
public static class WebApiConfig
|
||||||
|
{
|
||||||
|
public static void Register(HttpConfiguration config)
|
||||||
|
{
|
||||||
|
// Web API configuration and services
|
||||||
|
// Configure Web API to use only bearer token authentication.
|
||||||
|
config.SuppressDefaultHostAuthentication();
|
||||||
|
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
|
||||||
|
|
||||||
|
// Web API routes
|
||||||
|
config.MapHttpAttributeRoutes();
|
||||||
|
|
||||||
|
config.Routes.MapHttpRoute(
|
||||||
|
name: "DefaultApi",
|
||||||
|
routeTemplate: "api/{controller}/{id}",
|
||||||
|
defaults: new { id = RouteParameter.Optional }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
RMDataManager/Areas/HelpPage/ApiDescriptionExtensions.cs
Normal file
39
RMDataManager/Areas/HelpPage/ApiDescriptionExtensions.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Http.Description;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
public static class ApiDescriptionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Generates an URI-friendly ID for the <see cref="ApiDescription"/>. E.g. "Get-Values-id_name" instead of "GetValues/{id}?name={name}"
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="description">The <see cref="ApiDescription"/>.</param>
|
||||||
|
/// <returns>The ID as a string.</returns>
|
||||||
|
public static string GetFriendlyId(this ApiDescription description)
|
||||||
|
{
|
||||||
|
string path = description.RelativePath;
|
||||||
|
string[] urlParts = path.Split('?');
|
||||||
|
string localPath = urlParts[0];
|
||||||
|
string queryKeyString = null;
|
||||||
|
if (urlParts.Length > 1)
|
||||||
|
{
|
||||||
|
string query = urlParts[1];
|
||||||
|
string[] queryKeys = HttpUtility.ParseQueryString(query).AllKeys;
|
||||||
|
queryKeyString = String.Join("_", queryKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder friendlyPath = new StringBuilder();
|
||||||
|
friendlyPath.AppendFormat("{0}-{1}",
|
||||||
|
description.HttpMethod.Method,
|
||||||
|
localPath.Replace("/", "-").Replace("{", String.Empty).Replace("}", String.Empty));
|
||||||
|
if (queryKeyString != null)
|
||||||
|
{
|
||||||
|
friendlyPath.AppendFormat("_{0}", queryKeyString.Replace('.', '-'));
|
||||||
|
}
|
||||||
|
return friendlyPath.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
113
RMDataManager/Areas/HelpPage/App_Start/HelpPageConfig.cs
Normal file
113
RMDataManager/Areas/HelpPage/App_Start/HelpPageConfig.cs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// Uncomment the following to provide samples for PageResult<T>. Must also add the Microsoft.AspNet.WebApi.OData
|
||||||
|
// package to your project.
|
||||||
|
////#define Handle_PageResultOfT
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Http;
|
||||||
|
#if Handle_PageResultOfT
|
||||||
|
using System.Web.Http.OData;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Use this class to customize the Help Page.
|
||||||
|
/// For example you can set a custom <see cref="System.Web.Http.Description.IDocumentationProvider"/> to supply the documentation
|
||||||
|
/// or you can provide the samples for the requests/responses.
|
||||||
|
/// </summary>
|
||||||
|
public static class HelpPageConfig
|
||||||
|
{
|
||||||
|
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters",
|
||||||
|
MessageId = "RMDataManager.Areas.HelpPage.TextSample.#ctor(System.String)",
|
||||||
|
Justification = "End users may choose to merge this string with existing localized resources.")]
|
||||||
|
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly",
|
||||||
|
MessageId = "bsonspec",
|
||||||
|
Justification = "Part of a URI.")]
|
||||||
|
public static void Register(HttpConfiguration config)
|
||||||
|
{
|
||||||
|
//// Uncomment the following to use the documentation from XML documentation file.
|
||||||
|
//config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
|
||||||
|
|
||||||
|
//// Uncomment the following to use "sample string" as the sample for all actions that have string as the body parameter or return type.
|
||||||
|
//// Also, the string arrays will be used for IEnumerable<string>. The sample objects will be serialized into different media type
|
||||||
|
//// formats by the available formatters.
|
||||||
|
//config.SetSampleObjects(new Dictionary<Type, object>
|
||||||
|
//{
|
||||||
|
// {typeof(string), "sample string"},
|
||||||
|
// {typeof(IEnumerable<string>), new string[]{"sample 1", "sample 2"}}
|
||||||
|
//});
|
||||||
|
|
||||||
|
// Extend the following to provide factories for types not handled automatically (those lacking parameterless
|
||||||
|
// constructors) or for which you prefer to use non-default property values. Line below provides a fallback
|
||||||
|
// since automatic handling will fail and GeneratePageResult handles only a single type.
|
||||||
|
#if Handle_PageResultOfT
|
||||||
|
config.GetHelpPageSampleGenerator().SampleObjectFactories.Add(GeneratePageResult);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Extend the following to use a preset object directly as the sample for all actions that support a media
|
||||||
|
// type, regardless of the body parameter or return type. The lines below avoid display of binary content.
|
||||||
|
// The BsonMediaTypeFormatter (if available) is not used to serialize the TextSample object.
|
||||||
|
config.SetSampleForMediaType(
|
||||||
|
new TextSample("Binary JSON content. See http://bsonspec.org for details."),
|
||||||
|
new MediaTypeHeaderValue("application/bson"));
|
||||||
|
|
||||||
|
//// Uncomment the following to use "[0]=foo&[1]=bar" directly as the sample for all actions that support form URL encoded format
|
||||||
|
//// and have IEnumerable<string> as the body parameter or return type.
|
||||||
|
//config.SetSampleForType("[0]=foo&[1]=bar", new MediaTypeHeaderValue("application/x-www-form-urlencoded"), typeof(IEnumerable<string>));
|
||||||
|
|
||||||
|
//// Uncomment the following to use "1234" directly as the request sample for media type "text/plain" on the controller named "Values"
|
||||||
|
//// and action named "Put".
|
||||||
|
//config.SetSampleRequest("1234", new MediaTypeHeaderValue("text/plain"), "Values", "Put");
|
||||||
|
|
||||||
|
//// Uncomment the following to use the image on "../images/aspNetHome.png" directly as the response sample for media type "image/png"
|
||||||
|
//// on the controller named "Values" and action named "Get" with parameter "id".
|
||||||
|
//config.SetSampleResponse(new ImageSample("../images/aspNetHome.png"), new MediaTypeHeaderValue("image/png"), "Values", "Get", "id");
|
||||||
|
|
||||||
|
//// Uncomment the following to correct the sample request when the action expects an HttpRequestMessage with ObjectContent<string>.
|
||||||
|
//// The sample will be generated as if the controller named "Values" and action named "Get" were having string as the body parameter.
|
||||||
|
//config.SetActualRequestType(typeof(string), "Values", "Get");
|
||||||
|
|
||||||
|
//// Uncomment the following to correct the sample response when the action returns an HttpResponseMessage with ObjectContent<string>.
|
||||||
|
//// The sample will be generated as if the controller named "Values" and action named "Post" were returning a string.
|
||||||
|
//config.SetActualResponseType(typeof(string), "Values", "Post");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if Handle_PageResultOfT
|
||||||
|
private static object GeneratePageResult(HelpPageSampleGenerator sampleGenerator, Type type)
|
||||||
|
{
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
Type openGenericType = type.GetGenericTypeDefinition();
|
||||||
|
if (openGenericType == typeof(PageResult<>))
|
||||||
|
{
|
||||||
|
// Get the T in PageResult<T>
|
||||||
|
Type[] typeParameters = type.GetGenericArguments();
|
||||||
|
Debug.Assert(typeParameters.Length == 1);
|
||||||
|
|
||||||
|
// Create an enumeration to pass as the first parameter to the PageResult<T> constuctor
|
||||||
|
Type itemsType = typeof(List<>).MakeGenericType(typeParameters);
|
||||||
|
object items = sampleGenerator.GetSampleObject(itemsType);
|
||||||
|
|
||||||
|
// Fill in the other information needed to invoke the PageResult<T> constuctor
|
||||||
|
Type[] parameterTypes = new Type[] { itemsType, typeof(Uri), typeof(long?), };
|
||||||
|
object[] parameters = new object[] { items, null, (long)ObjectGenerator.DefaultCollectionSize, };
|
||||||
|
|
||||||
|
// Call PageResult(IEnumerable<T> items, Uri nextPageLink, long? count) constructor
|
||||||
|
ConstructorInfo constructor = type.GetConstructor(parameterTypes);
|
||||||
|
return constructor.Invoke(parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
63
RMDataManager/Areas/HelpPage/Controllers/HelpController.cs
Normal file
63
RMDataManager/Areas/HelpPage/Controllers/HelpController.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Web.Http;
|
||||||
|
using System.Web.Mvc;
|
||||||
|
using RMDataManager.Areas.HelpPage.ModelDescriptions;
|
||||||
|
using RMDataManager.Areas.HelpPage.Models;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.Controllers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The controller that will handle requests for the help page.
|
||||||
|
/// </summary>
|
||||||
|
public class HelpController : Controller
|
||||||
|
{
|
||||||
|
private const string ErrorViewName = "Error";
|
||||||
|
|
||||||
|
public HelpController()
|
||||||
|
: this(GlobalConfiguration.Configuration)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public HelpController(HttpConfiguration config)
|
||||||
|
{
|
||||||
|
Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpConfiguration Configuration { get; private set; }
|
||||||
|
|
||||||
|
public ActionResult Index()
|
||||||
|
{
|
||||||
|
ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider();
|
||||||
|
return View(Configuration.Services.GetApiExplorer().ApiDescriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResult Api(string apiId)
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(apiId))
|
||||||
|
{
|
||||||
|
HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId);
|
||||||
|
if (apiModel != null)
|
||||||
|
{
|
||||||
|
return View(apiModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return View(ErrorViewName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResult ResourceModel(string modelName)
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(modelName))
|
||||||
|
{
|
||||||
|
ModelDescriptionGenerator modelDescriptionGenerator = Configuration.GetModelDescriptionGenerator();
|
||||||
|
ModelDescription modelDescription;
|
||||||
|
if (modelDescriptionGenerator.GeneratedModels.TryGetValue(modelName, out modelDescription))
|
||||||
|
{
|
||||||
|
return View(modelDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return View(ErrorViewName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
134
RMDataManager/Areas/HelpPage/HelpPage.css
Normal file
134
RMDataManager/Areas/HelpPage/HelpPage.css
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
.help-page h1,
|
||||||
|
.help-page .h1,
|
||||||
|
.help-page h2,
|
||||||
|
.help-page .h2,
|
||||||
|
.help-page h3,
|
||||||
|
.help-page .h3,
|
||||||
|
#body.help-page,
|
||||||
|
.help-page-table th,
|
||||||
|
.help-page-table pre,
|
||||||
|
.help-page-table p {
|
||||||
|
font-family: "Segoe UI Light", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page pre.wrapped {
|
||||||
|
white-space: -moz-pre-wrap;
|
||||||
|
white-space: -pre-wrap;
|
||||||
|
white-space: -o-pre-wrap;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page .warning-message-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 0 10px;
|
||||||
|
color: #525252;
|
||||||
|
background: #EFDCA9;
|
||||||
|
border: 1px solid #CCCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0px 0px 20px 0px;
|
||||||
|
border-top: 1px solid #D4D4D4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page-table th {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 1px solid #D4D4D4;
|
||||||
|
padding: 5px 6px 5px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page-table td {
|
||||||
|
border-bottom: 1px solid #D4D4D4;
|
||||||
|
padding: 10px 8px 10px 8px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page-table pre,
|
||||||
|
.help-page-table p {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page-table tbody tr:hover td {
|
||||||
|
background-color: #F3F3F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page a:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page .sample-header {
|
||||||
|
border: 2px solid #D4D4D4;
|
||||||
|
background: #00497E;
|
||||||
|
color: #FFFFFF;
|
||||||
|
padding: 8px 15px;
|
||||||
|
border-bottom: none;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 10px 0px 0px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page .sample-content {
|
||||||
|
display: block;
|
||||||
|
border-width: 0;
|
||||||
|
padding: 15px 20px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border: 2px solid #D4D4D4;
|
||||||
|
margin: 0px 0px 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page .api-name {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page .api-documentation {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page .parameter-name {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page .parameter-documentation {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page .parameter-type {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page .parameter-annotations {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page h1,
|
||||||
|
.help-page .h1 {
|
||||||
|
font-size: 36px;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page h2,
|
||||||
|
.help-page .h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page h3,
|
||||||
|
.help-page .h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#body.help-page {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 143%;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-page a {
|
||||||
|
color: #0000EE;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
26
RMDataManager/Areas/HelpPage/HelpPageAreaRegistration.cs
Normal file
26
RMDataManager/Areas/HelpPage/HelpPageAreaRegistration.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System.Web.Http;
|
||||||
|
using System.Web.Mvc;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
public class HelpPageAreaRegistration : AreaRegistration
|
||||||
|
{
|
||||||
|
public override string AreaName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "HelpPage";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RegisterArea(AreaRegistrationContext context)
|
||||||
|
{
|
||||||
|
context.MapRoute(
|
||||||
|
"HelpPage_Default",
|
||||||
|
"Help/{action}/{apiId}",
|
||||||
|
new { controller = "Help", action = "Index", apiId = UrlParameter.Optional });
|
||||||
|
|
||||||
|
HelpPageConfig.Register(GlobalConfiguration.Configuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
467
RMDataManager/Areas/HelpPage/HelpPageConfigurationExtensions.cs
Normal file
467
RMDataManager/Areas/HelpPage/HelpPageConfigurationExtensions.cs
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Web.Http;
|
||||||
|
using System.Web.Http.Controllers;
|
||||||
|
using System.Web.Http.Description;
|
||||||
|
using RMDataManager.Areas.HelpPage.ModelDescriptions;
|
||||||
|
using RMDataManager.Areas.HelpPage.Models;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
public static class HelpPageConfigurationExtensions
|
||||||
|
{
|
||||||
|
private const string ApiModelPrefix = "MS_HelpPageApiModel_";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the documentation provider for help page.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="documentationProvider">The documentation provider.</param>
|
||||||
|
public static void SetDocumentationProvider(this HttpConfiguration config, IDocumentationProvider documentationProvider)
|
||||||
|
{
|
||||||
|
config.Services.Replace(typeof(IDocumentationProvider), documentationProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the objects that will be used by the formatters to produce sample requests/responses.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="sampleObjects">The sample objects.</param>
|
||||||
|
public static void SetSampleObjects(this HttpConfiguration config, IDictionary<Type, object> sampleObjects)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().SampleObjects = sampleObjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the sample request directly for the specified media type and action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="sample">The sample request.</param>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, new[] { "*" }), sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the sample request directly for the specified media type and action with parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="sample">The sample request.</param>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
/// <param name="parameterNames">The parameter names.</param>
|
||||||
|
public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, parameterNames), sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the sample request directly for the specified media type of the action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="sample">The sample response.</param>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, new[] { "*" }), sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the sample response directly for the specified media type of the action with specific parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="sample">The sample response.</param>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
/// <param name="parameterNames">The parameter names.</param>
|
||||||
|
public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, parameterNames), sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the sample directly for all actions with the specified media type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="sample">The sample.</param>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
public static void SetSampleForMediaType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType), sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the sample directly for all actions with the specified type and media type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="sample">The sample.</param>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
/// <param name="type">The parameter type or return type of an action.</param>
|
||||||
|
public static void SetSampleForType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, Type type)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, type), sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> passed to the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
|
||||||
|
/// The help page will use this information to produce more accurate request samples.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="type">The type.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, new[] { "*" }), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> passed to the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
|
||||||
|
/// The help page will use this information to produce more accurate request samples.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="type">The type.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
/// <param name="parameterNames">The parameter names.</param>
|
||||||
|
public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, parameterNames), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> returned as part of the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
|
||||||
|
/// The help page will use this information to produce more accurate response samples.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="type">The type.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, new[] { "*" }), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> returned as part of the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
|
||||||
|
/// The help page will use this information to produce more accurate response samples.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="type">The type.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
/// <param name="parameterNames">The parameter names.</param>
|
||||||
|
public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames)
|
||||||
|
{
|
||||||
|
config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, parameterNames), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the help page sample generator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <returns>The help page sample generator.</returns>
|
||||||
|
public static HelpPageSampleGenerator GetHelpPageSampleGenerator(this HttpConfiguration config)
|
||||||
|
{
|
||||||
|
return (HelpPageSampleGenerator)config.Properties.GetOrAdd(
|
||||||
|
typeof(HelpPageSampleGenerator),
|
||||||
|
k => new HelpPageSampleGenerator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the help page sample generator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="sampleGenerator">The help page sample generator.</param>
|
||||||
|
public static void SetHelpPageSampleGenerator(this HttpConfiguration config, HelpPageSampleGenerator sampleGenerator)
|
||||||
|
{
|
||||||
|
config.Properties.AddOrUpdate(
|
||||||
|
typeof(HelpPageSampleGenerator),
|
||||||
|
k => sampleGenerator,
|
||||||
|
(k, o) => sampleGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the model description generator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The configuration.</param>
|
||||||
|
/// <returns>The <see cref="ModelDescriptionGenerator"/></returns>
|
||||||
|
public static ModelDescriptionGenerator GetModelDescriptionGenerator(this HttpConfiguration config)
|
||||||
|
{
|
||||||
|
return (ModelDescriptionGenerator)config.Properties.GetOrAdd(
|
||||||
|
typeof(ModelDescriptionGenerator),
|
||||||
|
k => InitializeModelDescriptionGenerator(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the model that represents an API displayed on the help page. The model is initialized on the first call and cached for subsequent calls.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The <see cref="HttpConfiguration"/>.</param>
|
||||||
|
/// <param name="apiDescriptionId">The <see cref="ApiDescription"/> ID.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// An <see cref="HelpPageApiModel"/>
|
||||||
|
/// </returns>
|
||||||
|
public static HelpPageApiModel GetHelpPageApiModel(this HttpConfiguration config, string apiDescriptionId)
|
||||||
|
{
|
||||||
|
object model;
|
||||||
|
string modelId = ApiModelPrefix + apiDescriptionId;
|
||||||
|
if (!config.Properties.TryGetValue(modelId, out model))
|
||||||
|
{
|
||||||
|
Collection<ApiDescription> apiDescriptions = config.Services.GetApiExplorer().ApiDescriptions;
|
||||||
|
ApiDescription apiDescription = apiDescriptions.FirstOrDefault(api => String.Equals(api.GetFriendlyId(), apiDescriptionId, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (apiDescription != null)
|
||||||
|
{
|
||||||
|
model = GenerateApiModel(apiDescription, config);
|
||||||
|
config.Properties.TryAdd(modelId, model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (HelpPageApiModel)model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config)
|
||||||
|
{
|
||||||
|
HelpPageApiModel apiModel = new HelpPageApiModel()
|
||||||
|
{
|
||||||
|
ApiDescription = apiDescription,
|
||||||
|
};
|
||||||
|
|
||||||
|
ModelDescriptionGenerator modelGenerator = config.GetModelDescriptionGenerator();
|
||||||
|
HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
|
||||||
|
GenerateUriParameters(apiModel, modelGenerator);
|
||||||
|
GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator);
|
||||||
|
GenerateResourceDescription(apiModel, modelGenerator);
|
||||||
|
GenerateSamples(apiModel, sampleGenerator);
|
||||||
|
|
||||||
|
return apiModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateUriParameters(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator)
|
||||||
|
{
|
||||||
|
ApiDescription apiDescription = apiModel.ApiDescription;
|
||||||
|
foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions)
|
||||||
|
{
|
||||||
|
if (apiParameter.Source == ApiParameterSource.FromUri)
|
||||||
|
{
|
||||||
|
HttpParameterDescriptor parameterDescriptor = apiParameter.ParameterDescriptor;
|
||||||
|
Type parameterType = null;
|
||||||
|
ModelDescription typeDescription = null;
|
||||||
|
ComplexTypeModelDescription complexTypeDescription = null;
|
||||||
|
if (parameterDescriptor != null)
|
||||||
|
{
|
||||||
|
parameterType = parameterDescriptor.ParameterType;
|
||||||
|
typeDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
|
||||||
|
complexTypeDescription = typeDescription as ComplexTypeModelDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example:
|
||||||
|
// [TypeConverter(typeof(PointConverter))]
|
||||||
|
// public class Point
|
||||||
|
// {
|
||||||
|
// public Point(int x, int y)
|
||||||
|
// {
|
||||||
|
// X = x;
|
||||||
|
// Y = y;
|
||||||
|
// }
|
||||||
|
// public int X { get; set; }
|
||||||
|
// public int Y { get; set; }
|
||||||
|
// }
|
||||||
|
// Class Point is bindable with a TypeConverter, so Point will be added to UriParameters collection.
|
||||||
|
//
|
||||||
|
// public class Point
|
||||||
|
// {
|
||||||
|
// public int X { get; set; }
|
||||||
|
// public int Y { get; set; }
|
||||||
|
// }
|
||||||
|
// Regular complex class Point will have properties X and Y added to UriParameters collection.
|
||||||
|
if (complexTypeDescription != null
|
||||||
|
&& !IsBindableWithTypeConverter(parameterType))
|
||||||
|
{
|
||||||
|
foreach (ParameterDescription uriParameter in complexTypeDescription.Properties)
|
||||||
|
{
|
||||||
|
apiModel.UriParameters.Add(uriParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parameterDescriptor != null)
|
||||||
|
{
|
||||||
|
ParameterDescription uriParameter =
|
||||||
|
AddParameterDescription(apiModel, apiParameter, typeDescription);
|
||||||
|
|
||||||
|
if (!parameterDescriptor.IsOptional)
|
||||||
|
{
|
||||||
|
uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
object defaultValue = parameterDescriptor.DefaultValue;
|
||||||
|
if (defaultValue != null)
|
||||||
|
{
|
||||||
|
uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Default value is " + Convert.ToString(defaultValue, CultureInfo.InvariantCulture) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Assert(parameterDescriptor == null);
|
||||||
|
|
||||||
|
// If parameterDescriptor is null, this is an undeclared route parameter which only occurs
|
||||||
|
// when source is FromUri. Ignored in request model and among resource parameters but listed
|
||||||
|
// as a simple string here.
|
||||||
|
ModelDescription modelDescription = modelGenerator.GetOrCreateModelDescription(typeof(string));
|
||||||
|
AddParameterDescription(apiModel, apiParameter, modelDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsBindableWithTypeConverter(Type parameterType)
|
||||||
|
{
|
||||||
|
if (parameterType == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TypeDescriptor.GetConverter(parameterType).CanConvertFrom(typeof(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ParameterDescription AddParameterDescription(HelpPageApiModel apiModel,
|
||||||
|
ApiParameterDescription apiParameter, ModelDescription typeDescription)
|
||||||
|
{
|
||||||
|
ParameterDescription parameterDescription = new ParameterDescription
|
||||||
|
{
|
||||||
|
Name = apiParameter.Name,
|
||||||
|
Documentation = apiParameter.Documentation,
|
||||||
|
TypeDescription = typeDescription,
|
||||||
|
};
|
||||||
|
|
||||||
|
apiModel.UriParameters.Add(parameterDescription);
|
||||||
|
return parameterDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateRequestModelDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator, HelpPageSampleGenerator sampleGenerator)
|
||||||
|
{
|
||||||
|
ApiDescription apiDescription = apiModel.ApiDescription;
|
||||||
|
foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions)
|
||||||
|
{
|
||||||
|
if (apiParameter.Source == ApiParameterSource.FromBody)
|
||||||
|
{
|
||||||
|
Type parameterType = apiParameter.ParameterDescriptor.ParameterType;
|
||||||
|
apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
|
||||||
|
apiModel.RequestDocumentation = apiParameter.Documentation;
|
||||||
|
}
|
||||||
|
else if (apiParameter.ParameterDescriptor != null &&
|
||||||
|
apiParameter.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage))
|
||||||
|
{
|
||||||
|
Type parameterType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription);
|
||||||
|
|
||||||
|
if (parameterType != null)
|
||||||
|
{
|
||||||
|
apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateResourceDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator)
|
||||||
|
{
|
||||||
|
ResponseDescription response = apiModel.ApiDescription.ResponseDescription;
|
||||||
|
Type responseType = response.ResponseType ?? response.DeclaredType;
|
||||||
|
if (responseType != null && responseType != typeof(void))
|
||||||
|
{
|
||||||
|
apiModel.ResourceDescription = modelGenerator.GetOrCreateModelDescription(responseType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as ErrorMessages.")]
|
||||||
|
private static void GenerateSamples(HelpPageApiModel apiModel, HelpPageSampleGenerator sampleGenerator)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var item in sampleGenerator.GetSampleRequests(apiModel.ApiDescription))
|
||||||
|
{
|
||||||
|
apiModel.SampleRequests.Add(item.Key, item.Value);
|
||||||
|
LogInvalidSampleAsError(apiModel, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in sampleGenerator.GetSampleResponses(apiModel.ApiDescription))
|
||||||
|
{
|
||||||
|
apiModel.SampleResponses.Add(item.Key, item.Value);
|
||||||
|
LogInvalidSampleAsError(apiModel, item.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
apiModel.ErrorMessages.Add(String.Format(CultureInfo.CurrentCulture,
|
||||||
|
"An exception has occurred while generating the sample. Exception message: {0}",
|
||||||
|
HelpPageSampleGenerator.UnwrapException(e).Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetResourceParameter(ApiDescription apiDescription, HttpConfiguration config, out ApiParameterDescription parameterDescription, out Type resourceType)
|
||||||
|
{
|
||||||
|
parameterDescription = apiDescription.ParameterDescriptions.FirstOrDefault(
|
||||||
|
p => p.Source == ApiParameterSource.FromBody ||
|
||||||
|
(p.ParameterDescriptor != null && p.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage)));
|
||||||
|
|
||||||
|
if (parameterDescription == null)
|
||||||
|
{
|
||||||
|
resourceType = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceType = parameterDescription.ParameterDescriptor.ParameterType;
|
||||||
|
|
||||||
|
if (resourceType == typeof(HttpRequestMessage))
|
||||||
|
{
|
||||||
|
HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
|
||||||
|
resourceType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceType == null)
|
||||||
|
{
|
||||||
|
parameterDescription = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModelDescriptionGenerator InitializeModelDescriptionGenerator(HttpConfiguration config)
|
||||||
|
{
|
||||||
|
ModelDescriptionGenerator modelGenerator = new ModelDescriptionGenerator(config);
|
||||||
|
Collection<ApiDescription> apis = config.Services.GetApiExplorer().ApiDescriptions;
|
||||||
|
foreach (ApiDescription api in apis)
|
||||||
|
{
|
||||||
|
ApiParameterDescription parameterDescription;
|
||||||
|
Type parameterType;
|
||||||
|
if (TryGetResourceParameter(api, config, out parameterDescription, out parameterType))
|
||||||
|
{
|
||||||
|
modelGenerator.GetOrCreateModelDescription(parameterType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modelGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LogInvalidSampleAsError(HelpPageApiModel apiModel, object sample)
|
||||||
|
{
|
||||||
|
InvalidSample invalidSample = sample as InvalidSample;
|
||||||
|
if (invalidSample != null)
|
||||||
|
{
|
||||||
|
apiModel.ErrorMessages.Add(invalidSample.ErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public class CollectionModelDescription : ModelDescription
|
||||||
|
{
|
||||||
|
public ModelDescription ElementDescription { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public class ComplexTypeModelDescription : ModelDescription
|
||||||
|
{
|
||||||
|
public ComplexTypeModelDescription()
|
||||||
|
{
|
||||||
|
Properties = new Collection<ParameterDescription>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ParameterDescription> Properties { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public class DictionaryModelDescription : KeyValuePairModelDescription
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public class EnumTypeModelDescription : ModelDescription
|
||||||
|
{
|
||||||
|
public EnumTypeModelDescription()
|
||||||
|
{
|
||||||
|
Values = new Collection<EnumValueDescription>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<EnumValueDescription> Values { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public class EnumValueDescription
|
||||||
|
{
|
||||||
|
public string Documentation { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public interface IModelDocumentationProvider
|
||||||
|
{
|
||||||
|
string GetDocumentation(MemberInfo member);
|
||||||
|
|
||||||
|
string GetDocumentation(Type type);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public class KeyValuePairModelDescription : ModelDescription
|
||||||
|
{
|
||||||
|
public ModelDescription KeyModelDescription { get; set; }
|
||||||
|
|
||||||
|
public ModelDescription ValueModelDescription { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a type model.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ModelDescription
|
||||||
|
{
|
||||||
|
public string Documentation { get; set; }
|
||||||
|
|
||||||
|
public Type ModelType { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,451 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Web.Http;
|
||||||
|
using System.Web.Http.Description;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Generates model descriptions for given types.
|
||||||
|
/// </summary>
|
||||||
|
public class ModelDescriptionGenerator
|
||||||
|
{
|
||||||
|
// Modify this to support more data annotation attributes.
|
||||||
|
private readonly IDictionary<Type, Func<object, string>> AnnotationTextGenerator = new Dictionary<Type, Func<object, string>>
|
||||||
|
{
|
||||||
|
{ typeof(RequiredAttribute), a => "Required" },
|
||||||
|
{ typeof(RangeAttribute), a =>
|
||||||
|
{
|
||||||
|
RangeAttribute range = (RangeAttribute)a;
|
||||||
|
return String.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ typeof(MaxLengthAttribute), a =>
|
||||||
|
{
|
||||||
|
MaxLengthAttribute maxLength = (MaxLengthAttribute)a;
|
||||||
|
return String.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ typeof(MinLengthAttribute), a =>
|
||||||
|
{
|
||||||
|
MinLengthAttribute minLength = (MinLengthAttribute)a;
|
||||||
|
return String.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ typeof(StringLengthAttribute), a =>
|
||||||
|
{
|
||||||
|
StringLengthAttribute strLength = (StringLengthAttribute)a;
|
||||||
|
return String.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ typeof(DataTypeAttribute), a =>
|
||||||
|
{
|
||||||
|
DataTypeAttribute dataType = (DataTypeAttribute)a;
|
||||||
|
return String.Format(CultureInfo.CurrentCulture, "Data type: {0}", dataType.CustomDataType ?? dataType.DataType.ToString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ typeof(RegularExpressionAttribute), a =>
|
||||||
|
{
|
||||||
|
RegularExpressionAttribute regularExpression = (RegularExpressionAttribute)a;
|
||||||
|
return String.Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Modify this to add more default documentations.
|
||||||
|
private readonly IDictionary<Type, string> DefaultTypeDocumentation = new Dictionary<Type, string>
|
||||||
|
{
|
||||||
|
{ typeof(Int16), "integer" },
|
||||||
|
{ typeof(Int32), "integer" },
|
||||||
|
{ typeof(Int64), "integer" },
|
||||||
|
{ typeof(UInt16), "unsigned integer" },
|
||||||
|
{ typeof(UInt32), "unsigned integer" },
|
||||||
|
{ typeof(UInt64), "unsigned integer" },
|
||||||
|
{ typeof(Byte), "byte" },
|
||||||
|
{ typeof(Char), "character" },
|
||||||
|
{ typeof(SByte), "signed byte" },
|
||||||
|
{ typeof(Uri), "URI" },
|
||||||
|
{ typeof(Single), "decimal number" },
|
||||||
|
{ typeof(Double), "decimal number" },
|
||||||
|
{ typeof(Decimal), "decimal number" },
|
||||||
|
{ typeof(String), "string" },
|
||||||
|
{ typeof(Guid), "globally unique identifier" },
|
||||||
|
{ typeof(TimeSpan), "time interval" },
|
||||||
|
{ typeof(DateTime), "date" },
|
||||||
|
{ typeof(DateTimeOffset), "date" },
|
||||||
|
{ typeof(Boolean), "boolean" },
|
||||||
|
};
|
||||||
|
|
||||||
|
private Lazy<IModelDocumentationProvider> _documentationProvider;
|
||||||
|
|
||||||
|
public ModelDescriptionGenerator(HttpConfiguration config)
|
||||||
|
{
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("config");
|
||||||
|
}
|
||||||
|
|
||||||
|
_documentationProvider = new Lazy<IModelDocumentationProvider>(() => config.Services.GetDocumentationProvider() as IModelDocumentationProvider);
|
||||||
|
GeneratedModels = new Dictionary<string, ModelDescription>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, ModelDescription> GeneratedModels { get; private set; }
|
||||||
|
|
||||||
|
private IModelDocumentationProvider DocumentationProvider
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _documentationProvider.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelDescription GetOrCreateModelDescription(Type modelType)
|
||||||
|
{
|
||||||
|
if (modelType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("modelType");
|
||||||
|
}
|
||||||
|
|
||||||
|
Type underlyingType = Nullable.GetUnderlyingType(modelType);
|
||||||
|
if (underlyingType != null)
|
||||||
|
{
|
||||||
|
modelType = underlyingType;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelDescription modelDescription;
|
||||||
|
string modelName = ModelNameHelper.GetModelName(modelType);
|
||||||
|
if (GeneratedModels.TryGetValue(modelName, out modelDescription))
|
||||||
|
{
|
||||||
|
if (modelType != modelDescription.ModelType)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
String.Format(
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
"A model description could not be created. Duplicate model name '{0}' was found for types '{1}' and '{2}'. " +
|
||||||
|
"Use the [ModelName] attribute to change the model name for at least one of the types so that it has a unique name.",
|
||||||
|
modelName,
|
||||||
|
modelDescription.ModelType.FullName,
|
||||||
|
modelType.FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return modelDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DefaultTypeDocumentation.ContainsKey(modelType))
|
||||||
|
{
|
||||||
|
return GenerateSimpleTypeModelDescription(modelType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelType.IsEnum)
|
||||||
|
{
|
||||||
|
return GenerateEnumTypeModelDescription(modelType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelType.IsGenericType)
|
||||||
|
{
|
||||||
|
Type[] genericArguments = modelType.GetGenericArguments();
|
||||||
|
|
||||||
|
if (genericArguments.Length == 1)
|
||||||
|
{
|
||||||
|
Type enumerableType = typeof(IEnumerable<>).MakeGenericType(genericArguments);
|
||||||
|
if (enumerableType.IsAssignableFrom(modelType))
|
||||||
|
{
|
||||||
|
return GenerateCollectionModelDescription(modelType, genericArguments[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (genericArguments.Length == 2)
|
||||||
|
{
|
||||||
|
Type dictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments);
|
||||||
|
if (dictionaryType.IsAssignableFrom(modelType))
|
||||||
|
{
|
||||||
|
return GenerateDictionaryModelDescription(modelType, genericArguments[0], genericArguments[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type keyValuePairType = typeof(KeyValuePair<,>).MakeGenericType(genericArguments);
|
||||||
|
if (keyValuePairType.IsAssignableFrom(modelType))
|
||||||
|
{
|
||||||
|
return GenerateKeyValuePairModelDescription(modelType, genericArguments[0], genericArguments[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelType.IsArray)
|
||||||
|
{
|
||||||
|
Type elementType = modelType.GetElementType();
|
||||||
|
return GenerateCollectionModelDescription(modelType, elementType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelType == typeof(NameValueCollection))
|
||||||
|
{
|
||||||
|
return GenerateDictionaryModelDescription(modelType, typeof(string), typeof(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(IDictionary).IsAssignableFrom(modelType))
|
||||||
|
{
|
||||||
|
return GenerateDictionaryModelDescription(modelType, typeof(object), typeof(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(IEnumerable).IsAssignableFrom(modelType))
|
||||||
|
{
|
||||||
|
return GenerateCollectionModelDescription(modelType, typeof(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateComplexTypeModelDescription(modelType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change this to provide different name for the member.
|
||||||
|
private static string GetMemberName(MemberInfo member, bool hasDataContractAttribute)
|
||||||
|
{
|
||||||
|
JsonPropertyAttribute jsonProperty = member.GetCustomAttribute<JsonPropertyAttribute>();
|
||||||
|
if (jsonProperty != null && !String.IsNullOrEmpty(jsonProperty.PropertyName))
|
||||||
|
{
|
||||||
|
return jsonProperty.PropertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDataContractAttribute)
|
||||||
|
{
|
||||||
|
DataMemberAttribute dataMember = member.GetCustomAttribute<DataMemberAttribute>();
|
||||||
|
if (dataMember != null && !String.IsNullOrEmpty(dataMember.Name))
|
||||||
|
{
|
||||||
|
return dataMember.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return member.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ShouldDisplayMember(MemberInfo member, bool hasDataContractAttribute)
|
||||||
|
{
|
||||||
|
JsonIgnoreAttribute jsonIgnore = member.GetCustomAttribute<JsonIgnoreAttribute>();
|
||||||
|
XmlIgnoreAttribute xmlIgnore = member.GetCustomAttribute<XmlIgnoreAttribute>();
|
||||||
|
IgnoreDataMemberAttribute ignoreDataMember = member.GetCustomAttribute<IgnoreDataMemberAttribute>();
|
||||||
|
NonSerializedAttribute nonSerialized = member.GetCustomAttribute<NonSerializedAttribute>();
|
||||||
|
ApiExplorerSettingsAttribute apiExplorerSetting = member.GetCustomAttribute<ApiExplorerSettingsAttribute>();
|
||||||
|
|
||||||
|
bool hasMemberAttribute = member.DeclaringType.IsEnum ?
|
||||||
|
member.GetCustomAttribute<EnumMemberAttribute>() != null :
|
||||||
|
member.GetCustomAttribute<DataMemberAttribute>() != null;
|
||||||
|
|
||||||
|
// Display member only if all the followings are true:
|
||||||
|
// no JsonIgnoreAttribute
|
||||||
|
// no XmlIgnoreAttribute
|
||||||
|
// no IgnoreDataMemberAttribute
|
||||||
|
// no NonSerializedAttribute
|
||||||
|
// no ApiExplorerSettingsAttribute with IgnoreApi set to true
|
||||||
|
// no DataContractAttribute without DataMemberAttribute or EnumMemberAttribute
|
||||||
|
return jsonIgnore == null &&
|
||||||
|
xmlIgnore == null &&
|
||||||
|
ignoreDataMember == null &&
|
||||||
|
nonSerialized == null &&
|
||||||
|
(apiExplorerSetting == null || !apiExplorerSetting.IgnoreApi) &&
|
||||||
|
(!hasDataContractAttribute || hasMemberAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CreateDefaultDocumentation(Type type)
|
||||||
|
{
|
||||||
|
string documentation;
|
||||||
|
if (DefaultTypeDocumentation.TryGetValue(type, out documentation))
|
||||||
|
{
|
||||||
|
return documentation;
|
||||||
|
}
|
||||||
|
if (DocumentationProvider != null)
|
||||||
|
{
|
||||||
|
documentation = DocumentationProvider.GetDocumentation(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return documentation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateAnnotations(MemberInfo property, ParameterDescription propertyModel)
|
||||||
|
{
|
||||||
|
List<ParameterAnnotation> annotations = new List<ParameterAnnotation>();
|
||||||
|
|
||||||
|
IEnumerable<Attribute> attributes = property.GetCustomAttributes();
|
||||||
|
foreach (Attribute attribute in attributes)
|
||||||
|
{
|
||||||
|
Func<object, string> textGenerator;
|
||||||
|
if (AnnotationTextGenerator.TryGetValue(attribute.GetType(), out textGenerator))
|
||||||
|
{
|
||||||
|
annotations.Add(
|
||||||
|
new ParameterAnnotation
|
||||||
|
{
|
||||||
|
AnnotationAttribute = attribute,
|
||||||
|
Documentation = textGenerator(attribute)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rearrange the annotations
|
||||||
|
annotations.Sort((x, y) =>
|
||||||
|
{
|
||||||
|
// Special-case RequiredAttribute so that it shows up on top
|
||||||
|
if (x.AnnotationAttribute is RequiredAttribute)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (y.AnnotationAttribute is RequiredAttribute)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the rest based on alphabetic order of the documentation
|
||||||
|
return String.Compare(x.Documentation, y.Documentation, StringComparison.OrdinalIgnoreCase);
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (ParameterAnnotation annotation in annotations)
|
||||||
|
{
|
||||||
|
propertyModel.Annotations.Add(annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CollectionModelDescription GenerateCollectionModelDescription(Type modelType, Type elementType)
|
||||||
|
{
|
||||||
|
ModelDescription collectionModelDescription = GetOrCreateModelDescription(elementType);
|
||||||
|
if (collectionModelDescription != null)
|
||||||
|
{
|
||||||
|
return new CollectionModelDescription
|
||||||
|
{
|
||||||
|
Name = ModelNameHelper.GetModelName(modelType),
|
||||||
|
ModelType = modelType,
|
||||||
|
ElementDescription = collectionModelDescription
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModelDescription GenerateComplexTypeModelDescription(Type modelType)
|
||||||
|
{
|
||||||
|
ComplexTypeModelDescription complexModelDescription = new ComplexTypeModelDescription
|
||||||
|
{
|
||||||
|
Name = ModelNameHelper.GetModelName(modelType),
|
||||||
|
ModelType = modelType,
|
||||||
|
Documentation = CreateDefaultDocumentation(modelType)
|
||||||
|
};
|
||||||
|
|
||||||
|
GeneratedModels.Add(complexModelDescription.Name, complexModelDescription);
|
||||||
|
bool hasDataContractAttribute = modelType.GetCustomAttribute<DataContractAttribute>() != null;
|
||||||
|
PropertyInfo[] properties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
foreach (PropertyInfo property in properties)
|
||||||
|
{
|
||||||
|
if (ShouldDisplayMember(property, hasDataContractAttribute))
|
||||||
|
{
|
||||||
|
ParameterDescription propertyModel = new ParameterDescription
|
||||||
|
{
|
||||||
|
Name = GetMemberName(property, hasDataContractAttribute)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DocumentationProvider != null)
|
||||||
|
{
|
||||||
|
propertyModel.Documentation = DocumentationProvider.GetDocumentation(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
GenerateAnnotations(property, propertyModel);
|
||||||
|
complexModelDescription.Properties.Add(propertyModel);
|
||||||
|
propertyModel.TypeDescription = GetOrCreateModelDescription(property.PropertyType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldInfo[] fields = modelType.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
foreach (FieldInfo field in fields)
|
||||||
|
{
|
||||||
|
if (ShouldDisplayMember(field, hasDataContractAttribute))
|
||||||
|
{
|
||||||
|
ParameterDescription propertyModel = new ParameterDescription
|
||||||
|
{
|
||||||
|
Name = GetMemberName(field, hasDataContractAttribute)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DocumentationProvider != null)
|
||||||
|
{
|
||||||
|
propertyModel.Documentation = DocumentationProvider.GetDocumentation(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
complexModelDescription.Properties.Add(propertyModel);
|
||||||
|
propertyModel.TypeDescription = GetOrCreateModelDescription(field.FieldType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return complexModelDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DictionaryModelDescription GenerateDictionaryModelDescription(Type modelType, Type keyType, Type valueType)
|
||||||
|
{
|
||||||
|
ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType);
|
||||||
|
ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType);
|
||||||
|
|
||||||
|
return new DictionaryModelDescription
|
||||||
|
{
|
||||||
|
Name = ModelNameHelper.GetModelName(modelType),
|
||||||
|
ModelType = modelType,
|
||||||
|
KeyModelDescription = keyModelDescription,
|
||||||
|
ValueModelDescription = valueModelDescription
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private EnumTypeModelDescription GenerateEnumTypeModelDescription(Type modelType)
|
||||||
|
{
|
||||||
|
EnumTypeModelDescription enumDescription = new EnumTypeModelDescription
|
||||||
|
{
|
||||||
|
Name = ModelNameHelper.GetModelName(modelType),
|
||||||
|
ModelType = modelType,
|
||||||
|
Documentation = CreateDefaultDocumentation(modelType)
|
||||||
|
};
|
||||||
|
bool hasDataContractAttribute = modelType.GetCustomAttribute<DataContractAttribute>() != null;
|
||||||
|
foreach (FieldInfo field in modelType.GetFields(BindingFlags.Public | BindingFlags.Static))
|
||||||
|
{
|
||||||
|
if (ShouldDisplayMember(field, hasDataContractAttribute))
|
||||||
|
{
|
||||||
|
EnumValueDescription enumValue = new EnumValueDescription
|
||||||
|
{
|
||||||
|
Name = field.Name,
|
||||||
|
Value = field.GetRawConstantValue().ToString()
|
||||||
|
};
|
||||||
|
if (DocumentationProvider != null)
|
||||||
|
{
|
||||||
|
enumValue.Documentation = DocumentationProvider.GetDocumentation(field);
|
||||||
|
}
|
||||||
|
enumDescription.Values.Add(enumValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GeneratedModels.Add(enumDescription.Name, enumDescription);
|
||||||
|
|
||||||
|
return enumDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyValuePairModelDescription GenerateKeyValuePairModelDescription(Type modelType, Type keyType, Type valueType)
|
||||||
|
{
|
||||||
|
ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType);
|
||||||
|
ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType);
|
||||||
|
|
||||||
|
return new KeyValuePairModelDescription
|
||||||
|
{
|
||||||
|
Name = ModelNameHelper.GetModelName(modelType),
|
||||||
|
ModelType = modelType,
|
||||||
|
KeyModelDescription = keyModelDescription,
|
||||||
|
ValueModelDescription = valueModelDescription
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModelDescription GenerateSimpleTypeModelDescription(Type modelType)
|
||||||
|
{
|
||||||
|
SimpleTypeModelDescription simpleModelDescription = new SimpleTypeModelDescription
|
||||||
|
{
|
||||||
|
Name = ModelNameHelper.GetModelName(modelType),
|
||||||
|
ModelType = modelType,
|
||||||
|
Documentation = CreateDefaultDocumentation(modelType)
|
||||||
|
};
|
||||||
|
GeneratedModels.Add(simpleModelDescription.Name, simpleModelDescription);
|
||||||
|
|
||||||
|
return simpleModelDescription;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Use this attribute to change the name of the <see cref="ModelDescription"/> generated for a type.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = false, Inherited = false)]
|
||||||
|
public sealed class ModelNameAttribute : Attribute
|
||||||
|
{
|
||||||
|
public ModelNameAttribute(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
internal static class ModelNameHelper
|
||||||
|
{
|
||||||
|
// Modify this to provide custom model name mapping.
|
||||||
|
public static string GetModelName(Type type)
|
||||||
|
{
|
||||||
|
ModelNameAttribute modelNameAttribute = type.GetCustomAttribute<ModelNameAttribute>();
|
||||||
|
if (modelNameAttribute != null && !String.IsNullOrEmpty(modelNameAttribute.Name))
|
||||||
|
{
|
||||||
|
return modelNameAttribute.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
string modelName = type.Name;
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
// Format the generic type name to something like: GenericOfAgurment1AndArgument2
|
||||||
|
Type genericType = type.GetGenericTypeDefinition();
|
||||||
|
Type[] genericArguments = type.GetGenericArguments();
|
||||||
|
string genericTypeName = genericType.Name;
|
||||||
|
|
||||||
|
// Trim the generic parameter counts from the name
|
||||||
|
genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`'));
|
||||||
|
string[] argumentTypeNames = genericArguments.Select(t => GetModelName(t)).ToArray();
|
||||||
|
modelName = String.Format(CultureInfo.InvariantCulture, "{0}Of{1}", genericTypeName, String.Join("And", argumentTypeNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
return modelName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public class ParameterAnnotation
|
||||||
|
{
|
||||||
|
public Attribute AnnotationAttribute { get; set; }
|
||||||
|
|
||||||
|
public string Documentation { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public class ParameterDescription
|
||||||
|
{
|
||||||
|
public ParameterDescription()
|
||||||
|
{
|
||||||
|
Annotations = new Collection<ParameterAnnotation>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ParameterAnnotation> Annotations { get; private set; }
|
||||||
|
|
||||||
|
public string Documentation { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public ModelDescription TypeDescription { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
{
|
||||||
|
public class SimpleTypeModelDescription : ModelDescription
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
108
RMDataManager/Areas/HelpPage/Models/HelpPageApiModel.cs
Normal file
108
RMDataManager/Areas/HelpPage/Models/HelpPageApiModel.cs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Web.Http.Description;
|
||||||
|
using RMDataManager.Areas.HelpPage.ModelDescriptions;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The model that represents an API displayed on the help page.
|
||||||
|
/// </summary>
|
||||||
|
public class HelpPageApiModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="HelpPageApiModel"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public HelpPageApiModel()
|
||||||
|
{
|
||||||
|
UriParameters = new Collection<ParameterDescription>();
|
||||||
|
SampleRequests = new Dictionary<MediaTypeHeaderValue, object>();
|
||||||
|
SampleResponses = new Dictionary<MediaTypeHeaderValue, object>();
|
||||||
|
ErrorMessages = new Collection<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ApiDescription"/> that describes the API.
|
||||||
|
/// </summary>
|
||||||
|
public ApiDescription ApiDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ParameterDescription"/> collection that describes the URI parameters for the API.
|
||||||
|
/// </summary>
|
||||||
|
public Collection<ParameterDescription> UriParameters { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the documentation for the request.
|
||||||
|
/// </summary>
|
||||||
|
public string RequestDocumentation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ModelDescription"/> that describes the request body.
|
||||||
|
/// </summary>
|
||||||
|
public ModelDescription RequestModelDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the request body parameter descriptions.
|
||||||
|
/// </summary>
|
||||||
|
public IList<ParameterDescription> RequestBodyParameters
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetParameterDescriptions(RequestModelDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ModelDescription"/> that describes the resource.
|
||||||
|
/// </summary>
|
||||||
|
public ModelDescription ResourceDescription { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the resource property descriptions.
|
||||||
|
/// </summary>
|
||||||
|
public IList<ParameterDescription> ResourceProperties
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetParameterDescriptions(ResourceDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the sample requests associated with the API.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<MediaTypeHeaderValue, object> SampleRequests { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the sample responses associated with the API.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<MediaTypeHeaderValue, object> SampleResponses { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the error messages associated with this model.
|
||||||
|
/// </summary>
|
||||||
|
public Collection<string> ErrorMessages { get; private set; }
|
||||||
|
|
||||||
|
private static IList<ParameterDescription> GetParameterDescriptions(ModelDescription modelDescription)
|
||||||
|
{
|
||||||
|
ComplexTypeModelDescription complexTypeModelDescription = modelDescription as ComplexTypeModelDescription;
|
||||||
|
if (complexTypeModelDescription != null)
|
||||||
|
{
|
||||||
|
return complexTypeModelDescription.Properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectionModelDescription collectionModelDescription = modelDescription as CollectionModelDescription;
|
||||||
|
if (collectionModelDescription != null)
|
||||||
|
{
|
||||||
|
complexTypeModelDescription = collectionModelDescription.ElementDescription as ComplexTypeModelDescription;
|
||||||
|
if (complexTypeModelDescription != null)
|
||||||
|
{
|
||||||
|
return complexTypeModelDescription.Properties;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,444 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Formatting;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Web.Http.Description;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class will generate the samples for the help page.
|
||||||
|
/// </summary>
|
||||||
|
public class HelpPageSampleGenerator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="HelpPageSampleGenerator"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public HelpPageSampleGenerator()
|
||||||
|
{
|
||||||
|
ActualHttpMessageTypes = new Dictionary<HelpPageSampleKey, Type>();
|
||||||
|
ActionSamples = new Dictionary<HelpPageSampleKey, object>();
|
||||||
|
SampleObjects = new Dictionary<Type, object>();
|
||||||
|
SampleObjectFactories = new List<Func<HelpPageSampleGenerator, Type, object>>
|
||||||
|
{
|
||||||
|
DefaultSampleObjectFactory,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets CLR types that are used as the content of <see cref="HttpRequestMessage"/> or <see cref="HttpResponseMessage"/>.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<HelpPageSampleKey, Type> ActualHttpMessageTypes { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the objects that are used directly as samples for certain actions.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<HelpPageSampleKey, object> ActionSamples { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the objects that are serialized as samples by the supported formatters.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<Type, object> SampleObjects { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets factories for the objects that the supported formatters will serialize as samples. Processed in order,
|
||||||
|
/// stopping when the factory successfully returns a non-<see langref="null"/> object.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Collection includes just <see cref="ObjectGenerator.GenerateObject(Type)"/> initially. Use
|
||||||
|
/// <code>SampleObjectFactories.Insert(0, func)</code> to provide an override and
|
||||||
|
/// <code>SampleObjectFactories.Add(func)</code> to provide a fallback.</remarks>
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
|
||||||
|
Justification = "This is an appropriate nesting of generic types")]
|
||||||
|
public IList<Func<HelpPageSampleGenerator, Type, object>> SampleObjectFactories { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the request body samples for a given <see cref="ApiDescription"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="api">The <see cref="ApiDescription"/>.</param>
|
||||||
|
/// <returns>The samples keyed by media type.</returns>
|
||||||
|
public IDictionary<MediaTypeHeaderValue, object> GetSampleRequests(ApiDescription api)
|
||||||
|
{
|
||||||
|
return GetSample(api, SampleDirection.Request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the response body samples for a given <see cref="ApiDescription"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="api">The <see cref="ApiDescription"/>.</param>
|
||||||
|
/// <returns>The samples keyed by media type.</returns>
|
||||||
|
public IDictionary<MediaTypeHeaderValue, object> GetSampleResponses(ApiDescription api)
|
||||||
|
{
|
||||||
|
return GetSample(api, SampleDirection.Response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the request or response body samples.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="api">The <see cref="ApiDescription"/>.</param>
|
||||||
|
/// <param name="sampleDirection">The value indicating whether the sample is for a request or for a response.</param>
|
||||||
|
/// <returns>The samples keyed by media type.</returns>
|
||||||
|
public virtual IDictionary<MediaTypeHeaderValue, object> GetSample(ApiDescription api, SampleDirection sampleDirection)
|
||||||
|
{
|
||||||
|
if (api == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("api");
|
||||||
|
}
|
||||||
|
string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName;
|
||||||
|
string actionName = api.ActionDescriptor.ActionName;
|
||||||
|
IEnumerable<string> parameterNames = api.ParameterDescriptions.Select(p => p.Name);
|
||||||
|
Collection<MediaTypeFormatter> formatters;
|
||||||
|
Type type = ResolveType(api, controllerName, actionName, parameterNames, sampleDirection, out formatters);
|
||||||
|
var samples = new Dictionary<MediaTypeHeaderValue, object>();
|
||||||
|
|
||||||
|
// Use the samples provided directly for actions
|
||||||
|
var actionSamples = GetAllActionSamples(controllerName, actionName, parameterNames, sampleDirection);
|
||||||
|
foreach (var actionSample in actionSamples)
|
||||||
|
{
|
||||||
|
samples.Add(actionSample.Key.MediaType, WrapSampleIfString(actionSample.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the sample generation based on formatters only if an action doesn't return an HttpResponseMessage.
|
||||||
|
// Here we cannot rely on formatters because we don't know what's in the HttpResponseMessage, it might not even use formatters.
|
||||||
|
if (type != null && !typeof(HttpResponseMessage).IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
object sampleObject = GetSampleObject(type);
|
||||||
|
foreach (var formatter in formatters)
|
||||||
|
{
|
||||||
|
foreach (MediaTypeHeaderValue mediaType in formatter.SupportedMediaTypes)
|
||||||
|
{
|
||||||
|
if (!samples.ContainsKey(mediaType))
|
||||||
|
{
|
||||||
|
object sample = GetActionSample(controllerName, actionName, parameterNames, type, formatter, mediaType, sampleDirection);
|
||||||
|
|
||||||
|
// If no sample found, try generate sample using formatter and sample object
|
||||||
|
if (sample == null && sampleObject != null)
|
||||||
|
{
|
||||||
|
sample = WriteSampleObjectUsingFormatter(formatter, sampleObject, type, mediaType);
|
||||||
|
}
|
||||||
|
|
||||||
|
samples.Add(mediaType, WrapSampleIfString(sample));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search for samples that are provided directly through <see cref="ActionSamples"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
/// <param name="parameterNames">The parameter names.</param>
|
||||||
|
/// <param name="type">The CLR type.</param>
|
||||||
|
/// <param name="formatter">The formatter.</param>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
/// <param name="sampleDirection">The value indicating whether the sample is for a request or for a response.</param>
|
||||||
|
/// <returns>The sample that matches the parameters.</returns>
|
||||||
|
public virtual object GetActionSample(string controllerName, string actionName, IEnumerable<string> parameterNames, Type type, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, SampleDirection sampleDirection)
|
||||||
|
{
|
||||||
|
object sample;
|
||||||
|
|
||||||
|
// First, try to get the sample provided for the specified mediaType, sampleDirection, controllerName, actionName and parameterNames.
|
||||||
|
// If not found, try to get the sample provided for the specified mediaType, sampleDirection, controllerName and actionName regardless of the parameterNames.
|
||||||
|
// If still not found, try to get the sample provided for the specified mediaType and type.
|
||||||
|
// Finally, try to get the sample provided for the specified mediaType.
|
||||||
|
if (ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, parameterNames), out sample) ||
|
||||||
|
ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, new[] { "*" }), out sample) ||
|
||||||
|
ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, type), out sample) ||
|
||||||
|
ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType), out sample))
|
||||||
|
{
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the sample object that will be serialized by the formatters.
|
||||||
|
/// First, it will look at the <see cref="SampleObjects"/>. If no sample object is found, it will try to create
|
||||||
|
/// one using <see cref="DefaultSampleObjectFactory"/> (which wraps an <see cref="ObjectGenerator"/>) and other
|
||||||
|
/// factories in <see cref="SampleObjectFactories"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type.</param>
|
||||||
|
/// <returns>The sample object.</returns>
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
|
||||||
|
Justification = "Even if all items in SampleObjectFactories throw, problem will be visible as missing sample.")]
|
||||||
|
public virtual object GetSampleObject(Type type)
|
||||||
|
{
|
||||||
|
object sampleObject;
|
||||||
|
|
||||||
|
if (!SampleObjects.TryGetValue(type, out sampleObject))
|
||||||
|
{
|
||||||
|
// No specific object available, try our factories.
|
||||||
|
foreach (Func<HelpPageSampleGenerator, Type, object> factory in SampleObjectFactories)
|
||||||
|
{
|
||||||
|
if (factory == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sampleObject = factory(this, type);
|
||||||
|
if (sampleObject != null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore any problems encountered in the factory; go on to the next one (if any).
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sampleObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> passed to the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="api">The <see cref="ApiDescription"/>.</param>
|
||||||
|
/// <returns>The type.</returns>
|
||||||
|
public virtual Type ResolveHttpRequestMessageType(ApiDescription api)
|
||||||
|
{
|
||||||
|
string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName;
|
||||||
|
string actionName = api.ActionDescriptor.ActionName;
|
||||||
|
IEnumerable<string> parameterNames = api.ParameterDescriptions.Select(p => p.Name);
|
||||||
|
Collection<MediaTypeFormatter> formatters;
|
||||||
|
return ResolveType(api, controllerName, actionName, parameterNames, SampleDirection.Request, out formatters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves the type of the action parameter or return value when <see cref="HttpRequestMessage"/> or <see cref="HttpResponseMessage"/> is used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="api">The <see cref="ApiDescription"/>.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
/// <param name="parameterNames">The parameter names.</param>
|
||||||
|
/// <param name="sampleDirection">The value indicating whether the sample is for a request or a response.</param>
|
||||||
|
/// <param name="formatters">The formatters.</param>
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is only used in advanced scenarios.")]
|
||||||
|
public virtual Type ResolveType(ApiDescription api, string controllerName, string actionName, IEnumerable<string> parameterNames, SampleDirection sampleDirection, out Collection<MediaTypeFormatter> formatters)
|
||||||
|
{
|
||||||
|
if (!Enum.IsDefined(typeof(SampleDirection), sampleDirection))
|
||||||
|
{
|
||||||
|
throw new InvalidEnumArgumentException("sampleDirection", (int)sampleDirection, typeof(SampleDirection));
|
||||||
|
}
|
||||||
|
if (api == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("api");
|
||||||
|
}
|
||||||
|
Type type;
|
||||||
|
if (ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, parameterNames), out type) ||
|
||||||
|
ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, new[] { "*" }), out type))
|
||||||
|
{
|
||||||
|
// Re-compute the supported formatters based on type
|
||||||
|
Collection<MediaTypeFormatter> newFormatters = new Collection<MediaTypeFormatter>();
|
||||||
|
foreach (var formatter in api.ActionDescriptor.Configuration.Formatters)
|
||||||
|
{
|
||||||
|
if (IsFormatSupported(sampleDirection, formatter, type))
|
||||||
|
{
|
||||||
|
newFormatters.Add(formatter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formatters = newFormatters;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (sampleDirection)
|
||||||
|
{
|
||||||
|
case SampleDirection.Request:
|
||||||
|
ApiParameterDescription requestBodyParameter = api.ParameterDescriptions.FirstOrDefault(p => p.Source == ApiParameterSource.FromBody);
|
||||||
|
type = requestBodyParameter == null ? null : requestBodyParameter.ParameterDescriptor.ParameterType;
|
||||||
|
formatters = api.SupportedRequestBodyFormatters;
|
||||||
|
break;
|
||||||
|
case SampleDirection.Response:
|
||||||
|
default:
|
||||||
|
type = api.ResponseDescription.ResponseType ?? api.ResponseDescription.DeclaredType;
|
||||||
|
formatters = api.SupportedResponseFormatters;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the sample object using formatter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="formatter">The formatter.</param>
|
||||||
|
/// <param name="value">The value.</param>
|
||||||
|
/// <param name="type">The type.</param>
|
||||||
|
/// <param name="mediaType">Type of the media.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as InvalidSample.")]
|
||||||
|
public virtual object WriteSampleObjectUsingFormatter(MediaTypeFormatter formatter, object value, Type type, MediaTypeHeaderValue mediaType)
|
||||||
|
{
|
||||||
|
if (formatter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("formatter");
|
||||||
|
}
|
||||||
|
if (mediaType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("mediaType");
|
||||||
|
}
|
||||||
|
|
||||||
|
object sample = String.Empty;
|
||||||
|
MemoryStream ms = null;
|
||||||
|
HttpContent content = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (formatter.CanWriteType(type))
|
||||||
|
{
|
||||||
|
ms = new MemoryStream();
|
||||||
|
content = new ObjectContent(type, value, formatter, mediaType);
|
||||||
|
formatter.WriteToStreamAsync(type, value, ms, content, null).Wait();
|
||||||
|
ms.Position = 0;
|
||||||
|
StreamReader reader = new StreamReader(ms);
|
||||||
|
string serializedSampleString = reader.ReadToEnd();
|
||||||
|
if (mediaType.MediaType.ToUpperInvariant().Contains("XML"))
|
||||||
|
{
|
||||||
|
serializedSampleString = TryFormatXml(serializedSampleString);
|
||||||
|
}
|
||||||
|
else if (mediaType.MediaType.ToUpperInvariant().Contains("JSON"))
|
||||||
|
{
|
||||||
|
serializedSampleString = TryFormatJson(serializedSampleString);
|
||||||
|
}
|
||||||
|
|
||||||
|
sample = new TextSample(serializedSampleString);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sample = new InvalidSample(String.Format(
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
"Failed to generate the sample for media type '{0}'. Cannot use formatter '{1}' to write type '{2}'.",
|
||||||
|
mediaType,
|
||||||
|
formatter.GetType().Name,
|
||||||
|
type.Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
sample = new InvalidSample(String.Format(
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
"An exception has occurred while using the formatter '{0}' to generate sample for media type '{1}'. Exception message: {2}",
|
||||||
|
formatter.GetType().Name,
|
||||||
|
mediaType.MediaType,
|
||||||
|
UnwrapException(e).Message));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (ms != null)
|
||||||
|
{
|
||||||
|
ms.Dispose();
|
||||||
|
}
|
||||||
|
if (content != null)
|
||||||
|
{
|
||||||
|
content.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Exception UnwrapException(Exception exception)
|
||||||
|
{
|
||||||
|
AggregateException aggregateException = exception as AggregateException;
|
||||||
|
if (aggregateException != null)
|
||||||
|
{
|
||||||
|
return aggregateException.Flatten().InnerException;
|
||||||
|
}
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default factory for sample objects
|
||||||
|
private static object DefaultSampleObjectFactory(HelpPageSampleGenerator sampleGenerator, Type type)
|
||||||
|
{
|
||||||
|
// Try to create a default sample object
|
||||||
|
ObjectGenerator objectGenerator = new ObjectGenerator();
|
||||||
|
return objectGenerator.GenerateObject(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")]
|
||||||
|
private static string TryFormatJson(string str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object parsedJson = JsonConvert.DeserializeObject(str);
|
||||||
|
return JsonConvert.SerializeObject(parsedJson, Formatting.Indented);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// can't parse JSON, return the original string
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")]
|
||||||
|
private static string TryFormatXml(string str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
XDocument xml = XDocument.Parse(str);
|
||||||
|
return xml.ToString();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// can't parse XML, return the original string
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsFormatSupported(SampleDirection sampleDirection, MediaTypeFormatter formatter, Type type)
|
||||||
|
{
|
||||||
|
switch (sampleDirection)
|
||||||
|
{
|
||||||
|
case SampleDirection.Request:
|
||||||
|
return formatter.CanReadType(type);
|
||||||
|
case SampleDirection.Response:
|
||||||
|
return formatter.CanWriteType(type);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<KeyValuePair<HelpPageSampleKey, object>> GetAllActionSamples(string controllerName, string actionName, IEnumerable<string> parameterNames, SampleDirection sampleDirection)
|
||||||
|
{
|
||||||
|
HashSet<string> parameterNamesSet = new HashSet<string>(parameterNames, StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var sample in ActionSamples)
|
||||||
|
{
|
||||||
|
HelpPageSampleKey sampleKey = sample.Key;
|
||||||
|
if (String.Equals(controllerName, sampleKey.ControllerName, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
String.Equals(actionName, sampleKey.ActionName, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
(sampleKey.ParameterNames.SetEquals(new[] { "*" }) || parameterNamesSet.SetEquals(sampleKey.ParameterNames)) &&
|
||||||
|
sampleDirection == sampleKey.SampleDirection)
|
||||||
|
{
|
||||||
|
yield return sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object WrapSampleIfString(object sample)
|
||||||
|
{
|
||||||
|
string stringSample = sample as string;
|
||||||
|
if (stringSample != null)
|
||||||
|
{
|
||||||
|
return new TextSample(stringSample);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is used to identify the place where the sample should be applied.
|
||||||
|
/// </summary>
|
||||||
|
public class HelpPageSampleKey
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="HelpPageSampleKey"/> based on media type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
public HelpPageSampleKey(MediaTypeHeaderValue mediaType)
|
||||||
|
{
|
||||||
|
if (mediaType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("mediaType");
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionName = String.Empty;
|
||||||
|
ControllerName = String.Empty;
|
||||||
|
MediaType = mediaType;
|
||||||
|
ParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="HelpPageSampleKey"/> based on media type and CLR type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
/// <param name="type">The CLR type.</param>
|
||||||
|
public HelpPageSampleKey(MediaTypeHeaderValue mediaType, Type type)
|
||||||
|
: this(mediaType)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
}
|
||||||
|
|
||||||
|
ParameterType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="HelpPageSampleKey"/> based on <see cref="SampleDirection"/>, controller name, action name and parameter names.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sampleDirection">The <see cref="SampleDirection"/>.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
/// <param name="parameterNames">The parameter names.</param>
|
||||||
|
public HelpPageSampleKey(SampleDirection sampleDirection, string controllerName, string actionName, IEnumerable<string> parameterNames)
|
||||||
|
{
|
||||||
|
if (!Enum.IsDefined(typeof(SampleDirection), sampleDirection))
|
||||||
|
{
|
||||||
|
throw new InvalidEnumArgumentException("sampleDirection", (int)sampleDirection, typeof(SampleDirection));
|
||||||
|
}
|
||||||
|
if (controllerName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("controllerName");
|
||||||
|
}
|
||||||
|
if (actionName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("actionName");
|
||||||
|
}
|
||||||
|
if (parameterNames == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("parameterNames");
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerName = controllerName;
|
||||||
|
ActionName = actionName;
|
||||||
|
ParameterNames = new HashSet<string>(parameterNames, StringComparer.OrdinalIgnoreCase);
|
||||||
|
SampleDirection = sampleDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="HelpPageSampleKey"/> based on media type, <see cref="SampleDirection"/>, controller name, action name and parameter names.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mediaType">The media type.</param>
|
||||||
|
/// <param name="sampleDirection">The <see cref="SampleDirection"/>.</param>
|
||||||
|
/// <param name="controllerName">Name of the controller.</param>
|
||||||
|
/// <param name="actionName">Name of the action.</param>
|
||||||
|
/// <param name="parameterNames">The parameter names.</param>
|
||||||
|
public HelpPageSampleKey(MediaTypeHeaderValue mediaType, SampleDirection sampleDirection, string controllerName, string actionName, IEnumerable<string> parameterNames)
|
||||||
|
: this(sampleDirection, controllerName, actionName, parameterNames)
|
||||||
|
{
|
||||||
|
if (mediaType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("mediaType");
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaType = mediaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the controller.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The name of the controller.
|
||||||
|
/// </value>
|
||||||
|
public string ControllerName { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the action.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The name of the action.
|
||||||
|
/// </value>
|
||||||
|
public string ActionName { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the media type.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The media type.
|
||||||
|
/// </value>
|
||||||
|
public MediaTypeHeaderValue MediaType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parameter names.
|
||||||
|
/// </summary>
|
||||||
|
public HashSet<string> ParameterNames { get; private set; }
|
||||||
|
|
||||||
|
public Type ParameterType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="SampleDirection"/>.
|
||||||
|
/// </summary>
|
||||||
|
public SampleDirection? SampleDirection { get; private set; }
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
HelpPageSampleKey otherKey = obj as HelpPageSampleKey;
|
||||||
|
if (otherKey == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.Equals(ControllerName, otherKey.ControllerName, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
String.Equals(ActionName, otherKey.ActionName, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
(MediaType == otherKey.MediaType || (MediaType != null && MediaType.Equals(otherKey.MediaType))) &&
|
||||||
|
ParameterType == otherKey.ParameterType &&
|
||||||
|
SampleDirection == otherKey.SampleDirection &&
|
||||||
|
ParameterNames.SetEquals(otherKey.ParameterNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hashCode = ControllerName.ToUpperInvariant().GetHashCode() ^ ActionName.ToUpperInvariant().GetHashCode();
|
||||||
|
if (MediaType != null)
|
||||||
|
{
|
||||||
|
hashCode ^= MediaType.GetHashCode();
|
||||||
|
}
|
||||||
|
if (SampleDirection != null)
|
||||||
|
{
|
||||||
|
hashCode ^= SampleDirection.GetHashCode();
|
||||||
|
}
|
||||||
|
if (ParameterType != null)
|
||||||
|
{
|
||||||
|
hashCode ^= ParameterType.GetHashCode();
|
||||||
|
}
|
||||||
|
foreach (string parameterName in ParameterNames)
|
||||||
|
{
|
||||||
|
hashCode ^= parameterName.ToUpperInvariant().GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
RMDataManager/Areas/HelpPage/SampleGeneration/ImageSample.cs
Normal file
41
RMDataManager/Areas/HelpPage/SampleGeneration/ImageSample.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This represents an image sample on the help page. There's a display template named ImageSample associated with this class.
|
||||||
|
/// </summary>
|
||||||
|
public class ImageSample
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ImageSample"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="src">The URL of an image.</param>
|
||||||
|
public ImageSample(string src)
|
||||||
|
{
|
||||||
|
if (src == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("src");
|
||||||
|
}
|
||||||
|
Src = src;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Src { get; private set; }
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
ImageSample other = obj as ImageSample;
|
||||||
|
return other != null && Src == other.Src;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Src.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This represents an invalid sample on the help page. There's a display template named InvalidSample associated with this class.
|
||||||
|
/// </summary>
|
||||||
|
public class InvalidSample
|
||||||
|
{
|
||||||
|
public InvalidSample(string errorMessage)
|
||||||
|
{
|
||||||
|
if (errorMessage == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("errorMessage");
|
||||||
|
}
|
||||||
|
ErrorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ErrorMessage { get; private set; }
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
InvalidSample other = obj as InvalidSample;
|
||||||
|
return other != null && ErrorMessage == other.ErrorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return ErrorMessage.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return ErrorMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
456
RMDataManager/Areas/HelpPage/SampleGeneration/ObjectGenerator.cs
Normal file
456
RMDataManager/Areas/HelpPage/SampleGeneration/ObjectGenerator.cs
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class will create an object of a given type and populate it with sample data.
|
||||||
|
/// </summary>
|
||||||
|
public class ObjectGenerator
|
||||||
|
{
|
||||||
|
internal const int DefaultCollectionSize = 2;
|
||||||
|
private readonly SimpleTypeObjectGenerator SimpleObjectGenerator = new SimpleTypeObjectGenerator();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates an object for a given type. The type needs to be public, have a public default constructor and settable public properties/fields. Currently it supports the following types:
|
||||||
|
/// Simple types: <see cref="int"/>, <see cref="string"/>, <see cref="Enum"/>, <see cref="DateTime"/>, <see cref="Uri"/>, etc.
|
||||||
|
/// Complex types: POCO types.
|
||||||
|
/// Nullables: <see cref="Nullable{T}"/>.
|
||||||
|
/// Arrays: arrays of simple types or complex types.
|
||||||
|
/// Key value pairs: <see cref="KeyValuePair{TKey,TValue}"/>
|
||||||
|
/// Tuples: <see cref="Tuple{T1}"/>, <see cref="Tuple{T1,T2}"/>, etc
|
||||||
|
/// Dictionaries: <see cref="IDictionary{TKey,TValue}"/> or anything deriving from <see cref="IDictionary{TKey,TValue}"/>.
|
||||||
|
/// Collections: <see cref="IList{T}"/>, <see cref="IEnumerable{T}"/>, <see cref="ICollection{T}"/>, <see cref="IList"/>, <see cref="IEnumerable"/>, <see cref="ICollection"/> or anything deriving from <see cref="ICollection{T}"/> or <see cref="IList"/>.
|
||||||
|
/// Queryables: <see cref="IQueryable"/>, <see cref="IQueryable{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type.</param>
|
||||||
|
/// <returns>An object of the given type.</returns>
|
||||||
|
public object GenerateObject(Type type)
|
||||||
|
{
|
||||||
|
return GenerateObject(type, new Dictionary<Type, object>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Here we just want to return null if anything goes wrong.")]
|
||||||
|
private object GenerateObject(Type type, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (SimpleTypeObjectGenerator.CanGenerateObject(type))
|
||||||
|
{
|
||||||
|
return SimpleObjectGenerator.GenerateObject(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsArray)
|
||||||
|
{
|
||||||
|
return GenerateArray(type, DefaultCollectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
return GenerateGenericType(type, DefaultCollectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == typeof(IDictionary))
|
||||||
|
{
|
||||||
|
return GenerateDictionary(typeof(Hashtable), DefaultCollectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(IDictionary).IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
return GenerateDictionary(type, DefaultCollectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == typeof(IList) ||
|
||||||
|
type == typeof(IEnumerable) ||
|
||||||
|
type == typeof(ICollection))
|
||||||
|
{
|
||||||
|
return GenerateCollection(typeof(ArrayList), DefaultCollectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(IList).IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
return GenerateCollection(type, DefaultCollectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == typeof(IQueryable))
|
||||||
|
{
|
||||||
|
return GenerateQueryable(type, DefaultCollectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsEnum)
|
||||||
|
{
|
||||||
|
return GenerateEnum(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsPublic || type.IsNestedPublic)
|
||||||
|
{
|
||||||
|
return GenerateComplexObject(type, createdObjectReferences);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Returns null if anything fails
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateGenericType(Type type, int collectionSize, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
Type genericTypeDefinition = type.GetGenericTypeDefinition();
|
||||||
|
if (genericTypeDefinition == typeof(Nullable<>))
|
||||||
|
{
|
||||||
|
return GenerateNullable(type, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (genericTypeDefinition == typeof(KeyValuePair<,>))
|
||||||
|
{
|
||||||
|
return GenerateKeyValuePair(type, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsTuple(genericTypeDefinition))
|
||||||
|
{
|
||||||
|
return GenerateTuple(type, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type[] genericArguments = type.GetGenericArguments();
|
||||||
|
if (genericArguments.Length == 1)
|
||||||
|
{
|
||||||
|
if (genericTypeDefinition == typeof(IList<>) ||
|
||||||
|
genericTypeDefinition == typeof(IEnumerable<>) ||
|
||||||
|
genericTypeDefinition == typeof(ICollection<>))
|
||||||
|
{
|
||||||
|
Type collectionType = typeof(List<>).MakeGenericType(genericArguments);
|
||||||
|
return GenerateCollection(collectionType, collectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (genericTypeDefinition == typeof(IQueryable<>))
|
||||||
|
{
|
||||||
|
return GenerateQueryable(type, collectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type closedCollectionType = typeof(ICollection<>).MakeGenericType(genericArguments[0]);
|
||||||
|
if (closedCollectionType.IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
return GenerateCollection(type, collectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (genericArguments.Length == 2)
|
||||||
|
{
|
||||||
|
if (genericTypeDefinition == typeof(IDictionary<,>))
|
||||||
|
{
|
||||||
|
Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(genericArguments);
|
||||||
|
return GenerateDictionary(dictionaryType, collectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type closedDictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments[0], genericArguments[1]);
|
||||||
|
if (closedDictionaryType.IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
return GenerateDictionary(type, collectionSize, createdObjectReferences);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsPublic || type.IsNestedPublic)
|
||||||
|
{
|
||||||
|
return GenerateComplexObject(type, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateTuple(Type type, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
Type[] genericArgs = type.GetGenericArguments();
|
||||||
|
object[] parameterValues = new object[genericArgs.Length];
|
||||||
|
bool failedToCreateTuple = true;
|
||||||
|
ObjectGenerator objectGenerator = new ObjectGenerator();
|
||||||
|
for (int i = 0; i < genericArgs.Length; i++)
|
||||||
|
{
|
||||||
|
parameterValues[i] = objectGenerator.GenerateObject(genericArgs[i], createdObjectReferences);
|
||||||
|
failedToCreateTuple &= parameterValues[i] == null;
|
||||||
|
}
|
||||||
|
if (failedToCreateTuple)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
object result = Activator.CreateInstance(type, parameterValues);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTuple(Type genericTypeDefinition)
|
||||||
|
{
|
||||||
|
return genericTypeDefinition == typeof(Tuple<>) ||
|
||||||
|
genericTypeDefinition == typeof(Tuple<,>) ||
|
||||||
|
genericTypeDefinition == typeof(Tuple<,,>) ||
|
||||||
|
genericTypeDefinition == typeof(Tuple<,,,>) ||
|
||||||
|
genericTypeDefinition == typeof(Tuple<,,,,>) ||
|
||||||
|
genericTypeDefinition == typeof(Tuple<,,,,,>) ||
|
||||||
|
genericTypeDefinition == typeof(Tuple<,,,,,,>) ||
|
||||||
|
genericTypeDefinition == typeof(Tuple<,,,,,,,>);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateKeyValuePair(Type keyValuePairType, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
Type[] genericArgs = keyValuePairType.GetGenericArguments();
|
||||||
|
Type typeK = genericArgs[0];
|
||||||
|
Type typeV = genericArgs[1];
|
||||||
|
ObjectGenerator objectGenerator = new ObjectGenerator();
|
||||||
|
object keyObject = objectGenerator.GenerateObject(typeK, createdObjectReferences);
|
||||||
|
object valueObject = objectGenerator.GenerateObject(typeV, createdObjectReferences);
|
||||||
|
if (keyObject == null && valueObject == null)
|
||||||
|
{
|
||||||
|
// Failed to create key and values
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
object result = Activator.CreateInstance(keyValuePairType, keyObject, valueObject);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateArray(Type arrayType, int size, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
Type type = arrayType.GetElementType();
|
||||||
|
Array result = Array.CreateInstance(type, size);
|
||||||
|
bool areAllElementsNull = true;
|
||||||
|
ObjectGenerator objectGenerator = new ObjectGenerator();
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
object element = objectGenerator.GenerateObject(type, createdObjectReferences);
|
||||||
|
result.SetValue(element, i);
|
||||||
|
areAllElementsNull &= element == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areAllElementsNull)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateDictionary(Type dictionaryType, int size, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
Type typeK = typeof(object);
|
||||||
|
Type typeV = typeof(object);
|
||||||
|
if (dictionaryType.IsGenericType)
|
||||||
|
{
|
||||||
|
Type[] genericArgs = dictionaryType.GetGenericArguments();
|
||||||
|
typeK = genericArgs[0];
|
||||||
|
typeV = genericArgs[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
object result = Activator.CreateInstance(dictionaryType);
|
||||||
|
MethodInfo addMethod = dictionaryType.GetMethod("Add") ?? dictionaryType.GetMethod("TryAdd");
|
||||||
|
MethodInfo containsMethod = dictionaryType.GetMethod("Contains") ?? dictionaryType.GetMethod("ContainsKey");
|
||||||
|
ObjectGenerator objectGenerator = new ObjectGenerator();
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
object newKey = objectGenerator.GenerateObject(typeK, createdObjectReferences);
|
||||||
|
if (newKey == null)
|
||||||
|
{
|
||||||
|
// Cannot generate a valid key
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsKey = (bool)containsMethod.Invoke(result, new object[] { newKey });
|
||||||
|
if (!containsKey)
|
||||||
|
{
|
||||||
|
object newValue = objectGenerator.GenerateObject(typeV, createdObjectReferences);
|
||||||
|
addMethod.Invoke(result, new object[] { newKey, newValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateEnum(Type enumType)
|
||||||
|
{
|
||||||
|
Array possibleValues = Enum.GetValues(enumType);
|
||||||
|
if (possibleValues.Length > 0)
|
||||||
|
{
|
||||||
|
return possibleValues.GetValue(0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateQueryable(Type queryableType, int size, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
bool isGeneric = queryableType.IsGenericType;
|
||||||
|
object list;
|
||||||
|
if (isGeneric)
|
||||||
|
{
|
||||||
|
Type listType = typeof(List<>).MakeGenericType(queryableType.GetGenericArguments());
|
||||||
|
list = GenerateCollection(listType, size, createdObjectReferences);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list = GenerateArray(typeof(object[]), size, createdObjectReferences);
|
||||||
|
}
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (isGeneric)
|
||||||
|
{
|
||||||
|
Type argumentType = typeof(IEnumerable<>).MakeGenericType(queryableType.GetGenericArguments());
|
||||||
|
MethodInfo asQueryableMethod = typeof(Queryable).GetMethod("AsQueryable", new[] { argumentType });
|
||||||
|
return asQueryableMethod.Invoke(null, new[] { list });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Queryable.AsQueryable((IEnumerable)list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateCollection(Type collectionType, int size, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
Type type = collectionType.IsGenericType ?
|
||||||
|
collectionType.GetGenericArguments()[0] :
|
||||||
|
typeof(object);
|
||||||
|
object result = Activator.CreateInstance(collectionType);
|
||||||
|
MethodInfo addMethod = collectionType.GetMethod("Add");
|
||||||
|
bool areAllElementsNull = true;
|
||||||
|
ObjectGenerator objectGenerator = new ObjectGenerator();
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
object element = objectGenerator.GenerateObject(type, createdObjectReferences);
|
||||||
|
addMethod.Invoke(result, new object[] { element });
|
||||||
|
areAllElementsNull &= element == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areAllElementsNull)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateNullable(Type nullableType, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
Type type = nullableType.GetGenericArguments()[0];
|
||||||
|
ObjectGenerator objectGenerator = new ObjectGenerator();
|
||||||
|
return objectGenerator.GenerateObject(type, createdObjectReferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GenerateComplexObject(Type type, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
object result = null;
|
||||||
|
|
||||||
|
if (createdObjectReferences.TryGetValue(type, out result))
|
||||||
|
{
|
||||||
|
// The object has been created already, just return it. This will handle the circular reference case.
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsValueType)
|
||||||
|
{
|
||||||
|
result = Activator.CreateInstance(type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConstructorInfo defaultCtor = type.GetConstructor(Type.EmptyTypes);
|
||||||
|
if (defaultCtor == null)
|
||||||
|
{
|
||||||
|
// Cannot instantiate the type because it doesn't have a default constructor
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = defaultCtor.Invoke(new object[0]);
|
||||||
|
}
|
||||||
|
createdObjectReferences.Add(type, result);
|
||||||
|
SetPublicProperties(type, result, createdObjectReferences);
|
||||||
|
SetPublicFields(type, result, createdObjectReferences);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetPublicProperties(Type type, object obj, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
ObjectGenerator objectGenerator = new ObjectGenerator();
|
||||||
|
foreach (PropertyInfo property in properties)
|
||||||
|
{
|
||||||
|
if (property.CanWrite)
|
||||||
|
{
|
||||||
|
object propertyValue = objectGenerator.GenerateObject(property.PropertyType, createdObjectReferences);
|
||||||
|
property.SetValue(obj, propertyValue, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetPublicFields(Type type, object obj, Dictionary<Type, object> createdObjectReferences)
|
||||||
|
{
|
||||||
|
FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
ObjectGenerator objectGenerator = new ObjectGenerator();
|
||||||
|
foreach (FieldInfo field in fields)
|
||||||
|
{
|
||||||
|
object fieldValue = objectGenerator.GenerateObject(field.FieldType, createdObjectReferences);
|
||||||
|
field.SetValue(obj, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SimpleTypeObjectGenerator
|
||||||
|
{
|
||||||
|
private long _index = 0;
|
||||||
|
private static readonly Dictionary<Type, Func<long, object>> DefaultGenerators = InitializeGenerators();
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These are simple type factories and cannot be split up.")]
|
||||||
|
private static Dictionary<Type, Func<long, object>> InitializeGenerators()
|
||||||
|
{
|
||||||
|
return new Dictionary<Type, Func<long, object>>
|
||||||
|
{
|
||||||
|
{ typeof(Boolean), index => true },
|
||||||
|
{ typeof(Byte), index => (Byte)64 },
|
||||||
|
{ typeof(Char), index => (Char)65 },
|
||||||
|
{ typeof(DateTime), index => DateTime.Now },
|
||||||
|
{ typeof(DateTimeOffset), index => new DateTimeOffset(DateTime.Now) },
|
||||||
|
{ typeof(DBNull), index => DBNull.Value },
|
||||||
|
{ typeof(Decimal), index => (Decimal)index },
|
||||||
|
{ typeof(Double), index => (Double)(index + 0.1) },
|
||||||
|
{ typeof(Guid), index => Guid.NewGuid() },
|
||||||
|
{ typeof(Int16), index => (Int16)(index % Int16.MaxValue) },
|
||||||
|
{ typeof(Int32), index => (Int32)(index % Int32.MaxValue) },
|
||||||
|
{ typeof(Int64), index => (Int64)index },
|
||||||
|
{ typeof(Object), index => new object() },
|
||||||
|
{ typeof(SByte), index => (SByte)64 },
|
||||||
|
{ typeof(Single), index => (Single)(index + 0.1) },
|
||||||
|
{
|
||||||
|
typeof(String), index =>
|
||||||
|
{
|
||||||
|
return String.Format(CultureInfo.CurrentCulture, "sample string {0}", index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(TimeSpan), index =>
|
||||||
|
{
|
||||||
|
return TimeSpan.FromTicks(1234567);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ typeof(UInt16), index => (UInt16)(index % UInt16.MaxValue) },
|
||||||
|
{ typeof(UInt32), index => (UInt32)(index % UInt32.MaxValue) },
|
||||||
|
{ typeof(UInt64), index => (UInt64)index },
|
||||||
|
{
|
||||||
|
typeof(Uri), index =>
|
||||||
|
{
|
||||||
|
return new Uri(String.Format(CultureInfo.CurrentCulture, "http://webapihelppage{0}.com", index));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanGenerateObject(Type type)
|
||||||
|
{
|
||||||
|
return DefaultGenerators.ContainsKey(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GenerateObject(Type type)
|
||||||
|
{
|
||||||
|
return DefaultGenerators[type](++_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the sample is used for request or response
|
||||||
|
/// </summary>
|
||||||
|
public enum SampleDirection
|
||||||
|
{
|
||||||
|
Request = 0,
|
||||||
|
Response
|
||||||
|
}
|
||||||
|
}
|
37
RMDataManager/Areas/HelpPage/SampleGeneration/TextSample.cs
Normal file
37
RMDataManager/Areas/HelpPage/SampleGeneration/TextSample.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This represents a preformatted text sample on the help page. There's a display template named TextSample associated with this class.
|
||||||
|
/// </summary>
|
||||||
|
public class TextSample
|
||||||
|
{
|
||||||
|
public TextSample(string text)
|
||||||
|
{
|
||||||
|
if (text == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("text");
|
||||||
|
}
|
||||||
|
Text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Text { get; private set; }
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
TextSample other = obj as TextSample;
|
||||||
|
return other != null && Text == other.Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Text.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
RMDataManager/Areas/HelpPage/Views/Help/Api.cshtml
Normal file
22
RMDataManager/Areas/HelpPage/Views/Help/Api.cshtml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@using System.Web.Http
|
||||||
|
@using RMDataManager.Areas.HelpPage.Models
|
||||||
|
@model HelpPageApiModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
var description = Model.ApiDescription;
|
||||||
|
ViewBag.Title = description.HttpMethod.Method + " " + description.RelativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
<link type="text/css" href="~/Areas/HelpPage/HelpPage.css" rel="stylesheet" />
|
||||||
|
<div id="body" class="help-page">
|
||||||
|
<section class="featured">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<p>
|
||||||
|
@Html.ActionLink("Help Page Home", "Index")
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="content-wrapper main-content clear-fix">
|
||||||
|
@Html.DisplayForModel()
|
||||||
|
</section>
|
||||||
|
</div>
|
@ -0,0 +1,41 @@
|
|||||||
|
@using System.Web.Http
|
||||||
|
@using System.Web.Http.Controllers
|
||||||
|
@using System.Web.Http.Description
|
||||||
|
@using RMDataManager.Areas.HelpPage
|
||||||
|
@using RMDataManager.Areas.HelpPage.Models
|
||||||
|
@model IGrouping<HttpControllerDescriptor, ApiDescription>
|
||||||
|
|
||||||
|
@{
|
||||||
|
var controllerDocumentation = ViewBag.DocumentationProvider != null ?
|
||||||
|
ViewBag.DocumentationProvider.GetDocumentation(Model.Key) :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2 id="@Model.Key.ControllerName">@Model.Key.ControllerName</h2>
|
||||||
|
@if (!String.IsNullOrEmpty(controllerDocumentation))
|
||||||
|
{
|
||||||
|
<p>@controllerDocumentation</p>
|
||||||
|
}
|
||||||
|
<table class="help-page-table">
|
||||||
|
<thead>
|
||||||
|
<tr><th>API</th><th>Description</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var api in Model)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td class="api-name"><a href="@Url.Action("Api", "Help", new { apiId = api.GetFriendlyId() })">@api.HttpMethod.Method @api.RelativePath</a></td>
|
||||||
|
<td class="api-documentation">
|
||||||
|
@if (api.Documentation != null)
|
||||||
|
{
|
||||||
|
<p>@api.Documentation</p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>No documentation available.</p>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
@ -0,0 +1,6 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model CollectionModelDescription
|
||||||
|
@if (Model.ElementDescription is ComplexTypeModelDescription)
|
||||||
|
{
|
||||||
|
@Html.DisplayFor(m => m.ElementDescription)
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model ComplexTypeModelDescription
|
||||||
|
@Html.DisplayFor(m => m.Properties, "Parameters")
|
@ -0,0 +1,4 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model DictionaryModelDescription
|
||||||
|
Dictionary of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key]
|
||||||
|
and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value]
|
@ -0,0 +1,24 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model EnumTypeModelDescription
|
||||||
|
|
||||||
|
<p>Possible enumeration values:</p>
|
||||||
|
|
||||||
|
<table class="help-page-table">
|
||||||
|
<thead>
|
||||||
|
<tr><th>Name</th><th>Value</th><th>Description</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (EnumValueDescription value in Model.Values)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td class="enum-name"><b>@value.Name</b></td>
|
||||||
|
<td class="enum-value">
|
||||||
|
<p>@value.Value</p>
|
||||||
|
</td>
|
||||||
|
<td class="enum-description">
|
||||||
|
<p>@value.Documentation</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
@ -0,0 +1,67 @@
|
|||||||
|
@using System.Web.Http
|
||||||
|
@using System.Web.Http.Description
|
||||||
|
@using RMDataManager.Areas.HelpPage.Models
|
||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model HelpPageApiModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ApiDescription description = Model.ApiDescription;
|
||||||
|
}
|
||||||
|
<h1>@description.HttpMethod.Method @description.RelativePath</h1>
|
||||||
|
<div>
|
||||||
|
<p>@description.Documentation</p>
|
||||||
|
|
||||||
|
<h2>Request Information</h2>
|
||||||
|
|
||||||
|
<h3>URI Parameters</h3>
|
||||||
|
@Html.DisplayFor(m => m.UriParameters, "Parameters")
|
||||||
|
|
||||||
|
<h3>Body Parameters</h3>
|
||||||
|
|
||||||
|
<p>@Model.RequestDocumentation</p>
|
||||||
|
|
||||||
|
@if (Model.RequestModelDescription != null)
|
||||||
|
{
|
||||||
|
@Html.DisplayFor(m => m.RequestModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.RequestModelDescription })
|
||||||
|
if (Model.RequestBodyParameters != null)
|
||||||
|
{
|
||||||
|
@Html.DisplayFor(m => m.RequestBodyParameters, "Parameters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>None.</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Model.SampleRequests.Count > 0)
|
||||||
|
{
|
||||||
|
<h3>Request Formats</h3>
|
||||||
|
@Html.DisplayFor(m => m.SampleRequests, "Samples")
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>Response Information</h2>
|
||||||
|
|
||||||
|
<h3>Resource Description</h3>
|
||||||
|
|
||||||
|
<p>@description.ResponseDescription.Documentation</p>
|
||||||
|
|
||||||
|
@if (Model.ResourceDescription != null)
|
||||||
|
{
|
||||||
|
@Html.DisplayFor(m => m.ResourceDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ResourceDescription })
|
||||||
|
if (Model.ResourceProperties != null)
|
||||||
|
{
|
||||||
|
@Html.DisplayFor(m => m.ResourceProperties, "Parameters")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>None.</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Model.SampleResponses.Count > 0)
|
||||||
|
{
|
||||||
|
<h3>Response Formats</h3>
|
||||||
|
@Html.DisplayFor(m => m.SampleResponses, "Samples")
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,4 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage
|
||||||
|
@model ImageSample
|
||||||
|
|
||||||
|
<img src="@Model.Src" />
|
@ -0,0 +1,13 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage
|
||||||
|
@model InvalidSample
|
||||||
|
|
||||||
|
@if (HttpContext.Current.IsDebuggingEnabled)
|
||||||
|
{
|
||||||
|
<div class="warning-message-container">
|
||||||
|
<p>@Model.ErrorMessage</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>Sample not available.</p>
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model KeyValuePairModelDescription
|
||||||
|
Pair of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key]
|
||||||
|
and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value]
|
@ -0,0 +1,26 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model Type
|
||||||
|
@{
|
||||||
|
ModelDescription modelDescription = ViewBag.modelDescription;
|
||||||
|
if (modelDescription is ComplexTypeModelDescription || modelDescription is EnumTypeModelDescription)
|
||||||
|
{
|
||||||
|
if (Model == typeof(Object))
|
||||||
|
{
|
||||||
|
@:Object
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@Html.ActionLink(modelDescription.Name, "ResourceModel", "Help", new { modelName = modelDescription.Name }, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (modelDescription is CollectionModelDescription)
|
||||||
|
{
|
||||||
|
var collectionDescription = modelDescription as CollectionModelDescription;
|
||||||
|
var elementDescription = collectionDescription.ElementDescription;
|
||||||
|
@:Collection of @Html.DisplayFor(m => elementDescription.ModelType, "ModelDescriptionLink", new { modelDescription = elementDescription })
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@Html.DisplayFor(m => modelDescription)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
@using System.Collections.Generic
|
||||||
|
@using System.Collections.ObjectModel
|
||||||
|
@using System.Web.Http.Description
|
||||||
|
@using System.Threading
|
||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model IList<ParameterDescription>
|
||||||
|
|
||||||
|
@if (Model.Count > 0)
|
||||||
|
{
|
||||||
|
<table class="help-page-table">
|
||||||
|
<thead>
|
||||||
|
<tr><th>Name</th><th>Description</th><th>Type</th><th>Additional information</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (ParameterDescription parameter in Model)
|
||||||
|
{
|
||||||
|
ModelDescription modelDescription = parameter.TypeDescription;
|
||||||
|
<tr>
|
||||||
|
<td class="parameter-name">@parameter.Name</td>
|
||||||
|
<td class="parameter-documentation">
|
||||||
|
<p>@parameter.Documentation</p>
|
||||||
|
</td>
|
||||||
|
<td class="parameter-type">
|
||||||
|
@Html.DisplayFor(m => modelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = modelDescription })
|
||||||
|
</td>
|
||||||
|
<td class="parameter-annotations">
|
||||||
|
@if (parameter.Annotations.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var annotation in parameter.Annotations)
|
||||||
|
{
|
||||||
|
<p>@annotation.Documentation</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>None.</p>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>None.</p>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
|||||||
|
@using System.Net.Http.Headers
|
||||||
|
@model Dictionary<MediaTypeHeaderValue, object>
|
||||||
|
|
||||||
|
@{
|
||||||
|
// Group the samples into a single tab if they are the same.
|
||||||
|
Dictionary<string, object> samples = Model.GroupBy(pair => pair.Value).ToDictionary(
|
||||||
|
pair => String.Join(", ", pair.Select(m => m.Key.ToString()).ToArray()),
|
||||||
|
pair => pair.Key);
|
||||||
|
var mediaTypes = samples.Keys;
|
||||||
|
}
|
||||||
|
<div>
|
||||||
|
@foreach (var mediaType in mediaTypes)
|
||||||
|
{
|
||||||
|
<h4 class="sample-header">@mediaType</h4>
|
||||||
|
<div class="sample-content">
|
||||||
|
<span><b>Sample:</b></span>
|
||||||
|
@{
|
||||||
|
var sample = samples[mediaType];
|
||||||
|
if (sample == null)
|
||||||
|
{
|
||||||
|
<p>Sample not available.</p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@Html.DisplayFor(s => sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
@ -0,0 +1,3 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model SimpleTypeModelDescription
|
||||||
|
@Model.Documentation
|
@ -0,0 +1,6 @@
|
|||||||
|
@using RMDataManager.Areas.HelpPage
|
||||||
|
@model TextSample
|
||||||
|
|
||||||
|
<pre class="wrapped">
|
||||||
|
@Model.Text
|
||||||
|
</pre>
|
38
RMDataManager/Areas/HelpPage/Views/Help/Index.cshtml
Normal file
38
RMDataManager/Areas/HelpPage/Views/Help/Index.cshtml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
@using System.Web.Http
|
||||||
|
@using System.Web.Http.Controllers
|
||||||
|
@using System.Web.Http.Description
|
||||||
|
@using System.Collections.ObjectModel
|
||||||
|
@using RMDataManager.Areas.HelpPage.Models
|
||||||
|
@model Collection<ApiDescription>
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "ASP.NET Web API Help Page";
|
||||||
|
|
||||||
|
// Group APIs by controller
|
||||||
|
ILookup<HttpControllerDescriptor, ApiDescription> apiGroups = Model.ToLookup(api => api.ActionDescriptor.ControllerDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
<link type="text/css" href="~/Areas/HelpPage/HelpPage.css" rel="stylesheet" />
|
||||||
|
<header class="help-page">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<div class="float-left">
|
||||||
|
<h1>@ViewBag.Title</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div id="body" class="help-page">
|
||||||
|
<section class="featured">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<h2>Introduction</h2>
|
||||||
|
<p>
|
||||||
|
Provide a general description of your APIs here.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="content-wrapper main-content clear-fix">
|
||||||
|
@foreach (var group in apiGroups)
|
||||||
|
{
|
||||||
|
@Html.DisplayFor(m => group, "ApiGroup")
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
</div>
|
19
RMDataManager/Areas/HelpPage/Views/Help/ResourceModel.cshtml
Normal file
19
RMDataManager/Areas/HelpPage/Views/Help/ResourceModel.cshtml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
@using System.Web.Http
|
||||||
|
@using RMDataManager.Areas.HelpPage.ModelDescriptions
|
||||||
|
@model ModelDescription
|
||||||
|
|
||||||
|
<link type="text/css" href="~/Areas/HelpPage/HelpPage.css" rel="stylesheet" />
|
||||||
|
<div id="body" class="help-page">
|
||||||
|
<section class="featured">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<p>
|
||||||
|
@Html.ActionLink("Help Page Home", "Index")
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<h1>@Model.Name</h1>
|
||||||
|
<p>@Model.Documentation</p>
|
||||||
|
<section class="content-wrapper main-content clear-fix">
|
||||||
|
@Html.DisplayFor(m => Model)
|
||||||
|
</section>
|
||||||
|
</div>
|
12
RMDataManager/Areas/HelpPage/Views/Shared/_Layout.cshtml
Normal file
12
RMDataManager/Areas/HelpPage/Views/Shared/_Layout.cshtml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<title>@ViewBag.Title</title>
|
||||||
|
@RenderSection("scripts", required: false)
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
@RenderBody()
|
||||||
|
</body>
|
||||||
|
</html>
|
41
RMDataManager/Areas/HelpPage/Views/Web.config
Normal file
41
RMDataManager/Areas/HelpPage/Views/Web.config
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||||
|
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||||
|
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||||
|
</sectionGroup>
|
||||||
|
</configSections>
|
||||||
|
|
||||||
|
<system.web.webPages.razor>
|
||||||
|
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
||||||
|
<pages pageBaseType="System.Web.Mvc.WebViewPage">
|
||||||
|
<namespaces>
|
||||||
|
<add namespace="System.Web.Mvc" />
|
||||||
|
<add namespace="System.Web.Mvc.Ajax" />
|
||||||
|
<add namespace="System.Web.Mvc.Html" />
|
||||||
|
<add namespace="System.Web.Routing" />
|
||||||
|
</namespaces>
|
||||||
|
</pages>
|
||||||
|
</system.web.webPages.razor>
|
||||||
|
|
||||||
|
<appSettings>
|
||||||
|
<add key="webpages:Enabled" value="false" />
|
||||||
|
</appSettings>
|
||||||
|
|
||||||
|
<system.web>
|
||||||
|
<compilation debug="true">
|
||||||
|
<assemblies>
|
||||||
|
<add assembly="System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||||
|
</assemblies>
|
||||||
|
</compilation>
|
||||||
|
</system.web>
|
||||||
|
|
||||||
|
<system.webServer>
|
||||||
|
<handlers>
|
||||||
|
<remove name="BlockViewHandler"/>
|
||||||
|
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
|
||||||
|
</handlers>
|
||||||
|
</system.webServer>
|
||||||
|
</configuration>
|
4
RMDataManager/Areas/HelpPage/Views/_ViewStart.cshtml
Normal file
4
RMDataManager/Areas/HelpPage/Views/_ViewStart.cshtml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@{
|
||||||
|
// Change the Layout path below to blend the look and feel of the help page with your existing web pages.
|
||||||
|
Layout = "~/Areas/HelpPage/Views/Shared/_Layout.cshtml";
|
||||||
|
}
|
161
RMDataManager/Areas/HelpPage/XmlDocumentationProvider.cs
Normal file
161
RMDataManager/Areas/HelpPage/XmlDocumentationProvider.cs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Web.Http.Controllers;
|
||||||
|
using System.Web.Http.Description;
|
||||||
|
using System.Xml.XPath;
|
||||||
|
using RMDataManager.Areas.HelpPage.ModelDescriptions;
|
||||||
|
|
||||||
|
namespace RMDataManager.Areas.HelpPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A custom <see cref="IDocumentationProvider"/> that reads the API documentation from an XML documentation file.
|
||||||
|
/// </summary>
|
||||||
|
public class XmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
|
||||||
|
{
|
||||||
|
private XPathNavigator _documentNavigator;
|
||||||
|
private const string TypeExpression = "/doc/members/member[@name='T:{0}']";
|
||||||
|
private const string MethodExpression = "/doc/members/member[@name='M:{0}']";
|
||||||
|
private const string PropertyExpression = "/doc/members/member[@name='P:{0}']";
|
||||||
|
private const string FieldExpression = "/doc/members/member[@name='F:{0}']";
|
||||||
|
private const string ParameterExpression = "param[@name='{0}']";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="XmlDocumentationProvider"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="documentPath">The physical path to XML document.</param>
|
||||||
|
public XmlDocumentationProvider(string documentPath)
|
||||||
|
{
|
||||||
|
if (documentPath == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("documentPath");
|
||||||
|
}
|
||||||
|
XPathDocument xpath = new XPathDocument(documentPath);
|
||||||
|
_documentNavigator = xpath.CreateNavigator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetDocumentation(HttpControllerDescriptor controllerDescriptor)
|
||||||
|
{
|
||||||
|
XPathNavigator typeNode = GetTypeNode(controllerDescriptor.ControllerType);
|
||||||
|
return GetTagValue(typeNode, "summary");
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor)
|
||||||
|
{
|
||||||
|
XPathNavigator methodNode = GetMethodNode(actionDescriptor);
|
||||||
|
return GetTagValue(methodNode, "summary");
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
|
||||||
|
{
|
||||||
|
ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor;
|
||||||
|
if (reflectedParameterDescriptor != null)
|
||||||
|
{
|
||||||
|
XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor);
|
||||||
|
if (methodNode != null)
|
||||||
|
{
|
||||||
|
string parameterName = reflectedParameterDescriptor.ParameterInfo.Name;
|
||||||
|
XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName));
|
||||||
|
if (parameterNode != null)
|
||||||
|
{
|
||||||
|
return parameterNode.Value.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor)
|
||||||
|
{
|
||||||
|
XPathNavigator methodNode = GetMethodNode(actionDescriptor);
|
||||||
|
return GetTagValue(methodNode, "returns");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetDocumentation(MemberInfo member)
|
||||||
|
{
|
||||||
|
string memberName = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name);
|
||||||
|
string expression = member.MemberType == MemberTypes.Field ? FieldExpression : PropertyExpression;
|
||||||
|
string selectExpression = String.Format(CultureInfo.InvariantCulture, expression, memberName);
|
||||||
|
XPathNavigator propertyNode = _documentNavigator.SelectSingleNode(selectExpression);
|
||||||
|
return GetTagValue(propertyNode, "summary");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetDocumentation(Type type)
|
||||||
|
{
|
||||||
|
XPathNavigator typeNode = GetTypeNode(type);
|
||||||
|
return GetTagValue(typeNode, "summary");
|
||||||
|
}
|
||||||
|
|
||||||
|
private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor)
|
||||||
|
{
|
||||||
|
ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor;
|
||||||
|
if (reflectedActionDescriptor != null)
|
||||||
|
{
|
||||||
|
string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo));
|
||||||
|
return _documentNavigator.SelectSingleNode(selectExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetMemberName(MethodInfo method)
|
||||||
|
{
|
||||||
|
string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(method.DeclaringType), method.Name);
|
||||||
|
ParameterInfo[] parameters = method.GetParameters();
|
||||||
|
if (parameters.Length != 0)
|
||||||
|
{
|
||||||
|
string[] parameterTypeNames = parameters.Select(param => GetTypeName(param.ParameterType)).ToArray();
|
||||||
|
name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTagValue(XPathNavigator parentNode, string tagName)
|
||||||
|
{
|
||||||
|
if (parentNode != null)
|
||||||
|
{
|
||||||
|
XPathNavigator node = parentNode.SelectSingleNode(tagName);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
return node.Value.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private XPathNavigator GetTypeNode(Type type)
|
||||||
|
{
|
||||||
|
string controllerTypeName = GetTypeName(type);
|
||||||
|
string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName);
|
||||||
|
return _documentNavigator.SelectSingleNode(selectExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTypeName(Type type)
|
||||||
|
{
|
||||||
|
string name = type.FullName;
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
// Format the generic type name to something like: Generic{System.Int32,System.String}
|
||||||
|
Type genericType = type.GetGenericTypeDefinition();
|
||||||
|
Type[] genericArguments = type.GetGenericArguments();
|
||||||
|
string genericTypeName = genericType.FullName;
|
||||||
|
|
||||||
|
// Trim the generic parameter counts from the name
|
||||||
|
genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`'));
|
||||||
|
string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray();
|
||||||
|
name = String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, String.Join(",", argumentTypeNames));
|
||||||
|
}
|
||||||
|
if (type.IsNested)
|
||||||
|
{
|
||||||
|
// Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax.
|
||||||
|
name = name.Replace("+", ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
RMDataManager/Content/Site.css
Normal file
23
RMDataManager/Content/Site.css
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
body {
|
||||||
|
padding-top: 50px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set padding to keep content from hitting the edges */
|
||||||
|
.body-content {
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set width on the form input elements since they're 100% wide by default */
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
max-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-inverse .navbar-toggle:hover,
|
||||||
|
.navbar-inverse .navbar-toggle:focus {
|
||||||
|
background-color: #777;
|
||||||
|
border-color: #fff
|
||||||
|
}
|
587
RMDataManager/Content/bootstrap-theme.css
vendored
Normal file
587
RMDataManager/Content/bootstrap-theme.css
vendored
Normal file
@ -0,0 +1,587 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap v3.4.1 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2019 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||||
|
*/
|
||||||
|
.btn-default,
|
||||||
|
.btn-primary,
|
||||||
|
.btn-success,
|
||||||
|
.btn-info,
|
||||||
|
.btn-warning,
|
||||||
|
.btn-danger {
|
||||||
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
|
}
|
||||||
|
.btn-default:active,
|
||||||
|
.btn-primary:active,
|
||||||
|
.btn-success:active,
|
||||||
|
.btn-info:active,
|
||||||
|
.btn-warning:active,
|
||||||
|
.btn-danger:active,
|
||||||
|
.btn-default.active,
|
||||||
|
.btn-primary.active,
|
||||||
|
.btn-success.active,
|
||||||
|
.btn-info.active,
|
||||||
|
.btn-warning.active,
|
||||||
|
.btn-danger.active {
|
||||||
|
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||||
|
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||||
|
}
|
||||||
|
.btn-default.disabled,
|
||||||
|
.btn-primary.disabled,
|
||||||
|
.btn-success.disabled,
|
||||||
|
.btn-info.disabled,
|
||||||
|
.btn-warning.disabled,
|
||||||
|
.btn-danger.disabled,
|
||||||
|
.btn-default[disabled],
|
||||||
|
.btn-primary[disabled],
|
||||||
|
.btn-success[disabled],
|
||||||
|
.btn-info[disabled],
|
||||||
|
.btn-warning[disabled],
|
||||||
|
.btn-danger[disabled],
|
||||||
|
fieldset[disabled] .btn-default,
|
||||||
|
fieldset[disabled] .btn-primary,
|
||||||
|
fieldset[disabled] .btn-success,
|
||||||
|
fieldset[disabled] .btn-info,
|
||||||
|
fieldset[disabled] .btn-warning,
|
||||||
|
fieldset[disabled] .btn-danger {
|
||||||
|
-webkit-box-shadow: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.btn-default .badge,
|
||||||
|
.btn-primary .badge,
|
||||||
|
.btn-success .badge,
|
||||||
|
.btn-info .badge,
|
||||||
|
.btn-warning .badge,
|
||||||
|
.btn-danger .badge {
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
.btn:active,
|
||||||
|
.btn.active {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.btn-default {
|
||||||
|
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
|
||||||
|
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #dbdbdb;
|
||||||
|
text-shadow: 0 1px 0 #fff;
|
||||||
|
border-color: #ccc;
|
||||||
|
}
|
||||||
|
.btn-default:hover,
|
||||||
|
.btn-default:focus {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
background-position: 0 -15px;
|
||||||
|
}
|
||||||
|
.btn-default:active,
|
||||||
|
.btn-default.active {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
border-color: #dbdbdb;
|
||||||
|
}
|
||||||
|
.btn-default.disabled,
|
||||||
|
.btn-default[disabled],
|
||||||
|
fieldset[disabled] .btn-default,
|
||||||
|
.btn-default.disabled:hover,
|
||||||
|
.btn-default[disabled]:hover,
|
||||||
|
fieldset[disabled] .btn-default:hover,
|
||||||
|
.btn-default.disabled:focus,
|
||||||
|
.btn-default[disabled]:focus,
|
||||||
|
fieldset[disabled] .btn-default:focus,
|
||||||
|
.btn-default.disabled.focus,
|
||||||
|
.btn-default[disabled].focus,
|
||||||
|
fieldset[disabled] .btn-default.focus,
|
||||||
|
.btn-default.disabled:active,
|
||||||
|
.btn-default[disabled]:active,
|
||||||
|
fieldset[disabled] .btn-default:active,
|
||||||
|
.btn-default.disabled.active,
|
||||||
|
.btn-default[disabled].active,
|
||||||
|
fieldset[disabled] .btn-default.active {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.btn-primary {
|
||||||
|
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
|
||||||
|
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #245580;
|
||||||
|
}
|
||||||
|
.btn-primary:hover,
|
||||||
|
.btn-primary:focus {
|
||||||
|
background-color: #265a88;
|
||||||
|
background-position: 0 -15px;
|
||||||
|
}
|
||||||
|
.btn-primary:active,
|
||||||
|
.btn-primary.active {
|
||||||
|
background-color: #265a88;
|
||||||
|
border-color: #245580;
|
||||||
|
}
|
||||||
|
.btn-primary.disabled,
|
||||||
|
.btn-primary[disabled],
|
||||||
|
fieldset[disabled] .btn-primary,
|
||||||
|
.btn-primary.disabled:hover,
|
||||||
|
.btn-primary[disabled]:hover,
|
||||||
|
fieldset[disabled] .btn-primary:hover,
|
||||||
|
.btn-primary.disabled:focus,
|
||||||
|
.btn-primary[disabled]:focus,
|
||||||
|
fieldset[disabled] .btn-primary:focus,
|
||||||
|
.btn-primary.disabled.focus,
|
||||||
|
.btn-primary[disabled].focus,
|
||||||
|
fieldset[disabled] .btn-primary.focus,
|
||||||
|
.btn-primary.disabled:active,
|
||||||
|
.btn-primary[disabled]:active,
|
||||||
|
fieldset[disabled] .btn-primary:active,
|
||||||
|
.btn-primary.disabled.active,
|
||||||
|
.btn-primary[disabled].active,
|
||||||
|
fieldset[disabled] .btn-primary.active {
|
||||||
|
background-color: #265a88;
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.btn-success {
|
||||||
|
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
|
||||||
|
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #3e8f3e;
|
||||||
|
}
|
||||||
|
.btn-success:hover,
|
||||||
|
.btn-success:focus {
|
||||||
|
background-color: #419641;
|
||||||
|
background-position: 0 -15px;
|
||||||
|
}
|
||||||
|
.btn-success:active,
|
||||||
|
.btn-success.active {
|
||||||
|
background-color: #419641;
|
||||||
|
border-color: #3e8f3e;
|
||||||
|
}
|
||||||
|
.btn-success.disabled,
|
||||||
|
.btn-success[disabled],
|
||||||
|
fieldset[disabled] .btn-success,
|
||||||
|
.btn-success.disabled:hover,
|
||||||
|
.btn-success[disabled]:hover,
|
||||||
|
fieldset[disabled] .btn-success:hover,
|
||||||
|
.btn-success.disabled:focus,
|
||||||
|
.btn-success[disabled]:focus,
|
||||||
|
fieldset[disabled] .btn-success:focus,
|
||||||
|
.btn-success.disabled.focus,
|
||||||
|
.btn-success[disabled].focus,
|
||||||
|
fieldset[disabled] .btn-success.focus,
|
||||||
|
.btn-success.disabled:active,
|
||||||
|
.btn-success[disabled]:active,
|
||||||
|
fieldset[disabled] .btn-success:active,
|
||||||
|
.btn-success.disabled.active,
|
||||||
|
.btn-success[disabled].active,
|
||||||
|
fieldset[disabled] .btn-success.active {
|
||||||
|
background-color: #419641;
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.btn-info {
|
||||||
|
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
|
||||||
|
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #28a4c9;
|
||||||
|
}
|
||||||
|
.btn-info:hover,
|
||||||
|
.btn-info:focus {
|
||||||
|
background-color: #2aabd2;
|
||||||
|
background-position: 0 -15px;
|
||||||
|
}
|
||||||
|
.btn-info:active,
|
||||||
|
.btn-info.active {
|
||||||
|
background-color: #2aabd2;
|
||||||
|
border-color: #28a4c9;
|
||||||
|
}
|
||||||
|
.btn-info.disabled,
|
||||||
|
.btn-info[disabled],
|
||||||
|
fieldset[disabled] .btn-info,
|
||||||
|
.btn-info.disabled:hover,
|
||||||
|
.btn-info[disabled]:hover,
|
||||||
|
fieldset[disabled] .btn-info:hover,
|
||||||
|
.btn-info.disabled:focus,
|
||||||
|
.btn-info[disabled]:focus,
|
||||||
|
fieldset[disabled] .btn-info:focus,
|
||||||
|
.btn-info.disabled.focus,
|
||||||
|
.btn-info[disabled].focus,
|
||||||
|
fieldset[disabled] .btn-info.focus,
|
||||||
|
.btn-info.disabled:active,
|
||||||
|
.btn-info[disabled]:active,
|
||||||
|
fieldset[disabled] .btn-info:active,
|
||||||
|
.btn-info.disabled.active,
|
||||||
|
.btn-info[disabled].active,
|
||||||
|
fieldset[disabled] .btn-info.active {
|
||||||
|
background-color: #2aabd2;
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.btn-warning {
|
||||||
|
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
|
||||||
|
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #e38d13;
|
||||||
|
}
|
||||||
|
.btn-warning:hover,
|
||||||
|
.btn-warning:focus {
|
||||||
|
background-color: #eb9316;
|
||||||
|
background-position: 0 -15px;
|
||||||
|
}
|
||||||
|
.btn-warning:active,
|
||||||
|
.btn-warning.active {
|
||||||
|
background-color: #eb9316;
|
||||||
|
border-color: #e38d13;
|
||||||
|
}
|
||||||
|
.btn-warning.disabled,
|
||||||
|
.btn-warning[disabled],
|
||||||
|
fieldset[disabled] .btn-warning,
|
||||||
|
.btn-warning.disabled:hover,
|
||||||
|
.btn-warning[disabled]:hover,
|
||||||
|
fieldset[disabled] .btn-warning:hover,
|
||||||
|
.btn-warning.disabled:focus,
|
||||||
|
.btn-warning[disabled]:focus,
|
||||||
|
fieldset[disabled] .btn-warning:focus,
|
||||||
|
.btn-warning.disabled.focus,
|
||||||
|
.btn-warning[disabled].focus,
|
||||||
|
fieldset[disabled] .btn-warning.focus,
|
||||||
|
.btn-warning.disabled:active,
|
||||||
|
.btn-warning[disabled]:active,
|
||||||
|
fieldset[disabled] .btn-warning:active,
|
||||||
|
.btn-warning.disabled.active,
|
||||||
|
.btn-warning[disabled].active,
|
||||||
|
fieldset[disabled] .btn-warning.active {
|
||||||
|
background-color: #eb9316;
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.btn-danger {
|
||||||
|
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
|
||||||
|
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #b92c28;
|
||||||
|
}
|
||||||
|
.btn-danger:hover,
|
||||||
|
.btn-danger:focus {
|
||||||
|
background-color: #c12e2a;
|
||||||
|
background-position: 0 -15px;
|
||||||
|
}
|
||||||
|
.btn-danger:active,
|
||||||
|
.btn-danger.active {
|
||||||
|
background-color: #c12e2a;
|
||||||
|
border-color: #b92c28;
|
||||||
|
}
|
||||||
|
.btn-danger.disabled,
|
||||||
|
.btn-danger[disabled],
|
||||||
|
fieldset[disabled] .btn-danger,
|
||||||
|
.btn-danger.disabled:hover,
|
||||||
|
.btn-danger[disabled]:hover,
|
||||||
|
fieldset[disabled] .btn-danger:hover,
|
||||||
|
.btn-danger.disabled:focus,
|
||||||
|
.btn-danger[disabled]:focus,
|
||||||
|
fieldset[disabled] .btn-danger:focus,
|
||||||
|
.btn-danger.disabled.focus,
|
||||||
|
.btn-danger[disabled].focus,
|
||||||
|
fieldset[disabled] .btn-danger.focus,
|
||||||
|
.btn-danger.disabled:active,
|
||||||
|
.btn-danger[disabled]:active,
|
||||||
|
fieldset[disabled] .btn-danger:active,
|
||||||
|
.btn-danger.disabled.active,
|
||||||
|
.btn-danger[disabled].active,
|
||||||
|
fieldset[disabled] .btn-danger.active {
|
||||||
|
background-color: #c12e2a;
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.thumbnail,
|
||||||
|
.img-thumbnail {
|
||||||
|
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
|
}
|
||||||
|
.dropdown-menu > li > a:hover,
|
||||||
|
.dropdown-menu > li > a:focus {
|
||||||
|
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||||
|
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
background-color: #e8e8e8;
|
||||||
|
}
|
||||||
|
.dropdown-menu > .active > a,
|
||||||
|
.dropdown-menu > .active > a:hover,
|
||||||
|
.dropdown-menu > .active > a:focus {
|
||||||
|
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||||
|
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
background-color: #2e6da4;
|
||||||
|
}
|
||||||
|
.navbar-default {
|
||||||
|
background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
|
||||||
|
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
|
border-radius: 4px;
|
||||||
|
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
|
||||||
|
}
|
||||||
|
.navbar-default .navbar-nav > .open > a,
|
||||||
|
.navbar-default .navbar-nav > .active > a {
|
||||||
|
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
|
||||||
|
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
|
||||||
|
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
|
||||||
|
}
|
||||||
|
.navbar-brand,
|
||||||
|
.navbar-nav > li > a {
|
||||||
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
.navbar-inverse {
|
||||||
|
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
|
||||||
|
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.navbar-inverse .navbar-nav > .open > a,
|
||||||
|
.navbar-inverse .navbar-nav > .active > a {
|
||||||
|
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
|
||||||
|
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
|
||||||
|
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.navbar-inverse .navbar-brand,
|
||||||
|
.navbar-inverse .navbar-nav > li > a {
|
||||||
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.navbar-static-top,
|
||||||
|
.navbar-fixed-top,
|
||||||
|
.navbar-fixed-bottom {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.navbar .navbar-nav .open .dropdown-menu > .active > a,
|
||||||
|
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
|
||||||
|
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
|
||||||
|
color: #fff;
|
||||||
|
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||||
|
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alert {
|
||||||
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||||
|
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.alert-success {
|
||||||
|
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
|
||||||
|
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #b2dba1;
|
||||||
|
}
|
||||||
|
.alert-info {
|
||||||
|
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
|
||||||
|
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #9acfea;
|
||||||
|
}
|
||||||
|
.alert-warning {
|
||||||
|
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
|
||||||
|
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #f5e79e;
|
||||||
|
}
|
||||||
|
.alert-danger {
|
||||||
|
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
|
||||||
|
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #dca7a7;
|
||||||
|
}
|
||||||
|
.progress {
|
||||||
|
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
|
||||||
|
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.progress-bar {
|
||||||
|
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
|
||||||
|
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.progress-bar-success {
|
||||||
|
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
|
||||||
|
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.progress-bar-info {
|
||||||
|
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
|
||||||
|
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.progress-bar-warning {
|
||||||
|
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
|
||||||
|
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.progress-bar-danger {
|
||||||
|
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
|
||||||
|
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.progress-bar-striped {
|
||||||
|
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
||||||
|
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
||||||
|
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
||||||
|
}
|
||||||
|
.list-group {
|
||||||
|
border-radius: 4px;
|
||||||
|
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
|
}
|
||||||
|
.list-group-item.active,
|
||||||
|
.list-group-item.active:hover,
|
||||||
|
.list-group-item.active:focus {
|
||||||
|
text-shadow: 0 -1px 0 #286090;
|
||||||
|
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
|
||||||
|
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #2b669a;
|
||||||
|
}
|
||||||
|
.list-group-item.active .badge,
|
||||||
|
.list-group-item.active:hover .badge,
|
||||||
|
.list-group-item.active:focus .badge {
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
.panel {
|
||||||
|
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.panel-default > .panel-heading {
|
||||||
|
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||||
|
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.panel-primary > .panel-heading {
|
||||||
|
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||||
|
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.panel-success > .panel-heading {
|
||||||
|
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
|
||||||
|
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.panel-info > .panel-heading {
|
||||||
|
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
|
||||||
|
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.panel-warning > .panel-heading {
|
||||||
|
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
|
||||||
|
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.panel-danger > .panel-heading {
|
||||||
|
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
|
||||||
|
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.well {
|
||||||
|
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
|
||||||
|
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border-color: #dcdcdc;
|
||||||
|
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=bootstrap-theme.css.map */
|
1
RMDataManager/Content/bootstrap-theme.css.map
Normal file
1
RMDataManager/Content/bootstrap-theme.css.map
Normal file
File diff suppressed because one or more lines are too long
6
RMDataManager/Content/bootstrap-theme.min.css
vendored
Normal file
6
RMDataManager/Content/bootstrap-theme.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
RMDataManager/Content/bootstrap-theme.min.css.map
Normal file
1
RMDataManager/Content/bootstrap-theme.min.css.map
Normal file
File diff suppressed because one or more lines are too long
6834
RMDataManager/Content/bootstrap.css
vendored
Normal file
6834
RMDataManager/Content/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
RMDataManager/Content/bootstrap.css.map
Normal file
1
RMDataManager/Content/bootstrap.css.map
Normal file
File diff suppressed because one or more lines are too long
6
RMDataManager/Content/bootstrap.min.css
vendored
Normal file
6
RMDataManager/Content/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
RMDataManager/Content/bootstrap.min.css.map
Normal file
1
RMDataManager/Content/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
494
RMDataManager/Controllers/AccountController.cs
Normal file
494
RMDataManager/Controllers/AccountController.cs
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Http;
|
||||||
|
using System.Web.Http.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Identity.EntityFramework;
|
||||||
|
using Microsoft.AspNet.Identity.Owin;
|
||||||
|
using Microsoft.Owin.Security;
|
||||||
|
using Microsoft.Owin.Security.Cookies;
|
||||||
|
using Microsoft.Owin.Security.OAuth;
|
||||||
|
using RMDataManager.Models;
|
||||||
|
using RMDataManager.Providers;
|
||||||
|
using RMDataManager.Results;
|
||||||
|
|
||||||
|
namespace RMDataManager.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
[RoutePrefix("api/Account")]
|
||||||
|
public class AccountController : ApiController
|
||||||
|
{
|
||||||
|
private const string LocalLoginProvider = "Local";
|
||||||
|
private ApplicationUserManager _userManager;
|
||||||
|
|
||||||
|
public AccountController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountController(ApplicationUserManager userManager,
|
||||||
|
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
|
||||||
|
{
|
||||||
|
UserManager = userManager;
|
||||||
|
AccessTokenFormat = accessTokenFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationUserManager UserManager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_userManager = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; private set; }
|
||||||
|
|
||||||
|
// GET api/Account/UserInfo
|
||||||
|
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
|
||||||
|
[Route("UserInfo")]
|
||||||
|
public UserInfoViewModel GetUserInfo()
|
||||||
|
{
|
||||||
|
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
|
||||||
|
|
||||||
|
return new UserInfoViewModel
|
||||||
|
{
|
||||||
|
Email = User.Identity.GetUserName(),
|
||||||
|
HasRegistered = externalLogin == null,
|
||||||
|
LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/Account/Logout
|
||||||
|
[Route("Logout")]
|
||||||
|
public IHttpActionResult Logout()
|
||||||
|
{
|
||||||
|
Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET api/Account/ManageInfo?returnUrl=%2F&generateState=true
|
||||||
|
[Route("ManageInfo")]
|
||||||
|
public async Task<ManageInfoViewModel> GetManageInfo(string returnUrl, bool generateState = false)
|
||||||
|
{
|
||||||
|
IdentityUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UserLoginInfoViewModel> logins = new List<UserLoginInfoViewModel>();
|
||||||
|
|
||||||
|
foreach (IdentityUserLogin linkedAccount in user.Logins)
|
||||||
|
{
|
||||||
|
logins.Add(new UserLoginInfoViewModel
|
||||||
|
{
|
||||||
|
LoginProvider = linkedAccount.LoginProvider,
|
||||||
|
ProviderKey = linkedAccount.ProviderKey
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.PasswordHash != null)
|
||||||
|
{
|
||||||
|
logins.Add(new UserLoginInfoViewModel
|
||||||
|
{
|
||||||
|
LoginProvider = LocalLoginProvider,
|
||||||
|
ProviderKey = user.UserName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ManageInfoViewModel
|
||||||
|
{
|
||||||
|
LocalLoginProvider = LocalLoginProvider,
|
||||||
|
Email = user.UserName,
|
||||||
|
Logins = logins,
|
||||||
|
ExternalLoginProviders = GetExternalLogins(returnUrl, generateState)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/Account/ChangePassword
|
||||||
|
[Route("ChangePassword")]
|
||||||
|
public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword,
|
||||||
|
model.NewPassword);
|
||||||
|
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
return GetErrorResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/Account/SetPassword
|
||||||
|
[Route("SetPassword")]
|
||||||
|
public async Task<IHttpActionResult> SetPassword(SetPasswordBindingModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
|
||||||
|
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
return GetErrorResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/Account/AddExternalLogin
|
||||||
|
[Route("AddExternalLogin")]
|
||||||
|
public async Task<IHttpActionResult> AddExternalLogin(AddExternalLoginBindingModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
|
||||||
|
|
||||||
|
AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken);
|
||||||
|
|
||||||
|
if (ticket == null || ticket.Identity == null || (ticket.Properties != null
|
||||||
|
&& ticket.Properties.ExpiresUtc.HasValue
|
||||||
|
&& ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow))
|
||||||
|
{
|
||||||
|
return BadRequest("External login failure.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity);
|
||||||
|
|
||||||
|
if (externalData == null)
|
||||||
|
{
|
||||||
|
return BadRequest("The external login is already associated with an account.");
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(),
|
||||||
|
new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey));
|
||||||
|
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
return GetErrorResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/Account/RemoveLogin
|
||||||
|
[Route("RemoveLogin")]
|
||||||
|
public async Task<IHttpActionResult> RemoveLogin(RemoveLoginBindingModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityResult result;
|
||||||
|
|
||||||
|
if (model.LoginProvider == LocalLoginProvider)
|
||||||
|
{
|
||||||
|
result = await UserManager.RemovePasswordAsync(User.Identity.GetUserId());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(),
|
||||||
|
new UserLoginInfo(model.LoginProvider, model.ProviderKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
return GetErrorResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET api/Account/ExternalLogin
|
||||||
|
[OverrideAuthentication]
|
||||||
|
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[Route("ExternalLogin", Name = "ExternalLogin")]
|
||||||
|
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
|
||||||
|
{
|
||||||
|
if (error != null)
|
||||||
|
{
|
||||||
|
return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!User.Identity.IsAuthenticated)
|
||||||
|
{
|
||||||
|
return new ChallengeResult(provider, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
|
||||||
|
|
||||||
|
if (externalLogin == null)
|
||||||
|
{
|
||||||
|
return InternalServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (externalLogin.LoginProvider != provider)
|
||||||
|
{
|
||||||
|
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
|
||||||
|
return new ChallengeResult(provider, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
|
||||||
|
externalLogin.ProviderKey));
|
||||||
|
|
||||||
|
bool hasRegistered = user != null;
|
||||||
|
|
||||||
|
if (hasRegistered)
|
||||||
|
{
|
||||||
|
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
|
||||||
|
|
||||||
|
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
|
||||||
|
OAuthDefaults.AuthenticationType);
|
||||||
|
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
|
||||||
|
CookieAuthenticationDefaults.AuthenticationType);
|
||||||
|
|
||||||
|
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
|
||||||
|
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IEnumerable<Claim> claims = externalLogin.GetClaims();
|
||||||
|
ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
|
||||||
|
Authentication.SignIn(identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true
|
||||||
|
[AllowAnonymous]
|
||||||
|
[Route("ExternalLogins")]
|
||||||
|
public IEnumerable<ExternalLoginViewModel> GetExternalLogins(string returnUrl, bool generateState = false)
|
||||||
|
{
|
||||||
|
IEnumerable<AuthenticationDescription> descriptions = Authentication.GetExternalAuthenticationTypes();
|
||||||
|
List<ExternalLoginViewModel> logins = new List<ExternalLoginViewModel>();
|
||||||
|
|
||||||
|
string state;
|
||||||
|
|
||||||
|
if (generateState)
|
||||||
|
{
|
||||||
|
const int strengthInBits = 256;
|
||||||
|
state = RandomOAuthStateGenerator.Generate(strengthInBits);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (AuthenticationDescription description in descriptions)
|
||||||
|
{
|
||||||
|
ExternalLoginViewModel login = new ExternalLoginViewModel
|
||||||
|
{
|
||||||
|
Name = description.Caption,
|
||||||
|
Url = Url.Route("ExternalLogin", new
|
||||||
|
{
|
||||||
|
provider = description.AuthenticationType,
|
||||||
|
response_type = "token",
|
||||||
|
client_id = Startup.PublicClientId,
|
||||||
|
redirect_uri = new Uri(Request.RequestUri, returnUrl).AbsoluteUri,
|
||||||
|
state = state
|
||||||
|
}),
|
||||||
|
State = state
|
||||||
|
};
|
||||||
|
logins.Add(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
return logins;
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/Account/Register
|
||||||
|
[AllowAnonymous]
|
||||||
|
[Route("Register")]
|
||||||
|
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
|
||||||
|
|
||||||
|
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
|
||||||
|
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
return GetErrorResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/Account/RegisterExternal
|
||||||
|
[OverrideAuthentication]
|
||||||
|
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
|
||||||
|
[Route("RegisterExternal")]
|
||||||
|
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = await Authentication.GetExternalLoginInfoAsync();
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
return InternalServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
|
||||||
|
|
||||||
|
IdentityResult result = await UserManager.CreateAsync(user);
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
return GetErrorResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = await UserManager.AddLoginAsync(user.Id, info.Login);
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
return GetErrorResult(result);
|
||||||
|
}
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && _userManager != null)
|
||||||
|
{
|
||||||
|
_userManager.Dispose();
|
||||||
|
_userManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
|
|
||||||
|
private IAuthenticationManager Authentication
|
||||||
|
{
|
||||||
|
get { return Request.GetOwinContext().Authentication; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private IHttpActionResult GetErrorResult(IdentityResult result)
|
||||||
|
{
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return InternalServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
if (result.Errors != null)
|
||||||
|
{
|
||||||
|
foreach (string error in result.Errors)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError("", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
// No ModelState errors are available to send, so just return an empty BadRequest.
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExternalLoginData
|
||||||
|
{
|
||||||
|
public string LoginProvider { get; set; }
|
||||||
|
public string ProviderKey { get; set; }
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
public IList<Claim> GetClaims()
|
||||||
|
{
|
||||||
|
IList<Claim> claims = new List<Claim>();
|
||||||
|
claims.Add(new Claim(ClaimTypes.NameIdentifier, ProviderKey, null, LoginProvider));
|
||||||
|
|
||||||
|
if (UserName != null)
|
||||||
|
{
|
||||||
|
claims.Add(new Claim(ClaimTypes.Name, UserName, null, LoginProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
return claims;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
|
||||||
|
{
|
||||||
|
if (identity == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
|
||||||
|
|
||||||
|
if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
|
||||||
|
|| String.IsNullOrEmpty(providerKeyClaim.Value))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExternalLoginData
|
||||||
|
{
|
||||||
|
LoginProvider = providerKeyClaim.Issuer,
|
||||||
|
ProviderKey = providerKeyClaim.Value,
|
||||||
|
UserName = identity.FindFirstValue(ClaimTypes.Name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RandomOAuthStateGenerator
|
||||||
|
{
|
||||||
|
private static RandomNumberGenerator _random = new RNGCryptoServiceProvider();
|
||||||
|
|
||||||
|
public static string Generate(int strengthInBits)
|
||||||
|
{
|
||||||
|
const int bitsPerByte = 8;
|
||||||
|
|
||||||
|
if (strengthInBits % bitsPerByte != 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("strengthInBits must be evenly divisible by 8.", "strengthInBits");
|
||||||
|
}
|
||||||
|
|
||||||
|
int strengthInBytes = strengthInBits / bitsPerByte;
|
||||||
|
|
||||||
|
byte[] data = new byte[strengthInBytes];
|
||||||
|
_random.GetBytes(data);
|
||||||
|
return HttpServerUtility.UrlTokenEncode(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
18
RMDataManager/Controllers/HomeController.cs
Normal file
18
RMDataManager/Controllers/HomeController.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Mvc;
|
||||||
|
|
||||||
|
namespace RMDataManager.Controllers
|
||||||
|
{
|
||||||
|
public class HomeController : Controller
|
||||||
|
{
|
||||||
|
public ActionResult Index()
|
||||||
|
{
|
||||||
|
ViewBag.Title = "Home Page";
|
||||||
|
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
RMDataManager/Controllers/ValuesController.cs
Normal file
40
RMDataManager/Controllers/ValuesController.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Web.Http;
|
||||||
|
|
||||||
|
namespace RMDataManager.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class ValuesController : ApiController
|
||||||
|
{
|
||||||
|
// GET api/values
|
||||||
|
public IEnumerable<string> Get()
|
||||||
|
{
|
||||||
|
return new string[] { "value1", "value2" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET api/values/5
|
||||||
|
public string Get(int id)
|
||||||
|
{
|
||||||
|
return "value";
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/values
|
||||||
|
public void Post([FromBody]string value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT api/values/5
|
||||||
|
public void Put(int id, [FromBody]string value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE api/values/5
|
||||||
|
public void Delete(int id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
RMDataManager/Global.asax
Normal file
1
RMDataManager/Global.asax
Normal file
@ -0,0 +1 @@
|
|||||||
|
<%@ Application Codebehind="Global.asax.cs" Inherits="RMDataManager.WebApiApplication" Language="C#" %>
|
23
RMDataManager/Global.asax.cs
Normal file
23
RMDataManager/Global.asax.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Http;
|
||||||
|
using System.Web.Mvc;
|
||||||
|
using System.Web.Optimization;
|
||||||
|
using System.Web.Routing;
|
||||||
|
|
||||||
|
namespace RMDataManager
|
||||||
|
{
|
||||||
|
public class WebApiApplication : System.Web.HttpApplication
|
||||||
|
{
|
||||||
|
protected void Application_Start()
|
||||||
|
{
|
||||||
|
AreaRegistration.RegisterAllAreas();
|
||||||
|
GlobalConfiguration.Configure(WebApiConfig.Register);
|
||||||
|
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
|
||||||
|
RouteConfig.RegisterRoutes(RouteTable.Routes);
|
||||||
|
BundleConfig.RegisterBundles(BundleTable.Bundles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
RMDataManager/Models/AccountBindingModels.cs
Normal file
84
RMDataManager/Models/AccountBindingModels.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace RMDataManager.Models
|
||||||
|
{
|
||||||
|
// Models used as parameters to AccountController actions.
|
||||||
|
|
||||||
|
public class AddExternalLoginBindingModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "External access token")]
|
||||||
|
public string ExternalAccessToken { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChangePasswordBindingModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Current password")]
|
||||||
|
public string OldPassword { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "New password")]
|
||||||
|
public string NewPassword { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirm new password")]
|
||||||
|
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||||
|
public string ConfirmPassword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RegisterBindingModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Password")]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirm password")]
|
||||||
|
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||||
|
public string ConfirmPassword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RegisterExternalBindingModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RemoveLoginBindingModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "Login provider")]
|
||||||
|
public string LoginProvider { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "Provider key")]
|
||||||
|
public string ProviderKey { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SetPasswordBindingModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "New password")]
|
||||||
|
public string NewPassword { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirm new password")]
|
||||||
|
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||||
|
public string ConfirmPassword { get; set; }
|
||||||
|
}
|
||||||
|
}
|
43
RMDataManager/Models/AccountViewModels.cs
Normal file
43
RMDataManager/Models/AccountViewModels.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace RMDataManager.Models
|
||||||
|
{
|
||||||
|
// Models returned by AccountController actions.
|
||||||
|
|
||||||
|
public class ExternalLoginViewModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
public string State { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ManageInfoViewModel
|
||||||
|
{
|
||||||
|
public string LocalLoginProvider { get; set; }
|
||||||
|
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<UserLoginInfoViewModel> Logins { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<ExternalLoginViewModel> ExternalLoginProviders { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserInfoViewModel
|
||||||
|
{
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
public bool HasRegistered { get; set; }
|
||||||
|
|
||||||
|
public string LoginProvider { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserLoginInfoViewModel
|
||||||
|
{
|
||||||
|
public string LoginProvider { get; set; }
|
||||||
|
|
||||||
|
public string ProviderKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
33
RMDataManager/Models/IdentityModels.cs
Normal file
33
RMDataManager/Models/IdentityModels.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Identity.EntityFramework;
|
||||||
|
using Microsoft.AspNet.Identity.Owin;
|
||||||
|
|
||||||
|
namespace RMDataManager.Models
|
||||||
|
{
|
||||||
|
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit https://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
|
||||||
|
public class ApplicationUser : IdentityUser
|
||||||
|
{
|
||||||
|
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
|
||||||
|
{
|
||||||
|
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
|
||||||
|
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
|
||||||
|
// Add custom user claims here
|
||||||
|
return userIdentity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
||||||
|
{
|
||||||
|
public ApplicationDbContext()
|
||||||
|
: base("DefaultConnection", throwIfV1Schema: false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApplicationDbContext Create()
|
||||||
|
{
|
||||||
|
return new ApplicationDbContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
RMDataManager/Properties/AssemblyInfo.cs
Normal file
35
RMDataManager/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("RMDataManager")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("RMDataManager")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2022")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("f24bedcc-c165-497c-9439-ec350db2e9f6")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Revision and Build Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
8
RMDataManager/Properties/serviceDependencies.json
Normal file
8
RMDataManager/Properties/serviceDependencies.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"mssql1": {
|
||||||
|
"type": "mssql",
|
||||||
|
"connectionId": "ConnectionStrings:DefaultConnection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
RMDataManager/Properties/serviceDependencies.local.json
Normal file
8
RMDataManager/Properties/serviceDependencies.local.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"mssql1": {
|
||||||
|
"type": "mssql.local",
|
||||||
|
"connectionId": "ConnectionStrings:DefaultConnection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
RMDataManager/Providers/ApplicationOAuthProvider.cs
Normal file
98
RMDataManager/Providers/ApplicationOAuthProvider.cs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Identity.EntityFramework;
|
||||||
|
using Microsoft.AspNet.Identity.Owin;
|
||||||
|
using Microsoft.Owin.Security;
|
||||||
|
using Microsoft.Owin.Security.Cookies;
|
||||||
|
using Microsoft.Owin.Security.OAuth;
|
||||||
|
using RMDataManager.Models;
|
||||||
|
|
||||||
|
namespace RMDataManager.Providers
|
||||||
|
{
|
||||||
|
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
|
||||||
|
{
|
||||||
|
private readonly string _publicClientId;
|
||||||
|
|
||||||
|
public ApplicationOAuthProvider(string publicClientId)
|
||||||
|
{
|
||||||
|
if (publicClientId == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("publicClientId");
|
||||||
|
}
|
||||||
|
|
||||||
|
_publicClientId = publicClientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
|
||||||
|
{
|
||||||
|
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
|
||||||
|
|
||||||
|
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
context.SetError("invalid_grant", "The user name or password is incorrect.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
|
||||||
|
OAuthDefaults.AuthenticationType);
|
||||||
|
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
|
||||||
|
CookieAuthenticationDefaults.AuthenticationType);
|
||||||
|
|
||||||
|
AuthenticationProperties properties = CreateProperties(user.UserName);
|
||||||
|
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
|
||||||
|
context.Validated(ticket);
|
||||||
|
context.Request.Context.Authentication.SignIn(cookiesIdentity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
|
||||||
|
{
|
||||||
|
context.AdditionalResponseParameters.Add(property.Key, property.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult<object>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
|
||||||
|
{
|
||||||
|
// Resource owner password credentials does not provide a client ID.
|
||||||
|
if (context.ClientId == null)
|
||||||
|
{
|
||||||
|
context.Validated();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult<object>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
|
||||||
|
{
|
||||||
|
if (context.ClientId == _publicClientId)
|
||||||
|
{
|
||||||
|
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
|
||||||
|
|
||||||
|
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
|
||||||
|
{
|
||||||
|
context.Validated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult<object>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthenticationProperties CreateProperties(string userName)
|
||||||
|
{
|
||||||
|
IDictionary<string, string> data = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "userName", userName }
|
||||||
|
};
|
||||||
|
return new AuthenticationProperties(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
351
RMDataManager/RMDataManager.csproj
Normal file
351
RMDataManager/RMDataManager.csproj
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\packages\EntityFramework.6.4.4\build\EntityFramework.props" Condition="Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.props')" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProductVersion>
|
||||||
|
</ProductVersion>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>{E884BD5C-FE2D-4410-9E00-1D8024B75BFC}</ProjectGuid>
|
||||||
|
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>RMDataManager</RootNamespace>
|
||||||
|
<AssemblyName>RMDataManager</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||||
|
<MvcBuildViews>false</MvcBuildViews>
|
||||||
|
<UseIISExpress>true</UseIISExpress>
|
||||||
|
<Use64BitIISExpress />
|
||||||
|
<IISExpressSSLPort>44372</IISExpressSSLPort>
|
||||||
|
<IISExpressAnonymousAuthentication />
|
||||||
|
<IISExpressWindowsAuthentication />
|
||||||
|
<IISExpressUseClassicPipelineMode />
|
||||||
|
<UseGlobalApplicationHostFile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=3.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="Microsoft.Owin, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.4.2.2\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Security, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Security.4.2.2\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Security.Cookies, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Security.Cookies.4.2.2\lib\net45\Microsoft.Owin.Security.Cookies.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Security.Facebook, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Security.Facebook.4.2.2\lib\net45\Microsoft.Owin.Security.Facebook.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Security.Google, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Security.Google.4.2.2\lib\net45\Microsoft.Owin.Security.Google.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Security.MicrosoftAccount, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Security.MicrosoftAccount.4.2.2\lib\net45\Microsoft.Owin.Security.MicrosoftAccount.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Security.OAuth, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Security.OAuth.4.2.2\lib\net45\Microsoft.Owin.Security.OAuth.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Security.Twitter, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Security.Twitter.4.2.2\lib\net45\Microsoft.Owin.Security.Twitter.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Web.Infrastructure, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.Web.Infrastructure.2.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Net.Http.Formatting, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.9\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security" />
|
||||||
|
<Reference Include="System.Web.Entity" />
|
||||||
|
<Reference Include="System.Web.ApplicationServices" />
|
||||||
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.Helpers.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.Http, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.9\lib\net45\System.Web.Http.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.Http.Owin, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebApi.Owin.5.2.9\lib\net45\System.Web.Http.Owin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.Http.WebHost, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.9\lib\net45\System.Web.Http.WebHost.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.Mvc, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.Mvc.5.2.9\lib\net45\System.Web.Mvc.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.Razor.3.2.9\lib\net45\System.Web.Razor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.WebPages.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.WebPages.Deployment, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.WebPages.Deployment.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="System.Web.Abstractions" />
|
||||||
|
<Reference Include="System.Web.Routing" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="System.Runtime.Serialization" />
|
||||||
|
<Reference Include="System.Net.Http">
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Http.WebRequest">
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.Optimization">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="WebGrease">
|
||||||
|
<Private>True</Private>
|
||||||
|
<HintPath>..\packages\WebGrease.1.6.0\lib\WebGrease.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Antlr3.Runtime">
|
||||||
|
<Private>True</Private>
|
||||||
|
<HintPath>..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="EntityFramework">
|
||||||
|
<HintPath>..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="EntityFramework.SqlServer">
|
||||||
|
<HintPath>..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.AspNet.Identity.Core">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.Identity.Core.2.2.3\lib\net45\Microsoft.AspNet.Identity.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.AspNet.Identity.Owin">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.Identity.Owin.2.2.3\lib\net45\Microsoft.AspNet.Identity.Owin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.AspNet.Identity.EntityFramework">
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.Identity.EntityFramework.2.2.3\lib\net45\Microsoft.AspNet.Identity.EntityFramework.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Owin">
|
||||||
|
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Security.Cookies">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Security.Cookies.4.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="App_Start\BundleConfig.cs" />
|
||||||
|
<Compile Include="App_Start\FilterConfig.cs" />
|
||||||
|
<Compile Include="App_Start\IdentityConfig.cs" />
|
||||||
|
<Compile Include="App_Start\RouteConfig.cs" />
|
||||||
|
<Compile Include="App_Start\Startup.Auth.cs" />
|
||||||
|
<Compile Include="App_Start\WebApiConfig.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ApiDescriptionExtensions.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\App_Start\HelpPageConfig.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\Controllers\HelpController.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\HelpPageAreaRegistration.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\HelpPageConfigurationExtensions.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\CollectionModelDescription.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\ComplexTypeModelDescription.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\DictionaryModelDescription.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\EnumTypeModelDescription.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\EnumValueDescription.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\IModelDocumentationProvider.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\KeyValuePairModelDescription.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\ModelDescription.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\ModelDescriptionGenerator.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\ModelNameAttribute.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\ModelNameHelper.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\ParameterAnnotation.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\ParameterDescription.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\ModelDescriptions\SimpleTypeModelDescription.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\Models\HelpPageApiModel.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\SampleGeneration\HelpPageSampleGenerator.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\SampleGeneration\HelpPageSampleKey.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\SampleGeneration\ImageSample.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\SampleGeneration\InvalidSample.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\SampleGeneration\ObjectGenerator.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\SampleGeneration\SampleDirection.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\SampleGeneration\TextSample.cs" />
|
||||||
|
<Compile Include="Areas\HelpPage\XmlDocumentationProvider.cs" />
|
||||||
|
<Compile Include="Controllers\AccountController.cs" />
|
||||||
|
<Compile Include="Controllers\HomeController.cs" />
|
||||||
|
<Compile Include="Controllers\ValuesController.cs" />
|
||||||
|
<Compile Include="Global.asax.cs">
|
||||||
|
<DependentUpon>Global.asax</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Models\AccountBindingModels.cs" />
|
||||||
|
<Compile Include="Models\AccountViewModels.cs" />
|
||||||
|
<Compile Include="Models\IdentityModels.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Providers\ApplicationOAuthProvider.cs" />
|
||||||
|
<Compile Include="Results\ChallengeResult.cs" />
|
||||||
|
<Compile Include="Startup.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Areas\HelpPage\HelpPage.css" />
|
||||||
|
<Content Include="Content\bootstrap-theme.css" />
|
||||||
|
<Content Include="Content\bootstrap-theme.min.css" />
|
||||||
|
<Content Include="Content\bootstrap.css" />
|
||||||
|
<Content Include="Content\bootstrap.min.css" />
|
||||||
|
<Content Include="favicon.ico" />
|
||||||
|
<Content Include="fonts\glyphicons-halflings-regular.svg" />
|
||||||
|
<Content Include="Global.asax" />
|
||||||
|
<Content Include="Scripts\bootstrap.js" />
|
||||||
|
<Content Include="Scripts\bootstrap.min.js" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Web.config" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Shared\_Layout.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\ResourceModel.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\Index.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\TextSample.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\SimpleTypeModelDescription.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\Samples.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\Parameters.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\ModelDescriptionLink.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\KeyValuePairModelDescription.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\InvalidSample.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\ImageSample.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\HelpPageApiModel.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\EnumTypeModelDescription.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\DictionaryModelDescription.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\ComplexTypeModelDescription.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\CollectionModelDescription.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\DisplayTemplates\ApiGroup.cshtml" />
|
||||||
|
<Content Include="Areas\HelpPage\Views\Help\Api.cshtml" />
|
||||||
|
<None Include="Scripts\jquery-3.6.0.intellisense.js" />
|
||||||
|
<Content Include="Scripts\jquery-3.6.0.js" />
|
||||||
|
<Content Include="Scripts\jquery-3.6.0.min.js" />
|
||||||
|
<Content Include="Scripts\jquery-3.6.0.slim.js" />
|
||||||
|
<Content Include="Scripts\jquery-3.6.0.slim.min.js" />
|
||||||
|
<None Include="Scripts\jquery.validate-vsdoc.js" />
|
||||||
|
<Content Include="Scripts\jquery.validate.js" />
|
||||||
|
<Content Include="Scripts\jquery.validate.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.validate.unobtrusive.js" />
|
||||||
|
<Content Include="Scripts\jquery.validate.unobtrusive.min.js" />
|
||||||
|
<Content Include="Scripts\modernizr-2.8.3.js" />
|
||||||
|
<Content Include="Web.config" />
|
||||||
|
<Content Include="Web.Debug.config">
|
||||||
|
<DependentUpon>Web.config</DependentUpon>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Web.Release.config">
|
||||||
|
<DependentUpon>Web.config</DependentUpon>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Areas\HelpPage\Views\_ViewStart.cshtml" />
|
||||||
|
<Content Include="Content\Site.css" />
|
||||||
|
<Content Include="Views\Web.config" />
|
||||||
|
<Content Include="Views\_ViewStart.cshtml" />
|
||||||
|
<Content Include="Views\Home\Index.cshtml" />
|
||||||
|
<Content Include="Views\Shared\Error.cshtml" />
|
||||||
|
<Content Include="Views\Shared\_Layout.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="App_Data\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="fonts\glyphicons-halflings-regular.woff2" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="fonts\glyphicons-halflings-regular.woff" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="fonts\glyphicons-halflings-regular.ttf" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="fonts\glyphicons-halflings-regular.eot" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Content\bootstrap.min.css.map" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Content\bootstrap.css.map" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Content\bootstrap-theme.min.css.map" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Content\bootstrap-theme.css.map" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<Content Include="Scripts\jquery-3.6.0.slim.min.map" />
|
||||||
|
<Content Include="Scripts\jquery-3.6.0.min.map" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
|
||||||
|
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
|
||||||
|
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
|
||||||
|
</Target>
|
||||||
|
<ProjectExtensions>
|
||||||
|
<VisualStudio>
|
||||||
|
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||||
|
<WebProjectProperties>
|
||||||
|
<UseIIS>True</UseIIS>
|
||||||
|
<AutoAssignPort>True</AutoAssignPort>
|
||||||
|
<DevelopmentServerPort>50275</DevelopmentServerPort>
|
||||||
|
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||||
|
<IISUrl>https://localhost:44372/</IISUrl>
|
||||||
|
<NTLMAuthentication>False</NTLMAuthentication>
|
||||||
|
<UseCustomServer>False</UseCustomServer>
|
||||||
|
<CustomServerUrl>
|
||||||
|
</CustomServerUrl>
|
||||||
|
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||||
|
</WebProjectProperties>
|
||||||
|
</FlavorProperties>
|
||||||
|
</VisualStudio>
|
||||||
|
</ProjectExtensions>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.4.4\build\EntityFramework.props'))" />
|
||||||
|
<Error Condition="!Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\EntityFramework.6.4.4\build\EntityFramework.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\build\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\build\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.targets'))" />
|
||||||
|
</Target>
|
||||||
|
<Import Project="..\packages\EntityFramework.6.4.4\build\EntityFramework.targets" Condition="Exists('..\packages\EntityFramework.6.4.4\build\EntityFramework.targets')" />
|
||||||
|
<Import Project="..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\build\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.targets" Condition="Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\build\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.targets')" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target> -->
|
||||||
|
</Project>
|
32
RMDataManager/Results/ChallengeResult.cs
Normal file
32
RMDataManager/Results/ChallengeResult.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web.Http;
|
||||||
|
|
||||||
|
namespace RMDataManager.Results
|
||||||
|
{
|
||||||
|
public class ChallengeResult : IHttpActionResult
|
||||||
|
{
|
||||||
|
public ChallengeResult(string loginProvider, ApiController controller)
|
||||||
|
{
|
||||||
|
LoginProvider = loginProvider;
|
||||||
|
Request = controller.Request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string LoginProvider { get; set; }
|
||||||
|
public HttpRequestMessage Request { get; set; }
|
||||||
|
|
||||||
|
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Request.GetOwinContext().Authentication.Challenge(LoginProvider);
|
||||||
|
|
||||||
|
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
response.RequestMessage = Request;
|
||||||
|
return Task.FromResult(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2580
RMDataManager/Scripts/bootstrap.js
vendored
Normal file
2580
RMDataManager/Scripts/bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
RMDataManager/Scripts/bootstrap.min.js
vendored
Normal file
6
RMDataManager/Scripts/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2670
RMDataManager/Scripts/jquery-3.6.0.intellisense.js
vendored
Normal file
2670
RMDataManager/Scripts/jquery-3.6.0.intellisense.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10881
RMDataManager/Scripts/jquery-3.6.0.js
vendored
Normal file
10881
RMDataManager/Scripts/jquery-3.6.0.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
RMDataManager/Scripts/jquery-3.6.0.min.js
vendored
Normal file
2
RMDataManager/Scripts/jquery-3.6.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
RMDataManager/Scripts/jquery-3.6.0.min.map
Normal file
1
RMDataManager/Scripts/jquery-3.6.0.min.map
Normal file
File diff suppressed because one or more lines are too long
8782
RMDataManager/Scripts/jquery-3.6.0.slim.js
Normal file
8782
RMDataManager/Scripts/jquery-3.6.0.slim.js
Normal file
File diff suppressed because it is too large
Load Diff
2
RMDataManager/Scripts/jquery-3.6.0.slim.min.js
vendored
Normal file
2
RMDataManager/Scripts/jquery-3.6.0.slim.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
RMDataManager/Scripts/jquery-3.6.0.slim.min.map
Normal file
1
RMDataManager/Scripts/jquery-3.6.0.slim.min.map
Normal file
File diff suppressed because one or more lines are too long
1288
RMDataManager/Scripts/jquery.validate-vsdoc.js
vendored
Normal file
1288
RMDataManager/Scripts/jquery.validate-vsdoc.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1661
RMDataManager/Scripts/jquery.validate.js
vendored
Normal file
1661
RMDataManager/Scripts/jquery.validate.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4
RMDataManager/Scripts/jquery.validate.min.js
vendored
Normal file
4
RMDataManager/Scripts/jquery.validate.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
711
RMDataManager/Scripts/jquery.validate.unobtrusive.js
vendored
Normal file
711
RMDataManager/Scripts/jquery.validate.unobtrusive.js
vendored
Normal file
@ -0,0 +1,711 @@
|
|||||||
|
// Unobtrusive validation support library for jQuery and jQuery Validate
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
// @version v3.2.12
|
||||||
|
|
||||||
|
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */
|
||||||
|
/*global document: false, jQuery: false */
|
||||||
|
|
||||||
|
(function (factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define("jquery.validate.unobtrusive", ['jquery-validation'], factory);
|
||||||
|
} else if (typeof module === 'object' && module.exports) {
|
||||||
|
// CommonJS-like environments that support module.exports
|
||||||
|
module.exports = factory(require('jquery-validation'));
|
||||||
|
} else {
|
||||||
|
// Browser global
|
||||||
|
jQuery.validator.unobtrusive = factory(jQuery);
|
||||||
|
}
|
||||||
|
}(function ($) {
|
||||||
|
var $jQval = $.validator,
|
||||||
|
adapters,
|
||||||
|
data_validation = "unobtrusiveValidation";
|
||||||
|
|
||||||
|
function setValidationValues(options, ruleName, value) {
|
||||||
|
options.rules[ruleName] = value;
|
||||||
|
if (options.message) {
|
||||||
|
options.messages[ruleName] = options.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitAndTrim(value) {
|
||||||
|
return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g);
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeAttributeValue(value) {
|
||||||
|
// As mentioned on http://api.jquery.com/category/selectors/
|
||||||
|
return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModelPrefix(fieldName) {
|
||||||
|
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendModelPrefix(value, prefix) {
|
||||||
|
if (value.indexOf("*.") === 0) {
|
||||||
|
value = value.replace("*.", prefix);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onError(error, inputElement) { // 'this' is the form element
|
||||||
|
var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"),
|
||||||
|
replaceAttrValue = container.attr("data-valmsg-replace"),
|
||||||
|
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null;
|
||||||
|
|
||||||
|
container.removeClass("field-validation-valid").addClass("field-validation-error");
|
||||||
|
error.data("unobtrusiveContainer", container);
|
||||||
|
|
||||||
|
if (replace) {
|
||||||
|
container.empty();
|
||||||
|
error.removeClass("input-validation-error").appendTo(container);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onErrors(event, validator) { // 'this' is the form element
|
||||||
|
var container = $(this).find("[data-valmsg-summary=true]"),
|
||||||
|
list = container.find("ul");
|
||||||
|
|
||||||
|
if (list && list.length && validator.errorList.length) {
|
||||||
|
list.empty();
|
||||||
|
container.addClass("validation-summary-errors").removeClass("validation-summary-valid");
|
||||||
|
|
||||||
|
$.each(validator.errorList, function () {
|
||||||
|
$("<li />").html(this.message).appendTo(list);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSuccess(error) { // 'this' is the form element
|
||||||
|
var container = error.data("unobtrusiveContainer");
|
||||||
|
|
||||||
|
if (container) {
|
||||||
|
var replaceAttrValue = container.attr("data-valmsg-replace"),
|
||||||
|
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null;
|
||||||
|
|
||||||
|
container.addClass("field-validation-valid").removeClass("field-validation-error");
|
||||||
|
error.removeData("unobtrusiveContainer");
|
||||||
|
|
||||||
|
if (replace) {
|
||||||
|
container.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onReset(event) { // 'this' is the form element
|
||||||
|
var $form = $(this),
|
||||||
|
key = '__jquery_unobtrusive_validation_form_reset';
|
||||||
|
if ($form.data(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Set a flag that indicates we're currently resetting the form.
|
||||||
|
$form.data(key, true);
|
||||||
|
try {
|
||||||
|
$form.data("validator").resetForm();
|
||||||
|
} finally {
|
||||||
|
$form.removeData(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form.find(".validation-summary-errors")
|
||||||
|
.addClass("validation-summary-valid")
|
||||||
|
.removeClass("validation-summary-errors");
|
||||||
|
$form.find(".field-validation-error")
|
||||||
|
.addClass("field-validation-valid")
|
||||||
|
.removeClass("field-validation-error")
|
||||||
|
.removeData("unobtrusiveContainer")
|
||||||
|
.find(">*") // If we were using valmsg-replace, get the underlying error
|
||||||
|
.removeData("unobtrusiveContainer");
|
||||||
|
}
|
||||||
|
|
||||||
|
function validationInfo(form) {
|
||||||
|
var $form = $(form),
|
||||||
|
result = $form.data(data_validation),
|
||||||
|
onResetProxy = $.proxy(onReset, form),
|
||||||
|
defaultOptions = $jQval.unobtrusive.options || {},
|
||||||
|
execInContext = function (name, args) {
|
||||||
|
var func = defaultOptions[name];
|
||||||
|
func && $.isFunction(func) && func.apply(form, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
result = {
|
||||||
|
options: { // options structure passed to jQuery Validate's validate() method
|
||||||
|
errorClass: defaultOptions.errorClass || "input-validation-error",
|
||||||
|
errorElement: defaultOptions.errorElement || "span",
|
||||||
|
errorPlacement: function () {
|
||||||
|
onError.apply(form, arguments);
|
||||||
|
execInContext("errorPlacement", arguments);
|
||||||
|
},
|
||||||
|
invalidHandler: function () {
|
||||||
|
onErrors.apply(form, arguments);
|
||||||
|
execInContext("invalidHandler", arguments);
|
||||||
|
},
|
||||||
|
messages: {},
|
||||||
|
rules: {},
|
||||||
|
success: function () {
|
||||||
|
onSuccess.apply(form, arguments);
|
||||||
|
execInContext("success", arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attachValidation: function () {
|
||||||
|
$form
|
||||||
|
.off("reset." + data_validation, onResetProxy)
|
||||||
|
.on("reset." + data_validation, onResetProxy)
|
||||||
|
.validate(this.options);
|
||||||
|
},
|
||||||
|
validate: function () { // a validation function that is called by unobtrusive Ajax
|
||||||
|
$form.validate();
|
||||||
|
return $form.valid();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$form.data(data_validation, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$jQval.unobtrusive = {
|
||||||
|
adapters: [],
|
||||||
|
|
||||||
|
parseElement: function (element, skipAttach) {
|
||||||
|
/// <summary>
|
||||||
|
/// Parses a single HTML element for unobtrusive validation attributes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element" domElement="true">The HTML element to be parsed.</param>
|
||||||
|
/// <param name="skipAttach" type="Boolean">[Optional] true to skip attaching the
|
||||||
|
/// validation to the form. If parsing just this single element, you should specify true.
|
||||||
|
/// If parsing several elements, you should specify false, and manually attach the validation
|
||||||
|
/// to the form when you are finished. The default is false.</param>
|
||||||
|
var $element = $(element),
|
||||||
|
form = $element.parents("form")[0],
|
||||||
|
valInfo, rules, messages;
|
||||||
|
|
||||||
|
if (!form) { // Cannot do client-side validation without a form
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
valInfo = validationInfo(form);
|
||||||
|
valInfo.options.rules[element.name] = rules = {};
|
||||||
|
valInfo.options.messages[element.name] = messages = {};
|
||||||
|
|
||||||
|
$.each(this.adapters, function () {
|
||||||
|
var prefix = "data-val-" + this.name,
|
||||||
|
message = $element.attr(prefix),
|
||||||
|
paramValues = {};
|
||||||
|
|
||||||
|
if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy)
|
||||||
|
prefix += "-";
|
||||||
|
|
||||||
|
$.each(this.params, function () {
|
||||||
|
paramValues[this] = $element.attr(prefix + this);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.adapt({
|
||||||
|
element: element,
|
||||||
|
form: form,
|
||||||
|
message: message,
|
||||||
|
params: paramValues,
|
||||||
|
rules: rules,
|
||||||
|
messages: messages
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$.extend(rules, { "__dummy__": true });
|
||||||
|
|
||||||
|
if (!skipAttach) {
|
||||||
|
valInfo.attachValidation();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
parse: function (selector) {
|
||||||
|
/// <summary>
|
||||||
|
/// Parses all the HTML elements in the specified selector. It looks for input elements decorated
|
||||||
|
/// with the [data-val=true] attribute value and enables validation according to the data-val-*
|
||||||
|
/// attribute values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="selector" type="String">Any valid jQuery selector.</param>
|
||||||
|
|
||||||
|
// $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one
|
||||||
|
// element with data-val=true
|
||||||
|
var $selector = $(selector),
|
||||||
|
$forms = $selector.parents()
|
||||||
|
.addBack()
|
||||||
|
.filter("form")
|
||||||
|
.add($selector.find("form"))
|
||||||
|
.has("[data-val=true]");
|
||||||
|
|
||||||
|
$selector.find("[data-val=true]").each(function () {
|
||||||
|
$jQval.unobtrusive.parseElement(this, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
$forms.each(function () {
|
||||||
|
var info = validationInfo(this);
|
||||||
|
if (info) {
|
||||||
|
info.attachValidation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
adapters = $jQval.unobtrusive.adapters;
|
||||||
|
|
||||||
|
adapters.add = function (adapterName, params, fn) {
|
||||||
|
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation.</summary>
|
||||||
|
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
||||||
|
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
|
||||||
|
/// <param name="params" type="Array" optional="true">[Optional] An array of parameter names (strings) that will
|
||||||
|
/// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and
|
||||||
|
/// mmmm is the parameter name).</param>
|
||||||
|
/// <param name="fn" type="Function">The function to call, which adapts the values from the HTML
|
||||||
|
/// attributes into jQuery Validate rules and/or messages.</param>
|
||||||
|
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
||||||
|
if (!fn) { // Called with no params, just a function
|
||||||
|
fn = params;
|
||||||
|
params = [];
|
||||||
|
}
|
||||||
|
this.push({ name: adapterName, params: params, adapt: fn });
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
adapters.addBool = function (adapterName, ruleName) {
|
||||||
|
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
|
||||||
|
/// the jQuery Validate validation rule has no parameter values.</summary>
|
||||||
|
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
||||||
|
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
|
||||||
|
/// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
|
||||||
|
/// of adapterName will be used instead.</param>
|
||||||
|
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
||||||
|
return this.add(adapterName, function (options) {
|
||||||
|
setValidationValues(options, ruleName || adapterName, true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) {
|
||||||
|
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
|
||||||
|
/// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and
|
||||||
|
/// one for min-and-max). The HTML parameters are expected to be named -min and -max.</summary>
|
||||||
|
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
||||||
|
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
|
||||||
|
/// <param name="minRuleName" type="String">The name of the jQuery Validate rule to be used when you only
|
||||||
|
/// have a minimum value.</param>
|
||||||
|
/// <param name="maxRuleName" type="String">The name of the jQuery Validate rule to be used when you only
|
||||||
|
/// have a maximum value.</param>
|
||||||
|
/// <param name="minMaxRuleName" type="String">The name of the jQuery Validate rule to be used when you
|
||||||
|
/// have both a minimum and maximum value.</param>
|
||||||
|
/// <param name="minAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
|
||||||
|
/// contains the minimum value. The default is "min".</param>
|
||||||
|
/// <param name="maxAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
|
||||||
|
/// contains the maximum value. The default is "max".</param>
|
||||||
|
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
||||||
|
return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) {
|
||||||
|
var min = options.params.min,
|
||||||
|
max = options.params.max;
|
||||||
|
|
||||||
|
if (min && max) {
|
||||||
|
setValidationValues(options, minMaxRuleName, [min, max]);
|
||||||
|
}
|
||||||
|
else if (min) {
|
||||||
|
setValidationValues(options, minRuleName, min);
|
||||||
|
}
|
||||||
|
else if (max) {
|
||||||
|
setValidationValues(options, maxRuleName, max);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
adapters.addSingleVal = function (adapterName, attribute, ruleName) {
|
||||||
|
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
|
||||||
|
/// the jQuery Validate validation rule has a single value.</summary>
|
||||||
|
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
||||||
|
/// in the data-val-nnnn HTML attribute(where nnnn is the adapter name).</param>
|
||||||
|
/// <param name="attribute" type="String">[Optional] The name of the HTML attribute that contains the value.
|
||||||
|
/// The default is "val".</param>
|
||||||
|
/// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
|
||||||
|
/// of adapterName will be used instead.</param>
|
||||||
|
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
||||||
|
return this.add(adapterName, [attribute || "val"], function (options) {
|
||||||
|
setValidationValues(options, ruleName || adapterName, options.params[attribute]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$jQval.addMethod("__dummy__", function (value, element, params) {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
$jQval.addMethod("regex", function (value, element, params) {
|
||||||
|
var match;
|
||||||
|
if (this.optional(element)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
match = new RegExp(params).exec(value);
|
||||||
|
return (match && (match.index === 0) && (match[0].length === value.length));
|
||||||
|
});
|
||||||
|
|
||||||
|
$jQval.addMethod("nonalphamin", function (value, element, nonalphamin) {
|
||||||
|
var match;
|
||||||
|
if (nonalphamin) {
|
||||||
|
match = value.match(/\W/g);
|
||||||
|
match = match && match.length >= nonalphamin;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($jQval.methods.extension) {
|
||||||
|
adapters.addSingleVal("accept", "mimtype");
|
||||||
|
adapters.addSingleVal("extension", "extension");
|
||||||
|
} else {
|
||||||
|
// for backward compatibility, when the 'extension' validation method does not exist, such as with versions
|
||||||
|
// of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for
|
||||||
|
// validating the extension, and ignore mime-type validations as they are not supported.
|
||||||
|
adapters.addSingleVal("extension", "extension", "accept");
|
||||||
|
}
|
||||||
|
|
||||||
|
adapters.addSingleVal("regex", "pattern");
|
||||||
|
adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");
|
||||||
|
adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range");
|
||||||
|
adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength");
|
||||||
|
adapters.add("equalto", ["other"], function (options) {
|
||||||
|
var prefix = getModelPrefix(options.element.name),
|
||||||
|
other = options.params.other,
|
||||||
|
fullOtherName = appendModelPrefix(other, prefix),
|
||||||
|
element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0];
|
||||||
|
|
||||||
|
setValidationValues(options, "equalTo", element);
|
||||||
|
});
|
||||||
|
adapters.add("required", function (options) {
|
||||||
|
// jQuery Validate equates "required" with "mandatory" for checkbox elements
|
||||||
|
if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
|
||||||
|
setValidationValues(options, "required", true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
adapters.add("remote", ["url", "type", "additionalfields"], function (options) {
|
||||||
|
var value = {
|
||||||
|
url: options.params.url,
|
||||||
|
type: options.params.type || "GET",
|
||||||
|
data: {}
|
||||||
|
},
|
||||||
|
prefix = getModelPrefix(options.element.name);
|
||||||
|
|
||||||
|
$.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) {
|
||||||
|
var paramName = appendModelPrefix(fieldName, prefix);
|
||||||
|
value.data[paramName] = function () {
|
||||||
|
var field = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']");
|
||||||
|
// For checkboxes and radio buttons, only pick up values from checked fields.
|
||||||
|
if (field.is(":checkbox")) {
|
||||||
|
return field.filter(":checked").val() || field.filter(":hidden").val() || '';
|
||||||
|
}
|
||||||
|
else if (field.is(":radio")) {
|
||||||
|
return field.filter(":checked").val() || '';
|
||||||
|
}
|
||||||
|
return field.val();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setValidationValues(options, "remote", value);
|
||||||
|
});
|
||||||
|
adapters.add("password", ["min", "nonalphamin", "regex"], function (options) {
|
||||||
|
if (options.params.min) {
|
||||||
|
setValidationValues(options, "minlength", options.params.min);
|
||||||
|
}
|
||||||
|
if (options.params.nonalphamin) {
|
||||||
|
setValidationValues(options, "nonalphamin", options.params.nonalphamin);
|
||||||
|
}
|
||||||
|
if (options.params.regex) {
|
||||||
|
setValidationValues(options, "regex", options.params.regex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
adapters.add("fileextensions", ["extensions"], function (options) {
|
||||||
|
setValidationValues(options, "extension", options.params.extensions);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
$jQval.unobtrusive.parse(document);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $jQval.unobtrusive;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// SIG // Begin signature block
|
||||||
|
// SIG // MIIjkAYJKoZIhvcNAQcCoIIjgTCCI30CAQExDzANBglg
|
||||||
|
// SIG // hkgBZQMEAgEFADB3BgorBgEEAYI3AgEEoGkwZzAyBgor
|
||||||
|
// SIG // BgEEAYI3AgEeMCQCAQEEEBDgyQbOONQRoqMAEEvTUJAC
|
||||||
|
// SIG // AQACAQACAQACAQACAQAwMTANBglghkgBZQMEAgEFAAQg
|
||||||
|
// SIG // HzBDmTBE+vPkBBdSH7wHk55TtOtFpw5ky2pRvfu2JCag
|
||||||
|
// SIG // gg2BMIIF/zCCA+egAwIBAgITMwAAAYdyF3IVWUDHCQAA
|
||||||
|
// SIG // AAABhzANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJV
|
||||||
|
// SIG // UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
|
||||||
|
// SIG // UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
|
||||||
|
// SIG // cmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBT
|
||||||
|
// SIG // aWduaW5nIFBDQSAyMDExMB4XDTIwMDMwNDE4Mzk0N1oX
|
||||||
|
// SIG // DTIxMDMwMzE4Mzk0N1owdDELMAkGA1UEBhMCVVMxEzAR
|
||||||
|
// SIG // BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
|
||||||
|
// SIG // bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
|
||||||
|
// SIG // bjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
|
||||||
|
// SIG // MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||||
|
// SIG // zrfJC3Oz90+zCiIaLmB3sDBZp6vAMruxToWQkGm1cAad
|
||||||
|
// SIG // lUuFsgdkHuE0AU/Ggc5wDQxD4xyjXT0/F8+XDWpYulx3
|
||||||
|
// SIG // n0vIv1l7RdL0rD/DRL+pgR7gNqdX8NsAfxdHR7Cdxn2e
|
||||||
|
// SIG // XNLDyY5JbImKj8OfcSeeJDPdSDoIjtjlM4zQJYz4m4wl
|
||||||
|
// SIG // nx+1M0NUzx3OHcHopbPBhCK2wUW+yFsIjmy9do1k+GIe
|
||||||
|
// SIG // 9TUILyfRZ+vlIQ/cdrpN3S4/OL8LdTbhUIrSicSFdH1b
|
||||||
|
// SIG // ETUd2m0FTi6qQ7oG69EszS+qPMczhy+Tl4hhsIOnpIlw
|
||||||
|
// SIG // Nf9l12O8lRXN/bZXnQ7WY0ozW3sdc88ElwIDAQABo4IB
|
||||||
|
// SIG // fjCCAXowHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYB
|
||||||
|
// SIG // BQUHAwMwHQYDVR0OBBYEFIaL+GcjvemsZCXTI6c7ts1V
|
||||||
|
// SIG // ziXLMFAGA1UdEQRJMEekRTBDMSkwJwYDVQQLEyBNaWNy
|
||||||
|
// SIG // b3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEWMBQG
|
||||||
|
// SIG // A1UEBRMNMjMwMDEyKzQ1ODM4NTAfBgNVHSMEGDAWgBRI
|
||||||
|
// SIG // bmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmg
|
||||||
|
// SIG // R6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
|
||||||
|
// SIG // b3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDct
|
||||||
|
// SIG // MDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcw
|
||||||
|
// SIG // AoZFaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w
|
||||||
|
// SIG // cy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDct
|
||||||
|
// SIG // MDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEL
|
||||||
|
// SIG // BQADggIBAIsZskuhOr6a1g/ShTSAfRuc8jLiI2QDrlCd
|
||||||
|
// SIG // RCv1ZYOhW92R1441MAEyiHF2xbhQulq+Cja1OA2P7AVa
|
||||||
|
// SIG // pmm+QAv43t26VKY7caRMqlKrT3N9MBIP6zvb5ipqiqCz
|
||||||
|
// SIG // 09+7L3NjVQZhjZfvOajuH1f8OwseydAW6pNfSnETXY7e
|
||||||
|
// SIG // niqE50zxwR5VR0CB2aTMWnGxTgJCa6gFZGGXc+4pDV08
|
||||||
|
// SIG // VfhkW9+rQuAcjDcRNgxe7xXb2omT9AlWeQcidoAIVzHS
|
||||||
|
// SIG // vfrrMc1ZPdd6inXtTgLlnb/q53apACJvH1JUZ6+LGkgo
|
||||||
|
// SIG // O3CG1MAgn9desFCexLiQ4NLx3soZwnh5wW8h90WZBxIt
|
||||||
|
// SIG // qH5n4JxSEiWQ3TAHlWRlTodtCaedFwc6qJKT83mes3Nf
|
||||||
|
// SIG // 4MiCzcolYBPkT5I51ELIXdX9TzIJ97Z7Ngs+2yYlVGqh
|
||||||
|
// SIG // Dt5/akRYMuSbi2nulMHhnwHjqN3YC2cYpCs2LN4QzGhL
|
||||||
|
// SIG // SavCD+9XF+0F3upZzJl1Px3X89qfPe2XfpFPr2byiN3M
|
||||||
|
// SIG // C37lUICtkWds/inNyt3UT89q18nCuVwrkWZrxmm/1m62
|
||||||
|
// SIG // Ygu8CUGqYAaHZbTCORjHRawYPSHhe/6z+BKlUF3irXr0
|
||||||
|
// SIG // 5WV46bjYYY7kftgzLf3Vrn416YlvdW6N2h+hGozgC15q
|
||||||
|
// SIG // MYJbQqdSu4a0uoJrL4/eHC0X+dEEOFPEMIIHejCCBWKg
|
||||||
|
// SIG // AwIBAgIKYQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCB
|
||||||
|
// SIG // iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
|
||||||
|
// SIG // b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
|
||||||
|
// SIG // Y3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWlj
|
||||||
|
// SIG // cm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
|
||||||
|
// SIG // IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
|
||||||
|
// SIG // OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
|
||||||
|
// SIG // aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
|
||||||
|
// SIG // ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQD
|
||||||
|
// SIG // Ex9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDEx
|
||||||
|
// SIG // MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
|
||||||
|
// SIG // q/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4Bjga
|
||||||
|
// SIG // BEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSH
|
||||||
|
// SIG // fpRgJGyvnkmc6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpg
|
||||||
|
// SIG // GgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpc
|
||||||
|
// SIG // oRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnn
|
||||||
|
// SIG // Db6gE3e+lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD
|
||||||
|
// SIG // 2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLT
|
||||||
|
// SIG // swM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOE
|
||||||
|
// SIG // y/S6A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2
|
||||||
|
// SIG // z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
|
||||||
|
// SIG // A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
|
||||||
|
// SIG // 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uD
|
||||||
|
// SIG // jexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmnEyim
|
||||||
|
// SIG // p31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8Hh
|
||||||
|
// SIG // hUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX
|
||||||
|
// SIG // 3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0wggHpMBAG
|
||||||
|
// SIG // CSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXT
|
||||||
|
// SIG // gqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMA
|
||||||
|
// SIG // dQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
|
||||||
|
// SIG // AwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx
|
||||||
|
// SIG // 0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3Js
|
||||||
|
// SIG // Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9N
|
||||||
|
// SIG // aWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4G
|
||||||
|
// SIG // CCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDov
|
||||||
|
// SIG // L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNS
|
||||||
|
// SIG // b29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
|
||||||
|
// SIG // HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
|
||||||
|
// SIG // BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br
|
||||||
|
// SIG // aW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsGAQUF
|
||||||
|
// SIG // BwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5
|
||||||
|
// SIG // AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3
|
||||||
|
// SIG // DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKbC5YR4WOS
|
||||||
|
// SIG // mUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np
|
||||||
|
// SIG // 22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r
|
||||||
|
// SIG // 4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6I/MTfaaQdION
|
||||||
|
// SIG // 9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWlu
|
||||||
|
// SIG // WpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiX
|
||||||
|
// SIG // mE0OPQvyCInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ
|
||||||
|
// SIG // 2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNA
|
||||||
|
// SIG // BQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPD
|
||||||
|
// SIG // XVJihsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yH
|
||||||
|
// SIG // PgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
|
||||||
|
// SIG // XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
|
||||||
|
// SIG // oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5
|
||||||
|
// SIG // GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33VtY5E9
|
||||||
|
// SIG // 0Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZO
|
||||||
|
// SIG // SEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFWcw
|
||||||
|
// SIG // ghVjAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQI
|
||||||
|
// SIG // EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
|
||||||
|
// SIG // HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAm
|
||||||
|
// SIG // BgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENB
|
||||||
|
// SIG // IDIwMTECEzMAAAGHchdyFVlAxwkAAAAAAYcwDQYJYIZI
|
||||||
|
// SIG // AWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQB
|
||||||
|
// SIG // gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC
|
||||||
|
// SIG // ARUwLwYJKoZIhvcNAQkEMSIEIHlrK/AUXnPsvbSO3EMY
|
||||||
|
// SIG // ThwAygU5hNCwZzIvPmOVGSyfMEIGCisGAQQBgjcCAQwx
|
||||||
|
// SIG // NDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRw
|
||||||
|
// SIG // Oi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
|
||||||
|
// SIG // BQAEggEAfWhrNK5UMPsIinBbBfs76p9/lCvnDoU9MxQ3
|
||||||
|
// SIG // BoYmnl74D1L7AN9ZvfJ8FSv69Ih25svUpfVMs+LYYylj
|
||||||
|
// SIG // uva5cAAi8Nn172jry4csRwQivnP2apHQAzN4yBxxZkxX
|
||||||
|
// SIG // vjoll/XFlSZHRWVAFcyC8nerixsaFSEZYXOxOSZZTLZN
|
||||||
|
// SIG // D9AJMYtZXp/jHXPtyv9eOJrb5twLWJc8Jtu6j+NX0I1x
|
||||||
|
// SIG // vSvztKjvMv6EuPHs/j4RUyEx5qUvpHv6tpSy9G1HUbp3
|
||||||
|
// SIG // V7Sb7P/6BSzJ0Ulj9TFTvb2rAvf+930HYQwqvmvBX1sj
|
||||||
|
// SIG // ivOJEh2LHN8YKorTqkTLNILgjJri2IGbhPLP2MyWyaGC
|
||||||
|
// SIG // EvEwghLtBgorBgEEAYI3AwMBMYIS3TCCEtkGCSqGSIb3
|
||||||
|
// SIG // DQEHAqCCEsowghLGAgEDMQ8wDQYJYIZIAWUDBAIBBQAw
|
||||||
|
// SIG // ggFVBgsqhkiG9w0BCRABBKCCAUQEggFAMIIBPAIBAQYK
|
||||||
|
// SIG // KwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCDUEWv8
|
||||||
|
// SIG // jgyxhrEplrjn+1rlBFI1VrIsjyBaPgKUKwtKJwIGX9uZ
|
||||||
|
// SIG // c7FpGBMyMDIxMDExMjE4Mzg0My41NjRaMASAAgH0oIHU
|
||||||
|
// SIG // pIHRMIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
|
||||||
|
// SIG // aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
|
||||||
|
// SIG // ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQL
|
||||||
|
// SIG // EyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmlj
|
||||||
|
// SIG // bzEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046ODk3QS1F
|
||||||
|
// SIG // MzU2LTE3MDExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1l
|
||||||
|
// SIG // LVN0YW1wIFNlcnZpY2Wggg5EMIIE9TCCA92gAwIBAgIT
|
||||||
|
// SIG // MwAAASwir0WXdfkb7gAAAAABLDANBgkqhkiG9w0BAQsF
|
||||||
|
// SIG // ADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
|
||||||
|
// SIG // Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
|
||||||
|
// SIG // TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
|
||||||
|
// SIG // aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0x
|
||||||
|
// SIG // OTEyMTkwMTE1MDNaFw0yMTAzMTcwMTE1MDNaMIHOMQsw
|
||||||
|
// SIG // CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
|
||||||
|
// SIG // MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
|
||||||
|
// SIG // b2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3Nv
|
||||||
|
// SIG // ZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UE
|
||||||
|
// SIG // CxMdVGhhbGVzIFRTUyBFU046ODk3QS1FMzU2LTE3MDEx
|
||||||
|
// SIG // JTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
|
||||||
|
// SIG // cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||||
|
// SIG // AoIBAQDytc4EkqvjMQGKN6qQrUA8UjDzycuppv8HxCTY
|
||||||
|
// SIG // zVJ2LSxQcJdQVEubaxJP7eNZXcSEynobPgAcWKqOEMbI
|
||||||
|
// SIG // NxstboCosBwJ2IonpHwvmYabTRYLZw2SJ+OcwtAJVa/+
|
||||||
|
// SIG // lHy7bC3BwodVxJS1B3xAtJwbkHDP0qWKPXt5damOTXTI
|
||||||
|
// SIG // AxsQrJginYmX2FyLvlNFGCAYXc5kh5wd38WTgVXK+YbR
|
||||||
|
// SIG // RxAQTbf6xSZZvwOMm/KAbKflH9KeUMJjv2wnHagdeSac
|
||||||
|
// SIG // pToWZlrNLFHySpSvRKwIQcBpItniERSrEAXZF0vT1qRd
|
||||||
|
// SIG // cNoCCUb0pAxGgn/pWxkz3Usx0m30RFjhfcGN4mI/AgMB
|
||||||
|
// SIG // AAGjggEbMIIBFzAdBgNVHQ4EFgQUUWoWUhn6wkIQsiMh
|
||||||
|
// SIG // h/Q5Imluy9MwHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xG
|
||||||
|
// SIG // G8UzaFqFbVUwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov
|
||||||
|
// SIG // L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj
|
||||||
|
// SIG // dHMvTWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoG
|
||||||
|
// SIG // CCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov
|
||||||
|
// SIG // L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNU
|
||||||
|
// SIG // aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/
|
||||||
|
// SIG // BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG
|
||||||
|
// SIG // 9w0BAQsFAAOCAQEATxcRyMIuLwB+PcPUr5s+it7TOeUZ
|
||||||
|
// SIG // VuaT3lC13brdkasumLNPlaWbG7mhKMbOmdQt4TG5IqmR
|
||||||
|
// SIG // cccpbcSabh08hk4Otc3zSBeZ+kbGBd7OyBJQ1zX2c5xd
|
||||||
|
// SIG // f9olnOrkT2SvK8cVCf+3pmF2QmMLlGNF47AqT/aW0USn
|
||||||
|
// SIG // iYuq+Wq0siPjXysb4KDNtSTbdQXHZV7gHnHXf5PFI8Qr
|
||||||
|
// SIG // HH32p6Ctp+ixcNT3GZRDuzSHe6PrKDNgtEGOQWHYIaZ+
|
||||||
|
// SIG // 7qqKQeoschSCJA1xbm/tROxpgBMH1OlcBBy+8vazRPG3
|
||||||
|
// SIG // fia0LPsgLZJB8vRZl4Pz7BqtXWlc19UqOwiep8qYabh0
|
||||||
|
// SIG // jd1X0zCCBnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJ
|
||||||
|
// SIG // KoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYD
|
||||||
|
// SIG // VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
|
||||||
|
// SIG // MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
|
||||||
|
// SIG // MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj
|
||||||
|
// SIG // YXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1
|
||||||
|
// SIG // NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMx
|
||||||
|
// SIG // EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
|
||||||
|
// SIG // ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
|
||||||
|
// SIG // dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3Rh
|
||||||
|
// SIG // bXAgUENBIDIwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||||
|
// SIG // DwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ
|
||||||
|
// SIG // 1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0Eb
|
||||||
|
// SIG // GpUdzgkTjnxhMFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zW
|
||||||
|
// SIG // czBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5Ff
|
||||||
|
// SIG // gVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJy
|
||||||
|
// SIG // GiGKr0tkiVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1
|
||||||
|
// SIG // ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16Hgc
|
||||||
|
// SIG // sOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEF
|
||||||
|
// SIG // TyJNAgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIB
|
||||||
|
// SIG // ADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqFbVUw
|
||||||
|
// SIG // GQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0P
|
||||||
|
// SIG // BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgw
|
||||||
|
// SIG // FoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8w
|
||||||
|
// SIG // TTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29t
|
||||||
|
// SIG // L3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIw
|
||||||
|
// SIG // MTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr
|
||||||
|
// SIG // BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29t
|
||||||
|
// SIG // L3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0y
|
||||||
|
// SIG // My5jcnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGC
|
||||||
|
// SIG // Ny4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1p
|
||||||
|
// SIG // Y3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQu
|
||||||
|
// SIG // aHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABf
|
||||||
|
// SIG // AFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQA
|
||||||
|
// SIG // LiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/
|
||||||
|
// SIG // gXEDPZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf
|
||||||
|
// SIG // 9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GASinbM
|
||||||
|
// SIG // QEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c
|
||||||
|
// SIG // 8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQi
|
||||||
|
// SIG // PM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWOM7tiX5rb
|
||||||
|
// SIG // V0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yj
|
||||||
|
// SIG // ojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRC
|
||||||
|
// SIG // g7i1gJsiOCC1JeVk7Pf0v35jWSUPei45V3aicaoGig+J
|
||||||
|
// SIG // FrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHa
|
||||||
|
// SIG // sFAeb73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3ep
|
||||||
|
// SIG // gcunCaw5u+zGy9iCtHLNHfS4hQEegPsbiSpUObJb2sgN
|
||||||
|
// SIG // VZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanb
|
||||||
|
// SIG // lrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvH
|
||||||
|
// SIG // Ia9Zta7cRDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9d
|
||||||
|
// SIG // T+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvT
|
||||||
|
// SIG // X4/edIhJEqGCAtIwggI7AgEBMIH8oYHUpIHRMIHOMQsw
|
||||||
|
// SIG // CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
|
||||||
|
// SIG // MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
|
||||||
|
// SIG // b2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3Nv
|
||||||
|
// SIG // ZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UE
|
||||||
|
// SIG // CxMdVGhhbGVzIFRTUyBFU046ODk3QS1FMzU2LTE3MDEx
|
||||||
|
// SIG // JTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
|
||||||
|
// SIG // cnZpY2WiIwoBATAHBgUrDgMCGgMVAAxOTikjKDcf5mMW
|
||||||
|
// SIG // FmqdUUzqIWydoIGDMIGApH4wfDELMAkGA1UEBhMCVVMx
|
||||||
|
// SIG // EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
|
||||||
|
// SIG // ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
|
||||||
|
// SIG // dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3Rh
|
||||||
|
// SIG // bXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDjqF4k
|
||||||
|
// SIG // MCIYDzIwMjEwMTEyMjE0MzAwWhgPMjAyMTAxMTMyMTQz
|
||||||
|
// SIG // MDBaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFAOOoXiQC
|
||||||
|
// SIG // AQAwCgIBAAICKKECAf8wBwIBAAICEakwCgIFAOOpr6QC
|
||||||
|
// SIG // AQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoD
|
||||||
|
// SIG // AqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG
|
||||||
|
// SIG // 9w0BAQUFAAOBgQB9vt+KUK8D5JoEH1g3Vo/tAvTDQ7Qp
|
||||||
|
// SIG // fdUZkmHSmwJz70P2hEU7gircglCaa0dRor5TnVyNtmoh
|
||||||
|
// SIG // bbMBogAm4BAJK35zpNA3rcVUPnZr/wYyeKQ3A/YMZdog
|
||||||
|
// SIG // e8mvhtgJRpEs+93B4H0iN9dY58dggzVV/1v3Z+mTuIzR
|
||||||
|
// SIG // D2Kug21FHDGCAw0wggMJAgEBMIGTMHwxCzAJBgNVBAYT
|
||||||
|
// SIG // AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
|
||||||
|
// SIG // EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
|
||||||
|
// SIG // cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l
|
||||||
|
// SIG // LVN0YW1wIFBDQSAyMDEwAhMzAAABLCKvRZd1+RvuAAAA
|
||||||
|
// SIG // AAEsMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0B
|
||||||
|
// SIG // CQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIE
|
||||||
|
// SIG // IMym1vQyDSwFW5JfizxucPivNGBd3VBNj94tWH2S3HSd
|
||||||
|
// SIG // MIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgW5/9
|
||||||
|
// SIG // LhRYeNoUzOVzqCnV3rwWoksZmCvSJXJ/Z7uWR+EwgZgw
|
||||||
|
// SIG // gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
|
||||||
|
// SIG // aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
|
||||||
|
// SIG // ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
|
||||||
|
// SIG // Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAIT
|
||||||
|
// SIG // MwAAASwir0WXdfkb7gAAAAABLDAiBCCo/6gf7+t5bNQS
|
||||||
|
// SIG // IgbHMkW3KFQ4Ad0UIMVgw/5FNlH6qzANBgkqhkiG9w0B
|
||||||
|
// SIG // AQsFAASCAQDDznFwos0rjb14Y81eCTcxZNLGLmB8wPt/
|
||||||
|
// SIG // OU4XGK4MCY7OmSAItIxSJ4n5VVVqETUwp1peDW/xeJ9h
|
||||||
|
// SIG // YxwIdNZeAGkBEj8394fDrcQoA/0eZSUNgIKbqZkGZuwN
|
||||||
|
// SIG // oKqpBnmYbAJB2s4ylhpUfZPA6hKt1opRJv14aBNuBFKo
|
||||||
|
// SIG // oSRcFRyXDYPUWvGWgKdlHtB9RF/iwnEx599MHNlujYPr
|
||||||
|
// SIG // sTNjze3i3h3C5/KXaFhdCztq5NnkEC7NG1PRAz5CNqWw
|
||||||
|
// SIG // Ru6mkXVlAnfzkL22DvJ7kXyEb9MHZx/Y2na63ATzOnVU
|
||||||
|
// SIG // izGQC1BmAkB/Q4J8RHTVmCIDSOiZoFO3QAzPKzZXVyr2
|
||||||
|
// SIG // End signature block
|
283
RMDataManager/Scripts/jquery.validate.unobtrusive.min.js
vendored
Normal file
283
RMDataManager/Scripts/jquery.validate.unobtrusive.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1406
RMDataManager/Scripts/modernizr-2.8.3.js
vendored
Normal file
1406
RMDataManager/Scripts/modernizr-2.8.3.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18
RMDataManager/Startup.cs
Normal file
18
RMDataManager/Startup.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Owin;
|
||||||
|
using Owin;
|
||||||
|
|
||||||
|
[assembly: OwinStartup(typeof(RMDataManager.Startup))]
|
||||||
|
|
||||||
|
namespace RMDataManager
|
||||||
|
{
|
||||||
|
public partial class Startup
|
||||||
|
{
|
||||||
|
public void Configuration(IAppBuilder app)
|
||||||
|
{
|
||||||
|
ConfigureAuth(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
RMDataManager/Views/Home/Index.cshtml
Normal file
24
RMDataManager/Views/Home/Index.cshtml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<div class="jumbotron">
|
||||||
|
<h1>ASP.NET</h1>
|
||||||
|
<p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS, and JavaScript.</p>
|
||||||
|
<p><a href="https://asp.net" class="btn btn-primary btn-lg">Learn more »</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h2>Getting started</h2>
|
||||||
|
<p>ASP.NET Web API is a framework that makes it easy to build HTTP services that reach
|
||||||
|
a broad range of clients, including browsers and mobile devices. ASP.NET Web API
|
||||||
|
is an ideal platform for building RESTful applications on the .NET Framework.</p>
|
||||||
|
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301870">Learn more »</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h2>Get more libraries</h2>
|
||||||
|
<p>NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.</p>
|
||||||
|
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301871">Learn more »</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h2>Web Hosting</h2>
|
||||||
|
<p>You can easily find a web hosting company that offers the right mix of features and price for your applications.</p>
|
||||||
|
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301872">Learn more »</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
13
RMDataManager/Views/Shared/Error.cshtml
Normal file
13
RMDataManager/Views/Shared/Error.cshtml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<title>Error</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<hgroup>
|
||||||
|
<h1>Error.</h1>
|
||||||
|
<h2>An error occurred while processing your request.</h2>
|
||||||
|
</hgroup>
|
||||||
|
</body>
|
||||||
|
</html>
|
41
RMDataManager/Views/Shared/_Layout.cshtml
Normal file
41
RMDataManager/Views/Shared/_Layout.cshtml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<title>@ViewBag.Title - My ASP.NET Application</title>
|
||||||
|
@Styles.Render("~/Content/css")
|
||||||
|
@Scripts.Render("~/bundles/modernizr")
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse" title="more options">
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
@Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
|
||||||
|
</div>
|
||||||
|
<div class="navbar-collapse collapse">
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<li>@Html.ActionLink("Home", "Index", "Home", new { area = "" }, null)</li>
|
||||||
|
<li>@Html.ActionLink("API", "Index", "Help", new { area = "" }, null)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container body-content">
|
||||||
|
@RenderBody()
|
||||||
|
<hr />
|
||||||
|
<footer>
|
||||||
|
<p>© @DateTime.Now.Year - My ASP.NET Application</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@Scripts.Render("~/bundles/jquery")
|
||||||
|
@Scripts.Render("~/bundles/bootstrap")
|
||||||
|
@RenderSection("scripts", required: false)
|
||||||
|
</body>
|
||||||
|
</html>
|
43
RMDataManager/Views/Web.config
Normal file
43
RMDataManager/Views/Web.config
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||||
|
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||||
|
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||||
|
</sectionGroup>
|
||||||
|
</configSections>
|
||||||
|
|
||||||
|
<system.web.webPages.razor>
|
||||||
|
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
||||||
|
<pages pageBaseType="System.Web.Mvc.WebViewPage">
|
||||||
|
<namespaces>
|
||||||
|
<add namespace="System.Web.Mvc" />
|
||||||
|
<add namespace="System.Web.Mvc.Ajax" />
|
||||||
|
<add namespace="System.Web.Mvc.Html" />
|
||||||
|
<add namespace="System.Web.Optimization"/>
|
||||||
|
<add namespace="System.Web.Routing" />
|
||||||
|
<add namespace="RMDataManager" />
|
||||||
|
</namespaces>
|
||||||
|
</pages>
|
||||||
|
</system.web.webPages.razor>
|
||||||
|
|
||||||
|
<appSettings>
|
||||||
|
<add key="webpages:Enabled" value="false" />
|
||||||
|
</appSettings>
|
||||||
|
|
||||||
|
<system.webServer>
|
||||||
|
<handlers>
|
||||||
|
<remove name="BlockViewHandler"/>
|
||||||
|
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
|
||||||
|
</handlers>
|
||||||
|
</system.webServer>
|
||||||
|
|
||||||
|
<system.web>
|
||||||
|
<compilation>
|
||||||
|
<assemblies>
|
||||||
|
<add assembly="System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
||||||
|
</assemblies>
|
||||||
|
</compilation>
|
||||||
|
</system.web>
|
||||||
|
</configuration>
|
3
RMDataManager/Views/_ViewStart.cshtml
Normal file
3
RMDataManager/Views/_ViewStart.cshtml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@{
|
||||||
|
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||||
|
}
|
30
RMDataManager/Web.Debug.config
Normal file
30
RMDataManager/Web.Debug.config
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<!-- For more information on using Web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=301874 -->
|
||||||
|
|
||||||
|
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||||
|
<!--
|
||||||
|
In the example below, the "SetAttributes" transform will change the value of
|
||||||
|
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
|
||||||
|
finds an attribute "name" that has a value of "MyDB".
|
||||||
|
|
||||||
|
<connectionStrings>
|
||||||
|
<add name="MyDB"
|
||||||
|
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
|
||||||
|
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
|
||||||
|
</connectionStrings>
|
||||||
|
-->
|
||||||
|
<system.web>
|
||||||
|
<!--
|
||||||
|
In the example below, the "Replace" transform will replace the entire
|
||||||
|
<customErrors> section of your Web.config file.
|
||||||
|
Note that because there is only one customErrors section under the
|
||||||
|
<system.web> node, there is no need to use the "xdt:Locator" attribute.
|
||||||
|
|
||||||
|
<customErrors defaultRedirect="GenericError.htm"
|
||||||
|
mode="RemoteOnly" xdt:Transform="Replace">
|
||||||
|
<error statusCode="500" redirect="InternalError.htm"/>
|
||||||
|
</customErrors>
|
||||||
|
-->
|
||||||
|
</system.web>
|
||||||
|
</configuration>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user