mirror of https://github.com/abpframework/abp.git
122 changed files with 2841 additions and 1174 deletions
@ -0,0 +1,188 @@ |
|||
# Cards |
|||
|
|||
## Introduction |
|||
|
|||
`abp-card` is a content container derived from bootstrap card element. |
|||
|
|||
Basic usage: |
|||
|
|||
````xml |
|||
<abp-card style="width: 18rem;"> |
|||
<img abp-card-image="Top" src="~/imgs/demo/300x200.png"/> |
|||
<abp-card-body> |
|||
<abp-card-title>Card Title</abp-card-title> |
|||
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text> |
|||
<a abp-button="Primary" href="#"> Go somewhere</a> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
```` |
|||
|
|||
|
|||
|
|||
##### Using Titles, Text and Links: |
|||
|
|||
Following tags can be used under main `abp-card` tag |
|||
|
|||
* `abp-card-title` |
|||
* `abp-card-subtitle` |
|||
* `a abp-card-link` |
|||
|
|||
Sample: |
|||
|
|||
````xml |
|||
<abp-card style="width: 18rem;"> |
|||
<abp-card-body> |
|||
<abp-card-title>Card title</abp-card-title> |
|||
<abp-card-subtitle class="mb-2 text-muted">Card subtitle</abp-card-subtitle> |
|||
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text> |
|||
<a abp-card-link href="#">Card link</a> |
|||
<a abp-card-link href="#">Another link</a> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
```` |
|||
|
|||
|
|||
|
|||
##### Using List Groups: |
|||
|
|||
* `abp-list-group flush="true"` : `flush` attribute renders into bootstrap `list-group-flush` class which is used for removing borders and rounded corners to render list group items edge to edge in a parent container. |
|||
* `abp-list-group-item` |
|||
|
|||
Kitchen Sink Sample: |
|||
|
|||
````xml |
|||
<abp-card style="width: 18rem;"> |
|||
<img abp-card-image="Top" src="~/imgs/demo/300x200.png" /> |
|||
<abp-card-body> |
|||
<abp-card-title>Card Title</abp-card-title> |
|||
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text> |
|||
</abp-card-body> |
|||
<abp-list-group flush="true"> |
|||
<abp-list-group-item>Cras justo odio</abp-list-group-item> |
|||
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item> |
|||
<abp-list-group-item>Vestibulum at eros</abp-list-group-item> |
|||
</abp-list-group> |
|||
<abp-card-body> |
|||
<a abp-card-link href="#">Card link</a> |
|||
<a abp-card-link href="#">Another link</a> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
```` |
|||
|
|||
|
|||
|
|||
##### Using Header, Footer and Blockquote: |
|||
|
|||
* `abp-card-header` |
|||
* `abp-card-footer` |
|||
* `abp-blockquote` |
|||
|
|||
Sample: |
|||
|
|||
```xml |
|||
<abp-card style="width: 18rem;"> |
|||
<abp-card-header>Featured</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-card-title> Special title treatment</abp-card-title> |
|||
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text> |
|||
<a abp-button="Primary" href="#"> Go somewhere</a> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
``` |
|||
|
|||
Quote Sample: |
|||
|
|||
```xml |
|||
<abp-card> |
|||
<abp-card-header>Quote</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-blockquote> |
|||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> |
|||
<footer>Someone famous in Source Title</footer> |
|||
</abp-blockquote> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
``` |
|||
|
|||
Footer Sample: |
|||
|
|||
```xml |
|||
<abp-card class="text-center"> |
|||
<abp-card-header>Featured</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-blockquote> |
|||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> |
|||
<footer>Someone famous in Source Title</footer> |
|||
</abp-blockquote> |
|||
</abp-card-body> |
|||
<abp-card-footer class="text-muted"> 2 days ago</abp-card-footer> |
|||
</abp-card> |
|||
``` |
|||
|
|||
|
|||
|
|||
## Demo |
|||
|
|||
See the [cards demo page](https://bootstrap-taghelpers.abp.io/Components/Cards) to see it in action. |
|||
|
|||
## abp-card Attributes |
|||
|
|||
- **background:** A value indicates the background color of the card. |
|||
- **text-color**: A value indicates the color of the text inside the card. |
|||
- **border:** A value indicates the color of the border inside the card. |
|||
|
|||
Should be one of the following values: |
|||
|
|||
* `Default` (default value) |
|||
* `Primary` |
|||
* `Secondary` |
|||
* `Success` |
|||
* `Danger` |
|||
* `Warning` |
|||
* `Info` |
|||
* `Light` |
|||
* `Dark` |
|||
|
|||
Example: |
|||
|
|||
````xml |
|||
<abp-card background="Success" text-color="Danger" border="Dark"> |
|||
```` |
|||
|
|||
### sizing |
|||
|
|||
Cards has default 100% with and can be changed with custom CSS, grid classes, grid Sass mixins or [utilities](https://getbootstrap.com/docs/4.0/utilities/sizing/). |
|||
|
|||
````xml |
|||
<abp-card style="width: 18rem;"> |
|||
```` |
|||
|
|||
### card-deck and card-columns |
|||
|
|||
`abp-card` can be used inside `card-deck` or `card-columns` aswell. |
|||
|
|||
````xml |
|||
<div class="card-deck"> |
|||
<abp-card background="Primary"> |
|||
<abp-card-header>First Deck</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-card-title> Ace </abp-card-title> |
|||
<abp-card-text>Here is the content for Ace.</abp-card-text> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
<abp-card background="Info"> |
|||
<abp-card-header>Second Deck</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-card-title> Beta </abp-card-title> |
|||
<abp-card-text>Beta content.</abp-card-text> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
<abp-card background="Warning"> |
|||
<abp-card-header>Third Deck</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-card-title> Epsilon </abp-card-title> |
|||
<abp-card-text>Content for Epsilon.</abp-card-text> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
</div> |
|||
```` |
|||
@ -0,0 +1,731 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Newtonsoft.Json.Linq; |
|||
using Volo.Abp.Cli.Args; |
|||
using Volo.Abp.Cli.ProjectBuilding; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
using Volo.Abp.DependencyInjection; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class GenerateProxyCommand : IConsoleCommand, ITransientDependency |
|||
{ |
|||
public static Dictionary<string, Dictionary<string, string>> propertyList = new Dictionary<string, Dictionary<string, string>>(); |
|||
public ILogger<GenerateProxyCommand> Logger { get; set; } |
|||
|
|||
protected TemplateProjectBuilder TemplateProjectBuilder { get; } |
|||
|
|||
public GenerateProxyCommand(TemplateProjectBuilder templateProjectBuilder) |
|||
{ |
|||
TemplateProjectBuilder = templateProjectBuilder; |
|||
|
|||
Logger = NullLogger<GenerateProxyCommand>.Instance; |
|||
} |
|||
|
|||
public async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
|||
{ |
|||
var angularPath = $"angular.json"; |
|||
if (!File.Exists(angularPath)) |
|||
{ |
|||
throw new CliUsageException( |
|||
"angular.json file not found. You must run this command in angular folder." + |
|||
Environment.NewLine + Environment.NewLine + |
|||
GetUsageInfo() |
|||
); |
|||
} |
|||
|
|||
var module = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.Module.Long); |
|||
module = module == null ? "app" : module.ToLower(); |
|||
|
|||
var apiUrl = commandLineArgs.Options.GetOrNull(Options.ApiUrl.Short, Options.ApiUrl.Long); |
|||
if (string.IsNullOrWhiteSpace(apiUrl)) |
|||
{ |
|||
var environmentJson = File.ReadAllText("src/environments/environment.ts").Split("export const environment = ")[1].Replace(";", " "); |
|||
var environment = JObject.Parse(environmentJson); |
|||
apiUrl = environment["apis"]["default"]["url"].ToString(); |
|||
} |
|||
apiUrl += "/api/abp/api-definition?IncludeTypes=true"; |
|||
|
|||
var uiFramework = GetUiFramework(commandLineArgs); |
|||
|
|||
WebClient client = new WebClient(); |
|||
string json = client.DownloadString(apiUrl); |
|||
//var sr = File.OpenText("api-definition.json");
|
|||
//var json = sr.ReadToEnd();
|
|||
|
|||
Logger.LogInformation("Downloading api definition..."); |
|||
Logger.LogInformation("Api Url: " + apiUrl); |
|||
|
|||
var data = JObject.Parse(json); |
|||
|
|||
Logger.LogInformation("Modules are combining"); |
|||
var moduleList = GetCombinedModules(data, module); |
|||
|
|||
if (moduleList.Count < 1) |
|||
{ |
|||
throw new CliUsageException( |
|||
"Module can not find!" + |
|||
Environment.NewLine + Environment.NewLine + |
|||
GetUsageInfo() |
|||
); |
|||
} |
|||
|
|||
Logger.LogInformation("Modules and types are creating"); |
|||
|
|||
foreach (var moduleItem in moduleList) |
|||
{ |
|||
var moduleValue = JObject.Parse(moduleItem.Value); |
|||
|
|||
var rootPath = moduleItem.Key; |
|||
|
|||
Logger.LogInformation($"{rootPath} directory is creating"); |
|||
|
|||
Directory.CreateDirectory($"src/app/{rootPath}/shared/models"); |
|||
Directory.CreateDirectory($"src/app/{rootPath}/shared/services"); |
|||
|
|||
var serviceIndexList = new List<string>(); |
|||
var modelIndexList = new List<string>(); |
|||
|
|||
foreach (var controller in moduleValue.Root.ToList().Select(item => item.First)) |
|||
{ |
|||
var serviceFileText = new StringBuilder(); |
|||
|
|||
serviceFileText.AppendLine("[firstTypeList]"); |
|||
serviceFileText.AppendLine("import { Injectable } from '@angular/core';"); |
|||
serviceFileText.AppendLine("import { Observable } from 'rxjs';"); |
|||
serviceFileText.AppendLine("[secondTypeList]"); |
|||
serviceFileText.AppendLine(""); |
|||
serviceFileText.AppendLine("@Injectable({providedIn: 'root'})"); |
|||
serviceFileText.AppendLine("export class [controllerName]Service {"); |
|||
serviceFileText.AppendLine(" constructor(private restService: RestService) {}"); |
|||
serviceFileText.AppendLine(""); |
|||
|
|||
var firstTypeList = new List<string>(); |
|||
var secondTypeList = new List<string>(); |
|||
|
|||
var controllerName = (string)controller["controllerName"]; |
|||
var controllerServiceName = controllerName.PascalToKebabCase() + ".service.ts"; |
|||
|
|||
foreach (var actionItem in controller["actions"]) |
|||
{ |
|||
var action = actionItem.First; |
|||
var actionName = (string)action["uniqueName"]; |
|||
|
|||
actionName = (char.ToLower(actionName[0]) + actionName.Substring(1)).Replace("Async", "").Replace("Controller", ""); |
|||
|
|||
var returnValueType = (string)action["returnValue"]["type"]; |
|||
|
|||
var parameters = action["parameters"]; |
|||
var parametersText = new StringBuilder(); |
|||
var parametersIndex = 0; |
|||
var bodyExtra = ""; |
|||
var modelBindingExtra = ""; |
|||
var modelBindingExtraList = new List<string>(); |
|||
var parameterModel = new List<ParameterModel>(); |
|||
|
|||
foreach (var parameter in parameters.OrderBy(p => p["bindingSourceId"])) |
|||
{ |
|||
var bindingSourceId = (string)parameter["bindingSourceId"]; |
|||
bindingSourceId = char.ToLower(bindingSourceId[0]) + bindingSourceId.Substring(1); |
|||
|
|||
var name = (string)parameter["name"]; |
|||
var typeSimple = (string)parameter["typeSimple"]; |
|||
var typeArray = ((string)parameter["type"]).Split("."); |
|||
var type = (typeArray[typeArray.Length - 1]).TrimEnd('>'); |
|||
var isOptional = (bool)parameter["isOptional"]; |
|||
var defaultValue = (string)parameter["defaultValue"]; |
|||
|
|||
var modelIndex = CreateType(data, (string)parameter["type"], rootPath, modelIndexList); |
|||
|
|||
if (!string.IsNullOrWhiteSpace(modelIndex)) |
|||
{ |
|||
modelIndexList.Add(modelIndex); |
|||
} |
|||
|
|||
if (bindingSourceId == "body") |
|||
{ |
|||
bodyExtra = ", body"; |
|||
parameterModel = AddParameter(bindingSourceId, type, isOptional, defaultValue, bindingSourceId, parameterModel); |
|||
} |
|||
else if (bindingSourceId == "path") |
|||
{ |
|||
parameterModel = AddParameter(name, typeSimple, isOptional, defaultValue, bindingSourceId, parameterModel); |
|||
} |
|||
else if (bindingSourceId == "modelBinding") |
|||
{ |
|||
var parameterNameOnMethod = (string)parameter["nameOnMethod"]; |
|||
|
|||
var parametersOnMethod = action["parametersOnMethod"]; |
|||
foreach (var parameterOnMethod in parametersOnMethod) |
|||
{ |
|||
var parametersOnMethodName = (string)parameterOnMethod["name"]; |
|||
if (parametersOnMethodName == parameterNameOnMethod) |
|||
{ |
|||
typeSimple = (string)parameterOnMethod["typeSimple"]; |
|||
typeArray = ((string)parameterOnMethod["type"]).Split("."); |
|||
type = typeArray[typeArray.Length - 1]; |
|||
isOptional = (bool)parameterOnMethod["isOptional"]; |
|||
defaultValue = (string)parameterOnMethod["defaultValue"]; |
|||
|
|||
if (typeSimple == "string" || typeSimple == "boolean" || typeSimple == "number") |
|||
{ |
|||
parameterModel = AddParameter(name, typeSimple, isOptional, defaultValue, bindingSourceId, parameterModel); |
|||
} |
|||
|
|||
modelIndex = CreateType(data, (string)parameterOnMethod["type"], rootPath, modelIndexList); |
|||
|
|||
if (!string.IsNullOrWhiteSpace(modelIndex)) |
|||
{ |
|||
modelIndexList.Add(modelIndex); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (typeSimple != "string" && typeSimple != "boolean" && typeSimple != "number") |
|||
{ |
|||
parametersText.Append($"params = {{}} as {type}"); |
|||
modelBindingExtra = ", params"; |
|||
if (!string.IsNullOrWhiteSpace(modelIndex)) |
|||
{ |
|||
secondTypeList.Add(type); |
|||
} |
|||
else { |
|||
firstTypeList.Add(type); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (parameterModel != null && parameterModel.Count > 0) |
|||
{ |
|||
foreach (var parameterItem in parameterModel.OrderBy(p => p.DisplayOrder)) |
|||
{ |
|||
var parameterItemModelName = parameterItem.Type.PascalToKebabCase() + ".ts"; |
|||
var parameterItemModelPath = $"src/app/{rootPath}/shared/models/{parameterItemModelName}"; |
|||
if (parameterItem.BindingSourceId == "body" && !File.Exists(parameterItemModelPath)) |
|||
{ |
|||
parameterItem.Type = "any"; |
|||
} |
|||
|
|||
parametersIndex++; |
|||
|
|||
if (parametersIndex > 1) |
|||
{ |
|||
parametersText.Append(", "); |
|||
} |
|||
|
|||
parametersText.Append(parameterItem.Name + (parameterItem.IsOptional ? "?" : "") + ": " + parameterItem.Type + (parameterItem.Value != null ? (" = " + (string.IsNullOrWhiteSpace(parameterItem.Value) ? "''" : parameterItem.Value)) : "")); |
|||
|
|||
if (parameterItem.BindingSourceId == "modelBinding") |
|||
{ |
|||
modelBindingExtraList.Add(parameterItem.Name); |
|||
} |
|||
else if (parameterItem.BindingSourceId == "body" && File.Exists(parameterItemModelPath)) |
|||
{ |
|||
secondTypeList.Add(parameterItem.Type); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (returnValueType != null) |
|||
{ |
|||
if (returnValueType.IndexOf('<') > -1) |
|||
{ |
|||
var firstTypeArray = returnValueType.Split("<")[0].Split("."); |
|||
var firstType = firstTypeArray[firstTypeArray.Length - 1]; |
|||
|
|||
var secondTypeArray = returnValueType.Split("<")[1].Split("."); |
|||
var secondType = secondTypeArray[secondTypeArray.Length - 1].TrimEnd('>'); |
|||
|
|||
var secondTypeModelName = secondType.PascalToKebabCase() + ".ts"; |
|||
var secondTypeModelPath = $"src/app/{rootPath}/shared/models/{secondTypeModelName}"; |
|||
if (firstType == "List" && !File.Exists(secondTypeModelPath)) |
|||
{ |
|||
secondType = "any"; |
|||
} |
|||
|
|||
serviceFileText.AppendLine( |
|||
firstType == "List" |
|||
? $" {actionName}({parametersText}): Observable<{secondType}[]> {{" |
|||
: $" {actionName}({parametersText}): Observable<{firstType}<{secondType}>> {{"); |
|||
|
|||
if (firstType != "List") |
|||
{ |
|||
firstTypeList.Add(firstType); |
|||
} |
|||
|
|||
if (secondType != "any") |
|||
{ |
|||
secondTypeList.Add(secondType); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
var typeArray = returnValueType.Split("."); |
|||
var type = typeArray[typeArray.Length - 1].TrimEnd('>'); |
|||
|
|||
type = type switch |
|||
{ |
|||
"Void" => "void", |
|||
"String" => "string", |
|||
"IActionResult" => "void", |
|||
"ActionResult" => "void", |
|||
_ => type |
|||
}; |
|||
|
|||
serviceFileText.AppendLine( |
|||
$" {actionName}({parametersText}): Observable<{type}> {{"); |
|||
|
|||
if (type != "void" && type != "string") |
|||
{ |
|||
secondTypeList.Add(type); |
|||
} |
|||
} |
|||
|
|||
var modelIndex = CreateType(data, returnValueType, rootPath, modelIndexList); |
|||
|
|||
if (!string.IsNullOrWhiteSpace(modelIndex)) |
|||
{ |
|||
modelIndexList.Add(modelIndex); |
|||
} |
|||
} |
|||
|
|||
if (modelBindingExtraList != null && modelBindingExtraList.Count > 0) |
|||
{ |
|||
modelBindingExtra = ", params: { " + string.Join(", ", modelBindingExtraList.ToArray()) + " }"; |
|||
} |
|||
|
|||
var url = ((string)action["url"]).Replace("/{", "/${"); |
|||
var httpMethod = (string)action["httpMethod"]; |
|||
|
|||
serviceFileText.AppendLine( |
|||
url.Contains("${") |
|||
? $" return this.restService.request({{ url: `/{url}`, method: '{httpMethod}'{bodyExtra}{modelBindingExtra} }});" |
|||
: $" return this.restService.request({{ url: '/{url}', method: '{httpMethod}'{bodyExtra}{modelBindingExtra} }});"); |
|||
|
|||
|
|||
serviceFileText.AppendLine(" }"); |
|||
} |
|||
|
|||
serviceIndexList.Add(controllerServiceName.Replace(".ts", "")); |
|||
|
|||
if (firstTypeList != null && firstTypeList.Count > 0) |
|||
{ |
|||
var firstTypeListDistinct = ", " + string.Join(", ", firstTypeList.Where(p => p != "void").Distinct().ToArray()); |
|||
serviceFileText.Replace("[firstTypeList]", |
|||
$"import {{ RestService {firstTypeListDistinct}}} from '@abp/ng.core';"); |
|||
} |
|||
else |
|||
{ |
|||
serviceFileText.Replace("[firstTypeList]", |
|||
$"import {{ RestService }} from '@abp/ng.core';"); |
|||
} |
|||
|
|||
if (secondTypeList != null && secondTypeList.Count > 0) |
|||
{ |
|||
var secondTypeListDistinct = string.Join(", ", secondTypeList.Where(p => p != "void").Distinct().ToArray()); |
|||
serviceFileText.Replace("[secondTypeList]", |
|||
$"import {{{secondTypeListDistinct}}} from '../models';"); |
|||
} |
|||
else |
|||
{ |
|||
serviceFileText.Replace("[secondTypeList]", ""); |
|||
} |
|||
|
|||
serviceFileText.AppendLine("}"); |
|||
|
|||
serviceFileText.Replace("[controllerName]", controllerName); |
|||
File.WriteAllText($"src/app/{rootPath}/shared/services/{controllerServiceName}", serviceFileText.ToString()); |
|||
} |
|||
|
|||
var serviceIndexFileText = new StringBuilder(); |
|||
|
|||
foreach (var serviceIndexItem in serviceIndexList.Distinct()) |
|||
{ |
|||
serviceIndexFileText.AppendLine($"export * from './{serviceIndexItem}';"); |
|||
} |
|||
|
|||
File.WriteAllText($"src/app/{rootPath}/shared/services/index.ts", serviceIndexFileText.ToString()); |
|||
|
|||
var modelIndexFileText = new StringBuilder(); |
|||
|
|||
foreach (var modelIndexItem in modelIndexList.Distinct()) |
|||
{ |
|||
modelIndexFileText.AppendLine($"export * from './{modelIndexItem}';"); |
|||
} |
|||
|
|||
File.WriteAllText($"src/app/{rootPath}/shared/models/index.ts", modelIndexFileText.ToString()); |
|||
} |
|||
|
|||
Logger.LogInformation("Completed!"); |
|||
} |
|||
|
|||
private Dictionary<string, string> GetCombinedModules(JToken data, string module) |
|||
{ |
|||
var moduleList = new Dictionary<string, string>(); |
|||
|
|||
foreach (var moduleItem in data["modules"]) |
|||
{ |
|||
var rootPath = ((string)moduleItem.First["rootPath"]).ToLower(); |
|||
|
|||
if (moduleList.Any(p => p.Key == rootPath)) |
|||
{ |
|||
var value = moduleList[rootPath]; |
|||
|
|||
moduleList[rootPath] = value.TrimEnd('}') + "," + moduleItem.First["controllers"].ToString().TrimStart('{'); |
|||
} |
|||
else |
|||
{ |
|||
moduleList.Add(rootPath, moduleItem.First["controllers"].ToString()); |
|||
} |
|||
} |
|||
|
|||
if (module != "all") |
|||
{ |
|||
moduleList = moduleList.Where(p => p.Key.ToLower() == module).ToDictionary(p => p.Key, s => s.Value); |
|||
} |
|||
|
|||
return moduleList; |
|||
} |
|||
|
|||
private static string CreateType(JObject data, string returnValueType, string rootPath, List<string> modelIndexList) |
|||
{ |
|||
var type = data["types"][returnValueType]; |
|||
|
|||
if (type == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
if (returnValueType.StartsWith("Volo.Abp.Application.Dtos") |
|||
|| returnValueType.StartsWith("System.Collections") |
|||
|| returnValueType == "System.String" |
|||
|| returnValueType == "System.Void" |
|||
|| returnValueType.Contains("System.Net.HttpStatusCode?") |
|||
|| returnValueType.Contains("IActionResult") |
|||
|| returnValueType.Contains("ActionResult") |
|||
|| returnValueType.Contains("IStringValueType") |
|||
|| returnValueType.Contains("IValueValidator") |
|||
) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var typeNameSplit = returnValueType.Split("."); |
|||
var typeName = typeNameSplit[typeNameSplit.Length - 1]; |
|||
|
|||
if (typeName.Contains("HttpStatusCode")) |
|||
{ |
|||
|
|||
} |
|||
|
|||
var typeModelName = typeName.Replace("<", "").Replace(">", "").Replace("?","").PascalToKebabCase() + ".ts"; |
|||
|
|||
var path = $"src/app/{rootPath}/shared/models/{typeModelName}"; |
|||
|
|||
var modelFileText = new StringBuilder(); |
|||
|
|||
var baseType = (string)type["baseType"]; |
|||
var extends = ""; |
|||
var customBaseTypeName = ""; |
|||
var baseTypeName = ""; |
|||
|
|||
if (!string.IsNullOrWhiteSpace(baseType)) |
|||
{ |
|||
var baseTypeSplit = baseType.Split("."); |
|||
baseTypeName = baseTypeSplit[baseTypeSplit.Length - 1].Replace("<", "").Replace(">", ""); |
|||
var baseTypeKebabCase = "./" + baseTypeName.PascalToKebabCase(); |
|||
|
|||
if (baseType != "System.Enum") |
|||
{ |
|||
if (baseType.Contains("Volo.Abp.Application.Dtos")) |
|||
{ |
|||
baseTypeKebabCase = "@abp/ng.core"; |
|||
|
|||
baseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].Split("<")[0].TrimStart('.'); |
|||
customBaseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].Replace("System.Guid", "string").TrimStart('.'); |
|||
} |
|||
|
|||
if (baseTypeName.Contains("guid") || baseTypeName.Contains("Guid")) |
|||
{ |
|||
baseTypeName = "string"; |
|||
} |
|||
|
|||
modelFileText.AppendLine($"import {{ {baseTypeName} }} from '{baseTypeKebabCase}';"); |
|||
extends = "extends " + (!string.IsNullOrWhiteSpace(customBaseTypeName) ? customBaseTypeName : baseTypeName); |
|||
|
|||
var modelIndex = CreateType(data, baseType, rootPath, modelIndexList); |
|||
if (!string.IsNullOrWhiteSpace(modelIndex)) |
|||
{ |
|||
modelIndexList.Add(modelIndex); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (baseType == "System.Enum" && (bool)type["isEnum"]) |
|||
{ |
|||
modelFileText.AppendLine($"export enum {typeName} {{"); |
|||
|
|||
var enumNameList = type["enumNames"].ToArray(); |
|||
var enumValueList = type["enumValues"].ToArray(); |
|||
|
|||
for (var i = 0; i < enumNameList.Length; i++) |
|||
{ |
|||
modelFileText.AppendLine($" {enumNameList[i]} = {enumValueList[i]},"); |
|||
} |
|||
|
|||
modelFileText.AppendLine("}"); |
|||
} |
|||
else |
|||
{ |
|||
modelFileText.AppendLine(""); |
|||
modelFileText.AppendLine($"export class {typeName} {extends} {{"); |
|||
|
|||
foreach (var property in type["properties"]) |
|||
{ |
|||
var propertyName = (string)property["name"]; |
|||
propertyName = (char.ToLower(propertyName[0]) + propertyName.Substring(1)); |
|||
var typeSimple = (string)property["typeSimple"]; |
|||
|
|||
var modelIndex = CreateType(data, (string)property["type"], rootPath, modelIndexList); |
|||
|
|||
if (typeSimple.IndexOf("[") > -1 && typeSimple.IndexOf("]") > -1) |
|||
{ |
|||
typeSimple = typeSimple.Replace("[", "").Replace("]", "") + "[]"; |
|||
} |
|||
|
|||
if (typeSimple.StartsWith("Volo.Abp")) |
|||
{ |
|||
var typeSimpleSplit = typeSimple.Split("."); |
|||
typeSimple = typeSimpleSplit[typeSimpleSplit.Length - 1]; |
|||
} |
|||
|
|||
if (typeSimple.StartsWith("System.Object")) |
|||
{ |
|||
typeSimple = typeSimple.Replace("System.Object", "object"); |
|||
} |
|||
|
|||
if (typeSimple.Contains("?")) |
|||
{ |
|||
typeSimple = typeSimple.Replace("?", ""); |
|||
propertyName += "?"; |
|||
} |
|||
|
|||
if (typeSimple != "boolean" |
|||
&& typeSimple != "string" |
|||
&& typeSimple != "number" |
|||
&& typeSimple != "boolean[]" |
|||
&& typeSimple != "string[]" |
|||
&& typeSimple != "number[]" |
|||
) |
|||
{ |
|||
var typeSimpleModelName = typeSimple.PascalToKebabCase() + ".ts"; |
|||
var modelPath = $"src/app/{rootPath}/shared/models/{typeSimpleModelName}"; |
|||
if (!File.Exists(modelPath)) |
|||
{ |
|||
typeSimple = "any" + (typeSimple.Contains("[]") ? "[]" : ""); |
|||
} |
|||
} |
|||
|
|||
if (propertyList.Any(p => p.Key == baseTypeName && p.Value.Any(q => q.Key == propertyName && q.Value == typeSimple))) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(modelIndex)) |
|||
{ |
|||
var propertyTypeSplit = ((string)property["type"]).Split("."); |
|||
var propertyType = propertyTypeSplit[propertyTypeSplit.Length - 1]; |
|||
modelFileText.Insert(0, ""); |
|||
modelFileText.Insert(0, $"import {{ {propertyType} }} from '../models';"); |
|||
modelFileText.Insert(0, ""); |
|||
modelIndexList.Add(modelIndex); |
|||
} |
|||
|
|||
if (propertyList.Any(p => p.Key == typeName && !p.Value.Any(q => q.Key == propertyName))) |
|||
{ |
|||
propertyList[typeName].Add(propertyName, typeSimple); |
|||
} |
|||
else if (!propertyList.Any(p => p.Key == typeName)) |
|||
{ |
|||
var newProperty = new Dictionary<string, string>(); |
|||
newProperty.Add(propertyName, typeSimple); |
|||
propertyList.Add(typeName, newProperty); |
|||
} |
|||
|
|||
modelFileText.AppendLine($" {propertyName}: {typeSimple};"); |
|||
} |
|||
|
|||
modelFileText.AppendLine(""); |
|||
|
|||
modelFileText.AppendLine($" constructor(initialValues: Partial<{typeName}> = {{}}) {{"); |
|||
|
|||
if (!string.IsNullOrWhiteSpace(baseType)) |
|||
{ |
|||
modelFileText.AppendLine(" super(initialValues);"); |
|||
modelFileText.AppendLine(" }"); |
|||
} |
|||
else |
|||
{ |
|||
modelFileText.AppendLine(" if (initialValues) {"); |
|||
modelFileText.AppendLine(" for (const key in initialValues) {"); |
|||
modelFileText.AppendLine(" if (initialValues.hasOwnProperty(key)) {"); |
|||
modelFileText.AppendLine(" this[key] = initialValues[key];"); |
|||
|
|||
modelFileText.AppendLine(" }"); |
|||
modelFileText.AppendLine(" }"); |
|||
modelFileText.AppendLine(" }"); |
|||
modelFileText.AppendLine(" }"); |
|||
} |
|||
|
|||
modelFileText.AppendLine("}"); |
|||
} |
|||
|
|||
File.WriteAllText($"src/app/{rootPath}/shared/models/{typeModelName}", modelFileText.ToString()); |
|||
|
|||
return typeModelName.Replace(".ts", ""); |
|||
} |
|||
|
|||
private static List<ParameterModel> AddParameter(string parameterName, string type, bool parameterIsOptional, string parameterDefaultValue, string bindingSourceId, List<ParameterModel> parameterModel) |
|||
{ |
|||
if (parameterDefaultValue != "null") |
|||
{ |
|||
parameterModel.Add(new ParameterModel |
|||
{ |
|||
DisplayOrder = 3, |
|||
Name = parameterName, |
|||
Type = type, |
|||
Value = parameterDefaultValue, |
|||
BindingSourceId = bindingSourceId |
|||
}); |
|||
} |
|||
else if (parameterDefaultValue == "null" && parameterIsOptional) |
|||
{ |
|||
parameterModel.Add(new ParameterModel |
|||
{ |
|||
DisplayOrder = 2, |
|||
Name = parameterName, |
|||
IsOptional = true, |
|||
Type = type, |
|||
BindingSourceId = bindingSourceId |
|||
}); |
|||
} |
|||
else |
|||
{ |
|||
parameterModel.Add(new ParameterModel |
|||
{ |
|||
DisplayOrder = 1, |
|||
Name = parameterName, |
|||
Type = type, |
|||
BindingSourceId = bindingSourceId |
|||
}); |
|||
} |
|||
|
|||
return parameterModel; |
|||
} |
|||
|
|||
public string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Usage:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine(" abp generate-proxy [options]"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Options:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("-a|--apiUrl <api-url> (default: environment.ts>apis>default>url)"); |
|||
sb.AppendLine("-u|--ui <ui-framework> (default: angular)"); |
|||
sb.AppendLine("-m|--module <module> (default: app)"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Examples:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine(" abp generate-proxy --apiUrl https://www.volosoft.com"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public string GetShortDescription() |
|||
{ |
|||
return "Generates typescript service proxies and DTOs"; |
|||
} |
|||
|
|||
private UiFramework GetUiFramework(CommandLineArgs commandLineArgs) |
|||
{ |
|||
var optionValue = commandLineArgs.Options.GetOrNull(Options.UiFramework.Short, Options.UiFramework.Long); |
|||
switch (optionValue) |
|||
{ |
|||
case "none": |
|||
return UiFramework.None; |
|||
case "mvc": |
|||
return UiFramework.Mvc; |
|||
case "angular": |
|||
return UiFramework.Angular; |
|||
default: |
|||
return UiFramework.Angular; |
|||
} |
|||
} |
|||
|
|||
public static class Options |
|||
{ |
|||
public static class Module |
|||
{ |
|||
public const string Short = "m"; |
|||
public const string Long = "module"; |
|||
} |
|||
|
|||
public static class ApiUrl |
|||
{ |
|||
public const string Short = "a"; |
|||
public const string Long = "apiUrl"; |
|||
} |
|||
|
|||
public static class UiFramework |
|||
{ |
|||
public const string Short = "u"; |
|||
public const string Long = "ui"; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static class StringExtensions |
|||
{ |
|||
public static string PascalToKebabCase(this string value) |
|||
{ |
|||
if (string.IsNullOrEmpty(value)) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
return Regex.Replace( |
|||
value, |
|||
"(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])", |
|||
"-$1", |
|||
RegexOptions.Compiled) |
|||
.Trim() |
|||
.ToLower(); |
|||
} |
|||
} |
|||
|
|||
public class ParameterModel |
|||
{ |
|||
public int DisplayOrder { get; set; } |
|||
public string Name { get; set; } |
|||
public string Value { get; set; } |
|||
public string Type { get; set; } |
|||
public bool IsOptional { get; set; } |
|||
public string BindingSourceId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Volo.Abp.Cli.ProjectBuilding.Files; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps |
|||
{ |
|||
public class RemoveRootFolderStep : ProjectBuildPipelineStep |
|||
{ |
|||
public override void Execute(ProjectBuildContext context) |
|||
{ |
|||
foreach (var file in context.Files) |
|||
{ |
|||
file.SetName(RemoveRootFolder(file.Name)); |
|||
} |
|||
} |
|||
|
|||
private string RemoveRootFolder(string fileName) |
|||
{ |
|||
if (!fileName.Contains('/')) |
|||
{ |
|||
return fileName; |
|||
} |
|||
|
|||
return string.Join("/", fileName.Split('/').Skip(1)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectModification |
|||
{ |
|||
public static class ProjectFileNameHelper |
|||
{ |
|||
|
|||
public static string GetAssemblyNameFromProjectPath(string projectFile) |
|||
{ |
|||
return projectFile |
|||
.Substring(projectFile.LastIndexOf(Path.DirectorySeparatorChar) + 1) |
|||
.RemovePostFix(StringComparison.OrdinalIgnoreCase, ".csproj"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.Http.Modeling |
|||
{ |
|||
public class ApplicationApiDescriptionModelRequestDto |
|||
{ |
|||
public bool IncludeTypes { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,127 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Http.Modeling |
|||
{ |
|||
public static class ModelingTypeHelper |
|||
{ |
|||
public static string GetFullNameHandlingNullableAndGenerics([NotNull] Type type) |
|||
{ |
|||
Check.NotNull(type, nameof(type)); |
|||
|
|||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) |
|||
{ |
|||
return type.GenericTypeArguments[0].FullName + "?"; |
|||
} |
|||
|
|||
if (type.IsGenericType) |
|||
{ |
|||
var genericType = type.GetGenericTypeDefinition(); |
|||
return $"{genericType.FullName.Left(genericType.FullName.IndexOf('`'))}<{type.GenericTypeArguments.Select(GetFullNameHandlingNullableAndGenerics).JoinAsString(",")}>"; |
|||
} |
|||
|
|||
return type.FullName; |
|||
} |
|||
|
|||
public static string GetSimplifiedName([NotNull] Type type) |
|||
{ |
|||
Check.NotNull(type, nameof(type)); |
|||
|
|||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) |
|||
{ |
|||
return GetSimplifiedName(type.GenericTypeArguments[0]) + "?"; |
|||
} |
|||
|
|||
if (type.IsGenericType) |
|||
{ |
|||
var genericType = type.GetGenericTypeDefinition(); |
|||
return $"{genericType.FullName.Left(genericType.FullName.IndexOf('`'))}<{type.GenericTypeArguments.Select(GetSimplifiedName).JoinAsString(",")}>"; |
|||
} |
|||
|
|||
if (type == typeof(string)) |
|||
{ |
|||
return "string"; |
|||
} |
|||
else if (type == typeof(int)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(long)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(bool)) |
|||
{ |
|||
return "boolean"; |
|||
} |
|||
else if (type == typeof(char)) |
|||
{ |
|||
return "string"; |
|||
} |
|||
else if (type == typeof(double)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(float)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(decimal)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(DateTime)) |
|||
{ |
|||
return "string"; |
|||
} |
|||
else if (type == typeof(DateTimeOffset)) |
|||
{ |
|||
return "string"; |
|||
} |
|||
else if (type == typeof(TimeSpan)) |
|||
{ |
|||
return "string"; |
|||
} |
|||
else if (type == typeof(Guid)) |
|||
{ |
|||
return "string"; |
|||
} |
|||
else if (type == typeof(byte)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(sbyte)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(short)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(ushort)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(uint)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(ulong)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(IntPtr)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
else if (type == typeof(UIntPtr)) |
|||
{ |
|||
return "number"; |
|||
} |
|||
|
|||
return type.FullName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using Volo.Abp.Reflection; |
|||
|
|||
namespace Volo.Abp.Http.Modeling |
|||
{ |
|||
[Serializable] |
|||
public class PropertyApiDescriptionModel |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string Type { get; set; } |
|||
|
|||
public string TypeSimple { get; set; } |
|||
|
|||
//TODO: Validation rules for this property
|
|||
public static PropertyApiDescriptionModel Create(PropertyInfo propertyInfo) |
|||
{ |
|||
string typeName; |
|||
string simpleTypeName; |
|||
|
|||
if (TypeHelper.IsEnumerable(propertyInfo.PropertyType, out var itemType, includePrimitives: false)) |
|||
{ |
|||
typeName = $"[{ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(itemType)}]"; |
|||
simpleTypeName = $"[{ModelingTypeHelper.GetSimplifiedName(itemType)}]"; |
|||
} |
|||
else if (TypeHelper.IsDictionary(propertyInfo.PropertyType, out var keyType, out var valueType)) |
|||
{ |
|||
typeName = $"{{{ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(keyType)}:{ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(valueType)}}}"; |
|||
simpleTypeName = $"{{{ModelingTypeHelper.GetSimplifiedName(keyType)}:{ModelingTypeHelper.GetSimplifiedName(valueType)}}}"; |
|||
} |
|||
else |
|||
{ |
|||
typeName = ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(propertyInfo.PropertyType); |
|||
simpleTypeName = ModelingTypeHelper.GetSimplifiedName(propertyInfo.PropertyType); |
|||
} |
|||
|
|||
return new PropertyApiDescriptionModel |
|||
{ |
|||
Name = propertyInfo.Name, |
|||
Type = typeName, |
|||
TypeSimple = simpleTypeName |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using System; |
|||
using System.Linq; |
|||
|
|||
namespace Volo.Abp.Http.Modeling |
|||
{ |
|||
[Serializable] |
|||
public class TypeApiDescriptionModel |
|||
{ |
|||
public string BaseType { get; set; } |
|||
|
|||
public bool IsEnum { get; set; } |
|||
|
|||
public string[] EnumNames { get; set; } |
|||
|
|||
public object[] EnumValues { get; set; } |
|||
|
|||
public PropertyApiDescriptionModel[] Properties { get; set; } |
|||
|
|||
private TypeApiDescriptionModel() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public static TypeApiDescriptionModel Create(Type type) |
|||
{ |
|||
var baseType = type.BaseType; |
|||
if (baseType == typeof(object)) |
|||
{ |
|||
baseType = null; |
|||
} |
|||
|
|||
var typeModel = new TypeApiDescriptionModel |
|||
{ |
|||
IsEnum = type.IsEnum, |
|||
BaseType = baseType != null ? ModelingTypeHelper.GetFullNameHandlingNullableAndGenerics(baseType) : null |
|||
}; |
|||
|
|||
if (typeModel.IsEnum) |
|||
{ |
|||
typeModel.EnumNames = type.GetEnumNames(); |
|||
typeModel.EnumValues = type.GetEnumValues().Cast<object>().ToArray(); |
|||
} |
|||
else |
|||
{ |
|||
typeModel.Properties = type |
|||
.GetProperties() |
|||
.Select(PropertyApiDescriptionModel.Create) |
|||
.ToArray(); |
|||
} |
|||
|
|||
return typeModel; |
|||
} |
|||
} |
|||
} |
|||
@ -1,3 +1,10 @@ |
|||
const fse = require("fs-extra"); |
|||
const fse = require('fs-extra'); |
|||
|
|||
console.log(fse.readJSONSync("package.json").version); |
|||
const commonProps = fse.readFileSync('../common.props').toString(); |
|||
|
|||
const versionTag = '<Version>'; |
|||
const versionEndTag = '</Version>'; |
|||
const first = commonProps.indexOf(versionTag) + versionTag.length; |
|||
const last = commonProps.indexOf(versionEndTag); |
|||
|
|||
console.log(commonProps.substring(first, last)); |
|||
|
|||
@ -0,0 +1,124 @@ |
|||
export class ListResultDto<T> { |
|||
items?: T[]; |
|||
|
|||
constructor(initialValues: Partial<ListResultDto<T>> = {}) { |
|||
for (const key in initialValues) { |
|||
if (initialValues.hasOwnProperty(key)) { |
|||
this[key] = initialValues[key]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
export class PagedResultDto<T> extends ListResultDto<T> { |
|||
totalCount?: number; |
|||
|
|||
constructor(initialValues: Partial<PagedResultDto<T>> = {}) { |
|||
super(initialValues); |
|||
} |
|||
} |
|||
|
|||
export class LimitedResultRequestDto { |
|||
maxResultCount = 10; |
|||
|
|||
constructor(initialValues: Partial<LimitedResultRequestDto> = {}) { |
|||
for (const key in initialValues) { |
|||
if (initialValues.hasOwnProperty(key)) { |
|||
this[key] = initialValues[key]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
export class PagedResultRequestDto extends LimitedResultRequestDto { |
|||
skipCount?: number; |
|||
|
|||
constructor(initialValues: Partial<PagedResultRequestDto> = {}) { |
|||
super(initialValues); |
|||
} |
|||
} |
|||
|
|||
export class PagedAndSortedResultRequestDto extends PagedResultRequestDto { |
|||
sorting?: string; |
|||
|
|||
constructor(initialValues: Partial<PagedAndSortedResultRequestDto> = {}) { |
|||
super(initialValues); |
|||
} |
|||
} |
|||
|
|||
export class EntityDto<TKey = string> { |
|||
id?: TKey; |
|||
|
|||
constructor(initialValues: Partial<EntityDto<TKey>> = {}) { |
|||
for (const key in initialValues) { |
|||
if (initialValues.hasOwnProperty(key)) { |
|||
this[key] = initialValues[key]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
export class CreationAuditedEntityDto<TPrimaryKey = string> extends EntityDto<TPrimaryKey> { |
|||
creationTime?: string | Date; |
|||
creatorId?: string; |
|||
|
|||
constructor(initialValues: Partial<CreationAuditedEntityDto<TPrimaryKey>> = {}) { |
|||
super(initialValues); |
|||
} |
|||
} |
|||
|
|||
export class CreationAuditedEntityWithUserDto< |
|||
TUserDto, |
|||
TPrimaryKey = string |
|||
> extends CreationAuditedEntityDto<TPrimaryKey> { |
|||
creator?: TUserDto; |
|||
|
|||
constructor( |
|||
initialValues: Partial<CreationAuditedEntityWithUserDto<TUserDto, TPrimaryKey>> = {}, |
|||
) { |
|||
super(initialValues); |
|||
} |
|||
} |
|||
|
|||
export class AuditedEntityDto<TPrimaryKey = string> extends CreationAuditedEntityDto<TPrimaryKey> { |
|||
lastModificationTime?: string | Date; |
|||
lastModifierId?: string; |
|||
|
|||
constructor(initialValues: Partial<AuditedEntityDto<TPrimaryKey>> = {}) { |
|||
super(initialValues); |
|||
} |
|||
} |
|||
|
|||
export class AuditedEntityWithUserDto<TUserDto, TPrimaryKey = string> extends AuditedEntityDto< |
|||
TPrimaryKey |
|||
> { |
|||
creator?: TUserDto; |
|||
lastModifier?: TUserDto; |
|||
|
|||
constructor(initialValues: Partial<AuditedEntityWithUserDto<TUserDto, TPrimaryKey>> = {}) { |
|||
super(initialValues); |
|||
} |
|||
} |
|||
|
|||
export class FullAuditedEntityDto<TPrimaryKey = string> extends AuditedEntityDto<TPrimaryKey> { |
|||
isDeleted?: boolean; |
|||
deleterId?: string; |
|||
deletionTime?: Date | string; |
|||
|
|||
constructor(initialValues: Partial<FullAuditedEntityDto<TPrimaryKey>> = {}) { |
|||
super(initialValues); |
|||
} |
|||
} |
|||
|
|||
export class FullAuditedEntityWithUserDto< |
|||
TUserDto, |
|||
TPrimaryKey = string |
|||
> extends FullAuditedEntityDto<TPrimaryKey> { |
|||
creator?: TUserDto; |
|||
lastModifier?: TUserDto; |
|||
deleter?: TUserDto; |
|||
|
|||
constructor(initialValues: Partial<FullAuditedEntityWithUserDto<TUserDto, TPrimaryKey>> = {}) { |
|||
super(initialValues); |
|||
} |
|||
} |
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/anchor-js", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"anchor-js": "^4.2.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,11 +1,11 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/aspnetcore.mvc.ui.theme.basic", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.2.0" |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,24 +1,24 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/aspnetcore.mvc.ui.theme.shared", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui": "^2.2.0", |
|||
"@abp/bootstrap": "^2.2.0", |
|||
"@abp/bootstrap-datepicker": "^2.2.0", |
|||
"@abp/datatables.net-bs4": "^2.2.0", |
|||
"@abp/font-awesome": "^2.2.0", |
|||
"@abp/jquery-form": "^2.2.0", |
|||
"@abp/jquery-validation-unobtrusive": "^2.2.0", |
|||
"@abp/lodash": "^2.2.0", |
|||
"@abp/luxon": "^2.2.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.2.0", |
|||
"@abp/select2": "^2.2.0", |
|||
"@abp/sweetalert": "^2.2.0", |
|||
"@abp/timeago": "^2.2.0", |
|||
"@abp/toastr": "^2.2.0" |
|||
"@abp/aspnetcore.mvc.ui": "^2.3.0", |
|||
"@abp/bootstrap": "^2.3.0", |
|||
"@abp/bootstrap-datepicker": "^2.3.0", |
|||
"@abp/datatables.net-bs4": "^2.3.0", |
|||
"@abp/font-awesome": "^2.3.0", |
|||
"@abp/jquery-form": "^2.3.0", |
|||
"@abp/jquery-validation-unobtrusive": "^2.3.0", |
|||
"@abp/lodash": "^2.3.0", |
|||
"@abp/luxon": "^2.3.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.3.0", |
|||
"@abp/select2": "^2.3.0", |
|||
"@abp/sweetalert": "^2.3.0", |
|||
"@abp/timeago": "^2.3.0", |
|||
"@abp/toastr": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,13 +1,13 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/blogging", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.2.0", |
|||
"@abp/owl.carousel": "^2.2.0", |
|||
"@abp/tui-editor": "^2.2.0" |
|||
"@abp/aspnetcore.mvc.ui.theme.shared": "^2.3.0", |
|||
"@abp/owl.carousel": "^2.3.0", |
|||
"@abp/tui-editor": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/bootstrap", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"bootstrap": "^4.3.1" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/clipboard", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"clipboard": "^2.0.4" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/codemirror", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"codemirror": "^5.49.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,8 +1,8 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/core", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/datatables.net-bs4", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/datatables.net": "^2.2.0", |
|||
"@abp/datatables.net": "^2.3.0", |
|||
"datatables.net-bs4": "^1.10.20" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/datatables.net", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"datatables.net": "^1.10.20" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,15 +1,15 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/docs", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/anchor-js": "^2.2.0", |
|||
"@abp/clipboard": "^2.2.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.2.0", |
|||
"@abp/popper.js": "^2.2.0", |
|||
"@abp/prismjs": "^2.2.0" |
|||
"@abp/anchor-js": "^2.3.0", |
|||
"@abp/clipboard": "^2.3.0", |
|||
"@abp/malihu-custom-scrollbar-plugin": "^2.3.0", |
|||
"@abp/popper.js": "^2.3.0", |
|||
"@abp/prismjs": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/font-awesome", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"@fortawesome/fontawesome-free": "^5.11.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,11 +1,11 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/highlight.js", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0" |
|||
"@abp/core": "^2.3.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/jquery-form", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"jquery-form": "^4.2.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/jquery-validation-unobtrusive", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery-validation": "^2.2.0", |
|||
"@abp/jquery-validation": "^2.3.0", |
|||
"jquery-validation-unobtrusive": "^3.2.11" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/jquery-validation", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"jquery-validation": "^1.19.1" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/jquery", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"jquery": "^3.4.1" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/lodash", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"lodash": "^4.17.15" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/malihu-custom-scrollbar-plugin", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"malihu-custom-scrollbar-plugin": "^3.1.5" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/markdown-it", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"markdown-it": "^10.0.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/owl.carousel", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"owl.carousel": "^2.3.4" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/popper.js", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"popper.js": "^1.16.0" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/prismjs", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"prismjs": "^1.17.1" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/select2", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"select2": "^4.0.12" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/sweetalert", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "^2.2.0", |
|||
"@abp/core": "^2.3.0", |
|||
"sweetalert": "^2.1.2" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/timeago", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"timeago": "^1.6.7" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/toastr", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"toastr": "^2.1.4" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
@ -1,15 +1,15 @@ |
|||
{ |
|||
"version": "2.2.0", |
|||
"version": "2.3.0", |
|||
"name": "@abp/tui-editor", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/codemirror": "^2.2.0", |
|||
"@abp/highlight.js": "^2.2.0", |
|||
"@abp/jquery": "^2.2.0", |
|||
"@abp/markdown-it": "^2.2.0", |
|||
"@abp/codemirror": "^2.3.0", |
|||
"@abp/highlight.js": "^2.3.0", |
|||
"@abp/jquery": "^2.3.0", |
|||
"@abp/markdown-it": "^2.3.0", |
|||
"tui-editor": "^1.4.8" |
|||
}, |
|||
"gitHead": "d5707967977fb14328661ae403c41eaa679ee263" |
|||
"gitHead": "e5aead708928c668494ed0dbd7e8ae5c5ef9b9e7" |
|||
} |
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue