Compare commits
4 Commits
62e863e343
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
50be1ea726 | |||
fe6f1581dd | |||
f322e3f485 | |||
c4dc16dde4 |
@ -44,7 +44,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
7
.github/workflows/main.yml
vendored
Normal file
7
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
on: workflow_call
|
||||
jobs:
|
||||
my_first_job:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run my action
|
||||
uses: ./.github/worklflows/dotnet
|
31
.vscode/launch.json
vendored
31
.vscode/launch.json
vendored
@ -4,32 +4,6 @@
|
||||
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C#: sampleWebAsWebApiClient Debug",
|
||||
"type": "dotnet",
|
||||
"request": "launch",
|
||||
"projectPath": "${workspaceFolder}/src/sampleWebAsWebApiClient/sampleWebAsWebApiClient.csproj"
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Launch (web)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/bin/Debug/<target-framework>/<project-name.dll>",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/Views": "${workspaceFolder}/Views"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/* {
|
||||
@ -109,11 +83,6 @@
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"presentation": {
|
||||
"hidden": false,
|
||||
"group": "run",
|
||||
"order": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
|
16
.vscode/settings.json
vendored
16
.vscode/settings.json
vendored
@ -1,6 +1,18 @@
|
||||
{
|
||||
"dotnet-test-explorer.testProjectPath": "test/**/*Tests.csproj",
|
||||
|
||||
"dotnet-test-explorer.testProjectPath": "**/*Tests.@(csproj|vbproj|fsproj)",
|
||||
"sqltools.connections": [
|
||||
{
|
||||
"previewLimit": 50,
|
||||
"server": "localhost",
|
||||
"port": 5432,
|
||||
"driver": "PostgreSQL",
|
||||
"name": "yavscdev",
|
||||
"group": "yavsc",
|
||||
"database": "YavscDev",
|
||||
"username": "yavscdev",
|
||||
"password": "admin"
|
||||
}
|
||||
],
|
||||
"cSpell.words": [
|
||||
"appsettings",
|
||||
"Newtonsoft",
|
||||
|
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@ -9,12 +9,8 @@
|
||||
"args": [
|
||||
"build",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign",
|
||||
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"group": "build",
|
||||
"isBuildCommand": true,
|
||||
"isTestCommand": false,
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
|
@ -1,50 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="AsciiDocNet" Version="1.0.0" />
|
||||
<PackageVersion Include="bootstrap" Version="5.3.7" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.69.0.3746" />
|
||||
<PackageVersion Include="Google.Apis.Compute.v1" Version="1.70.0.3829" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8" Version="8.0.5-preview-net9" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.AspNetIdentity" Version="8.0.5-preview-net9" />
|
||||
<PackageVersion Include="IdentityModel.AspNetCore" Version="4.3.0" />
|
||||
<PackageVersion Include="MailKit" Version="4.13.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Antiforgery" Version="2.3.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Hosting" Version="2.3.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Http.Features" Version="5.0.17" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Razor" Version="2.3.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.StaticFiles" Version="2.3.0" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.7" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageVersion Include="Microsoft.Playwright" Version="1.53.0" />
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
||||
<PackageVersion Include="MimeKit" Version="4.13.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageVersion Include="PayPalMerchantSDK" Version="2.16.250" />
|
||||
<PackageVersion Include="pazof.rules" Version="1.1.3" />
|
||||
<PackageVersion Include="popper.js" Version="1.16.1" />
|
||||
<PackageVersion Include="RazorEngine.NetCore" Version="3.1.0" />
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="9.0.7" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -15,13 +15,14 @@ WorkingDirectory=/srv/www/yavsc/
|
||||
ExecStart=/srv/www/yavsc/Yavsc
|
||||
Restart=always
|
||||
Environment="HOME=/srv/www/yavsc"
|
||||
Environment="ASPNETCORE_ENVIRONMENT=Production"
|
||||
Environment="ASPNETCORE_ConnectionStrings__DefaultConnection=YOUR Postgresql CONNECTION STRING"
|
||||
Environment="ASPNETCORE_ENVIRONMENT=lua"
|
||||
Environment="ASPNETCORE_ConnectionStrings__DefaultConnection=Server=localhost;Port=5432;Database=lua;Username=lua;Password=f3s-*Vx$;"
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=yavsc
|
||||
SyslogIdentifier=isnd
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>1c73094f-959f-4211-b1a1-6a69b236c283</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
|
||||
<ProjectReference Include="../Yavsc.Server/Yavsc.Server.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -111,9 +111,9 @@ namespace Yavsc.ApiControllers
|
||||
var user = dbContext.Users.Single(
|
||||
u => u.Id == uid
|
||||
);
|
||||
var info = user.MoveUserFileToDir(query.Id, query.To);
|
||||
var info = user.MoveUserFileToDir(query.id, query.to);
|
||||
if (!info.Done) return new BadRequestObjectResult(info);
|
||||
return Ok(new { moved = query.Id });
|
||||
return Ok(new { moved = query.id });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -124,21 +124,21 @@ namespace Yavsc.ApiControllers
|
||||
if (!ModelState.IsValid) {
|
||||
var idvr = new ValidRemoteUserFilePathAttribute();
|
||||
|
||||
return this.BadRequest(new { id = idvr.IsValid(query.Id), to = idvr.IsValid(query.To), errors = ModelState });
|
||||
return this.BadRequest(new { id = idvr.IsValid(query.id), to = idvr.IsValid(query.to), errors = ModelState });
|
||||
}
|
||||
_logger.LogInformation($"Valid move query: {query.Id} => {query.To}");
|
||||
_logger.LogInformation($"Valid move query: {query.id} => {query.to}");
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var user = dbContext.Users.Single(
|
||||
u => u.Id == uid
|
||||
);
|
||||
try {
|
||||
if (Config.UserFilesOptions.FileProvider.GetFileInfo(Path.Combine(user.UserName, query.Id)).Exists)
|
||||
if (Config.UserFilesOptions.FileProvider.GetFileInfo(Path.Combine(user.UserName, query.id)).Exists)
|
||||
{
|
||||
var result = user.MoveUserFile(query.Id, query.To);
|
||||
var result = user.MoveUserFile(query.id, query.to);
|
||||
if (!result.Done) return new BadRequestObjectResult(result);
|
||||
}
|
||||
else {
|
||||
var result = user.MoveUserDir(query.Id, query.To);
|
||||
var result = user.MoveUserDir(query.id, query.to);
|
||||
if (!result.Done) return new BadRequestObjectResult(result);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,23 @@
|
||||
using Yavsc.Attributes.Validation;
|
||||
namespace Yavsc.Models.FileSystem
|
||||
{
|
||||
public class RenameFileQuery {
|
||||
[ValidRemoteUserFilePath]
|
||||
[YaStringLength(1, 512)]
|
||||
public string id { get; set; }
|
||||
|
||||
public class MoveFileQuery
|
||||
{
|
||||
[ValidRemoteUserFilePath]
|
||||
[YaStringLength(1, 512)]
|
||||
public required string Id { get; set; }
|
||||
[YaStringLength(0, 512)]
|
||||
[ValidRemoteUserFilePath]
|
||||
public string to { get; set; }
|
||||
}
|
||||
public class MoveFileQuery {
|
||||
[ValidRemoteUserFilePath]
|
||||
[YaStringLength(1, 512)]
|
||||
public string id { get; set; }
|
||||
|
||||
[YaStringLength(0, 512)]
|
||||
[ValidRemoteUserFilePath]
|
||||
public required string To { get; set; }
|
||||
}
|
||||
[YaStringLength(0, 512)]
|
||||
[ValidRemoteUserFilePath]
|
||||
public string to { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
using Yavsc.Attributes.Validation;
|
||||
namespace Yavsc.Models.FileSystem
|
||||
{
|
||||
public class RenameFileQuery
|
||||
{
|
||||
[ValidRemoteUserFilePath]
|
||||
[YaStringLength(1, 512)]
|
||||
public required string Id { get; set; }
|
||||
|
||||
[YaStringLength(0, 512)]
|
||||
[ValidRemoteUserFilePath]
|
||||
public required string To { get; set; }
|
||||
}
|
||||
|
||||
}
|
@ -37,7 +37,7 @@ namespace Yavsc.Controllers
|
||||
}
|
||||
// GET: api/Estimate{?ownerId=User.GetUserId()}
|
||||
[HttpGet]
|
||||
public IActionResult GetEstimates(string? ownerId = null)
|
||||
public IActionResult GetEstimates(string ownerId = null)
|
||||
{
|
||||
if (ownerId == null) ownerId = User.GetUserId();
|
||||
else if (!UserIsAdminOrThis(ownerId)) // throw new Exception("Not authorized") ;
|
||||
|
@ -40,7 +40,7 @@ namespace Yavsc.ApiControllers
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("query/accept")]
|
||||
[HttpPost("query/reject")]
|
||||
public IActionResult AcceptQuery(string billingCode, long queryId)
|
||||
{
|
||||
if (billingCode == null) return BadRequest("billingCode");
|
||||
@ -51,6 +51,7 @@ namespace Yavsc.ApiControllers
|
||||
billing.Decided = true;
|
||||
dbContext.SaveChanges();
|
||||
return Ok();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,7 @@ public class NativeConfidentialController : Controller
|
||||
[FromBody] DeviceDeclaration declaration)
|
||||
{
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (uid == null)
|
||||
throw new InvalidOperationException("no name identifier from claims");
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
_logger.LogError("Invalid model for GCMD");
|
||||
|
@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Api.Helpers;
|
||||
using Yavsc.Server.Helpers;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Yavsc.WebApi.Controllers
|
||||
{
|
||||
@ -31,9 +30,9 @@ namespace Yavsc.WebApi.Controllers
|
||||
return new BadRequestObjectResult(
|
||||
new { error = "user not found" });
|
||||
var uid = User.GetUserId();
|
||||
Debug.Assert(uid != null, "uid is null");
|
||||
|
||||
var userData = await GetUserData(uid);
|
||||
Debug.Assert(userData != null, "userData is null");
|
||||
|
||||
var user = new Yavsc.Models.Auth.Me(userData.Id, userData.UserName, userData.Email,
|
||||
userData.Avatar,
|
||||
userData.PostalAddress, userData.DedicatedGoogleCalendar);
|
||||
@ -58,7 +57,7 @@ namespace Yavsc.WebApi.Controllers
|
||||
[HttpGet("myhost")]
|
||||
public IActionResult MyHost ()
|
||||
{
|
||||
return Ok(new { host = Request.ForwardedFor() });
|
||||
return Ok(new { host = Request.ForHost() });
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,8 +13,8 @@ namespace Yavsc.Api.Helpers
|
||||
public static class RequestHelpers
|
||||
{
|
||||
// Check for some apache proxy header, if any
|
||||
public static string? ForwardedFor(this HttpRequest request) {
|
||||
string? host = request.Headers["X-Forwarded-For"];
|
||||
public static string ForHost(this HttpRequest request) {
|
||||
string host = request.Headers["X-Forwarded-For"];
|
||||
if (string.IsNullOrEmpty(host)) {
|
||||
host = request.Host.Value;
|
||||
} else { // Using X-Forwarded-For last address
|
||||
|
@ -9,7 +9,7 @@ namespace Yavsc.Api.Helpers
|
||||
{
|
||||
public static class UserHelpers
|
||||
{
|
||||
public static string? GetUserId(this ClaimsPrincipal user)
|
||||
public static string GetUserId(this ClaimsPrincipal user)
|
||||
{
|
||||
return user.FindFirstValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
|
||||
}
|
||||
|
@ -11,6 +11,8 @@
|
||||
*/
|
||||
|
||||
using IdentityModel;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Helpers;
|
||||
@ -61,8 +63,7 @@ internal class Program
|
||||
options.IncludeErrorDetails = true;
|
||||
options.Authority = "https://localhost:5001";
|
||||
options.TokenValidationParameters =
|
||||
new() { ValidateAudience = false, RoleClaimType = JwtClaimTypes.Role };
|
||||
options.MapInboundClaims = true;
|
||||
new() { ValidateAudience = false };
|
||||
});
|
||||
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
@ -72,7 +73,12 @@ internal class Program
|
||||
.AddTransient<IBillingService, BillingService>()
|
||||
.AddTransient<ICalendarManager, CalendarManager>();
|
||||
services.AddTransient<IFileSystemAuthManager, FileSystemAuthManager>();
|
||||
|
||||
/*
|
||||
services.AddSingleton<IConnexionManager, HubConnectionManager>();
|
||||
services.AddSingleton<ILiveProcessor, LiveProcessor>();
|
||||
services.AddIdentityApiEndpoints<ApplicationUser>();
|
||||
services.AddSession();
|
||||
*/
|
||||
WorkflowHelpers.ConfigureBillingService();
|
||||
using (var app = builder.Build())
|
||||
{
|
||||
@ -95,10 +101,15 @@ internal class Program
|
||||
app.MapDefaultControllerRoute();
|
||||
app.MapGet("/identity", (HttpContext context) =>
|
||||
new JsonResult(context?.User?.Claims.Select(c => new { c.Type, c.Value }))
|
||||
);
|
||||
);
|
||||
|
||||
// app.UseSession();
|
||||
await app.RunAsync();
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<Project>
|
||||
<Project >
|
||||
<PropertyGroup>
|
||||
<Version>1.0.8</Version>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -1,19 +1,16 @@
|
||||
|
||||
|
||||
|
||||
using Yavsc.Abstract.Identity;
|
||||
|
||||
namespace Yavsc
|
||||
{
|
||||
public interface IBlogPostPayLoad
|
||||
{
|
||||
string? Content { get; set; }
|
||||
string? Photo { get; set; }
|
||||
string Content { get; set; }
|
||||
string Photo { get; set; }
|
||||
|
||||
}
|
||||
public interface IBlogPost : IBlogPostPayLoad, ITrackedEntity, IIdentified<long>, ITitle
|
||||
public interface IBlogPost :IBlogPostPayLoad, ITrackedEntity, IIdentified<long>, ITitle
|
||||
{
|
||||
string AuthorId { get; set; }
|
||||
IApplicationUser Author { get; }
|
||||
}
|
||||
}
|
||||
|
@ -5,21 +5,21 @@ namespace Yavsc
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly Scope[] SiteScopes = {
|
||||
new Scope { Id = "profile", Description = "Your profile informations" },
|
||||
new Scope { Id = "book" , Description ="Your booking interface"},
|
||||
new Scope { Id = "blog" , Description ="Your blogging interface"},
|
||||
new Scope { Id = "estimate" , Description ="Your estimation interface"},
|
||||
new Scope { Id = "contract" , Description ="Your contract signature access"},
|
||||
new Scope { Id = "admin" , Description ="Your administration rights on this site"},
|
||||
new Scope { Id = "moderation" , Description ="Your moderator interface"},
|
||||
public static readonly Scope[] SiteScopes = {
|
||||
new Scope { Id = "profile", Description = "Your profile informations" },
|
||||
new Scope { Id = "book" , Description ="Your booking interface"},
|
||||
new Scope { Id = "blog" , Description ="Your blogging interface"},
|
||||
new Scope { Id = "estimate" , Description ="Your estimation interface"},
|
||||
new Scope { Id = "contract" , Description ="Your contract signature access"},
|
||||
new Scope { Id = "admin" , Description ="Your administration rights on this site"},
|
||||
new Scope { Id = "moderation" , Description ="Your moderator interface"},
|
||||
new Scope { Id = "frontoffice" , Description ="Your front office interface" }
|
||||
};
|
||||
|
||||
public const string CompanyClaimType = "https://schemas.pschneider.fr/identity/claims/Company";
|
||||
public const string UserNameRegExp = @"^[a-zA-Z][a-zA-Z0-9._-]*$";
|
||||
public const string UserFileNamePatternRegExp = @"^([a-zA-Z0-9._-]*/)*[a-zA-Z0-9._-]+$";
|
||||
|
||||
|
||||
public const string LoginPath = "/signin";
|
||||
public const string LogoutPath = "/signout";
|
||||
|
||||
@ -37,7 +37,7 @@ namespace Yavsc
|
||||
public const string FrontOfficeGroupName = "FrontOffice";
|
||||
public const string DefaultAvatar = "/images/Users/icon_user.png";
|
||||
public const string AnonAvatar = "/images/Users/icon_anon_user.png";
|
||||
public const string YavscConnectionStringEnvName = "YAVSC_CONNECTION_STRING";
|
||||
public const string YavscConnectionStringEnvName = "DEFAULTCONNECTION_CONNECTIONSTRING";
|
||||
|
||||
// at the end, let 4*4 bytes in peace
|
||||
public const int WebSocketsMaxBufLen = 4096;
|
||||
@ -52,7 +52,9 @@ namespace Yavsc
|
||||
public const int MaxUserNameLength = 26;
|
||||
|
||||
public const string LivePath = "/live/cast";
|
||||
|
||||
|
||||
public const string StreamingPath = "/api/stream/put";
|
||||
|
||||
public const string RoleClaimName = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role";
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ namespace Yavsc.Models.Google.Messaging
|
||||
/// <summary>
|
||||
/// The error.
|
||||
/// </summary>
|
||||
public string? error;
|
||||
public string error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -3,10 +3,10 @@
|
||||
public interface IApplicationUser
|
||||
{
|
||||
string Id { get; set; }
|
||||
string? UserName { get; set; }
|
||||
string? Avatar { get ; set; }
|
||||
IAccountBalance? AccountBalance { get; }
|
||||
string? DedicatedGoogleCalendar { get; }
|
||||
ILocation? PostalAddress { get; }
|
||||
string UserName { get; set; }
|
||||
string Avatar { get ; set; }
|
||||
IAccountBalance AccountBalance { get; set; }
|
||||
string DedicatedGoogleCalendar { get; set; }
|
||||
ILocation PostalAddress { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ namespace Yavsc.Abstract.Identity.Security
|
||||
public interface ICircleAuthorized
|
||||
{
|
||||
long Id { get; set; }
|
||||
string AuthorId { get; }
|
||||
string OwnerId { get; }
|
||||
bool AuthorizeCircle(long circleId);
|
||||
ICircleAuthorization [] GetACL();
|
||||
|
||||
|
@ -1,12 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net9.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Description> A shared model for a little client/server app, dealing about establishing some contract, between some human client and provider.
|
||||
</Description>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
</Project>
|
||||
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "9.0.6",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
@ -80,12 +80,12 @@ public static class Config
|
||||
PostLogoutRedirectUris = { "https://localhost:5003/signout-callback-oidc",
|
||||
"http://localhost:5002/signout-callback-oidc" },
|
||||
|
||||
AllowedScopes = {
|
||||
AllowedScopes = {
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile,
|
||||
IdentityServerConstants.StandardScopes.Email,
|
||||
IdentityServerConstants.StandardScopes.OfflineAccess,
|
||||
"scope2" },
|
||||
"scope2" }
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
namespace Yavsc.Server.Exceptions;
|
||||
|
||||
[Serializable]
|
||||
public class AuthorizationFailureException : Exception
|
||||
{
|
||||
public AuthorizationFailureException(Microsoft.AspNetCore.Authorization.AuthorizationResult auth) : base(auth?.Failure?.ToString()??auth?.ToString()??"AuthorizationResult failure")
|
||||
{
|
||||
}
|
||||
|
||||
public AuthorizationFailureException(string? message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public AuthorizationFailureException(string? message, Exception? innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ namespace Yavsc.Helpers
|
||||
|
||||
public static string GetUserName(this ClaimsPrincipal user)
|
||||
{
|
||||
return user.FindFirstValue("name");
|
||||
return user.FindFirstValue(ClaimTypes.Name);
|
||||
}
|
||||
|
||||
public static bool IsSignedIn(this ClaimsPrincipal user)
|
||||
@ -24,14 +24,13 @@ namespace Yavsc.Helpers
|
||||
return user.Identity.IsAuthenticated;
|
||||
}
|
||||
|
||||
public static IEnumerable<BlogPost> UserPosts(this ApplicationDbContext dbContext, string posterId, string? readerId)
|
||||
public static IEnumerable<BlogPost> UserPosts(this ApplicationDbContext dbContext, string posterId, string readerId)
|
||||
{
|
||||
if (readerId == null)
|
||||
{
|
||||
var userPosts = dbContext.blogSpotPublications.Include(
|
||||
b => b.BlogPost
|
||||
).Where(x => x.BlogPost.AuthorId == posterId)
|
||||
.Select(x=>x.BlogPost).ToArray();
|
||||
var userPosts = dbContext.BlogSpot.Include(
|
||||
b => b.Author
|
||||
).Where(x => ((x.AuthorId == posterId))).ToArray();
|
||||
return userPosts;
|
||||
}
|
||||
else
|
||||
|
@ -16,80 +16,75 @@ namespace Yavsc.Helpers
|
||||
|
||||
public static class WorkflowHelpers
|
||||
{
|
||||
public static async Task<List<PerformerProfileViewModel>>
|
||||
ListPerformersAsync(this ApplicationDbContext context,
|
||||
public static async Task<List<PerformerProfileViewModel>>
|
||||
ListPerformersAsync(this ApplicationDbContext context,
|
||||
IBillingService billing,
|
||||
string actCode)
|
||||
{
|
||||
|
||||
|
||||
var actors = context.Performers
|
||||
.Include(p => p.Activity)
|
||||
.Include(p => p.Performer)
|
||||
.Where(p => p.Active && p.Activity.Any(u => u.DoesCode == actCode)).OrderBy(x => x.Rate)
|
||||
.Include(p=>p.Activity)
|
||||
.Include(p=>p.Performer)
|
||||
.Where(p => p.Active && p.Activity.Any(u=>u.DoesCode==actCode)).OrderBy( x => x.Rate )
|
||||
.ToArray();
|
||||
|
||||
List<PerformerProfileViewModel> result = new();
|
||||
List<PerformerProfileViewModel> result = new ();
|
||||
foreach (var a in actors)
|
||||
{
|
||||
var settings = await billing.GetPerformersSettingsAsync(actCode, a.PerformerId);
|
||||
result.Add(new PerformerProfileViewModel(a, actCode, settings));
|
||||
result.Add(new PerformerProfileViewModel(a, actCode,settings));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void RegisterBilling<T>(string code, Func<ApplicationDbContext, long,
|
||||
IDecidableQuery> getter) where T : IBillable
|
||||
{
|
||||
if (BillingService.Billing.ContainsKey(code)
|
||||
|| BillingService.GlobalBillingMap.ContainsKey(code))
|
||||
{
|
||||
throw new InvalidOperationException("Billing setup");
|
||||
}
|
||||
BillingService.Billing.Add(code, getter);
|
||||
BillingService.GlobalBillingMap.Add(typeof(T).Name, code);
|
||||
}
|
||||
public static void RegisterBilling<T>(string code, Func<ApplicationDbContext, long,
|
||||
IDecidableQuery> getter) where T : IBillable
|
||||
{
|
||||
BillingService.Billing.Add(code, getter);
|
||||
BillingService.GlobalBillingMap.Add(typeof(T).Name, code);
|
||||
}
|
||||
|
||||
public static void ConfigureBillingService()
|
||||
public static void ConfigureBillingService()
|
||||
{
|
||||
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
|
||||
foreach (var c in a.GetTypes())
|
||||
{
|
||||
foreach (var c in a.GetTypes())
|
||||
if (c.IsClass && !c.IsAbstract &&
|
||||
c.GetInterface("ISpecializationSettings") != null)
|
||||
{
|
||||
if (c.IsClass && !c.IsAbstract &&
|
||||
c.GetInterface("ISpecializationSettings") != null)
|
||||
{
|
||||
Config.ProfileTypes.Add(c);
|
||||
}
|
||||
Config.ProfileTypes.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var propertyInfo in typeof(ApplicationDbContext).GetProperties())
|
||||
foreach (var propertyInfo in typeof(ApplicationDbContext).GetProperties())
|
||||
{
|
||||
foreach (var attr in propertyInfo.CustomAttributes)
|
||||
{
|
||||
foreach (var attr in propertyInfo.CustomAttributes)
|
||||
// something like a DbSet?
|
||||
if (typeof(Yavsc.Attributes.ActivitySettingsAttribute).IsAssignableFrom(attr.AttributeType))
|
||||
{
|
||||
// something like a DbSet?
|
||||
if (typeof(Yavsc.Attributes.ActivitySettingsAttribute).IsAssignableFrom(attr.AttributeType))
|
||||
{
|
||||
BillingService.UserSettings.Add(propertyInfo);
|
||||
}
|
||||
BillingService.UserSettings.Add(propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) =>
|
||||
{
|
||||
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id);
|
||||
query.SelectedProfile = db.BrusherProfile.Single(b => b.UserId == query.PerformerId);
|
||||
return query;
|
||||
}));
|
||||
|
||||
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
|
||||
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) => db.RdvQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
}
|
||||
|
||||
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) =>
|
||||
{
|
||||
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id);
|
||||
query.SelectedProfile = db.BrusherProfile.Single(b => b.UserId == query.PerformerId);
|
||||
return query;
|
||||
}));
|
||||
|
||||
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
|
||||
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext, long, IDecidableQuery>
|
||||
((db, id) => db.RdvQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -29,13 +29,11 @@ using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Yavsc
|
||||
{
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Models;
|
||||
using Models.Chat;
|
||||
using Yavsc.Abstract.Chat;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Services;
|
||||
public partial class ChatHub : Hub, IDisposable
|
||||
{
|
||||
@ -194,11 +192,10 @@ namespace Yavsc
|
||||
NotifyUserInRoom(NotificationTypes.Error, room, "already registered.");
|
||||
return;
|
||||
}
|
||||
Debug.Assert(Context.User != null);
|
||||
string userName = Context.User.GetUserName();
|
||||
string userName = Context.User.Identity.Name;
|
||||
var user = _dbContext.Users.FirstOrDefault(u => u.UserName == userName);
|
||||
|
||||
var newroom = new ChatRoom { Name = room, OwnerId = Context.User.GetUserId() };
|
||||
var newroom = new ChatRoom { Name = room, OwnerId = user.Id };
|
||||
ChatRoomInfo chanInfo;
|
||||
if (_cxManager.TryGetChanInfo(room, out chanInfo))
|
||||
{
|
||||
@ -322,7 +319,7 @@ namespace Yavsc
|
||||
|
||||
async Task NotifyUser(string type, string targetId, string message)
|
||||
{
|
||||
_logger.LogInformation($"notifying user {type} {targetId} : {message}");
|
||||
_logger.LogInformation("notifying user {type} {targetId} : {message}");
|
||||
await Clients.Caller.SendAsync("notifyUser", type, targetId, message);
|
||||
}
|
||||
|
||||
@ -334,8 +331,6 @@ namespace Yavsc
|
||||
[Authorize]
|
||||
public async Task SendPV(string userName, string message)
|
||||
{
|
||||
// Authorized code
|
||||
Debug.Assert(Context.User != null);
|
||||
_logger.LogInformation($"Sending pv to {userName}");
|
||||
|
||||
if (!InputValidator.ValidateUserName(userName))
|
||||
@ -349,21 +344,19 @@ namespace Yavsc
|
||||
return ;
|
||||
}
|
||||
_logger.LogInformation($"Message form is validated.");
|
||||
var identityUserName = Context.User.GetUserName();
|
||||
|
||||
if (userName[0] != '?' && Context.User!=null)
|
||||
if (userName[0] != '?')
|
||||
if (!Context.User.IsInRole(Constants.AdminGroupName))
|
||||
{
|
||||
|
||||
var bl = _dbContext.BlackListed
|
||||
.Include(r => r.User)
|
||||
.Include(r => r.Owner)
|
||||
.Where(r => r.User.UserName == identityUserName && r.Owner.UserName == userName)
|
||||
.Where(r => r.User.UserName == Context.User.Identity.Name && r.Owner.UserName == userName)
|
||||
.Select(r => r.OwnerId);
|
||||
|
||||
if (bl.Count() > 0)
|
||||
{
|
||||
_logger.LogError($"Black listed : {identityUserName}");
|
||||
_logger.LogError($"Black listed : {Context.User.Identity.Name}");
|
||||
await NotifyUser(NotificationTypes.PrivateMessageDenied, userName, "you are black listed.");
|
||||
return;
|
||||
}
|
||||
@ -379,7 +372,7 @@ namespace Yavsc
|
||||
_logger.LogInformation($"cx: {connectionId}");
|
||||
var cli = Clients.Client(connectionId);
|
||||
_logger.LogInformation($"cli: {cli.ToString()}");
|
||||
await cli.SendAsync("addPV", identityUserName, message);
|
||||
await cli.SendAsync("addPV", Context.User.Identity.Name, message);
|
||||
_logger.LogInformation($"Sent pv to cx {connectionId}");
|
||||
}
|
||||
}
|
||||
@ -387,9 +380,6 @@ namespace Yavsc
|
||||
[Authorize]
|
||||
public async Task SendStream(string connectionId, long streamId, string message)
|
||||
{
|
||||
// Authorized code
|
||||
Debug.Assert(Context.User != null);
|
||||
Debug.Assert(Context.User.Identity != null);
|
||||
if (!InputValidator.ValidateMessage(message)) return;
|
||||
var sender = Context.User.Identity.Name;
|
||||
var cli = Clients.Client(connectionId);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,108 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ActivityNulls : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserModified",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserCreated",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "SettingsClassName",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Photo",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ModeratorGroupName",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserModified",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserCreated",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "SettingsClassName",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Photo",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ModeratorGroupName",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,59 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class BrusherProfileSchedulerId : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_BrusherProfile_Schedule_ScheduleOwnerId",
|
||||
table: "BrusherProfile");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ScheduleOwnerId",
|
||||
table: "BrusherProfile",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_BrusherProfile_Schedule_ScheduleOwnerId",
|
||||
table: "BrusherProfile",
|
||||
column: "ScheduleOwnerId",
|
||||
principalTable: "Schedule",
|
||||
principalColumn: "OwnerId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_BrusherProfile_Schedule_ScheduleOwnerId",
|
||||
table: "BrusherProfile");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ScheduleOwnerId",
|
||||
table: "BrusherProfile",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_BrusherProfile_Schedule_ScheduleOwnerId",
|
||||
table: "BrusherProfile",
|
||||
column: "ScheduleOwnerId",
|
||||
principalTable: "Schedule",
|
||||
principalColumn: "OwnerId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,38 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class blogPostPub : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "blogspotPublications",
|
||||
columns: table => new
|
||||
{
|
||||
BlogpostId = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_blogspotPublications", x => x.BlogpostId);
|
||||
table.ForeignKey(
|
||||
name: "FK_blogspotPublications_BlogSpot_BlogpostId",
|
||||
column: x => x.BlogpostId,
|
||||
principalTable: "BlogSpot",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "blogspotPublications");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,79 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class blogPusb2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_blogspotPublications_BlogSpot_BlogpostId",
|
||||
table: "blogspotPublications");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_blogspotPublications",
|
||||
table: "blogspotPublications");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "blogspotPublications",
|
||||
newName: "blogSpotPublications");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Publish",
|
||||
table: "BlogSpot",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_blogSpotPublications",
|
||||
table: "blogSpotPublications",
|
||||
column: "BlogpostId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_blogSpotPublications_BlogSpot_BlogpostId",
|
||||
table: "blogSpotPublications",
|
||||
column: "BlogpostId",
|
||||
principalTable: "BlogSpot",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_blogSpotPublications_BlogSpot_BlogpostId",
|
||||
table: "blogSpotPublications");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_blogSpotPublications",
|
||||
table: "blogSpotPublications");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Publish",
|
||||
table: "BlogSpot");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "blogSpotPublications",
|
||||
newName: "blogspotPublications");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_blogspotPublications",
|
||||
table: "blogspotPublications",
|
||||
column: "BlogpostId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_blogspotPublications_BlogSpot_BlogpostId",
|
||||
table: "blogspotPublications",
|
||||
column: "BlogpostId",
|
||||
principalTable: "BlogSpot",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class blogPub2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,54 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class commentAllowed : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Comment",
|
||||
table: "CircleAuthorizationToBlogPost",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Avatar",
|
||||
table: "AspNetUsers",
|
||||
type: "character varying(512)",
|
||||
maxLength: 512,
|
||||
nullable: true,
|
||||
defaultValue: "/images/Users/icon_user.png",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "character varying(512)",
|
||||
oldMaxLength: 512,
|
||||
oldDefaultValue: "/images/Users/icon_user.png");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Comment",
|
||||
table: "CircleAuthorizationToBlogPost");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Avatar",
|
||||
table: "AspNetUsers",
|
||||
type: "character varying(512)",
|
||||
maxLength: 512,
|
||||
nullable: false,
|
||||
defaultValue: "/images/Users/icon_user.png",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "character varying(512)",
|
||||
oldMaxLength: 512,
|
||||
oldNullable: true,
|
||||
oldDefaultValue: "/images/Users/icon_user.png");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,29 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class noPublishColumn : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Publish",
|
||||
table: "BlogSpot");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Publish",
|
||||
table: "BlogSpot",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace Yavsc.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.7")
|
||||
.HasAnnotation("ProductVersion", "9.0.6")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@ -303,9 +303,6 @@ namespace Yavsc.Migrations
|
||||
b.Property<long>("BlogPostId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("Comment")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.HasKey("CircleId", "BlogPostId");
|
||||
|
||||
b.HasIndex("BlogPostId");
|
||||
@ -341,6 +338,7 @@ namespace Yavsc.Migrations
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Avatar")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)")
|
||||
@ -859,16 +857,6 @@ namespace Yavsc.Migrations
|
||||
b.ToTable("Comment");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b =>
|
||||
{
|
||||
b.Property<long>("BlogpostId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("BlogpostId");
|
||||
|
||||
b.ToTable("blogSpotPublications");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b =>
|
||||
{
|
||||
b.Property<string>("OwnerId")
|
||||
@ -1135,6 +1123,7 @@ namespace Yavsc.Migrations
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("ScheduleOwnerId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal>("ShampooPrice")
|
||||
@ -2092,6 +2081,7 @@ namespace Yavsc.Migrations
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("ModeratorGroupName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
@ -2102,18 +2092,22 @@ namespace Yavsc.Migrations
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Photo")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Rate")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("SettingsClassName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserCreated")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserModified")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Code");
|
||||
@ -2775,17 +2769,6 @@ namespace Yavsc.Migrations
|
||||
b.Navigation("Post");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.Blog.BlogPost", "BlogPost")
|
||||
.WithMany()
|
||||
.HasForeignKey("BlogpostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("BlogPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.ApplicationUser", "Owner")
|
||||
@ -2857,7 +2840,9 @@ namespace Yavsc.Migrations
|
||||
{
|
||||
b.HasOne("Yavsc.Models.Calendar.Schedule", "Schedule")
|
||||
.WithMany()
|
||||
.HasForeignKey("ScheduleOwnerId");
|
||||
.HasForeignKey("ScheduleOwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "BaseProfile")
|
||||
.WithMany()
|
||||
@ -3392,7 +3377,8 @@ namespace Yavsc.Migrations
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Navigation("AccountBalance");
|
||||
b.Navigation("AccountBalance")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("BankInfo");
|
||||
|
||||
|
@ -8,8 +8,8 @@ namespace Yavsc.Models.Access
|
||||
|
||||
public class CircleAuthorizationToBlogPost : ICircleAuthorization
|
||||
{
|
||||
public long CircleId { get; set; }
|
||||
public long BlogPostId { get; set; }
|
||||
public long CircleId { get; set; }
|
||||
public long BlogPostId { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[ForeignKey("BlogPostId")]
|
||||
@ -17,10 +17,7 @@ namespace Yavsc.Models.Access
|
||||
|
||||
[JsonIgnore]
|
||||
[ForeignKey("CircleId")]
|
||||
public virtual Circle Allowed { get; set; }
|
||||
|
||||
public bool Comment { get; set; }
|
||||
|
||||
public virtual Circle Allowed { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using Yavsc.Models.Haircut;
|
||||
using Yavsc.Models.IT.Evolution;
|
||||
using Yavsc.Models.IT.Fixing;
|
||||
@ -6,6 +10,7 @@ using Yavsc.Server.Models.EMailing;
|
||||
using Yavsc.Server.Models.IT.SourceCode;
|
||||
using Yavsc.Server.Models.IT;
|
||||
using Yavsc.Models.Streaming;
|
||||
using Yavsc.Models.Musical;
|
||||
|
||||
namespace Yavsc.Models
|
||||
{
|
||||
@ -27,6 +32,7 @@ namespace Yavsc.Models
|
||||
using Attributes;
|
||||
using Bank;
|
||||
using Payment;
|
||||
using Calendar;
|
||||
using Blog;
|
||||
using Yavsc.Abstract.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -35,20 +41,17 @@ namespace Yavsc.Models
|
||||
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Yavsc.Abstract.Models.Messaging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Configuration;
|
||||
|
||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
||||
{
|
||||
private readonly ILogger<ApplicationDbContext> logger;
|
||||
|
||||
|
||||
public ApplicationDbContext(ILoggerFactory loggerFactory,
|
||||
DbContextOptions<ApplicationDbContext> options) : base(options)
|
||||
public ApplicationDbContext()
|
||||
{
|
||||
logger = loggerFactory.CreateLogger<ApplicationDbContext>();
|
||||
|
||||
}
|
||||
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
||||
{
|
||||
|
||||
}
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
@ -58,7 +61,7 @@ namespace Yavsc.Models
|
||||
builder.Entity<Contact>().HasKey(x => new { x.OwnerId, x.UserId });
|
||||
builder.Entity<DeviceDeclaration>().Property(x => x.DeclarationDate).HasDefaultValueSql("LOCALTIMESTAMP");
|
||||
builder.Entity<BlogTag>().HasKey(x => new { x.PostId, x.TagId });
|
||||
|
||||
|
||||
builder.Entity<ApplicationUser>().Property(u => u.FullName).IsRequired(false);
|
||||
builder.Entity<ApplicationUser>().Property(u => u.DedicatedGoogleCalendar).IsRequired(false);
|
||||
builder.Entity<ApplicationUser>().HasMany<ChatConnection>(c => c.Connections);
|
||||
@ -78,7 +81,7 @@ namespace Yavsc.Models
|
||||
builder.Entity<Models.Cratie.Option>().HasKey(o => new { o.Code, o.CodeScrutin });
|
||||
builder.Entity<Notification>().Property(n => n.icon).HasDefaultValue("exclam");
|
||||
builder.Entity<ChatRoomAccess>().HasKey(p => new { room = p.ChannelName, user = p.UserId });
|
||||
builder.Entity<InstrumentRating>().HasAlternateKey(i => new { Instrument = i.InstrumentId, owner = i.OwnerId });
|
||||
builder.Entity<InstrumentRating>().HasAlternateKey(i => new { Instrument= i.InstrumentId, owner = i.OwnerId });
|
||||
|
||||
foreach (var et in builder.Model.GetEntityTypes())
|
||||
{
|
||||
@ -86,8 +89,25 @@ namespace Yavsc.Models
|
||||
et.FindProperty("DateCreated").SetAfterSaveBehavior(Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Ignore);
|
||||
}
|
||||
|
||||
builder.Entity<Activity>().Property(a => a.ParentCode).IsRequired(false);
|
||||
// builder.Entity<IdentityUserLogin<String>>().HasKey(i=> new { i.LoginProvider, i.UserId, i.ProviderKey });
|
||||
builder.Entity<Activity>().Property(a=>a.ParentCode).IsRequired(false);
|
||||
//builder.Entity<BlogPost>().HasOne(p => p.Author).WithMany(a => a.Posts);
|
||||
|
||||
}
|
||||
|
||||
// this is not a failback procedure.
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
var appSetup = (string) AppDomain.CurrentDomain.GetData(Constants.YavscConnectionStringEnvName);
|
||||
if (!string.IsNullOrWhiteSpace(appSetup))
|
||||
{
|
||||
optionsBuilder.UseNpgsql(appSetup);
|
||||
return;
|
||||
}
|
||||
var envSetup = Environment.GetEnvironmentVariable(Constants.YavscConnectionStringEnvName);
|
||||
if (envSetup!=null)
|
||||
optionsBuilder.UseNpgsql(envSetup);
|
||||
else optionsBuilder.UseNpgsql();
|
||||
}
|
||||
|
||||
public DbSet<Client> Applications { get; set; }
|
||||
@ -105,7 +125,7 @@ namespace Yavsc.Models
|
||||
/// Users posts
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DbSet<BlogPost> BlogSpot { get; set; }
|
||||
public DbSet<Blog.BlogPost> BlogSpot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Skills powered by this site
|
||||
@ -206,14 +226,14 @@ namespace Yavsc.Models
|
||||
((ITrackedEntity)entity.Entity).UserModified = userId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int SaveChanges(string userId)
|
||||
{
|
||||
AddTimestamps(userId);
|
||||
return base.SaveChanges();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<int> SaveChangesAsync(string userId, CancellationToken ctoken = default(CancellationToken))
|
||||
{
|
||||
@ -262,7 +282,7 @@ namespace Yavsc.Models
|
||||
public DbSet<Comment> Comment { get; set; }
|
||||
|
||||
public DbSet<Announce> Announce { get; set; }
|
||||
|
||||
|
||||
// TODO remove and opt for for memory only storing,
|
||||
// as long as it must be set empty each time the service is restarted,
|
||||
// and that chatting should be kept as must as possible independent from db context
|
||||
@ -275,7 +295,7 @@ namespace Yavsc.Models
|
||||
public DbSet<GitRepositoryReference> GitRepositoryReference { get; set; }
|
||||
|
||||
public DbSet<Project> Project { get; set; }
|
||||
|
||||
|
||||
[Obsolete("use signaled flows")]
|
||||
public DbSet<LiveFlow> LiveFlow { get; set; }
|
||||
|
||||
@ -284,8 +304,6 @@ namespace Yavsc.Models
|
||||
public DbSet<InstrumentRating> InstrumentRating { get; set; }
|
||||
|
||||
public DbSet<Scope> Scopes { get; set; }
|
||||
|
||||
public DbSet<BlogSpotPublication> blogSpotPublications{ get; set; }
|
||||
// public DbSet<IdentityUserLogin<String>> AspNetUserLogins { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,10 @@ using Yavsc.Models.Identity;
|
||||
using Yavsc.Models.Chat;
|
||||
using Yavsc.Models.Bank;
|
||||
using Yavsc.Models.Access;
|
||||
using Yavsc.Abstract.Identity;
|
||||
|
||||
namespace Yavsc.Models
|
||||
{
|
||||
[Table("AspNetUsers")]
|
||||
public class ApplicationUser : IdentityUser, IApplicationUser
|
||||
public class ApplicationUser : IdentityUser
|
||||
{
|
||||
/// <summary>
|
||||
/// Another me, as a byte array.TG7@Eu%80rufzkhbb
|
||||
@ -24,10 +22,10 @@ namespace Yavsc.Models
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MaxLength(512)]
|
||||
public string? Avatar { get; set; }
|
||||
public string Avatar { get; set; }
|
||||
|
||||
[MaxLength(512)]
|
||||
public string? FullName { get; set; }
|
||||
public string FullName { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -35,31 +33,32 @@ namespace Yavsc.Models
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Display(Name = "Account balance")]
|
||||
public virtual AccountBalance? AccountBalance { get; set; }
|
||||
public virtual AccountBalance AccountBalance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User's posts
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[InverseProperty("Author"), JsonIgnore]
|
||||
public virtual List<Blog.BlogPost>? Posts { get; set; }
|
||||
public virtual List<Blog.BlogPost> Posts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User's contact list
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[InverseProperty("Owner"), JsonIgnore]
|
||||
public virtual List<Contact>? Book { get; set; }
|
||||
public virtual List<Contact> Book { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// External devices using the API
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[InverseProperty("DeviceOwner"), JsonIgnore]
|
||||
public virtual List<DeviceDeclaration>? DeviceDeclaration { get; set; }
|
||||
public virtual List<DeviceDeclaration> DeviceDeclaration { get; set; }
|
||||
|
||||
[InverseProperty("Owner"), JsonIgnore]
|
||||
public virtual List<ChatConnection>? Connections { get; set; }
|
||||
public virtual List<ChatConnection> Connections { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// User's circles
|
||||
@ -67,7 +66,7 @@ namespace Yavsc.Models
|
||||
/// <returns></returns>
|
||||
[InverseProperty("Owner"), JsonIgnore]
|
||||
|
||||
public virtual List<Circle>? Circles { get; set; }
|
||||
public virtual List<Circle> Circles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Billing postal address
|
||||
@ -82,14 +81,14 @@ namespace Yavsc.Models
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MaxLength(512)]
|
||||
public string? DedicatedGoogleCalendar { get; set; }
|
||||
public string DedicatedGoogleCalendar { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Id + " " + this.AccountBalance?.Credits.ToString() + this.Email + " " + this.UserName + " $" + this.AccountBalance?.Credits.ToString();
|
||||
}
|
||||
|
||||
public virtual List<BankIdentity>? BankInfo { get; set; }
|
||||
public virtual List<BankIdentity> BankInfo { get; set; }
|
||||
|
||||
public long DiskQuota { get; set; } = 512 * 1024 * 1024;
|
||||
public long DiskUsage { get; set; } = 0;
|
||||
@ -98,24 +97,21 @@ namespace Yavsc.Models
|
||||
|
||||
[JsonIgnore]
|
||||
[InverseProperty("Owner")]
|
||||
public virtual List<BlackListed>? BlackList { get; set; }
|
||||
public virtual List<BlackListed> BlackList { get; set; }
|
||||
|
||||
public bool AllowMonthlyEmail { get; set; } = false;
|
||||
|
||||
[JsonIgnore]
|
||||
[InverseProperty("Owner")]
|
||||
public virtual List<ChatRoom>? Rooms { get; set; }
|
||||
public virtual List<ChatRoom> Rooms { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[InverseProperty("User")]
|
||||
public virtual List<ChatRoomAccess>? RoomAccess { get; set; }
|
||||
public virtual List<ChatRoomAccess> RoomAccess { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[InverseProperty("Member")]
|
||||
public virtual List<CircleMember>? Membership { get; set; }
|
||||
public virtual List<CircleMember> Membership { get; set; }
|
||||
|
||||
IAccountBalance? IApplicationUser.AccountBalance => AccountBalance;
|
||||
|
||||
ILocation? IApplicationUser.PostalAddress { get => PostalAddress; }
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
using Yavsc;
|
||||
using Yavsc.Abstract.Identity;
|
||||
using Yavsc.Abstract.Identity.Security;
|
||||
using Yavsc.Attributes.Validation;
|
||||
using Yavsc.Interfaces;
|
||||
@ -16,8 +14,7 @@ using Yavsc.ViewModels.Blog;
|
||||
|
||||
namespace Yavsc.Models.Blog
|
||||
{
|
||||
public class BlogPost : BlogPostBase,
|
||||
IBlogPost, ICircleAuthorized, ITaggable<long>
|
||||
public class BlogPost : BlogPostInputViewModel, IBlogPost, ICircleAuthorized, ITaggable<long>, IIdentified<long>
|
||||
{
|
||||
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
[Display(Name="Identifiant du post")]
|
||||
@ -88,6 +85,7 @@ namespace Yavsc.Models.Blog
|
||||
[InverseProperty("Post")]
|
||||
public virtual List<Comment> Comments { get; set; }
|
||||
|
||||
IApplicationUser IBlogPost.Author { get => this.Author; }
|
||||
[NotMapped]
|
||||
public string OwnerId => AuthorId;
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Yavsc.Models.Blog;
|
||||
|
||||
namespace Yavsc.Models
|
||||
{
|
||||
public class BlogSpotPublication
|
||||
{
|
||||
[Key]
|
||||
public long BlogpostId { get; set; }
|
||||
|
||||
[ForeignKey("BlogpostId")]
|
||||
public virtual BlogPost BlogPost{ get; set; }
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ namespace Yavsc.Models.Haircut
|
||||
|
||||
[DisplayFormat(ConvertEmptyStringToNull = true, NullDisplayText = "[Pas d'emploi du temps spécifié]")]
|
||||
[Display(Name="Emploi du temps")]
|
||||
public virtual Schedule? Schedule { get; set; }
|
||||
public virtual Schedule Schedule { get; set; }
|
||||
|
||||
[Display(Name="Coupe femme cheveux longs"),DisplayFormat(DataFormatString="{0:C}")]
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
namespace Yavsc.Models.Auth
|
||||
{
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
public class YaIdentityUserLogin
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the login provider for the login (e.g. facebook, google)
|
||||
/// </summary>
|
||||
public virtual string LoginProvider { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unique provider identifier for this login.
|
||||
/// </summary>
|
||||
public virtual string ProviderKey { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the friendly name used in a UI for this login.
|
||||
/// </summary>
|
||||
public virtual string? ProviderDisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the primary key of the user associated with this login.
|
||||
/// </summary>
|
||||
public String UserId { get; set; } = default!;
|
||||
|
||||
[ForeignKey("UserId")]
|
||||
public virtual ApplicationUser User { get; set; }
|
||||
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ namespace Yavsc.Models.Workflow
|
||||
[YaStringLength(512)]
|
||||
[Display(Name = "Code du parent")]
|
||||
[ForeignKey("Parent")]
|
||||
public string? ParentCode { get; set; }
|
||||
public string ParentCode { get; set; }
|
||||
|
||||
[Display(Name = "Activité parent"), JsonIgnore]
|
||||
public virtual Activity Parent { get; set; }
|
||||
@ -41,7 +41,7 @@ namespace Yavsc.Models.Workflow
|
||||
public string Description { get; set; }
|
||||
|
||||
[Display(Name = "Photo")]
|
||||
public string? Photo { get; set; }
|
||||
public string Photo { get; set; }
|
||||
|
||||
[InverseProperty("Context")]
|
||||
[DisplayAttribute(Name = "Services liés")]
|
||||
@ -52,7 +52,7 @@ namespace Yavsc.Models.Workflow
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DisplayAttribute(Name = "Groupe de modération")]
|
||||
public string? ModeratorGroupName { get; set; }
|
||||
public string ModeratorGroupName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// indice de recherche de cette activité
|
||||
@ -64,7 +64,7 @@ namespace Yavsc.Models.Workflow
|
||||
[DisplayFormatAttribute(DataFormatString="{0}%")]
|
||||
public int Rate { get; set; }
|
||||
[DisplayAttribute(Name = "Classe de paramétrage")]
|
||||
public string? SettingsClassName { get; set; }
|
||||
public string SettingsClassName { get; set; }
|
||||
|
||||
[InverseProperty("Context")]
|
||||
[Display(Name="Formulaires de commande")]
|
||||
@ -77,7 +77,7 @@ namespace Yavsc.Models.Workflow
|
||||
}
|
||||
|
||||
[Display(Name="Createur")]
|
||||
public string? UserCreated
|
||||
public string UserCreated
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
@ -89,7 +89,7 @@ namespace Yavsc.Models.Workflow
|
||||
}
|
||||
|
||||
[Display(Name="Utilisateur ayant modifié le dernier")]
|
||||
public string? UserModified
|
||||
public string UserModified
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ namespace Yavsc.Models.societe.com
|
||||
public class CompanyInfoMessage
|
||||
{
|
||||
public bool success { get; set; }
|
||||
public string? errorType { get; set; }
|
||||
public string? errorCode { get; set; }
|
||||
public string? errorMessage { get; set; }
|
||||
public CompanyInfo? result { get; set; }
|
||||
public string errorType { get; set; }
|
||||
public string errorCode { get; set; }
|
||||
public string errorMessage { get; set; }
|
||||
public CompanyInfo result { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
using Yavsc.Models.Blog;
|
||||
|
||||
public class BlogPostEdition
|
||||
{
|
||||
public string Content { get; internal set; }
|
||||
public string Title { get; internal set; }
|
||||
public string Photo { get; internal set; }
|
||||
|
||||
internal static BlogPostEdition From(BlogPost blog)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Blog;
|
||||
using Yavsc.Server.Exceptions;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
using Yavsc.ViewModels.Blog;
|
||||
|
||||
public class BlogSpotService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
|
||||
|
||||
public BlogSpotService(ApplicationDbContext context,
|
||||
IAuthorizationService authorizationService)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public BlogPost Create(string userId, BlogPostEditViewModel blogInput)
|
||||
{
|
||||
BlogPost post = new BlogPost
|
||||
{
|
||||
Title = blogInput.Title,
|
||||
Content = blogInput.Content,
|
||||
Photo = blogInput.Photo,
|
||||
AuthorId = userId
|
||||
};
|
||||
_context.BlogSpot.Add(post);
|
||||
_context.SaveChanges(userId);
|
||||
return post;
|
||||
}
|
||||
public async Task<BlogPostEditViewModel> GetPostForEdition(ClaimsPrincipal user, long blogPostId)
|
||||
{
|
||||
var blog = await _context.BlogSpot.Include(x => x.Author).Include(x => x.ACL).SingleAsync(m => m.Id == blogPostId);
|
||||
var auth = await _authorizationService.AuthorizeAsync(user, blog, new EditPermission());
|
||||
if (!auth.Succeeded)
|
||||
{
|
||||
throw new AuthorizationFailureException(auth);
|
||||
}
|
||||
return BlogPostEditViewModel.From(blog);
|
||||
}
|
||||
|
||||
public async Task<BlogPost> Details(ClaimsPrincipal user, long blogPostId)
|
||||
{
|
||||
|
||||
BlogPost blog = await _context.BlogSpot
|
||||
.Include(p => p.Author)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Comments)
|
||||
.Include(p => p.ACL)
|
||||
.SingleAsync(m => m.Id == blogPostId);
|
||||
if (blog == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var auth = await _authorizationService.AuthorizeAsync(user, blog, new ReadPermission());
|
||||
if (!auth.Succeeded)
|
||||
{
|
||||
throw new AuthorizationFailureException(auth);
|
||||
}
|
||||
foreach (var c in blog.Comments)
|
||||
{
|
||||
c.Author = _context.Users.First(u => u.Id == c.AuthorId);
|
||||
}
|
||||
return blog;
|
||||
}
|
||||
|
||||
public async Task Modify(ClaimsPrincipal user, BlogPostEditViewModel blogEdit)
|
||||
{
|
||||
var blog = _context.BlogSpot.SingleOrDefault(b => b.Id == blogEdit.Id);
|
||||
Debug.Assert(blog != null);
|
||||
var auth = await _authorizationService.AuthorizeAsync(user, blog, new EditPermission());
|
||||
if (!auth.Succeeded)
|
||||
{
|
||||
throw new AuthorizationFailureException(auth);
|
||||
}
|
||||
blog.Content = blogEdit.Content;
|
||||
blog.Title = blogEdit.Title;
|
||||
blog.Photo = blogEdit.Photo;
|
||||
blog.ACL = blogEdit.ACL;
|
||||
// saves the change
|
||||
_context.Update(blog);
|
||||
var publication = await _context.blogSpotPublications.SingleOrDefaultAsync
|
||||
(p=>p.BlogpostId==blogEdit.Id);
|
||||
if (publication != null)
|
||||
{
|
||||
if (!blogEdit.Publish)
|
||||
{
|
||||
_context.blogSpotPublications.Remove(publication);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (blogEdit.Publish)
|
||||
{
|
||||
_context.blogSpotPublications.Add(
|
||||
new BlogSpotPublication
|
||||
{
|
||||
BlogpostId = blogEdit.Id
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
_context.SaveChanges(user.GetUserId());
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<IBlogPost>> Index(ClaimsPrincipal user, string id, int skip = 0, int take = 25)
|
||||
{
|
||||
IEnumerable<IBlogPost> posts;
|
||||
|
||||
if (user.Identity.IsAuthenticated)
|
||||
{
|
||||
string viewerId = user.GetUserId();
|
||||
long[] userCircles = await _context.Circle.Include(c => c.Members).
|
||||
Where(c => c.Members.Any(m => m.MemberId == viewerId))
|
||||
.Select(c => c.Id).ToArrayAsync();
|
||||
|
||||
posts = _context.BlogSpot
|
||||
.Include(b => b.Author)
|
||||
.Include(p => p.ACL)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Comments)
|
||||
.Where(p => p.ACL == null
|
||||
|| p.ACL.Count == 0
|
||||
|| (p.AuthorId == viewerId)
|
||||
|| (userCircles != null &&
|
||||
p.ACL.Any(a => userCircles.Contains(a.CircleId)))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
posts = _context.blogSpotPublications
|
||||
.Include(p => p.BlogPost)
|
||||
.Include(b => b.BlogPost.Author)
|
||||
.Include(p => p.BlogPost.ACL)
|
||||
.Include(p => p.BlogPost.Tags)
|
||||
.Include(p => p.BlogPost.Comments)
|
||||
.Where(p => p.BlogPost.ACL == null
|
||||
|| p.BlogPost.ACL.Count == 0)
|
||||
.Select(p => p.BlogPost).ToArray();
|
||||
}
|
||||
|
||||
var data = posts.OrderByDescending(p => p.DateModified);
|
||||
return data;
|
||||
}
|
||||
|
||||
public async Task Delete(ClaimsPrincipal user, long id)
|
||||
{
|
||||
var uid = user.GetUserId();
|
||||
BlogPost blog = _context.BlogSpot.Single(m => m.Id == id);
|
||||
|
||||
_context.BlogSpot.Remove(blog);
|
||||
_context.SaveChanges(user.GetUserId());
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BlogPost>> UserPosts(
|
||||
string posterName,
|
||||
string? readerId,
|
||||
int pageLen = 10,
|
||||
int pageNum = 0)
|
||||
{
|
||||
string? posterId = (await _context.Users.SingleOrDefaultAsync(u => u.UserName == posterName))?.Id ?? null;
|
||||
if (posterId == null) return Array.Empty<BlogPost>();
|
||||
return _context.UserPosts(posterId, readerId);
|
||||
}
|
||||
|
||||
public object? GetTitle(string title)
|
||||
{
|
||||
return _context.BlogSpot.Include(
|
||||
b => b.Author
|
||||
).Where(x => x.Title == title).OrderByDescending(
|
||||
x => x.DateCreated
|
||||
).ToList();
|
||||
}
|
||||
|
||||
public async Task<BlogPost?> GetBlogPostAsync(long value)
|
||||
{
|
||||
return await _context.BlogSpot
|
||||
.Include(b => b.Author)
|
||||
.Include(b => b.ACL)
|
||||
.SingleOrDefaultAsync(x => x.Id == value);
|
||||
}
|
||||
|
||||
}
|
@ -130,6 +130,7 @@ namespace Yavsc.Services
|
||||
public bool Part(string cxId, string roomName, string reason)
|
||||
{
|
||||
ChatRoomInfo chanInfo;
|
||||
var userName = ChatUserNames[cxId];
|
||||
if (Channels.TryGetValue(roomName, out chanInfo))
|
||||
{
|
||||
if (!chanInfo.Users.Contains(cxId))
|
||||
|
@ -1,9 +1,13 @@
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
using Yavsc.Abstract.Manage;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Yavsc.Interface;
|
||||
using Yavsc.Settings;
|
||||
@ -23,7 +27,8 @@ namespace Yavsc.Services
|
||||
public MailSender(
|
||||
IOptions<SiteSettings> sitesOptions,
|
||||
IOptions<SmtpSettings> smtpOptions,
|
||||
ILoggerFactory loggerFactory
|
||||
ILoggerFactory loggerFactory,
|
||||
IStringLocalizer<Yavsc.YavscLocalization> localizer
|
||||
)
|
||||
{
|
||||
this.localizer = localizer;
|
||||
|
@ -57,18 +57,18 @@ namespace Yavsc.Services
|
||||
var roles = await this._userManager.GetRolesAsync(user);
|
||||
if (roles.Count()>0)
|
||||
{
|
||||
claims.AddRange(roles.Select(r => new Claim(JwtClaimTypes.Role, r)));
|
||||
claims.AddRange(roles.Select(r => new Claim(Constants.RoleClaimName, r)));
|
||||
}
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
||||
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
||||
{
|
||||
var subjectId = GetSubjectId(context.Subject);
|
||||
if (subjectId == null) return;
|
||||
if (subjectId==null) return;
|
||||
var user = await _userManager.FindByIdAsync(subjectId);
|
||||
if (user == null) return;
|
||||
if (user==null) return ;
|
||||
context.IssuedClaims = await GetClaimsFromUserAsync(context, user);
|
||||
}
|
||||
|
||||
|
@ -6,9 +6,7 @@ namespace Yavsc
|
||||
public class SiteSettings
|
||||
{
|
||||
public string Title { get; set; } = "Yavsc";
|
||||
|
||||
public string Slogan { get; set; } = "";
|
||||
public string Banner { get; set; } = "";
|
||||
|
||||
public string StyleSheet { get; set; } = "site.css";
|
||||
public string FavIcon { get; set; } = "favicon.ico";
|
||||
|
@ -1,50 +1,11 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Yavsc.Models.Blog;
|
||||
|
||||
namespace Yavsc.ViewModels.Blog;
|
||||
|
||||
|
||||
public class BlogPostCreateViewModel : BlogPostBase
|
||||
{
|
||||
public bool Publish { get; set; }
|
||||
}
|
||||
|
||||
public class BlogPostEditViewModel : BlogPostCreateViewModel
|
||||
public class BlogPostEditViewModel : BlogPostInputViewModel
|
||||
{
|
||||
|
||||
[Required]
|
||||
|
||||
public required long Id { get; set; }
|
||||
|
||||
|
||||
public BlogPostEditViewModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static BlogPostEditViewModel From(BlogPost blogInput)
|
||||
{
|
||||
return new BlogPostEditViewModel
|
||||
{
|
||||
Id = blogInput.Id,
|
||||
Title = blogInput.Title,
|
||||
Publish = false,
|
||||
Photo = blogInput.Photo,
|
||||
Content = blogInput.Content,
|
||||
ACL = blogInput.ACL
|
||||
};
|
||||
}
|
||||
public static BlogPostEditViewModel FromViewModel(BlogPostEditViewModel blogInput)
|
||||
{
|
||||
return new BlogPostEditViewModel
|
||||
{
|
||||
Id = blogInput.Id,
|
||||
Title = blogInput.Title,
|
||||
Publish = false,
|
||||
Photo = blogInput.Photo,
|
||||
Content = blogInput.Content,
|
||||
ACL = blogInput.ACL
|
||||
};
|
||||
}
|
||||
public required long Id { get; set; }
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ using Yavsc.Models.Access;
|
||||
|
||||
namespace Yavsc.ViewModels.Blog
|
||||
{
|
||||
public class BlogPostBase
|
||||
{
|
||||
public class BlogPostInputViewModel
|
||||
{
|
||||
[StringLength(1024)]
|
||||
public string? Photo { get; set; }
|
||||
|
||||
@ -18,8 +18,8 @@ namespace Yavsc.ViewModels.Blog
|
||||
public string? Content { get; set; }
|
||||
|
||||
[InverseProperty("Target")]
|
||||
[Display(Name = "Liste de contrôle d'accès")]
|
||||
public virtual List<CircleAuthorizationToBlogPost>? ACL { get; set; }
|
||||
[Display(Name="Liste de contrôle d'accès")]
|
||||
public virtual List<CircleAuthorizationToBlogPost>? ACL { get; set; }
|
||||
|
||||
|
||||
}
|
@ -1,32 +1,39 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UserSecretsId>53bd70e8-ff81-497a-847f-a15fd8ea7a09</UserSecretsId>
|
||||
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
|
||||
<PackageReference Include="HigginsSoft.IdentityServer8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Features" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="HigginsSoft.IdentityServer8" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="5.0.17" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" />
|
||||
<PackageReference Include="Google.Apis.Calendar.v3" />
|
||||
<PackageReference Include="PayPalMerchantSDK" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
<PackageReference Include="RazorEngine.NetCore" />
|
||||
<PackageReference Include="MailKit" />
|
||||
<PackageReference Include="MimeKit" />
|
||||
<PackageReference Include="pazof.rules" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
||||
<PackageReference Include="Google.Apis.Calendar.v3" Version="1.60.0.2993" />
|
||||
<PackageReference Include="PayPalMerchantSDK" Version="2.16.250" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.9" />
|
||||
<PackageReference Include="RazorEngine.NetCore" Version="3.1.0" />
|
||||
<PackageReference Include="MailKit" Version="4.12.1" />
|
||||
<PackageReference Include="MimeKit" Version="4.12.0" />
|
||||
<PackageReference Include="pazof.rules" Version="1.1.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Yavsc.Abstract/Yavsc.Abstract.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
520
src/Yavsc.Server/ansi2html.sh
Executable file
520
src/Yavsc.Server/ansi2html.sh
Executable file
@ -0,0 +1,520 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Convert ANSI (terminal) colours and attributes to HTML
|
||||
|
||||
# Licence: LGPLv2
|
||||
# Author:
|
||||
# http://www.pixelbeat.org/docs/terminal_colours/
|
||||
# Examples:
|
||||
# ls -l --color=always | ansi2html.sh > ls.html
|
||||
# git show --color | ansi2html.sh > last_change.html
|
||||
# Generally one can use the `script` util to capture full terminal output.
|
||||
# Changes:
|
||||
# V0.1, 24 Apr 2008, Initial release
|
||||
# V0.2, 01 Jan 2009, Phil Harnish <philharnish@gmail.com>
|
||||
# Support `git diff --color` output by
|
||||
# matching ANSI codes that specify only
|
||||
# bold or background colour.
|
||||
# P@draigBrady.com
|
||||
# Support `ls --color` output by stripping
|
||||
# redundant leading 0s from ANSI codes.
|
||||
# Support `grep --color=always` by stripping
|
||||
# unhandled ANSI codes (specifically ^[[K).
|
||||
# V0.3, 20 Mar 2009, http://eexpress.blog.ubuntu.org.cn/
|
||||
# Remove cat -v usage which mangled non ascii input.
|
||||
# Cleanup regular expressions used.
|
||||
# Support other attributes like reverse, ...
|
||||
# P@draigBrady.com
|
||||
# Correctly nest <span> tags (even across lines).
|
||||
# Add a command line option to use a dark background.
|
||||
# Strip more terminal control codes.
|
||||
# V0.4, 17 Sep 2009, P@draigBrady.com
|
||||
# Handle codes with combined attributes and color.
|
||||
# Handle isolated <bold> attributes with css.
|
||||
# Strip more terminal control codes.
|
||||
# V0.24, 14 Sep 2017
|
||||
# http://github.com/pixelb/scripts/commits/master/scripts/ansi2html.sh
|
||||
|
||||
gawk --version >/dev/null || exit 1
|
||||
|
||||
if [ "$1" = "--version" ]; then
|
||||
printf '0.23\n' && exit
|
||||
fi
|
||||
|
||||
usage()
|
||||
{
|
||||
printf '%s\n' \
|
||||
'This utility converts ANSI codes in data passed to stdin
|
||||
It has 4 optional parameters:
|
||||
--bg=dark --palette=linux|solarized|tango|xterm --css-only|--body-only
|
||||
E.g.: ls -l --color=always | ansi2html.sh --bg=dark > ls.html' >&2
|
||||
exit
|
||||
}
|
||||
|
||||
if [ "$1" = "--help" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
processArg()
|
||||
{
|
||||
[ "$1" = "--bg=dark" ] && { dark_bg=yes; return; }
|
||||
[ "$1" = "--css-only" ] && { css_only=yes; return; }
|
||||
[ "$1" = "--body-only" ] && { body_only=yes; return; }
|
||||
if [ "$1" = "--palette=solarized" ]; then
|
||||
# See http://ethanschoonover.com/solarized
|
||||
P0=073642; P1=D30102; P2=859900; P3=B58900;
|
||||
P4=268BD2; P5=D33682; P6=2AA198; P7=EEE8D5;
|
||||
P8=002B36; P9=CB4B16; P10=586E75; P11=657B83;
|
||||
P12=839496; P13=6C71C4; P14=93A1A1; P15=FDF6E3;
|
||||
return;
|
||||
elif [ "$1" = "--palette=solarized-xterm" ]; then
|
||||
# Above mapped onto the xterm 256 color palette
|
||||
P0=262626; P1=AF0000; P2=5F8700; P3=AF8700;
|
||||
P4=0087FF; P5=AF005F; P6=00AFAF; P7=E4E4E4;
|
||||
P8=1C1C1C; P9=D75F00; P10=585858; P11=626262;
|
||||
P12=808080; P13=5F5FAF; P14=8A8A8A; P15=FFFFD7;
|
||||
return;
|
||||
elif [ "$1" = "--palette=tango" ]; then
|
||||
# Gnome default
|
||||
P0=000000; P1=CC0000; P2=4E9A06; P3=C4A000;
|
||||
P4=3465A4; P5=75507B; P6=06989A; P7=D3D7CF;
|
||||
P8=555753; P9=EF2929; P10=8AE234; P11=FCE94F;
|
||||
P12=729FCF; P13=AD7FA8; P14=34E2E2; P15=EEEEEC;
|
||||
return;
|
||||
elif [ "$1" = "--palette=xterm" ]; then
|
||||
P0=000000; P1=CD0000; P2=00CD00; P3=CDCD00;
|
||||
P4=0000EE; P5=CD00CD; P6=00CDCD; P7=E5E5E5;
|
||||
P8=7F7F7F; P9=FF0000; P10=00FF00; P11=FFFF00;
|
||||
P12=5C5CFF; P13=FF00FF; P14=00FFFF; P15=FFFFFF;
|
||||
return;
|
||||
else # linux console
|
||||
P0=000000; P1=AA0000; P2=00AA00; P3=AA5500;
|
||||
P4=0000AA; P5=AA00AA; P6=00AAAA; P7=AAAAAA;
|
||||
P8=555555; P9=FF5555; P10=55FF55; P11=FFFF55;
|
||||
P12=5555FF; P13=FF55FF; P14=55FFFF; P15=FFFFFF;
|
||||
[ "$1" = "--palette=linux" ] && return;
|
||||
fi
|
||||
}
|
||||
|
||||
processArg #defaults
|
||||
for var in "$@"; do processArg $var; done
|
||||
[ "$css_only" ] && [ "$body_only" ] && usage
|
||||
|
||||
# Mac OSX's GNU sed is installed as gsed
|
||||
# use e.g. homebrew 'gnu-sed' to get it
|
||||
if ! sed --version >/dev/null 2>&1; then
|
||||
if gsed --version >/dev/null 2>&1; then
|
||||
alias sed=gsed
|
||||
else
|
||||
echo "Error, can't find an acceptable GNU sed." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
[ "$css_only" ] || [ "$body_only" ] || printf '%s' "<html>
|
||||
<head>
|
||||
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
|
||||
<style type=\"text/css\">
|
||||
"
|
||||
[ "$body_only" ] || printf ".ef0,.f0 { color: #$P0; } .eb0,.b0 { background-color: #$P0; }
|
||||
.ef1,.f1 { color: #$P1; } .eb1,.b1 { background-color: #$P1; }
|
||||
.ef2,.f2 { color: #$P2; } .eb2,.b2 { background-color: #$P2; }
|
||||
.ef3,.f3 { color: #$P3; } .eb3,.b3 { background-color: #$P3; }
|
||||
.ef4,.f4 { color: #$P4; } .eb4,.b4 { background-color: #$P4; }
|
||||
.ef5,.f5 { color: #$P5; } .eb5,.b5 { background-color: #$P5; }
|
||||
.ef6,.f6 { color: #$P6; } .eb6,.b6 { background-color: #$P6; }
|
||||
.ef7,.f7 { color: #$P7; } .eb7,.b7 { background-color: #$P7; }
|
||||
.ef8, .f0 > .bold,.bold > .f0 { color: #$P8; font-weight: normal; }
|
||||
.ef9, .f1 > .bold,.bold > .f1 { color: #$P9; font-weight: normal; }
|
||||
.ef10,.f2 > .bold,.bold > .f2 { color: #$P10; font-weight: normal; }
|
||||
.ef11,.f3 > .bold,.bold > .f3 { color: #$P11; font-weight: normal; }
|
||||
.ef12,.f4 > .bold,.bold > .f4 { color: #$P12; font-weight: normal; }
|
||||
.ef13,.f5 > .bold,.bold > .f5 { color: #$P13; font-weight: normal; }
|
||||
.ef14,.f6 > .bold,.bold > .f6 { color: #$P14; font-weight: normal; }
|
||||
.ef15,.f7 > .bold,.bold > .f7 { color: #$P15; font-weight: normal; }
|
||||
.eb8 { background-color: #$P8; }
|
||||
.eb9 { background-color: #$P9; }
|
||||
.eb10 { background-color: #$P10; }
|
||||
.eb11 { background-color: #$P11; }
|
||||
.eb12 { background-color: #$P12; }
|
||||
.eb13 { background-color: #$P13; }
|
||||
.eb14 { background-color: #$P14; }
|
||||
.eb15 { background-color: #$P15; }
|
||||
"
|
||||
# The default xterm 256 colour palette
|
||||
for red in 0 1 2 3 4 5 ; do
|
||||
for green in 0 1 2 3 4 5 ; do
|
||||
for blue in 0 1 2 3 4 5 ; do
|
||||
c=$((16 + ($red * 36) + ($green * 6) + $blue))
|
||||
r=$((($red * 40 + 55) * ($red > 0)))
|
||||
g=$((($green * 40 + 55) * ($green > 0)))
|
||||
b=$((($blue * 40 + 55) * ($blue > 0)))
|
||||
[ "$body_only" ] || printf ".ef%d { color: #%2.2x%2.2x%2.2x; } " $c $r $g $b
|
||||
[ "$body_only" ] || printf ".eb%d { background-color: #%2.2x%2.2x%2.2x; }\n" $c $r $g $b
|
||||
done
|
||||
done
|
||||
done
|
||||
for gray in $(seq 0 23); do
|
||||
c=$(($gray+232))
|
||||
l=$(($gray*10 + 8))
|
||||
[ "$body_only" ] || printf ".ef%d { color: #%2.2x%2.2x%2.2x; } " $c $l $l $l
|
||||
[ "$body_only" ] || printf ".eb%d { background-color: #%2.2x%2.2x%2.2x; }\n" $c $l $l $l
|
||||
done
|
||||
|
||||
[ "$body_only" ] || printf '%s' '
|
||||
.f9 { color: '`[ "$dark_bg" ] && printf "#$P7;" || printf "#$P0;"`' }
|
||||
.b9 { background-color: #'`[ "$dark_bg" ] && printf $P0 || printf $P15`'; }
|
||||
.f9 > .bold,.bold > .f9, body.f9 > pre > .bold {
|
||||
/* Bold is heavy black on white, or bright white
|
||||
depending on the default background */
|
||||
color: '`[ "$dark_bg" ] && printf "#$P15;" || printf "#$P0;"`'
|
||||
font-weight: '`[ "$dark_bg" ] && printf 'normal;' || printf 'bold;'`'
|
||||
}
|
||||
.reverse {
|
||||
/* CSS does not support swapping fg and bg colours unfortunately,
|
||||
so just hardcode something that will look OK on all backgrounds. */
|
||||
'"color: #$P0; background-color: #$P7;"'
|
||||
}
|
||||
.underline { text-decoration: underline; }
|
||||
.line-through { text-decoration: line-through; }
|
||||
.blink { text-decoration: blink; }
|
||||
|
||||
/* Avoid pixels between adjacent span elements.
|
||||
Note this only works for lines less than 80 chars
|
||||
where we close span elements on the same line.
|
||||
span { display: inline-block; }
|
||||
*/
|
||||
'
|
||||
[ "$body_only" ] || [ "$css_only" ] && printf '%s\n' \
|
||||
'To use the css generated from --css-only, do: '\
|
||||
'<head><link rel="stylesheet" type="text/css" href="style.css"></head>' >&2
|
||||
[ "$css_only" ] && exit
|
||||
[ "$body_only" ] || printf '%s' '</style>
|
||||
</head>
|
||||
|
||||
<body class="f9 b9">
|
||||
<pre>
|
||||
'
|
||||
[ "$body_only" ] && printf '%s\n' 'Be sure to use <body class="f9 b9"> and <pre>' >&2
|
||||
|
||||
p='\x1b\[' #shortcut to match escape codes
|
||||
|
||||
# Handle various xterm control sequences.
|
||||
# See /usr/share/doc/xterm-*/ctlseqs.txt
|
||||
sed "
|
||||
# escape ampersand and quote
|
||||
s#&#\&#g; s#\"#\"#g;
|
||||
s#\x1b[^\x1b]*\x1b\\\##g # strip anything between \e and ST
|
||||
s#\x1b][0-9]*;[^\a]*\a##g # strip any OSC (xterm title etc.)
|
||||
|
||||
s#\r\$## # strip trailing \r
|
||||
|
||||
# strip other non SGR escape sequences
|
||||
s#[\x07]##g
|
||||
s#\x1b[]>=\][0-9;]*##g
|
||||
s#\x1bP+.\{5\}##g
|
||||
# Mark cursor positioning codes \"Jr;c;
|
||||
s#${p}\([0-9]\{1,2\}\)G#\"J;\1;#g
|
||||
s#${p}\([0-9]\{1,2\}\);\([0-9]\{1,2\}\)H#\"J\1;\2;#g
|
||||
|
||||
# Mark clear as \"Cn where n=1 is screen and n=0 is to end-of-line
|
||||
s#${p}H#\"C1;#g
|
||||
s#${p}K#\"C0;#g
|
||||
# Mark Cursor move columns as \"Mn where n is +ve for right, -ve for left
|
||||
s#${p}C#\"M1;#g
|
||||
s#${p}\([0-9]\{1,\}\)C#\"M\1;#g
|
||||
s#${p}\([0-9]\{1,\}\)D#\"M-\1;#g
|
||||
s#${p}\([0-9]\{1,\}\)P#\"X\1;#g
|
||||
|
||||
s#${p}[0-9;?]*[^0-9;?m]##g
|
||||
|
||||
" |
|
||||
|
||||
# Normalize the input before transformation
|
||||
sed "
|
||||
# escape HTML (ampersand and quote done above)
|
||||
s#>#\>#g; s#<#\<#g;
|
||||
|
||||
# handle truecolor
|
||||
s#${p}38;2;\([0-9]\{1,3\}\);\([0-9]\{1,3\}\);\([0-9]\{1,3\}\)m#\
|
||||
<span style=\"color:rgb(\1\,\2\,\3\)\">#g
|
||||
s#${p}48;2;\([0-9]\{1,3\}\);\([0-9]\{1,3\}\);\([0-9]\{1,3\}\)m#\
|
||||
<span style=\"background-color:rgb(\1\,\2\,\3\)\">#g
|
||||
|
||||
# normalize SGR codes a little
|
||||
|
||||
# split 256 colors out and mark so that they're not
|
||||
# recognised by the following 'split combined' line
|
||||
:e
|
||||
s#${p}\([0-9;]\{1,\}\);\([34]8;5;[0-9]\{1,3\}\)m#${p}\1m${p}¬\2m#g; t e
|
||||
s#${p}\([34]8;5;[0-9]\{1,3\}\)m#${p}¬\1m#g;
|
||||
|
||||
:c
|
||||
s#${p}\([0-9]\{1,\}\);\([0-9;]\{1,\}\)m#${p}\1m${p}\2m#g; t c # split combined
|
||||
s#${p}0\([0-7]\)#${p}\1#g #strip leading 0
|
||||
s#${p}1m\(\(${p}[4579]m\)*\)#\1${p}1m#g #bold last (with clr)
|
||||
s#${p}m#${p}0m#g #add leading 0 to norm
|
||||
|
||||
# undo any 256 color marking
|
||||
s#${p}¬\([34]8;5;[0-9]\{1,3\}\)m#${p}\1m#g;
|
||||
|
||||
# map 16 color codes to color + bold
|
||||
s#${p}9\([0-7]\)m#${p}3\1m${p}1m#g;
|
||||
s#${p}10\([0-7]\)m#${p}4\1m${p}1m#g;
|
||||
|
||||
# change 'reset' code to \"R
|
||||
s#${p}0m#\"R;#g
|
||||
" |
|
||||
|
||||
# Convert SGR sequences to HTML
|
||||
sed "
|
||||
# common combinations to minimise html (optional)
|
||||
:f
|
||||
s#${p}3[0-7]m${p}3\([0-7]\)m#${p}3\1m#g; t f
|
||||
:b
|
||||
s#${p}4[0-7]m${p}4\([0-7]\)m#${p}4\1m#g; t b
|
||||
s#${p}3\([0-7]\)m${p}4\([0-7]\)m#<span class=\"f\1 b\2\">#g
|
||||
s#${p}4\([0-7]\)m${p}3\([0-7]\)m#<span class=\"f\2 b\1\">#g
|
||||
|
||||
s#${p}1m#<span class=\"bold\">#g
|
||||
s#${p}4m#<span class=\"underline\">#g
|
||||
s#${p}5m#<span class=\"blink\">#g
|
||||
s#${p}7m#<span class=\"reverse\">#g
|
||||
s#${p}9m#<span class=\"line-through\">#g
|
||||
s#${p}3\([0-9]\)m#<span class=\"f\1\">#g
|
||||
s#${p}4\([0-9]\)m#<span class=\"b\1\">#g
|
||||
|
||||
s#${p}38;5;\([0-9]\{1,3\}\)m#<span class=\"ef\1\">#g
|
||||
s#${p}48;5;\([0-9]\{1,3\}\)m#<span class=\"eb\1\">#g
|
||||
|
||||
s#${p}[0-9;]*m##g # strip unhandled codes
|
||||
" |
|
||||
|
||||
# Convert alternative character set and handle cursor movement codes
|
||||
# Note we convert here, as if we do at start we have to worry about avoiding
|
||||
# conversion of SGR codes etc., whereas doing here we only have to
|
||||
# avoid conversions of stuff between &...; or <...>
|
||||
#
|
||||
# Note we could use sed to do this based around:
|
||||
# sed 'y/abcdefghijklmnopqrstuvwxyz{}`~/▒␉␌␍␊°±␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π£◆·/'
|
||||
# However that would be very awkward as we need to only conv some input.
|
||||
# The basic scheme that we do in the awk script below is:
|
||||
# 1. enable transliterate once "T1; is seen
|
||||
# 2. disable once "T0; is seen (may be on diff line)
|
||||
# 3. never transliterate between &; or <> chars
|
||||
# 4. track x,y movements and active display mode at each position
|
||||
# 5. buffer line/screen and dump when required
|
||||
sed "
|
||||
# change 'smacs' and 'rmacs' to \"T1 and \"T0 to simplify matching.
|
||||
s#\x1b(0#\"T1;#g;
|
||||
s#\x0E#\"T1;#g;
|
||||
|
||||
s#\x1b(B#\"T0;#g
|
||||
s#\x0F#\"T0;#g
|
||||
" |
|
||||
(
|
||||
gawk '
|
||||
function dump_line(l,del,c,blanks,ret) {
|
||||
for(c=1;c<maxX;c++) {
|
||||
if ((c SUBSEP l) in attr || length(cur)) {
|
||||
ret = ret blanks fixas(cur,attr[c,l])
|
||||
if(del) delete attr[c,l]
|
||||
blanks=""
|
||||
}
|
||||
if ((c SUBSEP l) in dump) {
|
||||
ret=ret blanks dump[c,l]
|
||||
if(del) delete dump[c,l]
|
||||
blanks=""
|
||||
} else blanks=blanks " "
|
||||
}
|
||||
if(length(cur)) ret=ret blanks
|
||||
return ret
|
||||
}
|
||||
|
||||
function dump_screen(l,ret) {
|
||||
for(l=1;l<=maxY;l++)
|
||||
ret=ret dump_line(l,0) "\n"
|
||||
return ret fixas(cur, "")
|
||||
}
|
||||
|
||||
function atos(a,i,ret) {
|
||||
for(i=1;i<=length(a);i++) if(i in a) ret=ret a[i]
|
||||
return ret
|
||||
}
|
||||
|
||||
function fixas(a,s,spc,i,attr,rm,ret) {
|
||||
spc=length(a)
|
||||
l=split(s,attr,">")
|
||||
for(i=1;i<=spc;i++) {
|
||||
rm=rm?rm:(a[i]!=attr[i]">")
|
||||
if(rm) {
|
||||
ret=ret "</span>"
|
||||
delete a[i];
|
||||
}
|
||||
}
|
||||
for(i=1;i<l;i++) {
|
||||
attr[i]=attr[i]">"
|
||||
if(a[i]!=attr[i]) {
|
||||
a[i]=attr[i]
|
||||
ret = ret attr[i]
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
function encode(string,start,end,i,ret,pos,sc,buf) {
|
||||
if(!end) end=length(string);
|
||||
if(!start) start=1;
|
||||
state=3
|
||||
for(i=1;i<=length(string);i++) {
|
||||
c=substr(string,i,1)
|
||||
if(state==2) {
|
||||
sc=sc c
|
||||
if(c==";") {
|
||||
c=sc
|
||||
state=last_mode
|
||||
} else continue
|
||||
} else {
|
||||
if(c=="\r") { x=1; continue }
|
||||
if(c=="<") {
|
||||
# Change attributes - store current active
|
||||
# attributes in span array
|
||||
split(substr(string,i),cord,">");
|
||||
i+=length(cord[1])
|
||||
span[++spc]=cord[1] ">"
|
||||
continue
|
||||
}
|
||||
else if(c=="&") {
|
||||
# All goes to single position till we see a semicolon
|
||||
sc=c
|
||||
state=2
|
||||
continue
|
||||
}
|
||||
else if(c=="\b") {
|
||||
# backspace move insertion point back 1
|
||||
if(spc) attr[x,y]=atos(span)
|
||||
x=x>1?x-1:1
|
||||
continue
|
||||
}
|
||||
else if(c=="\"") {
|
||||
split(substr(string,i+2),cord,";")
|
||||
cc=substr(string,i+1,1);
|
||||
if(cc=="T") {
|
||||
# Transliterate on/off
|
||||
if(cord[1]==1&&state==3) last_mode=state=4
|
||||
if(cord[1]==0&&state==4) last_mode=state=3
|
||||
}
|
||||
else if(cc=="C") {
|
||||
# Clear
|
||||
if(cord[1]+0) {
|
||||
# Screen - if Recording dump screen
|
||||
if(dumpStatus==dsActive) ret=ret dump_screen()
|
||||
dumpStatus=dsActive
|
||||
delete dump
|
||||
delete attr
|
||||
x=y=1
|
||||
} else {
|
||||
# To end of line
|
||||
for(pos=x;pos<maxX;pos++) {
|
||||
dump[pos,y]=" "
|
||||
if (!spc) delete attr[pos,y]
|
||||
else attr[pos,y]=atos(span)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(cc=="J") {
|
||||
# Jump to x,y
|
||||
i+=length(cord[2])+1
|
||||
# If line is higher - dump previous screen
|
||||
if(dumpStatus==dsActive&&cord[1]<y) {
|
||||
ret=ret dump_screen();
|
||||
dumpStatus=dsNew;
|
||||
}
|
||||
x=cord[2]
|
||||
if(length(cord[1]) && y!=cord[1]){
|
||||
y=cord[1]
|
||||
if(y>maxY) maxY=y
|
||||
# Change y - start recording
|
||||
dumpStatus=dumpStatus?dumpStatus:dsReset
|
||||
}
|
||||
}
|
||||
else if(cc=="M") {
|
||||
# Move left/right on current line
|
||||
x+=cord[1]
|
||||
}
|
||||
else if(cc=="X") {
|
||||
# delete on right
|
||||
for(pos=x;pos<=maxX;pos++) {
|
||||
nx=pos+cord[1]
|
||||
if(nx<maxX) {
|
||||
if((nx SUBSEP y) in attr) attr[pos,y] = attr[nx,y]
|
||||
else delete attr[pos,y]
|
||||
if((nx SUBSEP y) in dump) dump[pos,y] = dump[nx,y]
|
||||
else delete dump[pos,y]
|
||||
} else if(spc) {
|
||||
attr[pos,y]=atos(span)
|
||||
dump[pos,y]=" "
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(cc=="R") {
|
||||
# Reset attributes
|
||||
while(spc) delete span[spc--]
|
||||
}
|
||||
i+=length(cord[1])+2
|
||||
continue
|
||||
}
|
||||
else if(state==4&&i>=start&&i<=end&&c in Trans) c=Trans[c]
|
||||
}
|
||||
if(dumpStatus==dsReset) {
|
||||
delete dump
|
||||
delete attr
|
||||
ret=ret"\n"
|
||||
dumpStatus=dsActive
|
||||
}
|
||||
if(dumpStatus==dsNew) {
|
||||
# After moving/clearing we are now ready to write
|
||||
# somthing to the screen so start recording now
|
||||
ret=ret"\n"
|
||||
dumpStatus=dsActive
|
||||
}
|
||||
if(dumpStatus==dsActive||dumpStatus==dsOff) {
|
||||
dump[x,y] = c
|
||||
if(!spc) delete attr[x,y]
|
||||
else attr[x,y] = atos(span)
|
||||
if(++x>maxX) maxX=x;
|
||||
}
|
||||
}
|
||||
# End of line if dumping increment y and set x back to first col
|
||||
x=1
|
||||
if(!dumpStatus) return ret dump_line(y,1);
|
||||
else if(++y>maxY) maxY=y;
|
||||
return ret
|
||||
}
|
||||
BEGIN{
|
||||
OFS=FS
|
||||
# dump screen status
|
||||
dsOff=0 # Not dumping screen contents just write output direct
|
||||
dsNew=1 # Just after move/clear waiting for activity to start recording
|
||||
dsReset=2 # Screen cleared build new empty buffer and record
|
||||
dsActive=3 # Currently recording
|
||||
F="abcdefghijklmnopqrstuvwxyz{}`~"
|
||||
T="▒␉␌␍␊°±␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π£◆·"
|
||||
maxX=80
|
||||
delete cur;
|
||||
x=y=1
|
||||
for(i=1;i<=length(F);i++)Trans[substr(F,i,1)]=substr(T,i,1);
|
||||
}
|
||||
|
||||
{ $0=encode($0) }
|
||||
1
|
||||
END {
|
||||
if(dumpStatus) {
|
||||
print dump_screen();
|
||||
}
|
||||
}'
|
||||
)
|
||||
|
||||
[ "$body_only" ] || printf '</pre>
|
||||
</body>
|
||||
</html>\n'
|
@ -1,228 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/
|
||||
|
||||
Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
|
||||
Source code and license this software can be found
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
*/
|
||||
|
||||
using System.Security.Claims;
|
||||
using IdentityModel;
|
||||
using IdentityServer8;
|
||||
using IdentityServer8.Events;
|
||||
using IdentityServer8.Services;
|
||||
using IdentityServer8.Stores;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc;
|
||||
using Yavsc.Extensions;
|
||||
using Yavsc.Interfaces;
|
||||
using Yavsc.Models;
|
||||
|
||||
namespace IdentityServerHost.Quickstart.UI;
|
||||
|
||||
[SecurityHeaders]
|
||||
[AllowAnonymous]
|
||||
public class ExternalController : Controller
|
||||
{
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly IClientStore _clientStore;
|
||||
private readonly ILogger<ExternalController> _logger;
|
||||
private readonly IEventService _events;
|
||||
private IExternalIdentityManager _users;
|
||||
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
private readonly RoleManager<IdentityRole> _roleManager;
|
||||
private readonly ApplicationDbContext _dbContext;
|
||||
|
||||
public ExternalController(
|
||||
IIdentityServerInteractionService interaction,
|
||||
IClientStore clientStore,
|
||||
IEventService events,
|
||||
ILogger<ExternalController> logger,
|
||||
IExternalIdentityManager externalIdentityProviderManager,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
ApplicationDbContext dbContext,
|
||||
RoleManager<IdentityRole> roleManager
|
||||
)
|
||||
{
|
||||
// if the TestUserStore is not in DI, then we'll just use the global users collection
|
||||
// this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity)
|
||||
_users = externalIdentityProviderManager;
|
||||
_interaction = interaction;
|
||||
_clientStore = clientStore;
|
||||
_logger = logger;
|
||||
_events = events;
|
||||
_signInManager = signInManager;
|
||||
_roleManager = roleManager;
|
||||
_dbContext = dbContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// initiate roundtrip to external authentication provider
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public IActionResult Challenge(string scheme, string returnUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/";
|
||||
|
||||
// validate returnUrl - either it is a valid OIDC URL or back to a local page
|
||||
if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false)
|
||||
{
|
||||
// user might have clicked on a malicious link - should be logged
|
||||
throw new Exception("invalid return URL");
|
||||
}
|
||||
|
||||
// start challenge and roundtrip the return URL and scheme
|
||||
var props = new AuthenticationProperties
|
||||
{
|
||||
RedirectUri = Url.Action(nameof(Callback)),
|
||||
Items =
|
||||
{
|
||||
{ "returnUrl", returnUrl },
|
||||
{ "scheme", scheme },
|
||||
}
|
||||
};
|
||||
|
||||
return Challenge(props, scheme);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post processing of external authentication
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Callback()
|
||||
{
|
||||
// read external identity from the temporary cookie
|
||||
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
|
||||
if (result?.Succeeded != true)
|
||||
{
|
||||
throw new Exception("External authentication error");
|
||||
}
|
||||
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}");
|
||||
_logger.LogDebug("External claims: {@claims}", externalClaims);
|
||||
}
|
||||
|
||||
// lookup our user and external provider info
|
||||
var (user, provider, providerUserId, claims) = await FindUserFromExternalProvider(result);
|
||||
if (user == null)
|
||||
{
|
||||
// this might be where you might initiate a custom workflow for user registration
|
||||
// in this sample we don't show how that would be done, as our sample implementation
|
||||
// simply auto-provisions new external user
|
||||
user = AutoProvisionUser(provider, providerUserId, claims);
|
||||
}
|
||||
|
||||
// this allows us to collect any additional claims or properties
|
||||
// for the specific protocols used and store them in the local auth cookie.
|
||||
// this is typically used to store data needed for signout from those protocols.
|
||||
var additionalLocalClaims = new List<Claim>();
|
||||
var localSignInProps = new AuthenticationProperties();
|
||||
ProcessLoginCallback(result, additionalLocalClaims, localSignInProps);
|
||||
|
||||
// issue authentication cookie for user
|
||||
var isuser = new IdentityServerUser(user.Id)
|
||||
{
|
||||
DisplayName = user.UserName,
|
||||
IdentityProvider = provider,
|
||||
AdditionalClaims = additionalLocalClaims
|
||||
};
|
||||
await HttpContext.SignInAsync(isuser, localSignInProps);
|
||||
//await HttpContext.SignInAsync(user, _roleManager, false, _dbContext);
|
||||
|
||||
// delete temporary cookie used during external authentication
|
||||
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
|
||||
// retrieve return URL
|
||||
var returnUrl = result.Properties.Items["returnUrl"] ?? "~/";
|
||||
|
||||
// check if external login is in the context of an OIDC request
|
||||
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||
await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, user.UserName, true, context?.Client.ClientId));
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
if (context.IsNativeClient())
|
||||
{
|
||||
// The client is native, so this change in how to
|
||||
// return the response is for better UX for the end user.
|
||||
return this.LoadingPage("Redirect", returnUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
|
||||
private async Task<(ApplicationUser user,
|
||||
string provider,
|
||||
string providerUserId,
|
||||
IEnumerable<Claim> claims)>
|
||||
FindUserFromExternalProvider(AuthenticateResult result)
|
||||
{
|
||||
var externalUser = result.Principal;
|
||||
|
||||
// try to determine the unique id of the external user (issued by the provider)
|
||||
// the most common claim type for that are the sub claim and the NameIdentifier
|
||||
// depending on the external provider, some other claim type might be used
|
||||
var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ??
|
||||
externalUser.FindFirst(ClaimTypes.NameIdentifier) ??
|
||||
throw new Exception("Unknown userid");
|
||||
|
||||
// remove the user id claim so we don't include it as an extra claim if/when we provision the user
|
||||
var claims = externalUser.Claims.ToList();
|
||||
claims.Remove(userIdClaim);
|
||||
|
||||
var provider = result.Properties.Items["scheme"];
|
||||
var providerUserId = userIdClaim.Value;
|
||||
|
||||
// find external user
|
||||
|
||||
ApplicationUser? user = await _users.FindByExternaleProviderAsync (provider, providerUserId);
|
||||
|
||||
return (user, provider, providerUserId, claims);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a new user by external id
|
||||
/// </summary>
|
||||
/// <param name="provider"></param>
|
||||
/// <param name="providerUserId"></param>
|
||||
/// <param name="claims"></param>
|
||||
/// <returns></returns>
|
||||
private ApplicationUser AutoProvisionUser(string provider, string providerUserId, IEnumerable<Claim> claims)
|
||||
{
|
||||
var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList());
|
||||
return user;
|
||||
}
|
||||
|
||||
// if the external login is OIDC-based, there are certain things we need to preserve to make logout work
|
||||
// this will be different for WS-Fed, SAML2p or other protocols
|
||||
private void ProcessLoginCallback(AuthenticateResult externalResult, List<Claim> localClaims, AuthenticationProperties localSignInProps)
|
||||
{
|
||||
// if the external system sent a session id claim, copy it over
|
||||
// so we can use it for single sign-out
|
||||
var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId);
|
||||
if (sid != null)
|
||||
{
|
||||
localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value));
|
||||
}
|
||||
|
||||
// if the external provider issued an id_token, we'll keep it for signout
|
||||
var idToken = externalResult.Properties.GetTokenValue("id_token");
|
||||
if (idToken != null)
|
||||
{
|
||||
localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } });
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/
|
||||
|
||||
Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
|
||||
Source code and license this software can be found
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
*/
|
||||
|
||||
using System.Security.Claims;
|
||||
using Yavsc.Models;
|
||||
|
||||
namespace Yavsc.Interfaces;
|
||||
|
||||
public interface IExternalIdentityManager
|
||||
{
|
||||
ApplicationUser AutoProvisionUser(string provider, string providerUserId, List<Claim> claims);
|
||||
Task<ApplicationUser?> FindByExternaleProviderAsync(string provider, string providerUserId);
|
||||
}
|
@ -1,15 +1,22 @@
|
||||
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Yavsc.Models.Blog;
|
||||
using Yavsc.Helpers;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Diagnostics;
|
||||
using Yavsc.ViewModels.Blog;
|
||||
using Yavsc.Server.Exceptions;
|
||||
using System.Collections;
|
||||
|
||||
// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
@ -22,70 +29,111 @@ namespace Yavsc.Controllers
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
readonly RequestLocalizationOptions _localisationOptions;
|
||||
|
||||
readonly BlogSpotService blogSpotService;
|
||||
public BlogspotController(
|
||||
ApplicationDbContext context,
|
||||
ILoggerFactory loggerFactory,
|
||||
IAuthorizationService authorizationService,
|
||||
IOptions<RequestLocalizationOptions> localisationOptions,
|
||||
BlogSpotService blogSpotService)
|
||||
IOptions<RequestLocalizationOptions> localisationOptions)
|
||||
{
|
||||
_context = context;
|
||||
_logger = loggerFactory.CreateLogger<AccountController>();
|
||||
_authorizationService = authorizationService;
|
||||
_localisationOptions = localisationOptions.Value;
|
||||
this.blogSpotService = blogSpotService;
|
||||
}
|
||||
|
||||
// GET: Blog
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Index(string id, int skip = 0, int take = 25)
|
||||
public async Task<IActionResult> Index(string id, int skip=0, int take=25)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
return View("UserPosts",
|
||||
await blogSpotService.UserPosts(id, User.GetUserId(),
|
||||
skip, take));
|
||||
if (!string.IsNullOrEmpty(id)) {
|
||||
return View("UserPosts", await UserPosts(id));
|
||||
}
|
||||
IEnumerable<IBlogPost> index = await this.blogSpotService.Index(User, id, skip, take);
|
||||
return View(index);
|
||||
IEnumerable<BlogPost> posts;
|
||||
|
||||
if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
string viewerId = User.GetUserId();
|
||||
long[] usercircles = await _context.Circle.Include(c=>c.Members).
|
||||
Where(c=>c.Members.Any(m=>m.MemberId == viewerId))
|
||||
.Select(c=>c.Id).ToArrayAsync();
|
||||
|
||||
posts = _context.BlogSpot
|
||||
.Include(b => b.Author)
|
||||
.Include(p=>p.ACL)
|
||||
.Include(p=>p.Tags)
|
||||
.Include(p=>p.Comments)
|
||||
.Where(p =>(p.ACL.Count == 0)
|
||||
|| (p.AuthorId == viewerId)
|
||||
|| (usercircles != null && p.ACL.Any(a => usercircles.Contains(a.CircleId)))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
posts = _context.BlogSpot
|
||||
.Include(b => b.Author)
|
||||
.Include(p=>p.ACL)
|
||||
.Include(p=>p.Tags)
|
||||
.Include(p=>p.Comments)
|
||||
.Where(p => p.ACL.Count == 0 ).ToArray();
|
||||
}
|
||||
|
||||
var data = posts.OrderByDescending( p=> p.DateCreated);
|
||||
var grouped = data.GroupBy(p=> p.Title).Skip(skip).Take(take);
|
||||
|
||||
return View(grouped);
|
||||
}
|
||||
|
||||
[Route("~/Title/{id?}")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Title(string id)
|
||||
{
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
ViewData["Title"] = id;
|
||||
return View("Title", blogSpotService.GetTitle(id));
|
||||
return View("Title", _context.BlogSpot.Include(
|
||||
b => b.Author
|
||||
).Where(x => x.Title == id && (x.AuthorId == uid )).OrderByDescending(
|
||||
x => x.DateCreated
|
||||
).ToList());
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<BlogPost>> UserPosts(string userName, int pageLen = 10, int pageNum = 0)
|
||||
private async Task<IEnumerable<BlogPost>> UserPosts(string userName, int pageLen=10, int pageNum=0)
|
||||
{
|
||||
return await blogSpotService.UserPosts(userName, User.GetUserId(), pageLen, pageNum);
|
||||
|
||||
string posterId = (await _context.Users.SingleOrDefaultAsync(u=>u.UserName == userName))?.Id ?? null ;
|
||||
return _context.UserPosts(posterId, User.Identity.Name);
|
||||
}
|
||||
// GET: Blog/Details/5
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Details(long? id)
|
||||
{
|
||||
if (id == null) return this.NotFound();
|
||||
try
|
||||
if (id == null)
|
||||
{
|
||||
var blog = await blogSpotService.Details(User, id.Value);
|
||||
ViewData["apicmtctlr"] = "/api/blogcomments";
|
||||
ViewData["moderatoFlag"] = User.IsInRole(Constants.BlogModeratorGroupName);
|
||||
|
||||
return View(blog);
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
catch (AuthorizationFailureException ex)
|
||||
|
||||
BlogPost blog = _context.BlogSpot
|
||||
.Include(p => p.Author)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Comments)
|
||||
.Include(p => p.ACL)
|
||||
.Single(m => m.Id == id);
|
||||
if (blog == null)
|
||||
{
|
||||
return Challenge();
|
||||
return NotFound();
|
||||
}
|
||||
if ( _authorizationService.AuthorizeAsync(User, blog, new ReadPermission()).IsFaulted)
|
||||
{
|
||||
return new ChallengeResult();
|
||||
}
|
||||
foreach (var c in blog.Comments) {
|
||||
c.Author = _context.Users.First(u=>u.Id==c.AuthorId);
|
||||
}
|
||||
ViewData["apicmtctlr"] = "/api/blogcomments";
|
||||
ViewData["moderatoFlag"] = User.IsInRole(Constants.BlogModeratorGroupName);
|
||||
return View(blog);
|
||||
}
|
||||
void SetLangItems()
|
||||
{
|
||||
ViewBag.LangItems = _localisationOptions.SupportedUICultures?.Select
|
||||
ViewBag.LangItems = _localisationOptions.SupportedUICultures.Select
|
||||
(
|
||||
sc => new SelectListItem { Value = sc.IetfLanguageTag, Text = sc.NativeName, Selected = System.Globalization.CultureInfo.CurrentUICulture == sc }
|
||||
);
|
||||
@ -95,25 +143,33 @@ namespace Yavsc.Controllers
|
||||
[Authorize()]
|
||||
public IActionResult Create(string title)
|
||||
{
|
||||
var result = new BlogPostCreateViewModel
|
||||
{
|
||||
Title = title
|
||||
var result = new BlogPostInputViewModel{Title=title
|
||||
};
|
||||
ViewData["PostTarget"]="Create";
|
||||
SetLangItems();
|
||||
return View(result);
|
||||
}
|
||||
|
||||
// POST: Blog/Create
|
||||
[HttpPost, Authorize, ValidateAntiForgeryToken]
|
||||
public IActionResult Create(BlogPostEditViewModel blogInput)
|
||||
public IActionResult Create(BlogPostInputViewModel blogInput)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
BlogPost post = blogSpotService.Create(User.GetUserId(),
|
||||
BlogPostEditViewModel.FromViewModel(blogInput));
|
||||
BlogPost post = new BlogPost
|
||||
{
|
||||
Title = blogInput.Title,
|
||||
Content = blogInput.Content,
|
||||
Photo = blogInput.Photo,
|
||||
AuthorId = User.GetUserId()
|
||||
};
|
||||
_context.BlogSpot.Add(post);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
return View("Edit", blogInput);
|
||||
ModelState.AddModelError("Unknown","Invalid Blog posted ...");
|
||||
ViewData["PostTarget"]="Create";
|
||||
return View("Edit",blogInput);
|
||||
}
|
||||
|
||||
[Authorize()]
|
||||
@ -124,18 +180,37 @@ namespace Yavsc.Controllers
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
try
|
||||
{
|
||||
var blog = await blogSpotService.GetPostForEdition(User, id.Value);
|
||||
if (blog == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
SetLangItems();
|
||||
return View(blog);
|
||||
|
||||
ViewData["PostTarget"]="Edit";
|
||||
BlogPost blog = _context.BlogSpot.Include(x => x.Author).Include(x => x.ACL).Single(m => m.Id == id);
|
||||
|
||||
if (blog == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
catch (AuthorizationFailureException)
|
||||
if (!_authorizationService.AuthorizeAsync(User, blog, new EditPermission()).IsFaulted)
|
||||
{
|
||||
ViewBag.ACL = _context.Circle.Where(
|
||||
c=>c.OwnerId == blog.AuthorId)
|
||||
.Select(
|
||||
c => new SelectListItem
|
||||
{
|
||||
Text = c.Name,
|
||||
Value = c.Id.ToString(),
|
||||
Selected = blog.AuthorizeCircle(c.Id)
|
||||
}
|
||||
);
|
||||
SetLangItems();
|
||||
return View(new BlogPostEditViewModel
|
||||
{
|
||||
Id = blog.Id,
|
||||
Title = blog.Title,
|
||||
Content = blog.Content,
|
||||
ACL = blog.ACL,
|
||||
Photo = blog.Photo
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ChallengeResult();
|
||||
}
|
||||
@ -143,41 +218,65 @@ namespace Yavsc.Controllers
|
||||
|
||||
// POST: Blog/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken, Authorize()]
|
||||
[ValidateAntiForgeryToken,Authorize()]
|
||||
public async Task<IActionResult> Edit(BlogPostEditViewModel blogEdit)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
await blogSpotService.Modify(User, blogEdit);
|
||||
var blog = _context.BlogSpot.SingleOrDefault(b=>b.Id == blogEdit.Id);
|
||||
if (blog == null) {
|
||||
ModelState.AddModelError("Id", "not found");
|
||||
return View();
|
||||
}
|
||||
if (!(await _authorizationService.AuthorizeAsync(User, blog, new EditPermission())).Succeeded) {
|
||||
ViewData["StatusMessage"] = "Accès restreint";
|
||||
return new ChallengeResult();
|
||||
}
|
||||
blog.Content=blogEdit.Content;
|
||||
blog.Title = blogEdit.Title;
|
||||
blog.Photo = blogEdit.Photo;
|
||||
blog.ACL = blogEdit.ACL;
|
||||
// saves the change
|
||||
_context.Update(blog);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
ViewData["StatusMessage"] = "Post modified";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
ViewData["PostTarget"]="Edit";
|
||||
return View(blogEdit);
|
||||
}
|
||||
|
||||
// GET: Blog/Delete/5
|
||||
[ActionName("Delete"), Authorize()]
|
||||
public async Task<IActionResult> Delete(long? id)
|
||||
[ActionName("Delete"),Authorize()]
|
||||
public IActionResult Delete(long? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var blog = await blogSpotService.GetBlogPostAsync(id.Value);
|
||||
BlogPost blog = _context.BlogSpot.Include(
|
||||
b => b.Author
|
||||
).Single(m => m.Id == id);
|
||||
if (blog == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(blog);
|
||||
}
|
||||
|
||||
// POST: Blog/Delete/5
|
||||
[HttpPost, ActionName("Delete"), Authorize("TheAuthor")]
|
||||
[HttpPost, ActionName("Delete"), Authorize("IsTheAuthor")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DeleteConfirmed(long id)
|
||||
public IActionResult DeleteConfirmed(long id)
|
||||
{
|
||||
await blogSpotService.Delete(User, id);
|
||||
var uid = User.GetUserId();
|
||||
BlogPost blog = _context.BlogSpot.Single(m => m.Id == id);
|
||||
|
||||
_context.BlogSpot.Remove(blog);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
|
@ -33,342 +33,13 @@ using Yavsc.Server.Helpers;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.IdentityModel.Protocols.Configuration;
|
||||
using IdentityModel;
|
||||
using System.Security.Claims;
|
||||
using IdentityServer8.Security;
|
||||
using Yavsc.Interfaces;
|
||||
|
||||
namespace Yavsc.Extensions;
|
||||
|
||||
|
||||
public static class HostingExtensions
|
||||
{
|
||||
|
||||
public static WebApplication ConfigureWebAppServices(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = LoadConfiguration(builder);
|
||||
|
||||
services.AddSession();
|
||||
|
||||
// TODO .AddServerSideSessionStore<YavscServerSideSessionStore>()
|
||||
|
||||
|
||||
// Add the system clock service
|
||||
_ = services.AddSingleton<ISystemClock, SystemClock>();
|
||||
_ = services.AddSingleton<IConnexionManager, HubConnectionManager>();
|
||||
_ = services.AddSingleton<ILiveProcessor, LiveProcessor>();
|
||||
_ = services.AddTransient<IFileSystemAuthManager, FileSystemAuthManager>();
|
||||
|
||||
AddIdentityDBAndStores(builder).AddDefaultTokenProviders();
|
||||
AddIdentityServer(builder);
|
||||
|
||||
services.AddSignalR(o =>
|
||||
{
|
||||
o.EnableDetailedErrors = true;
|
||||
});
|
||||
|
||||
services.AddMvc(config =>
|
||||
{
|
||||
/* var policy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.Build();
|
||||
config.Filters.Add(new AuthorizeFilter(policy)); */
|
||||
config.Filters.Add(new ProducesAttribute("application/json"));
|
||||
// config.ModelBinders.Insert(0,new MyDateTimeModelBinder());
|
||||
// config.ModelBinders.Insert(0,new MyDecimalModelBinder());
|
||||
config.EnableEndpointRouting = true;
|
||||
}).AddFormatterMappings(
|
||||
config => config.SetMediaTypeMappingForFormat("text/pdf",
|
||||
new MediaTypeHeaderValue("text/pdf"))
|
||||
).AddFormatterMappings(
|
||||
config => config.SetMediaTypeMappingForFormat("text/x-tex",
|
||||
new MediaTypeHeaderValue("text/x-tex"))
|
||||
)
|
||||
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,
|
||||
options =>
|
||||
{
|
||||
options.ResourcesPath = "Resources";
|
||||
}).AddDataAnnotationsLocalization();
|
||||
|
||||
services.AddTransient<ITrueEmailSender, MailSender>()
|
||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>()
|
||||
.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
||||
.AddTransient<IBillingService, BillingService>()
|
||||
.AddTransient<IDataStore, FileDataStore>((sp) => new FileDataStore("googledatastore", false))
|
||||
.AddTransient<ICalendarManager, CalendarManager>()
|
||||
.AddTransient<BlogSpotService>();
|
||||
|
||||
// TODO for SMS: services.AddTransient<ISmsSender, AuthMessageSender>();
|
||||
|
||||
_ = services.AddLocalization(options =>
|
||||
{
|
||||
options.ResourcesPath = "Resources";
|
||||
});
|
||||
var dataDirConfig = builder.Configuration["Site:DataDir"] ?? "DataDir";
|
||||
|
||||
var dataDir = new DirectoryInfo(dataDirConfig);
|
||||
// Add session related services.
|
||||
|
||||
services.AddDataProtection().PersistKeysToFileSystem(dataDir);
|
||||
AddYavscPolicies(services);
|
||||
|
||||
services.AddScoped<IAuthorizationHandler, PermissionHandler>();
|
||||
services.AddTransient<IExternalIdentityManager, ExternalIdentityManager>();
|
||||
|
||||
|
||||
AddAuthentication(builder);
|
||||
// accepts any access token issued by identity server
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
public static IdentityBuilder AddIdentityDBAndStores(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = builder.Services;
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
return services.AddIdentity<ApplicationUser, IdentityRole>(
|
||||
options =>
|
||||
{
|
||||
options.SignIn.RequireConfirmedAccount = true;
|
||||
}
|
||||
)
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
}
|
||||
|
||||
private static void AddYavscPolicies(IServiceCollection services)
|
||||
{
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy("ApiScope", policy =>
|
||||
{
|
||||
policy.RequireAuthenticatedUser()
|
||||
.RequireClaim("scope", "scope2");
|
||||
});
|
||||
options.AddPolicy("Performer", policy =>
|
||||
{
|
||||
policy
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireClaim(JwtClaimTypes.Role, "Performer");
|
||||
});
|
||||
options.AddPolicy("AdministratorOnly", policy =>
|
||||
{
|
||||
_ = policy
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireClaim(JwtClaimTypes.Role, Constants.AdminGroupName);
|
||||
});
|
||||
|
||||
options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName));
|
||||
|
||||
// options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456"));
|
||||
// options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement()));
|
||||
options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
|
||||
options.AddPolicy("TheAuthor", policy => policy.Requirements.Add(new EditPermission()));
|
||||
})
|
||||
.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("default", builder =>
|
||||
{
|
||||
_ = builder.WithOrigins("*")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public static IServiceCollection LoadConfiguration(this WebApplicationBuilder builder)
|
||||
{
|
||||
var siteSection = builder.Configuration.GetSection("Site");
|
||||
var smtpSection = builder.Configuration.GetSection("Smtp");
|
||||
var paypalSection = builder.Configuration.GetSection("Authentication:PayPal");
|
||||
// OAuth2AppSettings
|
||||
var googleAuthSettings = builder.Configuration.GetSection("Authentication:Google");
|
||||
|
||||
//LoadGoogleConfig(builder.Configuration);
|
||||
|
||||
|
||||
var services = builder.Services;
|
||||
_ = services.AddControllersWithViews()
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
services.Configure<SiteSettings>(siteSection);
|
||||
services.Configure<SmtpSettings>(smtpSection);
|
||||
services.Configure<PayPalSettings>(paypalSection);
|
||||
services.Configure<GoogleAuthSettings>(googleAuthSettings);
|
||||
ConfigureRequestLocalization(services);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void AddAuthentication(WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services=builder.Services;
|
||||
IConfigurationRoot configurationRoot=builder.Configuration;
|
||||
string? googleClientId = configurationRoot["Authentication:Google:ClientId"];
|
||||
string? googleClientSecret = configurationRoot["Authentication:Google:ClientSecret"];
|
||||
|
||||
var authenticationBuilder = services.AddAuthentication();
|
||||
|
||||
if (googleClientId!=null && googleClientSecret!=null)
|
||||
authenticationBuilder.AddGoogle(options =>
|
||||
{
|
||||
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
|
||||
|
||||
// register your IdentityServer with Google at https://console.developers.google.com
|
||||
// enable the Google+ API
|
||||
// set the redirect URI to https://localhost:5001/signin-google
|
||||
options.ClientId = googleClientId;
|
||||
options.ClientSecret = googleClientSecret;
|
||||
|
||||
});
|
||||
}
|
||||
private static IIdentityServerBuilder AddIdentityServer(WebApplicationBuilder builder)
|
||||
{
|
||||
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
||||
{
|
||||
options.Events.RaiseErrorEvents = true;
|
||||
options.Events.RaiseInformationEvents = true;
|
||||
options.Events.RaiseFailureEvents = true;
|
||||
options.Events.RaiseSuccessEvents = true;
|
||||
|
||||
// see https://IdentityServer8.readthedocs.io/en/latest/topics/resources.html
|
||||
options.EmitStaticAudienceClaim = true;
|
||||
})
|
||||
.AddInMemoryIdentityResources(Config.IdentityResources)
|
||||
.AddInMemoryClients(Config.Clients)
|
||||
.AddInMemoryApiScopes(Config.ApiScopes)
|
||||
.AddAspNetIdentity<ApplicationUser>()
|
||||
;
|
||||
if (builder.Environment.IsDevelopment())
|
||||
{
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = builder.Configuration["SigningCert:Path"];
|
||||
var pass = builder.Configuration["SigningCert:Password"];
|
||||
if (path == null)
|
||||
throw new InvalidConfigurationException("No signing cert path");
|
||||
FileInfo certFileInfo = new FileInfo(path);
|
||||
Debug.Assert(certFileInfo.Exists);
|
||||
RSA rsa = RSA.Create();
|
||||
rsa.ImportFromPem(File.ReadAllText(certFileInfo.FullName));
|
||||
var signingCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256)
|
||||
{
|
||||
CryptoProviderFactory = new CryptoProviderFactory { CacheSignatureProviders = false }
|
||||
};
|
||||
identityServerBuilder.AddSigningCredential(signingCredentials);
|
||||
}
|
||||
return identityServerBuilder;
|
||||
}
|
||||
|
||||
private static void ConfigureRequestLocalization(IServiceCollection services)
|
||||
{
|
||||
services.Configure<RequestLocalizationOptions>(options =>
|
||||
{
|
||||
CultureInfo[] supportedCultures = new[]
|
||||
{
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("fr"),
|
||||
new CultureInfo("pt")
|
||||
};
|
||||
|
||||
CultureInfo[] supportedUICultures = new[]
|
||||
{
|
||||
new CultureInfo("fr"),
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("pt")
|
||||
};
|
||||
|
||||
// You must explicitly state which cultures your application supports.
|
||||
// These are the cultures the app supports for formatting numbers, dates, etc.
|
||||
options.SupportedCultures = supportedCultures;
|
||||
|
||||
// These are the cultures the app supports for UI strings, i.e. we have localized resources for.
|
||||
options.SupportedUICultures = supportedUICultures;
|
||||
|
||||
options.RequestCultureProviders = new List<IRequestCultureProvider>
|
||||
{
|
||||
new QueryStringRequestCultureProvider { Options = options },
|
||||
new CookieRequestCultureProvider { Options = options, CookieName="ASPNET_CULTURE" },
|
||||
new AcceptLanguageHeaderRequestCultureProvider { Options = options }
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public async static Task<WebApplication> ConfigurePipeline(this WebApplication app)
|
||||
{
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
}
|
||||
app.Use(async (context, next) => {
|
||||
if (context.Request.Path.StartsWithSegments("/robots.txt")) {
|
||||
var robotsTxtPath = System.IO.Path.Combine(app.Environment.WebRootPath, $"robots.txt");
|
||||
string output = "User-agent: * \nDisallow: /";
|
||||
if (File.Exists(robotsTxtPath)) {
|
||||
output = await File.ReadAllTextAsync(robotsTxtPath);
|
||||
}
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync(output);
|
||||
} else await next();
|
||||
});
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseIdentityServer();
|
||||
app.UseAuthorization();
|
||||
app.UseCors("default");
|
||||
app.MapDefaultControllerRoute();
|
||||
//app.MapRazorPages();
|
||||
app.MapHub<ChatHub>("/chatHub");
|
||||
|
||||
WorkflowHelpers.ConfigureBillingService();
|
||||
|
||||
var services = app.Services;
|
||||
ILoggerFactory loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
var siteSettings = services.GetRequiredService<IOptions<SiteSettings>>();
|
||||
var smtpSettings = services.GetRequiredService<IOptions<SmtpSettings>>();
|
||||
var payPalSettings = services.GetRequiredService<IOptions<PayPalSettings>>();
|
||||
var googleAuthSettings = services.GetRequiredService<IOptions<GoogleAuthSettings>>();
|
||||
var localization = services.GetRequiredService<IStringLocalizer<YavscLocalization>>();
|
||||
Startup.Configure(app, siteSettings, smtpSettings,
|
||||
payPalSettings, googleAuthSettings, localization, loggerFactory,
|
||||
app.Environment.EnvironmentName);
|
||||
app.ConfigureFileServerApp();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void LoadGoogleConfig(IConfigurationRoot configuration)
|
||||
{
|
||||
string? googleClientFile = configuration["Authentication:Google:GoogleWebClientJson"];
|
||||
string? googleServiceAccountJsonFile = configuration["Authentication:Google:GoogleServiceAccountJson"];
|
||||
if (googleClientFile != null)
|
||||
{
|
||||
Config.GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build();
|
||||
}
|
||||
|
||||
if (googleServiceAccountJsonFile != null)
|
||||
{
|
||||
FileInfo safile = new FileInfo(googleServiceAccountJsonFile);
|
||||
Config.GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
|
||||
}
|
||||
}
|
||||
|
||||
#region files config
|
||||
public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app,
|
||||
bool enableDirectoryBrowsing = false)
|
||||
{
|
||||
@ -420,5 +91,321 @@ public static class HostingExtensions
|
||||
return app;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
internal static WebApplication ConfigureWebAppServices(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = LoadConfiguration(builder);
|
||||
|
||||
//services.AddRazorPages();
|
||||
|
||||
|
||||
services.AddSession();
|
||||
|
||||
// TODO .AddServerSideSessionStore<YavscServerSideSessionStore>()
|
||||
|
||||
|
||||
// Add the system clock service
|
||||
_ = services.AddSingleton<ISystemClock, SystemClock>();
|
||||
_ = services.AddSingleton<IConnexionManager, HubConnectionManager>();
|
||||
_ = services.AddSingleton<ILiveProcessor, LiveProcessor>();
|
||||
_ = services.AddTransient<IFileSystemAuthManager, FileSystemAuthManager>();
|
||||
|
||||
AddIdentityDBAndStores(builder).AddDefaultTokenProviders();
|
||||
AddIdentityServer(builder);
|
||||
|
||||
services.AddSignalR(o =>
|
||||
{
|
||||
o.EnableDetailedErrors = true;
|
||||
});
|
||||
|
||||
services.AddMvc(config =>
|
||||
{
|
||||
/* var policy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.Build();
|
||||
config.Filters.Add(new AuthorizeFilter(policy)); */
|
||||
config.Filters.Add(new ProducesAttribute("application/json"));
|
||||
// config.ModelBinders.Insert(0,new MyDateTimeModelBinder());
|
||||
// config.ModelBinders.Insert(0,new MyDecimalModelBinder());
|
||||
config.EnableEndpointRouting = true;
|
||||
}).AddFormatterMappings(
|
||||
config => config.SetMediaTypeMappingForFormat("text/pdf",
|
||||
new MediaTypeHeaderValue("text/pdf"))
|
||||
).AddFormatterMappings(
|
||||
config => config.SetMediaTypeMappingForFormat("text/x-tex",
|
||||
new MediaTypeHeaderValue("text/x-tex"))
|
||||
)
|
||||
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,
|
||||
options =>
|
||||
{
|
||||
options.ResourcesPath = "Resources";
|
||||
}).AddDataAnnotationsLocalization();
|
||||
|
||||
services.AddTransient<ITrueEmailSender, MailSender>()
|
||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>()
|
||||
.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
||||
.AddTransient<IBillingService, BillingService>()
|
||||
.AddTransient<IDataStore, FileDataStore>((sp) => new FileDataStore("googledatastore", false))
|
||||
.AddTransient<ICalendarManager, CalendarManager>();
|
||||
|
||||
// TODO for SMS: services.AddTransient<ISmsSender, AuthMessageSender>();
|
||||
|
||||
_ = services.AddLocalization(options =>
|
||||
{
|
||||
options.ResourcesPath = "Resources";
|
||||
});
|
||||
var dataDirConfig = builder.Configuration["Site:DataDir"] ?? "DataDir";
|
||||
|
||||
var dataDir = new DirectoryInfo(dataDirConfig);
|
||||
// Add session related services.
|
||||
|
||||
services.AddDataProtection().PersistKeysToFileSystem(dataDir);
|
||||
AddYavscPolicies(services);
|
||||
|
||||
services.AddScoped<IAuthorizationHandler, PermissionHandler>();
|
||||
|
||||
|
||||
AddAuthentication(builder);
|
||||
// accepts any access token issued by identity server
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
public static IdentityBuilder AddIdentityDBAndStores(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = builder.Services;
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
return services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
}
|
||||
|
||||
private static void AddYavscPolicies(IServiceCollection services)
|
||||
{
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy("ApiScope", policy =>
|
||||
{
|
||||
policy.RequireAuthenticatedUser()
|
||||
.RequireClaim("scope", "scope2");
|
||||
});
|
||||
options.AddPolicy("Performer", policy =>
|
||||
{
|
||||
policy
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "Performer");
|
||||
});
|
||||
options.AddPolicy("AdministratorOnly", policy =>
|
||||
{
|
||||
_ = policy.RequireClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", Constants.AdminGroupName);
|
||||
});
|
||||
|
||||
options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName));
|
||||
|
||||
// options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456"));
|
||||
// options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement()));
|
||||
options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
|
||||
options.AddPolicy("IsTheAuthor", policy => policy.Requirements.Add(new EditPermission()));
|
||||
})
|
||||
.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("default", builder =>
|
||||
{
|
||||
_ = builder.WithOrigins("*")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public static IServiceCollection LoadConfiguration(this WebApplicationBuilder builder)
|
||||
{
|
||||
var siteSection = builder.Configuration.GetSection("Site");
|
||||
var smtpSection = builder.Configuration.GetSection("Smtp");
|
||||
var paypalSection = builder.Configuration.GetSection("Authentication:PayPal");
|
||||
// OAuth2AppSettings
|
||||
var googleAuthSettings = builder.Configuration.GetSection("Authentication:Google");
|
||||
|
||||
string? googleClientFile = builder.Configuration["Authentication:Google:GoogleWebClientJson"];
|
||||
string? googleServiceAccountJsonFile = builder.Configuration["Authentication:Google:GoogleServiceAccountJson"];
|
||||
if (googleClientFile != null)
|
||||
{
|
||||
Config.GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build();
|
||||
}
|
||||
|
||||
if (googleServiceAccountJsonFile != null)
|
||||
{
|
||||
FileInfo safile = new FileInfo(googleServiceAccountJsonFile);
|
||||
Config.GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
|
||||
}
|
||||
var services = builder.Services;
|
||||
_ = services.AddControllersWithViews()
|
||||
.AddNewtonsoftJson();
|
||||
LoadGoogleConfig(builder.Configuration);
|
||||
|
||||
services.Configure<SiteSettings>(siteSection);
|
||||
services.Configure<SmtpSettings>(smtpSection);
|
||||
services.Configure<PayPalSettings>(paypalSection);
|
||||
services.Configure<GoogleAuthSettings>(googleAuthSettings);
|
||||
ConfigureRequestLocalization(services);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void AddAuthentication(WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services=builder.Services;
|
||||
IConfigurationRoot configurationRoot=builder.Configuration;
|
||||
string? googleClientId = configurationRoot["Authentication:Google:ClientId"];
|
||||
string? googleClientSecret = configurationRoot["Authentication:Google:ClientSecret"];
|
||||
|
||||
var authenticationBuilder = services.AddAuthentication();
|
||||
|
||||
if (googleClientId!=null && googleClientSecret!=null)
|
||||
authenticationBuilder.AddGoogle(options =>
|
||||
{
|
||||
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
|
||||
|
||||
// register your IdentityServer with Google at https://console.developers.google.com
|
||||
// enable the Google+ API
|
||||
// set the redirect URI to https://localhost:5001/signin-google
|
||||
options.ClientId = googleClientId;
|
||||
options.ClientSecret = googleClientSecret;
|
||||
});
|
||||
}
|
||||
private static IIdentityServerBuilder AddIdentityServer(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddTransient<IProfileService,ProfileService>();
|
||||
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
||||
{
|
||||
options.Events.RaiseErrorEvents = true;
|
||||
options.Events.RaiseInformationEvents = true;
|
||||
options.Events.RaiseFailureEvents = true;
|
||||
options.Events.RaiseSuccessEvents = true;
|
||||
|
||||
// see https://IdentityServer8.readthedocs.io/en/latest/topics/resources.html
|
||||
options.EmitStaticAudienceClaim = true;
|
||||
})
|
||||
.AddInMemoryIdentityResources(Config.IdentityResources)
|
||||
.AddInMemoryClients(Config.Clients)
|
||||
.AddInMemoryApiScopes(Config.ApiScopes)
|
||||
.AddAspNetIdentity<ApplicationUser>()
|
||||
.AddProfileService<ProfileService>()
|
||||
;
|
||||
if (builder.Environment.IsDevelopment())
|
||||
{
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = builder.Configuration["SigningCert:Path"];
|
||||
var pass = builder.Configuration["SigningCert:Password"];
|
||||
if (path == null)
|
||||
throw new InvalidConfigurationException("No signing cert path");
|
||||
FileInfo certFileInfo = new FileInfo(path);
|
||||
Debug.Assert(certFileInfo.Exists);
|
||||
RSA rsa = RSA.Create();
|
||||
rsa.ImportFromPem(File.ReadAllText(certFileInfo.FullName));
|
||||
var signingCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256)
|
||||
{
|
||||
CryptoProviderFactory = new CryptoProviderFactory { CacheSignatureProviders = false }
|
||||
};
|
||||
identityServerBuilder.AddSigningCredential(signingCredentials);
|
||||
}
|
||||
return identityServerBuilder;
|
||||
}
|
||||
|
||||
private static void ConfigureRequestLocalization(IServiceCollection services)
|
||||
{
|
||||
services.Configure<RequestLocalizationOptions>(options =>
|
||||
{
|
||||
CultureInfo[] supportedCultures = new[]
|
||||
{
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("fr"),
|
||||
new CultureInfo("pt")
|
||||
};
|
||||
|
||||
CultureInfo[] supportedUICultures = new[]
|
||||
{
|
||||
new CultureInfo("fr"),
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("pt")
|
||||
};
|
||||
|
||||
// You must explicitly state which cultures your application supports.
|
||||
// These are the cultures the app supports for formatting numbers, dates, etc.
|
||||
options.SupportedCultures = supportedCultures;
|
||||
|
||||
// These are the cultures the app supports for UI strings, i.e. we have localized resources for.
|
||||
options.SupportedUICultures = supportedUICultures;
|
||||
|
||||
options.RequestCultureProviders = new List<IRequestCultureProvider>
|
||||
{
|
||||
new QueryStringRequestCultureProvider { Options = options },
|
||||
new CookieRequestCultureProvider { Options = options, CookieName="ASPNET_CULTURE" },
|
||||
new AcceptLanguageHeaderRequestCultureProvider { Options = options }
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
internal static WebApplication ConfigurePipeline(this WebApplication app)
|
||||
{
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseIdentityServer();
|
||||
app.UseAuthorization();
|
||||
app.UseCors("default");
|
||||
app.MapDefaultControllerRoute();
|
||||
//pp.MapRazorPages();
|
||||
app.MapHub<ChatHub>("/chatHub");
|
||||
|
||||
WorkflowHelpers.ConfigureBillingService();
|
||||
|
||||
var services = app.Services;
|
||||
ILoggerFactory loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
var siteSettings = services.GetRequiredService<IOptions<SiteSettings>>();
|
||||
var smtpSettings = services.GetRequiredService<IOptions<SmtpSettings>>();
|
||||
var payPalSettings = services.GetRequiredService<IOptions<PayPalSettings>>();
|
||||
var googleAuthSettings = services.GetRequiredService<IOptions<GoogleAuthSettings>>();
|
||||
var localization = services.GetRequiredService<IStringLocalizer<YavscLocalization>>();
|
||||
Startup.Configure(app, siteSettings, smtpSettings,
|
||||
payPalSettings, googleAuthSettings, localization, loggerFactory,
|
||||
app.Environment.EnvironmentName);
|
||||
app.ConfigureFileServerApp();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void LoadGoogleConfig(IConfigurationRoot configuration)
|
||||
{
|
||||
string? googleClientFile = configuration["Authentication:Google:GoogleWebClientJson"];
|
||||
string? googleServiceAccountJsonFile = configuration["Authentication:Google:GoogleServiceAccountJson"];
|
||||
if (googleClientFile != null)
|
||||
{
|
||||
Config.GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build();
|
||||
}
|
||||
|
||||
if (googleServiceAccountJsonFile != null)
|
||||
{
|
||||
FileInfo safile = new FileInfo(googleServiceAccountJsonFile);
|
||||
Config.GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RazorEngine.Compilation.ImpromptuInterface.Optimization;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Migrations;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Blog;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
@ -34,10 +32,6 @@ public class PermissionHandler : IAuthorizationHandler
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
else if (context.User.IsInRole("Administrator"))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
else if (requirement is EditPermission || requirement is DeletePermission)
|
||||
{
|
||||
@ -55,33 +49,18 @@ public class PermissionHandler : IAuthorizationHandler
|
||||
{
|
||||
if (resource is BlogPost blogPost)
|
||||
{
|
||||
return
|
||||
applicationDbContext.blogSpotPublications
|
||||
.Any(p=>p.BlogpostId == blogPost.Id);
|
||||
if (blogPost.ACL.Count==0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsOwner(ClaimsPrincipal user, object? resource)
|
||||
private static bool IsOwner(ClaimsPrincipal user, object? resource)
|
||||
{
|
||||
if (resource is BlogPost blogPost)
|
||||
{
|
||||
return blogPost.AuthorId == user.GetUserId();
|
||||
}
|
||||
else
|
||||
if (resource is DefaultHttpContext httpContext)
|
||||
{
|
||||
if (httpContext.Request.Path.StartsWithSegments("/blogpost/Delete", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string postId = (string)httpContext.GetRouteValue("id");
|
||||
if (long.TryParse(postId, out long id))
|
||||
{
|
||||
BlogPost b = applicationDbContext.BlogSpot.FirstOrDefault(b => b.Id == id && b.AuthorId == user.GetUserId());
|
||||
return b != null;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -91,7 +70,7 @@ public class PermissionHandler : IAuthorizationHandler
|
||||
{
|
||||
return applicationDbContext.CircleMembers
|
||||
.Include(c => c.Circle)
|
||||
.Where(m=>m.MemberId==user.GetUserId() && m.Circle.OwnerId == blogPost.AuthorId)
|
||||
.Where(m=>m.MemberId==user.GetUserId() && m.Circle.OwnerId == blogPost.OwnerId)
|
||||
.Any();
|
||||
}
|
||||
return true;
|
||||
|
@ -1,50 +1,27 @@
|
||||
using System.Text.Encodings.Web;
|
||||
using AsciiDocNet;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
namespace Yavsc.Helpers
|
||||
{
|
||||
public class AsciidocTagHelper : TagHelper
|
||||
{
|
||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
public override async Task ProcessAsync (TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
|
||||
if (context.AllAttributes.ContainsName ("summary"))
|
||||
{
|
||||
var summaryLength = context.AllAttributes["summary"].Value;
|
||||
}
|
||||
await base.ProcessAsync(context, output);
|
||||
var content = await output.GetChildContentAsync();
|
||||
string text = content.GetContent();
|
||||
if (string.IsNullOrWhiteSpace(text)) return;
|
||||
|
||||
try
|
||||
{
|
||||
if (context.AllAttributes.ContainsName("summary"))
|
||||
{
|
||||
var summaryLength = context.AllAttributes["summary"].Value;
|
||||
if (summaryLength is HtmlString sumLenStr)
|
||||
{
|
||||
if (int.TryParse(sumLenStr.Value, out var sumLen))
|
||||
{
|
||||
if (text.Length > sumLen)
|
||||
{
|
||||
text = text.Substring(0, sumLen) + "(...)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Document document = Document.Parse(text);
|
||||
var html = document.ToHtml(2);
|
||||
using var stringWriter = new StringWriter();
|
||||
html.WriteTo(stringWriter, HtmlEncoder.Default);
|
||||
var processedHere = stringWriter.ToString();
|
||||
output.Content.AppendHtml(processedHere);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// silently render the text
|
||||
output.Content.AppendHtml("<pre>" + text + "</pre>\n");
|
||||
// and an error
|
||||
output.Content.AppendHtml("<pre class=\"parsingError\">" + ex.Message + "</pre>\n");
|
||||
}
|
||||
Document document = Document.Parse(text);
|
||||
var html = document.ToHtml(2);
|
||||
using var stringWriter = new StringWriter();
|
||||
html.WriteTo(stringWriter, HtmlEncoder.Default);
|
||||
var processedHere = stringWriter.ToString();
|
||||
output.Content.AppendHtml(processedHere);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ namespace Yavsc.Helpers
|
||||
);
|
||||
return googleLogin;
|
||||
}
|
||||
public static async Task<UserCredential> GetGoogleCredential(GoogleAuthSettings googleAuthSettings, IDataStore store, string googleUserLoginKey)
|
||||
public static async Task<UserCredential> GetGoogleCredential(GoogleAuthSettings googleAuthSettings, IDataStore store, string googleUserLoginKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(googleUserLoginKey))
|
||||
throw new InvalidOperationException("No Google login");
|
||||
@ -82,7 +82,7 @@ namespace Yavsc.Helpers
|
||||
var token = await store.GetAsync<TokenResponse>(googleUserLoginKey);
|
||||
// token != null
|
||||
var c = SystemClock.Default;
|
||||
if (token.IsStale) {
|
||||
if (token.IsExpired(c)) {
|
||||
token = await RefreshToken(googleAuthSettings, token);
|
||||
}
|
||||
return new UserCredential(flow, googleUserLoginKey, token);
|
||||
|
@ -1,6 +1,6 @@
|
||||
DESTDIR=/srv/www/yavsc
|
||||
CONFIGURATION=Release
|
||||
USER_AND_GROUP=www-data:www-data
|
||||
USER=www-data
|
||||
SERVICE_PROD=yavsc
|
||||
DOTNET_FRAMEWORK=net9.0
|
||||
|
||||
@ -22,17 +22,12 @@ install_service:
|
||||
pushInProd: publish
|
||||
sudo systemctl stop $(SERVICE_PROD)
|
||||
sudo cp -a bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish/* $(DESTDIR)
|
||||
sudo chown -R $(USER_AND_GROUP) $(DESTDIR)
|
||||
sudo chown -R $(USER) $(DESTDIR)
|
||||
sudo sync
|
||||
sudo systemctl start $(SERVICE_PROD)
|
||||
|
||||
%.css: %.scss
|
||||
scss $^ > $@
|
||||
|
||||
%.min.js: %.js
|
||||
jsmin < $^ > $@
|
||||
|
||||
%.min.css: %.css
|
||||
jsmin < $^ > $@
|
||||
|
||||
css: wwwroot/css/site.css
|
||||
|
@ -6,7 +6,7 @@ namespace Yavsc
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -15,7 +15,7 @@ namespace Yavsc
|
||||
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
var app = await builder.ConfigureWebAppServices().ConfigurePipeline();
|
||||
var app = builder.ConfigureWebAppServices().ConfigurePipeline();
|
||||
app.UseSession();
|
||||
app.Run();
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Interfaces;
|
||||
using Yavsc.Models;
|
||||
|
||||
public class ExternalIdentityManager : IExternalIdentityManager
|
||||
{
|
||||
private ApplicationDbContext _applicationDbContext;
|
||||
private SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public ExternalIdentityManager(ApplicationDbContext applicationDbContext, SignInManager<ApplicationUser> signInManager)
|
||||
{
|
||||
_applicationDbContext = applicationDbContext;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
public ApplicationUser AutoProvisionUser(string provider, string providerUserId, List<Claim> claims)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<ApplicationUser?> FindByExternaleProviderAsync(string provider, string providerUserId)
|
||||
{
|
||||
|
||||
var user = await _applicationDbContext.UserLogins
|
||||
.FirstOrDefaultAsync(
|
||||
i => (i.LoginProvider == provider) && (i.ProviderKey == providerUserId)
|
||||
);
|
||||
if (user == null) return null;
|
||||
return await _applicationDbContext.Users.FirstOrDefaultAsync(u=>u.Id == user.UserId);
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Server.Helpers;
|
||||
using Yavsc.Settings;
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace Yavsc.ViewComponents
|
||||
{
|
||||
if (target!=null)
|
||||
{
|
||||
var oid = target.AuthorId;
|
||||
var oid = target.OwnerId;
|
||||
ViewBag.ACL = dbContext.Circle.Where(
|
||||
c=>c.OwnerId == oid)
|
||||
.Select(
|
||||
|
@ -21,7 +21,7 @@
|
||||
<input type="hidden" asp-for="RoleName" />
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Submit"]" class="btn btn-primary" />
|
||||
<input type="submit" value="Submit"]" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
<input type="hidden" asp-for="EnroledUserId" />
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Submit"]" class="btn btn-primary" />
|
||||
<input type="submit" value="Submit"]" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
<h2>@ViewBag.Title</h2>
|
||||
<p>
|
||||
<a asp-action="Enroll" asp-route-RoleName="@Model.Name" class="btn btn-primary" >Enroll</a>
|
||||
<a asp-action="Enroll" asp-route-RoleName="@Model.Name" class="btn btn-default" >Enroll</a>
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
|
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Create" class="btn btn-primary" />
|
||||
<input type="submit" value="Create" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
<form asp-action="Delete">
|
||||
<div class="form-actions no-color">
|
||||
<input type="submit" value="Delete" class="btn btn-primary" /> |
|
||||
<input type="submit" value="Delete" class="btn btn-default" /> |
|
||||
<a asp-action="Index">Back to List</a>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -42,7 +42,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Save" class="btn btn-primary" />
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
@model BlogPostEditViewModel
|
||||
@model Yavsc.ViewModels.Blog.BlogPostInputViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Blog post edition";
|
||||
@ -47,7 +47,6 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mdcoding">
|
||||
<label asp-for="Content" class="col-md-2 control-label" ></label>
|
||||
<div class="col-md-10">
|
||||
@ -57,7 +56,6 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="ACL" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
@ -65,13 +63,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Publish" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Publish" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button class="btn btn-primary" >Save</button>
|
||||
|
@ -51,7 +51,7 @@
|
||||
|
||||
<form asp-action="Delete">
|
||||
<div class="form-actions no-color">
|
||||
<input type="submit" value="Delete" class="btn btn-primary" /> |
|
||||
<input type="submit" value="Delete" class="btn btn-default" /> |
|
||||
<a asp-action="Index">Back to List</a>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -60,14 +60,10 @@ $('#commentValidation').html(
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
<div class="post">
|
||||
<div class="float-left">
|
||||
<img alt="" src="@Model.Photo" >
|
||||
</div>
|
||||
|
||||
<h1 ismarkdown>@Model.Title</h1>
|
||||
|
||||
<div class="container">
|
||||
<div class="blogpost">
|
||||
<h1 class="blogtitle" ismarkdown>@Model.Title</h1>
|
||||
<img class="blogphoto" alt="" src="@Model.Photo" >
|
||||
@Html.DisplayFor(m=>m.Author)
|
||||
<asciidoc>@Html.DisplayFor(m=>m.Content)</asciidoc>
|
||||
|
||||
@ -102,4 +98,4 @@ $('#commentValidation').html(
|
||||
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-link">Edit</a>
|
||||
}
|
||||
<a asp-action="Index" class="btn btn-link">Back to List</a>
|
||||
|
||||
</div>
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
}
|
||||
|
||||
<h2 >Blog post</h2>
|
||||
<h2 > Blog post edition </h2>
|
||||
<label><input type="checkbox" id="vcbtn" />Editer le code source Markdown</label>
|
||||
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
@ -97,16 +97,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Publish" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input type="checkbox" asp-for="Publish" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Save" class="btn btn-primary" />
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -118,3 +111,5 @@
|
||||
<div>
|
||||
<a asp-action="Index">Back to List</a>
|
||||
</div>
|
||||
|
||||
using Yavsc.Migrations;
|
||||
|
@ -1,4 +1,4 @@
|
||||
@model IEnumerable<IBlogPost>
|
||||
@model IEnumerable<IGrouping<string,BlogPost>>
|
||||
@{
|
||||
ViewData["Title"] = "Blogs, l'index";
|
||||
}
|
||||
@ -43,51 +43,58 @@
|
||||
<a asp-action="Create">Create a new article</a>
|
||||
</p>
|
||||
}
|
||||
|
||||
<div class="blog">
|
||||
@{
|
||||
int maxTextLen = 75;
|
||||
foreach (var post in Model) {
|
||||
<div class="post">
|
||||
|
||||
|
||||
<div class="float-left" >
|
||||
<a asp-action="Details" asp-route-id="@post.Id" class="bloglink" style="display: float-left;">
|
||||
<img src="@post.Photo" >
|
||||
</a>
|
||||
</div>
|
||||
<h3>@post.Title</h3>
|
||||
<div>
|
||||
<a asp-action="Details" asp-route-id="@post.Id">
|
||||
<asciidoc summary="@maxTextLen">@post.Content</asciidoc></a>
|
||||
<span style="font-size:x-small;">@Html.DisplayFor(m => post.Author)</span>
|
||||
<span style="font-size:xx-small;">
|
||||
posté le @post.DateCreated.ToString("dddd d MMM yyyy à H:mm")
|
||||
@if ((post.DateModified - post.DateCreated).Minutes > 0){
|
||||
@:- Modifié le @post.DateModified.ToString("dddd d MMM yyyy à H:mm")
|
||||
})
|
||||
</span>
|
||||
</div>
|
||||
<div class="actiongroup">
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, post, new ReadPermission())).Succeeded)
|
||||
{
|
||||
<a asp-action="Details" asp-route-id="@post.Id" class="btn btn-light">Details</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-action="Details" asp-route-id="@post.Id" class="btn btn-light">Details</a>
|
||||
}
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, post, new EditPermission())).Succeeded)
|
||||
{
|
||||
<a asp-action="Edit" asp-route-id="@post.Id" class="btn btn-primary">Edit</a>
|
||||
|
||||
<a asp-action="Delete" asp-route-id="@post.Id" class="btn btn-danger">Delete</a>
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
|
||||
<table class="table">
|
||||
@foreach (var group in Model) {
|
||||
var title = group.Key ?? "@";
|
||||
string secondclass="";
|
||||
var first = group.First();
|
||||
int maxTextLen = 256;
|
||||
|
||||
<tr><td colspan="3">
|
||||
<a asp-action="Title" asp-route-id="@group.Key" >@title</a></td></tr>
|
||||
@foreach (var item in group) {
|
||||
var trunked = item.Content?.Length > maxTextLen;
|
||||
<tr>
|
||||
<td><a asp-action="Details" asp-route-id="@item.Id" class="bloglink">
|
||||
<img src="@item.Photo" class="blogphoto"></a>
|
||||
</td>
|
||||
<td>
|
||||
<asciidoc summary="@maxTextLen">@item.Content</asciidoc>
|
||||
@if (trunked) { <a asp-action="Details" asp-route-id="@item.Id" class="bloglink">...</a> }
|
||||
<span style="font-size:x-small;">@Html.DisplayFor(m => item.Author)</span>
|
||||
<span style="font-size:xx-small;">
|
||||
posté le @item.DateCreated.ToString("dddd d MMM yyyy à H:mm")
|
||||
@if ((item.DateModified - item.DateCreated).Minutes > 0){
|
||||
@:- Modifié le @item.DateModified.ToString("dddd d MMM yyyy à H:mm")
|
||||
})
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ul class="actiongroup">
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new ReadPermission())).Succeeded) {
|
||||
<li>
|
||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-lg">Details</a>
|
||||
</li>
|
||||
}
|
||||
else {
|
||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-lg">Details DEBUG</a>
|
||||
}
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new EditPermission())).Succeeded) {
|
||||
<li><a asp-action="Edit" asp-route-id="@item.Id" class="btn btn-default">Edit</a>
|
||||
</li>
|
||||
<li><a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-danger">Delete</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</table>
|
||||
|
||||
@if(Model.Count()==0){<p>Néant</p>}
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -28,11 +28,11 @@
|
||||
<ul class="actiongroup">
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new ReadPermission())).Succeeded) {
|
||||
<li>
|
||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-light">Details</a>
|
||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-lg">Details</a>
|
||||
</li>
|
||||
}
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new EditPermission())).Succeeded) {
|
||||
<li><a asp-action="Edit" asp-route-id="@item.Id" class="btn btn-primary">Edit</a>
|
||||
<li><a asp-action="Edit" asp-route-id="@item.Id" class="btn btn-default">Edit</a>
|
||||
</li>
|
||||
<li><a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-danger">Delete</a>
|
||||
</li>
|
||||
|
@ -183,7 +183,7 @@
|
||||
|
||||
<form asp-action="Delete">
|
||||
<div class="form-actions no-color">
|
||||
<input type="submit" value="Delete" class="btn btn-primary" /> |
|
||||
<input type="submit" value="Delete" class="btn btn-default" /> |
|
||||
<a asp-action="Index">Annuler</a>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -308,7 +308,7 @@
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Save" class="btn btn-primary" />
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Create" class="btn btn-primary" />
|
||||
<input type="submit" value="Create" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
<form asp-action="Delete">
|
||||
<div class="form-actions no-color">
|
||||
<input type="submit" value="Delete" class="btn btn-primary" /> |
|
||||
<input type="submit" value="Delete" class="btn btn-default" /> |
|
||||
<a asp-action="Index">Back to List</a>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -46,7 +46,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Save" class="btn btn-primary" />
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Create" class="btn btn-primary" />
|
||||
<input type="submit" value="Create" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
<form asp-action="Delete">
|
||||
<div class="form-actions no-color">
|
||||
<input type="submit" value="Delete" class="btn btn-primary" /> |
|
||||
<input type="submit" value="Delete" class="btn btn-default" /> |
|
||||
<a asp-action="Index">Back to List</a>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -28,7 +28,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Save" class="btn btn-primary" />
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Create" class="btn btn-primary" />
|
||||
<input type="submit" value="Create" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -26,7 +26,7 @@ Membre
|
||||
</div>
|
||||
<form asp-action="Delete">
|
||||
<div class="form-actions no-color">
|
||||
<input type="submit" value="Delete" class="btn btn-primary" /> |
|
||||
<input type="submit" value="Delete" class="btn btn-default" /> |
|
||||
<a asp-action="Index">Back to List</a>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<input type="hidden" asp-for="CircleId" />
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Save" class="btn btn-primary" />
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user