mirror of https://github.com/abpframework/abp.git
491 changed files with 66439 additions and 643 deletions
@ -0,0 +1,9 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Property)] |
|||
public class DynamicFormIgnore : Attribute |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
{ |
|||
"iisSettings": { |
|||
"windowsAuthentication": false, |
|||
"anonymousAuthentication": true, |
|||
"iisExpress": { |
|||
"applicationUrl": "http://localhost:57333/", |
|||
"sslPort": 0 |
|||
} |
|||
}, |
|||
"profiles": { |
|||
"IIS Express": { |
|||
"commandName": "IISExpress", |
|||
"launchBrowser": true, |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
}, |
|||
"Volo.Abp.AspNetCore.Mvc.UI.Dashboards": { |
|||
"commandName": "Project", |
|||
"launchBrowser": true, |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
}, |
|||
"applicationUrl": "http://localhost:57343/" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Dashboards</AssemblyName> |
|||
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Dashboards</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<IsPackable>true</IsPackable> |
|||
<OutputType>Library</OutputType> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="Volo\Abp\AspNetCore\Mvc\UI\Dashboards\Components\**\*.cshtml" /> |
|||
<Content Remove="Volo\Abp\AspNetCore\Mvc\UI\Dashboards\Components\**\*.cshtml" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Content Remove="Volo\Abp\AspNetCore\Mvc\UI\Dashboards\_ViewImports.cshtml" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="Volo\Abp\AspNetCore\Mvc\UI\Dashboards\_ViewImports.cshtml" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Widgets\Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,20 @@ |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreMvcUiWidgetsModule) |
|||
)] |
|||
public class AbpAspNetCoreMvcUiDashboardsModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<VirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpAspNetCoreMvcUiDashboardsModule>(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.Options; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard |
|||
{ |
|||
public class DashboardViewComponent : AbpViewComponent |
|||
{ |
|||
public async Task<IViewComponentResult> InvokeAsync(string dashboardName, DashboardOptions dashboardOptions, WidgetOptions widgetOptions) |
|||
{ |
|||
var model = new DashboardViewModel(dashboardName, dashboardOptions, widgetOptions); |
|||
return View("~/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/Default.cshtml", model); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard |
|||
{ |
|||
public class DashboardViewModel |
|||
{ |
|||
public DashboardDefinition Dashboard { get; set; } |
|||
public DashboardOptions DashboardOptions { get; set; } |
|||
public WidgetOptions WidgetOptions { get; set; } |
|||
|
|||
public DashboardViewModel(string dashboardName, DashboardOptions dashboardOptions, WidgetOptions widgetOptions) |
|||
{ |
|||
Dashboard = dashboardOptions.Dashboards.Single(d => d.Name.Equals(dashboardName)); |
|||
DashboardOptions = dashboardOptions; |
|||
WidgetOptions = widgetOptions; |
|||
} |
|||
|
|||
public WidgetDefinition GetWidget(string name) |
|||
{ |
|||
return WidgetOptions.Widgets.Single(d => d.Name.Equals(name)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
@using Microsoft.Extensions.Localization |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets |
|||
@inject IWidgetRenderer WidgetRenderer |
|||
@inject IStringLocalizerFactory localizer |
|||
@model Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard.DashboardViewModel |
|||
@{ |
|||
} |
|||
<abp-row> |
|||
@foreach (var widgetConfiguration in Model.Dashboard.AvailableWidgets) |
|||
{ |
|||
var widgetDefinition = Model.GetWidget(widgetConfiguration.WidgetName); |
|||
widgetDefinition.DefaultDimensions = widgetConfiguration.Dimensions ?? widgetDefinition.DefaultDimensions ?? new WidgetDimensions(5, 5); |
|||
|
|||
<abp-column class="col-@widgetDefinition.DefaultDimensions.Width" |
|||
style="height: @(widgetDefinition.DefaultDimensions.Height * 100)px" |
|||
abp-border="Danger"> |
|||
|
|||
@await WidgetRenderer.RenderAsync(Component, widgetDefinition.Name) |
|||
</abp-column> |
|||
} |
|||
</abp-row> |
|||
@ -0,0 +1,49 @@ |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public class DashboardDefinition |
|||
{ |
|||
/// <summary>
|
|||
/// Unique name of the dashboard.
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public string Name { get; } |
|||
|
|||
/// <summary>
|
|||
/// A list of Widgets available for this dashboard.
|
|||
/// </summary>
|
|||
public List<DashboardWidgetConfiguration> AvailableWidgets { get; } |
|||
|
|||
/// <summary>
|
|||
/// Display name of the widget.
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public ILocalizableString DisplayName |
|||
{ |
|||
get => _displayName; |
|||
set => _displayName = Check.NotNull(value, nameof(value)); |
|||
} |
|||
private ILocalizableString _displayName; |
|||
|
|||
public DashboardDefinition( |
|||
[NotNull] string name, |
|||
[CanBeNull] ILocalizableString displayName) |
|||
{ |
|||
Name = Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
DisplayName = displayName ?? new FixedLocalizableString(name); |
|||
|
|||
AvailableWidgets = new List<DashboardWidgetConfiguration>(); |
|||
} |
|||
|
|||
public DashboardDefinition WithWidget(string widgetName, WidgetLocation location = null, WidgetDimensions dimensions = null) |
|||
{ |
|||
AvailableWidgets.Add( new DashboardWidgetConfiguration(widgetName, dimensions, location)); |
|||
|
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public class DashboardOptions |
|||
{ |
|||
public List<DashboardDefinition> Dashboards { get; } |
|||
|
|||
public DashboardOptions() |
|||
{ |
|||
Dashboards = new List<DashboardDefinition>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Html; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public class DashboardRenderer : IDashboardRenderer |
|||
{ |
|||
public async Task<IHtmlContent> RenderAsync(IViewComponentHelper componentHelper, object args = null) |
|||
{ |
|||
return await componentHelper.InvokeAsync(typeof(DashboardViewComponent), args ?? new object()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public class DashboardWidgetConfiguration |
|||
{ |
|||
[NotNull] |
|||
public string WidgetName { get; } |
|||
|
|||
[CanBeNull] |
|||
public WidgetDimensions Dimensions { get; set; } |
|||
|
|||
[CanBeNull] |
|||
public WidgetLocation Location { get; set; } |
|||
|
|||
public DashboardWidgetConfiguration( |
|||
[NotNull] string widgetName, |
|||
[CanBeNull] WidgetDimensions dimensions = null, |
|||
[CanBeNull] WidgetLocation location = null |
|||
) |
|||
{ |
|||
WidgetName = Check.NotNullOrWhiteSpace(widgetName, nameof(widgetName)); |
|||
Dimensions = dimensions; |
|||
Location = location; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Html; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public interface IDashboardRenderer : ITransientDependency |
|||
{ |
|||
Task<IHtmlContent> RenderAsync(IViewComponentHelper componentHelper, object args = null); |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public class WidgetLocation |
|||
{ |
|||
public int X { get; set; } |
|||
|
|||
public int Y { get; set; } |
|||
|
|||
public WidgetLocation(int x, int y) |
|||
{ |
|||
X = x; |
|||
Y = y; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers |
|||
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap |
|||
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Widgets</AssemblyName> |
|||
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Widgets</PackageId> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreMvcUiBootstrapModule) |
|||
)] |
|||
public class AbpAspNetCoreMvcUiWidgetsModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Html; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
|||
{ |
|||
public interface IWidgetRenderer : ITransientDependency |
|||
{ |
|||
Task<IHtmlContent> RenderAsync(IViewComponentHelper componentHelper, string widgetName, object args = null); |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
|||
{ |
|||
public class WidgetDefinition |
|||
{ |
|||
/// <summary>
|
|||
/// Unique name of the widget.
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public string Name { get; } |
|||
|
|||
/// <summary>
|
|||
/// Display name of the widget.
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public ILocalizableString DisplayName |
|||
{ |
|||
get => _displayName; |
|||
set => _displayName = Check.NotNull(value, nameof(value)); |
|||
} |
|||
private ILocalizableString _displayName; |
|||
|
|||
[NotNull] |
|||
public Type ViewComponentType { get; } |
|||
|
|||
[CanBeNull] |
|||
public WidgetDimensions DefaultDimensions { get; set; } |
|||
|
|||
public WidgetDefinition( |
|||
[NotNull] string name, |
|||
[NotNull] Type viewComponentType, |
|||
[CanBeNull] ILocalizableString displayName, |
|||
[CanBeNull] WidgetDimensions defaultDimensions = null) |
|||
{ |
|||
DefaultDimensions = defaultDimensions; |
|||
Name = Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
ViewComponentType = Check.NotNull(viewComponentType, nameof(viewComponentType)); |
|||
DisplayName = displayName ?? new FixedLocalizableString(name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
|||
{ |
|||
public class WidgetDimensions |
|||
{ |
|||
public int Width { get; } |
|||
|
|||
public int Height { get; } |
|||
|
|||
public WidgetDimensions(int width, int height) |
|||
{ |
|||
Width = width; |
|||
Height = height; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
|||
{ |
|||
public class WidgetOptions |
|||
{ |
|||
public List<WidgetDefinition> Widgets { get; } |
|||
|
|||
public WidgetOptions() |
|||
{ |
|||
Widgets = new List<WidgetDefinition>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Html; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets |
|||
{ |
|||
public class WidgetRenderer : IWidgetRenderer |
|||
{ |
|||
private readonly WidgetOptions _widgetOptions; |
|||
|
|||
public WidgetRenderer(IOptions<WidgetOptions> widgetOptions) |
|||
{ |
|||
_widgetOptions = widgetOptions.Value; |
|||
} |
|||
|
|||
public async Task<IHtmlContent> RenderAsync(IViewComponentHelper componentHelper, string widgetName, object args = null) |
|||
{ |
|||
var componentType = _widgetOptions.Widgets.Single(w=>w.Name.Equals(widgetName)).ViewComponentType; |
|||
|
|||
return await componentHelper.InvokeAsync(componentType, args ?? new object()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Cli |
|||
{ |
|||
public class CliUsageException : Exception |
|||
{ |
|||
public CliUsageException(string message) |
|||
: base(message) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public CliUsageException(string message, Exception innerException) |
|||
: base(message, innerException) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -1,55 +0,0 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Cli.Args; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.ProjectModification; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class AddCommand : IConsoleCommand, ITransientDependency |
|||
{ |
|||
public ILogger<AddCommand> Logger { get; set; } |
|||
|
|||
protected ModuleAdder ModuleAdder { get; } |
|||
|
|||
public AddCommand(ModuleAdder moduleAdder) |
|||
{ |
|||
ModuleAdder = moduleAdder; |
|||
Logger = NullLogger<AddCommand>.Instance; |
|||
} |
|||
|
|||
public async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
|||
{ |
|||
if (commandLineArgs.Target == null) |
|||
{ |
|||
Logger.LogWarning("Module name is missing."); |
|||
AddCommandHelper.WriteUsage(Logger); |
|||
return; |
|||
} |
|||
|
|||
await ModuleAdder.AddModuleAsync( |
|||
new AddModuleArgs( |
|||
commandLineArgs.Target, |
|||
commandLineArgs.Options.GetOrNull(Options.Solution.Short, Options.Solution.Long), |
|||
commandLineArgs.Options.GetOrNull(Options.Project.Short, Options.Project.Long) |
|||
) |
|||
); |
|||
} |
|||
|
|||
public static class Options |
|||
{ |
|||
public static class Solution |
|||
{ |
|||
public const string Short = "s"; |
|||
public const string Long = "solution"; |
|||
} |
|||
|
|||
public static class Project |
|||
{ |
|||
public const string Short = "p"; |
|||
public const string Long = "project"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Cli.Args; |
|||
using Volo.Abp.Cli.ProjectModification; |
|||
using Volo.Abp.Cli.Utils; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class AddModuleCommand : IConsoleCommand, ITransientDependency |
|||
{ |
|||
public ILogger<AddModuleCommand> Logger { get; set; } |
|||
|
|||
protected SolutionModuleAdder SolutionModuleAdder { get; } |
|||
|
|||
public AddModuleCommand(SolutionModuleAdder solutionModuleAdder) |
|||
{ |
|||
SolutionModuleAdder = solutionModuleAdder; |
|||
Logger = NullLogger<AddModuleCommand>.Instance; |
|||
} |
|||
|
|||
public async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
|||
{ |
|||
if (commandLineArgs.Target == null) |
|||
{ |
|||
throw new CliUsageException("Module name is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo()); |
|||
} |
|||
|
|||
var skipDbMigrations = Convert.ToBoolean( |
|||
commandLineArgs.Options.GetOrNull(Options.DbMigrations.Skip) ?? "false"); |
|||
|
|||
await SolutionModuleAdder.AddAsync( |
|||
GetSolutionFile(commandLineArgs), |
|||
commandLineArgs.Target, |
|||
skipDbMigrations |
|||
); |
|||
} |
|||
|
|||
protected virtual string GetSolutionFile(CommandLineArgs commandLineArgs) |
|||
{ |
|||
var providedSolutionFile = PathHelper.NormalizePath( |
|||
commandLineArgs.Options.GetOrNull( |
|||
AddPackageCommand.Options.Project.Short, |
|||
AddPackageCommand.Options.Project.Long |
|||
) |
|||
); |
|||
|
|||
if (!providedSolutionFile.IsNullOrWhiteSpace()) |
|||
{ |
|||
return providedSolutionFile; |
|||
} |
|||
|
|||
var foundSolutionFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.sln"); |
|||
if (foundSolutionFiles.Length == 1) |
|||
{ |
|||
return foundSolutionFiles[0]; |
|||
} |
|||
|
|||
if (foundSolutionFiles.Length == 0) |
|||
{ |
|||
throw new CliUsageException("'abp add-module' command should be used inside a folder contaning a .sln file!"); |
|||
} |
|||
|
|||
//foundSolutionFiles.Length > 1
|
|||
|
|||
var sb = new StringBuilder("There are multiple solution (.sln) files in the current directory. Please specify one of the files below:"); |
|||
|
|||
foreach (var foundSolutionFile in foundSolutionFiles) |
|||
{ |
|||
sb.AppendLine("* " + foundSolutionFile); |
|||
} |
|||
|
|||
sb.AppendLine("Example:"); |
|||
sb.AppendLine($"abp add-module {commandLineArgs.Target} -p {foundSolutionFiles[0]}"); |
|||
|
|||
throw new CliUsageException(sb.ToString()); |
|||
} |
|||
|
|||
protected virtual string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("'add-module' command is used to add a multi-package ABP module to a solution."); |
|||
sb.AppendLine("It should be used in a folder containing a .sln file."); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Usage:"); |
|||
sb.AppendLine(" abp add-module <module-name> [-s|--solution]"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Options:"); |
|||
sb.AppendLine(" -s|--solution <solution-file> Specify the solution file explicitly."); |
|||
sb.AppendLine(" --skip-db-migrations <boolean> Specify if a new migration will be added or not."); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Examples:"); |
|||
sb.AppendLine(" abp add-module Volo.Blogging Adds the module to the current soluton."); |
|||
sb.AppendLine(" abp add-module Volo.Blogging -s Acme.BookStore Adds the module to the given soluton."); |
|||
sb.AppendLine(" abp add-module Volo.Blogging -s Acme.BookStore --skip-db-migrations false Adds the module to the given soluton but doesn't add-migration."); |
|||
sb.AppendLine(""); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public static class Options |
|||
{ |
|||
public static class Solution |
|||
{ |
|||
public const string Short = "s"; |
|||
public const string Long = "solution"; |
|||
} |
|||
|
|||
public static class DbMigrations |
|||
{ |
|||
public const string Skip = "skip-db-migrations"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Volo.Abp.Cli.Args; |
|||
using Volo.Abp.Cli.ProjectModification; |
|||
using Volo.Abp.Cli.Utils; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class AddPackageCommand : IConsoleCommand, ITransientDependency |
|||
{ |
|||
public ILogger<AddPackageCommand> Logger { get; set; } |
|||
|
|||
protected ProjectNugetPackageAdder ProjectNugetPackageAdder { get; } |
|||
|
|||
public AddPackageCommand(ProjectNugetPackageAdder projectNugetPackageAdder) |
|||
{ |
|||
ProjectNugetPackageAdder = projectNugetPackageAdder; |
|||
Logger = NullLogger<AddPackageCommand>.Instance; |
|||
} |
|||
|
|||
public virtual async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
|||
{ |
|||
if (commandLineArgs.Target == null) |
|||
{ |
|||
throw new CliUsageException("Package name is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo()); |
|||
} |
|||
|
|||
await ProjectNugetPackageAdder.AddAsync( |
|||
GetProjectFile(commandLineArgs), |
|||
commandLineArgs.Target |
|||
); |
|||
} |
|||
|
|||
protected virtual string GetProjectFile(CommandLineArgs commandLineArgs) |
|||
{ |
|||
var providedProjectFile = PathHelper.NormalizePath( |
|||
commandLineArgs.Options.GetOrNull( |
|||
Options.Project.Short, |
|||
Options.Project.Long |
|||
) |
|||
); |
|||
|
|||
if (!providedProjectFile.IsNullOrWhiteSpace()) |
|||
{ |
|||
return providedProjectFile; |
|||
} |
|||
|
|||
var foundProjectFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.csproj"); |
|||
if (foundProjectFiles.Length == 1) |
|||
{ |
|||
return foundProjectFiles[0]; |
|||
} |
|||
|
|||
if (foundProjectFiles.Length == 0) |
|||
{ |
|||
throw new CliUsageException("'abp add-package' command should be used inside a folder contaning a .csproj file!"); |
|||
} |
|||
|
|||
//foundProjectFiles.Length > 1
|
|||
|
|||
var sb = new StringBuilder("There are multiple project (.csproj) files in the current directory. Please specify one of the files below:"); |
|||
|
|||
foreach (var foundProjectFile in foundProjectFiles) |
|||
{ |
|||
sb.AppendLine("* " + foundProjectFile); |
|||
} |
|||
|
|||
sb.AppendLine("Example:"); |
|||
sb.AppendLine($"abp add-package {commandLineArgs.Target} -p {foundProjectFiles[0]}"); |
|||
|
|||
throw new CliUsageException(sb.ToString()); |
|||
} |
|||
|
|||
protected virtual string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("'add-package' command is used to add an ABP package to a project."); |
|||
sb.AppendLine("It should be used in a folder containing a .csproj file."); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Usage:"); |
|||
sb.AppendLine(" abp add-package <package-name> [-p|--project]"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Options:"); |
|||
sb.AppendLine(" -p|--project <project-file> Specify the project file explicitly."); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Examples:"); |
|||
sb.AppendLine(" abp add-package Volo.Abp.FluentValidation Adds the package to the current project."); |
|||
sb.AppendLine(" abp add-package Volo.Abp.FluentValidation -p Acme.BookStore.Application Adds the package to the given project."); |
|||
sb.AppendLine(""); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public static class Options |
|||
{ |
|||
public static class Project |
|||
{ |
|||
public const string Short = "p"; |
|||
public const string Long = "project"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
using Newtonsoft.Json; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.Cli.NuGet |
|||
{ |
|||
public class NuGetService : ISingletonDependency |
|||
{ |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
|||
|
|||
public NuGetService( |
|||
IJsonSerializer jsonSerializer, |
|||
ICancellationTokenProvider cancellationTokenProvider) |
|||
{ |
|||
JsonSerializer = jsonSerializer; |
|||
CancellationTokenProvider = cancellationTokenProvider; |
|||
} |
|||
|
|||
public async Task<string> GetLatestVersionOrNullAsync(string packageId, bool includePreviews = false) |
|||
{ |
|||
using (var client = new HttpClient()) |
|||
{ |
|||
client.Timeout = TimeSpan.FromSeconds(30); |
|||
|
|||
var responseMessage = await client.GetAsync( |
|||
$"https://api.nuget.org/v3-flatcontainer/{packageId.ToLowerInvariant()}/index.json", |
|||
CancellationTokenProvider.Token |
|||
); |
|||
|
|||
if (!responseMessage.IsSuccessStatusCode) |
|||
{ |
|||
throw new Exception("Remote server returns error! HTTP status code: " + responseMessage.StatusCode); |
|||
} |
|||
|
|||
var result = await responseMessage.Content.ReadAsStringAsync(); |
|||
|
|||
var versions = JsonSerializer.Deserialize<NuGetVersionResultDto>(result).Versions; |
|||
|
|||
if (!includePreviews) |
|||
{ |
|||
versions = versions |
|||
.Where(x => !x.Contains("beta") && !x.Contains("preview") && !x.Contains("alpha") && !x.Contains("rc")) |
|||
.ToList(); |
|||
} |
|||
|
|||
return versions.Count > 0 ? versions.Last() : null; |
|||
} |
|||
} |
|||
|
|||
public class NuGetVersionResultDto |
|||
{ |
|||
[JsonProperty("versions")] |
|||
public List<string> Versions { get; set; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,159 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.IO; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http; |
|||
using Volo.Abp.IO; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
public class AbpIoTemplateStore : ITemplateStore, ITransientDependency |
|||
{ |
|||
public ILogger<AbpIoTemplateStore> Logger { get; set; } |
|||
|
|||
protected CliOptions Options { get; } |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
|||
|
|||
public AbpIoTemplateStore( |
|||
IOptions<CliOptions> options, |
|||
IJsonSerializer jsonSerializer, |
|||
ICancellationTokenProvider cancellationTokenProvider) |
|||
{ |
|||
JsonSerializer = jsonSerializer; |
|||
CancellationTokenProvider = cancellationTokenProvider; |
|||
Options = options.Value; |
|||
|
|||
Logger = NullLogger<AbpIoTemplateStore>.Instance; |
|||
} |
|||
|
|||
public async Task<TemplateFile> GetAsync( |
|||
string name, |
|||
DatabaseProvider databaseProvider, |
|||
string projectName, |
|||
string version = null) |
|||
{ |
|||
if (version == null) |
|||
{ |
|||
version = await GetLatestTemplateVersionAsync(name); |
|||
} |
|||
|
|||
DirectoryHelper.CreateIfNotExists(CliPaths.TemplateCache); |
|||
|
|||
var localCacheFile = Path.Combine(CliPaths.TemplateCache, name + "-" + version + ".zip"); |
|||
if (File.Exists(localCacheFile)) |
|||
{ |
|||
Logger.LogInformation("Using cached template: " + name + ", version: " + version); |
|||
return new TemplateFile(File.ReadAllBytes(localCacheFile), version); |
|||
} |
|||
|
|||
Logger.LogInformation("Downloading template: " + name + ", version: " + version); |
|||
|
|||
var fileContent = await DownloadTemplateFileContentAsync( |
|||
new TemplateDownloadInputDto |
|||
{ |
|||
Name = name, |
|||
Version = version, |
|||
DatabaseProvider = databaseProvider.ToString(), |
|||
ProjectName = projectName |
|||
} |
|||
); |
|||
|
|||
File.WriteAllBytes(localCacheFile, fileContent); |
|||
|
|||
return new TemplateFile(fileContent, version); |
|||
} |
|||
|
|||
private async Task<string> GetLatestTemplateVersionAsync(string name) |
|||
{ |
|||
var postData = JsonSerializer.Serialize(new GetLatestTemplateVersionDto { Name = name }); |
|||
|
|||
using (var client = new HttpClient()) |
|||
{ |
|||
client.Timeout = TimeSpan.FromSeconds(30); |
|||
|
|||
AddAuthentication(client); |
|||
|
|||
var responseMessage = await client.PostAsync( |
|||
Options.AbpIoWwwUrlRoot + "api/download/template/get-version/", |
|||
new StringContent(postData, Encoding.UTF8, MimeTypes.Application.Json), |
|||
CancellationTokenProvider.Token |
|||
); |
|||
|
|||
if (!responseMessage.IsSuccessStatusCode) |
|||
{ |
|||
throw new Exception("Remote server returns error! HTTP status code: " + responseMessage.StatusCode); |
|||
} |
|||
|
|||
var result = await responseMessage.Content.ReadAsStringAsync(); |
|||
return JsonSerializer.Deserialize<GetLatestTemplateVersionResultDto>(result).Version; |
|||
} |
|||
} |
|||
|
|||
private async Task<byte[]> DownloadTemplateFileContentAsync(TemplateDownloadInputDto input) |
|||
{ |
|||
var postData = JsonSerializer.Serialize(input); |
|||
|
|||
using (var client = new HttpClient()) |
|||
{ |
|||
client.Timeout = TimeSpan.FromMinutes(3); |
|||
|
|||
AddAuthentication(client); |
|||
|
|||
var responseMessage = await client.PostAsync( |
|||
Options.AbpIoWwwUrlRoot + "api/download/template/", |
|||
new StringContent(postData, Encoding.UTF8, MimeTypes.Application.Json), |
|||
CancellationTokenProvider.Token |
|||
); |
|||
|
|||
if (!responseMessage.IsSuccessStatusCode) |
|||
{ |
|||
throw new Exception("Remote server returns error! HTTP status code: " + responseMessage.StatusCode); |
|||
} |
|||
|
|||
return await responseMessage.Content.ReadAsByteArrayAsync(); |
|||
} |
|||
} |
|||
|
|||
private static void AddAuthentication(HttpClient client) |
|||
{ |
|||
if (File.Exists(CliPaths.AccessToken)) |
|||
{ |
|||
var accessToken = File.ReadAllText(CliPaths.AccessToken, Encoding.UTF8); |
|||
if (!accessToken.IsNullOrEmpty()) |
|||
{ |
|||
client.SetBearerToken(accessToken); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public class TemplateDownloadInputDto |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string Version { get; set; } |
|||
|
|||
public string DatabaseProvider { get; set; } |
|||
|
|||
public string ProjectName { get; set; } |
|||
} |
|||
|
|||
public class GetLatestTemplateVersionDto |
|||
{ |
|||
public string Name { get; set; } |
|||
} |
|||
|
|||
public class GetLatestTemplateVersionResultDto |
|||
{ |
|||
public string Version { get; set; } |
|||
} |
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
namespace Volo.Abp.ProjectBuilding.Building |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building |
|||
{ |
|||
public enum DatabaseProvider |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Building |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building |
|||
{ |
|||
public class GithubRepositoryInfo |
|||
{ |
|||
@ -1,8 +1,8 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using Volo.Abp.ProjectBuilding.Files; |
|||
using Volo.Abp.Cli.ProjectBuilding.Files; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Building |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building |
|||
{ |
|||
public static class ProjectBuildContextExtensions |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Building |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building |
|||
{ |
|||
public class ProjectBuildPipeline |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using Volo.Abp.ProjectBuilding.Building.Steps; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building.Steps; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Building |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building |
|||
{ |
|||
public static class ProjectBuildPipelineBuilder |
|||
{ |
|||
@ -1,4 +1,4 @@ |
|||
namespace Volo.Abp.ProjectBuilding.Building |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building |
|||
{ |
|||
public abstract class ProjectBuildPipelineStep |
|||
{ |
|||
@ -1,4 +1,4 @@ |
|||
namespace Volo.Abp.ProjectBuilding.Building |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building |
|||
{ |
|||
public class ProjectResult |
|||
{ |
|||
@ -0,0 +1,35 @@ |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps |
|||
{ |
|||
public class ChangeLocalhostPortStep : ProjectBuildPipelineStep |
|||
{ |
|||
public string LaunchSettingsFilePath { get; } |
|||
public int[] PortNumbers { get; } |
|||
|
|||
public ChangeLocalhostPortStep( |
|||
string launchSettingsFilePath, |
|||
params int[] portNumbers) |
|||
{ |
|||
Check.NotNullOrEmpty(portNumbers, nameof(portNumbers)); |
|||
|
|||
LaunchSettingsFilePath = launchSettingsFilePath; |
|||
PortNumbers = portNumbers; |
|||
} |
|||
|
|||
public override void Execute(ProjectBuildContext context) |
|||
{ |
|||
var launchSettings = context.GetFile(LaunchSettingsFilePath); |
|||
var randomPort = RandomHelper.GetRandom(50001, 59990); |
|||
var newContent = launchSettings.Content; |
|||
|
|||
for (var i = 0; i < PortNumbers.Length; i++) |
|||
{ |
|||
newContent = newContent.Replace( |
|||
PortNumbers[i].ToString(), |
|||
(randomPort + i).ToString() |
|||
); |
|||
} |
|||
|
|||
launchSettings.SetContent(newContent); |
|||
} |
|||
} |
|||
} |
|||
@ -1,8 +1,8 @@ |
|||
using Ionic.Zip; |
|||
using Volo.Abp.ProjectBuilding.Files; |
|||
using Volo.Abp.ProjectBuilding.Zipping; |
|||
using Volo.Abp.Cli.ProjectBuilding.Files; |
|||
using Volo.Abp.Cli.ProjectBuilding.Zipping; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Building.Steps |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps |
|||
{ |
|||
public class CreateProjectResultZipStep : ProjectBuildPipelineStep |
|||
{ |
|||
@ -1,9 +1,9 @@ |
|||
using System.IO; |
|||
using Ionic.Zip; |
|||
using Volo.Abp.ProjectBuilding.Files; |
|||
using Volo.Abp.ProjectBuilding.Zipping; |
|||
using Volo.Abp.Cli.ProjectBuilding.Files; |
|||
using Volo.Abp.Cli.ProjectBuilding.Zipping; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Building.Steps |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps |
|||
{ |
|||
public class FileEntryListReadStep : ProjectBuildPipelineStep |
|||
{ |
|||
@ -1,9 +1,9 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Volo.Abp.ProjectBuilding.Files; |
|||
using Volo.Abp.Cli.ProjectBuilding.Files; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Building.Steps |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps |
|||
{ |
|||
public class SolutionRenameStep : ProjectBuildPipelineStep |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using Volo.Abp.ProjectBuilding.Files; |
|||
using Volo.Abp.Cli.ProjectBuilding.Files; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Building.Steps |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps |
|||
{ |
|||
public class TemplateCodeDeleteStep : ProjectBuildPipelineStep |
|||
{ |
|||
@ -1,9 +1,9 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.ComponentModel.DataAnnotations.Schema; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.ProjectBuilding.Building; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding |
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
[Table("Downloads")] |
|||
public class DownloadInfo : CreationAuditedAggregateRoot<int> |
|||
@ -1,6 +1,6 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Files |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Files |
|||
{ |
|||
public static class FileEntryExtensions |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Files |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Files |
|||
{ |
|||
public class FileEntryList : List<FileEntry> |
|||
{ |
|||
@ -1,7 +1,7 @@ |
|||
using System; |
|||
using Ionic.Zip; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Files |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Files |
|||
{ |
|||
public static class FileEntryListExtensions |
|||
{ |
|||
@ -1,7 +1,7 @@ |
|||
using System; |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Github |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Github |
|||
{ |
|||
[JsonObject] |
|||
public class GithubRelease |
|||
@ -1,6 +1,6 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding |
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
public interface IProjectBuilder |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using Volo.Abp.ProjectBuilding.Building; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding |
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
public interface ITemplateInfoProvider |
|||
{ |
|||
@ -0,0 +1,16 @@ |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
public interface ITemplateStore |
|||
{ |
|||
Task<TemplateFile> GetAsync( |
|||
string name, |
|||
DatabaseProvider databaseProvider, |
|||
string projectName, |
|||
[CanBeNull] string version = null |
|||
); |
|||
} |
|||
} |
|||
@ -1,7 +1,7 @@ |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.ProjectBuilding.Building; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding |
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
public class ProjectBuildArgs |
|||
{ |
|||
@ -1,4 +1,4 @@ |
|||
namespace Volo.Abp.ProjectBuilding |
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
public class ProjectBuildResult |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding |
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
public class SolutionName |
|||
{ |
|||
@ -0,0 +1,15 @@ |
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
public class TemplateFile |
|||
{ |
|||
public string Version { get; } |
|||
|
|||
public byte[] FileBytes { get; } |
|||
|
|||
public TemplateFile(byte[] fileBytes, string version) |
|||
{ |
|||
FileBytes = fileBytes; |
|||
Version = version; |
|||
} |
|||
} |
|||
} |
|||
@ -1,23 +1,23 @@ |
|||
using System; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
using Volo.Abp.Cli.ProjectBuilding.Templates; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.ProjectBuilding.Building; |
|||
using Volo.Abp.ProjectBuilding.Templates; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding |
|||
namespace Volo.Abp.Cli.ProjectBuilding |
|||
{ |
|||
public class TemplateInfoProvider : ITemplateInfoProvider, ITransientDependency |
|||
{ |
|||
public TemplateInfo GetDefault() |
|||
{ |
|||
return Get(MvcApplicationTemplate.TemplateName); |
|||
return Get(MvcTemplate.TemplateName); |
|||
} |
|||
|
|||
public TemplateInfo Get(string name) |
|||
{ |
|||
switch (name) |
|||
{ |
|||
case MvcApplicationTemplate.TemplateName: |
|||
return new MvcApplicationTemplate(); |
|||
case MvcTemplate.TemplateName: |
|||
return new MvcTemplate(); |
|||
case MvcModuleTemplate.TemplateName: |
|||
return new MvcModuleTemplate(); |
|||
case ServiceTemplate.TemplateName: |
|||
@ -1,6 +1,6 @@ |
|||
using Volo.Abp.ProjectBuilding.Building; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Templates |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Templates |
|||
{ |
|||
public class MvcModuleTemplate : TemplateInfo |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using Volo.Abp.ProjectBuilding.Building; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
|
|||
namespace Volo.Abp.ProjectBuilding.Templates |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Templates |
|||
{ |
|||
public class ServiceTemplate : TemplateInfo |
|||
{ |
|||
@ -0,0 +1,84 @@ |
|||
using System; |
|||
using System.IO; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public class DbContextFileBuilderConfigureAdder : ITransientDependency |
|||
{ |
|||
public ILogger<DbContextFileBuilderConfigureAdder> Logger { get; set; } |
|||
|
|||
protected UsingStatementAdder UsingStatementAdder { get; } |
|||
|
|||
public DbContextFileBuilderConfigureAdder(UsingStatementAdder usingStatementAdder) |
|||
{ |
|||
UsingStatementAdder = usingStatementAdder; |
|||
Logger = NullLogger<DbContextFileBuilderConfigureAdder>.Instance; |
|||
} |
|||
|
|||
public void Add(string path, string moduleConfiguration) |
|||
{ |
|||
var file = File.ReadAllText(path); |
|||
|
|||
file = UsingStatementAdder.Add(file, GetNamespace(moduleConfiguration)); |
|||
|
|||
var stringToAdd = GetLineToAdd(moduleConfiguration); |
|||
if (!file.Contains(stringToAdd)) |
|||
{ |
|||
var indexToInsert = FindIndexToInsert(file); |
|||
|
|||
if (indexToInsert <= 0 || indexToInsert >= file.Length) |
|||
{ |
|||
Logger.LogWarning($"\"OnModelCreating(ModelBuilder builder)\" method couldn't be found in {path}"); |
|||
return; |
|||
} |
|||
file = file.Insert(indexToInsert, " " + stringToAdd + Environment.NewLine + " "); |
|||
} |
|||
|
|||
|
|||
File.WriteAllText(path, file); |
|||
} |
|||
|
|||
protected int FindIndexToInsert(string file) |
|||
{ |
|||
var indexOfMethodDeclaration = file.IndexOf("OnModelCreating(", StringComparison.Ordinal); |
|||
var indexOfOpeningBracket = indexOfMethodDeclaration + file.Substring(indexOfMethodDeclaration).IndexOf('{'); |
|||
|
|||
var stack = 1; |
|||
var index = indexOfOpeningBracket; |
|||
|
|||
while (stack > 0) |
|||
{ |
|||
index++; |
|||
|
|||
if (index >= file.Length) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
if (file[index] == '{') |
|||
{ |
|||
stack++; |
|||
} |
|||
else if (file[index] == '}') |
|||
{ |
|||
stack--; |
|||
} |
|||
} |
|||
|
|||
return index; |
|||
} |
|||
|
|||
protected string GetLineToAdd(string moduleConfiguration) |
|||
{ |
|||
return "builder." + moduleConfiguration.Split(':')[1] + "();"; |
|||
} |
|||
|
|||
protected string GetNamespace(string moduleConfiguration) |
|||
{ |
|||
return string.Join(".", moduleConfiguration.Split(':')[0]); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using Microsoft.CodeAnalysis.CSharp; |
|||
using Microsoft.CodeAnalysis.CSharp.Syntax; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public class DerivedClassFinder : ITransientDependency |
|||
{ |
|||
public ILogger<DerivedClassFinder> Logger { get; set; } |
|||
|
|||
public DerivedClassFinder() |
|||
{ |
|||
Logger = NullLogger<DerivedClassFinder>.Instance; |
|||
} |
|||
|
|||
public virtual List<string> Find(string csprojFilePath, string baseClass) |
|||
{ |
|||
var moduleFilePaths = new List<string>(); |
|||
var csprojFileDirectory = Path.GetDirectoryName(csprojFilePath); |
|||
var binFile = Path.Combine(csprojFileDirectory, "bin"); |
|||
var objFile = Path.Combine(csprojFileDirectory, "obj"); |
|||
|
|||
|
|||
var csFiles = new DirectoryInfo(csprojFileDirectory) |
|||
.GetFiles("*.cs", SearchOption.AllDirectories) |
|||
.Where(f => !f.DirectoryName.StartsWith(binFile)) |
|||
.Where(f => !f.DirectoryName.StartsWith(objFile)) |
|||
.Select(f => f.FullName) |
|||
.ToList(); |
|||
|
|||
foreach (var csFile in csFiles) |
|||
{ |
|||
try |
|||
{ |
|||
if (IsDerived(csFile, baseClass)) |
|||
{ |
|||
moduleFilePaths.Add(csFile); |
|||
} |
|||
} |
|||
catch (Exception) |
|||
{ |
|||
Logger.LogDebug($"Couldn't parse {csFile}."); |
|||
} |
|||
} |
|||
|
|||
return moduleFilePaths; |
|||
} |
|||
|
|||
protected bool IsDerived(string csFile, string baseClass) |
|||
{ |
|||
Logger.LogDebug(csFile); |
|||
var root = CSharpSyntaxTree.ParseText(File.ReadAllText(csFile)).GetRoot(); |
|||
var namespaceSyntax = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>().First(); |
|||
var classDeclaration = (namespaceSyntax.DescendantNodes().OfType<ClassDeclarationSyntax>()).First(); |
|||
var baseTypeList = classDeclaration.BaseList?.Types.Select(t => t.ToString()).ToList(); |
|||
|
|||
if (baseTypeList == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
foreach (var baseType in baseTypeList) |
|||
{ |
|||
if (baseType.Contains('<') && baseType.Substring(0, baseType.IndexOf('<')) == baseClass) |
|||
{ |
|||
return true; |
|||
} |
|||
if (baseType == baseClass) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using Volo.Abp.Cli.Utils; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public class EfCoreMigrationAdder : ITransientDependency |
|||
{ |
|||
public void AddMigration(string csprojFile, string module, bool updateDatabase = true) |
|||
{ |
|||
var moduleName = ParseModuleName(module); |
|||
var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix(); |
|||
|
|||
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(csprojFile) + "\" & dotnet ef migrations add " + migrationName); |
|||
|
|||
if (updateDatabase) |
|||
{ |
|||
UpdateDatabase(csprojFile); |
|||
} |
|||
} |
|||
|
|||
protected void UpdateDatabase(string csprojFile) |
|||
{ |
|||
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(csprojFile) + "\" & dotnet ef database update"); |
|||
} |
|||
|
|||
protected virtual string ParseModuleName(string fullModuleName) |
|||
{ |
|||
var words = fullModuleName?.Split('.'); |
|||
|
|||
if (words == null || words.Length <= 1) |
|||
{ |
|||
return ""; |
|||
} |
|||
|
|||
return words[words.Length - 1]; |
|||
} |
|||
|
|||
protected virtual string GetUniquePostFix() |
|||
{ |
|||
return "_" + new Random().Next(1,99999); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public class ModuleClassDependcyAdder : ITransientDependency |
|||
{ |
|||
protected UsingStatementAdder UsingStatementAdder { get; } |
|||
|
|||
public ModuleClassDependcyAdder(UsingStatementAdder usingStatementAdder) |
|||
{ |
|||
UsingStatementAdder = usingStatementAdder; |
|||
} |
|||
|
|||
public virtual void Add(string path, string module) |
|||
{ |
|||
ParseModuleNameAndNameSpace(module, out var nameSpace, out var moduleName); |
|||
|
|||
var file = File.ReadAllText(path); |
|||
|
|||
file = UsingStatementAdder.Add(file, nameSpace); |
|||
|
|||
if (!file.Contains(moduleName) ) |
|||
{ |
|||
file = InsertDependsOnAttribute(file, moduleName); |
|||
} |
|||
|
|||
File.WriteAllText(path, file); |
|||
} |
|||
|
|||
protected virtual string InsertDependsOnAttribute(string file, string moduleName) |
|||
{ |
|||
var indexOfPublicClassDeclaration = GetIndexOfWhereDependsOnWillBeAdded(file); |
|||
var dependsOnAttribute = GetDependsOnAttribute(moduleName); |
|||
|
|||
return file.Insert(indexOfPublicClassDeclaration, dependsOnAttribute); |
|||
} |
|||
|
|||
protected virtual int GetIndexOfWhereDependsOnWillBeAdded(string file) |
|||
{ |
|||
var indexOfPublicClassDeclaration = file.IndexOf("public class", StringComparison.Ordinal); |
|||
|
|||
if (indexOfPublicClassDeclaration < 0) |
|||
{ |
|||
throw new Exception("\"public class\" declaration not found!"); |
|||
} |
|||
|
|||
return indexOfPublicClassDeclaration; |
|||
} |
|||
|
|||
protected virtual string GetDependsOnAttribute(string moduleName) |
|||
{ |
|||
return "[DependsOn(typeof(" + moduleName + "))]" + Environment.NewLine + " "; |
|||
} |
|||
|
|||
protected virtual string GetUsingStatement(string nameSpace) |
|||
{ |
|||
return "using " + nameSpace + ";"; |
|||
} |
|||
|
|||
protected virtual void ParseModuleNameAndNameSpace(string module, out string nameSpace, out string moduleName) |
|||
{ |
|||
var words = module?.Split('.'); |
|||
|
|||
if (words == null || words.Length <= 1) |
|||
{ |
|||
nameSpace = null; |
|||
moduleName = module; |
|||
return; |
|||
} |
|||
|
|||
moduleName = words[words.Length - 1]; |
|||
nameSpace = string.Join(".", words.Take(words.Length - 1)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public class ModuleInfo |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string DisplayName { get; set; } |
|||
|
|||
public string EfCoreConfigureMethodName { get; set; } |
|||
|
|||
public List<NugetPackageInfo> NugetPackages { get; set; } |
|||
|
|||
public List<NpmPackageInfo> NpmPackages { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
[Flags] |
|||
public enum NpmApplicationType |
|||
{ |
|||
Mvc = 1 |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public class NpmPackageInfo |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public NpmApplicationType ApplicationType { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public class NugetPackageInfo |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string ModuleClass { get; set; } |
|||
|
|||
public NuGetPackageTarget Target { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public enum NuGetPackageTarget : byte |
|||
{ |
|||
DomainShared = 1, |
|||
Domain = 2, |
|||
ApplicationContracts = 3, |
|||
Application = 4, |
|||
HttpApi = 5, |
|||
HttpApiClient = 6, |
|||
Web = 7, |
|||
EntityFrameworkCore = 8, |
|||
MongoDB = 9 |
|||
} |
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public static class ProjectFinder |
|||
{ |
|||
public static string FindNuGetTargetProjectFile(string[] projectFiles, NuGetPackageTarget target) |
|||
{ |
|||
if (!projectFiles.Any()) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var assemblyNames = GetAssemblyNames(projectFiles); |
|||
|
|||
switch (target) |
|||
{ |
|||
case NuGetPackageTarget.Web: |
|||
return FindProjectEndsWith(projectFiles, assemblyNames, ".Web"); |
|||
case NuGetPackageTarget.EntityFrameworkCore: |
|||
return FindProjectEndsWith(projectFiles, assemblyNames, ".EntityFrameworkCore"); |
|||
case NuGetPackageTarget.MongoDB: |
|||
return FindProjectEndsWith(projectFiles, assemblyNames, ".MongoDB"); |
|||
case NuGetPackageTarget.Application: |
|||
return FindProjectEndsWith(projectFiles, assemblyNames, ".Application") ?? |
|||
FindProjectEndsWith(projectFiles, assemblyNames, ".Web"); |
|||
case NuGetPackageTarget.ApplicationContracts: |
|||
return FindProjectEndsWith(projectFiles, assemblyNames, ".Application.Contracts"); |
|||
case NuGetPackageTarget.Domain: |
|||
return FindProjectEndsWith(projectFiles, assemblyNames, ".Domain") ?? |
|||
FindProjectEndsWith(projectFiles, assemblyNames, ".Application") ?? |
|||
FindProjectEndsWith(projectFiles, assemblyNames, ".Web"); |
|||
case NuGetPackageTarget.DomainShared: |
|||
return FindProjectEndsWith(projectFiles, assemblyNames, ".Domain.Shared"); |
|||
case NuGetPackageTarget.HttpApi: |
|||
return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi"); |
|||
case NuGetPackageTarget.HttpApiClient: |
|||
return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Client"); |
|||
default: |
|||
throw new ApplicationException($"{nameof(NuGetPackageTarget)}.{target} has not implemented!"); |
|||
} |
|||
} |
|||
|
|||
public static string[] FindNpmTargetProjectFile(string[] projectFiles) |
|||
{ |
|||
if (!projectFiles.Any()) |
|||
{ |
|||
return Array.Empty<string>(); |
|||
} |
|||
|
|||
var assemblyNames = GetAssemblyNames(projectFiles); |
|||
|
|||
var projects = new List<string>(); |
|||
|
|||
var project = FindProjectEndsWith(projectFiles, assemblyNames, ".Web.Host"); |
|||
if (project != null) |
|||
{ |
|||
projects.Add(project); |
|||
} |
|||
|
|||
project = FindProjectEndsWith(projectFiles, assemblyNames, ".Host", ".HttpApi.Host"); |
|||
if (project != null) |
|||
{ |
|||
projects.Add(project); |
|||
} |
|||
|
|||
if (projects.Any()) |
|||
{ |
|||
return projects.ToArray(); |
|||
} |
|||
|
|||
project = FindProjectEndsWith(projectFiles, assemblyNames, ".Web"); |
|||
if (project != null) |
|||
{ |
|||
projects.Add(project); |
|||
} |
|||
|
|||
return projects.ToArray(); |
|||
} |
|||
|
|||
public static string[] GetProjectFiles(string solutionFile) |
|||
{ |
|||
var baseProjectFolder = GetBaseProjectFolder(solutionFile); |
|||
return Directory.GetFiles(baseProjectFolder, "*.csproj", SearchOption.AllDirectories); |
|||
} |
|||
|
|||
public static string[] GetAssemblyNames(string[] projectFiles) |
|||
{ |
|||
return projectFiles.Select(GetAssemblyName).ToArray(); |
|||
} |
|||
|
|||
public static string GetAssemblyName(string projectFile) |
|||
{ |
|||
return projectFile |
|||
.Substring(projectFile.LastIndexOf(Path.DirectorySeparatorChar) + 1) |
|||
.RemovePostFix(StringComparison.OrdinalIgnoreCase, ".csproj"); |
|||
} |
|||
|
|||
private static string FindProjectEndsWith( |
|||
string[] projectFiles, |
|||
string[] assemblyNames, |
|||
string postfix, |
|||
string excludePostfix = null) |
|||
{ |
|||
for (var i = 0; i < assemblyNames.Length; i++) |
|||
{ |
|||
var assemblyName = assemblyNames[i]; |
|||
if(assemblyName.EndsWith(postfix, StringComparison.OrdinalIgnoreCase) && |
|||
(excludePostfix == null || !assemblyName.EndsWith(excludePostfix))) |
|||
{ |
|||
return projectFiles[i]; |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
private static string GetBaseProjectFolder(string solutionFile) |
|||
{ |
|||
var baseFolder = Path.GetDirectoryName(solutionFile); |
|||
var srcFolder = Path.Combine(baseFolder, "src"); |
|||
if (Directory.Exists(srcFolder)) |
|||
{ |
|||
baseFolder = srcFolder; |
|||
} |
|||
|
|||
return baseFolder; |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue