mirror of https://github.com/abpframework/abp.git
committed by
GitHub
15 changed files with 793 additions and 26 deletions
@ -0,0 +1,96 @@ |
|||||
|
// <auto-generated/>
|
||||
|
#pragma warning disable 1591
|
||||
|
namespace Volo.Abp.AspNetCore.RazorViews |
||||
|
{ |
||||
|
#line hidden
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
#nullable restore |
||||
|
#line 1 "MultiTenancyMiddlewareErrorPage.cshtml"
|
||||
|
using System.Globalization; |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
#nullable restore |
||||
|
#line 2 "MultiTenancyMiddlewareErrorPage.cshtml"
|
||||
|
using Volo.Abp.AspNetCore.MultiTenancy.Views; |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
#nullable restore |
||||
|
#line 3 "MultiTenancyMiddlewareErrorPage.cshtml"
|
||||
|
using Volo.Abp.AspNetCore.RazorViews; |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
internal class MultiTenancyMiddlewareErrorPage : AbpCompilationRazorPageBase |
||||
|
{ |
||||
|
#pragma warning disable 1998
|
||||
|
public async override global::System.Threading.Tasks.Task ExecuteAsync() |
||||
|
{ |
||||
|
#nullable restore |
||||
|
#line 5 "MultiTenancyMiddlewareErrorPage.cshtml"
|
||||
|
|
||||
|
Response.ContentType = "text/html; charset=utf-8"; |
||||
|
Response.StatusCode = 404; |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
WriteLiteral("\n"); |
||||
|
WriteLiteral("\n<html"); |
||||
|
BeginWriteAttribute("lang", " lang=\"", 453, "\"", 512, 1); |
||||
|
#nullable restore |
||||
|
#line 19 "MultiTenancyMiddlewareErrorPage.cshtml"
|
||||
|
WriteAttributeValue("", 460, HtmlEncoder.Encode(CultureInfo.CurrentCulture.Name), 460, 52, false); |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
EndWriteAttribute(); |
||||
|
WriteLiteral(">\n <head>\n <meta charset=\"utf-8\" />\n <title>"); |
||||
|
#nullable restore |
||||
|
#line 22 "MultiTenancyMiddlewareErrorPage.cshtml"
|
||||
|
Write(HtmlEncoder.Encode(Model.Message)); |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
WriteLiteral("</title>\n </head>\n <body>\n <h3>"); |
||||
|
#nullable restore |
||||
|
#line 25 "MultiTenancyMiddlewareErrorPage.cshtml"
|
||||
|
Write(HtmlEncoder.Encode(Model.Message)); |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
WriteLiteral("</h3>\n <p>"); |
||||
|
#nullable restore |
||||
|
#line 26 "MultiTenancyMiddlewareErrorPage.cshtml"
|
||||
|
Write(HtmlEncoder.Encode(Model.Details)); |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
WriteLiteral("<p/>\n </body>\n</html>\n"); |
||||
|
} |
||||
|
#pragma warning restore 1998
|
||||
|
#nullable restore |
||||
|
#line 10 "MultiTenancyMiddlewareErrorPage.cshtml"
|
||||
|
|
||||
|
public MultiTenancyMiddlewareErrorPage(MultiTenancyMiddlewareErrorPageModel model) |
||||
|
{ |
||||
|
Model = model; |
||||
|
} |
||||
|
|
||||
|
public MultiTenancyMiddlewareErrorPageModel Model { get; set; } |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
} |
||||
|
} |
||||
|
#pragma warning restore 1591
|
||||
@ -0,0 +1,28 @@ |
|||||
|
@using System.Globalization |
||||
|
@using Volo.Abp.AspNetCore.MultiTenancy.Views |
||||
|
@using Volo.Abp.AspNetCore.RazorViews |
||||
|
@inherits AbpCompilationRazorPageBase |
||||
|
@{ |
||||
|
Response.ContentType = "text/html; charset=utf-8"; |
||||
|
Response.StatusCode = 404; |
||||
|
} |
||||
|
|
||||
|
@functions{ |
||||
|
public MultiTenancyMiddlewareErrorPage(MultiTenancyMiddlewareErrorPageModel model) |
||||
|
{ |
||||
|
Model = model; |
||||
|
} |
||||
|
|
||||
|
public MultiTenancyMiddlewareErrorPageModel Model { get; set; } |
||||
|
} |
||||
|
|
||||
|
<html lang="@HtmlEncoder.Encode(CultureInfo.CurrentCulture.Name)"> |
||||
|
<head> |
||||
|
<meta charset="utf-8" /> |
||||
|
<title>@HtmlEncoder.Encode(Model.Message)</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h3>@HtmlEncoder.Encode(Model.Message)</h3> |
||||
|
<p>@HtmlEncoder.Encode(Model.Details)<p/> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,14 @@ |
|||||
|
namespace Volo.Abp.AspNetCore.MultiTenancy.Views; |
||||
|
|
||||
|
public class MultiTenancyMiddlewareErrorPageModel |
||||
|
{ |
||||
|
public string Message { get; set; } |
||||
|
|
||||
|
public string Details { get; set; } |
||||
|
|
||||
|
public MultiTenancyMiddlewareErrorPageModel(string message, string details) |
||||
|
{ |
||||
|
Message = message; |
||||
|
Details = details; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
// <auto-generated/>
|
||||
|
#pragma warning disable 1591
|
||||
|
namespace Volo.Abp.AspNetCore.RazorViews |
||||
|
{ |
||||
|
#line hidden
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
#nullable restore |
||||
|
#line 1 "AbpMvcLibsErrorPage.cshtml"
|
||||
|
using Volo.Abp.AspNetCore.RazorViews; |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
internal class AbpMvcLibsErrorPage : AbpCompilationRazorPageBase |
||||
|
{ |
||||
|
#pragma warning disable 1998
|
||||
|
public async override global::System.Threading.Tasks.Task ExecuteAsync() |
||||
|
{ |
||||
|
#nullable restore |
||||
|
#line 3 "AbpMvcLibsErrorPage.cshtml"
|
||||
|
|
||||
|
Response.ContentType = "text/html; charset=utf-8"; |
||||
|
Response.StatusCode = 500; |
||||
|
|
||||
|
#line default
|
||||
|
#line hidden
|
||||
|
#nullable disable |
||||
|
WriteLiteral(@"
|
||||
|
<html> |
||||
|
<head> |
||||
|
<meta charset=""utf-8"" /> |
||||
|
<title>Error - The Libs folder is missing!</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1> ⚠️ The Libs folder under the <code style=""background-color: #e7e7e7;"">wwwroot/libs</code> directory is empty!</h1> |
||||
|
|
||||
|
<p>The Libs folder contains mandatory NPM Packages for running the project.</p> |
||||
|
|
||||
|
<p>Make sure you run the <code style=""background-color: #e7e7e7;"">abp install-libs</code> CLI tool command.</p> |
||||
|
|
||||
|
<p>For more information, check out the <a href=""https://abp.io/docs/latest/CLI#install-libs"">ABP CLI documentation</a></p>
|
||||
|
</body> |
||||
|
</html> |
||||
|
");
|
||||
|
} |
||||
|
#pragma warning restore 1998
|
||||
|
} |
||||
|
} |
||||
|
#pragma warning restore 1591
|
||||
@ -0,0 +1,22 @@ |
|||||
|
@using Volo.Abp.AspNetCore.RazorViews |
||||
|
@inherits AbpCompilationRazorPageBase |
||||
|
@{ |
||||
|
Response.ContentType = "text/html; charset=utf-8"; |
||||
|
Response.StatusCode = 500; |
||||
|
} |
||||
|
|
||||
|
<html> |
||||
|
<head> |
||||
|
<meta charset="utf-8" /> |
||||
|
<title>Error - The Libs folder is missing!</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1> ⚠️ The Libs folder under the <code style="background-color: #e7e7e7;">wwwroot/libs</code> directory is empty!</h1> |
||||
|
|
||||
|
<p>The Libs folder contains mandatory NPM Packages for running the project.</p> |
||||
|
|
||||
|
<p>Make sure you run the <code style="background-color: #e7e7e7;">abp install-libs</code> CLI tool command.</p> |
||||
|
|
||||
|
<p>For more information, check out the <a href="https://abp.io/docs/latest/CLI#install-libs">ABP CLI documentation</a></p> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,283 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
using System.Globalization; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Text.Encodings.Web; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.RazorViews; |
||||
|
|
||||
|
public abstract class AbpCompilationRazorPageBase |
||||
|
{ |
||||
|
private readonly static Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); |
||||
|
private readonly static char[] NewLineChars = new[] { '\r', '\n' }; |
||||
|
private readonly Stack<TextWriter> _textWriterStack = new Stack<TextWriter>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The request context
|
||||
|
/// </summary>
|
||||
|
protected HttpContext Context { get; private set; } = default!; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The request
|
||||
|
/// </summary>
|
||||
|
protected HttpRequest Request { get; private set; } = default!; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The response
|
||||
|
/// </summary>
|
||||
|
protected HttpResponse Response { get; private set; } = default!; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The output stream
|
||||
|
/// </summary>
|
||||
|
protected TextWriter Output { get; private set; } = default!; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Html encoder used to encode content.
|
||||
|
/// </summary>
|
||||
|
protected HtmlEncoder HtmlEncoder { get; set; } = HtmlEncoder.Default; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Url encoder used to encode content.
|
||||
|
/// </summary>
|
||||
|
protected UrlEncoder UrlEncoder { get; set; } = UrlEncoder.Default; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// JavaScript encoder used to encode content.
|
||||
|
/// </summary>
|
||||
|
protected JavaScriptEncoder JavaScriptEncoder { get; set; } = JavaScriptEncoder.Default; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Execute an individual request
|
||||
|
/// </summary>
|
||||
|
/// <param name="stream">The stream to write to</param>
|
||||
|
public async Task ExecuteAsync(Stream stream) |
||||
|
{ |
||||
|
// We technically don't need this intermediate buffer if this method accepts a memory stream.
|
||||
|
var buffer = new MemoryStream(); |
||||
|
Output = new StreamWriter(buffer, UTF8NoBOM, 4096, leaveOpen: true); |
||||
|
await ExecuteAsync(); |
||||
|
await Output.FlushAsync(); |
||||
|
await Output.DisposeAsync(); |
||||
|
buffer.Seek(0, SeekOrigin.Begin); |
||||
|
await buffer.CopyToAsync(stream); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Execute an individual request
|
||||
|
/// </summary>
|
||||
|
/// <param name="context"></param>
|
||||
|
public async Task ExecuteAsync(HttpContext context) |
||||
|
{ |
||||
|
Context = context; |
||||
|
Request = Context.Request; |
||||
|
Response = Context.Response; |
||||
|
var buffer = new MemoryStream(); |
||||
|
Output = new StreamWriter(buffer, UTF8NoBOM, 4096, leaveOpen: true); |
||||
|
await ExecuteAsync(); |
||||
|
await Output.FlushAsync(); |
||||
|
await Output.DisposeAsync(); |
||||
|
buffer.Seek(0, SeekOrigin.Begin); |
||||
|
await buffer.CopyToAsync(Response.Body); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Execute an individual request
|
||||
|
/// </summary>
|
||||
|
public abstract Task ExecuteAsync(); |
||||
|
|
||||
|
protected virtual void PushWriter(TextWriter writer) |
||||
|
{ |
||||
|
ArgumentNullException.ThrowIfNull(writer); |
||||
|
|
||||
|
_textWriterStack.Push(Output); |
||||
|
Output = writer; |
||||
|
} |
||||
|
|
||||
|
protected virtual TextWriter PopWriter() |
||||
|
{ |
||||
|
Output = _textWriterStack.Pop(); |
||||
|
return Output; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Write the given value without HTML encoding directly to <see cref="Output"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The <see cref="object"/> to write.</param>
|
||||
|
protected void WriteLiteral(object value) |
||||
|
{ |
||||
|
WriteLiteral(Convert.ToString(value, CultureInfo.InvariantCulture)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Write the given value without HTML encoding directly to <see cref="Output"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The <see cref="string"/> to write.</param>
|
||||
|
protected void WriteLiteral(string? value) |
||||
|
{ |
||||
|
if (!string.IsNullOrEmpty(value)) |
||||
|
{ |
||||
|
Output.Write(value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private List<string>? AttributeValues { get; set; } |
||||
|
|
||||
|
protected void WriteAttributeValue(string thingy, int startPostion, object value, int endValue, int dealyo, bool yesno) |
||||
|
{ |
||||
|
if (AttributeValues == null) |
||||
|
{ |
||||
|
AttributeValues = new List<string>(); |
||||
|
} |
||||
|
|
||||
|
AttributeValues.Add(value.ToString()!); |
||||
|
} |
||||
|
|
||||
|
private string? AttributeEnding { get; set; } |
||||
|
|
||||
|
protected void BeginWriteAttribute(string name, string beginning, int startPosition, string ending, int endPosition, int thingy) |
||||
|
{ |
||||
|
Debug.Assert(string.IsNullOrEmpty(AttributeEnding)); |
||||
|
|
||||
|
Output.Write(beginning); |
||||
|
AttributeEnding = ending; |
||||
|
} |
||||
|
|
||||
|
protected void EndWriteAttribute() |
||||
|
{ |
||||
|
Debug.Assert(AttributeValues != null); |
||||
|
Debug.Assert(!string.IsNullOrEmpty(AttributeEnding)); |
||||
|
|
||||
|
var attributes = string.Join(" ", AttributeValues); |
||||
|
Output.Write(attributes); |
||||
|
AttributeValues = null; |
||||
|
|
||||
|
Output.Write(AttributeEnding); |
||||
|
AttributeEnding = null; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes the given attribute to the given writer
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The name of the attribute to write</param>
|
||||
|
/// <param name="leader">The value of the prefix</param>
|
||||
|
/// <param name="trailer">The value of the suffix</param>
|
||||
|
/// <param name="values">The <see cref="AttributeValue"/>s to write.</param>
|
||||
|
protected void WriteAttribute( |
||||
|
string name, |
||||
|
string leader, |
||||
|
string trailer, |
||||
|
params AttributeValue[] values) |
||||
|
{ |
||||
|
ArgumentNullException.ThrowIfNull(name); |
||||
|
ArgumentNullException.ThrowIfNull(leader); |
||||
|
ArgumentNullException.ThrowIfNull(trailer); |
||||
|
|
||||
|
WriteLiteral(leader); |
||||
|
foreach (var value in values) |
||||
|
{ |
||||
|
WriteLiteral(value.Prefix); |
||||
|
|
||||
|
// The special cases here are that the value we're writing might already be a string, or that the
|
||||
|
// value might be a bool. If the value is the bool 'true' we want to write the attribute name
|
||||
|
// instead of the string 'true'. If the value is the bool 'false' we don't want to write anything.
|
||||
|
// Otherwise the value is another object (perhaps an HtmlString) and we'll ask it to format itself.
|
||||
|
string? stringValue; |
||||
|
if (value.Value is bool) |
||||
|
{ |
||||
|
if ((bool)value.Value) |
||||
|
{ |
||||
|
stringValue = name; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
stringValue = value.Value as string; |
||||
|
} |
||||
|
|
||||
|
// Call the WriteTo(string) overload when possible
|
||||
|
if (value.Literal && stringValue != null) |
||||
|
{ |
||||
|
WriteLiteral(stringValue); |
||||
|
} |
||||
|
else if (value.Literal) |
||||
|
{ |
||||
|
WriteLiteral(value.Value); |
||||
|
} |
||||
|
else if (stringValue != null) |
||||
|
{ |
||||
|
Write(stringValue); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Write(value.Value); |
||||
|
} |
||||
|
} |
||||
|
WriteLiteral(trailer); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// <see cref="HelperResult.WriteTo(TextWriter)"/> is invoked
|
||||
|
/// </summary>
|
||||
|
/// <param name="result">The <see cref="HelperResult"/> to invoke</param>
|
||||
|
protected void Write(HelperResult result) |
||||
|
{ |
||||
|
result.WriteTo(Output); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes the specified <paramref name="value"/> to <see cref="Output"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The <see cref="object"/> to write.</param>
|
||||
|
/// <remarks>
|
||||
|
/// <see cref="HelperResult.WriteTo(TextWriter)"/> is invoked for <see cref="HelperResult"/> types.
|
||||
|
/// For all other types, the encoded result of <see cref="object.ToString"/> is written to
|
||||
|
/// <see cref="Output"/>.
|
||||
|
/// </remarks>
|
||||
|
protected void Write(object value) |
||||
|
{ |
||||
|
if (value is HelperResult helperResult) |
||||
|
{ |
||||
|
helperResult.WriteTo(Output); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Write(Convert.ToString(value, CultureInfo.InvariantCulture)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes the specified <paramref name="value"/> with HTML encoding to <see cref="Output"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The <see cref="string"/> to write.</param>
|
||||
|
protected void Write(string? value) |
||||
|
{ |
||||
|
if (!string.IsNullOrEmpty(value)) |
||||
|
{ |
||||
|
WriteLiteral(HtmlEncoder.Encode(value)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected string HtmlEncodeAndReplaceLineBreaks(string input) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(input)) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
// Split on line breaks before passing it through the encoder.
|
||||
|
return string.Join("<br />" + Environment.NewLine, |
||||
|
input.Split("\r\n", StringSplitOptions.None) |
||||
|
.SelectMany(s => s.Split(NewLineChars, StringSplitOptions.None)) |
||||
|
.Select(HtmlEncoder.Encode)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.RazorViews; |
||||
|
|
||||
|
public class AttributeValue |
||||
|
{ |
||||
|
public AttributeValue(string prefix, object value, bool literal) |
||||
|
{ |
||||
|
Prefix = prefix; |
||||
|
Value = value; |
||||
|
Literal = literal; |
||||
|
} |
||||
|
|
||||
|
public string Prefix { get; } |
||||
|
|
||||
|
public object Value { get; } |
||||
|
|
||||
|
public bool Literal { get; } |
||||
|
|
||||
|
public static AttributeValue FromTuple(Tuple<string, object, bool> value) |
||||
|
{ |
||||
|
return new AttributeValue(value.Item1, value.Item2, value.Item3); |
||||
|
} |
||||
|
|
||||
|
public static AttributeValue FromTuple(Tuple<string, string, bool> value) |
||||
|
{ |
||||
|
return new AttributeValue(value.Item1, value.Item2, value.Item3); |
||||
|
} |
||||
|
|
||||
|
public static implicit operator AttributeValue(Tuple<string, object, bool> value) |
||||
|
{ |
||||
|
return FromTuple(value); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System; |
||||
|
using System.IO; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.RazorViews; |
||||
|
|
||||
|
public class HelperResult |
||||
|
{ |
||||
|
public HelperResult(Action<TextWriter> action) |
||||
|
{ |
||||
|
WriteAction = action; |
||||
|
} |
||||
|
|
||||
|
public Action<TextWriter> WriteAction { get; } |
||||
|
|
||||
|
public void WriteTo(TextWriter writer) |
||||
|
{ |
||||
|
WriteAction(writer); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,227 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Razor.Language; |
||||
|
using Microsoft.AspNetCore.Razor.Language.Extensions; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Volo.Abp.Cli.Args; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Commands; |
||||
|
|
||||
|
public class GenerateRazorPage : IConsoleCommand, ITransientDependency |
||||
|
{ |
||||
|
public const string Name = "generate-razor-page"; |
||||
|
|
||||
|
public ILogger<GenerateRazorPage> Logger { get; set; } |
||||
|
|
||||
|
public GenerateRazorPage() |
||||
|
{ |
||||
|
Logger = NullLogger<GenerateRazorPage>.Instance; |
||||
|
} |
||||
|
|
||||
|
public Task ExecuteAsync(CommandLineArgs commandLineArgs) |
||||
|
{ |
||||
|
var targetProjectDirectory = Directory.GetCurrentDirectory(); |
||||
|
var projectEngine = CreateProjectEngine(targetProjectDirectory); |
||||
|
|
||||
|
var results = MainCore(projectEngine, targetProjectDirectory); |
||||
|
|
||||
|
foreach (var result in results) |
||||
|
{ |
||||
|
File.WriteAllText(result.FilePath, result.GeneratedCode); |
||||
|
} |
||||
|
|
||||
|
Logger.LogInformation($"{results.Count} files successfully generated."); |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public string GetUsageInfo() |
||||
|
{ |
||||
|
var sb = new StringBuilder(); |
||||
|
|
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("Usage:"); |
||||
|
sb.AppendLine("abp generate-razor-page"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("See the documentation for more info: https://abp.io/docs/latest/cli"); |
||||
|
|
||||
|
return sb.ToString(); |
||||
|
} |
||||
|
|
||||
|
public string GetShortDescription() |
||||
|
{ |
||||
|
return "Generates code files for Razor page."; |
||||
|
} |
||||
|
|
||||
|
private RazorProjectEngine CreateProjectEngine(string targetProjectDirectory, Action<RazorProjectEngineBuilder>? configure = null) |
||||
|
{ |
||||
|
var fileSystem = RazorProjectFileSystem.Create(targetProjectDirectory); |
||||
|
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, builder => |
||||
|
{ |
||||
|
builder |
||||
|
.SetNamespace("Volo.Abp.AspNetCore.RazorViews") |
||||
|
.ConfigureClass((document, @class) => |
||||
|
{ |
||||
|
@class.ClassName = Path.GetFileNameWithoutExtension(document.Source.FilePath); |
||||
|
@class.Modifiers.Clear(); |
||||
|
@class.Modifiers.Add("internal"); |
||||
|
}); |
||||
|
|
||||
|
SectionDirective.Register(builder); |
||||
|
|
||||
|
builder.Features.Add(new SuppressChecksumOptionsFeature()); |
||||
|
builder.Features.Add(new SuppressMetadataAttributesFeature()); |
||||
|
|
||||
|
if (configure != null) |
||||
|
{ |
||||
|
configure(builder); |
||||
|
} |
||||
|
|
||||
|
builder.AddDefaultImports(@"
|
||||
|
@using System |
||||
|
@using System.Threading.Tasks");
|
||||
|
}); |
||||
|
|
||||
|
return projectEngine; |
||||
|
} |
||||
|
|
||||
|
private List<RazorPageGeneratorResult> MainCore(RazorProjectEngine projectEngine, string targetProjectDirectory) |
||||
|
{ |
||||
|
var results = new List<RazorPageGeneratorResult>(); |
||||
|
Logger.LogInformation("Generating code files for pages in {0}", targetProjectDirectory); |
||||
|
|
||||
|
var cshtmlFiles = projectEngine.FileSystem.EnumerateItems(targetProjectDirectory) |
||||
|
.Where(x => File.ReadAllText(x.PhysicalPath).Contains("@inherits AbpCompilationRazorPageBase")) |
||||
|
.ToList(); |
||||
|
|
||||
|
if (!cshtmlFiles.Any()) |
||||
|
{ |
||||
|
Logger.LogInformation("No .cshtml or .razor files were found."); |
||||
|
return results; |
||||
|
} |
||||
|
|
||||
|
foreach (var item in cshtmlFiles) |
||||
|
{ |
||||
|
Logger.LogInformation(" Generating code file for page {0} ...", item.FileName); |
||||
|
|
||||
|
results.Add(GenerateCodeFile(projectEngine, item)); |
||||
|
|
||||
|
Logger.LogInformation(" Done!"); |
||||
|
} |
||||
|
|
||||
|
return results; |
||||
|
} |
||||
|
|
||||
|
private RazorPageGeneratorResult GenerateCodeFile(RazorProjectEngine projectEngine, RazorProjectItem projectItem) |
||||
|
{ |
||||
|
var projectItemWrapper = new FileSystemRazorProjectItemWrapper(Logger, projectItem); |
||||
|
var codeDocument = projectEngine.Process(projectItemWrapper); |
||||
|
var cSharpDocument = codeDocument.GetCSharpDocument(); |
||||
|
if (cSharpDocument.Diagnostics.Any()) |
||||
|
{ |
||||
|
var diagnostics = string.Join(Environment.NewLine, cSharpDocument.Diagnostics); |
||||
|
Logger.LogInformation($"One or more parse errors encountered. This will not prevent the generator from continuing: {Environment.NewLine}{diagnostics}."); |
||||
|
} |
||||
|
|
||||
|
var generatedCodeFilePath = Path.ChangeExtension(projectItem.PhysicalPath, ".Designer.cs"); |
||||
|
return new RazorPageGeneratorResult |
||||
|
{ |
||||
|
FilePath = generatedCodeFilePath, |
||||
|
GeneratedCode = cSharpDocument.GeneratedCode, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private class SuppressChecksumOptionsFeature : RazorEngineFeatureBase, IConfigureRazorCodeGenerationOptionsFeature |
||||
|
{ |
||||
|
public int Order { get; set; } |
||||
|
|
||||
|
public void Configure(RazorCodeGenerationOptionsBuilder options) |
||||
|
{ |
||||
|
Check.NotNull(options, nameof(options)); |
||||
|
|
||||
|
options.SuppressChecksum = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class SuppressMetadataAttributesFeature : RazorEngineFeatureBase, IConfigureRazorCodeGenerationOptionsFeature |
||||
|
{ |
||||
|
public int Order { get; set; } |
||||
|
|
||||
|
public void Configure(RazorCodeGenerationOptionsBuilder options) |
||||
|
{ |
||||
|
Check.NotNull(options, nameof(options)); |
||||
|
options.SuppressMetadataAttributes = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class FileSystemRazorProjectItemWrapper : RazorProjectItem |
||||
|
{ |
||||
|
private readonly ILogger<GenerateRazorPage> _logger; |
||||
|
private readonly RazorProjectItem _source; |
||||
|
|
||||
|
public FileSystemRazorProjectItemWrapper(ILogger<GenerateRazorPage> logger, RazorProjectItem item) |
||||
|
{ |
||||
|
_logger = logger; |
||||
|
_source = item; |
||||
|
|
||||
|
// Mask the full name since we don't want a developer's local file paths to be committed.
|
||||
|
PhysicalPath = $"{_source.FileName}"; |
||||
|
} |
||||
|
|
||||
|
public override string BasePath => _source.BasePath; |
||||
|
|
||||
|
public override string FilePath => _source.FilePath; |
||||
|
|
||||
|
public override string PhysicalPath { get; } |
||||
|
|
||||
|
public override bool Exists => _source.Exists; |
||||
|
|
||||
|
public override Stream Read() |
||||
|
{ |
||||
|
var processedContent = ProcessFileIncludes(); |
||||
|
return new MemoryStream(Encoding.UTF8.GetBytes(processedContent)); |
||||
|
} |
||||
|
|
||||
|
private string ProcessFileIncludes() |
||||
|
{ |
||||
|
var basePath = Path.GetDirectoryName(_source.PhysicalPath); |
||||
|
var cshtmlContent = File.ReadAllText(_source.PhysicalPath); |
||||
|
|
||||
|
var startMatch = "<%$ include: "; |
||||
|
var endMatch = " %>"; |
||||
|
var startIndex = 0; |
||||
|
while (startIndex < cshtmlContent.Length) |
||||
|
{ |
||||
|
startIndex = cshtmlContent.IndexOf(startMatch, startIndex, StringComparison.Ordinal); |
||||
|
if (startIndex == -1) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
var endIndex = cshtmlContent.IndexOf(endMatch, startIndex, StringComparison.Ordinal); |
||||
|
if (endIndex == -1) |
||||
|
{ |
||||
|
throw new InvalidOperationException($"Invalid include file format in {_source.PhysicalPath}. Usage example: <%$ include: ErrorPage.js %>"); |
||||
|
} |
||||
|
var includeFileName = cshtmlContent.Substring(startIndex + startMatch.Length, endIndex - (startIndex + startMatch.Length)); |
||||
|
_logger.LogInformation(" Inlining file {0}", includeFileName); |
||||
|
var includeFileContent = File.ReadAllText(Path.Combine(basePath, includeFileName)); |
||||
|
cshtmlContent = string.Concat(cshtmlContent.Substring(0, startIndex), includeFileContent, cshtmlContent.Substring(endIndex + endMatch.Length)); |
||||
|
startIndex += includeFileContent.Length; |
||||
|
} |
||||
|
return cshtmlContent; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class RazorPageGeneratorResult |
||||
|
{ |
||||
|
public string FilePath { get; set; } |
||||
|
|
||||
|
public string GeneratedCode { get; set; } |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue