Compare commits

...

142 Commits

Author SHA1 Message Date
tomivt 27016668bd Code Smells from Pages/Edit
continuous-integration/drone/push Build is passing Details
2 months ago
tomivt ccf7e35f39 Code Smells from Pages/DeleteUser
continuous-integration/drone/push Build is passing Details
2 months ago
tomivt cc616955bc Code Smells from Pages/AddQuiz
continuous-integration/drone/push Build is passing Details
2 months ago
tomivt 04110bc503 Code Smells from Pages/Acceuil
continuous-integration/drone/push Build is passing Details
2 months ago
tomivt 0bead3de61 Code Smells from Service
continuous-integration/drone/push Build is passing Details
3 months ago
tomivt 2018e18e10 Code Smells from Program.cs
continuous-integration/drone/push Build is passing Details
3 months ago
tomivt 15392ab08a Code Smells from wwwroot/css
continuous-integration/drone/push Build is passing Details
3 months ago
Kentin BRONGNIART 7885b8c724 Random daily quote
continuous-integration/drone/push Build is passing Details
3 months ago
Kentin BRONGNIART dc8d2510af Random daily quote
3 months ago
Maxime ROCHER c9a9e592d4 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER cdf1c5777c ajout petits icons stylax
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 79f819295d Merge branch 'master' of https://codefirst.iut.uca.fr/git/WhatTheFantasy/WF-WebAdmin
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 3070676b20 Enfin le graphique
3 months ago
tomivt 4be846cd05 Code Smells from Converter
continuous-integration/drone/push Build is passing Details
3 months ago
tomivt 6aff34e983 Code Smells from Model
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 6b3a7283b5 retablissement CI
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER a16dca6156 retablissement CI
3 months ago
Maxime ROCHER 34abbf746b rien
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER afc393907a Merge branch 'master' of https://codefirst.iut.uca.fr/git/WhatTheFantasy/WF-WebAdmin
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER eab4b4fa05 rien
3 months ago
Kentin BRONGNIART b1edb3fec5 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/Error.cshtml.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 6fa4511781 Merge branch 'master' of https://codefirst.iut.uca.fr/git/WhatTheFantasy/WF-WebAdmin
3 months ago
Kentin BRONGNIART 73595560e5 correction erreur a la con
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 0e564f580b Merge branch 'master' of https://codefirst.iut.uca.fr/git/WhatTheFantasy/WF-WebAdmin
3 months ago
Maxime ROCHER db4edcb1d1 rien
3 months ago
Kentin BRONGNIART 4f730ec5f8 Merge branch 'master' of https://codefirst.iut.uca.fr/git/WhatTheFantasy/WF-WebAdmin
continuous-integration/drone/push Build is failing Details
3 months ago
Kentin BRONGNIART 9dcb541816 debut graph
3 months ago
Louis GUICHARD-MONTGUERS d735893b9d Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/Accueil.razor.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 7fe7395d9e Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/UserLogin.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 52b377fef8 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/User.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 8e700eeca2 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/Source.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 1c7397fe87 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/QuoteModel.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 6e4f1d8398 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/Quote.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 925528a922 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/QuizModel.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS d45847c03a Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/QuizModel.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 866156ca35 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/Quiz.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 9a71fea118 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/LoggerSaveStub.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 5a5d83fa02 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Model/Character.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS a06a14ffcc Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Converter/UserDto.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 45d8ba09ca Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Converter/UserDto.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS bfe46f272c Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Converter/DailyQuoteDto.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 0408cc51fe Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Converter/CommentaryDto.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS adc57b1ba9 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Converter/QuoteDto.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 02aa7a1b15 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/wwwroot/css/site.css'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 95db35940c Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/Error.cshtml.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS 6b6a859982 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/DeleteUser.razor.cs'
continuous-integration/drone/push Build is failing Details
3 months ago
Louis GUICHARD-MONTGUERS f6936c7caa Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Service/QuoteServiceLocal.cs'
continuous-integration/drone/push Build is passing Details
3 months ago
Louis GUICHARD-MONTGUERS 72e29790a3 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/Error.cshtml'
continuous-integration/drone/push Build is passing Details
3 months ago
Louis GUICHARD-MONTGUERS 72ff55a889 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/_Layout.cshtml'
continuous-integration/drone/push Build is passing Details
3 months ago
Louis GUICHARD-MONTGUERS 05b13c040a Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/Error.cshtml'
continuous-integration/drone/push Build is passing Details
3 months ago
Louis GUICHARD-MONTGUERS de00eac783 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/_Layout.cshtml'
continuous-integration/drone/push Build is passing Details
3 months ago
Louis GUICHARD-MONTGUERS 319ae73422 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/_Layout.cshtml'
continuous-integration/drone/push Build is passing Details
3 months ago
Louis GUICHARD-MONTGUERS d2188b3de8 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/ModifQuote.razor.cs'
continuous-integration/drone/push Build is passing Details
3 months ago
Louis GUICHARD-MONTGUERS 611de18362 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/Edit.razor.cs'
continuous-integration/drone/push Build is passing Details
3 months ago
Louis GUICHARD-MONTGUERS 5ad531d6bb Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/AddQuiz.razor.cs'
continuous-integration/drone/push Build is passing Details
3 months ago
Louis GUICHARD-MONTGUERS c358e55aa2 Mise à jour de 'WF-WebAdmin/WF-WebAdmin/Pages/ModifQuiz.razor.cs'
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER f6bd5d2fc1 ça ne marche pas mais je n'y arrive pas tant pis au moins la page est propre
continuous-integration/drone/push Build is passing Details
3 months ago
Leni BEAULATON 67b4094d81 Merge pull request 'trad Logs' (#32) from TradLog into master
continuous-integration/drone/push Build is passing Details
3 months ago
lebeaulato 5597617c48 trad Logs
continuous-integration/drone/push Build is passing Details
3 months ago
Kentin BRONGNIART ea6f8e9837 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
3 months ago
Kentin BRONGNIART 226af6cb8f Merge pull request 'Logs' (#31) from Logs into master
continuous-integration/drone/push Build is passing Details
3 months ago
kentin.brongniart 35badacd55 Merge de master sur login pour le merge
continuous-integration/drone/push Build is passing Details
3 months ago
kentin.brongniart 2d314b5a87 Logs complet
continuous-integration/drone/push Build is passing Details
3 months ago
Leni BEAULATON 6ea3c6e7f9 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
3 months ago
Leni BEAULATON 3cfc5fad2e Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
3 months ago
Leni BEAULATON 69a7d64a9f Merge pull request 'Docummentation' (#30) from Docummentation into master
continuous-integration/drone/push Build is passing Details
3 months ago
lebeaulato 1e89ed53c1 Merge branch 'Docummentation' of https://codefirst.iut.uca.fr/git/WhatTheFantasy/WF-WebAdmin into Docummentation
continuous-integration/drone/push Build is passing Details
3 months ago
lebeaulato 1707cfe514 Doc classe
3 months ago
kentin.brongniart b8af563ce9 merge de logs et de master
3 months ago
lebeaulato 7b71f7ea86 doc fait sur le code behind des pages
3 months ago
lebeaulato 6697bfc395 Correction drone
continuous-integration/drone/push Build is passing Details
3 months ago
lebeaulato e90dfa53f9 URL
continuous-integration/drone/push Build is failing Details
3 months ago
lebeaulato af56418753 Test URL
continuous-integration/drone/push Build is failing Details
3 months ago
Leni BEAULATON 2aa33effc9 Merge pull request 'SuppLogin' (#29) from SuppLogin into master
continuous-integration/drone/push Build is failing Details
3 months ago
lebeaulato f1ec7af13c Login retiré
continuous-integration/drone/push Build is passing Details
3 months ago
lebeaulato 8739576cec pb de URL corrigé
continuous-integration/drone/push Build is failing Details
3 months ago
lebeaulato f3b95d273d doc fait sur le code behind des pages
continuous-integration/drone/push Build is passing Details
3 months ago
Leni BEAULATON c0596f6810 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
3 months ago
lebeaulato 582bb13bcf problème de pare feu mais j'ai normalement retiré le login
continuous-integration/drone/push Build is passing Details
3 months ago
Kentin BRONGNIART 25963b2c38 Finition des logs (fonction créé a rajouter sur tous les bouton pour la création des logs / logs enregister et afficher dans la page logs)
3 months ago
Tommy NGUYEN 7ad7135804 Documentation réalisée
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 757643c47b roll back
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 6eef595777 roll back
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 21acb505ef roll back
continuous-integration/drone/push Build encountered an error Details
3 months ago
Maxime ROCHER 0af5d95878 deploiement 7
continuous-integration/drone/push Build was killed Details
3 months ago
Maxime ROCHER 37a0b06885 deploiement 7
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 5fbd65e3ba deploiement 6
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 0ba0670270 deploiement 5
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER d11ce9cbc9 deploiement 4
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 0875452004 deploiement 3
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 3b96126e7d deploiement 2
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 050d401443 deploiement
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER d83a99fec1 m
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 5a93a72b76 test deploy
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 0c6dd6026c test deploy
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER e77505db6d test deploy
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER fab75f2fe8 test deploy
continuous-integration/drone/push Build encountered an error Details
3 months ago
Maxime ROCHER 67252ff84f test deploy
continuous-integration/drone/push Build encountered an error Details
3 months ago
Maxime ROCHER 99db1ed7a2 Test CI 19
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER d3fe3e7323 Test CI 18
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 5a63b9916e Test CI 17
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER e052f95ad9 Test CI 16
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER fe0cef5afd Merge branch 'master' of https://codefirst.iut.uca.fr/git/WhatTheFantasy/WF-WebAdmin
continuous-integration/drone/push Build encountered an error Details
3 months ago
Maxime ROCHER 82a534d9c7 Test CI 15
3 months ago
Kevin MONDEJAR 356bff2072 Mise à jour de 'README.md'
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 10970ffd9a Test CI 14
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 6e40ec6ca9 Test CI 13
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 68483c3178 Test CI 12
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 9b1c324dfd Test CI 11
continuous-integration/drone/push Build is failing Details
3 months ago
Kentin BRONGNIART 6a9afeb6f4 Début Logs
3 months ago
Maxime ROCHER b9cb6fe4b3 Test CI 10
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 2b3e93121e Test CI 9
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 188c759f07 Test CI 8
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 7cf5395f14 Test CI 7
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER c4a7ae258a Test CI 6
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 4be7866a60 Test CI 5
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER d4dc0ce2e6 Test CI 4
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 24daff406c Test CI 3
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER daa004e9b2 Test CI 2
continuous-integration/drone/push Build encountered an error Details
3 months ago
Maxime ROCHER 18a4bfd070 Test CI
continuous-integration/drone/push Build encountered an error Details
3 months ago
Maxime ROCHER c31576b3cc retour a une version valable
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 21a6ba1179 retour a une version valable
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER 321dc6feb5 test CI 13
continuous-integration/drone/push Build encountered an error Details
3 months ago
Maxime ROCHER f0dce9458d test CI 12
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER bd925d8464 test CI 10
continuous-integration/drone/push Build is passing Details
3 months ago
Maxime ROCHER f71147c972 test CI 10
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 56d6cac2e2 test CI 9
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 9d64672696 test CI 8
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 193556acdb test CI 7
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 09552612b6 test CI 6
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 050fdcff63 test CI 5
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER cf739c6b2e test CI 4
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER 9603dbbb53 test CI 3
continuous-integration/drone/push Build is failing Details
3 months ago
Maxime ROCHER f75579fe99 test CI 2
3 months ago
Maxime ROCHER 9210407757 test CI
3 months ago
Leni BEAULATON c11f071714 Mise à jour de 'README.md'
3 months ago
Leni BEAULATON bb660e1ff8 Mise à jour de 'README.md'
3 months ago
Leni BEAULATON 9caa42b511 Merge pull request 'Traduction' (#28) from Traduction into master
3 months ago
lebeaulato 13f435d606 fin trad
3 months ago
lebeaulato 3a896aa3ce Trad AddQuiz et ValidQuote
3 months ago
Kevin MONDEJAR b58a71448d resolve build problem
3 months ago
Kevin MONDEJAR 37edad86db Merge pull request 'Service' (#26) from Service into master
3 months ago

@ -0,0 +1,71 @@
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 ]

@ -0,0 +1,27 @@
# 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,21 +60,22 @@ L'application devrait se lancer automatiquement dans votre navigateur par défau
# Blazor Apps (30 points)
🟨 En cours / ✅ Fait / ❌ Pas fait<br/><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'édition d'un élement avec validation (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/>
Utilisation IOC & DI (4 point)<br/>
🟨 Localisation & Globalisation (au moins deux langues) (1 point) <br/>
Utilisation de la configuration (1 point)<br/>
🟨 Logs (2 point)<br/>
Propreté du code (Vous pouvez vous servir de sonarqube) (2 point)<br/>
Utilisation IOC & DI (4 point)<br/>
Localisation & Globalisation (au moins deux langues) (1 point) <br/>
Utilisation de la configuration (1 point)<br/>
Logs (2 point)<br/>
🟨 Propreté du code (Vous pouvez vous servir de sonarqube) (2 point)<br/>
✅ IHM (Design global, placement des boutons, ...) (2 point)<br/>
✅ Emplacement du code (Pas de code dans les vues) (2 point)<br/>
# Documentation (10 points)
🟨Le Readme (2 points)<br/>
Description du fonctionnement de la solution client (illustrutration au niveau du code) (6 points)<br/>
✅Merge request (2 points)<br/>
Le Readme (2 points)<br/>
Description du fonctionnement de la solution client (illustrutration au niveau du code) (6 points)<br/>
Merge request (2 points)<br/>

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

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

@ -0,0 +1,23 @@
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;
}
}

@ -0,0 +1,46 @@
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
};
}
}
}

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

@ -4,9 +4,9 @@ namespace WF_WebAdmin.Converter
{
public class DailyQuoteExtension
{
public DailyQuoteDTO DailyQuoteToDTO(DailyQuote dq)
public DailyQuoteDto DailyQuoteToDto(DailyQuote dq)
{
DailyQuoteDTO dailyQuote = new DailyQuoteDTO(dq.Id);
DailyQuoteDto dailyQuote = new DailyQuoteDto(dq.Id);
return dailyQuote;
}
}

@ -3,7 +3,7 @@ using System;
namespace WF_WebAdmin.Converter
{
public class QuoteDTO
public class QuoteDto
{
public int Id { get; set; }
public string Content { get; set; }
@ -21,7 +21,7 @@ namespace WF_WebAdmin.Converter
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)
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;

@ -4,13 +4,13 @@ namespace WF_WebAdmin.Converter
{
public class QuoteExtension
{
public QuoteDTO QuoteToDTO(Quote q)
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);
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);
return quote;
}
public Quote DTOToQuote(QuoteDTO q)
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);
return quote;

@ -2,7 +2,7 @@
namespace WF_WebAdmin.Converter
{
public class UserDTO
public class UserDto
{
public string Image { get; set; }
public string Name { get; set; }
@ -10,9 +10,9 @@ namespace WF_WebAdmin.Converter
public DateTime DateCreation { get; set; }
public Boolean IsAdmin { get; set; }
public List<Commentary> Comments { get; set; }
public List<Commentary>? Comments { get; set; }
public UserDTO(string image, string name, string email, DateTime dateCreation)
public UserDto(string image, string name, string email, DateTime dateCreation)
{
this.Image = image;

@ -4,15 +4,15 @@ namespace WF_WebAdmin.Converter
{
public class UserExtension
{
public User UserToDTO(UserDTO u)
public User UserToDto(UserDto u)
{
User user = new User(u.Image, u.Name, u.Email, u.DateCreation,u.IsAdmin);
var user = new User(u.Image, u.Name, u.Email, u.DateCreation,u.IsAdmin);
return user;
}
public UserDTO DTOToUser(User u)
public UserDto DtoToUser(User u)
{
UserDTO user = new UserDTO(u.Image, u.Name, u.Email, u.DateCreation);
var user = new UserDto(u.Image ?? "default.png", u.Name ?? "Bob", u.Email ?? "bob@mail.com", u.DateCreation);
return user;
}
}

@ -3,6 +3,6 @@
public class Character
{
public int id_caracter { get; set; }
public string caracter { get; set; }
public string? caracter { get; set; }
}
}

@ -1,10 +1,12 @@
namespace WF_WebAdmin.Model
using System;
namespace WF_WebAdmin.Model
{
public class Commentary
{
//public int Id { get; set; }
//public int IdUser { get; set; }
public string Text { get; set; }
public int Id { get; set; }
public int IdUser { get; set; }
public DateTime DateCreation { get; set; }
public string Text { get; set; } = string.Empty;
}
}

@ -0,0 +1,24 @@
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);
}
}
}

@ -0,0 +1,14 @@
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;
}
}
}

@ -3,7 +3,7 @@ namespace WF_WebAdmin.Model
public class Quiz
{
public int Id { get; set; }
public string Question { get; set; }
public string? Question { get; set; }
public string AnswerA { get; set; }
public string AnswerB { get; set; }
public string AnswerC { get; set; }
@ -12,19 +12,6 @@ namespace WF_WebAdmin.Model
public bool IsValid { get; set; }
public string UserProposition { get; set; }
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;
}
public Quiz(int id, string question, string answerA, string answerB, string answerC, string answerD, string cAnswer)
{
Id = id;
@ -38,7 +25,5 @@ namespace WF_WebAdmin.Model
UserProposition = "Admin";
}
public Quiz() {}
}
}

@ -6,25 +6,25 @@ namespace WF_WebAdmin.Model
{
[Required]
[StringLength(200, ErrorMessage = "La question ne peut pas depasser les 200 caractère.")]
public string Question { get; set; }
public string? Question { get; set; }
[Required]
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")]
public string AnswerA { get; set; }
public string? AnswerA { get; set; }
[Required]
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")]
public string AnswerB { get; set; }
public string? AnswerB { get; set; }
[Required]
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")]
public string AnswerC { get; set; }
public string? AnswerC { get; set; }
[Required]
[StringLength(50, ErrorMessage = "La réponse ne peut pas depasser les 50 caractère.")]
public string AnswerD { get; set; }
public string? AnswerD { get; set; }
[Required]
public string CAnswer { get; set; }
public string? CAnswer { get; set; }
}
}

@ -28,42 +28,6 @@ namespace WF_WebAdmin.Model
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;
}
*/
}
}

@ -8,20 +8,20 @@ namespace WF_WebAdmin.Model
[Required]
[StringLength(300, ErrorMessage = "La citation ne peut pas dépasser les 300 caractère.")]
public string Content { get; set; }
public string? Content { get; set; }
public int Like { get; set; }
[Required]
[StringLength(2, ErrorMessage = "La langue ne peut pas dépasser 2 caractère.")]
public string Langue { get; set; }
public string? Langue { get; set; }
public string Charac { get; set; }
public string ImgPath { get; set; }
public string TitleSrc { get; set; }
public string? Charac { get; set; }
public string? ImgPath { get; set; }
public string? TitleSrc { get; set; }
public DateTime DateSrc { get; set; }
public string UserProposition { get; set; }
public string? UserProposition { get; set; }
public bool IsValid { get; set; }
}
}

@ -4,7 +4,7 @@
{
public int id_source { get; set; }
public string title { get; set; }
public string? title { get; set; }
public int date { get; set; }
}

@ -3,14 +3,14 @@
public class User
{
public int Id { get; set; }
public string Image { get; set; }
public string Name { get; set; }
public string Email { get; set; }
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 List<Commentary>? Comments { get; set; }
public User(string image, string name, string email, DateTime dateCreation, bool isAdmin)
{

@ -4,10 +4,10 @@ namespace WF_WebAdmin.Model
public class UserLogin
{
public int Id { get; set; }
public string Image { get; set; }
public string Name { get; set;}
public string? Image { get; set; }
public string? Name { get; set;}
public Boolean IsAdmin { get; set; }
public string Mdp { get; set; }
public string? Mdp { get; set; }
public UserLogin(int id,string image, string name, bool isAdmin, string mdp)
{
Id = id;

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

@ -1,6 +1,9 @@
using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using System;
using System.Security.Claims;
using System.Text.Json;
using WF_WebAdmin.Model;
@ -8,20 +11,76 @@ namespace WF_WebAdmin.Pages
{
public partial class Accueil
{
private Quote[] Dailyquote;
private Quote[]? Dailyquote;
[Inject]
public ILogger<Accueil>? Logger { get; set; }
[Inject]
public HttpClient Http { get; set; }
public HttpClient? Http { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
public NavigationManager? NavigationManager { get; set; }
[Inject]
public IStringLocalizer<Accueil> Localizer { get; set; }
public IStringLocalizer<Accueil>? Localizer { get; set; }
/// <summary>
/// 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>
protected override async Task OnInitializedAsync()
{
Dailyquote = await Http.GetFromJsonAsync<Quote[]>($"{NavigationManager.BaseUri}fake-dataDailyQuote.json");
var url = $"{NavigationManager.BaseUri}fake-dataDailyQuote.json";
try
{
var response = await Http.GetFromJsonAsync<Quote[]>(url);
Dailyquote = response ?? Array.Empty<Quote>(); // Assurer qu'on ne stocke pas null
}
catch (Exception ex)
{
Console.WriteLine($"Erreur lors du chargement des citations : {ex.Message}");
Dailyquote = Array.Empty<Quote>(); // Éviter une exception en cas d'erreur réseau
}
}
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"
<h3>Ajouter une Question</h3>
<h3>@Localizer["TitleAddQuiz"]</h3>
<EditForm Model="@QuizModel" OnValidSubmit="@HandleValidSubmit">
<EditForm Model="@_quizModel" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<p>
<label for="display-quest">
Question:
<InputText id="display-quest" @bind-Value="QuizModel.Question" />
@Localizer["TitleQuestion"]
<InputText id="display-quest" @bind-Value="_quizModel.Question" />
</label>
</p>
<p>
<label for="display-a">
Réponse A:
<InputText id="display-a" @bind-Value="QuizModel.AnswerA" />
@Localizer["AnswerA"]
<InputText id="display-a" @bind-Value="_quizModel.AnswerA" />
</label>
</p>
<p>
<label for="display-b">
Réponse B:
<InputText id="display-b" @bind-Value="QuizModel.AnswerB" />
@Localizer["AnswerB"]
<InputText id="display-b" @bind-Value="_quizModel.AnswerB" />
</label>
</p>
<p>
<label for="display-c">
Réponse C:
<InputText id="display-c" @bind-Value="QuizModel.AnswerC" />
@Localizer["AnswerC"]
<InputText id="display-c" @bind-Value="_quizModel.AnswerC" />
</label>
</p>
<p>
<label for="display-d">
Réponse D:
<InputText id="display-d" @bind-Value="QuizModel.AnswerD" />
@Localizer["AnswerD"]
<InputText id="display-d" @bind-Value="_quizModel.AnswerD" />
</label>
</p>
<p>
<label for="cA">
Bonne réponse:
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("A", e.Value))" /> A
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("B", e.Value))" /> B
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("C", e.Value))" /> C
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("D", e.Value))" /> D
@Localizer["GoodAnswer"]
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("A"))" /> A
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("B"))" /> B
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("C"))" /> C
<input name="cA" type="radio" @onchange="@(e => OnCAwnserChange("D"))" /> D
</label>
</p>
<button type="submit">Submit</button>
<button type="submit">@Localizer["Submit"]</button>
</EditForm>

@ -3,6 +3,9 @@ using WF_WebAdmin.Service;
using WF_WebAdmin.Model;
using Microsoft.AspNetCore.Mvc;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Security.Claims;
namespace WF_WebAdmin.Pages
@ -10,71 +13,116 @@ namespace WF_WebAdmin.Pages
public partial class AddQuiz
{
[Inject]
private IQuizService quizService { get; set; }
public ILogger<AddQuiz>? Logger { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
public IStringLocalizer<AddQuiz>? Localizer { get; set; }
private QuizModel QuizModel = new();
[Inject]
private IQuizService? QuizService { get; set; }
private async void HandleValidSubmit()
{
[Inject]
public NavigationManager? NavigationManager { get; set; }
private readonly QuizModel _quizModel = new();
/// <summary>
/// Handles the valid submission of a quiz form.
/// This method is triggered when the form is successfully validated and the user submits the quiz data.
/// It retrieves the current quiz count, increments it, and then adds a new quiz entry to the quiz service.
/// Finally, it navigates to the "modifquiz" page.
/// </summary>
private async Task HandleValidSubmit()
{
// Declare a variable to hold the ID of the new quiz.
int id;
id = await quizService.getNbQuiz();
// Get the current number of quizzes from the quiz service.
id = await QuizService.getNbQuiz();
// Increment the quiz ID for the new quiz.
id++;
await quizService.addQuiz(new Quiz(
id,
validateInformation(QuizModel.Question),
validateInformation(QuizModel.AnswerA),
validateInformation(QuizModel.AnswerB),
validateInformation(QuizModel.AnswerC),
validateInformation(QuizModel.AnswerD),
validateReponse(QuizModel.CAnswer)
// Create a new quiz and add it using the quiz service.
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Creation of the {_quizModel.Question} question");
await QuizService.addQuiz(new Quiz(
id, // New quiz ID
ValidateInformation(_quizModel.Question), // Validated question
ValidateInformation(_quizModel.AnswerA), // Validated answer A
ValidateInformation(_quizModel.AnswerB), // Validated answer B
ValidateInformation(_quizModel.AnswerC), // Validated answer C
ValidateInformation(_quizModel.AnswerD), // Validated answer D
ValidateReponse(_quizModel.CAnswer) // Validated correct answer
));
// Navigate to the "modifquiz" page after adding the quiz.
NavigationManager.NavigateTo("modifquiz");
}
private void OnCAwnserChange(string item, object checkedValue)
/// <summary>
/// Handles the change of the correct answer for the quiz.
/// This method is triggered when the user selects or changes the correct answer for the quiz question.
/// It updates the QuizModel's Correct Answer property with the selected answer.
/// </summary>
/// <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>
private void OnCAwnserChange(string item)
{
QuizModel.CAnswer = item;
// Update the correct answer in the QuizModel with the selected answer.
_quizModel.CAnswer = item;
}
private static string validateInformation(string item)
/// <summary>
/// Validates the provided string item.
/// This method is used to validate input data, but the validation logic is not yet implemented.
/// </summary>
/// <param name="item">The string input to be validated.</param>
/// <returns>
/// Returns the input string as it is for now. The validation logic is yet to be implemented.
/// </returns>
private string ValidateInformation(string? item)
{
return item; // VALIDATION A FAIRE
return string.IsNullOrWhiteSpace(item) ? "Valeur par défaut" : item;
}
private static string validateReponse(string 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)
{
try
// Check if the item is null or empty
if (string.IsNullOrEmpty(item))
{
if (!string.IsNullOrEmpty(item))
{
switch (item)
{
case "A":
break;
case "B":
break;
case "C":
break;
case "D":
break;
default:
throw new InvalidDataException("Invalid item (validateReponse) : item must be A,B,C or D " + item + "give.");
}
}
else
{
throw new ArgumentNullException("Invalid item (validateReponse): null given.");
}
return item;
// Throw exception if the item is null or empty
throw new ArgumentNullException(nameof(item), "The item cannot be null or empty.");
}
catch (Exception ex)
// Validate that the item is one of the allowed values: A, B, C, or D
switch (item)
{
return "A"; //Default Argument
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;
}
}
}

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

@ -1,6 +1,7 @@
using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
using WF_WebAdmin.Model;
@ -10,119 +11,204 @@ namespace WF_WebAdmin.Pages
{
public partial class DeleteUser
{
[Inject]
public ILogger<DeleteUser>? Logger { get; set; }
private List<User>? _users;
private bool showDeletePopup = false;
private bool showModifyPopup = false;
private List<User> users;
private User userToDelete = null;
private User selectedUser;
private bool showPopupDelete = false;
private bool showPopupAdmin = false;
private User userToAdmin = null;
private int MaxValue = 5;
private int totalItem;
private int page = 1;
private bool _showModifyPopup = false;
private User? _userToDelete = null;
private User? _selectedUser;
private bool _showPopupDelete = false;
private bool _showPopupAdmin = false;
private User? _userToAdmin = null;
private const int MaxValue = 5;
private int _totalItem;
private int _page = 1;
[Inject]
public HttpClient Http { get; set; }
public HttpClient? Http { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
public NavigationManager? NavigationManager { get; set; }
[Inject]
private IUserService userService { get; set; }
private IUserService? UserService { get; set; }
[Inject]
public IStringLocalizer<DeleteUser> Localizer { get; set; }
public IStringLocalizer<DeleteUser>? Localizer { get; set; }
/// <summary>
/// This method is called when the component is initialized.
/// 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>
protected override async Task OnInitializedAsync()
{
users = await userService.getSomeUser(MaxValue, 1);
// Retrieve a list of users using the user service. The number of users and page number are specified.
// MaxValue determines how many users to retrieve, and '1' refers to the first page of results.
_users = await UserService.getSomeUser(MaxValue, 1);
}
/// <summary>
/// Handles the event when data is read in the data grid.
/// 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>
/// <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)
{
// If the cancellation token is requested, exit the method without processing the request.
if (e.CancellationToken.IsCancellationRequested)
{
return;
}
// Fetch a page of users from the user service using the page size and page number provided by the event arguments.
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)
{
totalItem = await userService.getNbUser();
users = new List<User>(response.ToArray());
page = e.Page;
_totalItem = await UserService.getNbUser(); // Get the total number of users
_users = new List<User>(response.ToArray()); // Store the retrieved users in the users list
_page = e.Page; // Update the current page number
}
}
// ------- Popup remove user -------
private void ShowConfirmation(User user)
/// <summary>
/// Displays a confirmation popup to confirm the deletion of a user.
/// 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>
/// <param name="user">The user to be deleted, which is passed to the method for confirmation.</param>
private void ShowConfirmation(User? user)
{
userToDelete = user;
showPopupDelete = true;
// Set the user to be deleted and show the confirmation popup.
_userToDelete = user; // Store the user to be deleted in a variable
_showPopupDelete = true; // Display the confirmation popup
}
private void ShowModifyConfirmation(User user)
/// <summary>
/// Displays a confirmation popup for modifying a user's information.
/// 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>
/// <param name="user">The user whose information is to be modified, passed to the method for confirmation.</param>
private void ShowModifyConfirmation(User? user)
{
// Afficher la modale et mémoriser l'utilisateur à supprimer
selectedUser = user;
showModifyPopup = true;
// Set the selected user and show the modification confirmation popup.
_selectedUser = user; // Store the user to be modified
_showModifyPopup = true; // Display the confirmation popup for modification
}
/// <summary>
/// Removes the specified user from the system.
/// 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>
private async Task RemoveUser()
{
if (userToDelete != null)
// Check if there is a user to delete
if (_userToDelete != null)
{
await userService.removeUser(userToDelete);
// Remove the selected user from the system using the user service
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Delete user {_userToDelete.Name}");
await UserService.removeUser(_userToDelete);
// Close the confirmation popup after the deletion
ClosePopup();
var response = await userService.getSomeUser(MaxValue, page);
users = new List<User>(response.ToArray());
// 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>
/// Modifies the selected user's information.
/// 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>
private async Task ModifyUser()
{
await userService.updateUser(selectedUser);
// Update the selected user's information using the user service
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Modification of user {_selectedUser.Name}");
await UserService.updateUser(_selectedUser);
// Close the modification popup after the update is complete
ClosePopup();
}
/// <summary>
/// Closes all open popups in the UI by setting their visibility flags to false.
/// 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>
private void ClosePopup()
{
showDeletePopup = false;
showModifyPopup = false;
showPopupDelete = false;
showPopupAdmin = false;
// Set all popup visibility flags to false to hide the popups
_showModifyPopup = false; // Close the modify confirmation popup
_showPopupDelete = false; // Close any additional delete popups
_showPopupAdmin = false; // Close the admin-related popup (if any)
}
// ------- Popup admin -------
private void ShowConfirmationAdmin(User user)
/// <summary>
/// Displays a confirmation popup to confirm the promotion of a user to admin status.
/// 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>
/// <param name="user">The user to be promoted to admin, passed to the method for confirmation.</param>
private void ShowConfirmationAdmin(User? user)
{
userToAdmin = user;
showPopupAdmin = true;
// Set the user to be promoted to admin and show the confirmation popup.
_userToAdmin = user; // Store the user to be promoted
_showPopupAdmin = true; // Display the confirmation popup for admin promotion
}
/// <summary>
/// Toggles the admin status of the selected user.
/// 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>
private async Task setAdmin()
{
if (!userToAdmin.IsAdmin)
// Check if the user is not already an admin
if (!_userToAdmin.IsAdmin)
{
userToAdmin.IsAdmin = true;
await userService.updateUser(userToAdmin);
ClosePopup();
// Promote the user to admin
LoggerSaveStub.Log(Logger, LogLevel.Information, $"User {_userToAdmin.Name} is now administrator");
_userToAdmin.IsAdmin = true;
await UserService.updateUser(_userToAdmin); // Update the user status in the service
ClosePopup(); // Close the confirmation popup
}
else
{
userToAdmin.IsAdmin = false;
await userService.updateUser(userToAdmin);
ClosePopup();
// Demote the user from admin to normal user
LoggerSaveStub.Log(Logger, LogLevel.Information, $"User {_userToAdmin.Name} is no longer an administator");
_userToAdmin.IsAdmin = false;
await UserService.updateUser(_userToAdmin); // Update the user status in the service
ClosePopup(); // Close the confirmation popup
}
}

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

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components;
using System.Security.Claims;
using WF_WebAdmin.Model;
using WF_WebAdmin.Service;
@ -10,56 +11,97 @@ namespace WF_WebAdmin.Pages
public int Id { get; set; }
[Inject]
private IQuoteService quoteService { get; set; }
public ILogger<Edit>? Logger { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
private IQuoteService? quoteService { get; set; }
private Quote q{ get; set; }
[Inject]
public NavigationManager? NavigationManager { get; set; }
private Quote? Q { get; set; }
private QuoteModel _quoteModel = new();
private QuoteModel quoteModel = new();
private List<Character> _charac = new List<Character>();
private List<Character> charac = new List<Character>();
private List<Source> _src = new List<Source>();
private List<Source> src = new List<Source>();
/// <summary>
/// Asynchronously initializes the component by loading data related to a quote.
/// This method fetches a specific quote based on the provided ID and populates the `quoteModel`
/// 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>
protected override async Task OnInitializedAsync()
{
q = await quoteService.getOnequote(Id);
quoteModel.Content = q.Content;
quoteModel.Langue = q.Langue;
quoteModel.Charac = q.Charac;
quoteModel.TitleSrc = q.TitleSrc;
quoteModel.Id = q.Id;
quoteModel.Like = q.Like;
quoteModel.ImgPath = q.ImgPath;
quoteModel.DateSrc = q.DateSrc;
quoteModel.UserProposition = q.UserProposition;
quoteModel.IsValid = q.IsValid;
charac = await quoteService.getChar();
src = await quoteService.getSrc();
}
// Fetch the quote data based on the provided ID.
Q = await quoteService.getOnequote(Id);
protected async void HandleValidSubmit()
{
q.Content = quoteModel.Content;
q.Langue = quoteModel.Langue;
q.TitleSrc = quoteModel.TitleSrc;
q.Charac = quoteModel.Charac;
await quoteService.updateQuote(q);
NavigationManager.NavigateTo("modifquote");
// Populate the quoteModel with the data from the retrieved quote.
_quoteModel.Content = Q.Content;
_quoteModel.Langue = Q.Langue;
_quoteModel.Charac = Q.Charac;
_quoteModel.TitleSrc = Q.TitleSrc;
_quoteModel.Id = Q.Id;
_quoteModel.Like = Q.Like;
_quoteModel.ImgPath = Q.ImgPath;
_quoteModel.DateSrc = Q.DateSrc;
_quoteModel.UserProposition = Q.UserProposition;
_quoteModel.IsValid = Q.IsValid;
// Fetch additional data related to the quote, such as character and source.
_charac = await quoteService.getChar();
_src = await quoteService.getSrc();
}
private void OnlangChange(string item, object checkedValue)
/// <summary>
/// Handles the submission of a valid form for updating a quote.
/// This method takes the data from `quoteModel`, updates the selected quote (`q`) with the new values,
/// and then calls the `quoteService.updateQuote` method to persist the changes.
/// After updating, it navigates to the "modifquote" page.
/// </summary>
protected async Task HandleValidSubmit()
{
if(item == "fr" || item == "en")
if (Q == null)
{
quoteModel.Langue = item;
LoggerSaveStub.Log(Logger, LogLevel.Error, "Quote is null.");
return;
}
}
// Update the properties of the selected quote (`q`) with the data from `quoteModel`.
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Editing the quote {Q.Content}");
Q.Content = _quoteModel.Content ?? "Content";
Q.Langue = _quoteModel.Langue ?? "EN";
Q.TitleSrc = _quoteModel.TitleSrc ?? "Something";
Q.Charac = _quoteModel.Charac ?? "Someone";
// Call the quote service to update the quote in the data source.
await quoteService.updateQuote(Q);
// Navigate to the "modifquote" page after updating the quote.
NavigationManager.NavigateTo("modifquote");
}
/// <summary>
/// Handles the language change event for the quote.
/// This method updates the `Langue` property of the `quoteModel` based on the selected language (`item`).
/// 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>
/// <param name="checkedValue">The checked value (unused in this method but may be used for other purposes).</param>
private void OnlangChange(string item)
{
// Check if the selected language is either "fr" or "en"
if (item == "fr" || item == "en")
{
// Update the Langue property of the quoteModel with the selected language
_quoteModel.Langue = item;
}
}
}
}

@ -7,7 +7,7 @@
<head>
<meta charset="utf-8" />
<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/site.css" rel="stylesheet" asp-append-version="true" />
</head>
@ -27,12 +27,12 @@
<h3>Development Mode</h3>
<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>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
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.
</p>
</div>

@ -6,6 +6,7 @@ namespace WF_WebAdmin.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
@ -14,13 +15,26 @@ namespace WF_WebAdmin.Pages
private readonly ILogger<ErrorModel> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="ErrorModel"/> class.
/// This constructor is used to inject the <see cref="ILogger{ErrorModel}"/> into the model for logging purposes.
/// </summary>
/// <param name="logger">An instance of the <see cref="ILogger{ErrorModel}"/> used for logging error-related information.</param>
public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}
/// <summary>
/// Handles the GET request for the page or endpoint.
/// This method retrieves the request ID for tracing purposes, using the `Activity.Current?.Id`
/// if it's available, or the `HttpContext.TraceIdentifier` as a fallback if no current activity ID is present.
/// </summary>
public void OnGet()
{
// Retrieve the current request ID for tracing
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}

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

@ -15,7 +15,7 @@ namespace WF_WebAdmin.Pages
private UserLogin userLogin = new UserLogin();
[Inject]
public UserLogin uLogin { get; set; }
public UserLogin uLogin { get; set; }
private string ErrorConnexion;
@ -26,45 +26,63 @@ namespace WF_WebAdmin.Pages
[Inject]
public NavigationManager NavigationManager { get; set; }
/// <summary>
/// Asynchronously initializes the component by loading user login data.
/// 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>
protected override async Task OnInitializedAsync()
{
// Fetch user login data from the specified JSON file.
usersConnexion = await Http.GetFromJsonAsync<List<UserLogin>>($"{NavigationManager.BaseUri}fake-dataUserLogin.json");
}
/// <summary>
/// Validates the login credentials of the user.
/// 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>
public void validlogin()
{
// Check if both the username and password are provided
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)
{
if(userLogin.Name == user.Name && userLogin.Mdp == user.Mdp)
// Check if the username and password match the user credentials
if (userLogin.Name == user.Name && userLogin.Mdp == user.Mdp)
{
if(user.IsAdmin)
// Check if the user is an admin
if (user.IsAdmin)
{
// If the user is an admin, store their information and navigate to the home page
uLogin.Id = userLogin.Id;
uLogin.Name = user.Name;
uLogin.Image = user.Image;
// Redirect to the homepage
NavigationManager.NavigateTo(NavigationManager.BaseUri + "accueil");
return;
}
else
{
// If the user is not an admin, display an error message
ErrorConnexion = "Connexion échouée, le nom ou le mot de passe sont incorrectes";
}
}
else
{
// If credentials do not match, set the error message
ErrorConnexion = "Connexion échouée, le nom ou le mot de passe sont incorrectes";
}
}
}
}
}
}

@ -0,0 +1,31 @@
@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>
}

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

@ -1,5 +1,7 @@
using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using System.Security.Claims;
using WF_WebAdmin.Model;
using WF_WebAdmin.Service;
@ -21,63 +23,140 @@ namespace WF_WebAdmin.Pages
private int page = 1;
[Inject]
public ILogger<ModifQuiz>? Logger { get; set; }
[Inject]
public IStringLocalizer<ModifQuiz> Localizer { get; set; }
[Inject]
public IQuizService QuizService { get; set; }
/// <summary>
/// Handles the data reading event for a data grid, fetching quiz data based on the specified page and page size.
/// 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>
/// <param name="e">The event arguments containing pagination details such as page size and page number.</param>
private async Task OnReadData(DataGridReadDataEventArgs<Quiz> e)
{
// Check if the cancellation token has been requested
if (e.CancellationToken.IsCancellationRequested)
{
return;
}
// Fetch the quiz data for the specified page and page size
var response = await QuizService.getSommeQuiz(e.PageSize, e.Page);
// If cancellation hasn't been requested, process the data
if (!e.CancellationToken.IsCancellationRequested)
{
// Get the total number of quizzes for pagination purposes
totalItem = await QuizService.getNbQuiz();
// Update the quiz data for the current page
quiz = response.ToArray();
// Update the current page number
page = e.Page;
}
}
/// <summary>
/// Handles the event when the "Edit" button is clicked for a quiz.
/// 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>
/// <param name="quiz">The quiz object that was clicked for editing.</param>
private void OnEditButtonClicked(Quiz quiz)
{
// If the quiz is null, return early
if (quiz == null) return;
// Set the selected quiz to the one clicked by the user
selectedQuiz = quiz;
// Show the modal or UI for editing the 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>
private void ClosePopup()
{
// Hide the edit quiz popup
showEditQuiz = false;
// Hide the delete confirmation popup
showPopupDelete = false;
// Reset the selected quiz to null
selectedQuiz = null;
}
/// <summary>
/// Edits the selected quiz by updating it in the quiz service.
/// This method asynchronously sends the updated quiz data to the service for persistence.
/// After updating the quiz, it clears the selected quiz and closes any open popups.
/// </summary>
private async Task EditQuiz()
{
// Update the quiz in the service
LoggerSaveStub.Log(Logger, LogLevel.Information, $"Editing the question {selectedQuiz.Question}");
await QuizService.updateQuiz(selectedQuiz);
// Clear the selected quiz after successful update
selectedQuiz = null;
// Close the popups after the edit operation
ClosePopup();
}
/// <summary>
/// Handles the event when the delete action is triggered for a quiz.
/// This method sets the selected quiz to the one passed as a parameter and shows the delete confirmation popup.
/// </summary>
/// <param name="q">The quiz to be deleted.</param>
private void OnDelete(Quiz q)
{
// Set the selected quiz to the one passed in
selectedQuiz = q;
// Show the delete confirmation popup
showPopupDelete = true;
}
private async void RemoveQuote()
/// <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();
}
showPopupDelete= false;
// Close the delete confirmation popup
showPopupDelete = false;
}
}
}

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

@ -1,5 +1,8 @@
using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Security.Claims;
using WF_WebAdmin.Model;
using WF_WebAdmin.Service;
@ -21,22 +24,43 @@ namespace WF_WebAdmin.Pages
private int page = 1;
[Inject]
public ILogger<ModifQuote>? Logger { get; set; }
[Inject]
public IStringLocalizer<ModifQuote> Localizer { get; set; }
[Inject]
public IQuoteService QuoteService { get; set; }
/// <summary>
/// Handles the data reading event for a data grid, fetching quote data based on the specified page and page size.
/// This method makes an asynchronous call to retrieve a specific page of quotes and updates the `quotes` list and pagination details.
/// If the cancellation token is requested, it exits early without making further calls or updates.
/// </summary>
/// <param name="e">The event arguments containing pagination details such as page size and page number.</param>
private async Task OnReadData(DataGridReadDataEventArgs<Quote> e)
{
// Check if the cancellation token has been requested
if (e.CancellationToken.IsCancellationRequested)
{
return;
}
// Fetch the quote data for the specified page and page size
var response = await QuoteService.getSomeQuote(e.PageSize, e.Page);
// If cancellation hasn't been requested, process the data
if (!e.CancellationToken.IsCancellationRequested)
{
// Get the total number of quotes for pagination purposes
totalItem = await QuoteService.getNbQuote();
// Update the quotes data for the current page
quotes = response.ToArray();
// Update the current page number
page = e.Page;
}
}
@ -48,10 +72,16 @@ namespace WF_WebAdmin.Pages
showEditQuote = true;
}*/
/// <summary>
/// Closes the open popups and resets any related states.
/// This method hides the delete confirmation popup and clears the selected quote.
/// </summary>
private void ClosePopup()
{
/*showEditQuote = false;*/
// Hide the delete confirmation popup
showPopupDelete = false;
// Reset the selected quote to null
selectedQuote = null;
}
@ -62,22 +92,44 @@ namespace WF_WebAdmin.Pages
ClosePopup();
}*/
/// <summary>
/// Handles the event when the delete action is triggered for a quote.
/// This method sets the selected quote to the one passed as a parameter and displays the delete confirmation popup.
/// </summary>
/// <param name="q">The quote that is being deleted.</param>
private void OnDelete(Quote q)
{
// Set the selected quote to the one passed in
selectedQuote = q;
// Display the delete confirmation popup
showPopupDelete = true;
}
private async void RemoveQuote()
/// <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);
selectedQuote= null;
// 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();
}
showPopupDelete= false;
// Close the delete confirmation popup
showPopupDelete = false;
}
}
}

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

@ -1,5 +1,8 @@
using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Security.Claims;
using WF_WebAdmin.Model;
using WF_WebAdmin.Service;
@ -8,7 +11,11 @@ namespace WF_WebAdmin.Pages
public partial class ValidQuiz
{
private List<Quiz> quizzes;
[Inject]
public ILogger<ValidQuiz>? Logger { get; set; }
[Inject]
public IStringLocalizer<ValidQuiz> Localizer { get; set; }
[Inject]
public HttpClient Http { get; set; }
@ -19,36 +26,74 @@ namespace WF_WebAdmin.Pages
[Inject]
public IQuizService QuizService { get; set; }
/// <summary>
/// Initializes the component asynchronously by fetching the quizzes that need validation.
/// This method retrieves a list of quizzes from the `QuizService` that are pending validation when the component is initialized.
/// </summary>
protected override async Task OnInitializedAsync()
{
// Fetch quizzes that need validation
quizzes = await QuizService.getQuizzesToValidate();
}
/// <summary>
/// Handles the event when the "Validate" button is clicked for a quiz.
/// This method calls the `ValidateQuiz` method, passing the specified quiz for validation.
/// </summary>
/// <param name="quiz">The quiz that is being validated.</param>
private void OnValidButton(Quiz quiz)
{
// Call the ValidateQuiz method to validate the quiz
ValidateQuiz(quiz);
}
/// <summary>
/// Validates the specified quiz by setting its `IsValid` property to true and updating its state in the service.
/// 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>
/// <param name="quiz">The quiz that is being validated.</param>
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!");
// Create a new quiz instance (or modify the existing one)
Quiz newQuiz = quiz;
newQuiz.IsValid = true;
// Mis à jour de l'état du quiz
// Update the quiz state in the QuizService
QuizService.updateQuiz(quiz);
}
/// <summary>
/// Handles the event when the "Reject" button is clicked for a quiz.
/// This method calls the `RejectQuiz` method, passing the specified quiz to be rejected.
/// </summary>
/// <param name="quiz">The quiz that is being rejected.</param>
private void OnRejectButton(Quiz quiz)
{
// Call the RejectQuiz method to reject the quiz
RejectQuiz(quiz);
}
/// <summary>
/// Rejects the specified quiz by logging a rejection message and removing it from the QuizService.
/// 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>
/// <param name="quiz">The quiz that is being rejected.</param>
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!");
// Remove the rejected quiz from the QuizService
QuizService.removeQuiz(quiz.Id);
}
}

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

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.Extensions.Localization;
namespace WF_WebAdmin.Pages
{
@ -13,14 +14,23 @@ namespace WF_WebAdmin.Pages
{
private Quote[] quotes;
[Inject]
public IStringLocalizer<ValidQuote> Localizer { get; set; }
[Inject]
public HttpClient Http { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
/// <summary>
/// Initializes the component asynchronously by fetching a list of quotes from a JSON file.
/// This method makes an asynchronous HTTP request to retrieve an array of `Quote` objects from a specified JSON file
/// located at the base URI, and then assigns the result to the `quotes` variable.
/// </summary>
protected override async Task OnInitializedAsync()
{
// 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,4 +1,13 @@
@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
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
@ -6,3 +15,7 @@
}
<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,10 +1,20 @@
@using Microsoft.AspNetCore.Components.Web
@*
File: _Layout.cshtml
Project: WF_WebAdmin
Description: ...
Author: What The Fantasy
Created: 10/02/2025
Last Modified: 10/02/2025
*@
@using Microsoft.AspNetCore.Components.Web
@namespace WF_WebAdmin.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<title>layout</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="~/" />
@ -24,7 +34,7 @@
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a href="" class="reload">reload</a>
<a class="dismiss">🗙</a>
</div>

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

@ -0,0 +1,144 @@
<?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>

@ -0,0 +1,144 @@
<?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>

@ -0,0 +1,135 @@
<?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>

@ -0,0 +1,135 @@
<?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>

@ -0,0 +1,171 @@
<?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>

@ -0,0 +1,171 @@
<?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>

@ -0,0 +1,159 @@
<?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>

@ -0,0 +1,159 @@
<?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>

@ -0,0 +1,153 @@
<?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>

@ -0,0 +1,153 @@
<?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>

@ -0,0 +1,153 @@
<?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>

@ -0,0 +1,153 @@
<?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>

@ -0,0 +1,32 @@
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;
}
}
}

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

@ -0,0 +1,17 @@
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();
}
}

@ -8,15 +8,15 @@ namespace WF_WebAdmin.Service
public Task removeQuote(Quote quote);
public Task validQuote(Quote quote);
public Task validQuote(Quote? quote);
public Task updateQuote(Quote quote);
public Task updateQuote(Quote? quote);
public Task<List<Quote>> getAllQuote();
public Task<List<Quote>> getSomeQuote(int nb, int page);
public Task<Quote> getOnequote(int id);
public Task<Quote?> getOnequote(int id);
public Task<List<Quote>> reserchQuote(string reserch, List<string> argument);

@ -6,15 +6,15 @@ namespace WF_WebAdmin.Service
{
public Task removeUser(User user);
public Task updateRole(User user);
public Task updateRole(User? user);
public Task downgradeRole(User user);
public Task downgradeRole(User? user);
public Task updateUser(User user);
public Task updateUser(User? user);
public Task<List<User>> getAllUser();
public Task<List<User>> getSomeUser(int nb, int page);
public Task<List<User>?> getSomeUser(int nb, int page);
public Task<User> getOneUser(int id);

@ -0,0 +1,67 @@
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);
}
}
}
}

@ -7,12 +7,35 @@ public class QuizServiceStub: IQuizService
{
private readonly string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake_data_quiz.json");
/// <summary>
/// Asynchronously saves a list of quiz objects to a JSON file.
/// </summary>
/// <param name="quizzes">A list of <see cref="Quiz"/> objects to be serialized and saved.</param>
/// <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 });
await File.WriteAllTextAsync(_jsonFilePath, json);
}
/// <summary>
/// Asynchronously adds a new quiz to the list of quizzes and saves the updated list to a JSON file.
/// </summary>
/// <param name="quiz">The <see cref="Quiz"/> object to be added to the list of quizzes.</param>
/// <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();
@ -21,6 +44,17 @@ public class QuizServiceStub: IQuizService
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();
@ -39,6 +73,17 @@ public class QuizServiceStub: IQuizService
}
}
/// <summary>
/// Asynchronously removes a quiz from the list of quizzes by its ID and saves the updated list to a JSON file.
/// </summary>
/// <param name="id">The ID of the <see cref="Quiz"/> to be removed.</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 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();
@ -55,6 +100,16 @@ public class QuizServiceStub: IQuizService
throw new NotImplementedException();
}
/// <summary>
/// Asynchronously retrieves the list of quizzes from a JSON file.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a <see cref="List{Quiz}"/> result containing the quizzes.</returns>
/// <remarks>
/// 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))
@ -67,40 +122,81 @@ public class QuizServiceStub: IQuizService
return JsonSerializer.Deserialize<List<Quiz>>(json) ?? new List<Quiz>();
}
/// <summary>
/// Asynchronously retrieves the list of quizzes that are marked as invalid and need validation.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a <see cref="List{Quiz}"/> result containing quizzes that are not valid.</returns>
/// <remarks>
/// 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 no quizzes are invalid, an empty list will be returned.
/// </remarks>
public async Task<List<Quiz>> getQuizzesToValidate()
{
var quizzes = await getQuizzes();
return quizzes.Where(quiz => quiz.IsValid == false).ToList();
return quizzes.Where(quiz => !quiz.IsValid).ToList();
}
/// <summary>
/// Asynchronously retrieves a specific quiz by its ID from the list of quizzes.
/// </summary>
/// <param name="id">The ID of the <see cref="Quiz"/> to retrieve.</param>
/// <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>
/// <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)
if (q == null)
{
return q;
throw new KeyNotFoundException($"Quiz with ID {id} not found.");
}
return null;
return q;
}
/// <summary>
/// Asynchronously retrieves a paginated list of quizzes, returning a specific number of quizzes for the given page.
/// </summary>
/// <param name="nb">The number of quizzes to retrieve per page.</param>
/// <param name="page">The page number to retrieve, where the first page is 1.</param>
/// <returns>A task representing the asynchronous operation, with a <see cref="List{Quiz}"/> result containing the quizzes for the specified page.</returns>
/// <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 ((page - 1) * nb + nb > data.Count)
{
if(nb > data.Count())
if (nb > data.Count)
{
return data.GetRange(0, data.Count()-1);
return data.GetRange(0, data.Count - 1);
}
return data.GetRange(data.Count() - nb, nb);
return data.GetRange(data.Count - nb, nb);
}
return data.GetRange((page - 1) * nb, nb);
}
/// <summary>
/// Asynchronously retrieves the total number of quizzes in the list.
/// </summary>
/// <returns>A task representing the asynchronous operation, with an <see cref="int"/> result containing the total number of quizzes.</returns>
/// <remarks>
/// 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.
/// </remarks>
public async Task<int> getNbQuiz()
{
var data = await getQuizzes();
return data.Count;
}
}

@ -6,15 +6,25 @@ namespace WF_WebAdmin.Service
{
public class QuoteServiceLocal: IQuoteService
{
private readonly string? _connectionString = "Host=localhost;Port=5432;Username=loguichard3;Password=Reglisse15.;Database=dbloguichard3";
public async Task<QuoteDTO> AddQuoteAsync(Quote quote)
private readonly string? _connectionString = "Host=localhost;Port=5432;Username=;Password=;Database=";
/// <summary>
/// Asynchronously adds a new quote to the database and returns the corresponding <see cref="QuoteDto"/>.
/// </summary>
/// <param name="quote">The <see cref="Quote"/> object to be added to the database.</param>
/// <returns>A task representing the asynchronous operation, with a <see cref="QuoteDto"/> result containing the added quote's data.</returns>
/// <remarks>
/// This method converts the provided <see cref="Quote"/> object into a <see cref="QuoteDto"/> using <see cref="QuoteExtension"/>.
/// 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.
/// Error handling is in place to catch any issues during the database insertion process, with the exception message logged in case of failure.
/// </remarks>
public async Task<QuoteDto> AddQuoteAsync(Quote? quote)
{
QuoteExtension extension = new QuoteExtension();
QuoteDTO quoteDTO = extension.QuoteToDTO(quote);
QuoteDto quoteDTO = extension.QuoteToDTO(quote);
// Utilisation de NpgsqlConnection pour PostgreSQL
using (var connection = new NpgsqlConnection(_connectionString))
@ -28,7 +38,7 @@ namespace WF_WebAdmin.Service
/*
// Ajouter des paramètres à la commande
Ajouter des paramètres à la commande
command.Parameters.AddWithValue("@content", quote.Content);
command.Parameters.AddWithValue("@langue", quote.Langue);
command.Parameters.AddWithValue("@reason", "À vérifier"); // Vous pouvez changer ça si nécessaire
@ -64,35 +74,72 @@ namespace WF_WebAdmin.Service
}
public Task RemoveQuote(Quote quote)
/// <summary>
/// Asynchronously handles the removal of a quote and returns the corresponding <see cref="QuoteDto"/>.
/// </summary>
/// <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>
/// <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`,
/// it currently only converts the quote to a DTO and does not actually perform any database removal operation.
/// You may need to implement additional logic to remove the quote from the database.
/// </remarks>
public Task RemoveQuote(Quote? quote)
{
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);
}
public Task validQuote(Quote quote)
/// <summary>
/// Asynchronously validates a quote and returns the corresponding <see cref="QuoteDto"/>.
/// </summary>
/// <param name="quote">The <see cref="Quote"/> object to be validated.</param>
/// <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
/// <see cref="QuoteExtension"/>, and returns the DTO. The method is named `validQuote`, but currently, it only
/// converts the quote into a DTO and does not perform any actual validation logic.
/// If you intend to validate the quote (e.g., updating its status in a database), you will need to implement
/// the actual validation logic separately.
/// </remarks>
public Task validQuote(Quote? quote)
{
QuoteExtension extension = new QuoteExtension();
QuoteDTO quoteDTO = extension.QuoteToDTO(quote);
QuoteDto quoteDto = extension.QuoteToDTO(quote);
return Task.FromResult(quoteDTO);
// Return the DTO as the result of this asynchronous operation (though no validation logic is currently implemented)
return Task.FromResult(quoteDto);
}
public Task updateQuote(Quote quote)
/// <summary>
/// Asynchronously updates a quote and returns the corresponding <see cref="QuoteDto"/>.
/// </summary>
/// <param name="quote">The <see cref="Quote"/> object to be updated.</param>
/// <returns>A task representing the asynchronous operation, with a <see cref="QuoteDto"/> result corresponding to the updated quote.</returns>
/// <remarks>
/// 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.
/// If you intend to update the quote (e.g., modifying the quote in a database or data source),
/// you will need to implement the actual update logic separately.
/// </remarks>
public Task updateQuote(Quote? quote)
{
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 update logic is currently implemented)
return Task.FromResult(quoteDTO);
}
public Task addQuote(Quote quote)
{
throw new NotImplementedException();
@ -128,7 +175,7 @@ namespace WF_WebAdmin.Service
throw new NotImplementedException();
}
public Task<Quote> getOnequote(int id)
public Task<Quote?> getOnequote(int id)
{
throw new NotImplementedException();
}

@ -9,143 +9,285 @@ namespace WF_WebAdmin.Service;
private readonly string _char = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataCaracter.json");
private readonly string _src = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake-dataSource.json");
/// <summary>
/// Asynchronously saves a list of quotes to a JSON file.
/// </summary>
/// <param name="quotes">The list of <see cref="Quote"/> objects to be serialized and saved to the file.</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)
{
var json = JsonSerializer.Serialize(quotes, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(_jsonFilePath, json);
}
{
var json = JsonSerializer.Serialize(quotes, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(_jsonFilePath, json);
}
/// <summary>
/// Asynchronously adds a new quote to the list and saves it to a JSON file.
/// </summary>
/// <param name="quote">The <see cref="Quote"/> object to be added to the list.</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)
{
var data = await getAllQuote();
quote.Id = data.Count > 0 ? data.Max(p => p.Id) + 1 : 1;
data.Add(quote);
await saveQuoteJson(data);
}
public async Task addQuote(Quote quote)
/// <summary>
/// Asynchronously removes a quote from the list and saves the updated list to a JSON file.
/// </summary>
/// <param name="quote">The <see cref="Quote"/> object to be removed from the list.</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)
{
var data = await getAllQuote();
var q = data.FirstOrDefault(p => p.Id == quote.Id);
if (q != null)
{
var data = await getAllQuote();
quote.Id = data.Count > 0 ? data.Max(p => p.Id) + 1 : 1;
data.Add(quote);
data.Remove(q);
await saveQuoteJson(data);
}
}
public Task validQuote(Quote? quote)
{
throw new NotImplementedException();
}
public async Task removeQuote(Quote quote)
{
var data = await getAllQuote();
var q = data.FirstOrDefault(p => p.Id == quote.Id);
if (q != null)
{
data.Remove(q);
await saveQuoteJson(data);
}
}
public async Task validQuote(Quote quote)
/// <summary>
/// Asynchronously updates the details of an existing quote and saves the updated list to a JSON file.
/// </summary>
/// <param name="quote">The <see cref="Quote"/> object containing the updated details of the quote.</param>
/// <returns>A task representing the asynchronous operation of updating 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 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 q = data.FirstOrDefault(p => p.Id == quote.Id);
if (q != null)
{
throw new NotImplementedException();
q.Content = quote.Content;
q.Charac = quote.Charac;
q.ImgPath = quote.ImgPath;
q.TitleSrc = quote.TitleSrc;
q.DateSrc = quote.DateSrc;
q.Langue = quote.Langue;
await saveQuoteJson(data);
}
}
public async Task updateQuote(Quote quote)
/// <summary>
/// Asynchronously retrieves all quotes from a JSON file.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="Quote"/> objects.</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()
{
if (!File.Exists(_jsonFilePath))
{
var data = await getAllQuote();
var q = data.FirstOrDefault(p => p.Id == quote.Id);
if (q != null)
{
q.Content = quote.Content;
q.Charac = quote.Charac;
q.ImgPath = quote.ImgPath;
q.TitleSrc = quote.TitleSrc;
q.DateSrc = quote.DateSrc;
q.Langue = quote.Langue;
await saveQuoteJson(data);
}
Console.Out.WriteLine($"{_jsonFilePath} not found");
return new List<Quote>();
}
public async Task<List<Quote>> getAllQuote()
{
if (!File.Exists(_jsonFilePath))
{
Console.Out.WriteLine($"{_jsonFilePath} not found");
return new List<Quote>();
}
var json = await File.ReadAllTextAsync(_jsonFilePath);
return JsonSerializer.Deserialize<List<Quote>>(json) ?? new List<Quote>();
}
var json = await File.ReadAllTextAsync(_jsonFilePath);
return JsonSerializer.Deserialize<List<Quote>>(json) ?? new List<Quote>();
}
public async Task<List<Quote>> getSomeQuote(int nb, int page)
/// <summary>
/// Asynchronously retrieves a subset of quotes based on the specified page number and the number of quotes per page.
/// </summary>
/// <param name="nb">The number of quotes to retrieve per page.</param>
/// <param name="page">The page number for pagination.</param>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="Quote"/> objects for the specified page.</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)
{
var quotes = await getAllQuote();
if ((page - 1) * nb + nb > quotes.Count())
{
var quotes = await getAllQuote();
if((page - 1) * nb + nb > quotes.Count())
if (nb > quotes.Count())
{
if (nb > quotes.Count())
{
return quotes.GetRange(0, quotes.Count());
}
return quotes.GetRange(quotes.Count()-nb, nb);
return quotes.GetRange(0, quotes.Count());
}
return quotes.GetRange((page - 1) * nb, nb);
return quotes.GetRange(quotes.Count() - nb, nb);
}
return quotes.GetRange((page - 1) * nb, nb);
}
public async Task<Quote> getOnequote(int id)
/// <summary>
/// Asynchronously retrieves a single quote based on its ID.
/// </summary>
/// <param name="id">The unique identifier of the <see cref="Quote"/> to be retrieved.</param>
/// <returns>A task representing the asynchronous operation, with a result of the <see cref="Quote"/> object if found, otherwise null.</returns>
/// <remarks>
/// 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 q = data.FirstOrDefault(p => p.Id == id);
if (q == null)
{
var data = await getAllQuote();
var q = data.FirstOrDefault(p => p.Id == id);
if (q != null)
{
return q;
}
return null;
throw new KeyNotFoundException($"Quote with ID {id} not found.");
}
return q;
}
public async Task<List<Quote>> reserchQuote(string reserch, List<string> argument)
public Task<List<Quote>> reserchQuote(string reserch, List<string> argument)
{
throw new NotImplementedException();
}
public async Task<List<Quote>> getAllQuoteInvalid()
{
var quotes = await getAllQuote();
quotes = quotes.Where(q => q.IsValid == false).ToList();
return quotes;
}
public async Task<List<Quote>> getSomeQuoteInvalid(int nb, int page)
/// <summary>
/// Asynchronously retrieves all invalid quotes from the list.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of invalid <see cref="Quote"/> objects.</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()
{
var quotes = await getAllQuote();
quotes = quotes.Where(q => !q.IsValid).ToList();
return quotes;
}
/// <summary>
/// Asynchronously retrieves a subset of invalid quotes based on the specified page number and the number of quotes per page.
/// </summary>
/// <param name="nb">The number of invalid quotes to retrieve per page.</param>
/// <param name="page">The page number for pagination.</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>
/// <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)
{
var quotes = await getAllQuoteInvalid();
if ((page - 1) * nb + nb > quotes.Count)
{
var quotes = await getAllQuoteInvalid();
if ((page - 1) * nb + nb > quotes.Count())
if (nb > quotes.Count)
{
if (nb > quotes.Count())
{
return quotes.GetRange(0, quotes.Count());
}
return quotes.GetRange(quotes.Count() - nb, nb);
return quotes.GetRange(0, quotes.Count);
}
return quotes.GetRange((page - 1) * nb, nb);
return quotes.GetRange(quotes.Count - nb, nb);
}
return quotes.GetRange((page - 1) * nb, nb);
}
/// <summary>
/// Asynchronously retrieves the total number of quotes.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a result of the total number of <see cref="Quote"/> objects.</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()
{
var data = await getAllQuote();
return data.Count;
}
public async Task<int> getNbQuote()
/// <summary>
/// Asynchronously retrieves a list of characters from a JSON file.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="Character"/> objects.</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()
{
if (!File.Exists(_char))
{
var data = await getAllQuote();
return data.Count;
Console.Out.WriteLine($"{_char} not found");
return new List<Character>();
}
public async Task<List<Character>> getChar()
{
if (!File.Exists(_char))
{
Console.Out.WriteLine($"{_char} not found");
return new List<Character>();
}
var json = await File.ReadAllTextAsync(_char);
return JsonSerializer.Deserialize<List<Character>>(json) ?? new List<Character>();
}
var json = await File.ReadAllTextAsync(_char);
return JsonSerializer.Deserialize<List<Character>>(json) ?? new List<Character>();
}
public async Task<List<Source>> getSrc()
/// <summary>
/// Asynchronously retrieves a list of sources from a JSON file.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="Source"/> objects.</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()
{
if (!File.Exists(_src))
{
if (!File.Exists(_src))
{
Console.Out.WriteLine($"{_src} not found");
return new List<Source>();
}
var json = await File.ReadAllTextAsync(_src);
return JsonSerializer.Deserialize<List<Source>>(json) ?? new List<Source>();
Console.Out.WriteLine($"{_src} not found");
return new List<Source>();
}
}
var json = await File.ReadAllTextAsync(_src);
return JsonSerializer.Deserialize<List<Source>>(json) ?? new List<Source>();
}
}

@ -8,12 +8,32 @@ public class UserServiceStub : IUserService
private readonly string _jsonFilePath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "fake_data_users.json");
/// <summary>
/// Asynchronously saves a list of users to a JSON file.
/// </summary>
/// <param name="users">The list of <see cref="User"/> objects to be saved to the file.</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)
{
var json = JsonSerializer.Serialize(users, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(_jsonFilePath, json);
}
/// <summary>
/// Asynchronously removes a user from the list of users and saves the updated list to a JSON file.
/// </summary>
/// <param name="user">The <see cref="User"/> object to be removed from the list.</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)
{
var data = await getAllUser();
@ -25,18 +45,49 @@ public class UserServiceStub : IUserService
}
}
public Task updateRole(User user)
/// <summary>
/// Asynchronously updates the role of a user, setting the user as an administrator.
/// </summary>
/// <param name="user">The <see cref="User"/> object whose role is to be updated.</param>
/// <returns>A task representing the asynchronous operation of updating the user's role.</returns>
/// <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;
return updateUser(user);
}
public Task downgradeRole(User user)
/// <summary>
/// Asynchronously downgrades the role of a user, removing their administrator privileges.
/// </summary>
/// <param name="user">The <see cref="User"/> object whose role is to be downgraded.</param>
/// <returns>A task representing the asynchronous operation of downgrading the user's role.</returns>
/// <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;
return updateUser(user);
}
/// <summary>
/// Asynchronously retrieves a list of all users from a JSON file.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="User"/> objects.</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()
{
if (!File.Exists(_jsonFilePath))
@ -49,49 +100,94 @@ public class UserServiceStub : IUserService
return JsonSerializer.Deserialize<List<User>>(json) ?? new List<User>();
}
public async Task<List<User>> getSomeUser(int nb, int page)
/// <summary>
/// Asynchronously retrieves a paginated list of users from a JSON file.
/// </summary>
/// <param name="nb">The number of users to retrieve per page.</param>
/// <param name="page">The page number for the data to retrieve.</param>
/// <returns>A task representing the asynchronous operation, with a result of a list of <see cref="User"/> objects.</returns>
/// <remarks>
/// 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();
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);
}
/// <summary>
/// Asynchronously retrieves a single user by their ID from the JSON file.
/// </summary>
/// <param name="id">The ID of the user to retrieve.</param>
/// <returns>A task representing the asynchronous operation, with a result of the <see cref="User"/> object if found, otherwise null.</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)
{
var data = await getAllUser();
var u = data.FirstOrDefault(p => p.Id == id);
if (u != null)
var user = data.FirstOrDefault(p => p.Id == id);
if (user == null)
{
return u;
throw new KeyNotFoundException($"User with ID {id} not found.");
}
return null;
return user;
}
public Task<List<User>> reserchUsers(string reserch, List<string> args)
{
throw new NotImplementedException();
}
/// <summary>
/// Asynchronously retrieves the total number of users from the JSON file.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a result of the total number of users.</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()
{
var data = await getAllUser();
return data.Count;
}
public async Task updateUser(User user)
/// <summary>
/// Asynchronously updates the details of a user in the JSON file.
/// </summary>
/// <param name="user">The <see cref="User"/> object containing the updated user details.</param>
/// <returns>A task representing the asynchronous operation of updating the user.</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, 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 person = data.FirstOrDefault(p => p.Id == user.Id);
if (person != null)
{
person.Name = user.Name;
person.Email = user.Email;
person.Image = user.Image;
person.IsAdmin = user.IsAdmin;
await saveUsersJson(data);
}
var data = await getAllUser();
var person = data.FirstOrDefault(p => p.Id == user.Id);
if (person != null)
{
person.Name = user.Name;
person.Email = user.Email;
person.Image = user.Image;
person.IsAdmin = user.IsAdmin;
await saveUsersJson(data);
}
}
}

@ -21,6 +21,16 @@
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
{
get => CultureInfo.CurrentCulture;
@ -40,4 +50,5 @@
this.NavigationManager.NavigateTo("/Culture/SetCulture" + query, forceLoad: true);
}
}
}

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

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

@ -15,6 +15,7 @@
<PackageReference Include="bootstrap" Version="5.3.3" />
<PackageReference Include="Microsoft.Extensions.Localization" 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="Blazored.Modal" Version="7.2.0" />
</ItemGroup>

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

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

@ -7,6 +7,17 @@ html, body {
color: black;
}
h1 {
text-align: center;
font-size: 32px;
margin-top: 10%;
font-family: "Roboto", serif;
}
h1:focus {
outline: none;
}
@ -169,12 +180,7 @@ td {
text-align: center;
}
h1 {
text-align: center;
font-size: 32px;
margin-top: 10%;
font-family: "Roboto", serif;
}
p {
margin-bottom: 2%;
@ -221,35 +227,6 @@ p {
color: red;
}
table {
border-collapse: collapse;
width: 100%;
}
td {
padding: 10px 20px;
border: none;
}
.boutons {
display: flex;
justify-content: space-between;
gap: 10px;
}
.boutons button {
border: none;
background-color: transparent;
padding: 5px;
cursor: pointer;
}
.boutons button img {
width: 24px;
height: 24px;
object-fit: contain;
}
.imageProfil {
height: auto;

@ -0,0 +1,702 @@
[
{
"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."
}
]

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

@ -1,12 +1,14 @@
[
{
"Id": "1",
"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.",
"Likes": 0,
"Id": 11,
"Content": "Vous ne pouvez pas vivre sans causer de dommages \u00E0 quelqu\u0027un d\u0027autre.",
"Like": 110,
"Langue": "fr",
"Charac": "Superman",
"TitleSrc": "SuperMan : le film",
"UserProposition": "joe",
"ImgPath": "https://tse4.mm.bing.net/th/id/OIP.fc5TQflh0cbxB1GUeOdk6gHaK8?w=123&h=180&c=7&r=0&o=5&pid=1.7"
"Charac": "test",
"ImgPath": "http://thematrix.com",
"TitleSrc": "The Matrix",
"DateSrc": "2025-01-21T00:00:00",
"UserProposition": "user11",
"IsValid": false
}
]

@ -1,7 +1,7 @@
[
{
"Id": 1,
"Content": "Que la force soit avec toi.",
"Content": "Que la force soit avec toi",
"Like": 150,
"Langue": "en",
"Charac": "Drago Malefoy",
@ -25,7 +25,7 @@
},
{
"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,
"Langue": "fr",
"Charac": "test",

@ -0,0 +1,261 @@
[
{
"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,26 +1,4 @@
[
{
"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,
"Question": "nv question",
@ -29,19 +7,19 @@
"AnswerC": "do officia",
"AnswerD": "ut nostrud",
"CAnswer": "C",
"IsValid": false,
"IsValid": true,
"UserProposition": "Brooks Martinez"
},
{
"Id": 12,
"Question": "Irure occaecat sit laborum nulla ea dolore et aliqua sunt Lorem enim esse.",
"AnswerA": "excepteur occaecat",
"AnswerB": "pariatur in",
"AnswerC": "reprehenderit excepteur",
"AnswerD": "laborum adipisicing",
"CAnswer": "D",
"Question": "question",
"AnswerA": "repA",
"AnswerB": "non",
"AnswerC": "do",
"AnswerD": "ut",
"CAnswer": "A",
"IsValid": false,
"UserProposition": "Shields Roth"
"UserProposition": "Brooks Martinez"
},
{
"Id": 13,
@ -60,7 +38,7 @@
"AnswerA": "ca",
"AnswerB": "va",
"AnswerC": "marcher",
"AnswerD": "!",
"AnswerD": "L",
"CAnswer": "A",
"IsValid": true,
"UserProposition": "Admin"

@ -5,11 +5,13 @@
"Name": "testeur",
"Email": "testeur@gmail.com",
"DateCreation": "2024-08-02T00:00:00",
"IsAdmin": true,
"IsAdmin": false,
"Comments": [
{
"Text": "Premier test effectu\u00E9, tout semble OK.",
"DateCreation": "2024-08-02T00:00:00"
"Id": 0,
"IdUser": 0,
"DateCreation": "2024-08-02T00:00:00",
"Text": "Premier test effectu\u00E9, tout semble OK."
}
]
},
@ -19,7 +21,7 @@
"Name": "dev",
"Email": "dev@gmail.com",
"DateCreation": "2024-10-10T00:00:00",
"IsAdmin": false,
"IsAdmin": true,
"Comments": null
},
{
@ -31,20 +33,13 @@
"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",
"DateCreation": "2024-07-15T00:00:00",
"IsAdmin": false,
"Comments": null
},
{
"Id": 7,
"Image": "https://tse2.mm.bing.net/th/id/OIP.MMpXBB5RDRYQm05FJmevGAHaKl?w=137\u0026h=195\u0026c=7\u0026r=0\u0026o=5\u0026pid=1.7",
@ -54,29 +49,6 @@
"IsAdmin": false,
"Comments": null
},
{
"Id": 8,
"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",
"DateCreation": "2024-09-05T00:00:00",
"IsAdmin": false,
"Comments": null
},
{
"Id": 9,
"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",
@ -94,14 +66,5 @@
"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