Compare commits

..

4 Commits

@ -1,71 +0,0 @@
kind: pipeline
name: CI
type: docker
trigger:
event:
- push
steps:
- name: build
image: mcr.microsoft.com/dotnet/sdk:6.0
commands:
- cd WF-WebAdmin/WF-WebAdmin
- dotnet restore WF-WebAdmin.csproj
- dotnet build WF-WebAdmin.csproj -c Release --no-restore
- dotnet publish WF-WebAdmin.csproj -c Release --no-restore -o $CI_PROJECT_DIR/build/publish
- name: tests
image: mcr.microsoft.com/dotnet/sdk:6.0
commands:
- cd WF-WebAdmin/WF-WebAdmin
- dotnet restore WF-WebAdmin.csproj
- dotnet test WF-WebAdmin.csproj --no-restore
depends_on: [ build ]
- name: code-analysis
image: hub.codefirst.iut.uca.fr/marc.chevaldonne/codefirst-dronesonarplugin-dotnet8
commands:
- cd WF-WebAdmin/
- dotnet restore WF-WebAdmin.sln
- dotnet sonarscanner begin /k:$${project_key} /d:sonar.host.url=$${sonar_host} /d:sonar.coverageReportPaths="coveragereport/SonarQube.xml" /d:sonar.coverage.exclusions=$${coverage_exclusions} /d:sonar.login=$${sonar_token}
- dotnet build WF-WebAdmin.sln -c Release --no-restore
- dotnet test WF-WebAdmin.sln --logger trx --no-restore /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --collect "XPlat Code Coverage"
- reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport"
- dotnet publish WF-WebAdmin.sln -c Release --no-restore -o $CI_PROJECT_DIR/build/release
- dotnet sonarscanner end /d:sonar.login=$${sonar_token}
secrets: [ SECRET_SONAR_LOGIN ]
environment:
sonar_host: https://codefirst.iut.uca.fr/sonar/
sonar_token:
from_secret: sonar_token
project_key: web_admin
coverage_exclusions: "Tests/**"
depends_on: [ tests ]
- name: generate-and-deploy-docs
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-docdeployer
failure: ignore
volumes:
- name: docs
path: /docs
commands:
- /entrypoint.sh
when:
branch:
- master
depends_on: [ build ]
- name: docker_build
image: plugins/docker
settings:
repo: hub.codefirst.iut.uca.fr/whatthefantasy/wf-webadmin
registry: hub.codefirst.iut.uca.fr
dockerfile: Docker/Dockerfile
tags:
- latest
username:
from_secret: docker_username
password:
from_secret: docker_password
depends_on: [ build, tests, code-analysis ]

@ -1,27 +0,0 @@
# 1. Étape de build (SDK .NET 6)
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
# Copier le csproj et restaurer les dépendances
COPY WF-WebAdmin/WF-WebAdmin/WF-WebAdmin.csproj ./
RUN dotnet restore WF-WebAdmin.csproj
# Copier le reste du code et compiler
COPY WF-WebAdmin/WF-WebAdmin/ ./
RUN dotnet publish WF-WebAdmin.csproj -c Release -o /app/publish
# 2. Étape finale (runtime .NET 6)
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS final
WORKDIR /app
# Désactiver le rechargement de config pour éviter les erreurs inotify
ENV ASPNETCORE_HOSTBUILDER__RELOADCONFIGONCHANGE=false
# Copier les binaires publiés
COPY --from=build /app/publish ./
# Exposer le port HTTP (80) ; adapte si besoin
EXPOSE 80
# Lancement
ENTRYPOINT ["dotnet", "WF-WebAdmin.dll"]

@ -60,22 +60,21 @@ L'application devrait se lancer automatiquement dans votre navigateur par défau
# Blazor Apps (30 points) # Blazor Apps (30 points)
🟨 En cours / ✅ Fait / ❌ Pas fait<br/><br/> 🟨 En cours / ✅ Fait / ❌ Pas fait<br/><br/>
✅ Mise en place d'une page de visualisation des données avec pagination (2 points) <br/> ✅ Mise en place d'une page de visualisation des données avec pagination (2 points) <br/>
✅ Page d'ajout d'un élement avec validation (2 point)<br/> ✅ Page d'ajout d'un élement avec validation (2 point)<br/>
✅ Page d'édition d'un élement avec validation (2 point)<br/> ✅ Page d'édition d'un élement avec validation (2 point)<br/>
✅ Supression d'un élement avec une confirmation (2 point)<br/> ✅ Supression d'un élement avec une confirmation (2 point)<br/>
Composant complexe (5 point)<br/> 🟨 Composant complexe (5 point)<br/>
🟨 Use API (Get / Insert / Update / Delete) (3 point)<br/> 🟨 Use API (Get / Insert / Update / Delete) (3 point)<br/>
Utilisation IOC & DI (4 point)<br/> Utilisation IOC & DI (4 point)<br/>
Localisation & Globalisation (au moins deux langues) (1 point) <br/> 🟨 Localisation & Globalisation (au moins deux langues) (1 point) <br/>
Utilisation de la configuration (1 point)<br/> Utilisation de la configuration (1 point)<br/>
Logs (2 point)<br/> 🟨 Logs (2 point)<br/>
🟨 Propreté du code (Vous pouvez vous servir de sonarqube) (2 point)<br/> Propreté du code (Vous pouvez vous servir de sonarqube) (2 point)<br/>
✅ IHM (Design global, placement des boutons, ...) (2 point)<br/> ✅ IHM (Design global, placement des boutons, ...) (2 point)<br/>
✅ Emplacement du code (Pas de code dans les vues) (2 point)<br/> ✅ Emplacement du code (Pas de code dans les vues) (2 point)<br/>
# Documentation (10 points) # Documentation (10 points)
Le Readme (2 points)<br/> 🟨Le Readme (2 points)<br/>
Description du fonctionnement de la solution client (illustrutration au niveau du code) (6 points)<br/> Description du fonctionnement de la solution client (illustrutration au niveau du code) (6 points)<br/>
Merge request (2 points)<br/> ✅Merge request (2 points)<br/>

@ -1,11 +0,0 @@
namespace UnitTestWF
{
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}
}

@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
</Project>

@ -5,8 +5,6 @@ VisualStudioVersion = 17.9.34723.18
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WF-WebAdmin", "WF-WebAdmin\WF-WebAdmin.csproj", "{0E8D1007-ADDC-4103-847D-8EE48ACCDC62}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WF-WebAdmin", "WF-WebAdmin\WF-WebAdmin.csproj", "{0E8D1007-ADDC-4103-847D-8EE48ACCDC62}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestWF", "UnitTestWF\UnitTestWF.csproj", "{A4EC0EC4-7A46-4F8E-99C3-4FD32F173F2F}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -17,10 +15,6 @@ Global
{0E8D1007-ADDC-4103-847D-8EE48ACCDC62}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E8D1007-ADDC-4103-847D-8EE48ACCDC62}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E8D1007-ADDC-4103-847D-8EE48ACCDC62}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E8D1007-ADDC-4103-847D-8EE48ACCDC62}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E8D1007-ADDC-4103-847D-8EE48ACCDC62}.Release|Any CPU.Build.0 = Release|Any CPU {0E8D1007-ADDC-4103-847D-8EE48ACCDC62}.Release|Any CPU.Build.0 = Release|Any CPU
{A4EC0EC4-7A46-4F8E-99C3-4FD32F173F2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4EC0EC4-7A46-4F8E-99C3-4FD32F173F2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4EC0EC4-7A46-4F8E-99C3-4FD32F173F2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4EC0EC4-7A46-4F8E-99C3-4FD32F173F2F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

@ -1,23 +0,0 @@
using System;
using System.Text.Json.Serialization;
namespace WF_WebAdmin.Converter
{
public class CommentaryDto
{
[JsonPropertyName("id_comment")]
public int Id { get; set; }
[JsonPropertyName("quote")]
public int Quote { get; set; }
[JsonPropertyName("users")]
public int IdUser { get; set; }
[JsonPropertyName("dateC")]
public string DateCreationRaw { get; set; } = string.Empty;
[JsonPropertyName("comment")]
public string Text { get; set; } = string.Empty;
}
}

@ -1,46 +0,0 @@
using System;
using System.Globalization;
using WF_WebAdmin.Model;
namespace WF_WebAdmin.Converter
{
public static class CommentaryExtensions
{
public static Commentary ToModel(this CommentaryDto dto)
{
DateTime parsedDate = DateTime.MinValue;
if (!string.IsNullOrEmpty(dto.DateCreationRaw) &&
DateTime.TryParseExact(dto.DateCreationRaw, "yyyy-MM-dd",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime result))
{
parsedDate = result;
}
else
{
Console.Out.WriteLine($"Erreur de conversion de la date : {dto.DateCreationRaw}");
}
return new Commentary
{
Id = dto.Id,
IdUser = dto.IdUser,
DateCreation = parsedDate,
Text = dto.Text
};
}
public static CommentaryDto ToDTO(this Commentary model)
{
return new CommentaryDto
{
Id = model.Id,
IdUser = model.IdUser,
DateCreationRaw = model.DateCreation.ToString("yyyy-MM-dd"),
Text = model.Text
};
}
}
}

@ -0,0 +1,22 @@
namespace WF_WebAdmin.Converter
{
/// <summary>
/// Représente un objet de transfert de données (DTO) pour une citation quotidienne.
/// </summary>
public class DailyQuoteDTO
{
/// <summary>
/// Identifiant unique de la citation quotidienne.
/// </summary>
private int Id { get; set; }
/// <summary>
/// Initialise une nouvelle instance de la classe <see cref="DailyQuoteDTO"/> avec un identifiant.
/// </summary>
/// <param name="id">L'identifiant unique de la citation quotidienne.</param>
public DailyQuoteDTO(int id)
{
this.Id = id;
}
}
}

@ -1,12 +0,0 @@
namespace WF_WebAdmin.Converter
{
public class DailyQuoteDto
{
private int Id { get; set; }
public DailyQuoteDto(int id)
{
this.Id = id;
}
}
}

@ -2,11 +2,20 @@
namespace WF_WebAdmin.Converter namespace WF_WebAdmin.Converter
{ {
/// <summary>
/// Fournit des extensions pour convertir des objets de type <see cref="DailyQuote"/> en objets de type <see cref="DailyQuoteDTO"/>.
/// </summary>
public class DailyQuoteExtension public class DailyQuoteExtension
{ {
public DailyQuoteDto DailyQuoteToDto(DailyQuote dq) /// <summary>
/// Convertit une instance de <see cref="DailyQuote"/> en une instance de <see cref="DailyQuoteDTO"/>.
/// </summary>
/// <param name="dq">L'objet <see cref="DailyQuote"/> à convertir.</param>
/// <returns>Une instance de <see cref="DailyQuoteDTO"/> contenant l'identifiant de la citation quotidienne.</returns>
public DailyQuoteDTO DailyQuoteToDTO(DailyQuote dq)
{ {
DailyQuoteDto dailyQuote = new DailyQuoteDto(dq.Id); // Création de l'objet DTO à partir de l'objet DailyQuote
DailyQuoteDTO dailyQuote = new DailyQuoteDTO(dq.Id);
return dailyQuote; return dailyQuote;
} }
} }

@ -0,0 +1,123 @@
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using System;
namespace WF_WebAdmin.Converter
{
/// <summary>
/// Représente un objet Data Transfer Object (DTO) pour une citation.
/// </summary>
public class QuoteDTO
{
/// <summary>
/// Identifiant de la citation.
/// </summary>
public int Id { get; set; }
/// <summary>
/// Contenu de la citation.
/// </summary>
public string Content { get; set; }
/// <summary>
/// Nombre de "likes" de la citation.
/// </summary>
public int Likes { get; set; }
/// <summary>
/// Langue de la citation.
/// </summary>
public string Langue { get; set; }
/// <summary>
/// Indique si la citation est valide.
/// </summary>
public bool IsValide { get; set; }
/// <summary>
/// Raison pour laquelle la citation pourrait être invalide.
/// </summary>
public string? Reason { get; set; }
/// <summary>
/// Identifiant du caractère associé à la citation (si applicable).
/// </summary>
public int? IdCaracter { get; set; }
/// <summary>
/// Nom du caractère associé à la citation (si applicable).
/// </summary>
public string NameCharac { get; set; }
/// <summary>
/// Identifiant de la source de la citation.
/// </summary>
public int? IdSource { get; set; }
/// <summary>
/// Titre de la source de la citation.
/// </summary>
public string TitleSrc { get; set; }
/// <summary>
/// Date de la source de la citation.
/// </summary>
public DateTime DateSrc { get; set; }
/// <summary>
/// Identifiant de l'utilisateur qui a vérifié la citation (si applicable).
/// </summary>
public int? IdUserVerif { get; set; }
/// <summary>
/// Nom de l'utilisateur qui a vérifié la citation.
/// </summary>
public string NameUser { get; set; }
/// <summary>
/// Identifiant de l'image associée à la citation (si applicable).
/// </summary>
public int? IdImg { get; set; }
/// <summary>
/// Chemin de l'image associée à la citation.
/// </summary>
public string ImgPath { get; set; }
/// <summary>
/// Initialise une nouvelle instance de <see cref="QuoteDTO"/>.
/// </summary>
/// <param name="id_quote">L'identifiant de la citation.</param>
/// <param name="content">Le contenu de la citation.</param>
/// <param name="likes">Le nombre de likes de la citation.</param>
/// <param name="langue">La langue de la citation.</param>
/// <param name="isValide">Indique si la citation est valide.</param>
/// <param name="reason">La raison pour laquelle la citation peut être invalide.</param>
/// <param name="id_caracter">L'identifiant du caractère associé à la citation.</param>
/// <param name="name_charac">Le nom du caractère associé à la citation.</param>
/// <param name="id_source">L'identifiant de la source de la citation.</param>
/// <param name="title">Le titre de la source de la citation.</param>
/// <param name="date">La date de la source de la citation.</param>
/// <param name="id_user_verif">L'identifiant de l'utilisateur ayant vérifié la citation.</param>
/// <param name="name_user">Le nom de l'utilisateur ayant vérifié la citation.</param>
/// <param name="id_img">L'identifiant de l'image associée à la citation.</param>
/// <param name="img_path">Le chemin de l'image associée à la citation.</param>
public QuoteDTO(int id_quote, string content, int likes, string langue, bool isValide, string? reason, int? id_caracter, string name_charac, int? id_source, string title, DateTime date, int? id_user_verif, string name_user, int? id_img, string img_path)
{
this.Id = id_quote;
this.Content = content;
this.Likes = likes;
this.Langue = langue;
this.IsValide = isValide;
this.Reason = reason;
this.IdCaracter = id_caracter;
this.NameCharac = name_charac;
this.IdSource = id_source;
this.TitleSrc = title;
this.DateSrc = date;
this.IdUserVerif = id_user_verif;
this.NameUser = name_user;
this.IdImg = id_img;
this.ImgPath = img_path;
}
}
}

@ -1,43 +0,0 @@
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using System;
namespace WF_WebAdmin.Converter
{
public class QuoteDto
{
public int Id { get; set; }
public string Content { get; set; }
public int Likes { get; set; }
public string Langue { get; set; }
public bool IsValide { get; set; }
public string? Reason { get; set; }
public int? IdCaracter { get; set; }
public string NameCharac { get; set; }
public int? IdSource { get; set; }
public string TitleSrc { get; set; }
public DateTime DateSrc { get; set; }
public int? IdUserVerif { get; set; }
public string NameUser { get; set; }
public int? IdImg { get; set; }
public string ImgPath { get; set; }
public QuoteDto(int id_quote,string content,int likes,string langue,bool isValide,string? reason,int? id_caracter,string name_charac,int? id_source,string title,DateTime date,int? id_user_verif,string name_user ,int? id_img,string img_path)
{
this.Id = id_quote;
this.Content = content;
this.Likes = likes;
this.Langue = langue;
this.IsValide = isValide;
this.Reason = reason;
this.IdCaracter = id_caracter;
this.NameCharac = name_charac;
this.IdSource = id_source;
this.TitleSrc = title;
this.DateSrc = date;
this.IdUserVerif = id_user_verif;
this.NameUser = name_user;
this.IdImg = id_img;
this.ImgPath =img_path;
}
}
}

@ -2,17 +2,59 @@
namespace WF_WebAdmin.Converter namespace WF_WebAdmin.Converter
{ {
/// <summary>
/// Classe d'extension pour la conversion entre les objets <see cref="Quote"/> et <see cref="QuoteDTO"/>.
/// </summary>
public class QuoteExtension public class QuoteExtension
{ {
public QuoteDto QuoteToDTO(Quote? q) /// <summary>
/// Convertit un objet <see cref="Quote"/> en un objet <see cref="QuoteDTO"/>.
/// </summary>
/// <param name="q">L'objet <see cref="Quote"/> à convertir.</param>
/// <returns>Un objet <see cref="QuoteDTO"/> contenant les données de l'objet <see cref="Quote"/>.</returns>
public QuoteDTO QuoteToDTO(Quote q)
{ {
QuoteDto quote = new QuoteDto(q.Id, q.Content, q.Like, q.Langue, q.IsValid,null, null,q.Charac,null,q.TitleSrc,q.DateSrc,null,q.UserProposition,null,q.ImgPath); // Conversion des propriétés de Quote en QuoteDTO
QuoteDTO quote = new QuoteDTO(
q.Id,
q.Content,
q.Like,
q.Langue,
q.IsValid,
null, // Reason non défini ici
null, // IdCaracter non défini ici
q.Charac,
null, // IdSource non défini ici
q.TitleSrc,
q.DateSrc,
null, // IdUserVerif non défini ici
q.UserProposition,
null, // IdImg non défini ici
q.ImgPath
);
return quote; return quote;
} }
public Quote DTOToQuote(QuoteDto q) /// <summary>
/// Convertit un objet <see cref="QuoteDTO"/> en un objet <see cref="Quote"/>.
/// </summary>
/// <param name="q">L'objet <see cref="QuoteDTO"/> à convertir.</param>
/// <returns>Un objet <see cref="Quote"/> contenant les données de l'objet <see cref="QuoteDTO"/>.</returns>
public Quote DTOToQuote(QuoteDTO q)
{ {
Quote quote = new Quote(q.Id, q.Content,q.NameCharac,q.ImgPath,q.TitleSrc,q.DateSrc,q.Likes,q.Langue,q.NameUser,q.IsValide); // Conversion des propriétés de QuoteDTO en Quote
Quote quote = new Quote(
q.Id,
q.Content,
q.NameCharac,
q.ImgPath,
q.TitleSrc,
q.DateSrc,
q.Likes,
q.Langue,
q.NameUser,
q.IsValide
);
return quote; return quote;
} }
} }

@ -0,0 +1,56 @@
using WF_WebAdmin.Model;
namespace WF_WebAdmin.Converter
{
/// <summary>
/// Représente un objet Data Transfer Object (DTO) pour un utilisateur.
/// Utilisé pour transmettre les informations d'un utilisateur entre différentes couches de l'application.
/// </summary>
public class UserDTO
{
/// <summary>
/// Obtient ou définit l'image de l'utilisateur.
/// </summary>
public string Image { get; set; }
/// <summary>
/// Obtient ou définit le nom de l'utilisateur.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Obtient ou définit l'adresse email de l'utilisateur.
/// </summary>
public string Email { get; set; }
/// <summary>
/// Obtient ou définit la date de création de l'utilisateur.
/// </summary>
public DateTime DateCreation { get; set; }
/// <summary>
/// Obtient ou définit une valeur indiquant si l'utilisateur est un administrateur.
/// </summary>
public Boolean IsAdmin { get; set; }
/// <summary>
/// Obtient ou définit la liste des commentaires associés à l'utilisateur.
/// </summary>
public List<Commentary> Comments { get; set; }
/// <summary>
/// Initialise une nouvelle instance de la classe <see cref="UserDTO"/> avec les informations de base d'un utilisateur.
/// </summary>
/// <param name="image">L'image de l'utilisateur.</param>
/// <param name="name">Le nom de l'utilisateur.</param>
/// <param name="email">L'email de l'utilisateur.</param>
/// <param name="dateCreation">La date de création du compte utilisateur.</param>
public UserDTO(string image, string name, string email, DateTime dateCreation)
{
this.Image = image;
this.Name = name;
this.Email = email;
this.DateCreation = dateCreation;
}
}
}

@ -1,24 +0,0 @@
using WF_WebAdmin.Model;
namespace WF_WebAdmin.Converter
{
public class UserDto
{
public string Image { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime DateCreation { get; set; }
public Boolean IsAdmin { get; set; }
public List<Commentary>? Comments { get; set; }
public UserDto(string image, string name, string email, DateTime dateCreation)
{
this.Image = image;
this.Name = name;
this.Email = email;
this.DateCreation = dateCreation;
}
}
}

@ -2,17 +2,32 @@
namespace WF_WebAdmin.Converter namespace WF_WebAdmin.Converter
{ {
/// <summary>
/// Classe d'extension permettant la conversion entre un utilisateur et son Data Transfer Object (DTO).
/// </summary>
public class UserExtension public class UserExtension
{ {
public User UserToDto(UserDto u) /// <summary>
/// Convertit un objet <see cref="UserDTO"/> en un objet <see cref="User"/>.
/// </summary>
/// <param name="u">L'objet <see cref="UserDTO"/> à convertir.</param>
/// <returns>Retourne un nouvel objet <see cref="User"/> avec les données du DTO.</returns>
public User UserToDTO(UserDTO u)
{ {
var user = new User(u.Image, u.Name, u.Email, u.DateCreation,u.IsAdmin); // Création d'un objet User à partir des données du DTO
User user = new User(u.Image, u.Name, u.Email, u.DateCreation, u.IsAdmin);
return user; return user;
} }
public UserDto DtoToUser(User u) /// <summary>
/// Convertit un objet <see cref="User"/> en un objet <see cref="UserDTO"/>.
/// </summary>
/// <param name="u">L'objet <see cref="User"/> à convertir.</param>
/// <returns>Retourne un nouvel objet <see cref="UserDTO"/> avec les données de l'utilisateur.</returns>
public UserDTO DTOToUser(User u)
{ {
var user = new UserDto(u.Image ?? "default.png", u.Name ?? "Bob", u.Email ?? "bob@mail.com", u.DateCreation); // Création d'un objet UserDTO à partir des données de l'utilisateur
UserDTO user = new UserDTO(u.Image, u.Name, u.Email, u.DateCreation);
return user; return user;
} }
} }

@ -1,13 +0,0 @@
namespace WF_WebAdmin.Data
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
}

@ -1,20 +0,0 @@
namespace WF_WebAdmin.Data
{
public class WeatherForecastService
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray());
}
}
}

@ -1,8 +1,18 @@
namespace WF_WebAdmin.Model namespace WF_WebAdmin.Model
{ {
public class Character /// <summary>
{ /// Représente un personnage dans le système.
public int id_caracter { get; set; } /// </summary>
public string? caracter { get; set; } public class Character
} {
} /// <summary>
/// Identifiant unique du personnage.
/// </summary>
public int id_caracter { get; set; }
/// <summary>
/// Nom ou description du personnage.
/// </summary>
public string caracter { get; set; }
}
}

@ -1,12 +1,21 @@
using System; namespace WF_WebAdmin.Model
namespace WF_WebAdmin.Model
{ {
/// <summary>
/// Représente un commentaire dans le système.
/// </summary>
public class Commentary public class Commentary
{ {
public int Id { get; set; } //public int Id { get; set; }
public int IdUser { get; set; } //public int IdUser { get; set; }
/// <summary>
/// Le texte du commentaire.
/// </summary>
public string Text { get; set; }
/// <summary>
/// La date de création du commentaire.
/// </summary>
public DateTime DateCreation { get; set; } public DateTime DateCreation { get; set; }
public string Text { get; set; } = string.Empty;
} }
} }

@ -1,9 +1,19 @@
namespace WF_WebAdmin.Model namespace WF_WebAdmin.Model
{ {
/// <summary>
/// Représente une citation quotidienne dans le système.
/// </summary>
public class DailyQuote public class DailyQuote
{ {
/// <summary>
/// Identifiant unique de la citation quotidienne.
/// </summary>
public int Id { get; set; } public int Id { get; set; }
/// <summary>
/// Initialise une nouvelle instance de la classe <see cref="DailyQuote"/>.
/// </summary>
/// <param name="Id">L'identifiant de la citation quotidienne.</param>
public DailyQuote(int Id) public DailyQuote(int Id)
{ {
this.Id = Id; this.Id = Id;

@ -1,24 +0,0 @@
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.Extensions.Logging;
using Microsoft.VisualBasic;
using System;
using System.Diagnostics;
using System.Security.Claims;
using WF_WebAdmin.Pages;
using WF_WebAdmin.Service;
namespace WF_WebAdmin.Model
{
public static partial class LoggerSaveStub
{
public static void Log(ILogger? logs, LogLevel logLevel, string message, params object[] args)
{
ILogsService logsService = new LogsServiceStub();
logsService.addLogs(new Logs(logLevel, string.Format(message, args)));
logs.Log(logLevel, message, args);
}
}
}

@ -1,14 +0,0 @@
namespace WF_WebAdmin.Model
{
public class Logs
{
public LogLevel LogLevel { get; set; }
public string Message { get; set; }
public Logs(LogLevel logLevel , string message) {
this.LogLevel=logLevel;
this.Message=message;
}
}
}

@ -1,17 +1,90 @@
namespace WF_WebAdmin.Model namespace WF_WebAdmin.Model
{ {
/// <summary>
/// Représente un quiz avec une question et plusieurs réponses possibles.
/// </summary>
public class Quiz public class Quiz
{ {
/// <summary>
/// Identifiant unique du quiz.
/// </summary>
public int Id { get; set; } public int Id { get; set; }
public string? Question { get; set; }
/// <summary>
/// La question posée dans le quiz.
/// </summary>
public string Question { get; set; }
/// <summary>
/// Réponse A possible pour le quiz.
/// </summary>
public string AnswerA { get; set; } public string AnswerA { get; set; }
/// <summary>
/// Réponse B possible pour le quiz.
/// </summary>
public string AnswerB { get; set; } public string AnswerB { get; set; }
/// <summary>
/// Réponse C possible pour le quiz.
/// </summary>
public string AnswerC { get; set; } public string AnswerC { get; set; }
/// <summary>
/// Réponse D possible pour le quiz.
/// </summary>
public string AnswerD { get; set; } public string AnswerD { get; set; }
/// <summary>
/// Réponse correcte au quiz (A, B, C ou D).
/// </summary>
public string CAnswer { get; set; } public string CAnswer { get; set; }
/// <summary>
/// Indique si le quiz est valide.
/// </summary>
public bool IsValid { get; set; } public bool IsValid { get; set; }
/// <summary>
/// Proposition de l'utilisateur pour valider le quiz.
/// </summary>
public string UserProposition { get; set; } public string UserProposition { get; set; }
/// <summary>
/// Initialise une nouvelle instance de la classe <see cref="Quiz"/>.
/// </summary>
/// <param name="id">L'identifiant du quiz.</param>
/// <param name="question">La question posée dans le quiz.</param>
/// <param name="answerA">Réponse A possible.</param>
/// <param name="answerB">Réponse B possible.</param>
/// <param name="answerC">Réponse C possible.</param>
/// <param name="answerD">Réponse D possible.</param>
/// <param name="cAnswer">Réponse correcte (A, B, C ou D).</param>
/// <param name="isValid">Indique si le quiz est valide.</param>
/// <param name="userProposition">Proposition de l'utilisateur.</param>
public Quiz(int id, string question, string answerA, string answerB, string answerC, string answerD, string cAnswer, bool isValid, string userProposition)
{
Id = id;
Question = question;
AnswerA = answerA;
AnswerB = answerB;
AnswerC = answerC;
AnswerD = answerD;
CAnswer = cAnswer;
IsValid = isValid;
UserProposition = userProposition;
}
/// <summary>
/// Initialise une nouvelle instance de la classe <see cref="Quiz"/> avec une valeur par défaut pour la validité et la proposition de l'utilisateur.
/// </summary>
/// <param name="id">L'identifiant du quiz.</param>
/// <param name="question">La question posée dans le quiz.</param>
/// <param name="answerA">Réponse A possible.</param>
/// <param name="answerB">Réponse B possible.</param>
/// <param name="answerC">Réponse C possible.</param>
/// <param name="answerD">Réponse D possible.</param>
/// <param name="cAnswer">Réponse correcte (A, B, C ou D).</param>
public Quiz(int id, string question, string answerA, string answerB, string answerC, string answerD, string cAnswer) public Quiz(int id, string question, string answerA, string answerB, string answerC, string answerD, string cAnswer)
{ {
Id = id; Id = id;
@ -25,5 +98,9 @@ namespace WF_WebAdmin.Model
UserProposition = "Admin"; UserProposition = "Admin";
} }
/// <summary>
/// Constructeur par défaut pour la classe <see cref="Quiz"/>.
/// </summary>
public Quiz() {}
} }
} }

@ -1,30 +1,57 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace WF_WebAdmin.Model namespace WF_WebAdmin.Model
{ {
public class QuizModel /// <summary>
{ /// Modèle représentant les données nécessaires à la création ou à la modification d'un quiz.
[Required] /// </summary>
[StringLength(200, ErrorMessage = "La question ne peut pas depasser les 200 caractère.")] public class QuizModel
public string? Question { get; set; } {
/// <summary>
[Required] /// Question posée dans le quiz.
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")] /// Cette propriété est obligatoire et doit être d'une longueur maximale de 200 caractères.
public string? AnswerA { get; set; } /// </summary>
[Required]
[Required] [StringLength(200, ErrorMessage = "La question ne peut pas depasser les 200 caractère.")]
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")] public string Question { get; set; }
public string? AnswerB { get; set; }
/// <summary>
[Required] /// Première réponse possible pour le quiz.
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")] /// Cette propriété est obligatoire et doit être d'une longueur maximale de 50 caractères.
public string? AnswerC { get; set; } /// </summary>
[Required]
[Required] [StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")]
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")] public string AnswerA { get; set; }
public string? AnswerD { get; set; }
/// <summary>
[Required] /// Deuxième réponse possible pour le quiz.
public string? CAnswer { get; set; } /// Cette propriété est obligatoire et doit être d'une longueur maximale de 50 caractères.
} /// </summary>
} [Required]
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")]
public string AnswerB { get; set; }
/// <summary>
/// Troisième réponse possible pour le quiz.
/// Cette propriété est obligatoire et doit être d'une longueur maximale de 50 caractères.
/// </summary>
[Required]
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")]
public string AnswerC { get; set; }
/// <summary>
/// Quatrième réponse possible pour le quiz.
/// Cette propriété est obligatoire et doit être d'une longueur maximale de 50 caractères.
/// </summary>
[Required]
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")]
public string AnswerD { get; set; }
/// <summary>
/// Réponse correcte au quiz, choisie parmi A, B, C ou D.
/// Cette propriété est obligatoire.
/// </summary>
[Required]
public string CAnswer { get; set; }
}
}

@ -1,33 +1,131 @@
using Microsoft.AspNetCore.DataProtection.KeyManagement; namespace WF_WebAdmin.Model
{
namespace WF_WebAdmin.Model /// <summary>
{ /// Représente une citation dans le système.
public class Quote /// </summary>
{ public class Quote
public int Id { get; set; } {
public string Content { get; set; } /// <summary>
public int Like { get; set; } /// Identifiant unique de la citation.
public string Langue { get; set; } /// </summary>
public string Charac { get; set; } public int Id { get; set; }
public string ImgPath { get; set; }
public string TitleSrc { get; set; } /// <summary>
public DateTime DateSrc { get; set; } /// Contenu de la citation.
public string UserProposition { get; set; } /// </summary>
public bool IsValid { get; set; } public string Content { get; set; }
public Quote(int id, string content, string charac, string imgPath, string titleSrc, DateTime dateSrc, int like, string langue, string userProposition, bool isvalid) /// <summary>
{ /// Nombre de "likes" reçus pour cette citation.
Id = id; /// </summary>
Content = content; public int Like { get; set; }
Charac = charac;
ImgPath = imgPath; /// <summary>
TitleSrc = titleSrc; /// Langue dans laquelle la citation est écrite.
DateSrc = dateSrc; /// </summary>
Like = like; public string Langue { get; set; }
Langue = langue;
UserProposition = userProposition; /// <summary>
IsValid = isvalid; /// Caractère associé à la citation (ex : personnage, auteur, etc.).
} /// </summary>
public string Charac { get; set; }
}
} /// <summary>
/// Chemin d'accès à l'image associée à la citation.
/// </summary>
public string ImgPath { get; set; }
/// <summary>
/// Titre de la source de la citation (ex : livre, film, etc.).
/// </summary>
public string TitleSrc { get; set; }
/// <summary>
/// Date de la source de la citation.
/// </summary>
public DateTime DateSrc { get; set; }
/// <summary>
/// Nom de l'utilisateur ayant proposé la citation.
/// </summary>
public string UserProposition { get; set; }
/// <summary>
/// Indique si la citation est valide.
/// </summary>
public bool IsValid { get; set; }
/// <summary>
/// Constructeur pour créer une nouvelle citation avec tous ses attributs.
/// </summary>
/// <param name="id">Identifiant de la citation.</param>
/// <param name="content">Contenu de la citation.</param>
/// <param name="charac">Caractère associé à la citation.</param>
/// <param name="imgPath">Chemin de l'image associée.</param>
/// <param name="titleSrc">Titre de la source.</param>
/// <param name="dateSrc">Date de la source.</param>
/// <param name="like">Nombre de "likes" reçus.</param>
/// <param name="langue">Langue de la citation.</param>
/// <param name="userProposition">Nom de l'utilisateur ayant proposé la citation.</param>
/// <param name="isvalid">Indicateur de validité de la citation.</param>
public Quote(int id, string content, string charac, string imgPath, string titleSrc, DateTime dateSrc, int like, string langue, string userProposition, bool isvalid)
{
Id = id;
Content = content;
Charac = charac;
ImgPath = imgPath;
TitleSrc = titleSrc;
DateSrc = dateSrc;
Like = like;
Langue = langue;
UserProposition = userProposition;
IsValid = isvalid;
}
}
}
/*
public int Id { get; set; }
public string Content { get; set; }
public int Likes { get; set; }
public string Langue { get; set; }
public bool IsValide { get; set; }
public string? Reason { get; set; }
public int IdCaracter { get; set; }
public int IdSource { get; set; }
public int? IdUserVerif { get; set; }
public Quote(int id, string content, int likes, string langue, bool isValide, string? reason, int idCaracter, int idSource, int idUserVerif)
{
Id = id;
Content = content;
Likes = likes;
Langue = langue;
IsValide = isValide;
Reason = reason;
IdCaracter = idCaracter;
IdSource = idSource;
IdUserVerif = idUserVerif;
}
public Quote(int id, string content, int likes, string langue, bool isValide, string? reason, int idCaracter, int idSource, int? idUserVerif)
{
Id = id;
Content = content;
Likes = likes;
Langue = langue;
IsValide = isValide;
Reason = reason;
IdCaracter = idCaracter;
IdSource = idSource;
IdUserVerif = idUserVerif;
}
*/

@ -1,27 +1,65 @@
using System.ComponentModel.DataAnnotations; namespace WF_WebAdmin.Model
{
namespace WF_WebAdmin.Model /// <summary>
{ /// Modèle représentant une citation dans le système.
public class QuoteModel /// Ce modèle est utilisé pour la validation des données avant leur persistance.
{ /// </summary>
public int Id { get; set; } public class QuoteModel
{
[Required] /// <summary>
[StringLength(300, ErrorMessage = "La citation ne peut pas dépasser les 300 caractère.")] /// Identifiant unique de la citation.
public string? Content { get; set; } /// </summary>
public int Id { get; set; }
public int Like { get; set; }
/// <summary>
[Required] /// Contenu de la citation.
[StringLength(2, ErrorMessage = "La langue ne peut pas dépasser 2 caractère.")] /// La citation ne peut pas dépasser 300 caractères.
public string? Langue { get; set; } /// </summary>
[Required]
[StringLength(300, ErrorMessage = "La citation ne peut pas dépasser les 300 caractères.")]
public string? Charac { get; set; } public string Content { get; set; }
public string? ImgPath { get; set; }
public string? TitleSrc { get; set; } /// <summary>
public DateTime DateSrc { get; set; } /// Nombre de "likes" que la citation a reçus.
public string? UserProposition { get; set; } /// </summary>
public bool IsValid { get; set; } public int Like { get; set; }
}
} /// <summary>
/// Langue dans laquelle la citation est écrite.
/// La langue est limitée à 2 caractères.
/// </summary>
[Required]
[StringLength(2, ErrorMessage = "La langue ne peut pas dépasser 2 caractères.")]
public string Langue { get; set; }
/// <summary>
/// Caractère associé à la citation (ex : auteur ou personnage).
/// </summary>
public string Charac { get; set; }
/// <summary>
/// Chemin d'accès à l'image associée à la citation.
/// </summary>
public string ImgPath { get; set; }
/// <summary>
/// Titre de la source de la citation (ex : livre, film, etc.).
/// </summary>
public string TitleSrc { get; set; }
/// <summary>
/// Date de la source de la citation.
/// </summary>
public DateTime DateSrc { get; set; }
/// <summary>
/// Nom de l'utilisateur ayant proposé la citation.
/// </summary>
public string UserProposition { get; set; }
/// <summary>
/// Indique si la citation est validée par le système.
/// </summary>
public bool IsValid { get; set; }
}
}

@ -1,11 +1,25 @@
namespace WF_WebAdmin.Model namespace WF_WebAdmin.Model
{ {
public class Source /// <summary>
{ /// Représente une source d'une citation dans le système.
public int id_source { get; set; } /// Cette classe contient des informations sur la source d'une citation, telles que son titre et sa date de publication.
/// </summary>
public string? title { get; set; } public class Source
{
public int date { get; set; } /// <summary>
} /// Identifiant unique de la source.
} /// </summary>
public int id_source { get; set; }
/// <summary>
/// Titre de la source, tel qu'un livre, un article ou une œuvre.
/// </summary>
public string title { get; set; }
/// <summary>
/// Date de publication de la source.
/// Il s'agit d'une représentation de l'année sous forme d'entier.
/// </summary>
public int date { get; set; }
}
}

@ -1,26 +1,67 @@
namespace WF_WebAdmin.Model namespace WF_WebAdmin.Model
{ {
public class User /// <summary>
{ /// Représente un utilisateur dans le système.
public int Id { get; set; } /// Cette classe contient des informations sur l'utilisateur, telles que son image, son nom, son email, la date de création de son compte,
public string? Image { get; set; } /// son statut d'administrateur et les commentaires qu'il a publiés.
public string? Name { get; set; } /// </summary>
public string? Email { get; set; } public class User
public DateTime DateCreation { get; set; } {
/// <summary>
public Boolean IsAdmin { get; set; } /// Identifiant unique de l'utilisateur.
/// </summary>
public List<Commentary>? Comments { get; set; } public int Id { get; set; }
public User(string image, string name, string email, DateTime dateCreation, bool isAdmin) /// <summary>
{ /// URL de l'image de profil de l'utilisateur.
this.Image = image; /// </summary>
this.Name = name; public string Image { get; set; }
this.Email = email;
this.DateCreation = dateCreation; /// <summary>
IsAdmin = isAdmin; /// Nom de l'utilisateur.
} /// </summary>
public string Name { get; set; }
public User() { }
} /// <summary>
} /// Adresse email de l'utilisateur.
/// </summary>
public string Email { get; set; }
/// <summary>
/// Date de création du compte utilisateur.
/// </summary>
public DateTime DateCreation { get; set; }
/// <summary>
/// Indique si l'utilisateur a des privilèges d'administrateur.
/// </summary>
public bool IsAdmin { get; set; }
/// <summary>
/// Liste des commentaires publiés par l'utilisateur.
/// </summary>
public List<Commentary> Comments { get; set; }
/// <summary>
/// Constructeur pour créer un nouvel utilisateur avec des informations spécifiques.
/// </summary>
/// <param name="image">URL de l'image de profil de l'utilisateur.</param>
/// <param name="name">Nom de l'utilisateur.</param>
/// <param name="email">Adresse email de l'utilisateur.</param>
/// <param name="dateCreation">Date de création du compte de l'utilisateur.</param>
/// <param name="isAdmin">Indicateur si l'utilisateur est un administrateur.</param>
public User(string image, string name, string email, DateTime dateCreation, bool isAdmin)
{
this.Image = image;
this.Name = name;
this.Email = email;
this.DateCreation = dateCreation;
IsAdmin = isAdmin;
}
/// <summary>
/// Constructeur par défaut pour créer un utilisateur sans initialiser ses propriétés.
/// </summary>
public User() { }
}
}

@ -1,22 +1,56 @@
using System; namespace WF_WebAdmin.Model
namespace WF_WebAdmin.Model {
{ /// <summary>
public class UserLogin /// Représente un utilisateur dans le système de connexion.
{ /// Cette classe contient des informations nécessaires pour l'authentification et la gestion du profil utilisateur.
public int Id { get; set; } /// </summary>
public string? Image { get; set; } public class UserLogin
public string? Name { get; set;} {
public Boolean IsAdmin { get; set; } /// <summary>
public string? Mdp { get; set; } /// Identifiant unique de l'utilisateur.
public UserLogin(int id,string image, string name, bool isAdmin, string mdp) /// </summary>
{ public int Id { get; set; }
Id = id;
this.Image = image; /// <summary>
this.Name = name; /// URL de l'image de profil de l'utilisateur.
this.IsAdmin = isAdmin; /// </summary>
this.Mdp = mdp; public string Image { get; set; }
}
/// <summary>
public UserLogin() { } /// Nom de l'utilisateur.
} /// </summary>
} public string Name { get; set; }
/// <summary>
/// Indique si l'utilisateur possède des privilèges d'administrateur.
/// </summary>
public bool IsAdmin { get; set; }
/// <summary>
/// Mot de passe de l'utilisateur.
/// </summary>
public string Mdp { get; set; }
/// <summary>
/// Constructeur pour créer un objet <see cref="UserLogin"/> avec des informations d'authentification et de profil.
/// </summary>
/// <param name="id">Identifiant de l'utilisateur.</param>
/// <param name="image">URL de l'image de profil de l'utilisateur.</param>
/// <param name="name">Nom de l'utilisateur.</param>
/// <param name="isAdmin">Indicateur si l'utilisateur est un administrateur.</param>
/// <param name="mdp">Mot de passe de l'utilisateur.</param>
public UserLogin(int id, string image, string name, bool isAdmin, string mdp)
{
Id = id;
this.Image = image;
this.Name = name;
this.IsAdmin = isAdmin;
this.Mdp = mdp;
}
/// <summary>
/// Constructeur par défaut pour créer un objet <see cref="UserLogin"/> sans initialiser les propriétés.
/// </summary>
public UserLogin() { }
}
}

@ -1,9 +1,9 @@
@page "/" @page "/Accueil"
@using WF_WebAdmin.Model @using WF_WebAdmin.Model
<PageTitle>Accueil</PageTitle> <PageTitle>Accueil</PageTitle>
<h2><strong>@Localizer["AccueilWelcome"] ?? "Bienvenue"</strong></h2> <h2><strong>@Localizer["AccueilWelcome"]</strong></h2>
<h4>@Localizer["AccueilTitle"]</h4> <h4>@Localizer["AccueilTitle"]</h4>
@ -28,5 +28,5 @@ else
} }
<h4>@Localizer["AccueilManualChange"]</h4> <h4>@Localizer["AccueilManualChange"]</h4>
<button @onclick="() => RandomDailyquote()">@Localizer["AccueilAddRandomQuote"]</button> <button>@Localizer["AccueilAddRandomQuote"]</button>

@ -1,86 +1,47 @@
using Blazorise.DataGrid; using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using System; using WF_WebAdmin.Model;
using System.Security.Claims;
using System.Text.Json; namespace WF_WebAdmin.Pages
using WF_WebAdmin.Model; {
/// <summary>
/// Page d'accueil qui affiche des citations quotidiennes.
namespace WF_WebAdmin.Pages /// </summary>
{ public partial class Accueil
public partial class Accueil {
{ /// <summary>
private Quote[]? Dailyquote; /// Tableau contenant les citations quotidiennes.
[Inject] /// </summary>
public ILogger<Accueil>? Logger { get; set; } private Quote[] Dailyquote;
[Inject] /// <summary>
public HttpClient? Http { get; set; } /// Service HttpClient pour faire des requêtes HTTP.
/// </summary>
[Inject] [Inject]
public NavigationManager? NavigationManager { get; set; } public HttpClient Http { get; set; }
[Inject] /// <summary>
public IStringLocalizer<Accueil>? Localizer { get; set; } /// Service NavigationManager pour la gestion de la navigation dans l'application.
/// </summary>
[Inject]
/// <summary> public NavigationManager NavigationManager { get; set; }
/// This method is called during the initialization of the Blazor component.
/// It is asynchronous and is used to load data or perform actions before the component is rendered. /// <summary>
/// </summary> /// Service pour la localisation des ressources (traductions) sur la page.
protected override async Task OnInitializedAsync() /// </summary>
{ [Inject]
var url = $"{NavigationManager.BaseUri}fake-dataDailyQuote.json"; public IStringLocalizer<Accueil> Localizer { get; set; }
try /// <summary>
{ /// Méthode appelée lors de l'initialisation du composant.
var response = await Http.GetFromJsonAsync<Quote[]>(url); /// Elle charge les citations quotidiennes depuis un fichier JSON.
Dailyquote = response ?? Array.Empty<Quote>(); // Assurer qu'on ne stocke pas null /// </summary>
} /// <returns>Une tâche représentant l'opération asynchrone.</returns>
catch (Exception ex) protected override async Task OnInitializedAsync()
{ {
Console.WriteLine($"Erreur lors du chargement des citations : {ex.Message}"); // Chargement des citations quotidiennes depuis le fichier JSON
Dailyquote = Array.Empty<Quote>(); // Éviter une exception en cas d'erreur réseau Dailyquote = await Http.GetFromJsonAsync<Quote[]>($"{NavigationManager.BaseUri}fake-dataDailyQuote.json");
} }
} }
private async Task RandomDailyquote() }
{
try
{
string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataDailyQuote.json");
Random random = new Random();
var response = await Http.GetFromJsonAsync<Quote[]>($"{NavigationManager.BaseUri}fake-dataQuote.json");
Quote[] quotes = response ?? Array.Empty<Quote>(); // Empêche null
if (quotes.Length > 0)
{
Dailyquote = new Quote[] { quotes.OrderBy(x => random.Next()).First() };
}
else
{
Console.WriteLine("Aucune citation disponible dans le fichier JSON.");
return;
}
var json = JsonSerializer.Serialize(Dailyquote, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(_jsonFilePath, json);
if (Logger != null)
{
LoggerSaveStub.Log(Logger, LogLevel.Information, "Random change of quote of the day");
}
else
{
Console.WriteLine("Logger non initialisé.");
}
}
catch (Exception ex)
{
Console.WriteLine($"Erreur dans RandomDailyquote: {ex.Message}");
}
}
}
}

@ -2,57 +2,57 @@
@page "/add" @page "/add"
<h3>@Localizer["TitleAddQuiz"]</h3> <h3>Ajouter une Question</h3>
<EditForm Model="@_quizModel" OnValidSubmit="@HandleValidSubmit"> <EditForm Model="@QuizModel" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator /> <DataAnnotationsValidator />
<ValidationSummary /> <ValidationSummary />
<p> <p>
<label for="display-quest"> <label for="display-quest">
@Localizer["TitleQuestion"] Question:
<InputText id="display-quest" @bind-Value="_quizModel.Question" /> <InputText id="display-quest" @bind-Value="QuizModel.Question" />
</label> </label>
</p> </p>
<p> <p>
<label for="display-a"> <label for="display-a">
@Localizer["AnswerA"] Réponse A:
<InputText id="display-a" @bind-Value="_quizModel.AnswerA" /> <InputText id="display-a" @bind-Value="QuizModel.AnswerA" />
</label> </label>
</p> </p>
<p> <p>
<label for="display-b"> <label for="display-b">
@Localizer["AnswerB"] Réponse B:
<InputText id="display-b" @bind-Value="_quizModel.AnswerB" /> <InputText id="display-b" @bind-Value="QuizModel.AnswerB" />
</label> </label>
</p> </p>
<p> <p>
<label for="display-c"> <label for="display-c">
@Localizer["AnswerC"] Réponse C:
<InputText id="display-c" @bind-Value="_quizModel.AnswerC" /> <InputText id="display-c" @bind-Value="QuizModel.AnswerC" />
</label> </label>
</p> </p>
<p> <p>
<label for="display-d"> <label for="display-d">
@Localizer["AnswerD"] Réponse D:
<InputText id="display-d" @bind-Value="_quizModel.AnswerD" /> <InputText id="display-d" @bind-Value="QuizModel.AnswerD" />
</label> </label>
</p> </p>
<p> <p>
<label for="cA"> <label for="cA">
@Localizer["GoodAnswer"] Bonne réponse:
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("A"))" /> A <input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("A", e.Value))" /> A
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("B"))" /> B <input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("B", e.Value))" /> B
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("C"))" /> C <input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("C", e.Value))" /> C
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("D"))" /> D <input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("D", e.Value))" /> D
</label> </label>
</p> </p>
<button type="submit">@Localizer["Submit"]</button> <button type="submit">Submit</button>
</EditForm> </EditForm>

@ -1,128 +1,92 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using WF_WebAdmin.Service; using WF_WebAdmin.Service;
using WF_WebAdmin.Model; using WF_WebAdmin.Model;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.Extensions.Localization; using System;
using Microsoft.Extensions.Logging;
using System.Security.Claims; namespace WF_WebAdmin.Pages
{
public partial class AddQuiz
namespace WF_WebAdmin.Pages {
{ [Inject]
public partial class AddQuiz private IQuizService quizService { get; set; }
{
[Inject] [Inject]
public ILogger<AddQuiz>? Logger { get; set; } public NavigationManager NavigationManager { get; set; }
[Inject] private QuizModel QuizModel = new();
public IStringLocalizer<AddQuiz>? Localizer { get; set; }
/// <summary>
[Inject] /// Handles the form submission to create a new quiz.
private IQuizService? QuizService { get; set; } /// </summary>
private async void HandleValidSubmit()
[Inject] {
public NavigationManager? NavigationManager { get; set; } int id;
id = await quizService.getNbQuiz(); // Fetch the current quiz count
private readonly QuizModel _quizModel = new(); id++; // Increment the quiz ID to create a new quiz ID
/// <summary> // Create and add the quiz using the validated inputs
/// Handles the valid submission of a quiz form. await quizService.addQuiz(new Quiz(
/// This method is triggered when the form is successfully validated and the user submits the quiz data. id,
/// It retrieves the current quiz count, increments it, and then adds a new quiz entry to the quiz service. validateInformation(QuizModel.Question),
/// Finally, it navigates to the "modifquiz" page. validateInformation(QuizModel.AnswerA),
/// </summary> validateInformation(QuizModel.AnswerB),
validateInformation(QuizModel.AnswerC),
private async Task HandleValidSubmit() validateInformation(QuizModel.AnswerD),
{ validateReponse(QuizModel.CAnswer)
// Declare a variable to hold the ID of the new quiz. ));
int id;
// Navigate to the quiz management page after adding the quiz
// Get the current number of quizzes from the quiz service. NavigationManager.NavigateTo("modifquiz");
id = await QuizService.getNbQuiz(); }
// Increment the quiz ID for the new quiz. /// <summary>
id++; /// Handles the change in the correct answer selection.
/// </summary>
// Create a new quiz and add it using the quiz service. private void OnCAwnserChange(string item, object checkedValue)
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Creation of the {_quizModel.Question} question"); {
await QuizService.addQuiz(new Quiz( QuizModel.CAnswer = item;
id, // New quiz ID }
ValidateInformation(_quizModel.Question), // Validated question
ValidateInformation(_quizModel.AnswerA), // Validated answer A /// <summary>
ValidateInformation(_quizModel.AnswerB), // Validated answer B /// Validates each input (question or answers) to ensure it isn't null or empty.
ValidateInformation(_quizModel.AnswerC), // Validated answer C /// </summary>
ValidateInformation(_quizModel.AnswerD), // Validated answer D private static string validateInformation(string item)
ValidateReponse(_quizModel.CAnswer) // Validated correct answer {
)); if (string.IsNullOrWhiteSpace(item))
{
// Navigate to the "modifquiz" page after adding the quiz. throw new ArgumentException("Input cannot be empty or whitespace.");
NavigationManager.NavigateTo("modifquiz"); }
} return item; // Return the valid input
}
/// <summary>
/// <summary> /// Validates the selected correct answer to be one of the valid options (A, B, C, D).
/// Handles the change of the correct answer for the quiz. /// </summary>
/// This method is triggered when the user selects or changes the correct answer for the quiz question. private static string validateReponse(string item)
/// It updates the QuizModel's Correct Answer property with the selected answer. {
/// </summary> try
/// <param name="item">The selected answer that will be marked as the correct answer.</param> {
/// <param name="checkedValue">The value of the selected option, typically used for validation or additional logic.</param> if (string.IsNullOrEmpty(item))
private void OnCAwnserChange(string item) {
{ throw new ArgumentNullException("Correct answer must be selected.");
// Update the correct answer in the QuizModel with the selected answer. }
_quizModel.CAnswer = item;
} // Ensure the answer is one of A, B, C, or D
if (!Regex.IsMatch(item, "^[A-D]$"))
{
/// <summary> throw new InvalidDataException($"Invalid answer choice: '{item}'. Answer must be one of A, B, C, or D.");
/// Validates the provided string item. }
/// This method is used to validate input data, but the validation logic is not yet implemented.
/// </summary> return item; // Return the valid answer
/// <param name="item">The string input to be validated.</param> }
/// <returns> catch (Exception ex)
/// Returns the input string as it is for now. The validation logic is yet to be implemented. {
/// </returns> // Log error or show a message to the user
private string ValidateInformation(string? item) Console.WriteLine(ex.Message);
{ return "A"; // Return default answer "A" if validation fails
return string.IsNullOrWhiteSpace(item) ? "Valeur par défaut" : item; }
} }
}
/// <summary> }
/// Validates the provided answer item (A, B, C, or D) for the quiz.
/// This method ensures that the input corresponds to one of the allowed values for the correct answer.
/// If the input is invalid or null, it throws an exception and returns a default value ("A") in case of error.
/// </summary>
/// <param name="item">The answer item (A, B, C, or D) to be validated.</param>
/// <returns>
/// Returns the input item if valid (A, B, C, or D). If the item is invalid or null, it returns a default value ("A").
/// </returns>
private static string ValidateReponse(string item)
{
// Check if the item is null or empty
if (string.IsNullOrEmpty(item))
{
// Throw exception if the item is null or empty
throw new ArgumentNullException(nameof(item), "The item cannot be null or empty.");
}
// Validate that the item is one of the allowed values: A, B, C, or D
switch (item)
{
case "A":
case "B":
case "C":
case "D":
// Valid values, no action needed
break;
default:
// Throw exception if the item is not one of the allowed answers
throw new InvalidDataException($"Invalid item '{item}' provided. Item must be one of: A, B, C, or D.");
}
// Return the validated item
return item;
}
}
}

@ -1,37 +0,0 @@
@page "/commentary-chart"
<h1>Nombre de commentaires par mois</h1>
<MudChart ChartType="ChartType.Bar" ChartSeries="@Series" @bind-SelectedIndex="Index" LegendPosition="Position.Bottom" XAxisLabels="@XAxisLabels" Width="100%" Height="350px"></MudChart>
@code {
private int Index = -1;
private List<ChartSeries> Series = new();
private string[] XAxisLabels = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
[Inject] private WF_WebAdmin.Service.ICommentaryService CommentaryService { get; set; } = default!;
protected override async Task OnInitializedAsync()
{
var comments = await CommentaryService.GetCommentsAsync();
var groupedData = comments.GroupBy(c => c.DateCreation.Month)
.OrderBy(g => g.Key)
.Select(g => new { Month = g.Key - 1, Value = g.Count() })
.ToList();
double[] data = new double[12];
foreach (var item in groupedData)
{
data[item.Month] = item.Value;
}
Series = new List<ChartSeries>
{
new ChartSeries
{
Name = "",
Data = data
}
};
}
}

@ -5,7 +5,7 @@
<h3>@Localizer["TitleUser"]</h3> <h3>@Localizer["TitleUser"]</h3>
@if(_users == null) @if(users == null)
{ {
<p><strong>@Localizer["UserNobody"]</strong></p> <p><strong>@Localizer["UserNobody"]</strong></p>
} }
@ -13,10 +13,10 @@
else else
{ {
<DataGrid TItem="User" <DataGrid TItem="User"
Data="@_users" Data="@users"
PageSize="@MaxValue" PageSize="@MaxValue"
ReadData="@OnReadData" ReadData="@OnReadData"
TotalItems="@_totalItem" TotalItems="@totalItem"
ShowPager ShowPager
Responsive> Responsive>
@ -29,7 +29,7 @@ else
<p>@Localizer["UserHere"]</p> <p>@Localizer["UserHere"]</p>
@foreach (var user in _users) @foreach (var user in users)
{ {
<div class="userDiv" id="@user.Id"> <div class="userDiv" id="@user.Id">
<img class="imgProfil" src="@user.Image" /> <img class="imgProfil" src="@user.Image" />
@ -74,7 +74,7 @@ else
</div> </div>
<!-- Fenêtre de confirmation de suppression --> <!-- Fenêtre de confirmation de suppression -->
@if (_showPopupDelete) @if (showPopupDelete)
{ {
<div class="divPopup"> <div class="divPopup">
<div class="contentPopup"> <div class="contentPopup">
@ -84,17 +84,17 @@ else
</div> </div>
</div> </div>
} }
@if (_showModifyPopup) @if (showModifyPopup)
{ {
<div class="divPopup"> <div class="divPopup">
<div class="contentPopup"> <div class="contentPopup">
<p>Modifier les informations de l'utilisateur :</p> <p>Modifier les informations de l'utilisateur :</p>
<label>Nom d'utilisateur:</label> <label>Nom d'utilisateur:</label>
<input type="text" @bind="_selectedUser.Name"/> <input type="text" @bind="selectedUser.Name"/>
<label>Email:</label> <label>Email:</label>
<input type="email" @bind="_selectedUser.Email" /> <input type="email" @bind="selectedUser.Email" />
<label>Image:</label> <label>Image:</label>
<input type="text" @bind="_selectedUser.Image" /> <input type="text" @bind="selectedUser.Image" />
<button @onclick="ModifyUser">Sauvegarder</button> <button @onclick="ModifyUser">Sauvegarder</button>
<button @onclick="ClosePopup">Annuler</button> <button @onclick="ClosePopup">Annuler</button>
</div> </div>
@ -103,12 +103,12 @@ else
} }
<!-- Fenêtre de confirmation d'ajout admin--> <!-- Fenêtre de confirmation d'ajout admin-->
@if (_showPopupAdmin) @if (showPopupAdmin)
{ {
<div class="divPopup"> <div class="divPopup">
<div class="contentPopup"> <div class="contentPopup">
<p>@Localizer["UserPopupTitle2"]</p> <p>@Localizer["UserPopupTitle2"]</p>
<button @onclick="() => setAdmin()">@Localizer["UserConfirmButton"]</button> <button @onclick="() => Admin()">@Localizer["UserConfirmButton"]</button>
<button @onclick="ClosePopup">@Localizer["UserDeleteButton"]</button> <button @onclick="ClosePopup">@Localizer["UserDeleteButton"]</button>
</div> </div>
</div> </div>

@ -1,7 +1,5 @@
using Blazorise.DataGrid; using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using System.Collections.Generic; using System.Collections.Generic;
using WF_WebAdmin.Model; using WF_WebAdmin.Model;
@ -9,208 +7,194 @@ using WF_WebAdmin.Service;
namespace WF_WebAdmin.Pages namespace WF_WebAdmin.Pages
{ {
/// <summary>
/// Page permettant de gérer les utilisateurs, y compris la suppression, la modification, et l'attribution du rôle admin.
/// </summary>
public partial class DeleteUser public partial class DeleteUser
{ {
[Inject] /// <summary>
public ILogger<DeleteUser>? Logger { get; set; } /// Indicateur pour afficher ou non la popup de confirmation de suppression.
/// </summary>
private bool showDeletePopup = false;
private List<User>? _users; /// <summary>
/// Indicateur pour afficher ou non la popup de confirmation de modification d'utilisateur.
/// </summary>
private bool showModifyPopup = false;
private bool _showModifyPopup = false; /// <summary>
private User? _userToDelete = null; /// Liste des utilisateurs à afficher dans la page.
private User? _selectedUser; /// </summary>
private bool _showPopupDelete = false; private List<User> users;
private bool _showPopupAdmin = false;
private User? _userToAdmin = null;
private const int MaxValue = 5;
private int _totalItem;
private int _page = 1;
/// <summary>
/// Utilisateur sélectionné pour la suppression.
/// </summary>
private User userToDelete = null;
[Inject] /// <summary>
public HttpClient? Http { get; set; } /// Utilisateur sélectionné pour la modification.
/// </summary>
private User selectedUser;
[Inject] /// <summary>
public NavigationManager? NavigationManager { get; set; } /// Indicateur pour afficher ou non la popup de confirmation de suppression.
/// </summary>
private bool showPopupDelete = false;
/// <summary>
/// Indicateur pour afficher ou non la popup de confirmation de l'attribution du rôle admin.
/// </summary>
private bool showPopupAdmin = false;
/// <summary>
/// Utilisateur sélectionné pour devenir administrateur.
/// </summary>
private User userToAdmin = null;
/// <summary>
/// Nombre maximum d'utilisateurs à afficher par page.
/// </summary>
private int MaxValue = 5;
/// <summary>
/// Nombre total d'utilisateurs dans la base de données.
/// </summary>
private int totalItem;
/// <summary>
/// Numéro de la page actuelle dans le DataGrid.
/// </summary>
private int page = 1;
[Inject] [Inject]
private IUserService? UserService { get; set; } public HttpClient Http { get; set; }
[Inject] [Inject]
public IStringLocalizer<DeleteUser>? Localizer { get; set; } public NavigationManager NavigationManager { get; set; }
/// <summary>
/// Service permettant de gérer les utilisateurs (CRUD).
/// </summary>
[Inject]
private IUserService userService { get; set; }
/// <summary>
/// Localisateur de chaîne pour gérer la localisation de la page.
/// </summary>
[Inject]
public IStringLocalizer<DeleteUser> Localizer { get; set; }
/// <summary> /// <summary>
/// This method is called when the component is initialized. /// Méthode appelée lors de l'initialisation de la page. Charge les utilisateurs à afficher.
/// It is an asynchronous method that retrieves a list of users from the user service.
/// The method fetches a subset of users with a specified maximum value and page number (1 in this case).
/// </summary> /// </summary>
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
// Retrieve a list of users using the user service. The number of users and page number are specified. // Charge la première page des utilisateurs
// MaxValue determines how many users to retrieve, and '1' refers to the first page of results. users = await userService.getSomeUser(MaxValue, 1);
_users = await UserService.getSomeUser(MaxValue, 1);
} }
/// <summary> /// <summary>
/// Handles the event when data is read in the data grid. /// Méthode appelée lors de la lecture des données dans le DataGrid. Gère la pagination et l'affichage des utilisateurs.
/// This method is triggered during pagination or when data is loaded into the grid.
/// It asynchronously fetches a page of users based on the requested page size and page number,
/// and updates the list of users and total item count if the operation is not cancelled.
/// </summary> /// </summary>
/// <param name="e">The event arguments containing pagination details (page size and page number) and a cancellation token.</param>
private async Task OnReadData(DataGridReadDataEventArgs<User> e) private async Task OnReadData(DataGridReadDataEventArgs<User> e)
{ {
// If the cancellation token is requested, exit the method without processing the request.
if (e.CancellationToken.IsCancellationRequested) if (e.CancellationToken.IsCancellationRequested)
{ {
return; return;
} }
// Fetch a page of users from the user service using the page size and page number provided by the event arguments. // Récupère les utilisateurs pour la page demandée
var response = await UserService.getSomeUser(e.PageSize, e.Page); var response = await userService.getSomeUser(e.PageSize, e.Page);
// If the operation is not cancelled, update the total number of users and the list of users.
if (!e.CancellationToken.IsCancellationRequested) if (!e.CancellationToken.IsCancellationRequested)
{ {
_totalItem = await UserService.getNbUser(); // Get the total number of users totalItem = await userService.getNbUser(); // Récupère le nombre total d'utilisateurs
_users = new List<User>(response.ToArray()); // Store the retrieved users in the users list users = new List<User>(response.ToArray()); // Met à jour la liste des utilisateurs
_page = e.Page; // Update the current page number page = e.Page; // Met à jour le numéro de la page
} }
} }
// ------- Popup remove user ------- // ------- Popup remove user -------
/// <summary> /// <summary>
/// Displays a confirmation popup to confirm the deletion of a user. /// Affiche la popup de confirmation de suppression de l'utilisateur sélectionné.
/// This method is triggered when the user intends to delete a user,
/// and it sets the user to be deleted and shows the confirmation popup.
/// </summary> /// </summary>
/// <param name="user">The user to be deleted, which is passed to the method for confirmation.</param> private void ShowConfirmation(User user)
private void ShowConfirmation(User? user)
{ {
// Set the user to be deleted and show the confirmation popup. userToDelete = user;
_userToDelete = user; // Store the user to be deleted in a variable showPopupDelete = true;
_showPopupDelete = true; // Display the confirmation popup
} }
/// <summary> /// <summary>
/// Displays a confirmation popup for modifying a user's information. /// Affiche la popup de modification de l'utilisateur sélectionné.
/// This method is triggered when the user intends to modify a specific user's data,
/// and it sets the selected user and shows the modification confirmation popup.
/// </summary> /// </summary>
/// <param name="user">The user whose information is to be modified, passed to the method for confirmation.</param> private void ShowModifyConfirmation(User user)
private void ShowModifyConfirmation(User? user)
{ {
// Set the selected user and show the modification confirmation popup. selectedUser = user;
_selectedUser = user; // Store the user to be modified showModifyPopup = true;
_showModifyPopup = true; // Display the confirmation popup for modification
} }
/// <summary> /// <summary>
/// Removes the specified user from the system. /// Supprime l'utilisateur sélectionné et met à jour la liste des utilisateurs affichés.
/// This method is triggered when the user confirms the deletion of a user.
/// It calls the user service to remove the user, closes the confirmation popup,
/// and then refreshes the list of users by fetching the updated data.
/// </summary> /// </summary>
private async Task RemoveUser() private async Task RemoveUser()
{ {
// Check if there is a user to delete if (userToDelete != null)
if (_userToDelete != null)
{ {
// Remove the selected user from the system using the user service await userService.removeUser(userToDelete); // Appelle le service pour supprimer l'utilisateur
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Delete user {_userToDelete.Name}"); ClosePopup(); // Ferme les popups
await UserService.removeUser(_userToDelete); var response = await userService.getSomeUser(MaxValue, page); // Récupère à nouveau les utilisateurs
users = new List<User>(response.ToArray()); // Met à jour la liste des utilisateurs
// Close the confirmation popup after the deletion
ClosePopup();
// Refresh the list of users by fetching the updated data from the user service
var response = await UserService.getSomeUser(MaxValue, _page);
// Update the users list with the latest data
_users = new List<User>(response.ToArray());
} }
} }
/// <summary> /// <summary>
/// Modifies the selected user's information. /// Met à jour l'utilisateur sélectionné.
/// This method is triggered when the user confirms the modification of a user's details.
/// It calls the user service to update the user's information and then closes the modification popup.
/// </summary> /// </summary>
private async Task ModifyUser() private async Task ModifyUser()
{ {
// Update the selected user's information using the user service await userService.updateUser(selectedUser); // Appelle le service pour mettre à jour l'utilisateur
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Modification of user {_selectedUser.Name}"); ClosePopup(); // Ferme les popups
await UserService.updateUser(_selectedUser);
// Close the modification popup after the update is complete
ClosePopup();
} }
/// <summary> /// <summary>
/// Closes all open popups in the UI by setting their visibility flags to false. /// Ferme toutes les popups actuellement ouvertes.
/// This method is typically called after an action (like deleting or modifying a user)
/// to hide any active popups and reset the UI state.
/// </summary> /// </summary>
private void ClosePopup() private void ClosePopup()
{ {
// Set all popup visibility flags to false to hide the popups showDeletePopup = false;
_showModifyPopup = false; // Close the modify confirmation popup showModifyPopup = false;
_showPopupDelete = false; // Close any additional delete popups showPopupDelete = false;
_showPopupAdmin = false; // Close the admin-related popup (if any) showPopupAdmin = false;
} }
// ------- Popup admin ------- // ------- Popup admin -------
/// <summary> /// <summary>
/// Displays a confirmation popup to confirm the promotion of a user to admin status. /// Affiche la popup de confirmation pour attribuer ou révoquer le rôle d'administrateur.
/// This method is triggered when the user intends to promote a specific user to admin.
/// It sets the selected user to be promoted and shows the confirmation popup for admin promotion.
/// </summary> /// </summary>
/// <param name="user">The user to be promoted to admin, passed to the method for confirmation.</param> private void ShowConfirmationAdmin(User user)
private void ShowConfirmationAdmin(User? user)
{ {
// Set the user to be promoted to admin and show the confirmation popup. userToAdmin = user;
_userToAdmin = user; // Store the user to be promoted showPopupAdmin = true;
_showPopupAdmin = true; // Display the confirmation popup for admin promotion
} }
/// <summary> /// <summary>
/// Toggles the admin status of the selected user. /// Attribue ou révoque le rôle d'administrateur pour l'utilisateur sélectionné.
/// This method checks the current admin status of the user, and if the user is not an admin,
/// it promotes them to admin. If the user is already an admin, it demotes them.
/// After the change, the user's information is updated, and the confirmation popup is closed.
/// </summary> /// </summary>
private async Task setAdmin() private async Task setAdmin()
{ {
// Check if the user is not already an admin if (!userToAdmin.IsAdmin) // Si l'utilisateur n'est pas administrateur, on l'attribue comme admin
if (!_userToAdmin.IsAdmin)
{ {
// Promote the user to admin userToAdmin.IsAdmin = true;
LoggerSaveStub.Log(Logger, LogLevel.Information, $"User {_userToAdmin.Name} is now administrator"); await userService.updateUser(userToAdmin); // Met à jour l'utilisateur
_userToAdmin.IsAdmin = true; ClosePopup(); // Ferme les popups
await UserService.updateUser(_userToAdmin); // Update the user status in the service
ClosePopup(); // Close the confirmation popup
} }
else else // Si l'utilisateur est déjà administrateur, on révoque ce rôle
{ {
// Demote the user from admin to normal user userToAdmin.IsAdmin = false;
LoggerSaveStub.Log(Logger, LogLevel.Information, $"User {_userToAdmin.Name} is no longer an administator"); await userService.updateUser(userToAdmin); // Met à jour l'utilisateur
_userToAdmin.IsAdmin = false; ClosePopup(); // Ferme les popups
await UserService.updateUser(_userToAdmin); // Update the user status in the service
ClosePopup(); // Close the confirmation popup
} }
} }
} }
} }

@ -3,29 +3,29 @@
<h3>Editer</h3> <h3>Editer</h3>
<EditForm Model="@_quoteModel" OnValidSubmit="@HandleValidSubmit"> <EditForm Model="@quoteModel" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator /> <DataAnnotationsValidator />
<ValidationSummary /> <ValidationSummary />
<p> <p>
<label for="display-cit"> <label for="display-cit">
Citation: Citation:
<InputText id="display-cit" @bind-Value="_quoteModel.Content" /> <InputText id="display-cit" @bind-Value="quoteModel.Content" />
</label> </label>
</p> </p>
<p> <p>
<label for="lang"> <label for="lang">
Langue: Langue:
<input name="lang" type="radio" @onchange="@(e => OnlangChange("fr"))" /> fr <input name="lang" type="radio" @onchange="@(e => OnlangChange("fr", e.Value))" /> fr
<input name="lang" type="radio" @onchange="@(e => OnlangChange("en"))" /> en <input name="lang" type="radio" @onchange="@(e => OnlangChange("en", e.Value))" /> en
</label> </label>
</p> </p>
<p> <p>
<label for="charac"> <label for="charac">
<InputSelect id="charac" @bind-Value="_quoteModel.Charac"> <InputSelect id="charac" @bind-Value="quoteModel.Charac">
@foreach (Character display in _charac) @foreach (Character display in charac)
{ {
<option value="@display.caracter">@display.caracter (ID: @display.id_caracter)</option> <option value="@display.caracter">@display.caracter (ID: @display.id_caracter)</option>
} }
@ -35,8 +35,8 @@
<p> <p>
<label for="src"> <label for="src">
<InputSelect id="src" @bind-Value="_quoteModel.TitleSrc"> <InputSelect id="src" @bind-Value="quoteModel.TitleSrc">
@foreach (Source display in _src) @foreach (Source display in src)
{ {
<option value="@display.title">@display.title (ID: @display.id_source)</option> <option value="@display.title">@display.title (ID: @display.id_source)</option>
} }

@ -1,107 +1,109 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using System.Security.Claims; using WF_WebAdmin.Model;
using WF_WebAdmin.Model; using WF_WebAdmin.Service;
using WF_WebAdmin.Service;
namespace WF_WebAdmin.Pages
namespace WF_WebAdmin.Pages {
{ /// <summary>
public partial class Edit /// Page permettant de modifier une citation existante.
{ /// </summary>
[Parameter] public partial class Edit
public int Id { get; set; } {
/// <summary>
[Inject] /// Identifiant de la citation à éditer passé en paramètre de la route.
public ILogger<Edit>? Logger { get; set; } /// </summary>
[Parameter]
[Inject] public int Id { get; set; }
private IQuoteService? quoteService { get; set; }
/// <summary>
[Inject] /// Service pour gérer les citations.
public NavigationManager? NavigationManager { get; set; } /// </summary>
[Inject]
private Quote? Q { get; set; } private IQuoteService quoteService { get; set; }
private QuoteModel _quoteModel = new(); /// <summary>
/// Service pour la navigation entre les pages.
private List<Character> _charac = new List<Character>(); /// </summary>
[Inject]
private List<Source> _src = new List<Source>(); public NavigationManager NavigationManager { get; set; }
/// <summary>
/// <summary> /// Citation actuelle à éditer.
/// Asynchronously initializes the component by loading data related to a quote. /// </summary>
/// This method fetches a specific quote based on the provided ID and populates the `quoteModel` private Quote q { get; set; }
/// with the quote's content, language, character, source, and other associated data.
/// It also loads additional data such as character and source information for the quote. /// <summary>
/// </summary> /// Modèle de données pour l'édition de la citation.
protected override async Task OnInitializedAsync() /// </summary>
{ private QuoteModel quoteModel = new();
// Fetch the quote data based on the provided ID.
Q = await quoteService.getOnequote(Id); /// <summary>
/// Liste des personnages disponibles pour l'édition.
// Populate the quoteModel with the data from the retrieved quote. /// </summary>
_quoteModel.Content = Q.Content; private List<Character> charac = new List<Character>();
_quoteModel.Langue = Q.Langue;
_quoteModel.Charac = Q.Charac; /// <summary>
_quoteModel.TitleSrc = Q.TitleSrc; /// Liste des sources disponibles pour l'édition.
_quoteModel.Id = Q.Id; /// </summary>
_quoteModel.Like = Q.Like; private List<Source> src = new List<Source>();
_quoteModel.ImgPath = Q.ImgPath;
_quoteModel.DateSrc = Q.DateSrc; /// <summary>
_quoteModel.UserProposition = Q.UserProposition; /// Méthode appelée lors de l'initialisation de la page.
_quoteModel.IsValid = Q.IsValid; /// Elle charge la citation à éditer ainsi que les données associées (personnages, sources).
/// </summary>
// Fetch additional data related to the quote, such as character and source. protected override async Task OnInitializedAsync()
_charac = await quoteService.getChar(); {
_src = await quoteService.getSrc(); // Récupère la citation à éditer à partir de l'ID
} q = await quoteService.getOnequote(Id);
/// <summary> // Initialise le modèle de données avec les informations de la citation
/// Handles the submission of a valid form for updating a quote. quoteModel.Content = q.Content;
/// This method takes the data from `quoteModel`, updates the selected quote (`q`) with the new values, quoteModel.Langue = q.Langue;
/// and then calls the `quoteService.updateQuote` method to persist the changes. quoteModel.Charac = q.Charac;
/// After updating, it navigates to the "modifquote" page. quoteModel.TitleSrc = q.TitleSrc;
/// </summary> quoteModel.Id = q.Id;
protected async Task HandleValidSubmit() quoteModel.Like = q.Like;
{ quoteModel.ImgPath = q.ImgPath;
if (Q == null) quoteModel.DateSrc = q.DateSrc;
{ quoteModel.UserProposition = q.UserProposition;
LoggerSaveStub.Log(Logger, LogLevel.Error, "Quote is null."); quoteModel.IsValid = q.IsValid;
return;
} // Charge la liste des personnages et des sources
charac = await quoteService.getChar();
// Update the properties of the selected quote (`q`) with the data from `quoteModel`. src = await quoteService.getSrc();
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Editing the quote {Q.Content}"); }
Q.Content = _quoteModel.Content ?? "Content"; /// <summary>
Q.Langue = _quoteModel.Langue ?? "EN"; /// Méthode appelée lors de la soumission valide du formulaire d'édition.
Q.TitleSrc = _quoteModel.TitleSrc ?? "Something"; /// Elle met à jour la citation avec les nouvelles données.
Q.Charac = _quoteModel.Charac ?? "Someone"; /// </summary>
protected async void HandleValidSubmit()
// Call the quote service to update the quote in the data source. {
await quoteService.updateQuote(Q); // Met à jour les informations de la citation
q.Content = quoteModel.Content;
// Navigate to the "modifquote" page after updating the quote. q.Langue = quoteModel.Langue;
NavigationManager.NavigateTo("modifquote"); q.TitleSrc = quoteModel.TitleSrc;
} q.Charac = quoteModel.Charac;
// Appelle le service pour mettre à jour la citation dans la base de données
await quoteService.updateQuote(q);
/// <summary>
/// Handles the language change event for the quote. // Redirige vers la page des citations à modifier
/// This method updates the `Langue` property of the `quoteModel` based on the selected language (`item`). NavigationManager.NavigateTo("modifquote");
/// It only accepts "fr" (French) or "en" (English) as valid language options. }
/// </summary>
/// <param name="item">The selected language ("fr" or "en") passed to the method.</param> /// <summary>
/// <param name="checkedValue">The checked value (unused in this method but may be used for other purposes).</param> /// Méthode appelée lors du changement de langue dans le formulaire.
private void OnlangChange(string item) /// </summary>
{ /// <param name="item">Langue sélectionnée.</param>
// Check if the selected language is either "fr" or "en" /// <param name="checkedValue">Valeur de l'élément vérifié.</param>
if (item == "fr" || item == "en") private void OnlangChange(string item, object checkedValue)
{ {
// Update the Langue property of the quoteModel with the selected language // Si la langue est "fr" ou "en", met à jour la langue dans le modèle de données
_quoteModel.Langue = item; if (item == "fr" || item == "en")
} {
} quoteModel.Langue = item;
} }
} }
}
}

@ -1,42 +1,42 @@
@page @page
@model WF_WebAdmin.Pages.ErrorModel @model WF_WebAdmin.Pages.ErrorModel
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>error</title> <title>Error</title>
<link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet" /> <link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true" /> <link href="~/css/site.css" rel="stylesheet" asp-append-version="true" />
</head> </head>
<body> <body>
<div class="main"> <div class="main">
<div class="content px-4"> <div class="content px-4">
<h1 class="text-danger">Error.</h1> <h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2> <h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId) @if (Model.ShowRequestId)
{ {
<p> <p>
<strong>Request ID:</strong> <code>@Model.RequestId</code> <strong>Request ID:</strong> <code>@Model.RequestId</code>
</p> </p>
} }
<h3>Development Mode</h3> <h3>Development Mode</h3>
<p> <p>
Swapping to the <strong>development</strong> environment displays detailed information about the error that occurred. Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p> </p>
<p> <p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong> <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users. It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong> For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app. and restarting the app.
</p> </p>
</div> </div>
</div> </div>
</body> </body>
</html> </html>

@ -1,41 +1,45 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics; using System.Diagnostics;
namespace WF_WebAdmin.Pages namespace WF_WebAdmin.Pages
{ {
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] /// <summary>
[IgnoreAntiforgeryToken] /// Modèle de page Razor pour afficher les erreurs dans l'application.
/// </summary>
public class ErrorModel : PageModel [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
{ [IgnoreAntiforgeryToken]
public string? RequestId { get; set; } public class ErrorModel : PageModel
{
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); /// <summary>
/// Identifiant unique de la requête pour le suivi des erreurs.
private readonly ILogger<ErrorModel> _logger; /// </summary>
public string? RequestId { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ErrorModel"/> class. /// Indicateur pour afficher l'ID de la requête si ce dernier est disponible.
/// This constructor is used to inject the <see cref="ILogger{ErrorModel}"/> into the model for logging purposes. /// </summary>
/// </summary> public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
/// <param name="logger">An instance of the <see cref="ILogger{ErrorModel}"/> used for logging error-related information.</param>
public ErrorModel(ILogger<ErrorModel> logger) private readonly ILogger<ErrorModel> _logger;
{
_logger = logger; /// <summary>
} /// Constructeur pour initialiser le modèle de page avec le service de journalisation.
/// </summary>
/// <param name="logger">Service de journalisation pour capturer les erreurs.</param>
/// <summary> public ErrorModel(ILogger<ErrorModel> logger)
/// Handles the GET request for the page or endpoint. {
/// This method retrieves the request ID for tracing purposes, using the `Activity.Current?.Id` _logger = logger;
/// if it's available, or the `HttpContext.TraceIdentifier` as a fallback if no current activity ID is present. }
/// </summary>
public void OnGet() /// <summary>
{ /// Méthode appelée lors de l'accès à la page d'erreur.
// Retrieve the current request ID for tracing /// Elle enregistre l'ID de la requête pour la journalisation et l'affichage des erreurs.
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; /// </summary>
} public void OnGet()
} {
} // Capture de l'ID de la requête à partir de l'activité en cours ou du trace identifier
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}

@ -1,4 +1,4 @@
@page "/login" @page "/"
@using WF_WebAdmin.Model @using WF_WebAdmin.Model
@using System.Globalization @using System.Globalization

@ -1,88 +1,101 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using WF_WebAdmin.Model; using WF_WebAdmin.Model;
namespace WF_WebAdmin.Pages namespace WF_WebAdmin.Pages
{ {
/// <summary>
/// Composant Blazor pour la gestion de la connexion des utilisateurs.
/// </summary>
public partial class Login public partial class Login
{ {
/// <summary>
/// Service de localisation pour gérer les textes multilingues dans la page de connexion.
/// </summary>
[Inject] [Inject]
public IStringLocalizer<Login> Localizer { get; set; } public IStringLocalizer<Login> Localizer { get; set; }
/// <summary>
/// Objet représentant les informations de connexion de l'utilisateur.
/// </summary>
private UserLogin userLogin = new UserLogin(); private UserLogin userLogin = new UserLogin();
/// <summary>
/// Objet représentant l'utilisateur connecté.
/// </summary>
[Inject] [Inject]
public UserLogin uLogin { get; set; } public UserLogin uLogin { get; set; }
/// <summary>
/// Message d'erreur affiché en cas de connexion échouée.
/// </summary>
private string ErrorConnexion; private string ErrorConnexion;
/// <summary>
/// Liste des utilisateurs enregistrés pour la connexion.
/// </summary>
private List<UserLogin> usersConnexion; private List<UserLogin> usersConnexion;
/// <summary>
/// Service HTTP pour effectuer des requêtes.
/// </summary>
[Inject] [Inject]
public HttpClient Http { get; set; } public HttpClient Http { get; set; }
/// <summary>
/// Service de gestion de la navigation dans l'application Blazor.
/// </summary>
[Inject] [Inject]
public NavigationManager NavigationManager { get; set; } public NavigationManager NavigationManager { get; set; }
/// <summary> /// <summary>
/// Asynchronously initializes the component by loading user login data. /// Méthode appelée lors de l'initialisation du composant pour charger les données des utilisateurs.
/// This method retrieves a list of user login information from a JSON file located at the specified URI
/// and populates the `usersConnexion` list with the data.
/// </summary> /// </summary>
/// <returns>Une tâche asynchrone représentant l'initialisation du composant.</returns>
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
// Fetch user login data from the specified JSON file. // Chargement des utilisateurs depuis un fichier JSON simulé.
usersConnexion = await Http.GetFromJsonAsync<List<UserLogin>>($"{NavigationManager.BaseUri}fake-dataUserLogin.json"); usersConnexion = await Http.GetFromJsonAsync<List<UserLogin>>($"{NavigationManager.BaseUri}fake-dataUserLogin.json");
} }
/// <summary> /// <summary>
/// Validates the login credentials of the user. /// Méthode pour valider les informations de connexion de l'utilisateur.
/// This method checks if the provided username and password match any user in the `usersConnexion` list.
/// If the credentials are correct and the user is an admin, it stores the user details and navigates to the home page.
/// If the credentials are incorrect, an error message is set for display.
/// </summary> /// </summary>
public void validlogin() public void validlogin()
{ {
// Check if both the username and password are provided // Vérification si les informations de connexion sont renseignées
if (!string.IsNullOrEmpty(userLogin.Name) || !string.IsNullOrEmpty(userLogin.Mdp)) if (!string.IsNullOrEmpty(userLogin.Name) || !string.IsNullOrEmpty(userLogin.Mdp))
{ {
// Loop through the list of connected users to find a match
foreach (var user in usersConnexion) foreach (var user in usersConnexion)
{ {
// Check if the username and password match the user credentials // Vérification si les identifiants correspondent
if (userLogin.Name == user.Name && userLogin.Mdp == user.Mdp) if (userLogin.Name == user.Name && userLogin.Mdp == user.Mdp)
{ {
// Check if the user is an admin // Vérification si l'utilisateur est un administrateur
if (user.IsAdmin) if (user.IsAdmin)
{ {
// If the user is an admin, store their information and navigate to the home page // Assignation de l'utilisateur à la session
uLogin.Id = userLogin.Id; uLogin.Id = userLogin.Id;
uLogin.Name = user.Name; uLogin.Name = user.Name;
uLogin.Image = user.Image; uLogin.Image = user.Image;
// Redirect to the homepage // Redirection vers la page d'accueil
NavigationManager.NavigateTo(NavigationManager.BaseUri + "accueil"); NavigationManager.NavigateTo(NavigationManager.BaseUri + "accueil");
return; return;
} }
else else
{ {
// If the user is not an admin, display an error message // Message d'erreur pour utilisateur non admin
ErrorConnexion = "Connexion échouée, le nom ou le mot de passe sont incorrectes"; ErrorConnexion = "Connexion échouée, le nom ou le mot de passe sont incorrectes";
} }
} }
else else
{ {
// If credentials do not match, set the error message // Message d'erreur pour informations invalides
ErrorConnexion = "Connexion échouée, le nom ou le mot de passe sont incorrectes"; ErrorConnexion = "Connexion échouée, le nom ou le mot de passe sont incorrectes";
} }
} }
} }
} }
} }
} }

@ -1,31 +0,0 @@
@page "/logs"
<h3>@Localizer["LogTitle"]</h3>
@if (logs is null)
{
<p>@Localizer["NotLog"]</p>
}
else
{
<p>@Localizer["Log"]</p>
<table>
<thead>
<tr>
<th>@Localizer["LogLvl"]</th>
<th>@Localizer["LogContent"]</th>
</tr>
</thead>
<tbody>
@foreach (var log in logs)
{
<tr>
<td>@log.LogLevel</td>
<td>@log.Message</td>
</tr>
}
</tbody>
</table>
}

@ -1,25 +0,0 @@
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using WF_WebAdmin.Model;
namespace WF_WebAdmin.Pages
{
public partial class LogsPage
{
private Logs[] logs;
[Inject]
public HttpClient Http { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
public IStringLocalizer<LogsPage> Localizer { get; set; }
protected override async Task OnInitializedAsync()
{
logs = await Http.GetFromJsonAsync<Logs[]>($"{NavigationManager.BaseUri}fake_data_logs.json");
}
}
}

@ -1,13 +1,13 @@
@using WF_WebAdmin.Model @using WF_WebAdmin.Model
@page "/modifquiz" @page "/modifquiz"
<PageTitle>@Localizer["TitlePage"]</PageTitle> <PageTitle>Gestion des question</PageTitle>
<h3>@Localizer["TitlePage"]</h3> <h3>Gestion des quiz</h3>
<div> <div>
<NavLink class="btn btn-primary" href="Add" Match="NavLinkMatch.All"> <NavLink class="btn btn-primary" href="Add" Match="NavLinkMatch.All">
<i class="fa fa-plus"></i> @Localizer["Add"] <i class="fa fa-plus"></i> Ajouter
</NavLink> </NavLink>
</div> </div>
@ -19,17 +19,17 @@
ShowPager ShowPager
Responsive> Responsive>
<DataGridColumn TItem="Quiz" Field="@nameof(Quiz.Id)" Caption="@Localizer["Id"]" /> <DataGridColumn TItem="Quiz" Field="@nameof(Quiz.Id)" Caption="Id"/>
<DataGridColumn TItem="Quiz" Field="@nameof(Quiz.Question)" Caption="@Localizer["Question"]" /> <DataGridColumn TItem="Quiz" Field="@nameof(Quiz.Question)" Caption="Rep A" />
<DataGridColumn TItem="Quiz" Field="@nameof(Quiz.AnswerA)" Caption="@Localizer["AnswerA"]" /> <DataGridColumn TItem="Quiz" Field="@nameof(Quiz.AnswerA)" Caption="Rep A" />
<DataGridColumn TItem="Quiz" Field="@nameof(Quiz.AnswerB)" Caption="@Localizer["AnswerB"]" /> <DataGridColumn TItem="Quiz" Field="@nameof(Quiz.AnswerB)" Caption="Rep B" />
<DataGridColumn TItem="Quiz" Field="@nameof(Quiz.AnswerC)" Caption="@Localizer["AnswerC"]" /> <DataGridColumn TItem="Quiz" Field="@nameof(Quiz.AnswerC)" Caption="Rep C" />
<DataGridColumn TItem="Quiz" Field="@nameof(Quiz.AnswerD)" Caption="@Localizer["AnswerD"]" /> <DataGridColumn TItem="Quiz" Field="@nameof(Quiz.AnswerD)" Caption="Rep D" />
<DataGridColumn TItem="Quiz" Field="@nameof(Quiz.CAnswer)" Caption="@Localizer["GoodAnswer"]" /> <DataGridColumn TItem="Quiz" Field="@nameof(Quiz.CAnswer)" Caption="Bonne Rep" />
<DataGridColumn TItem="Quiz" Field="@nameof(Quiz.Id)" Caption="@Localizer["Action"]"> <DataGridColumn TItem="Quiz" Field="@nameof(Quiz.Id)" Caption="Action">
<DisplayTemplate> <DisplayTemplate>
<button type="button" class="btn btn-primary" @onclick="() => OnEditButtonClicked(context)"><i class="fa fa-edit"></i> @Localizer["Edit"]</button> <button type="button" class="btn btn-primary" @onclick="() => OnEditButtonClicked(context)"><i class="fa fa-edit"></i> Editer</button>
<button type="button" class="btn btn-primary" @onclick="() => OnDelete(context)"><i class="fa fa-trash"></i> @Localizer["Delete"]</button> <button type="button" class="btn btn-primary" @onclick="() => OnDelete(context)"><i class="fa fa-trash"></i> Supprimer</button>
</DisplayTemplate> </DisplayTemplate>
</DataGridColumn> </DataGridColumn>
</DataGrid> </DataGrid>
@ -38,21 +38,21 @@
{ {
<div class="divPopup"> <div class="divPopup">
<div class="contentPopup"> <div class="contentPopup">
<p>@Localizer["ModifInfoUser"]</p> <p>Modifier les informations de l'utilisateur :</p>
<label>@Localizer["Question"]</label> <label>Question:</label>
<input type="text" @bind="selectedQuiz.Question"/> <input type="text" @bind="selectedQuiz.Question"/>
<label>@Localizer["AnswerA"]</label> <label>Rep A:</label>
<input type="text" @bind="selectedQuiz.AnswerA" /> <input type="text" @bind="selectedQuiz.AnswerA" />
<label>@Localizer["AnswerB"]</label> <label>Rep B:</label>
<input type="text" @bind="selectedQuiz.AnswerB" /> <input type="text" @bind="selectedQuiz.AnswerB" />
<label>@Localizer["AnswerC"]</label> <label>Rep C:</label>
<input type="text" @bind="selectedQuiz.AnswerC" /> <input type="text" @bind="selectedQuiz.AnswerC" />
<label>@Localizer["AnswerD"]</label> <label>Rep D:</label>
<input type="text" @bind="selectedQuiz.AnswerD" /> <input type="text" @bind="selectedQuiz.AnswerD" />
<label>@Localizer["GoodAnswer"]</label> <label>Bonne Rep:</label>
<input type="text" @bind="selectedQuiz.CAnswer" /> <input type="text" @bind="selectedQuiz.CAnswer" />
<button @onclick="EditQuiz">@Localizer["Save"] </button> <button @onclick="EditQuiz">Sauvegarder </button>
<button @onclick="ClosePopup">@Localizer["Delete"]</button> <button @onclick="ClosePopup">Annuler</button>
</div> </div>
</div> </div>
} }
@ -61,9 +61,9 @@
{ {
<div class="divPopup"> <div class="divPopup">
<div class="contentPopup"> <div class="contentPopup">
<p>@Localizer["PopupQuestion"]</p> <p>Êtes-vous sûr de vouloir supprimer ce quiz ?</p>
<button @onclick="RemoveQuote">@Localizer["Yes"]</button> <button @onclick="RemoveQuote">Confirmer</button>
<button @onclick="ClosePopup">@Localizer["Cancel"]</button> <button @onclick="ClosePopup">Annuler</button>
</div> </div>
</div> </div>
} }

@ -1,162 +1,134 @@
using Blazorise.DataGrid; using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization; using WF_WebAdmin.Model;
using System.Security.Claims; using WF_WebAdmin.Service;
using WF_WebAdmin.Model;
using WF_WebAdmin.Service; namespace WF_WebAdmin.Pages
{
namespace WF_WebAdmin.Pages /// <summary>
{ /// Composant Blazor pour la gestion des quiz, permettant leur modification et suppression.
public partial class ModifQuiz /// </summary>
{ public partial class ModifQuiz
private Quiz[] quiz; {
/// <summary>
private int MaxValue = 5; /// Liste des quiz à afficher dans le tableau.
/// </summary>
private int totalItem; private Quiz[] quiz;
private bool showEditQuiz = false; /// <summary>
/// Valeur maximale d'éléments par page.
private Quiz? selectedQuiz; /// </summary>
private int MaxValue = 5;
private bool showPopupDelete = false;
/// <summary>
private int page = 1; /// Nombre total d'éléments dans la liste des quiz.
/// </summary>
[Inject] private int totalItem;
public ILogger<ModifQuiz>? Logger { get; set; }
/// <summary>
[Inject] /// Indicateur de l'affichage de la popup de modification du quiz.
public IStringLocalizer<ModifQuiz> Localizer { get; set; } /// </summary>
private bool showEditQuiz = false;
[Inject]
public IQuizService QuizService { get; set; } /// <summary>
/// Quiz actuellement sélectionné pour modification.
/// <summary> /// </summary>
/// Handles the data reading event for a data grid, fetching quiz data based on the specified page and page size. private Quiz? selectedQuiz;
/// This method makes an asynchronous call to retrieve a specific page of quizzes and updates the `quiz` list and pagination details.
/// If the cancellation token is requested, it exits early without making further calls or updates. /// <summary>
/// </summary> /// Indicateur de l'affichage de la popup de suppression.
/// <param name="e">The event arguments containing pagination details such as page size and page number.</param> /// </summary>
private async Task OnReadData(DataGridReadDataEventArgs<Quiz> e) private bool showPopupDelete = false;
{
// Check if the cancellation token has been requested /// <summary>
if (e.CancellationToken.IsCancellationRequested) /// Numéro de la page actuellement affichée.
{ /// </summary>
return; private int page = 1;
}
/// <summary>
// Fetch the quiz data for the specified page and page size /// Service pour gérer les quiz injecté.
var response = await QuizService.getSommeQuiz(e.PageSize, e.Page); /// </summary>
[Inject]
// If cancellation hasn't been requested, process the data public IQuizService QuizService { get; set; }
if (!e.CancellationToken.IsCancellationRequested)
{ /// <summary>
// Get the total number of quizzes for pagination purposes /// Méthode appelée pour récupérer les données de la page actuelle dans le tableau.
totalItem = await QuizService.getNbQuiz(); /// </summary>
/// <param name="e">Arguments de lecture de données pour le DataGrid.</param>
// Update the quiz data for the current page private async Task OnReadData(DataGridReadDataEventArgs<Quiz> e)
quiz = response.ToArray(); {
if (e.CancellationToken.IsCancellationRequested)
// Update the current page number {
page = e.Page; return;
} }
}
var response = await QuizService.getSommeQuiz(e.PageSize, e.Page);
/// <summary>
/// Handles the event when the "Edit" button is clicked for a quiz. if (!e.CancellationToken.IsCancellationRequested)
/// This method checks if a valid quiz is passed. If so, it sets the `selectedQuiz` to the clicked quiz and shows the quiz edit modal. {
/// </summary> totalItem = await QuizService.getNbQuiz();
/// <param name="quiz">The quiz object that was clicked for editing.</param> quiz = response.ToArray();
private void OnEditButtonClicked(Quiz quiz) page = e.Page;
{ }
// If the quiz is null, return early }
if (quiz == null) return;
/// <summary>
// Set the selected quiz to the one clicked by the user /// Méthode pour afficher la popup de modification d'un quiz.
selectedQuiz = quiz; /// </summary>
/// <param name="quiz">Quiz à modifier.</param>
// Show the modal or UI for editing the quiz private void OnEditButtonClicked(Quiz quiz)
showEditQuiz = true; {
} if (quiz == null) return;
selectedQuiz = quiz;
showEditQuiz = true;
/// <summary> }
/// Closes the open popups and resets any related states.
/// This method hides the quiz edit popup, the delete confirmation popup, and resets the selected quiz to `null`. /// <summary>
/// </summary> /// Méthode pour fermer la popup (modification ou suppression).
private void ClosePopup() /// </summary>
{ private void ClosePopup()
// Hide the edit quiz popup {
showEditQuiz = false; showEditQuiz = false;
showPopupDelete = false;
// Hide the delete confirmation popup selectedQuiz = null;
showPopupDelete = false; }
// Reset the selected quiz to null /// <summary>
selectedQuiz = null; /// Méthode pour modifier un quiz sélectionné.
} /// </summary>
private async Task EditQuiz()
{
if (selectedQuiz != null)
/// <summary> {
/// Edits the selected quiz by updating it in the quiz service. await QuizService.updateQuiz(selectedQuiz);
/// This method asynchronously sends the updated quiz data to the service for persistence. selectedQuiz = null;
/// After updating the quiz, it clears the selected quiz and closes any open popups. ClosePopup();
/// </summary> }
private async Task EditQuiz() }
{
// Update the quiz in the service /// <summary>
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Editing the question {selectedQuiz.Question}"); /// Méthode pour afficher la popup de suppression d'un quiz.
await QuizService.updateQuiz(selectedQuiz); /// </summary>
/// <param name="q">Quiz à supprimer.</param>
// Clear the selected quiz after successful update private void OnDelete(Quiz q)
selectedQuiz = null; {
selectedQuiz = q;
// Close the popups after the edit operation showPopupDelete = true;
ClosePopup(); }
}
/// <summary>
/// Méthode pour supprimer un quiz sélectionné.
/// <summary> /// </summary>
/// Handles the event when the delete action is triggered for a quiz. private async void RemoveQuote()
/// This method sets the selected quiz to the one passed as a parameter and shows the delete confirmation popup. {
/// </summary> if (selectedQuiz != null)
/// <param name="q">The quiz to be deleted.</param> {
private void OnDelete(Quiz q) await QuizService.removeQuiz(selectedQuiz.Id);
{ selectedQuiz = null;
// Set the selected quiz to the one passed in var response = await QuizService.getSommeQuiz(MaxValue, page);
selectedQuiz = q; quiz = response.ToArray();
}
// Show the delete confirmation popup showPopupDelete = false;
showPopupDelete = true; }
} }
}
/// <summary>
/// Removes the selected quiz from the quiz service and updates the quiz list.
/// This method first checks if a quiz is selected, and if so, it deletes the quiz by calling the service.
/// After removal, it clears the `selectedQuiz`, updates the quiz list, and closes the delete confirmation popup.
/// </summary>
private async Task RemoveQuote()
{
// Check if a quiz is selected for deletion
if (selectedQuiz != null)
{
// Remove the selected quiz from the service by its ID
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Delete the question {selectedQuiz.Question}");
await QuizService.removeQuiz(selectedQuiz.Id);
// Clear the selected quiz after successful removal
selectedQuiz = null;
// Update the quiz list by fetching the latest data
var response = await QuizService.getSommeQuiz(MaxValue, page);
quiz = response.ToArray();
}
// Close the delete confirmation popup
showPopupDelete = false;
}
}
}

@ -1,9 +1,9 @@
@using WF_WebAdmin.Model @using WF_WebAdmin.Model
@page "/modifquote" @page "/modifquote"
<PageTitle>@Localizer["TitlePage"]</PageTitle> <PageTitle>Corection des citation</PageTitle>
<h3>@Localizer["TitlePage"]</h3> <h3>Correction des citations</h3>
<DataGrid TItem="Quote" <DataGrid TItem="Quote"
Data="@quotes" Data="@quotes"
@ -13,19 +13,19 @@
ShowPager ShowPager
Responsive> Responsive>
<DataGridColumn TItem="Quote" Field="@nameof(Quote.Id)" Caption="@Localizer["Id"]" /> <DataGridColumn TItem="Quote" Field="@nameof(Quote.Id)" Caption="Id"/>
<DataGridColumn TItem="Quote" Field="@nameof(Quote.Content)" Caption="@Localizer["Quote"]" /> <DataGridColumn TItem="Quote" Field="@nameof(Quote.Content)" Caption="Citation"/>
<DataGridColumn TItem="Quote" Field="@nameof(Quote.Charac)" Caption="@Localizer["Character"]" /> <DataGridColumn TItem="Quote" Field="@nameof(Quote.Charac)" Caption="Personage"/>
<DataGridColumn TItem="Quote" Field="@nameof(Quote.TitleSrc)" Caption="@Localizer["Source"]" /> <DataGridColumn TItem="Quote" Field="@nameof(Quote.TitleSrc)" Caption="Source" />
<DataGridColumn TItem="Quote" Field="@nameof(Quote.Langue)" Caption="@Localizer["Language"]" /> <DataGridColumn TItem="Quote" Field="@nameof(Quote.Langue)" Caption="Langue" />
<DataGridColumn TItem="Quote" Field="@nameof(Quote.DateSrc)" Caption="@Localizer["Date"]" DisplayFormat="{0:d}" DisplayFormatProvider="@System.Globalization.CultureInfo.GetCultureInfo("fr-FR")" /> <DataGridColumn TItem="Quote" Field="@nameof(Quote.DateSrc)" Caption="Date" DisplayFormat="{0:d}" DisplayFormatProvider="@System.Globalization.CultureInfo.GetCultureInfo("fr-FR")" />
<DataGridColumn TItem="Quote" Field="@nameof(Quote.Id)" Caption="@Localizer["Action"]"> <DataGridColumn TItem="Quote" Field="@nameof(Quote.Id)" Caption="Action">
<DisplayTemplate> <DisplayTemplate>
@* <button @onclick="() => OnEditButtonClicked(context) " style="background-color: lightgray; padding: 0"> @* <button @onclick="() => OnEditButtonClicked(context) " style="background-color: lightgray; padding: 0">
<img alt="Bouton Modifier" src="edit.png" width="30" height="30"/> <img alt="Bouton Modifier" src="edit.png" width="30" height="30"/>
</button> *@ </button> *@
<a href="Edit/@(context.Id)" class="btn btn-primary"><i class="fa fa-edit"></i> @Localizer["Edit"]</a> <a href="Edit/@(context.Id)" class="btn btn-primary"><i class="fa fa-edit"></i> Editer</a>
<button type="button" class="btn btn-primary" @onclick="() => OnDelete(context)"><i class="fa fa-trash"></i> @Localizer["Delete"]</button> <button type="button" class="btn btn-primary" @onclick="() => OnDelete(context)"><i class="fa fa-trash"></i> Supprimer</button>
</DisplayTemplate> </DisplayTemplate>
</DataGridColumn> </DataGridColumn>
</DataGrid> </DataGrid>
@ -50,9 +50,9 @@
{ {
<div class="divPopup"> <div class="divPopup">
<div class="contentPopup"> <div class="contentPopup">
<p>@Localizer["PopupQuestion"]</p> <p>Êtes-vous sûr de vouloir supprimer cette citation ?</p>
<button @onclick="RemoveQuote">@Localizer["Yes"]</button> <button @onclick="RemoveQuote">Confirmer</button>
<button @onclick="ClosePopup">@Localizer["Cancel"]</button> <button @onclick="ClosePopup">Annuler</button>
</div> </div>
</div> </div>
} }

@ -1,135 +1,104 @@
using Blazorise.DataGrid; using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization; using WF_WebAdmin.Model;
using Microsoft.Extensions.Logging; using WF_WebAdmin.Service;
using System.Security.Claims;
using WF_WebAdmin.Model; namespace WF_WebAdmin.Pages
using WF_WebAdmin.Service; {
/// <summary>
namespace WF_WebAdmin.Pages /// Composant Blazor pour la gestion des citations, permettant la modification et la suppression de celles-ci.
{ /// </summary>
public partial class ModifQuote public partial class ModifQuote
{ {
private Quote[] quotes; /// <summary>
/// Liste des citations à afficher dans le tableau.
private int MaxValue = 5; /// </summary>
private Quote[] quotes;
private int totalItem;
/// <summary>
/*private bool showEditQuote = false;*/ /// Valeur maximale d'éléments par page.
/// </summary>
private Quote? selectedQuote; private int MaxValue = 5;
private bool showPopupDelete = false; /// <summary>
/// Nombre total d'éléments dans la liste des citations.
private int page = 1; /// </summary>
private int totalItem;
[Inject]
public ILogger<ModifQuote>? Logger { get; set; } /// <summary>
/// Citation actuellement sélectionnée pour modification.
[Inject] /// </summary>
public IStringLocalizer<ModifQuote> Localizer { get; set; } private Quote? selectedQuote;
[Inject] /// <summary>
public IQuoteService QuoteService { get; set; } /// Indicateur de l'affichage de la popup de suppression.
/// </summary>
private bool showPopupDelete = false;
/// <summary>
/// Handles the data reading event for a data grid, fetching quote data based on the specified page and page size. /// <summary>
/// This method makes an asynchronous call to retrieve a specific page of quotes and updates the `quotes` list and pagination details. /// Numéro de la page actuellement affichée.
/// If the cancellation token is requested, it exits early without making further calls or updates. /// </summary>
/// </summary> private int page = 1;
/// <param name="e">The event arguments containing pagination details such as page size and page number.</param>
private async Task OnReadData(DataGridReadDataEventArgs<Quote> e) /// <summary>
{ /// Service pour gérer les citations injecté.
// Check if the cancellation token has been requested /// </summary>
if (e.CancellationToken.IsCancellationRequested) [Inject]
{ public IQuoteService QuoteService { get; set; }
return;
} /// <summary>
/// Méthode appelée pour récupérer les données de la page actuelle dans le tableau.
// Fetch the quote data for the specified page and page size /// </summary>
var response = await QuoteService.getSomeQuote(e.PageSize, e.Page); /// <param name="e">Arguments de lecture de données pour le DataGrid.</param>
private async Task OnReadData(DataGridReadDataEventArgs<Quote> e)
// If cancellation hasn't been requested, process the data {
if (!e.CancellationToken.IsCancellationRequested) if (e.CancellationToken.IsCancellationRequested)
{ {
// Get the total number of quotes for pagination purposes return;
totalItem = await QuoteService.getNbQuote(); }
// Update the quotes data for the current page var response = await QuoteService.getSomeQuote(e.PageSize, e.Page);
quotes = response.ToArray();
if (!e.CancellationToken.IsCancellationRequested)
// Update the current page number {
page = e.Page; totalItem = await QuoteService.getNbQuote();
} quotes = response.ToArray();
} page = e.Page;
}
/*private void OnEditButtonClicked(Quote quote) }
{
if (selectedQuote == null) return; /// <summary>
selectedQuote = quote; /// Méthode pour fermer la popup (modification ou suppression).
showEditQuote = true; /// </summary>
}*/ private void ClosePopup()
{
/// <summary> showPopupDelete = false;
/// Closes the open popups and resets any related states. selectedQuote = null;
/// This method hides the delete confirmation popup and clears the selected quote. }
/// </summary>
private void ClosePopup() /// <summary>
{ /// Méthode appelée pour afficher la popup de suppression d'une citation.
// Hide the delete confirmation popup /// </summary>
showPopupDelete = false; /// <param name="q">Citation à supprimer.</param>
private void OnDelete(Quote q)
// Reset the selected quote to null {
selectedQuote = null; selectedQuote = q;
} showPopupDelete = true;
}
/*private async Task EditQuote()
{ /// <summary>
await QuoteService.updateQuote(selectedQuote); /// Méthode pour supprimer une citation sélectionnée.
selectedQuote = null; /// </summary>
ClosePopup(); private async void RemoveQuote()
}*/ {
if (selectedQuote != null)
/// <summary> {
/// Handles the event when the delete action is triggered for a quote. await QuoteService.removeQuote(selectedQuote);
/// This method sets the selected quote to the one passed as a parameter and displays the delete confirmation popup. selectedQuote= null;
/// </summary> var response = await QuoteService.getSomeQuote(MaxValue, page);
/// <param name="q">The quote that is being deleted.</param> quotes = response.ToArray();
private void OnDelete(Quote q) }
{ showPopupDelete= false;
// Set the selected quote to the one passed in }
selectedQuote = q; }
}
// Display the delete confirmation popup
showPopupDelete = true;
}
/// <summary>
/// Removes the selected quote by calling the remove service and updates the quote list.
/// This method checks if a quote is selected. If so, it removes the quote using the `QuoteService`, clears the selected quote,
/// and fetches the updated list of quotes. It also closes the delete confirmation popup after the operation.
/// </summary>
private async Task RemoveQuote()
{
// Check if a quote is selected for removal
if (selectedQuote != null)
{
// Remove the selected quote using the QuoteService
LoggerSaveStub.Log(Logger, LogLevel.Information, $"The quote {selectedQuote.Content} has been deleted");
await QuoteService.removeQuote(selectedQuote);
// Clear the selected quote after removal
selectedQuote = null;
// Update the quotes list by fetching the latest quotes data
var response = await QuoteService.getSomeQuote(MaxValue, page);
quotes = response.ToArray();
}
// Close the delete confirmation popup
showPopupDelete = false;
}
}
}

@ -2,28 +2,28 @@
@using System.Dynamic @using System.Dynamic
@using WF_WebAdmin.Model @using WF_WebAdmin.Model
<h3>@Localizer["TitleQuiz"]</h3> <h3> Quiz à valider </h3>
@if (quizzes == null) @if (quizzes == null)
{ {
<p> @Localizer["LoadQuiz"] </p> <p> Chargement des quiz ... </p>
} }
else else
{ {
<p>@Localizer["QuizAwait"] </p> <p> Quizs en attente de validation : </p>
<table> <table>
<thead> <thead>
<tr> <tr>
<th>#</th> <th>#</th>
<th>@Localizer["Question"]</th> <th>Question</th>
<th>@Localizer["AnswerA"]</th> <th>Réponse A</th>
<th>@Localizer["AnswerB"]</th> <th>Réponse B</th>
<th>@Localizer["AnswerC"]</th> <th>Réponse C</th>
<th>@Localizer["AnswerD"]</th> <th>Réponse D</th>
<th>@Localizer["GoodAnswer"]</th> <th>Réponse Correcte</th>
<th>@Localizer["User"]</th> <th>Utilisateur</th>
<th>@Localizer["Action"]</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

@ -1,100 +1,88 @@
using Blazorise.DataGrid; using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Security.Claims;
using WF_WebAdmin.Model; using WF_WebAdmin.Model;
using WF_WebAdmin.Service; using WF_WebAdmin.Service;
namespace WF_WebAdmin.Pages namespace WF_WebAdmin.Pages
{ {
/// <summary>
/// Composant Blazor pour gérer la validation des quiz.
/// </summary>
public partial class ValidQuiz public partial class ValidQuiz
{ {
/// <summary>
/// Liste des quiz à valider.
/// </summary>
private List<Quiz> quizzes; private List<Quiz> quizzes;
[Inject]
public ILogger<ValidQuiz>? Logger { get; set; } /// <summary>
/// Client HTTP injecté pour effectuer des requêtes API.
[Inject] /// </summary>
public IStringLocalizer<ValidQuiz> Localizer { get; set; }
[Inject] [Inject]
public HttpClient Http { get; set; } public HttpClient Http { get; set; }
/// <summary>
/// Gestionnaire de navigation injecté pour gérer les URL.
/// </summary>
[Inject] [Inject]
public NavigationManager NavigationManager { get; set; } public NavigationManager NavigationManager { get; set; }
/// <summary>
/// Service pour gérer les quiz injecté.
/// </summary>
[Inject] [Inject]
public IQuizService QuizService { get; set; } public IQuizService QuizService { get; set; }
/// <summary> /// <summary>
/// Initializes the component asynchronously by fetching the quizzes that need validation. /// Méthode appelée lors de l'initialisation du composant. Charge la liste des quiz à valider.
/// This method retrieves a list of quizzes from the `QuizService` that are pending validation when the component is initialized.
/// </summary> /// </summary>
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
// Fetch quizzes that need validation
quizzes = await QuizService.getQuizzesToValidate(); quizzes = await QuizService.getQuizzesToValidate();
} }
/// <summary> /// <summary>
/// Handles the event when the "Validate" button is clicked for a quiz. /// Méthode appelée lorsqu'on clique sur le bouton de validation d'un quiz.
/// This method calls the `ValidateQuiz` method, passing the specified quiz for validation.
/// </summary> /// </summary>
/// <param name="quiz">The quiz that is being validated.</param> /// <param name="quiz">Le quiz à valider.</param>
private void OnValidButton(Quiz quiz) private void OnValidButton(Quiz quiz)
{ {
// Call the ValidateQuiz method to validate the quiz
ValidateQuiz(quiz); ValidateQuiz(quiz);
} }
/// <summary> /// <summary>
/// Validates the specified quiz by setting its `IsValid` property to true and updating its state in the service. /// Valide un quiz en mettant à jour son état.
/// This method logs a message to the console indicating the quiz has been validated, then updates the quiz's validation status.
/// It then calls the `QuizService.updateQuiz` method to persist the changes.
/// </summary> /// </summary>
/// <param name="quiz">The quiz that is being validated.</param> /// <param name="quiz">Le quiz à valider.</param>
private void ValidateQuiz(Quiz quiz) private void ValidateQuiz(Quiz quiz)
{ {
// Log the validation action to the console
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Quiz {quiz.Id} validated!");
Console.WriteLine($"Quiz {quiz.Id} validated!"); Console.WriteLine($"Quiz {quiz.Id} validated!");
// Create a new quiz instance (or modify the existing one)
Quiz newQuiz = quiz; Quiz newQuiz = quiz;
newQuiz.IsValid = true; newQuiz.IsValid = true;
// Update the quiz state in the QuizService // Mis à jour de l'état du quiz
QuizService.updateQuiz(quiz); QuizService.updateQuiz(quiz);
} }
/// <summary> /// <summary>
/// Handles the event when the "Reject" button is clicked for a quiz. /// Méthode appelée lorsqu'on clique sur le bouton de rejet d'un quiz.
/// This method calls the `RejectQuiz` method, passing the specified quiz to be rejected.
/// </summary> /// </summary>
/// <param name="quiz">The quiz that is being rejected.</param> /// <param name="quiz">Le quiz à rejeter.</param>
private void OnRejectButton(Quiz quiz) private void OnRejectButton(Quiz quiz)
{ {
// Call the RejectQuiz method to reject the quiz
RejectQuiz(quiz); RejectQuiz(quiz);
} }
/// <summary> /// <summary>
/// Rejects the specified quiz by logging a rejection message and removing it from the QuizService. /// Rejette un quiz en supprimant son état de validation.
/// This method logs a message to the console indicating the quiz has been rejected, and then calls the `QuizService.removeQuiz`
/// method to remove the quiz from the system.
/// </summary> /// </summary>
/// <param name="quiz">The quiz that is being rejected.</param> /// <param name="quiz">Le quiz à rejeter.</param>
private void RejectQuiz(Quiz quiz) private void RejectQuiz(Quiz quiz)
{ {
// Log the rejection action to the console
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Quiz {quiz.Id} rejected");
Console.WriteLine($"Quiz {quiz.Id} rejected!"); Console.WriteLine($"Quiz {quiz.Id} rejected!");
// Remove the rejected quiz from the QuizService
QuizService.removeQuiz(quiz.Id); QuizService.removeQuiz(quiz.Id);
} }
} }
} }

@ -1,11 +1,11 @@
@page "/ValidQuote" @page "/ValidQuote"
@using WF_WebAdmin.Model @using WF_WebAdmin.Model
<h3>@Localizer["TitleValid"]</h3> <h3>Citations non validées</h3>
@if (quotes is null) @if (quotes is null)
{ {
<p>@Localizer["LoginQuote"]</p> <p>Chargement des citations...</p>
} }
@* else if (quotes.Count == 0) @* else if (quotes.Count == 0)
{ {
@ -13,20 +13,20 @@
} *@ } *@
else else
{ {
<p>@Localizer["QuoteValid"]</p> <p>Citations en attente de validation :</p>
@foreach (var quote in quotes) @foreach (var quote in quotes)
{ {
<div class="QuoteDiv"> <div class="QuoteDiv">
<p><strong>@Localizer["Id"]</strong> @quote.Id</p> <p><strong>ID :</strong> @quote.Id</p>
<p><strong>@Localizer["Content"]</strong> @quote.Content</p> <p><strong>Contenu :</strong> @quote.Content</p>
<p><strong>@Localizer["Language"]</strong> @quote.Langue</p> <p><strong>Langue :</strong> @quote.Langue</p>
<p><strong>@Localizer["Character"]</strong> @quote.Charac</p> <p><strong>Personnage :</strong> @quote.Charac</p>
<p><strong>@Localizer["Image"]</strong> @quote.ImgPath</p> <p><strong>Image :</strong> @quote.ImgPath</p>
<p><strong>@Localizer["Source"]</strong> @quote.TitleSrc</p> <p><strong>Source :</strong> @quote.TitleSrc</p>
<p><strong>@Localizer["Date"]</strong> @quote.DateSrc.ToShortDateString()</p> <p><strong>Date de source :</strong> @quote.DateSrc.ToShortDateString()</p>
<p><strong>@Localizer["User"]</strong> @quote.UserProposition</p> <p><strong>Utilisateur :</strong> @quote.UserProposition</p>
@* <button @onclick="() => ValiderQuote(quote.Id)">Valider</button> @* <button @onclick="() => ValiderQuote(quote.Id)">Valider</button>
<button @onclick="() => RejeterQuote(quote.Id)">Rejeter</button> *@ <button @onclick="() => RejeterQuote(quote.Id)">Rejeter</button> *@

@ -6,32 +6,37 @@ using System.Collections.Generic;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq; using System.Linq;
using Microsoft.Extensions.Localization;
namespace WF_WebAdmin.Pages namespace WF_WebAdmin.Pages
{ {
/// <summary>
/// Composant Blazor pour afficher et valider des citations.
/// </summary>
public partial class ValidQuote public partial class ValidQuote
{ {
private Quote[] quotes; /// <summary>
/// Tableau contenant les citations à valider.
[Inject] /// </summary>
public IStringLocalizer<ValidQuote> Localizer { get; set; } private Quote[] quotes;
/// <summary>
/// Client HTTP injecté pour effectuer des requêtes API.
/// </summary>
[Inject] [Inject]
public HttpClient Http { get; set; } public HttpClient Http { get; set; }
/// <summary>
/// Gestionnaire de navigation injecté pour gérer les URL.
/// </summary>
[Inject] [Inject]
public NavigationManager NavigationManager { get; set; } public NavigationManager NavigationManager { get; set; }
/// <summary> /// <summary>
/// Initializes the component asynchronously by fetching a list of quotes from a JSON file. /// Méthode appelée à l'initialisation du composant pour charger les citations.
/// This method makes an asynchronous HTTP request to retrieve an array of `Quote` objects from a specified JSON file /// </summary>
/// located at the base URI, and then assigns the result to the `quotes` variable. protected override async Task OnInitializedAsync()
/// </summary> {
protected override async Task OnInitializedAsync() quotes = await Http.GetFromJsonAsync<Quote[]>($"{NavigationManager.BaseUri}fake-dataQuote.json");
{
// Fetch the list of quotes from the JSON file located at the base URI
quotes = await Http.GetFromJsonAsync<Quote[]>($"{NavigationManager.BaseUri}fake-dataQuote.json");
} }
} }
} }

@ -1,13 +1,4 @@
@* @page "/"
File: _Host.cshtml
Project: WF_WebAdmin
Description: ...
Author: What The Fantasy
Created: 10/02/2025
Last Modified: 10/02/2025
*@
@page "/"
@namespace WF_WebAdmin.Pages @namespace WF_WebAdmin.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{ @{
@ -15,7 +6,3 @@
} }
<component type="typeof(App)" render-mode="ServerPrerendered" /> <component type="typeof(App)" render-mode="ServerPrerendered" />
<body>
<script src="https://cdn.syncfusion.com/blazor/22.1.38/syncfusion-blazor.min.js" type="text/javascript"></script>
</body>

@ -1,49 +1,39 @@
@* @using Microsoft.AspNetCore.Components.Web
File: _Layout.cshtml @namespace WF_WebAdmin.Pages
Project: WF_WebAdmin @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Description: ...
Author: What The Fantasy <!DOCTYPE html>
Created: 10/02/2025 <html lang="en">
Last Modified: 10/02/2025 <head>
*@ <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
@using Microsoft.AspNetCore.Components.Web <base href="~/" />
@namespace WF_WebAdmin.Pages <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers <link href="css/site.css" rel="stylesheet" />
<link href="WF-WebAdmin.styles.css" rel="stylesheet" />
<!DOCTYPE html> <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
<html lang="en"> <link rel="icon" type="image/svg" href="../Shared/iconwf.svg" />
<head> </head>
<title>layout</title> <body>
<meta charset="utf-8" /> @RenderBody()
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="~/" /> <div id="blazor-error-ui">
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" /> <environment include="Staging,Production">
<link href="css/site.css" rel="stylesheet" /> An error has occurred. This application may no longer respond until reloaded.
<link href="WF-WebAdmin.styles.css" rel="stylesheet" /> </environment>
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" /> <environment include="Development">
<link rel="icon" type="image/svg" href="../Shared/iconwf.svg" /> An unhandled exception has occurred. See browser dev tools for details.
</head> </environment>
<body> <a href="" class="reload">Reload</a>
@RenderBody() <a class="dismiss">🗙</a>
</div>
<div id="blazor-error-ui">
<environment include="Staging,Production"> <script src="_framework/blazor.server.js"></script>
An error has occurred. This application may no longer respond until reloaded.
</environment> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css">
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details. <link href="_content/Blazorise/blazorise.css" rel="stylesheet" />
</environment> <link href="_content/Blazorise.Bootstrap/blazorise.bootstrap.css" rel="stylesheet" />
<a href="" class="reload">reload</a>
<a class="dismiss">🗙</a> </body>
</div> </html>
<script src="_framework/blazor.server.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css">
<link href="_content/Blazorise/blazorise.css" rel="stylesheet" />
<link href="_content/Blazorise.Bootstrap/blazorise.bootstrap.css" rel="stylesheet" />
</body>
</html>

@ -1,6 +1,8 @@
using Blazorise; using Blazorise;
using Blazorise.Bootstrap; using Blazorise.Bootstrap;
using Blazorise.Icons.FontAwesome; using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;
using System.Globalization; using System.Globalization;
using WF_WebAdmin.Data; using WF_WebAdmin.Data;
@ -9,24 +11,21 @@ using WF_WebAdmin.Model;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Blazored.Modal; using Blazored.Modal;
using MudBlazor.Services; using WF_WebAdmin.Service;
[assembly: RootNamespace("WF_WebAdmin")] [assembly: RootNamespace("WF_WebAdmin")]
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(); builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>(); builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddScoped<IQuoteService,QuoteServiceStub>(); builder.Services.AddScoped<IQuoteService,QuoteServiceStub>();
builder.Services.AddScoped<IQuizService,QuizServiceStub>(); builder.Services.AddScoped<IQuizService,QuizServiceStub>();
builder.Services.AddScoped<IUserService, UserServiceStub>(); builder.Services.AddScoped<IUserService, UserServiceStub>();
builder.Services.AddScoped<ICommentaryService, CommentaryServiceStub>();
builder.Services.AddHttpClient(); builder.Services.AddHttpClient();
builder.Services.AddScoped<UserLogin>(); builder.Services.AddScoped<UserLogin>();
builder.Services.AddMudServices();
builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
builder.Services builder.Services
.AddBlazorise() .AddBlazorise()

@ -1,144 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AnswerA" xml:space="preserve">
<value>Answer A:</value>
</data>
<data name="AnswerB" xml:space="preserve">
<value>Answer B:</value>
</data>
<data name="AnswerC" xml:space="preserve">
<value>Answer C:</value>
</data>
<data name="AnswerD" xml:space="preserve">
<value>Answer D:</value>
</data>
<data name="GoodAnswer" xml:space="preserve">
<value>Good answer:</value>
</data>
<data name="Submit" xml:space="preserve">
<value>Submit</value>
</data>
<data name="TitleAddQuiz" xml:space="preserve">
<value>Add a question</value>
</data>
<data name="TitleQuestion" xml:space="preserve">
<value>Question:</value>
</data>
</root>

@ -1,144 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AnswerA" xml:space="preserve">
<value>Réponse A:</value>
</data>
<data name="AnswerB" xml:space="preserve">
<value>Réponse B:</value>
</data>
<data name="AnswerC" xml:space="preserve">
<value>Réponse C:</value>
</data>
<data name="AnswerD" xml:space="preserve">
<value>Réponse D:</value>
</data>
<data name="GoodAnswer" xml:space="preserve">
<value>Bonne réponse:</value>
</data>
<data name="Submit" xml:space="preserve">
<value>Valider</value>
</data>
<data name="TitleAddQuiz" xml:space="preserve">
<value>Ajouter une Question</value>
</data>
<data name="TitleQuestion" xml:space="preserve">
<value>Question:</value>
</data>
</root>

@ -1,135 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Log" xml:space="preserve">
<value>Current Logs</value>
</data>
<data name="LogContent" xml:space="preserve">
<value>Content</value>
</data>
<data name="LogLvl" xml:space="preserve">
<value>Type of Logs :</value>
</data>
<data name="LogTitle" xml:space="preserve">
<value>Logs</value>
</data>
<data name="NotLog" xml:space="preserve">
<value>No Log</value>
</data>
</root>

@ -1,135 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Log" xml:space="preserve">
<value>Logs actuels</value>
</data>
<data name="LogContent" xml:space="preserve">
<value>Contenu</value>
</data>
<data name="LogLvl" xml:space="preserve">
<value>Type de Logs :</value>
</data>
<data name="LogTitle" xml:space="preserve">
<value>Logs</value>
</data>
<data name="NotLog" xml:space="preserve">
<value>Aucun Log</value>
</data>
</root>

@ -1,171 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Action" xml:space="preserve">
<value>Action</value>
</data>
<data name="Add" xml:space="preserve">
<value>Add</value>
</data>
<data name="AnswerA" xml:space="preserve">
<value>Answer A</value>
</data>
<data name="AnswerB" xml:space="preserve">
<value>Answer B</value>
</data>
<data name="AnswerC" xml:space="preserve">
<value>Answer C</value>
</data>
<data name="AnswerD" xml:space="preserve">
<value>Answer D</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Edit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="GoodAnswer" xml:space="preserve">
<value>Good Answer</value>
</data>
<data name="Id" xml:space="preserve">
<value>ID</value>
</data>
<data name="ModifInfoUser" xml:space="preserve">
<value>Edit user information :</value>
</data>
<data name="PopupQuestion" xml:space="preserve">
<value>Are you sure you want to delete this quiz ?</value>
</data>
<data name="Question" xml:space="preserve">
<value>Question</value>
</data>
<data name="Save" xml:space="preserve">
<value>Save</value>
</data>
<data name="TitlePage" xml:space="preserve">
<value>Quiz Management</value>
</data>
<data name="Yes" xml:space="preserve">
<value>Confirm</value>
</data>
</root>

@ -1,171 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Action" xml:space="preserve">
<value>Action</value>
</data>
<data name="Add" xml:space="preserve">
<value>Ajouter</value>
</data>
<data name="AnswerA" xml:space="preserve">
<value>Réponse A</value>
</data>
<data name="AnswerB" xml:space="preserve">
<value>Réponse B</value>
</data>
<data name="AnswerC" xml:space="preserve">
<value>Réponse C</value>
</data>
<data name="AnswerD" xml:space="preserve">
<value>Réponse D</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Annuler</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Supprimer</value>
</data>
<data name="Edit" xml:space="preserve">
<value>Editer</value>
</data>
<data name="GoodAnswer" xml:space="preserve">
<value>Bonne réponse</value>
</data>
<data name="Id" xml:space="preserve">
<value>ID</value>
</data>
<data name="ModifInfoUser" xml:space="preserve">
<value>Modifier les informations de l'utilisateur :</value>
</data>
<data name="PopupQuestion" xml:space="preserve">
<value>Êtes-vous sûr de vouloir supprimer ce quiz ?</value>
</data>
<data name="Question" xml:space="preserve">
<value>Question</value>
</data>
<data name="Save" xml:space="preserve">
<value>Sauvegarder</value>
</data>
<data name="TitlePage" xml:space="preserve">
<value>Gestion des quiz</value>
</data>
<data name="Yes" xml:space="preserve">
<value>Confirmer</value>
</data>
</root>

@ -1,159 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Action" xml:space="preserve">
<value>Action</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Character" xml:space="preserve">
<value>Character</value>
</data>
<data name="Date" xml:space="preserve">
<value>Date</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Edit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Id" xml:space="preserve">
<value>Id</value>
</data>
<data name="Language" xml:space="preserve">
<value>Language</value>
</data>
<data name="PopupQuestion" xml:space="preserve">
<value>Are you sure you want to delete this quote ?</value>
</data>
<data name="Quote" xml:space="preserve">
<value>Quote</value>
</data>
<data name="Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="TitlePage" xml:space="preserve">
<value>Corrections of quotes</value>
</data>
<data name="Yes" xml:space="preserve">
<value>Confirm</value>
</data>
</root>

@ -1,159 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Action" xml:space="preserve">
<value>Action</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Annuler</value>
</data>
<data name="Character" xml:space="preserve">
<value>Personage</value>
</data>
<data name="Date" xml:space="preserve">
<value>Date</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Supprimer</value>
</data>
<data name="Edit" xml:space="preserve">
<value>Editer</value>
</data>
<data name="Id" xml:space="preserve">
<value>Id</value>
</data>
<data name="Language" xml:space="preserve">
<value>Langue</value>
</data>
<data name="PopupQuestion" xml:space="preserve">
<value>Êtes-vous sûr de vouloir supprimer cette citation ?</value>
</data>
<data name="Quote" xml:space="preserve">
<value>Citation</value>
</data>
<data name="Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="TitlePage" xml:space="preserve">
<value>Correction des citations</value>
</data>
<data name="Yes" xml:space="preserve">
<value>Confirmer</value>
</data>
</root>

@ -1,153 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Action" xml:space="preserve">
<value>Actions</value>
</data>
<data name="AnswerA" xml:space="preserve">
<value>Answer A</value>
</data>
<data name="AnswerB" xml:space="preserve">
<value>Answer B</value>
</data>
<data name="AnswerC" xml:space="preserve">
<value>Answer C</value>
</data>
<data name="AnswerD" xml:space="preserve">
<value>Answer D</value>
</data>
<data name="GoodAnswer" xml:space="preserve">
<value>Correct answer</value>
</data>
<data name="LoadQuiz" xml:space="preserve">
<value>Loading quizzes</value>
</data>
<data name="Question" xml:space="preserve">
<value>Question</value>
</data>
<data name="QuizAwait" xml:space="preserve">
<value>Quiz awaiting validation</value>
</data>
<data name="TitleQuiz" xml:space="preserve">
<value>Quiz to validate</value>
</data>
<data name="User" xml:space="preserve">
<value>User</value>
</data>
</root>

@ -1,153 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Action" xml:space="preserve">
<value>Actions</value>
</data>
<data name="AnswerA" xml:space="preserve">
<value>Réponse A </value>
</data>
<data name="AnswerB" xml:space="preserve">
<value>Réponse B</value>
</data>
<data name="AnswerC" xml:space="preserve">
<value>Réponse C</value>
</data>
<data name="AnswerD" xml:space="preserve">
<value>Réponse D</value>
</data>
<data name="GoodAnswer" xml:space="preserve">
<value>Réponse Correcte</value>
</data>
<data name="LoadQuiz" xml:space="preserve">
<value>Chargement des quiz ...</value>
</data>
<data name="Question" xml:space="preserve">
<value>Question</value>
</data>
<data name="QuizAwait" xml:space="preserve">
<value>Quizs en attente de validation :</value>
</data>
<data name="TitleQuiz" xml:space="preserve">
<value>Quiz à valider</value>
</data>
<data name="User" xml:space="preserve">
<value>Utilisateur</value>
</data>
</root>

@ -1,153 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Character" xml:space="preserve">
<value>Character :</value>
</data>
<data name="Content" xml:space="preserve">
<value>Content :</value>
</data>
<data name="Date" xml:space="preserve">
<value>Source date</value>
</data>
<data name="Id" xml:space="preserve">
<value>ID :</value>
</data>
<data name="Image" xml:space="preserve">
<value>Image :</value>
</data>
<data name="Language" xml:space="preserve">
<value>Language :</value>
</data>
<data name="LoginQuote" xml:space="preserve">
<value>Loading quotes</value>
</data>
<data name="QuoteValid" xml:space="preserve">
<value>Quotes awaiting validation</value>
</data>
<data name="Source" xml:space="preserve">
<value>Source :</value>
</data>
<data name="TitleValid" xml:space="preserve">
<value>Unvalidated quotes</value>
</data>
<data name="User" xml:space="preserve">
<value>User :</value>
</data>
</root>

@ -1,153 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Character" xml:space="preserve">
<value>Personnage :</value>
</data>
<data name="Content" xml:space="preserve">
<value>Contenu :</value>
</data>
<data name="Date" xml:space="preserve">
<value>Date de la source :</value>
</data>
<data name="Id" xml:space="preserve">
<value>ID :</value>
</data>
<data name="Image" xml:space="preserve">
<value>Image :</value>
</data>
<data name="Language" xml:space="preserve">
<value>Langue :</value>
</data>
<data name="LoginQuote" xml:space="preserve">
<value>Chargement des citations...</value>
</data>
<data name="QuoteValid" xml:space="preserve">
<value>Citations en attente de validation :</value>
</data>
<data name="Source" xml:space="preserve">
<value>Source :</value>
</data>
<data name="TitleValid" xml:space="preserve">
<value>Citations non validées</value>
</data>
<data name="User" xml:space="preserve">
<value>Utilisateur :</value>
</data>
</root>

@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using WF_WebAdmin.Converter;
using WF_WebAdmin.Model;
namespace WF_WebAdmin.Service
{
public class CommentaryServiceStub : ICommentaryService
{
private readonly string? _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataCommentary.json");
public async Task<List<Commentary>> GetCommentsAsync()
{
if (!File.Exists(_jsonFilePath))
{
Console.Out.WriteLine($"{_jsonFilePath} not found");
return new List<Commentary>();
}
var json = await File.ReadAllTextAsync(_jsonFilePath);
var dtoList = JsonSerializer.Deserialize<List<CommentaryDto>>(json) ?? new List<CommentaryDto>();
var comments = dtoList.ConvertAll(dto => dto.ToModel());
Console.Out.WriteLine($"Nombre de commentaires chargés : {comments.Count}");
return comments;
}
}
}

@ -1,11 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using WF_WebAdmin.Model;
namespace WF_WebAdmin.Service
{
public interface ICommentaryService
{
Task<List<Commentary>> GetCommentsAsync();
}
}

@ -1,17 +0,0 @@
using WF_WebAdmin.Model;
namespace WF_WebAdmin.Service
{
public interface ILogsService
{
public Task removeLogs(Logs logs);
public Task<List<Logs>> getAllLogs();
public Task<List<Logs>> getSomeLogs(int nb, int page);
public Task addLogs(Logs logs);
public Task<int> getNbLogs();
}
}

@ -2,25 +2,66 @@ using WF_WebAdmin.Model;
namespace WF_WebAdmin.Service namespace WF_WebAdmin.Service
{ {
/// <summary>
/// Interface définissant les opérations de gestion des quiz.
/// </summary>
public interface IQuizService public interface IQuizService
{ {
/// <summary>
/// Ajoute un quiz.
/// </summary>
/// <param name="quiz">Quiz à ajouter.</param>
public Task addQuiz(Quiz quiz); public Task addQuiz(Quiz quiz);
/// <summary>
/// Met à jour un quiz existant.
/// </summary>
/// <param name="quiz">Quiz à mettre à jour.</param>
public Task updateQuiz(Quiz quiz); public Task updateQuiz(Quiz quiz);
/// <summary>
/// Supprime un quiz par son identifiant.
/// </summary>
/// <param name="id">Identifiant du quiz à supprimer.</param>
public Task removeQuiz(int id); public Task removeQuiz(int id);
/// <summary>
/// Valide un quiz par son identifiant.
/// </summary>
/// <param name="id">Identifiant du quiz à valider.</param>
public Task validateQuiz(int id); public Task validateQuiz(int id);
/// <summary>
/// Récupère la liste de tous les quiz.
/// </summary>
/// <returns>Liste de tous les quiz.</returns>
public Task<List<Quiz>> getQuizzes(); public Task<List<Quiz>> getQuizzes();
/// <summary>
/// Récupère la liste des quiz en attente de validation.
/// </summary>
/// <returns>Liste des quiz non validés.</returns>
public Task<List<Quiz>> getQuizzesToValidate(); public Task<List<Quiz>> getQuizzesToValidate();
/// <summary>
/// Récupère un quiz spécifique par son identifiant.
/// </summary>
/// <param name="id">Identifiant du quiz.</param>
/// <returns>Le quiz correspondant ou null s'il n'existe pas.</returns>
public Task<Quiz> getQuiz(int id); public Task<Quiz> getQuiz(int id);
/// <summary>
/// Récupère une liste paginée de quiz.
/// </summary>
/// <param name="nb">Nombre de quiz par page.</param>
/// <param name="page">Numéro de la page.</param>
/// <returns>Liste des quiz correspondant à la pagination.</returns>
public Task<List<Quiz>> getSommeQuiz(int nb, int page); public Task<List<Quiz>> getSommeQuiz(int nb, int page);
/// <summary>
/// Récupère le nombre total de quiz enregistrés.
/// </summary>
/// <returns>Nombre total de quiz.</returns>
public Task<int> getNbQuiz(); public Task<int> getNbQuiz();
} }
} }

@ -2,32 +2,94 @@
namespace WF_WebAdmin.Service namespace WF_WebAdmin.Service
{ {
/// <summary>
/// Interface définissant les opérations de gestion des citations.
/// </summary>
public interface IQuoteService public interface IQuoteService
{ {
/// <summary>
/// Ajoute une citation.
/// </summary>
/// <param name="quote">Citation à ajouter.</param>
public Task addQuote(Quote quote); public Task addQuote(Quote quote);
/// <summary>
/// Supprime une citation.
/// </summary>
/// <param name="quote">Citation à supprimer.</param>
public Task removeQuote(Quote quote); public Task removeQuote(Quote quote);
public Task validQuote(Quote? quote); /// <summary>
/// Valide une citation.
/// </summary>
/// <param name="quote">Citation à valider.</param>
public Task validQuote(Quote quote);
public Task updateQuote(Quote? quote); /// <summary>
/// Met à jour une citation existante.
/// </summary>
/// <param name="quote">Citation à mettre à jour.</param>
public Task updateQuote(Quote quote);
/// <summary>
/// Récupère toutes les citations.
/// </summary>
/// <returns>Liste de toutes les citations.</returns>
public Task<List<Quote>> getAllQuote(); public Task<List<Quote>> getAllQuote();
/// <summary>
/// Récupère une liste paginée de citations.
/// </summary>
/// <param name="nb">Nombre de citations par page.</param>
/// <param name="page">Numéro de la page.</param>
/// <returns>Liste des citations correspondant à la pagination.</returns>
public Task<List<Quote>> getSomeQuote(int nb, int page); public Task<List<Quote>> getSomeQuote(int nb, int page);
public Task<Quote?> getOnequote(int id); /// <summary>
/// Récupère une citation spécifique par son identifiant.
/// </summary>
/// <param name="id">Identifiant de la citation.</param>
/// <returns>La citation correspondante ou null si elle n'existe pas.</returns>
public Task<Quote> getOnequote(int id);
/// <summary>
/// Recherche des citations en fonction d'un critère.
/// </summary>
/// <param name="reserch">Terme de recherche.</param>
/// <param name="argument">Liste des arguments pour affiner la recherche.</param>
/// <returns>Liste des citations correspondant aux critères.</returns>
public Task<List<Quote>> reserchQuote(string reserch, List<string> argument); public Task<List<Quote>> reserchQuote(string reserch, List<string> argument);
/// <summary>
/// Récupère toutes les citations non validées.
/// </summary>
/// <returns>Liste des citations non validées.</returns>
public Task<List<Quote>> getAllQuoteInvalid(); public Task<List<Quote>> getAllQuoteInvalid();
/// <summary>
/// Récupère une liste paginée de citations non validées.
/// </summary>
/// <param name="nb">Nombre de citations par page.</param>
/// <param name="page">Numéro de la page.</param>
/// <returns>Liste des citations non validées correspondant à la pagination.</returns>
public Task<List<Quote>> getSomeQuoteInvalid(int nb, int page); public Task<List<Quote>> getSomeQuoteInvalid(int nb, int page);
/// <summary>
/// Récupère le nombre total de citations enregistrées.
/// </summary>
/// <returns>Nombre total de citations.</returns>
public Task<int> getNbQuote(); public Task<int> getNbQuote();
/// <summary>
/// Récupère la liste des personnages associés aux citations.
/// </summary>
/// <returns>Liste des personnages.</returns>
public Task<List<Character>> getChar(); public Task<List<Character>> getChar();
/// <summary>
/// Récupère la liste des sources associées aux citations.
/// </summary>
/// <returns>Liste des sources.</returns>
public Task<List<Source>> getSrc(); public Task<List<Source>> getSrc();
} }
} }

@ -2,24 +2,68 @@
namespace WF_WebAdmin.Service namespace WF_WebAdmin.Service
{ {
/// <summary>
/// Interface définissant les opérations de gestion des utilisateurs.
/// </summary>
public interface IUserService public interface IUserService
{ {
/// <summary>
/// Supprime un utilisateur du système.
/// </summary>
/// <param name="user">Utilisateur à supprimer.</param>
public Task removeUser(User user); public Task removeUser(User user);
public Task updateRole(User? user); /// <summary>
/// Met à jour le rôle d'un utilisateur.
/// </summary>
/// <param name="user">Utilisateur dont le rôle doit être mis à jour.</param>
public Task updateRole(User user);
public Task downgradeRole(User? user); /// <summary>
/// Rétrograde le rôle d'un utilisateur.
/// </summary>
/// <param name="user">Utilisateur à rétrograder.</param>
public Task downgradeRole(User user);
public Task updateUser(User? user); /// <summary>
/// Met à jour les informations d'un utilisateur.
/// </summary>
/// <param name="user">Utilisateur à mettre à jour.</param>
public Task updateUser(User user);
/// <summary>
/// Récupère la liste complète des utilisateurs.
/// </summary>
/// <returns>Liste des utilisateurs.</returns>
public Task<List<User>> getAllUser(); public Task<List<User>> getAllUser();
public Task<List<User>?> getSomeUser(int nb, int page); /// <summary>
/// Récupère une liste paginée d'utilisateurs.
/// </summary>
/// <param name="nb">Nombre d'utilisateurs par page.</param>
/// <param name="page">Numéro de la page.</param>
/// <returns>Liste des utilisateurs correspondant à la pagination.</returns>
public Task<List<User>> getSomeUser(int nb, int page);
/// <summary>
/// Récupère un utilisateur spécifique par son identifiant.
/// </summary>
/// <param name="id">Identifiant de l'utilisateur.</param>
/// <returns>L'utilisateur correspondant ou null s'il n'existe pas.</returns>
public Task<User> getOneUser(int id); public Task<User> getOneUser(int id);
/// <summary>
/// Recherche des utilisateurs en fonction d'un critère.
/// </summary>
/// <param name="reserch">Terme de recherche.</param>
/// <param name="args">Liste des arguments pour affiner la recherche.</param>
/// <returns>Liste des utilisateurs correspondant aux critères.</returns>
public Task<List<User>> reserchUsers(string reserch, List<string> args); public Task<List<User>> reserchUsers(string reserch, List<string> args);
/// <summary>
/// Récupère le nombre total d'utilisateurs enregistrés.
/// </summary>
/// <returns>Nombre total d'utilisateurs.</returns>
public Task<int> getNbUser(); public Task<int> getNbUser();
} }
} }

@ -1,67 +0,0 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Text.Json;
using WF_WebAdmin.Model;
namespace WF_WebAdmin.Service
{
public class LogsServiceStub : ILogsService
{
private readonly string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake_data_logs.json");
public async Task saveLogsJson(List<Logs> logs)
{
var json = JsonSerializer.Serialize(logs, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(_jsonFilePath, json);
}
public async Task addLogs(Logs logs)
{
var data = await getAllLogs();
data.Add(logs);
await saveLogsJson(data);
}
public async Task<List<Logs>> getAllLogs()
{
if (!File.Exists(_jsonFilePath))
{
Console.Out.WriteLine($"{_jsonFilePath} not found");
return new List<Logs>();
}
var json = await File.ReadAllTextAsync(_jsonFilePath);
return JsonSerializer.Deserialize<List<Logs>>(json) ?? new List<Logs>();
}
public Task<int> getNbLogs()
{
throw new NotImplementedException();
}
public async Task<List<Logs>> getSomeLogs(int nb, int page)
{
var logs = await getAllLogs();
if ((page - 1) * nb + nb > logs.Count)
{
return logs.GetRange(logs.Count - nb, nb);
}
return logs.GetRange((page - 1) * nb, nb);
}
public async Task removeLogs(Logs logs)
{
var data = await getAllLogs();
var l = data.FirstOrDefault(p => p.Message ==logs.Message && p.LogLevel==logs.LogLevel);
if (l != null)
{
data.Remove(l);
await saveLogsJson(data);
}
}
}
}

@ -1,202 +1,153 @@
using System.Text.Json; using System.Text.Json;
using WF_WebAdmin.Model; using WF_WebAdmin.Model;
namespace WF_WebAdmin.Service; namespace WF_WebAdmin.Service
public class QuizServiceStub: IQuizService
{ {
private readonly string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake_data_quiz.json");
/// <summary> /// <summary>
/// Asynchronously saves a list of quiz objects to a JSON file. /// Service de gestion des quiz utilisant un fichier JSON comme stockage de données.
/// </summary> /// </summary>
/// <param name="quizzes">A list of <see cref="Quiz"/> objects to be serialized and saved.</param> public class QuizServiceStub : IQuizService
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>
/// This method serializes the list of quizzes to a well-formatted JSON string and saves it
/// to a specified file path. The <paramref name="quizzes"/> list is serialized using
/// <see cref="JsonSerializer"/> with indented formatting to make the JSON human-readable.
/// </remarks>
public async Task saveQuizJson(List<Quiz> quizzes)
{ {
var json = JsonSerializer.Serialize(quizzes, new JsonSerializerOptions { WriteIndented = true }); private readonly string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake_data_quiz.json");
await File.WriteAllTextAsync(_jsonFilePath, json);
}
/// <summary>
/// <summary> /// Sauvegarde la liste des quiz dans un fichier JSON.
/// Asynchronously adds a new quiz to the list of quizzes and saves the updated list to a JSON file. /// </summary>
/// </summary> /// <param name="quizzes">Liste des quiz à sauvegarder.</param>
/// <param name="quiz">The <see cref="Quiz"/> object to be added to the list of quizzes.</param> public async Task saveQuizJson(List<Quiz> quizzes)
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>
/// This method retrieves the current list of quizzes using <see cref="getQuizzes"/>, assigns a unique ID to the
/// new quiz (based on the highest existing ID), and adds the new quiz to the list. Afterward, the updated list
/// of quizzes is saved back to the JSON file using <see cref="saveQuizJson"/>. The new quiz will have an ID
/// that's one greater than the highest existing ID or 1 if no quizzes exist.
/// </remarks>
public async Task addQuiz(Quiz quiz)
{
var data = await getQuizzes();
quiz.Id = data.Count > 0 ? data.Max(p => p.Id) + 1 : 1;
data.Add(quiz);
await saveQuizJson(data);
}
/// <summary>
/// Asynchronously updates an existing quiz in the list of quizzes and saves the updated list to a JSON file.
/// </summary>
/// <param name="quiz">The <see cref="Quiz"/> object containing the updated data.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>
/// This method retrieves the current list of quizzes using <see cref="getQuizzes"/>, searches for the quiz
/// with the same ID as the one provided, and updates its properties with the new values from the given quiz object.
/// If the quiz is found, the updated list is saved back to the JSON file using <see cref="saveQuizJson"/>.
/// If no quiz with the matching ID is found, no update is performed.
/// </remarks>
public async Task updateQuiz(Quiz quiz)
{
var data = await getQuizzes();
var existingQuiz = data.FirstOrDefault(q => q.Id == quiz.Id);
if (existingQuiz != null)
{ {
existingQuiz.Question = quiz.Question; var json = JsonSerializer.Serialize(quizzes, new JsonSerializerOptions { WriteIndented = true });
existingQuiz.AnswerA = quiz.AnswerA; await File.WriteAllTextAsync(_jsonFilePath, json);
existingQuiz.AnswerB = quiz.AnswerB;
existingQuiz.AnswerC = quiz.AnswerC;
existingQuiz.AnswerD = quiz.AnswerD;
existingQuiz.CAnswer = quiz.CAnswer;
existingQuiz.IsValid = quiz.IsValid;
existingQuiz.UserProposition = quiz.UserProposition;
await saveQuizJson(data);
} }
}
/// <summary> /// <summary>
/// Asynchronously removes a quiz from the list of quizzes by its ID and saves the updated list to a JSON file. /// Ajoute un quiz à la liste et le sauvegarde dans le fichier JSON.
/// </summary> /// </summary>
/// <param name="id">The ID of the <see cref="Quiz"/> to be removed.</param> /// <param name="quiz">Quiz à ajouter.</param>
/// <returns>A task representing the asynchronous operation.</returns> public async Task addQuiz(Quiz quiz)
/// <remarks>
/// This method retrieves the current list of quizzes using <see cref="getQuizzes"/>, searches for the quiz
/// with the specified ID, and removes it from the list if found. After removal, the updated list of quizzes is
/// saved back to the JSON file using <see cref="saveQuizJson"/>. If no quiz with the matching ID is found,
/// no changes are made.
/// </remarks>
public async Task removeQuiz(int id)
{
var data = await getQuizzes();
var quiz = data.FirstOrDefault(q => q.Id == id);
if (quiz != null)
{ {
data.Remove(quiz); var data = await getQuizzes();
quiz.Id = data.Count > 0 ? data.Max(p => p.Id) + 1 : 1;
data.Add(quiz);
await saveQuizJson(data); await saveQuizJson(data);
} }
}
public Task validateQuiz(int id) /// <summary>
{ /// Met à jour un quiz existant dans la liste et le sauvegarde.
throw new NotImplementedException(); /// </summary>
} /// <param name="quiz">Quiz mis à jour.</param>
public async Task updateQuiz(Quiz quiz)
{
var data = await getQuizzes();
var existingQuiz = data.FirstOrDefault(q => q.Id == quiz.Id);
if (existingQuiz != null)
{
existingQuiz.Question = quiz.Question;
existingQuiz.AnswerA = quiz.AnswerA;
existingQuiz.AnswerB = quiz.AnswerB;
existingQuiz.AnswerC = quiz.AnswerC;
existingQuiz.AnswerD = quiz.AnswerD;
existingQuiz.CAnswer = quiz.CAnswer;
existingQuiz.IsValid = quiz.IsValid;
existingQuiz.UserProposition = quiz.UserProposition;
await saveQuizJson(data);
}
}
/// <summary> /// <summary>
/// Asynchronously retrieves the list of quizzes from a JSON file. /// Supprime un quiz de la liste et met à jour le fichier JSON.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with a <see cref="List{Quiz}"/> result containing the quizzes.</returns> /// <param name="id">Identifiant du quiz à supprimer.</param>
/// <remarks> public async Task removeQuiz(int id)
/// This method checks if the JSON file exists at the specified file path. If the file does not exist, it logs a
/// message to the console and returns an empty list of quizzes. If the file exists, it reads the JSON content,
/// deserializes it into a list of <see cref="Quiz"/> objects, and returns the list. If the deserialization is
/// unsuccessful or the file is empty, it returns an empty list instead.
/// </remarks>
public async Task<List<Quiz>> getQuizzes()
{
if (!File.Exists(_jsonFilePath))
{ {
Console.Out.WriteLine($"{_jsonFilePath} not found"); var data = await getQuizzes();
return new List<Quiz>(); var quiz = data.FirstOrDefault(q => q.Id == id);
if (quiz != null)
{
data.Remove(quiz);
await saveQuizJson(data);
}
} }
var json = await File.ReadAllTextAsync(_jsonFilePath); /// <summary>
return JsonSerializer.Deserialize<List<Quiz>>(json) ?? new List<Quiz>(); /// Valide un quiz (non implémenté).
} /// </summary>
/// <param name="id">Identifiant du quiz à valider.</param>
public Task validateQuiz(int id)
{
throw new NotImplementedException();
}
/// <summary> /// <summary>
/// Asynchronously retrieves the list of quizzes that are marked as invalid and need validation. /// Récupère la liste complète des quiz à partir du fichier JSON.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with a <see cref="List{Quiz}"/> result containing quizzes that are not valid.</returns> /// <returns>Liste des quiz.</returns>
/// <remarks> public async Task<List<Quiz>> getQuizzes()
/// This method retrieves the full list of quizzes using <see cref="getQuizzes"/> and filters it to return only those {
/// quizzes where the <see cref="Quiz.IsValid"/> property is set to <c>false</c>. The filtered list is then returned. if (!File.Exists(_jsonFilePath))
/// If no quizzes are invalid, an empty list will be returned. {
/// </remarks> Console.Out.WriteLine($"{_jsonFilePath} not found");
public async Task<List<Quiz>> getQuizzesToValidate() return new List<Quiz>();
{ }
var quizzes = await getQuizzes();
return quizzes.Where(quiz => !quiz.IsValid).ToList();
}
var json = await File.ReadAllTextAsync(_jsonFilePath);
return JsonSerializer.Deserialize<List<Quiz>>(json) ?? new List<Quiz>();
}
/// <summary> /// <summary>
/// Asynchronously retrieves a specific quiz by its ID from the list of quizzes. /// Récupère la liste des quiz non validés.
/// </summary> /// </summary>
/// <param name="id">The ID of the <see cref="Quiz"/> to retrieve.</param> /// <returns>Liste des quiz en attente de validation.</returns>
/// <returns>A task representing the asynchronous operation, with a <see cref="Quiz"/> result containing the matching quiz, or <c>null</c> if not found.</returns> public async Task<List<Quiz>> getQuizzesToValidate()
/// <remarks>
/// This method retrieves the full list of quizzes using <see cref="getQuizzes"/> and searches for a quiz with
/// the specified ID. If a quiz with the matching ID is found, it is returned; otherwise, the method returns <c>null</c>.
/// </remarks>
public async Task<Quiz> getQuiz(int id)
{
var data = await getQuizzes();
var q = data.FirstOrDefault(p => p.Id == id);
if (q == null)
{ {
throw new KeyNotFoundException($"Quiz with ID {id} not found."); var quizzes = await getQuizzes();
return quizzes.Where(quiz => quiz.IsValid == false).ToList();
} }
return q;
}
/// <summary> /// <summary>
/// Asynchronously retrieves a paginated list of quizzes, returning a specific number of quizzes for the given page. /// Récupère un quiz spécifique par son identifiant.
/// </summary> /// </summary>
/// <param name="nb">The number of quizzes to retrieve per page.</param> /// <param name="id">Identifiant du quiz.</param>
/// <param name="page">The page number to retrieve, where the first page is 1.</param> /// <returns>Le quiz correspondant ou null s'il n'existe pas.</returns>
/// <returns>A task representing the asynchronous operation, with a <see cref="List{Quiz}"/> result containing the quizzes for the specified page.</returns> public async Task<Quiz> getQuiz(int id)
/// <remarks>
/// This method retrieves the full list of quizzes using <see cref="getQuizzes"/> and returns a subset of quizzes based
/// on the specified page number and the number of quizzes per page. If the requested page exceeds the available quizzes,
/// the method returns the last page with the remaining quizzes. If the number of quizzes requested per page exceeds the
/// total number of quizzes, the method will return all quizzes available.
/// </remarks>
public async Task<List<Quiz>> getSommeQuiz(int nb, int page)
{
var data = await getQuizzes();
if ((page - 1) * nb + nb > data.Count)
{ {
if (nb > data.Count) var data = await getQuizzes();
var q = data.FirstOrDefault(p => p.Id == id);
if (q != null)
{ {
return data.GetRange(0, data.Count - 1); return q;
} }
return data.GetRange(data.Count - nb, nb); return null;
} }
return data.GetRange((page - 1) * nb, nb);
}
/// <summary>
/// Récupère une liste paginée de quiz.
/// </summary>
/// <param name="nb">Nombre de quiz par page.</param>
/// <param name="page">Numéro de la page.</param>
/// <returns>Liste des quiz correspondant à la pagination.</returns>
public async Task<List<Quiz>> getSommeQuiz(int nb, int page)
{
var data = await getQuizzes();
if ((page - 1) * nb + nb > data.Count())
{
if (nb > data.Count())
{
return data.GetRange(0, data.Count() - 1);
}
return data.GetRange(data.Count() - nb, nb);
}
return data.GetRange((page - 1) * nb, nb);
}
/// <summary> /// <summary>
/// Asynchronously retrieves the total number of quizzes in the list. /// Récupère le nombre total de quiz enregistrés.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with an <see cref="int"/> result containing the total number of quizzes.</returns> /// <returns>Nombre total de quiz.</returns>
/// <remarks> public async Task<int> getNbQuiz()
/// This method retrieves the full list of quizzes using <see cref="getQuizzes"/> and returns the count of quizzes in the list. {
/// It simply returns the number of quizzes available in the data source. var data = await getQuizzes();
/// </remarks> return data.Count;
public async Task<int> getNbQuiz() }
{
var data = await getQuizzes();
return data.Count;
} }
} }

@ -1,198 +1,184 @@
using WF_WebAdmin.Converter; using WF_WebAdmin.Converter;
using WF_WebAdmin.Model; using WF_WebAdmin.Model;
using Npgsql; using Npgsql;
namespace WF_WebAdmin.Service namespace WF_WebAdmin.Service
{ {
public class QuoteServiceLocal: IQuoteService /// <summary>
{ /// Service de gestion des citations utilisant une base de données PostgreSQL.
private readonly string? _connectionString = "Host=localhost;Port=5432;Username=;Password=;Database="; /// </summary>
public class QuoteServiceLocal : IQuoteService
{
private readonly string? _connectionString = "Host=localhost;Port=5432;Username=loguichard3;Password=Reglisse15.;Database=dbloguichard3";
/// <summary>
/// Asynchronously adds a new quote to the database and returns the corresponding <see cref="QuoteDto"/>. /// <summary>
/// </summary> /// Ajoute une nouvelle citation à la base de données PostgreSQL.
/// <param name="quote">The <see cref="Quote"/> object to be added to the database.</param> /// </summary>
/// <returns>A task representing the asynchronous operation, with a <see cref="QuoteDto"/> result containing the added quote's data.</returns> /// <param name="quote">Citation à ajouter.</param>
/// <remarks> /// <returns>Un objet <see cref="QuoteDTO"/> représentant la citation ajoutée.</returns>
/// This method converts the provided <see cref="Quote"/> object into a <see cref="QuoteDto"/> using <see cref="QuoteExtension"/>. public async Task<QuoteDTO> AddQuoteAsync(Quote quote)
/// It then inserts the quote into the PostgreSQL database using a parameterized SQL query with the help of Npgsql. {
/// After successfully inserting the quote, the corresponding <see cref="QuoteDto"/> is returned to the caller. QuoteExtension extension = new QuoteExtension();
/// Error handling is in place to catch any issues during the database insertion process, with the exception message logged in case of failure. QuoteDTO quoteDTO = extension.QuoteToDTO(quote);
/// </remarks>
public async Task<QuoteDto> AddQuoteAsync(Quote? quote) using (var connection = new NpgsqlConnection(_connectionString))
{ {
QuoteExtension extension = new QuoteExtension(); var commandText = "INSERT INTO Quote (content, langue, reason, id_source, id_caracter, id_user_verif, img_path) " +
QuoteDto quoteDTO = extension.QuoteToDTO(quote); "VALUES (@content, @langue, @reason, @source, @character, @user, @img_path)";
// Utilisation de NpgsqlConnection pour PostgreSQL var command = new NpgsqlCommand(commandText, connection);
using (var connection = new NpgsqlConnection(_connectionString))
{ /*
// Définir la requête SQL d'insertion // Ajouter des paramètres à la commande
var commandText = "INSERT INTO Quote (content, langue, reason, id_source, id_caracter, id_user_verif, img_path) " + command.Parameters.AddWithValue("@content", quote.Content);
"VALUES (@content, @langue, @reason, @source, @character, @user, @img_path)"; command.Parameters.AddWithValue("@langue", quote.Langue);
command.Parameters.AddWithValue("@reason", "À vérifier"); // Vous pouvez changer ça si nécessaire
// Créer une commande Npgsql command.Parameters.AddWithValue("@source", quote.Source);
var command = new NpgsqlCommand(commandText, connection); command.Parameters.AddWithValue("@character", quote.Character);
command.Parameters.AddWithValue("@user", quote.User); // Assurez-vous que `quote.User` est correctement défini
command.Parameters.AddWithValue("@img_path", quote.ImgPath);
/* */
Ajouter des paramètres à la commande
command.Parameters.AddWithValue("@content", quote.Content); try
command.Parameters.AddWithValue("@langue", quote.Langue); {
command.Parameters.AddWithValue("@reason", "À vérifier"); // Vous pouvez changer ça si nécessaire await connection.OpenAsync();
command.Parameters.AddWithValue("@source", quote.Source); await command.ExecuteNonQueryAsync();
command.Parameters.AddWithValue("@character", quote.Character); }
command.Parameters.AddWithValue("@user", quote.User); // Assurez-vous que `quote.User` est correctement défini catch (Exception ex)
command.Parameters.AddWithValue("@img_path", quote.ImgPath); {
*/ Console.WriteLine($"Une erreur est survenue lors de l'ajout de la citation : {ex.Message}");
}
finally
try {
{ await connection.CloseAsync();
// Ouvrir la connexion à la base de données }
await connection.OpenAsync(); }
// Exécuter la commande d'insertion return quoteDTO;
await command.ExecuteNonQueryAsync(); }
}
catch (Exception ex) /// <summary>
{ /// Supprime une citation de la base de données.
// Gérer les erreurs ici (par exemple, afficher ou enregistrer les erreurs) /// </summary>
Console.WriteLine($"Une erreur est survenue lors de l'ajout de la citation : {ex.Message}"); /// <param name="quote">Citation à supprimer.</param>
} public Task RemoveQuote(Quote quote)
finally {
{ QuoteExtension extension = new QuoteExtension();
// Fermer la connexion (automatiquement géré avec `using`, mais ajouté pour explicitement montrer le processus) QuoteDTO quoteDTO = extension.QuoteToDTO(quote);
await connection.CloseAsync();
} return Task.FromResult(quoteDTO);
} }
// Retourner l'objet DTO pour que vous puissiez l'utiliser ailleurs dans votre application /// <summary>
return quoteDTO; /// Valide une citation.
} /// </summary>
/// <param name="quote">Citation à valider.</param>
public Task validQuote(Quote quote)
/// <summary> {
/// Asynchronously handles the removal of a quote and returns the corresponding <see cref="QuoteDto"/>. QuoteExtension extension = new QuoteExtension();
/// </summary> QuoteDTO quoteDTO = extension.QuoteToDTO(quote);
/// <param name="quote">The <see cref="Quote"/> object to be removed.</param>
/// <returns>A task representing the asynchronous operation, with a <see cref="QuoteDto"/> result corresponding to the removed quote.</returns> return Task.FromResult(quoteDTO);
/// <remarks> }
/// This method takes a <see cref="Quote"/> object, converts it into a <see cref="QuoteDto"/> using the
/// <see cref="QuoteExtension"/>, and then returns the DTO. Note that while this function is named `RemoveQuote`, /// <summary>
/// it currently only converts the quote to a DTO and does not actually perform any database removal operation. /// Met à jour une citation existante.
/// You may need to implement additional logic to remove the quote from the database. /// </summary>
/// </remarks> /// <param name="quote">Citation mise à jour.</param>
public Task RemoveQuote(Quote? quote) public Task updateQuote(Quote quote)
{ {
QuoteExtension extension = new QuoteExtension(); QuoteExtension extension = new QuoteExtension();
QuoteDto quoteDTO = extension.QuoteToDTO(quote); QuoteDTO quoteDTO = extension.QuoteToDTO(quote);
// Return the DTO as the result of this asynchronous operation (though no removal logic is currently implemented) return Task.FromResult(quoteDTO);
return Task.FromResult(quoteDTO); }
}
/// <summary>
/// Ajoute une citation (non implémenté).
/// <summary> /// </summary>
/// Asynchronously validates a quote and returns the corresponding <see cref="QuoteDto"/>. public Task addQuote(Quote quote)
/// </summary> {
/// <param name="quote">The <see cref="Quote"/> object to be validated.</param> throw new NotImplementedException();
/// <returns>A task representing the asynchronous operation, with a <see cref="QuoteDto"/> result corresponding to the validated quote.</returns> }
/// <remarks>
/// This method takes a <see cref="Quote"/> object, converts it into a <see cref="QuoteDto"/> using the /// <summary>
/// <see cref="QuoteExtension"/>, and returns the DTO. The method is named `validQuote`, but currently, it only /// Supprime une citation (non implémenté).
/// converts the quote into a DTO and does not perform any actual validation logic. /// </summary>
/// If you intend to validate the quote (e.g., updating its status in a database), you will need to implement public Task removeQuote(Quote quote)
/// the actual validation logic separately. {
/// </remarks> throw new NotImplementedException();
public Task validQuote(Quote? quote) }
{
QuoteExtension extension = new QuoteExtension(); /// <summary>
QuoteDto quoteDto = extension.QuoteToDTO(quote); /// Récupère toutes les citations (non implémenté).
/// </summary>
// Return the DTO as the result of this asynchronous operation (though no validation logic is currently implemented) public Task<List<Quote>> getAllQuote()
return Task.FromResult(quoteDto); {
} throw new NotImplementedException();
}
/// <summary> /// <summary>
/// Asynchronously updates a quote and returns the corresponding <see cref="QuoteDto"/>. /// Récupère un ensemble de citations paginées (non implémenté).
/// </summary> /// </summary>
/// <param name="quote">The <see cref="Quote"/> object to be updated.</param> public Task<List<Quote>> getSomeQuote(int nb, int page)
/// <returns>A task representing the asynchronous operation, with a <see cref="QuoteDto"/> result corresponding to the updated quote.</returns> {
/// <remarks> throw new NotImplementedException();
/// This method takes a <see cref="Quote"/> object, converts it into a <see cref="QuoteDto"/> using the }
/// <see cref="QuoteExtension"/>, and returns the DTO. The method is named `updateQuote`, but currently, it only
/// converts the quote into a DTO and does not perform any actual update logic. /// <summary>
/// If you intend to update the quote (e.g., modifying the quote in a database or data source), /// Recherche des citations selon des critères spécifiques (non implémenté).
/// you will need to implement the actual update logic separately. /// </summary>
/// </remarks> public Task<List<Quote>> reserchQuote(string reserch, List<string> argument)
public Task updateQuote(Quote? quote) {
{ throw new NotImplementedException();
QuoteExtension extension = new QuoteExtension(); }
QuoteDto quoteDTO = extension.QuoteToDTO(quote);
/// <summary>
// Return the DTO as the result of this asynchronous operation (though no update logic is currently implemented) /// Récupère toutes les citations invalides (non implémenté).
return Task.FromResult(quoteDTO); /// </summary>
} public Task<List<Quote>> getAllQuoteInvalid()
{
throw new NotImplementedException();
}
public Task addQuote(Quote quote)
{ /// <summary>
throw new NotImplementedException(); /// Récupère une liste paginée de citations invalides (non implémenté).
} /// </summary>
public Task<List<Quote>> getSomeQuoteInvalid(int nb, int page)
public Task removeQuote(Quote quote) {
{ throw new NotImplementedException();
throw new NotImplementedException(); }
}
/// <summary>
public Task<List<Quote>> getAllQuote() /// Récupère une citation spécifique en fonction de son ID (non implémenté).
{ /// </summary>
throw new NotImplementedException(); public Task<Quote> getOnequote(int id)
} {
throw new NotImplementedException();
public Task<List<Quote>> getSomeQuote(int nb, int page) }
{
throw new NotImplementedException(); /// <summary>
} /// Récupère le nombre total de citations enregistrées (non implémenté).
/// </summary>
public Task<List<Quote>> reserchQuote(string reserch, List<string> argument) public Task<int> getNbQuote()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public Task<List<Quote>> getAllQuoteInvalid() /// <summary>
{ /// Récupère la liste des personnages (non implémenté).
throw new NotImplementedException(); /// </summary>
} public Task<List<Character>> getChar()
{
public Task<List<Quote>> getSomeQuoteInvalid(int nb, int page) throw new NotImplementedException();
{ }
throw new NotImplementedException();
} /// <summary>
/// Récupère la liste des sources (non implémenté).
public Task<Quote?> getOnequote(int id) /// </summary>
{ public Task<List<Source>> getSrc()
throw new NotImplementedException(); {
} throw new NotImplementedException();
}
public Task<int> getNbQuote() }
{ }
throw new NotImplementedException();
}
public Task<List<Character>> getChar()
{
throw new NotImplementedException();
}
public Task<List<Source>> getSrc()
{
throw new NotImplementedException();
}
}
}

@ -3,42 +3,29 @@ using WF_WebAdmin.Model;
namespace WF_WebAdmin.Service; namespace WF_WebAdmin.Service;
public class QuoteServiceStub : IQuoteService /// <summary>
{ /// Service de gestion des citations utilisant un fichier JSON comme stockage.
/// </summary>
public class QuoteServiceStub : IQuoteService
{
private readonly string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataQuote.json"); private readonly string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataQuote.json");
private readonly string _char = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataCaracter.json"); private readonly string _char = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataCaracter.json");
private readonly string _src = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataSource.json"); private readonly string _src = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataSource.json");
/// <summary> /// <summary>
/// Asynchronously saves a list of quotes to a JSON file. /// Sauvegarde la liste des citations dans le fichier JSON.
/// </summary> /// </summary>
/// <param name="quotes">The list of <see cref="Quote"/> objects to be serialized and saved to the file.</param> /// <param name="quotes">Liste des citations à sauvegarder.</param>
/// <returns>A task representing the asynchronous operation of saving the quotes to the file.</returns>
/// <remarks>
/// This method serializes the provided list of <see cref="Quote"/> objects into JSON format using <see cref="JsonSerializer"/>.
/// The serialized JSON is then saved to the file path specified in the <see cref="_jsonFilePath"/> field. The JSON is written
/// with indentation for better readability.
/// If the file does not already exist, it will be created.
/// </remarks>
public async Task saveQuoteJson(List<Quote> quotes) public async Task saveQuoteJson(List<Quote> quotes)
{ {
var json = JsonSerializer.Serialize(quotes, new JsonSerializerOptions { WriteIndented = true }); var json = JsonSerializer.Serialize(quotes, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(_jsonFilePath, json); await File.WriteAllTextAsync(_jsonFilePath, json);
} }
/// <summary> /// <summary>
/// Asynchronously adds a new quote to the list and saves it to a JSON file. /// Ajoute une nouvelle citation et l'enregistre dans le fichier JSON.
/// </summary> /// </summary>
/// <param name="quote">The <see cref="Quote"/> object to be added to the list.</param> /// <param name="quote">Citation à ajouter.</param>
/// <returns>A task representing the asynchronous operation of adding the quote and saving the updated list to the file.</returns>
/// <remarks>
/// This method retrieves the current list of quotes using the <see cref="getAllQuote"/> method, assigns a new ID to the
/// provided quote (incremented from the maximum existing ID), and adds the quote to the list. After updating the list,
/// the method saves the updated list back to the JSON file using <see cref="saveQuoteJson"/>.
/// If the list is empty, the new quote is assigned an ID of 1.
/// </remarks>
public async Task addQuote(Quote quote) public async Task addQuote(Quote quote)
{ {
var data = await getAllQuote(); var data = await getAllQuote();
@ -47,18 +34,10 @@ namespace WF_WebAdmin.Service;
await saveQuoteJson(data); await saveQuoteJson(data);
} }
/// <summary> /// <summary>
/// Asynchronously removes a quote from the list and saves the updated list to a JSON file. /// Supprime une citation et met à jour le fichier JSON.
/// </summary> /// </summary>
/// <param name="quote">The <see cref="Quote"/> object to be removed from the list.</param> /// <param name="quote">Citation à supprimer.</param>
/// <returns>A task representing the asynchronous operation of removing the quote and saving the updated list to the file.</returns>
/// <remarks>
/// This method retrieves the current list of quotes using the <see cref="getAllQuote"/> method.
/// It searches for the provided quote by its `Id` and, if found, removes it from the list.
/// After removing the quote, the method saves the updated list back to the JSON file using <see cref="saveQuoteJson"/>.
/// If the quote is not found in the list, no action is taken.
/// </remarks>
public async Task removeQuote(Quote quote) public async Task removeQuote(Quote quote)
{ {
var data = await getAllQuote(); var data = await getAllQuote();
@ -70,25 +49,20 @@ namespace WF_WebAdmin.Service;
} }
} }
/// <summary>
public Task validQuote(Quote? quote) /// Marque une citation comme valide.
/// </summary>
/// <param name="quote">Citation à valider.</param>
public async Task validQuote(Quote quote)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary> /// <summary>
/// Asynchronously updates the details of an existing quote and saves the updated list to a JSON file. /// Met à jour une citation existante.
/// </summary> /// </summary>
/// <param name="quote">The <see cref="Quote"/> object containing the updated details of the quote.</param> /// <param name="quote">Citation mise à jour.</param>
/// <returns>A task representing the asynchronous operation of updating the quote and saving the updated list to the file.</returns> public async Task updateQuote(Quote quote)
/// <remarks>
/// This method retrieves the current list of quotes using the <see cref="getAllQuote"/> method.
/// It searches for the quote with the provided `Id` and, if found, updates its properties (e.g., content, character, image path, etc.)
/// with the values from the provided `quote` object. After updating the quote, the method saves the updated list back to the JSON file
/// using <see cref="saveQuoteJson"/>. If the quote with the specified `Id` is not found, no action is taken.
/// </remarks>
public async Task updateQuote(Quote? quote)
{ {
var data = await getAllQuote(); var data = await getAllQuote();
var q = data.FirstOrDefault(p => p.Id == quote.Id); var q = data.FirstOrDefault(p => p.Id == quote.Id);
@ -104,17 +78,10 @@ namespace WF_WebAdmin.Service;
} }
} }
/// <summary> /// <summary>
/// Asynchronously retrieves all quotes from a JSON file. /// Récupère toutes les citations stockées dans le fichier JSON.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="Quote"/> objects.</returns> /// <returns>Une liste de citations.</returns>
/// <remarks>
/// This method checks if the JSON file exists at the specified file path (<see cref="_jsonFilePath"/>).
/// If the file does not exist, it logs a message and returns an empty list of quotes.
/// If the file exists, it reads the JSON content, deserializes it into a list of <see cref="Quote"/> objects,
/// and returns that list. If the deserialization results in a null value, an empty list is returned.
/// </remarks>
public async Task<List<Quote>> getAllQuote() public async Task<List<Quote>> getAllQuote()
{ {
if (!File.Exists(_jsonFilePath)) if (!File.Exists(_jsonFilePath))
@ -127,21 +94,12 @@ namespace WF_WebAdmin.Service;
return JsonSerializer.Deserialize<List<Quote>>(json) ?? new List<Quote>(); return JsonSerializer.Deserialize<List<Quote>>(json) ?? new List<Quote>();
} }
/// <summary> /// <summary>
/// Asynchronously retrieves a subset of quotes based on the specified page number and the number of quotes per page. /// Récupère une liste paginée de citations.
/// </summary> /// </summary>
/// <param name="nb">The number of quotes to retrieve per page.</param> /// <param name="nb">Nombre de citations par page.</param>
/// <param name="page">The page number for pagination.</param> /// <param name="page">Numéro de la page.</param>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="Quote"/> objects for the specified page.</returns> /// <returns>Une liste de citations correspondant à la page demandée.</returns>
/// <remarks>
/// This method retrieves all quotes using the <see cref="getAllQuote"/> method and then calculates the range of quotes
/// to be returned based on the provided `nb` (number of quotes per page) and `page` (the page number). It ensures that
/// the returned subset does not exceed the total number of quotes available.
///
/// If the calculated range is larger than the available quotes, it returns a subset of quotes from the end of the list.
/// If the requested page number exceeds the total number of pages, the method will return the last available page of quotes.
/// </remarks>
public async Task<List<Quote>> getSomeQuote(int nb, int page) public async Task<List<Quote>> getSomeQuote(int nb, int page)
{ {
var quotes = await getAllQuote(); var quotes = await getAllQuote();
@ -156,105 +114,74 @@ namespace WF_WebAdmin.Service;
return quotes.GetRange((page - 1) * nb, nb); return quotes.GetRange((page - 1) * nb, nb);
} }
/// <summary> /// <summary>
/// Asynchronously retrieves a single quote based on its ID. /// Récupère une citation spécifique en fonction de son ID.
/// </summary> /// </summary>
/// <param name="id">The unique identifier of the <see cref="Quote"/> to be retrieved.</param> /// <param name="id">ID de la citation recherchée.</param>
/// <returns>A task representing the asynchronous operation, with a result of the <see cref="Quote"/> object if found, otherwise null.</returns> /// <returns>La citation correspondante ou null si elle n'existe pas.</returns>
/// <remarks> public async Task<Quote> getOnequote(int id)
/// This method retrieves all quotes using the <see cref="getAllQuote"/> method and searches for the quote that matches the provided `id`.
/// If a matching quote is found, it returns that quote; otherwise, it returns `null`.
/// </remarks>
public async Task<Quote?> getOnequote(int id)
{ {
var data = await getAllQuote(); var data = await getAllQuote();
var q = data.FirstOrDefault(p => p.Id == id); var q = data.FirstOrDefault(p => p.Id == id);
if (q == null)
{
throw new KeyNotFoundException($"Quote with ID {id} not found.");
}
return q; return q;
} }
/// <summary>
public Task<List<Quote>> reserchQuote(string reserch, List<string> argument) /// Recherche des citations selon des critères spécifiques.
{ /// </summary>
throw new NotImplementedException(); /// <param name="reserch">Terme de recherche.</param>
} /// <param name="argument">Liste d'arguments pour affiner la recherche.</param>
/// <returns>Une liste de citations correspondant aux critères de recherche.</returns>
public async Task<List<Quote>> reserchQuote(string reserch, List<string> argument)
{
throw new NotImplementedException();
}
/// <summary> /// <summary>
/// Asynchronously retrieves all invalid quotes from the list. /// Récupère toutes les citations invalides.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of invalid <see cref="Quote"/> objects.</returns> /// <returns>Une liste de citations non validées.</returns>
/// <remarks>
/// This method retrieves all quotes using the <see cref="getAllQuote"/> method and filters them by the `IsValid` property.
/// It returns only those quotes where `IsValid` is set to `false`.
/// If no invalid quotes are found, an empty list is returned.
/// </remarks>
public async Task<List<Quote>> getAllQuoteInvalid() public async Task<List<Quote>> getAllQuoteInvalid()
{ {
var quotes = await getAllQuote(); var quotes = await getAllQuote();
quotes = quotes.Where(q => !q.IsValid).ToList(); quotes = quotes.Where(q => q.IsValid == false).ToList();
return quotes; return quotes;
} }
/// <summary> /// <summary>
/// Asynchronously retrieves a subset of invalid quotes based on the specified page number and the number of quotes per page. /// Récupère une liste paginée de citations invalides.
/// </summary> /// </summary>
/// <param name="nb">The number of invalid quotes to retrieve per page.</param> /// <param name="nb">Nombre de citations par page.</param>
/// <param name="page">The page number for pagination.</param> /// <param name="page">Numéro de la page.</param>
/// <returns>A task representing the asynchronous operation, with a result of a list of invalid <see cref="Quote"/> objects for the specified page.</returns> /// <returns>Une liste de citations non validées correspondant à la page demandée.</returns>
/// <remarks>
/// This method retrieves all invalid quotes using the <see cref="getAllQuoteInvalid"/> method and then calculates the range of invalid quotes
/// to be returned based on the provided `nb` (number of quotes per page) and `page` (the page number). It ensures that
/// the returned subset does not exceed the total number of invalid quotes available.
///
/// If the calculated range is larger than the available invalid quotes, it returns a subset of quotes from the end of the list.
/// If the requested page number exceeds the total number of pages, the method will return the last available page of invalid quotes.
/// </remarks>
public async Task<List<Quote>> getSomeQuoteInvalid(int nb, int page) public async Task<List<Quote>> getSomeQuoteInvalid(int nb, int page)
{ {
var quotes = await getAllQuoteInvalid(); var quotes = await getAllQuoteInvalid();
if ((page - 1) * nb + nb > quotes.Count) if ((page - 1) * nb + nb > quotes.Count())
{ {
if (nb > quotes.Count) if (nb > quotes.Count())
{ {
return quotes.GetRange(0, quotes.Count); return quotes.GetRange(0, quotes.Count());
} }
return quotes.GetRange(quotes.Count - nb, nb); return quotes.GetRange(quotes.Count() - nb, nb);
} }
return quotes.GetRange((page - 1) * nb, nb); return quotes.GetRange((page - 1) * nb, nb);
} }
/// <summary> /// <summary>
/// Asynchronously retrieves the total number of quotes. /// Récupère le nombre total de citations enregistrées.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with a result of the total number of <see cref="Quote"/> objects.</returns> /// <returns>Nombre total de citations.</returns>
/// <remarks>
/// This method retrieves all quotes using the <see cref="getAllQuote"/> method and returns the count of quotes.
/// It provides the total number of quotes currently available in the data source.
/// </remarks>
public async Task<int> getNbQuote() public async Task<int> getNbQuote()
{ {
var data = await getAllQuote(); var data = await getAllQuote();
return data.Count; return data.Count;
} }
/// <summary> /// <summary>
/// Asynchronously retrieves a list of characters from a JSON file. /// Récupère la liste des personnages depuis le fichier JSON.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="Character"/> objects.</returns> /// <returns>Une liste de personnages.</returns>
/// <remarks>
/// This method checks if the JSON file containing character data exists at the specified file path (`_char`).
/// If the file does not exist, it logs a message to the console and returns an empty list of characters.
/// If the file exists, it reads the JSON content, deserializes it into a list of <see cref="Character"/> objects,
/// and returns that list. If the deserialization results in a null value, an empty list is returned.
/// </remarks>
public async Task<List<Character>> getChar() public async Task<List<Character>> getChar()
{ {
if (!File.Exists(_char)) if (!File.Exists(_char))
@ -267,17 +194,10 @@ namespace WF_WebAdmin.Service;
return JsonSerializer.Deserialize<List<Character>>(json) ?? new List<Character>(); return JsonSerializer.Deserialize<List<Character>>(json) ?? new List<Character>();
} }
/// <summary> /// <summary>
/// Asynchronously retrieves a list of sources from a JSON file. /// Récupère la liste des sources depuis le fichier JSON.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="Source"/> objects.</returns> /// <returns>Une liste de sources.</returns>
/// <remarks>
/// This method checks if the JSON file containing source data exists at the specified file path (`_src`).
/// If the file does not exist, it logs a message to the console and returns an empty list of sources.
/// If the file exists, it reads the JSON content, deserializes it into a list of <see cref="Source"/> objects,
/// and returns that list. If the deserialization results in a null value, an empty list is returned.
/// </remarks>
public async Task<List<Source>> getSrc() public async Task<List<Source>> getSrc()
{ {
if (!File.Exists(_src)) if (!File.Exists(_src))
@ -289,5 +209,4 @@ namespace WF_WebAdmin.Service;
var json = await File.ReadAllTextAsync(_src); var json = await File.ReadAllTextAsync(_src);
return JsonSerializer.Deserialize<List<Source>>(json) ?? new List<Source>(); return JsonSerializer.Deserialize<List<Source>>(json) ?? new List<Source>();
} }
}
}

@ -3,37 +3,27 @@ using WF_WebAdmin.Model;
namespace WF_WebAdmin.Service; namespace WF_WebAdmin.Service;
/// <summary>
/// Service de gestion des utilisateurs utilisant un fichier JSON comme stockage.
/// </summary>
public class UserServiceStub : IUserService public class UserServiceStub : IUserService
{ {
private readonly string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake_data_users.json"); private readonly string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake_data_users.json");
/// <summary> /// <summary>
/// Asynchronously saves a list of users to a JSON file. /// Sauvegarde la liste des utilisateurs dans le fichier JSON.
/// </summary> /// </summary>
/// <param name="users">The list of <see cref="User"/> objects to be saved to the file.</param> /// <param name="users">Liste des utilisateurs à sauvegarder.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>
/// This method serializes the provided list of <see cref="User"/> objects into a JSON format using the `JsonSerializer`.
/// It then writes the serialized JSON string to the file specified by the `_jsonFilePath`. The JSON is written with indentation for readability.
/// </remarks>
public async Task saveUsersJson(List<User> users) public async Task saveUsersJson(List<User> users)
{ {
var json = JsonSerializer.Serialize(users, new JsonSerializerOptions { WriteIndented = true }); var json = JsonSerializer.Serialize(users, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(_jsonFilePath, json); await File.WriteAllTextAsync(_jsonFilePath, json);
} }
/// <summary> /// <summary>
/// Asynchronously removes a user from the list of users and saves the updated list to a JSON file. /// Supprime un utilisateur de la liste et met à jour le fichier JSON.
/// </summary> /// </summary>
/// <param name="user">The <see cref="User"/> object to be removed from the list.</param> /// <param name="user">Utilisateur à supprimer.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>
/// This method retrieves the list of all users using the <see cref="getAllUser"/> method,
/// then searches for the specified user by their `Id`. If a matching user is found,
/// they are removed from the list, and the updated list is saved back to the JSON file using the <see cref="saveUsersJson"/> method.
/// </remarks>
public async Task removeUser(User user) public async Task removeUser(User user)
{ {
var data = await getAllUser(); var data = await getAllUser();
@ -45,49 +35,30 @@ public class UserServiceStub : IUserService
} }
} }
/// <summary> /// <summary>
/// Asynchronously updates the role of a user, setting the user as an administrator. /// Met à jour le rôle d'un utilisateur pour le passer en administrateur.
/// </summary> /// </summary>
/// <param name="user">The <see cref="User"/> object whose role is to be updated.</param> /// <param name="user">Utilisateur à promouvoir.</param>
/// <returns>A task representing the asynchronous operation of updating the user's role.</returns> public Task updateRole(User user)
/// <remarks>
/// This method updates the `IsAdmin` property of the specified user to `true`, indicating that the user is an administrator.
/// It then calls the <see cref="updateUser"/> method to persist the updated user information.
/// </remarks>
public Task updateRole(User? user)
{ {
user.IsAdmin = true; user.IsAdmin = true;
return updateUser(user); return updateUser(user);
} }
/// <summary> /// <summary>
/// Asynchronously downgrades the role of a user, removing their administrator privileges. /// Rétrograde un administrateur en utilisateur normal.
/// </summary> /// </summary>
/// <param name="user">The <see cref="User"/> object whose role is to be downgraded.</param> /// <param name="user">Utilisateur à rétrograder.</param>
/// <returns>A task representing the asynchronous operation of downgrading the user's role.</returns> public Task downgradeRole(User user)
/// <remarks>
/// This method updates the `IsAdmin` property of the specified user to `false`, removing their administrator status.
/// It then calls the <see cref="updateUser"/> method to persist the updated user information.
/// </remarks>
public Task downgradeRole(User? user)
{ {
user.IsAdmin = false; user.IsAdmin = false;
return updateUser(user); return updateUser(user);
} }
/// <summary> /// <summary>
/// Asynchronously retrieves a list of all users from a JSON file. /// Récupère tous les utilisateurs stockés dans le fichier JSON.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="User"/> objects.</returns> /// <returns>Une liste d'utilisateurs.</returns>
/// <remarks>
/// This method checks if the JSON file containing user data exists at the specified file path (`_jsonFilePath`).
/// If the file does not exist, it logs a message to the console and returns an empty list of users.
/// If the file exists, it reads the JSON content, deserializes it into a list of <see cref="User"/> objects,
/// and returns that list. If the deserialization results in a null value, an empty list is returned.
/// </remarks>
public async Task<List<User>> getAllUser() public async Task<List<User>> getAllUser()
{ {
if (!File.Exists(_jsonFilePath)) if (!File.Exists(_jsonFilePath))
@ -100,84 +71,60 @@ public class UserServiceStub : IUserService
return JsonSerializer.Deserialize<List<User>>(json) ?? new List<User>(); return JsonSerializer.Deserialize<List<User>>(json) ?? new List<User>();
} }
/// <summary> /// <summary>
/// Asynchronously retrieves a paginated list of users from a JSON file. /// Récupère une liste paginée d'utilisateurs.
/// </summary> /// </summary>
/// <param name="nb">The number of users to retrieve per page.</param> /// <param name="nb">Nombre d'utilisateurs par page.</param>
/// <param name="page">The page number for the data to retrieve.</param> /// <param name="page">Numéro de la page.</param>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="User"/> objects.</returns> /// <returns>Une liste d'utilisateurs correspondant à la page demandée.</returns>
/// <remarks> public async Task<List<User>> getSomeUser(int nb, int page)
/// This method retrieves all users using the <see cref="getAllUser"/> method, then calculates the range of users to return
/// based on the specified page number and the number of users per page (`nb`).
/// It returns the corresponding subset of users for the given page. If the page exceeds the available number of users,
/// it returns the last `nb` users available.
/// </remarks>
public async Task<List<User>?> getSomeUser(int nb, int page)
{ {
var users = await getAllUser(); var users = await getAllUser();
if ((page - 1) * nb + nb > users.Count) if ((page - 1) * nb + nb > users.Count())
{ {
return users.GetRange(users.Count - nb, nb); return users.GetRange(users.Count() - nb, nb);
} }
return users.GetRange((page - 1) * nb, nb); return users.GetRange((page - 1) * nb, nb);
} }
/// <summary> /// <summary>
/// Asynchronously retrieves a single user by their ID from the JSON file. /// Récupère un utilisateur spécifique en fonction de son ID.
/// </summary> /// </summary>
/// <param name="id">The ID of the user to retrieve.</param> /// <param name="id">ID de l'utilisateur recherché.</param>
/// <returns>A task representing the asynchronous operation, with a result of the <see cref="User"/> object if found, otherwise null.</returns> /// <returns>L'utilisateur correspondant à l'ID ou null s'il n'existe pas.</returns>
/// <remarks>
/// This method retrieves all users using the <see cref="getAllUser"/> method,
/// then searches for the user with the specified `id`. If a user with the given ID is found,
/// the user is returned. Otherwise, it returns null.
/// </remarks>
public async Task<User> getOneUser(int id) public async Task<User> getOneUser(int id)
{ {
var data = await getAllUser(); var data = await getAllUser();
var user = data.FirstOrDefault(p => p.Id == id); var u = data.FirstOrDefault(p => p.Id == id);
return u;
if (user == null)
{
throw new KeyNotFoundException($"User with ID {id} not found.");
}
return user;
} }
/// <summary>
/// Recherche des utilisateurs selon des critères spécifiques.
/// </summary>
/// <param name="reserch">Terme de recherche.</param>
/// <param name="args">Liste des arguments supplémentaires pour affiner la recherche.</param>
/// <returns>Une liste d'utilisateurs correspondant aux critères de recherche.</returns>
public Task<List<User>> reserchUsers(string reserch, List<string> args) public Task<List<User>> reserchUsers(string reserch, List<string> args)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary> /// <summary>
/// Asynchronously retrieves the total number of users from the JSON file. /// Récupère le nombre total d'utilisateurs enregistrés.
/// </summary> /// </summary>
/// <returns>A task representing the asynchronous operation, with a result of the total number of users.</returns> /// <returns>Nombre total d'utilisateurs.</returns>
/// <remarks>
/// This method retrieves all users using the <see cref="getAllUser"/> method and returns the count of users in the list.
/// </remarks>
public async Task<int> getNbUser() public async Task<int> getNbUser()
{ {
var data = await getAllUser(); var data = await getAllUser();
return data.Count; return data.Count;
} }
/// <summary> /// <summary>
/// Asynchronously updates the details of a user in the JSON file. /// Met à jour les informations d'un utilisateur existant.
/// </summary> /// </summary>
/// <param name="user">The <see cref="User"/> object containing the updated user details.</param> /// <param name="user">Utilisateur avec les nouvelles informations.</param>
/// <returns>A task representing the asynchronous operation of updating the user.</returns> public async Task updateUser(User user)
/// <remarks>
/// This method retrieves all users using the <see cref="getAllUser"/> method, then searches for the user with the specified ID.
/// If a user with the given ID is found, it updates their details (Name, Email, Image, IsAdmin) based on the provided `user` object.
/// After updating the user, the modified list of users is saved back to the JSON file using the <see cref="saveUsersJson"/> method.
/// </remarks>
public async Task updateUser(User? user)
{ {
var data = await getAllUser(); var data = await getAllUser();
var person = data.FirstOrDefault(p => p.Id == user.Id); var person = data.FirstOrDefault(p => p.Id == user.Id);
@ -190,4 +137,4 @@ public class UserServiceStub : IUserService
await saveUsersJson(data); await saveUsersJson(data);
} }
} }
} }

@ -21,16 +21,6 @@
new CultureInfo("fr-FR") new CultureInfo("fr-FR")
}; };
/// <summary>
/// Gets or sets the current culture for the application, triggering a navigation to set the culture cookie when changed.
/// </summary>
/// <remarks>
/// The getter retrieves the current culture of the application using <see cref="CultureInfo.CurrentCulture"/>.
/// The setter checks if the current UI culture matches the provided value. If they are the same, no action is taken.
/// If the cultures differ, it constructs a query string that includes the new culture and a redirect URI,
/// and then navigates to a "/Culture/SetCulture" endpoint to set the culture cookie.
/// The user is redirected to the same page with the new culture applied after the redirect.
/// </remarks>
private CultureInfo Culture private CultureInfo Culture
{ {
get => CultureInfo.CurrentCulture; get => CultureInfo.CurrentCulture;
@ -50,5 +40,4 @@
this.NavigationManager.NavigateTo("/Culture/SetCulture" + query, forceLoad: true); this.NavigationManager.NavigateTo("/Culture/SetCulture" + query, forceLoad: true);
} }
} }
} }

@ -3,32 +3,31 @@
@inject UserLogin uLogin @inject UserLogin uLogin
<PageTitle>WF-WebAdmin</PageTitle> <PageTitle>WF-WebAdmin</PageTitle>
<MudThemeProvider />
<div class="page"> <div class="page">
@* @if (uLogin.Name != null) @if (uLogin.Name != null)
{ *@ {
<div class="sidebar"> <div class="sidebar">
<NavMenu/> <NavMenu/>
</div> </div>
@*}*@ }
<main> <main>
<div class="top-row px-4"> <div class="top-row px-4">
<div class="px-4"> <div class="px-4">
<CultureSelector /> <CultureSelector />
</div> </div>
@* @if (!string.IsNullOrEmpty(uLogin.Name)) @if (!string.IsNullOrEmpty(uLogin.Name))
{ {
<button class="buttonProfil" type="button"> <img class="imageProfil" src="@uLogin.Image" height="90" width="480" /></button> <button class="buttonProfil" type="button"> <img class="imageProfil" src="@uLogin.Image" height="90" width="480" /></button>
@* <img class="imageProfil" src="@uLogin.Image" /> @* <img class="imageProfil" src="@uLogin.Image" /> *@
} }
else else
{*@ {
<img class="imageProfil" src="https://cdn-icons-png.flaticon.com/512/61/61205.png"/> <img class="imageProfil" src="https://cdn-icons-png.flaticon.com/512/61/61205.png"/>
@* } *@ }
</div> </div>
<article class="content px-4"> <article class="content px-4">
@Body @Body

@ -2,66 +2,56 @@
@inject UserLogin uLogin @inject UserLogin uLogin
<div class="top-row ps-3 navbar navbar-dark"> <div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="">What the Fantasy</a> <a class="navbar-brand" href="">What the Fantasy</a>
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu"> <button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
</div>
</div> </div>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu"> <div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<nav class="flex-column"> <nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="Accueil">
<span class="oi oi-plus" aria-hidden="true"></span> Accueil
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="ValidQuote">
<span class="oi oi-list-rich" aria-hidden="true"></span> Validation de citations
</NavLink>
</div>
<div class="nav-item px-3"> <div class="nav-item px-3">
<NavLink class="nav-link" href=""> <NavLink class="nav-link" href="ValidQuiz">
🏠 Accueil <span class="oi oi-list-rich" aria-hidden="true"></span> Validation de quiz
</NavLink> </NavLink>
</div> </div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="ValidQuote">
📜 Validation de citations
</NavLink>
</div>
<div class="nav-item px-3"> <div class="nav-item px-3">
<NavLink class="nav-link" href="ValidQuiz"> <NavLink class="nav-link" href="deleteuser">
📝 Validation de quiz <span class="oi oi-list-rich" aria-hidden="true"></span> Gestion des utilisateurs
</NavLink> </NavLink>
</div> </div>
<div class="nav-item px-3"> <div class="nav-item px-3">
<NavLink class="nav-link" href="deleteuser"> <NavLink class="nav-link" href="modifquote">
👤 Gestion des utilisateurs <span class="oi oi-list-rich" aria-hidden="true"></span> Gestion des citations
</NavLink> </NavLink>
</div> </div>
<div class="nav-item px-3"> <div class="nav-item px-3">
<NavLink class="nav-link" href="modifquote"> <NavLink class="nav-link" href="modifquiz">
✍️ Gestion des citations <span class="oi oi-list-rich" aria-hidden="true"></span> Gestion des Question
</NavLink> </NavLink>
</div> </div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="modifquiz">
❓ Gestion des questions
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="logs">
📊 Logs
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="/commentary-chart">
📈 Stats commentaires
</NavLink>
</div>
</nav>
</div>
</nav>
</div>
@code { @code {
private bool collapseNavMenu = true; private bool collapseNavMenu = true;

@ -15,7 +15,6 @@
<PackageReference Include="bootstrap" Version="5.3.3" /> <PackageReference Include="bootstrap" Version="5.3.3" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" /> <PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.1" /> <PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.1" />
<PackageReference Include="MudBlazor" Version="6.2.0" />
<PackageReference Include="Npgsql" Version="9.0.2" /> <PackageReference Include="Npgsql" Version="9.0.2" />
<PackageReference Include="Blazored.Modal" Version="7.2.0" /> <PackageReference Include="Blazored.Modal" Version="7.2.0" />
</ItemGroup> </ItemGroup>

@ -10,5 +10,4 @@
@using WF_WebAdmin.Shared @using WF_WebAdmin.Shared
@using Blazorise.DataGrid @using Blazorise.DataGrid
@using Blazored.Modal @using Blazored.Modal
@using MudBlazor

@ -1,5 +0,0 @@
{
"version": "3.0",
"defaultProvider": "cdnjs",
"libraries": []
}

@ -1,239 +1,262 @@
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); @import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap');
html, body { html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: white; background: white;
color: black; color: black;
} }
h1:focus {
outline: none;
}
h1 {
text-align: center; a, .btn-link {
font-size: 32px; color: #ffffff;
margin-top: 10%; text-decoration: none;
font-family: "Roboto", serif; }
}
.btn-primary {
color: #fff;
h1:focus { background-color: #1b6ec2;
outline: none; border-color: #1861ac;
} }
a, .btn-link { .content {
color: #ffffff; padding-top: 1.1rem;
text-decoration: none; }
}
.valid.modified:not([type=checkbox]) {
.btn-primary { outline: 1px solid #26b050;
color: #fff; }
background-color: #1b6ec2;
border-color: #1861ac; .invalid {
} outline: 1px solid red;
}
.content {
padding-top: 1.1rem; .validation-message {
} color: red;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050; #blazor-error-ui {
} background: lightyellow;
bottom: 0;
.invalid { box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
outline: 1px solid red; display: none;
} left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
.validation-message { position: fixed;
color: red; width: 100%;
} z-index: 1000;
}
#blazor-error-ui {
background: lightyellow; #blazor-error-ui .dismiss {
bottom: 0; cursor: pointer;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); position: absolute;
display: none; right: 0.75rem;
left: 0; top: 0.5rem;
padding: 0.6rem 1.25rem 0.7rem 1.25rem; }
position: fixed;
width: 100%; .blazor-error-boundary {
z-index: 1000; background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
} padding: 1rem 1rem 1rem 3.7rem;
color: white;
#blazor-error-ui .dismiss { }
cursor: pointer;
position: absolute; .blazor-error-boundary::after {
right: 0.75rem; content: "An error has occurred."
top: 0.5rem; }
}
.blazor-error-boundary { button {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; border: none;
padding: 1rem 1rem 1rem 3.7rem; border-radius: 25px;
color: white; background-color: lightgrey;
} padding: 1vh;
}
.blazor-error-boundary::after {
content: "An error has occurred." /*Page DeleteUser*/
} .userDiv, .QuoteDiv {
margin-right: 20px;
margin-bottom: 10px;
button { border-radius: 25px;
border: none; background-color: #C3C4C5;
border-radius: 25px; }
background-color: lightgrey;
padding: 1vh; .imgProfil {
} border-radius: 25px;
width: 150px; /* Taille standard */
/*Page DeleteUser*/ height: 150px; /* Taille standard */
.userDiv, .QuoteDiv { object-fit: cover;
margin-right: 20px; }
margin-bottom: 10px;
border-radius: 25px; .imgQuote {
background-color: #C3C4C5; border-radius: 20px;
} width: 300px; /* Taille standard */
height: 300px; /* Taille standard */
.imgProfil { object-fit: cover;
border-radius: 25px; }
width: 150px; /* Taille standard */
height: 150px; /* Taille standard */ .pseudo, .mail, .idUser, .dateCrea, .idQuote, .contentQuote, .CaracterQuote, .SourceQuote, .langueQuote, .UserPropositionQuote {
object-fit: cover; margin-left: 10px;
} }
.imgQuote { /*ModifQuote*/
border-radius: 20px;
width: 300px; /* Taille standard */ .imgTab{
height: 300px; /* Taille standard */ width: 5vw;
object-fit: cover; height: 5vw;
} object-fit: contain;
}
.pseudo, .mail, .idUser, .dateCrea, .idQuote, .contentQuote, .CaracterQuote, .SourceQuote, .langueQuote, .UserPropositionQuote {
margin-left: 10px; /*Popup DeleteUser*/
} .divPopup {
position: fixed;
/*ModifQuote*/ top: 0;
left: 0;
.imgTab{ width: 100%;
width: 5vw; height: 100%;
height: 5vw; border-radius:20px;
object-fit: contain; display: flex;
} justify-content: center;
align-items: center;
/*Popup DeleteUser*/ z-index: 2;
.divPopup { }
position: fixed;
top: 0; .contentPopup {
left: 0; padding: 20px;
width: 100%; border-radius: 20px;
height: 100%; border: 3px solid black;
border-radius:20px; display: flex;
display: flex; background-color: white;
justify-content: center; flex-direction: column;
align-items: center; gap: 10px;
z-index: 2; width: 300px;
} text-align: center;
}
.contentPopup {
padding: 20px; table {
border-radius: 20px; border-collapse: collapse;
border: 3px solid black; width: 100%;
display: flex; }
background-color: white;
flex-direction: column; td {
gap: 10px; padding: 10px 20px;
width: 300px; border: none;
text-align: center; }
}
.boutons {
table { display: flex;
border-collapse: collapse; justify-content: space-between;
width: 100%; gap: 10px;
} }
td { .boutons button {
padding: 10px 20px; border: none;
border: none; background-color: transparent;
} padding: 5px;
cursor: pointer;
.boutons { }
display: flex;
justify-content: space-between; .boutons button img {
gap: 10px; width: 24px;
} height: 24px;
object-fit: contain;
.boutons button { }
border: none;
background-color: transparent;
padding: 5px; .buttonSubmitDiv {
cursor: pointer; text-align: center;
} }
.boutons button img { h1 {
width: 24px; text-align: center;
height: 24px; font-size: 32px;
object-fit: contain; margin-top: 10%;
} font-family: "Roboto", serif;
}
.buttonSubmitDiv { p {
text-align: center; margin-bottom: 2%;
} font-size: 20px;
font-family: "Roboto", serif;
}
p { .login {
margin-bottom: 2%; width: 35vw;
font-size: 20px; margin-left: 30.5vw;
font-family: "Roboto", serif; margin-top: 3vh;
} border-radius: 25px;
padding: 2vw;
.login { background-color: #cfcfcf;
width: 35vw; }
margin-left: 30.5vw;
margin-top: 3vh;
border-radius: 25px; /*Page login*/
padding: 2vw; .buttonSudmite {
background-color: #cfcfcf; border: none;
} padding: 2%;
margin-top: 5%;
border-radius: 25px;
/*Page login*/ width: 50%;
.buttonSudmite { font-size: 1.25em;
border: none; background-color: white;
padding: 2%; font-family: "Roboto", serif;
margin-top: 5%; }
border-radius: 25px;
width: 50%; .connexion {
font-size: 1.25em; width: 94%;
background-color: white; height: 40px;
font-family: "Roboto", serif; padding-left: 3%;
} margin-left: 1%;
margin-top: -1%;
.connexion { border-radius: 25px;
width: 94%; border: none;
height: 40px; font-size: 15px;
padding-left: 3%; }
margin-left: 1%;
margin-top: -1%;
border-radius: 25px;
border: none; .ErrorMsg {
font-size: 15px; color: red;
} }
table {
border-collapse: collapse;
.ErrorMsg { width: 100%;
color: red; }
}
td {
padding: 10px 20px;
.imageProfil { border: none;
height: auto; }
width: 30px;
border-radius: 45%; .boutons {
} display: flex;
justify-content: space-between;
.buttonProfil{ gap: 10px;
background-color:transparent; }
.boutons button {
border: none;
background-color: transparent;
padding: 5px;
cursor: pointer;
}
.boutons button img {
width: 24px;
height: 24px;
object-fit: contain;
}
.imageProfil {
height: auto;
width: 30px;
border-radius: 45%;
}
.buttonProfil{
background-color:transparent;
} }

@ -1,702 +0,0 @@
[
{
"id_comment": 1,
"quote": 19,
"users": 29,
"dateC": "2024-01-22",
"comment": "Citation magnifique."
},
{
"id_comment": 2,
"quote": 6,
"users": 18,
"dateC": "2024-01-20",
"comment": "Paroles sages."
},
{
"id_comment": 3,
"quote": 2,
"users": 8,
"dateC": "2024-01-25",
"comment": "Citation puissante."
},
{
"id_comment": 4,
"quote": 11,
"users": 31,
"dateC": "2024-01-01",
"comment": "Très poétique."
},
{
"id_comment": 5,
"quote": 8,
"users": 6,
"dateC": "2024-02-06",
"comment": "Belle pensée."
},
{
"id_comment": 6,
"quote": 13,
"users": 37,
"dateC": "2024-02-01",
"comment": "Citation exceptionnelle."
},
{
"id_comment": 7,
"quote": 2,
"users": 33,
"dateC": "2024-02-11",
"comment": "Belle pensée."
},
{
"id_comment": 8,
"quote": 7,
"users": 28,
"dateC": "2024-02-07",
"comment": "Très émouvant."
},
{
"id_comment": 9,
"quote": 1,
"users": 48,
"dateC": "2024-03-05",
"comment": "Très motivant."
},
{
"id_comment": 10,
"quote": 16,
"users": 14,
"dateC": "2024-03-13",
"comment": "Très motivant."
},
{
"id_comment": 11,
"quote": 10,
"users": 9,
"dateC": "2024-03-13",
"comment": "Très touchant."
},
{
"id_comment": 12,
"quote": 4,
"users": 44,
"dateC": "2024-03-01",
"comment": "Très touchant."
},
{
"id_comment": 13,
"quote": 9,
"users": 8,
"dateC": "2024-04-04",
"comment": "Citation magnifique."
},
{
"id_comment": 14,
"quote": 3,
"users": 49,
"dateC": "2024-04-18",
"comment": "Très émouvant."
},
{
"id_comment": 15,
"quote": 19,
"users": 3,
"dateC": "2024-04-07",
"comment": "Très motivant."
},
{
"id_comment": 16,
"quote": 4,
"users": 9,
"dateC": "2024-04-25",
"comment": "Très motivant."
},
{
"id_comment": 17,
"quote": 17,
"users": 40,
"dateC": "2024-05-04",
"comment": "Très émouvant."
},
{
"id_comment": 18,
"quote": 6,
"users": 21,
"dateC": "2024-05-11",
"comment": "Très poétique."
},
{
"id_comment": 19,
"quote": 16,
"users": 3,
"dateC": "2024-05-08",
"comment": "Citation exceptionnelle."
},
{
"id_comment": 20,
"quote": 1,
"users": 17,
"dateC": "2024-05-04",
"comment": "Très touchant."
},
{
"id_comment": 21,
"quote": 14,
"users": 34,
"dateC": "2024-06-01",
"comment": "Citation magnifique."
},
{
"id_comment": 22,
"quote": 12,
"users": 23,
"dateC": "2024-06-03",
"comment": "Citation magnifique."
},
{
"id_comment": 23,
"quote": 11,
"users": 47,
"dateC": "2024-06-05",
"comment": "Citation magnifique."
},
{
"id_comment": 24,
"quote": 16,
"users": 28,
"dateC": "2024-06-07",
"comment": "Citation magnifique."
},
{
"id_comment": 25,
"quote": 19,
"users": 39,
"dateC": "2024-07-01",
"comment": "Citation magnifique."
},
{
"id_comment": 26,
"quote": 13,
"users": 31,
"dateC": "2024-07-02",
"comment": "Citation magnifique."
},
{
"id_comment": 27,
"quote": 1,
"users": 33,
"dateC": "2024-07-03",
"comment": "Citation magnifique."
},
{
"id_comment": 28,
"quote": 17,
"users": 20,
"dateC": "2024-07-04",
"comment": "Citation magnifique."
},
{
"id_comment": 29,
"quote": 13,
"users": 29,
"dateC": "2024-08-01",
"comment": "Citation magnifique."
},
{
"id_comment": 30,
"quote": 11,
"users": 27,
"dateC": "2024-08-02",
"comment": "Citation magnifique."
},
{
"id_comment": 31,
"quote": 18,
"users": 35,
"dateC": "2024-08-03",
"comment": "Citation magnifique."
},
{
"id_comment": 32,
"quote": 13,
"users": 32,
"dateC": "2024-08-04",
"comment": "Citation magnifique."
},
{
"id_comment": 33,
"quote": 13,
"users": 3,
"dateC": "2024-09-01",
"comment": "Citation magnifique."
},
{
"id_comment": 34,
"quote": 15,
"users": 43,
"dateC": "2024-09-02",
"comment": "Citation magnifique."
},
{
"id_comment": 35,
"quote": 11,
"users": 15,
"dateC": "2024-09-03",
"comment": "Citation magnifique."
},
{
"id_comment": 36,
"quote": 10,
"users": 22,
"dateC": "2024-09-04",
"comment": "Citation magnifique."
},
{
"id_comment": 37,
"quote": 17,
"users": 23,
"dateC": "2024-10-01",
"comment": "Citation magnifique."
},
{
"id_comment": 38,
"quote": 14,
"users": 40,
"dateC": "2024-10-02",
"comment": "Citation magnifique."
},
{
"id_comment": 39,
"quote": 15,
"users": 41,
"dateC": "2024-10-03",
"comment": "Citation magnifique."
},
{
"id_comment": 40,
"quote": 16,
"users": 42,
"dateC": "2024-10-04",
"comment": "Citation magnifique."
},
{
"id_comment": 41,
"quote": 10,
"users": 39,
"dateC": "2024-11-01",
"comment": "Citation magnifique."
},
{
"id_comment": 42,
"quote": 17,
"users": 43,
"dateC": "2024-11-02",
"comment": "Citation magnifique."
},
{
"id_comment": 43,
"quote": 13,
"users": 27,
"dateC": "2024-11-03",
"comment": "Citation magnifique."
},
{
"id_comment": 44,
"quote": 12,
"users": 29,
"dateC": "2024-11-04",
"comment": "Citation magnifique."
},
{
"id_comment": 45,
"quote": 11,
"users": 32,
"dateC": "2024-12-01",
"comment": "Citation magnifique."
},
{
"id_comment": 46,
"quote": 15,
"users": 31,
"dateC": "2024-12-02",
"comment": "Citation magnifique."
},
{
"id_comment": 47,
"quote": 18,
"users": 33,
"dateC": "2024-12-03",
"comment": "Citation magnifique."
},
{
"id_comment": 48,
"quote": 14,
"users": 34,
"dateC": "2024-12-04",
"comment": "Citation magnifique."
},
{
"id_comment": 49,
"quote": 19,
"users": 35,
"dateC": "2024-12-05",
"comment": "Citation magnifique."
},
{
"id_comment": 50,
"quote": 17,
"users": 36,
"dateC": "2024-12-06",
"comment": "Citation magnifique."
},
{
"id_comment": 51,
"quote": 16,
"users": 37,
"dateC": "2024-12-07",
"comment": "Citation magnifique."
},
{
"id_comment": 52,
"quote": 13,
"users": 38,
"dateC": "2024-12-08",
"comment": "Citation magnifique."
},
{
"id_comment": 53,
"quote": 12,
"users": 39,
"dateC": "2024-12-09",
"comment": "Citation magnifique."
},
{
"id_comment": 54,
"quote": 11,
"users": 40,
"dateC": "2024-12-10",
"comment": "Citation magnifique."
},
{
"id_comment": 55,
"quote": 10,
"users": 41,
"dateC": "2024-12-11",
"comment": "Citation magnifique."
},
{
"id_comment": 56,
"quote": 9,
"users": 42,
"dateC": "2024-12-12",
"comment": "Citation magnifique."
},
{
"id_comment": 57,
"quote": 8,
"users": 43,
"dateC": "2024-12-13",
"comment": "Citation magnifique."
},
{
"id_comment": 58,
"quote": 7,
"users": 44,
"dateC": "2024-12-14",
"comment": "Citation magnifique."
},
{
"id_comment": 59,
"quote": 6,
"users": 45,
"dateC": "2024-12-15",
"comment": "Citation magnifique."
},
{
"id_comment": 60,
"quote": 5,
"users": 46,
"dateC": "2024-12-16",
"comment": "Citation magnifique."
},
{
"id_comment": 61,
"quote": 4,
"users": 47,
"dateC": "2024-12-17",
"comment": "Citation magnifique."
},
{
"id_comment": 62,
"quote": 3,
"users": 48,
"dateC": "2024-12-18",
"comment": "Citation magnifique."
},
{
"id_comment": 63,
"quote": 2,
"users": 49,
"dateC": "2024-12-19",
"comment": "Citation magnifique."
},
{
"id_comment": 64,
"quote": 1,
"users": 50,
"dateC": "2024-12-20",
"comment": "Citation magnifique."
},
{
"id_comment": 65,
"quote": 19,
"users": 1,
"dateC": "2024-12-21",
"comment": "Citation magnifique."
},
{
"id_comment": 66,
"quote": 18,
"users": 2,
"dateC": "2024-12-22",
"comment": "Citation magnifique."
},
{
"id_comment": 67,
"quote": 17,
"users": 3,
"dateC": "2024-12-23",
"comment": "Citation magnifique."
},
{
"id_comment": 68,
"quote": 16,
"users": 4,
"dateC": "2024-12-24",
"comment": "Citation magnifique."
},
{
"id_comment": 69,
"quote": 15,
"users": 5,
"dateC": "2024-12-25",
"comment": "Citation magnifique."
},
{
"id_comment": 70,
"quote": 14,
"users": 6,
"dateC": "2024-12-26",
"comment": "Citation magnifique."
},
{
"id_comment": 71,
"quote": 13,
"users": 7,
"dateC": "2024-12-27",
"comment": "Citation magnifique."
},
{
"id_comment": 72,
"quote": 12,
"users": 8,
"dateC": "2024-12-28",
"comment": "Citation magnifique."
},
{
"id_comment": 73,
"quote": 11,
"users": 9,
"dateC": "2024-01-01",
"comment": "Citation magnifique."
},
{
"id_comment": 74,
"quote": 10,
"users": 10,
"dateC": "2024-01-02",
"comment": "Citation magnifique."
},
{
"id_comment": 75,
"quote": 9,
"users": 11,
"dateC": "2024-01-03",
"comment": "Citation magnifique."
},
{
"id_comment": 76,
"quote": 8,
"users": 12,
"dateC": "2024-01-04",
"comment": "Citation magnifique."
},
{
"id_comment": 77,
"quote": 7,
"users": 13,
"dateC": "2024-01-05",
"comment": "Citation magnifique."
},
{
"id_comment": 78,
"quote": 6,
"users": 14,
"dateC": "2024-01-06",
"comment": "Citation magnifique."
},
{
"id_comment": 79,
"quote": 5,
"users": 15,
"dateC": "2024-01-07",
"comment": "Citation magnifique."
},
{
"id_comment": 80,
"quote": 4,
"users": 16,
"dateC": "2024-01-08",
"comment": "Citation magnifique."
},
{
"id_comment": 81,
"quote": 3,
"users": 17,
"dateC": "2024-01-09",
"comment": "Citation magnifique."
},
{
"id_comment": 82,
"quote": 2,
"users": 18,
"dateC": "2024-01-10",
"comment": "Citation magnifique."
},
{
"id_comment": 83,
"quote": 15,
"users": 33,
"dateC": "2024-06-24",
"comment": "Citation exceptionnelle."
},
{
"id_comment": 84,
"quote": 19,
"users": 15,
"dateC": "2024-03-11",
"comment": "Citation merveilleuse."
},
{
"id_comment": 85,
"quote": 4,
"users": 16,
"dateC": "2024-01-19",
"comment": "Citation merveilleuse."
},
{
"id_comment": 86,
"quote": 10,
"users": 20,
"dateC": "2024-01-15",
"comment": "Très émouvant."
},
{
"id_comment": 87,
"quote": 3,
"users": 33,
"dateC": "2024-12-17",
"comment": "Très touchant."
},
{
"id_comment": 88,
"quote": 5,
"users": 8,
"dateC": "2024-07-01",
"comment": "Citation exceptionnelle."
},
{
"id_comment": 89,
"quote": 13,
"users": 28,
"dateC": "2024-02-20",
"comment": "Citation merveilleuse."
},
{
"id_comment": 90,
"quote": 18,
"users": 24,
"dateC": "2024-09-16",
"comment": "Citation merveilleuse."
},
{
"id_comment": 91,
"quote": 17,
"users": 43,
"dateC": "2024-06-20",
"comment": "Très réfléchi."
},
{
"id_comment": 92,
"quote": 13,
"users": 36,
"dateC": "2024-11-08",
"comment": "Super citation !"
},
{
"id_comment": 93,
"quote": 19,
"users": 41,
"dateC": "2024-01-20",
"comment": "Super citation !"
},
{
"id_comment": 94,
"quote": 13,
"users": 10,
"dateC": "2024-05-03",
"comment": "Citation magnifique."
},
{
"id_comment": 95,
"quote": 4,
"users": 49,
"dateC": "2024-07-01",
"comment": "Citation profonde."
},
{
"id_comment": 96,
"quote": 16,
"users": 21,
"dateC": "2024-11-08",
"comment": "Citation merveilleuse."
},
{
"id_comment": 97,
"quote": 14,
"users": 27,
"dateC": "2024-11-20",
"comment": "Citation puissante."
},
{
"id_comment": 98,
"quote": 4,
"users": 39,
"dateC": "2024-12-11",
"comment": "Très motivant."
},
{
"id_comment": 99,
"quote": 9,
"users": 26,
"dateC": "2024-09-27",
"comment": "Paroles sages."
},
{
"id_comment": 100,
"quote": 3,
"users": 42,
"dateC": "2024-02-12",
"comment": "Citation merveilleuse."
}
]

@ -0,0 +1,9 @@
[
{
"id_comment": 1,
"quote": 1,
"users": 1,
"dateC":"2024-10-10",
"comment": "coucou"
}
]

@ -1,14 +1,12 @@
[ [
{ {
"Id": 11, "Id": "1",
"Content": "Vous ne pouvez pas vivre sans causer de dommages \u00E0 quelqu\u0027un d\u0027autre.", "Content": "Dans le monde il ny a pas dun côté le bien et le mal, il y a une part de lumière et dombre en chacun de nous. Ce qui compte cest celle que lon choisit de montrer dans nos actes, ça cest ce que lon est vraiment.",
"Like": 110, "Likes": 0,
"Langue": "fr", "Langue": "fr",
"Charac": "test", "Charac": "Superman",
"ImgPath": "http://thematrix.com", "TitleSrc": "SuperMan : le film",
"TitleSrc": "The Matrix", "UserProposition": "joe",
"DateSrc": "2025-01-21T00:00:00", "ImgPath": "https://tse4.mm.bing.net/th/id/OIP.fc5TQflh0cbxB1GUeOdk6gHaK8?w=123&h=180&c=7&r=0&o=5&pid=1.7"
"UserProposition": "user11",
"IsValid": false
} }
] ]

@ -1,7 +1,7 @@
[ [
{ {
"Id": 1, "Id": 1,
"Content": "Que la force soit avec toi", "Content": "Que la force soit avec toi.",
"Like": 150, "Like": 150,
"Langue": "en", "Langue": "en",
"Charac": "Drago Malefoy", "Charac": "Drago Malefoy",
@ -25,7 +25,7 @@
}, },
{ {
"Id": 3, "Id": 3,
"Content": "C\u0027est le choix qui fait l\u0027homme, non le destin", "Content": "C\u0027est le choix qui fait l\u0027homme, non le destin.",
"Like": 90, "Like": 90,
"Langue": "fr", "Langue": "fr",
"Charac": "test", "Charac": "test",

@ -1,261 +0,0 @@
[
{
"LogLevel": 1,
"Message": "Logs de test"
},
{
"LogLevel": 2,
"Message": "User testeur1 is no longer an administator"
},
{
"LogLevel": 2,
"Message": "User dev is now administrator"
},
{
"LogLevel": 2,
"Message": "Modification of user testeur"
},
{
"LogLevel": 2,
"Message": "Delete user jane_smith"
},
{
"LogLevel": 2,
"Message": "Editing the quote C\u0027est le choix qui fait l\u0027homme, non le destin."
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
<<<<<<< HEAD
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
},
{
"LogLevel": 2,
"Message": "Random change of quote of the day"
=======
>>>>>>> c9a9e592d46e0842b954daa1f9b008f8302e78ad
}
]

@ -1,4 +1,26 @@
[ [
{
"Id": 9,
"Question": "Question_quiz_1",
"AnswerA": "rep_1",
"AnswerB": "rep_2",
"AnswerC": "rep_3",
"AnswerD": "rep_3",
"CAnswer": "A",
"IsValid": false,
"UserProposition": "Earnestine Poole"
},
{
"Id": 10,
"Question": "Voluptate pariatur ipsum magna sint Lorem adipisicing.",
"AnswerA": "sint velit",
"AnswerB": "non culpa",
"AnswerC": "nisi ut",
"AnswerD": "excepteur labore",
"CAnswer": "B",
"IsValid": false,
"UserProposition": "Alexis Cross"
},
{ {
"Id": 11, "Id": 11,
"Question": "nv question", "Question": "nv question",
@ -7,19 +29,19 @@
"AnswerC": "do officia", "AnswerC": "do officia",
"AnswerD": "ut nostrud", "AnswerD": "ut nostrud",
"CAnswer": "C", "CAnswer": "C",
"IsValid": true, "IsValid": false,
"UserProposition": "Brooks Martinez" "UserProposition": "Brooks Martinez"
}, },
{ {
"Id": 12, "Id": 12,
"Question": "question", "Question": "Irure occaecat sit laborum nulla ea dolore et aliqua sunt Lorem enim esse.",
"AnswerA": "repA", "AnswerA": "excepteur occaecat",
"AnswerB": "non", "AnswerB": "pariatur in",
"AnswerC": "do", "AnswerC": "reprehenderit excepteur",
"AnswerD": "ut", "AnswerD": "laborum adipisicing",
"CAnswer": "A", "CAnswer": "D",
"IsValid": false, "IsValid": false,
"UserProposition": "Brooks Martinez" "UserProposition": "Shields Roth"
}, },
{ {
"Id": 13, "Id": 13,
@ -38,7 +60,7 @@
"AnswerA": "ca", "AnswerA": "ca",
"AnswerB": "va", "AnswerB": "va",
"AnswerC": "marcher", "AnswerC": "marcher",
"AnswerD": "L", "AnswerD": "!",
"CAnswer": "A", "CAnswer": "A",
"IsValid": true, "IsValid": true,
"UserProposition": "Admin" "UserProposition": "Admin"

@ -1,70 +1,107 @@
[ [
{ {
"Id": 3, "Id": 3,
"Image": "https://tse4.mm.bing.net/th/id/OIP.XNQPKwc1OUfvnSO9MsxDYgHaE7?w=202\u0026h=180\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7", "Image": "https://tse4.mm.bing.net/th/id/OIP.XNQPKwc1OUfvnSO9MsxDYgHaE7?w=202\u0026h=180\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
"Name": "testeur", "Name": "testeur",
"Email": "testeur@gmail.com", "Email": "testeur@gmail.com",
"DateCreation": "2024-08-02T00:00:00", "DateCreation": "2024-08-02T00:00:00",
"IsAdmin": false, "IsAdmin": true,
"Comments": [ "Comments": [
{ {
"Id": 0, "Text": "Premier test effectu\u00E9, tout semble OK.",
"IdUser": 0, "DateCreation": "2024-08-02T00:00:00"
"DateCreation": "2024-08-02T00:00:00", }
"Text": "Premier test effectu\u00E9, tout semble OK." ]
} },
] {
}, "Id": 4,
{ "Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
"Id": 4, "Name": "dev",
"Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7", "Email": "dev@gmail.com",
"Name": "dev", "DateCreation": "2024-10-10T00:00:00",
"Email": "dev@gmail.com", "IsAdmin": false,
"DateCreation": "2024-10-10T00:00:00", "Comments": null
"IsAdmin": true, },
"Comments": null {
}, "Id": 5,
{ "Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
"Id": 5, "Name": "jean_doe",
"Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7", "Email": "jean.doe@gmail.com",
"Name": "jean_doe", "DateCreation": "2024-06-25T00:00:00",
"Email": "jean.doe@gmail.com", "IsAdmin": false,
"DateCreation": "2024-06-25T00:00:00", "Comments": [
"IsAdmin": false, {
"Comments": [ "Text": "Utilisateur tr\u00E8s actif, peut \u00EAtre un peu trop intrusif.",
{ "DateCreation": "2024-06-25T00:00:00"
"Id": 0, }
"IdUser": 0, ]
"DateCreation": "2024-06-25T00:00:00", },
"Text": "Utilisateur tr\u00E8s actif, peut \u00EAtre un peu trop intrusif." {
} "Id": 6,
] "Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
}, "Name": "jane_smith",
{ "Email": "jane.smith@gmail.com",
"Id": 7, "DateCreation": "2024-07-15T00:00:00",
"Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7", "IsAdmin": false,
"Name": "test_n1", "Comments": null
"Email": "admin.joe@gmail.com", },
"DateCreation": "2024-05-30T00:00:00", {
"IsAdmin": false, "Id": 7,
"Comments": null "Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
}, "Name": "test_n1",
{ "Email": "admin.joe@gmail.com",
"Id": 10, "DateCreation": "2024-05-30T00:00:00",
"Image": "https://th.bing.com/th/id/OIP.24T00MDK-RUhFnm1Do5PFwHaFj?w=229\u0026h=180\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7", "IsAdmin": false,
"Name": "dev_susan", "Comments": null
"Email": "dev.susan@gmail.com", },
"DateCreation": "2024-08-12T00:00:00", {
"IsAdmin": false, "Id": 8,
"Comments": null "Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
}, "Name": "dev_anna",
{ "Email": "dev.anna@gmail.com",
"Id": 12, "DateCreation": "2024-09-05T00:00:00",
"Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7", "IsAdmin": false,
"Name": "admin_lucas", "Comments": null
"Email": "admin.lucas@gmail.com", },
"DateCreation": "2024-09-22T00:00:00", {
"IsAdmin": false, "Id": 9,
"Comments": null "Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
} "Name": "support_mark",
"Email": "support.mark@gmail.com",
"DateCreation": "2024-11-20T00:00:00",
"IsAdmin": false,
"Comments": [
{
"Text": "Support rapide et efficace, mais manquant un peu de d\u00E9tails.",
"DateCreation": "2024-11-20T00:00:00"
}
]
},
{
"Id": 10,
"Image": "https://th.bing.com/th/id/OIP.24T00MDK-RUhFnm1Do5PFwHaFj?w=229\u0026h=180\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
"Name": "dev_susan",
"Email": "dev.susan@gmail.com",
"DateCreation": "2024-08-12T00:00:00",
"IsAdmin": false,
"Comments": null
},
{
"Id": 12,
"Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
"Name": "admin_lucas",
"Email": "admin.lucas@gmail.com",
"DateCreation": "2024-09-22T00:00:00",
"IsAdmin": false,
"Comments": null
},
{
"Id": 14,
"Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
"Name": "developer_mike",
"Email": "developer.mike@gmail.com",
"DateCreation": "2024-11-02T00:00:00",
"IsAdmin": false,
"Comments": null
}
] ]
Loading…
Cancel
Save