Login registration

main
Paul Schneider 1 year ago
parent 009015ce3c
commit 0bcabbd4ff
22 changed files with 171 additions and 176 deletions

@ -8,27 +8,28 @@ namespace Yavsc.ViewModels.Account
public class RegisterModel public class RegisterModel
{ {
[YaStringLength(2,Constants.MaxUserNameLength)] [StringLength(Constants.MaxUserNameLength)]
[YaRegularExpression(Constants.UserNameRegExp)] [RegularExpression(Constants.UserNameRegExp)]
[DataType(DataType.Text)]
[Display(Name = "UserName", Description = "User name")]
public string UserName { get; set; } public string UserName { get; set; }
[YaRequired()] [Required()]
[YaStringLength(2,102)] [StringLength( maximumLength:102, MinimumLength = 5)]
// [EmailAddress] // [EmailAddress]
[Display(Name = "Email")] [Display(Name = "Email", Description = "E-Mail")]
public string Email { get; set; } public string Email { get; set; }
[YaStringLength(6,100)] [StringLength(maximumLength:100, MinimumLength = 6,
ErrorMessage = "Le mot de passe doit contenir au moins 8 caratères")]
[DataType(DataType.Password)] [DataType(DataType.Password)]
[Display(Name = "Password")]
// ErrorMessage = "Les mots de passe doivent contenir au moins un caractère spécial, qui ne soit ni une lettre ni un chiffre.")]
public string Password { get; set; } public string Password { get; set; }
[DataType(DataType.Password)] [DataType(DataType.Password)]
[Compare("Password")] [Compare("Password")]
[Display(Name = "ConfirmPassword", Description ="Password Confirmation")]
public string ConfirmPassword { get; set; } public string ConfirmPassword { get; set; }
} }
} }

@ -19,23 +19,15 @@ namespace Yavsc
public const string CompanyClaimType = "https://schemas.pschneider.fr/identity/claims/Company"; 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 UserNameRegExp = @"^[a-zA-Z][a-zA-Z0-9._-]*$";
public const string UserFileNamePatternRegExp = @"^([a-zA-Z0-9._-]*/)*[a-zA-Z0-9._-]+$"; public const string UserFileNamePatternRegExp = @"^([a-zA-Z0-9._-]*/)*[a-zA-Z0-9._-]+$";
public const string AuthorizePath = "/authorize";
public const string TokenPath = "/token";
public const string LoginPath = "/signin"; public const string LoginPath = "/signin";
public const string LogoutPath = "/signout"; public const string LogoutPath = "/signout";
public const string SignalRPath = "/api/signalr";
public const string UserFilesPath = "/files"; public const string UserFilesPath = "/files";
public const string AvatarsPath = "/avatars"; public const string AvatarsPath = "/avatars";
public const string GitPath = "/sources"; public const string GitPath = "/sources";
public const string LiveUserPath = "live";
public const string ApplicationAuthenticationSheme = "ServerCookie";
public const string ExternalAuthenticationSheme = "ExternalCookie";
public const string DefaultFactor = "Default"; public const string DefaultFactor = "Default";
public const string MobileAppFactor = "Mobile Application"; public const string MobileAppFactor = "Mobile Application";
public const string EMailFactor = "Email";
public const string SMSFactor = "SMS"; public const string SMSFactor = "SMS";
public const string AdminGroupName = "Administrator"; public const string AdminGroupName = "Administrator";
public const string PerformerGroupName = "Performer"; public const string PerformerGroupName = "Performer";

@ -157,40 +157,7 @@ namespace Yavsc.Controllers
// only set explicit expiration here if user chooses "remember me". // only set explicit expiration here if user chooses "remember me".
// otherwise we rely upon expiration configured in cookie middleware. // otherwise we rely upon expiration configured in cookie middleware.
AuthenticationProperties props = null; await HttpContext.SignInAsync(user, _roleManager, model.RememberLogin,_dbContext);
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration),
// Parameters =
};
};
// roles
var roles = _dbContext.UserRoles.Where(r=>r.UserId == user.Id).ToArray();
// issue authentication cookie with subject ID and username
List<Claim> additionalClaims = new List<Claim>();
foreach (var role in roles)
{
var idRole = await _roleManager.Roles.SingleOrDefaultAsync(i=>i.Id == role.RoleId);
if (idRole != null)
{
additionalClaims.Add(new Claim(ClaimTypes.Role, idRole.Name));
}
}
additionalClaims.Add(new Claim(ClaimTypes.Name, user.UserName));
var isUser = new IdentityServerUser(user.Id)
{
DisplayName = user.UserName,
AdditionalClaims = additionalClaims.ToArray()
};
await HttpContext.SignInAsync(isUser, props);
if (context != null) if (context != null)
{ {
@ -231,7 +198,6 @@ namespace Yavsc.Controllers
return View(vm); return View(vm);
} }
/// <summary> /// <summary>
/// Show logout page /// Show logout page
/// </summary> /// </summary>

@ -392,13 +392,13 @@ namespace Yavsc.Controllers
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
} }
public IActionResult ChangeUserName() public IActionResult SetUserName()
{ {
return View(new ChangeUserNameViewModel() { NewUserName = User.Identity.Name }); return View(new SetUserNameViewModel() { UserName = User.Identity.Name });
} }
[HttpPost] [HttpPost]
public async Task<IActionResult> ChangeUserName(ChangeUserNameViewModel model) public async Task<IActionResult> SetUserName(SetUserNameViewModel model)
{ {
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
@ -409,7 +409,7 @@ namespace Yavsc.Controllers
{ {
var oldUserName = user.UserName; var oldUserName = user.UserName;
var result = await this._userManager.SetUserNameAsync(user, model.NewUserName); var result = await this._userManager.SetUserNameAsync(user, model.UserName);
if (result.Succeeded) if (result.Succeeded)
{ {
@ -418,7 +418,7 @@ namespace Yavsc.Controllers
Path.Combine(_siteSettings.Blog, Path.Combine(_siteSettings.Blog,
oldUserName)); oldUserName));
var newdir = Path.Combine(_siteSettings.Blog, var newdir = Path.Combine(_siteSettings.Blog,
model.NewUserName); model.UserName);
if (userdirinfo.Exists) if (userdirinfo.Exists)
userdirinfo.MoveTo(newdir); userdirinfo.MoveTo(newdir);
// Renames the Avatars files // Renames the Avatars files
@ -429,7 +429,7 @@ namespace Yavsc.Controllers
oldUserName+s)); oldUserName+s));
if (fi.Exists) if (fi.Exists)
fi.MoveTo(Path.Combine(_siteSettings.Avatars, fi.MoveTo(Path.Combine(_siteSettings.Avatars,
model.NewUserName+s)); model.UserName+s));
} }
await _signInManager.SignInAsync(user, isPersistent: false); await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User changed his user name successfully."); _logger.LogInformation(3, "User changed his user name successfully.");

@ -153,10 +153,6 @@ namespace Yavsc.Controllers
return NotFound(); return NotFound();
} }
ViewBag.Files = Yavsc.Helpers.FileSystemHelpers.GetFileName(null);
// Yavsc.Helpers.GetUserFiles(User, null);
return View(estimate); return View(estimate);
} }

@ -67,8 +67,7 @@ namespace Yavsc.Controllers
} }
public async Task<IActionResult> About() public async Task<IActionResult> About()
{ {
FileInfo fi = new FileInfo("wwwroot/version"); return View("About");
return View("About", fi.Exists ? _localizer["Version logicielle: "] + await fi.OpenText().ReadToEndAsync() : _localizer["Aucune information sur la version logicielle n'est publiée."]);
} }
public IActionResult Privacy() public IActionResult Privacy()
{ {

@ -1,9 +1,6 @@
using System.Globalization; using System.Globalization;
using System.Security.Permissions;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Util.Store; using Google.Apis.Util.Store;
using IdentityServer4; using IdentityServer4;
using IdentityServer4.Test;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
@ -11,7 +8,6 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
@ -25,7 +21,6 @@ using Yavsc.Interface;
using Yavsc.Models; using Yavsc.Models;
using Yavsc.Models.Billing; using Yavsc.Models.Billing;
using Yavsc.Models.Haircut; using Yavsc.Models.Haircut;
using Yavsc.Models.Market;
using Yavsc.Models.Workflow; using Yavsc.Models.Workflow;
using Yavsc.Services; using Yavsc.Services;
using Yavsc.Settings; using Yavsc.Settings;
@ -36,8 +31,7 @@ namespace Yavsc.Extensions;
internal static class HostingExtensions internal static class HostingExtensions
{ {
public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app,
public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app,
bool enableDirectoryBrowsing = false) bool enableDirectoryBrowsing = false)
{ {
@ -180,8 +174,9 @@ internal static class HostingExtensions
.AddEntityFrameworkStores<ApplicationDbContext>() .AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders(); .AddDefaultTokenProviders();
services
.AddIdentityServer(options =>
services.AddIdentityServer(options =>
{ {
options.Events.RaiseErrorEvents = true; options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true; options.Events.RaiseInformationEvents = true;
@ -194,7 +189,7 @@ internal static class HostingExtensions
.AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients) .AddInMemoryClients(Config.Clients)
.AddDeveloperSigningCredential() .AddAspNetIdentity<ApplicationUser>()
; ;
services.AddSession(); services.AddSession();

@ -0,0 +1,54 @@
using System.Security.Claims;
using IdentityServer4;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Yavsc.Models;
using Yavsc.Models.Access;
namespace Yavsc.Extensions;
internal static class HttpContextExtensions
{
public static async Task SignInAsync(this HttpContext context,
ApplicationUser user, RoleManager<IdentityRole> roleManager,
bool rememberMe,
ApplicationDbContext applicationDbContext)
{
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin && rememberMe)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration),
// Parameters =
};
};
// roles
var roles = applicationDbContext.UserRoles.Where(r => r.UserId == user.Id).ToArray();
// issue authentication cookie with subject ID and username
List<Claim> additionalClaims = new List<Claim>();
foreach (var role in roles)
{
var idRole = await roleManager.Roles.SingleOrDefaultAsync(i => i.Id == role.RoleId);
if (idRole != null)
{
additionalClaims.Add(new Claim(ClaimTypes.Role, idRole.Name));
}
}
additionalClaims.Add(new Claim(ClaimTypes.Name, user.UserName));
var isUser = new IdentityServerUser(user.Id)
{
DisplayName = user.UserName,
AdditionalClaims = additionalClaims.ToArray()
};
await context.SignInAsync(isUser, props);
}
}

@ -65,6 +65,11 @@ namespace Yavsc.Helpers
} }
} }
public static string GetAvatarUri(this ApplicationUser user)
{
return $"/{Config.SiteSetup.Avatars}/{user.UserName}.png";
}
public static string InitPostToFileSystem( public static string InitPostToFileSystem(
this ClaimsPrincipal user, this ClaimsPrincipal user,
string subpath) string subpath)

@ -1,14 +0,0 @@
using System.ComponentModel.DataAnnotations;
using Yavsc.Attributes.Validation;
namespace Yavsc.ViewModels.Manage
{
public class ChangeUserNameViewModel
{
[YaRequired]
[Display(Name = "New user name"),RegularExpression(Constants.UserNameRegExp)]
public string NewUserName { get; set; }
}
}

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
using Yavsc.Attributes.Validation;
namespace Yavsc.ViewModels.Manage
{
public class SetUserNameViewModel
{
[Required]
[Display(Name = "User name"),RegularExpression(Constants.UserNameRegExp)]
public string UserName { get; set; }
}
}

@ -0,0 +1,8 @@
@model RegisterModel
<partial name="_ValidationSummary" />
<form method="post">
@Html.EditorForModel()
<button class="btn btn-primary" name="button"
value="Register">Register</button>
</form>

@ -69,22 +69,7 @@ $('span.field-validation-valid[data-valmsg-for="Content"]').html(
<h1 class="blogtitle" ismarkdown>@Model.Title</h1> <h1 class="blogtitle" ismarkdown>@Model.Title</h1>
<img class="blogphoto" alt="" src="@Model.Photo" > <img class="blogphoto" alt="" src="@Model.Photo" >
<div class="blogpost"> <div class="blogpost">
@Html.AsciiDocFor(model => model.Content) @Html.DisplayForModel()
<p class="blog" asp-for="Content"></p>
<hr/>
<div class="meta">
@Html.DisplayFor(model => model.Author)
@Html.DisplayNameFor(model => model.DateModified) :
@Html.DisplayFor(model => model.DateModified)
@Html.DisplayNameFor(model => model.DateCreated) :
@Html.DisplayFor(model => model.DateCreated)
@await Component.InvokeAsync("Tagger",Model)
</div> </div>
<div id="comments"> <div id="comments">

@ -1,14 +1,10 @@
@using System.Diagnostics @using System.Diagnostics
@{
ViewData["Title"] = SR["About"] + " " + SiteSettings.Value.Title; <h1>@SiteSettings.Value.Title - À Propos</h1>
}
<h1>@ViewData["Title"]</h1>
**Version de Development** **Version de Development**
<asciidoc> <asciidoc>
= À propos de Yavsc
== L'objectif == L'objectif
Cette application est construite pour mettre en relation des artistes Cette application est construite pour mettre en relation des artistes
@ -91,38 +87,4 @@ et programme la suppression complète de ces dites informations dans les quinze
à compter de la demande, sauf demande contradictoire. à compter de la demande, sauf demande contradictoire.
L'opération est annulable, jusqu'à deux semaines après sa programmation. L'opération est annulable, jusqu'à deux semaines après sa programmation.
</asciidoc> </asciidoc>
<p> <p>@Model</p>
@Model
</p>
@{
var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First();
}
<div class="welcome-page">
<h1>
<img src="~/icon.jpg">
Welcome to IdentityServer4
<small class="text-muted">(version @version)</small>
</h1>
<ul>
<li>
IdentityServer publishes a
<a href="~/.well-known/openid-configuration">discovery document</a>
where you can find metadata and links to all the endpoints, key material, etc.
</li>
<li>
Click <a href="~/diagnostics">here</a> to see the claims for your current session.
</li>
<li>
Click <a href="~/grants">here</a> to manage your stored grants.
</li>
<li>
Here are links to the
<a href="https://github.com/identityserver/IdentityServer4">source code repository</a>,
and <a href="https://github.com/IdentityServer/IdentityServer4/tree/main/samples">ready to use samples</a>.
</li>
</ul>
</div>

@ -1,16 +1,13 @@
@{ <h1>@SiteSettings.Value.Title - objetivo</h1>
ViewData["Title"] = @SR["About"]+" "+@SiteSettings.Value.Title;
}
<h1>@ViewData["Title"]</h1>
<environment names="freefield,Development"> <environment names="freefield,Development">
<asciidoc> <asciidoc>
## O objetivo = O objetivo
Esta aplicação é construída para conectar artistas Esta aplicação é construída para conectar artistas
do campo musical com seu público. do campo musical com seu público.
## Operation == Operation
Os usuários do site são artista, cliente ou administrador. Todos eles têm direito ao seu blog. Os usuários do site são artista, cliente ou administrador. Todos eles têm direito ao seu blog.
Para artistas, é uma maneira de promover seus negócios. Para artistas, é uma maneira de promover seus negócios.
@ -52,7 +49,7 @@ Depois que o serviço associado tiver sido executado, os pagamentos relativos se
Para um contrato executado e não honrado pelo cliente, o processo de processo de recuperação é contratado, caso contrário, o contrato é arquivado, Para um contrato executado e não honrado pelo cliente, o processo de processo de recuperação é contratado, caso contrário, o contrato é arquivado,
Os certificados de pagamento estão disponíveis para o artista e a fatura é marcada como paga e depois repassada ao cliente. Os certificados de pagamento estão disponíveis para o artista e a fatura é marcada como paga e depois repassada ao cliente.
### Para o artista === Para o artista
O artista escolhe vários parâmetros que farão o seu perfil: O artista escolhe vários parâmetros que farão o seu perfil:
@ -65,7 +62,7 @@ O artista escolhe vários parâmetros que farão o seu perfil:
* Parâmetros adicionais dependendo do tipo de atividade, por exemplo, para * Parâmetros adicionais dependendo do tipo de atividade, por exemplo, para
conjuntos, seu tamanho, se houver, seu repertório ou indicações do estilo de sua música) conjuntos, seu tamanho, se houver, seu repertório ou indicações do estilo de sua música)
### Para o cliente === Para o cliente
Ele escolhe um lugar e uma data para declarar um evento futuro Ele escolhe um lugar e uma data para declarar um evento futuro
(Ele pode programar o quanto quiser). (Ele pode programar o quanto quiser).
@ -75,7 +72,7 @@ com base em um de seus projetos de eventos, a negociação de um contrato de ser
Ele tem acesso ao conhecimento de dias conhecidos como artistas livres pelo sistema. Ele tem acesso ao conhecimento de dias conhecidos como artistas livres pelo sistema.
## Confidencialidade == Confidencialidade
Em nenhum momento, nenhum endereço de correspondência, nenhum endereço de e-mail e nenhum número de telefone Em nenhum momento, nenhum endereço de correspondência, nenhum endereço de e-mail e nenhum número de telefone
não são transmitidos para clientes ou artistas. Apenas o sistema tem acesso a essas informações. não são transmitidos para clientes ou artistas. Apenas o sistema tem acesso a essas informações.
@ -158,6 +155,4 @@ A "pré-produção" exibe os seguintes sites:
</asciidoc> </asciidoc>
</environment> </environment>
<p> <p>@Model</p>
@Model
</p>

@ -0,0 +1,32 @@
@using System.Diagnostics
@{
var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First();
}
<div class="welcome-page">
<h1>
<img src="~/icon.jpg">
Welcome to IdentityServer4
<small class="text-muted">(version @version)</small>
</h1>
<ul>
<li>
IdentityServer publishes a
<a href="~/.well-known/openid-configuration">discovery document</a>
where you can find metadata and links to all the endpoints, key material, etc.
</li>
<li>
Click <a href="~/diagnostics">here</a> to see the claims for your current session.
</li>
<li>
Click <a href="~/grants">here</a> to manage your stored grants.
</li>
<li>
Here are links to the
<a href="https://github.com/identityserver/IdentityServer4">source code repository</a>,
and <a href="https://github.com/IdentityServer/IdentityServer4/tree/main/samples">ready to use samples</a>.
</li>
</ul>
</div>

@ -14,18 +14,19 @@
<dd> <dd>
@Model.UserName @Model.UserName
<a asp-action="SetUserName">[modifier]</a>
</dd> </dd>
<dt>EMail:</dt> <dt>E-mail</dt>
<dd> <dd>
@Model.EMail @Model.EMail
@if (Model.EmailConfirmed) { @if (Model.EmailConfirmed) {
<text>(Adresse E-mail confirmée"])</text> <text>(Adresse E-mail confirmée)</text>
} else { } else {
<text> <text>
<i> (Adresse non confirmée."])</i> <i> (Adresse non confirmée.)</i>
<form asp-action="SendConfirationEmail" asp-controller="Account" enctype="multipart/form-data"> <form asp-action="SendConfirationEmail" asp-controller="Account" enctype="multipart/form-data">
<input type="submit" value="Confirmer cette adresse"]"/> <input type="submit" value="Confirmer cette adresse"]"/>
</form> </form>
@ -35,7 +36,7 @@
<dd> <dd>
<dl> <dl>
<dt>AllowMonthlyEmail:</dt> <dt>Allow Monthly Email</dt>
<dd> <dd>
<a asp-action="ProfileEMailUsage"> @Html.DisplayFor(m=>m.AllowMonthlyEmail) [modifier] <a asp-action="ProfileEMailUsage"> @Html.DisplayFor(m=>m.AllowMonthlyEmail) [modifier]
</a> </a>
@ -46,7 +47,8 @@
<dt>FullName:</dt> <dt>FullName:</dt>
<dd> <dd>
@Html.DisplayFor(m=>m.FullName) <a asp-action="SetFullName">[modifier]</a> @Html.DisplayFor(m=>m.FullName)
<a asp-action="SetFullName">[modifier]</a>
</dd> </dd>
@if (Model.Roles.Count()>0) { @if (Model.Roles.Count()>0) {

@ -1,19 +1,19 @@
@model ChangeUserNameViewModel @model SetUserNameViewModel
@{ @{
ViewData["Title"] = "Changer de nom d'utilisateur"; ViewData["Title"] = "Changer de nom d'utilisateur";
} }
<h2>@ViewData["Title"].</h2> <h2>@ViewData["Title"].</h2>
<form asp-controller="Manage" asp-action="ChangeUserName" method="post" class="form-horizontal" role="form"> <form asp-controller="Manage" asp-action="SetUserName" method="post" class="form-horizontal" role="form">
<h4>Change user name form</h4> <h4>Change user name form</h4>
<hr /> <hr />
<div asp-validation-summary="All" class="text-danger"></div> <div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group"> <div class="form-group">
<label asp-for="NewUserName" class="col-md-2 control-label"></label> <label asp-for="UserName" class="col-md-2 control-label"></label>
<div class="col-md-10"> <div class="col-md-10">
<input asp-for="NewUserName" class="form-control" /> <input asp-for="UserName" class="form-control" />
<span asp-validation-for="NewUserName" class="text-danger"></span> <span asp-validation-for="UserName" class="text-danger"></span>
</div> </div>
</div> </div>
</form> </form>

@ -34,7 +34,7 @@
<td> <td>
<asciidoc summary="256">@item.Content</asciidoc> <asciidoc summary="256">@item.Content</asciidoc>
@if (trunked) { <a asp-action="Details" asp-route-id="@item.Id" class="bloglink">...</a> } @if (trunked) { <a asp-action="Details" asp-route-id="@item.Id" class="bloglink">...</a> }
<span style="font-size:x-small;">(@item.Author.UserName </span>, <span style="font-size:x-small;">(@Html.DisplayFor(m => item.Author)</span>,
<span style="font-size:xx-small;"> <span style="font-size:xx-small;">
posté le @item.DateCreated.ToString("dddd d MMM yyyy à H:mm") posté le @item.DateCreated.ToString("dddd d MMM yyyy à H:mm")
@if ((item.DateModified - item.DateCreated).Minutes > 0){  @if ((item.DateModified - item.DateCreated).Minutes > 0){ 

@ -1,12 +1,14 @@
@model ApplicationUser @model ApplicationUser
@{ @{
var avuri = "/Avatars/"+Model.UserName+".s.png"; var avuri = "/Avatars/"+Model.UserName+".s.png";
var userPosted = Model.Posts!=null && Model.Posts.Count()>1;
} }
<div class="userinfo"> <div class="userinfo">
<h3> @if (userPosted) { <a asp-controller="Blogspot" asp-action="UserPosts"
<img src="@avuri" asp-append-version="true" class="smalltofhol" /> asp-route-id="@Model.UserName" class="btn btn-primary">
</h3> <img src="@avuri" asp-append-version="true" class="smalltofhol" alt="" title="@Model.UserName"/>
@if (Model.Posts!=null && Model.Posts.Count()>1) { <a asp-controller="Blogspot" asp-action="UserPosts" </a>
asp-route-id="@Model.UserName" class="btn btn-primary">index de ses articles</a> }else {
Html.LabelFor(m=>m.UserName);
} }
</div> </div>

@ -35,7 +35,7 @@
@addTagHelper *, Yavsc @addTagHelper *, Yavsc
@inject IAuthorizationService AuthorizationService @inject IAuthorizationService AuthorizationService
@inject Microsoft.AspNetCore.Mvc.Localization.IHtmlLocalizer<Yavsc.Startup> SR @inject Microsoft.AspNetCore.Mvc.Localization.IHtmlLocalizer<Yavsc.YavscLocalization> SR
@inject Microsoft.Extensions.Options.IOptions<SiteSettings> SiteSettings @inject Microsoft.Extensions.Options.IOptions<SiteSettings> SiteSettings
@inject SignInManager<ApplicationUser> SignInManager @inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager @inject UserManager<ApplicationUser> UserManager

@ -13,6 +13,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="IdentityServer4" Version="4.1.2" /> <PackageReference Include="IdentityServer4" Version="4.1.2" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="4.1.2" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" /> <PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />

Loading…