Merge branch 'dev' into j2/feat/interface

# Conflicts:
#	Webzine.Repository/LocalEntityRepository.cs
This commit is contained in:
mirage
2026-03-25 14:11:07 +01:00
48 changed files with 561 additions and 537 deletions

View File

@@ -0,0 +1,171 @@
# Dossier de configuration — Projet Webzine
**Équipe 1**
**Formation :** Développement .NET niveau 1 / Dr1-P4
**Date :** Mars 2026
---
## Table des modifications
| Date | Auteur |
|------|--------|
| 25/03 | Clément Bobin |
---
## 1. Prérequis
Avant de lancer l'application, assurez-vous d'avoir installé les outils suivants :
| Outil | Version minimale | Lien |
|-------|-----------------|------|
| .NET SDK | 10.0 | https://dotnet.microsoft.com/download |
| Node.js (optionnel, pour outils front) | 18+ | https://nodejs.org |
| Git | 2.x | https://git-scm.com |
| Un IDE | Visual Studio 2022+ ou Rider | — |
---
## 2. Récupérer le projet
```bash
git clone <url-du-dépôt>
cd Webzine
```
---
## 3. Structure des projets
Le projet est organisé en plusieurs bibliothèques de classes et une application web :
```
Webzine.sln
├── Webzine.Entity/ → Entités du domaine (modèles)
├── Webzine.Entity.Tests/ → Tests unitaires MSTest
├── Webzine.Repository.Contracts/ → Interfaces des repositories
├── Webzine.Repository/ → Implémentations des repositories
├── Webzine.Business.Contracts/ → Interfaces de la couche métier
├── Webzine.Business/ → Logique métier
├── Webzine.EntitiesContext/ → Contexte Entity Framework (à venir)
├── Webzine.Documentation/ → Documentation, StyleCop, rapports
└── Webzine.WebApplication/ → Application ASP.NET Core MVC (.NET 10)
```
---
## 4. Lancer l'application
### Via la ligne de commande
```bash
cd Webzine.WebApplication
dotnet run
```
L'application est accessible par défaut à :
- **HTTP :** http://localhost:5038
- **HTTPS :** https://localhost:7095
Ces URLs sont configurées dans `Webzine.WebApplication/Properties/launchSettings.json`.
### Via Visual Studio
Ouvrir `Webzine.sln`, sélectionner le profil `https` ou `http`, puis lancer avec F5 ou Ctrl+F5.
---
## 5. Configuration applicative
Le fichier principal de configuration est `Webzine.WebApplication/appsettings.json` :
```json
{
"Webzine": {
"NombreDerniereChronique": 3,
"NombreDeTopTitres": 3
}
}
```
| Propriété | Type | Description | Valeur par défaut |
|-----------|------|-------------|-------------------|
| `NombreDerniereChronique` | int | Nombre de chroniques affichées sur la page d'accueil (section "Derniers titres") | 3 |
| `NombreDeTopTitres` | int | Nombre de titres affichés dans le bloc "Titres les plus populaires" | 3 |
Ces valeurs sont injectées dans `AccueilController` via `IConfiguration` et peuvent être modifiées sans recompilation.
Pour l'environnement de développement, les surcharges se font dans `appsettings.Development.json`.
---
## 6. Logging (NLog)
La configuration du logging se trouve dans `Webzine.WebApplication/nlog.config`.
Les logs sont écrits dans le dossier `/Logs/` avec deux fichiers :
| Fichier | Contenu |
|---------|---------|
| `nlog-all-{date}.log` | Tous les logs (Debug et supérieur pour l'application) |
| `nlog-own-{date}.log` | Logs applicatifs avec URL de la requête |
Les logs de la console sont également activés, utiles lors du développement.
**Niveaux configurés :**
- `Webzine.WebApplication.*` → Debug et supérieur
- `Microsoft.*` → Warning et supérieur (pour réduire le bruit)
- `Microsoft.Hosting.Lifetime*` → Info (pour voir le démarrage de l'appli)
Pour modifier le niveau de log minimum sans recompiler, éditer la règle correspondante dans `nlog.config`. NLog recharge automatiquement sa configuration (`autoReload="true"`).
---
## 7. Lancer les tests
```bash
cd Webzine.Entity.Tests
dotnet test
```
Ou depuis la solution complète :
```bash
dotnet test Webzine.sln
```
Les tests vérifient les contraintes de validation (annotations DataAnnotations) sur toutes les entités. Ils s'exécutent en parallèle au niveau des méthodes (`[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]`).
**Résultat attendu :** 55 tests passants, 0 échec.
---
## 8. Routes principales
| URL | Contrôleur | Description |
|-----|-----------|-------------|
| `/` | `AccueilController.Index` | Page d'accueil |
| `/artiste/{nom}` | `ArtisteController.Index` | Page d'un artiste |
| `/titre/{id}` | `TitreController.Details` | Détail d'un titre |
| `/titre/style/{style}` | `TitreController.Style` | Titres par style |
| `/recherche` (POST) | `RechercheController.Index` | Résultats de recherche |
| `/Administration/Dashboard` | `DashboardController.Index` | Tableau de bord admin |
| `/Administration/Artiste` | `ArtisteController` (admin) | Gestion des artistes |
| `/Administration/Titre` | `TitreController` (admin) | Gestion des titres |
| `/Administration/Style` | `StyleController` | Gestion des styles |
| `/Administration/Commentaire` | `CommentaireController` | Gestion des commentaires |
---
## 10. Variables d'environnement
L'environnement est contrôlé par la variable `ASPNETCORE_ENVIRONMENT`. En développement, elle vaut `Development` (configuré dans `launchSettings.json`).
En production, positionner :
```bash
export ASPNETCORE_ENVIRONMENT=Production
```
Cela désactive les pages d'erreur détaillées et active les optimisations de performance ASP.NET Core.

View File

@@ -8,6 +8,8 @@
<ItemGroup>
<PackageReference Include="Faker.Net" Version="2.0.163" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.5" />
<PackageReference Include="NLog" Version="6.1.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
@@ -21,4 +23,8 @@
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,97 @@
// <copyright file="WebzineDbContext.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace Webzine.EntitiesContext
{
using Microsoft.EntityFrameworkCore;
using Webzine.Entity;
public class WebzineDbContext : DbContext
{
/// <summary>
/// Initializes a new instance of the <see cref="WebzineDbContext"/> class.
/// </summary>
/// <param name="options">Options.</param>
public WebzineDbContext(DbContextOptions<WebzineDbContext> options)
: base(options) { }
/// <summary>
/// Gets Obtient les artistes de la base.
/// </summary>
public DbSet<Artiste> Artistes => this.Set<Artiste>();
/// <summary>
/// Gets Obtient les styles de la base.
/// </summary>
public DbSet<Style> Styles => this.Set<Style>();
/// <summary>
/// Gets obtient les titres de la base.
/// </summary>
public DbSet<Titre> Titres => this.Set<Titre>();
/// <summary>
/// Gets obtient les commentaires de la base.
/// </summary>
public DbSet<Commentaire> Commentaires => this.Set<Commentaire>();
/// <inheritdoc/>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Artiste>(entity =>
{
entity.HasKey(a => a.IdArtiste)
.HasName("PK_Artiste");
entity.HasMany(a => a.Titres)
.WithOne(t => t.Artiste)
.HasForeignKey(t => t.IdArtiste)
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity<Style>(entity =>
{
entity.HasKey(s => s.IdStyle)
.HasName("PK_Style");
});
modelBuilder.Entity<Commentaire>(entity =>
{
entity.HasKey(c => c.IdCommentaire)
.HasName("PK_Commentaire");
entity.HasOne(c => c.Titre)
.WithMany(t => t.Commentaires)
.HasForeignKey(c => c.IdTitre)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<Titre>(entity =>
{
entity.HasKey(t => t.IdTitre)
.HasName("PK_Titre");
entity.HasMany(t => t.Styles)
.WithMany(s => s.Titres)
.UsingEntity<Dictionary<string, object>>(
"TitreStyle",
right => right.HasOne<Style>()
.WithMany()
.HasForeignKey("IdStyle")
.OnDelete(DeleteBehavior.Cascade),
left => left.HasOne<Titre>()
.WithMany()
.HasForeignKey("IdTitre")
.OnDelete(DeleteBehavior.Cascade),
join =>
{
join.HasKey("IdTitre", "IdStyle");
join.ToTable("TitreStyle");
});
});
}
}
}

View File

@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using System.Timers;
using System.ComponentModel.DataAnnotations;
namespace Webzine.Entity
{

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using System.ComponentModel.DataAnnotations;
namespace Webzine.Entity
{

View File

@@ -1,4 +1,4 @@
namespace Webzine.EntitiesContext;
namespace Webzine.Entity.Fixtures;
public class SeedDataSpotify
{

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using System.ComponentModel.DataAnnotations;
namespace Webzine.Entity
{

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using System.ComponentModel.DataAnnotations;
namespace Webzine.Entity
{

View File

@@ -8,7 +8,8 @@
<ItemGroup>
<PackageReference Include="Faker.Net" Version="2.0.163" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageReference Include="NLog" Version="6.1.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>

View File

@@ -1,9 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Webzine.Entity;
using Webzine.Entity.Fixtures;
using Webzine.WebApplication.Areas.Administration.ViewModels.Artiste;
using Webzine.WebApplication.Areas.Administration.ViewModels.Titre;
namespace Webzine.WebApplication.Areas.Administration.Controllers;
@@ -17,15 +15,16 @@ public class ArtisteController : Controller
public ArtisteController(ILogger<ArtisteController> logger)
{
_logger = logger;
this._logger = logger;
this._logger.LogDebug(1, "initialisation du ArtisteController d'administration");
var factory = new DataFactory();
_artistes = factory.GenerateArtists(10);
}
/// <summary>
/// Affiche la liste des artistes. Pour l'instant, les artistes sont générés à partir de noms prédéfinis via la méthode SeedArtisteByName de la classe ArtisteFactory.
/// Chaque artiste est ensuite ajouté à une liste d'artistes qui est passée à la vue via un objet GroupeArtisteViewModel.
/// Affiche la liste des artistes. Pour l'instant, les artistes sont générés à partir de noms prédéfinis via la méthode SeedArtisteByName de la classe ArtisteFactory.
/// Chaque artiste est ensuite ajouté à une liste d'artistes qui est passée à la vue.
/// </summary>
/// <returns>Redirection.</returns>
public IActionResult Index()
@@ -33,14 +32,9 @@ public class ArtisteController : Controller
var _artistes_ordre = _artistes.OrderBy(t => t.Nom).ToList();
_logger.LogInformation("Initialisation du contrôleur TitreController pour l'Administration.");
this._logger.LogInformation("Initialisation du contrôleur TitreController pour l'Administration.");
GroupeArtisteViewModel groupeArtisteModel = new GroupeArtisteViewModel
{
Artistes = _artistes_ordre
};
return View(groupeArtisteModel);
return View(_artistes_ordre);
}
/// <summary>

View File

@@ -18,11 +18,12 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
/// <param name="logger">Service de journalisation injecté.</param>
public CommentaireController(ILogger<CommentaireController> logger)
{
_logger = logger;
this._logger = logger;
_logger.LogInformation("Initialisation du contrôleur CommentaireController.");
this._logger.LogInformation("Initialisation du contrôleur CommentaireController.");
var factory = new DataFactory();
var factory = new DataFactory(); // TODO injecter le factory via DI pour éviter de le recréer à chaque fois
// faire une classe statique
var _artistes = factory.GenerateArtists(10);
var _styles = factory.GenerateStyles(10);
@@ -30,49 +31,21 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
_commentaires = factory.GenerateCommentaires(50, _titres);
_logger.LogInformation("Données fictives générées avec succès.");
this._logger.LogInformation("Données fictives générées avec succès.");
}
/// <summary>
/// Affiche la liste des commentaires dans la vue Index.
/// </summary>
/// <returns>>La vue Index avec le ViewModel contenant la liste des commentaires.</returns>
public ActionResult Index()
public IActionResult Index()
{
// Création de données "bouchon" (mock) pour tester l'affichage
var listeCommentaires = new List<Commentaire>
{
new Commentaire
{
IdCommentaire = 1, // Correction: Id -> IdCommentaire
Auteur = "Michel", // Correction: Nom -> Auteur
Contenu = "Nulla sed velit nec tellus gravida molestie",
DateCreation = new DateTime(2023, 1, 22, 15, 59, 28),
// Important : On initialise l'objet Titre pour accéder à Titre.Libelle
Titre = new Titre { Libelle = "St Germain - So Flute" },
},
new Commentaire
{
IdCommentaire = 2,
Auteur = "Jeff",
Contenu = "Lorem ipsum dolor sit.",
DateCreation = new DateTime(2023, 1, 22, 14, 27, 8),
Titre = new Titre { Libelle = "Queen - Bohemian Rapsody" },
},
new Commentaire
{
IdCommentaire = 3,
Auteur = "Eva",
Contenu = "Aenean vulputate eleifend tellus.",
DateCreation = new DateTime(2023, 1, 22, 13, 2, 17),
Titre = new Titre { Libelle = "Rammstein - Du hast" },
},
};
// Initialisation du ViewModel
var viewModel = new CommentaireViewModel
{
Commentaires = listeCommentaires
Commentaires = _commentaires
};
return View(viewModel);
@@ -83,14 +56,17 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
/// Affiche la vue de confirmation de suppression d'un commentaire, en récupérant les détails du commentaire à supprimer à partir de l'identifiant fourni.
/// </summary>
/// <param name="id">L'identifiant du commentaire à supprimer.</param>
/// <returns>La vue de confirmation de suppression avec le ViewModel contenant les détails du commentaire à supprimer, ou une réponse NotFound si le commentaire n'existe pas.</returns>
public ActionResult Delete(int id)
/// <returns>La vue de confirmation de suppression avec le ViewModel contenant les détails du commentaire à supprimer, ou une redirection vers l'index si le commentaire n'existe pas.</returns>
public IActionResult Delete(int id)
{
var commentaire = _commentaires
.FirstOrDefault(c => c.IdCommentaire == id);
if (commentaire == null)
return NotFound();
{
this._logger.LogWarning("Commentaire avec ID {Id} introuvable pour suppression.", id);
return RedirectToAction("Index");
}
var vm = new CommentaireDeleteViewModel
{
@@ -103,27 +79,5 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
return View(vm);
}
/// <summary>
/// Traite la confirmation de suppression d'un commentaire. En cas de succès, redirige vers la liste des commentaires. En cas d'erreur, affiche à nouveau la vue de confirmation avec le message d'erreur.
/// </summary>
/// <param name="id">L'identifiant du commentaire à supprimer.</param>
/// <param name="model">Le ViewModel contenant les détails du commentaire à supprimer, utilisé pour afficher les informations en cas d'erreur.</param>
/// <returns>Redirection vers la liste des commentaires en cas de succès, ou la vue de confirmation avec le message d'erreur en cas d'échec.</returns>
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id, CommentaireDeleteViewModel model)
{
try
{
return RedirectToAction();
}
catch (Exception e)
{
// Log de l'erreur
Console.WriteLine(e);
return View(model);
}
}
}
}

View File

@@ -20,9 +20,9 @@ public class DashboardController : Controller
/// <param name="logger">Service de journalisation injecté.</param>
public DashboardController(ILogger<DashboardController> logger)
{
_logger = logger;
this._logger = logger;
_logger.LogInformation("Initialisation du contrôleur TitreController.");
this._logger.LogInformation("Initialisation du contrôleur TitreController.");
var factory = new DataFactory();
@@ -32,7 +32,7 @@ public class DashboardController : Controller
factory.GenerateCommentaires(50, _titres);
_logger.LogInformation("Données fictives générées avec succès.");
this._logger.LogInformation("Données fictives générées avec succès.");
}
/// <summary>
@@ -41,40 +41,40 @@ public class DashboardController : Controller
/// <returns>La vue Index du tableau de bord.</returns>
public IActionResult Index()
{
var mostChronicledArtist = _titres
var artisteLePlusChronique = _titres
.GroupBy(t => t.Artiste)
.OrderByDescending(g => g.Count())
.FirstOrDefault();
var topArtistAlbums = _titres
var albumLePlusChronique = _titres
.GroupBy(t => t.Artiste)
.OrderByDescending(g => g.Select(t => t.Album).Distinct().Count())
.FirstOrDefault();
var mostPlayedTrack = _titres
var musiqueLaPlusJouee = _titres
.OrderByDescending(t => t.NbLectures)
.FirstOrDefault();
var model = new DashboardViewModel
{
ArtistCount = _artistes.Count,
NombreArtistes = _artistes.Count,
MostChronicledArtistName = mostChronicledArtist?.Key.Nom,
ArtisteLePlusChronique = artisteLePlusChronique?.Key.Nom,
TopArtistAlbumsName = topArtistAlbums?.Key.Nom,
AlbumLePlusChronique = albumLePlusChronique?.Key.Nom,
BiographyCount = _artistes.Count(a => !string.IsNullOrEmpty(a.Biographie)),
NombreBiographies = _artistes.Count(a => !string.IsNullOrEmpty(a.Biographie)),
MostPlayedTrackId = mostPlayedTrack?.IdTitre ?? 0,
MostPlayedTrack = mostPlayedTrack?.Libelle,
IdMusiqueLaPlusJouee = musiqueLaPlusJouee?.IdTitre ?? 0,
MusiqueLaPlusJouee = musiqueLaPlusJouee?.Libelle,
TrackCount = _titres.Count,
NombreTitres = _titres.Count,
GenreCount = _styles.Count,
NombreGenres = _styles.Count,
TotalPlays = _titres.Sum(t => t.NbLectures),
NombreLectures = _titres.Sum(t => t.NbLectures),
TotalLikes = _titres.Sum(t => t.NbLikes)
NombreLikes = _titres.Sum(t => t.NbLikes)
};
return View(model);

View File

@@ -21,39 +21,22 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
/// <param name="logger">Service de journalisation injecté.</param>
public StyleController(ILogger<StyleController> logger)
{
_logger = logger;
this._logger = logger;
_logger.LogInformation("Initialisation du contrôleur StyleController.");
this._logger.LogInformation("Initialisation du contrôleur StyleController.");
var factory = new DataFactory();
_styles = factory.GenerateStyles(10);
_logger.LogInformation("Données fictives générées avec succès.");
this._logger.LogInformation("Données fictives générées avec succès.");
}
// GET: Administration/Styles
public ActionResult Index()
public IActionResult Index()
{
// Création de données "bouchon" (mock) pour tester l'affichage
var listeStyles = new List<Style>
{
new Style
{
IdStyle = 1,
Libelle = "Rock",
},
new Style
{
IdStyle = 2,
Libelle = "Pop",
},
new Style
{
IdStyle = 3,
Libelle = "Jazz",
},
};
var listeStyles = this._styles;
// Initialisation du ViewModel
var viewModel = new StyleViewModel
@@ -65,45 +48,22 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
}
// GET: Administration/Styles/Create
public ActionResult Create()
public IActionResult Create()
{
return View();
}
// POST: Administration/Styles/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(StyleCreateViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
try
{
_logger.LogInformation("Nouveau style créé : {Libelle}", model.Libelle);
return RedirectToAction(nameof(Index));
}
catch (Exception e)
{
_logger.LogError(e, "Erreur lors de la création du style");
ModelState.AddModelError("", "Une erreur est survenue lors de la création.");
return View(model);
}
}
// GET: Administration/Styles/Delete/5
public ActionResult Delete(int id)
public IActionResult Delete(int id)
{
var style = this._styles
.FirstOrDefault(c => c.IdStyle == id);
if (style == null)
{
return this.NotFound();
this._logger.LogWarning("Style avec ID {Id} introuvable pour suppression.", id);
return RedirectToAction("Index");
}
var vm = new StyleDeleteViewModel
@@ -115,33 +75,17 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
return View(vm);
}
// POST: Administration/Styles/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id, StyleDeleteViewModel model)
{
try
{
return RedirectToAction(nameof(Index));
}
catch (Exception e)
{
// Log de l'erreur
_logger.LogError(e, "Erreur lors de la suppression du style avec l'ID {StyleId}", id);
return View(model);
}
}
// GET: Administration/Styles/Edit/5
[HttpGet]
public ActionResult Edit(int id)
public IActionResult Edit(int id)
{
// Recherche du style (simulation avec la liste _styles)
var style = _styles.FirstOrDefault(s => s.IdStyle == id);
if (style == null)
{
return NotFound();
this._logger.LogWarning("Style avec ID {Id} introuvable pour style.", id);
return RedirectToAction("Index");
}
// Mapping vers le ViewModel
@@ -153,28 +97,5 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
return View(model);
}
// POST: Administration/Styles/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, StyleEditViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
try
{
_logger.LogInformation("Style {Id} mis à jour : {Libelle}", id, model.Libelle);
return RedirectToAction(nameof(Index));
}
catch (Exception e)
{
_logger.LogError(e, "Erreur lors de la modification du style {Id}", id);
ModelState.AddModelError("", "Une erreur est survenue lors de la modification.");
return View(model);
}
}
}
}

View File

@@ -22,9 +22,9 @@ public class TitreController : Controller
/// <param name="logger">Service de journalisation injecté.</param>
public TitreController(ILogger<TitreController> logger)
{
_logger = logger;
this._logger = logger;
_logger.LogInformation("Initialisation du contrôleur TitreController pour l'Administration.");
this._logger.LogInformation("Initialisation du contrôleur TitreController pour l'Administration.");
var factory = new DataFactory();
@@ -34,14 +34,14 @@ public class TitreController : Controller
factory.GenerateCommentaires(50, _titres);
_logger.LogInformation("Données fictives générées avec succès.");
this._logger.LogInformation("Données fictives générées avec succès.");
}
/// <summary>
/// Affiche la liste des titres dans la vue Index.
/// </summary>
/// <returns>La vue Index avec le ViewModel contenant la liste des titres.</returns>
public ActionResult Index()
public IActionResult Index()
{
var model = _titres.Select(t => new AdminTitreList
{
@@ -62,7 +62,7 @@ public class TitreController : Controller
/// Affiche le formulaire de création d'un nouveau titre dans la vue Create.
/// </summary>
/// <returns>La vue Create avec le ViewModel contenant les listes déroulantes pour les artistes et les styles.</returns>
public ActionResult Create()
public IActionResult Create()
{
var model = new AdminTitreForm
{
@@ -82,32 +82,12 @@ public class TitreController : Controller
return View(model);
}
/// <summary>
/// Traite la soumission du formulaire de création d'un nouveau titre. Actuellement, cette méthode est un stub qui redirige vers l'index sans effectuer de logique de création réelle.
/// </summary>
/// <param name="collection">Le formulaire soumis contenant les données du nouveau titre. Actuellement, ce paramètre n'est pas utilisé.</param>
/// <returns>Redirige vers l'action Index après la soumission du formulaire. En cas d'erreur, retourne la vue Create pour permettre à l'utilisateur de corriger les données.</returns>
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(IFormCollection collection)
{
try
{
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
/// <summary>
/// Affiche le formulaire de modification d'un titre existant dans la vue Edit, en préremplissant les champs avec les données du titre sélectionné. Les listes déroulantes pour les artistes et les styles sont également remplies pour permettre à l'utilisateur de modifier ces associations.
/// </summary>
/// <param name="id">L'identifiant du titre à modifier, utilisé pour récupérer les données du titre à partir de la liste des titres générés.</param>
/// <returns>La vue Edit avec le ViewModel contenant les données du titre à modifier, ainsi que les listes déroulantes pour les artistes et les styles. En cas d'erreur, retourne une réponse NotFound si le titre n'existe pas.</returns>
public ActionResult Edit(int id)
/// <returns>La vue Edit avec le ViewModel contenant les données du titre à modifier, ainsi que les listes déroulantes pour les artistes et les styles. </returns>
public IActionResult Edit(int id)
{
var titre = _titres.First(t => t.IdTitre == id);
@@ -142,32 +122,12 @@ public class TitreController : Controller
return View(model);
}
/// <summary>
/// Traite la soumission du formulaire de modification d'un titre existant. Actuellement, cette méthode est un stub qui redirige vers l'index sans effectuer de logique de modification réelle.
/// </summary>
/// <param name="id">L'identifiant du titre à modifier, utilisé pour identifier le titre à mettre à jour. Actuellement, ce paramètre n'est pas utilisé dans la logique de traitement.</param>
/// <param name="collection">Le formulaire soumis contenant les données modifiées du titre. Actuellement, ce paramètre n'est pas utilisé dans la logique de traitement.</param>
/// <returns>Redirige vers l'action Index après la soumission du formulaire. En cas d'erreur, retourne la vue Edit pour permettre à l'utilisateur de corriger les données.</returns>
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, IFormCollection collection)
{
try
{
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
/// <summary>
/// Affiche la vue de confirmation de suppression d'un titre, en récupérant les détails du titre à supprimer à partir de l'identifiant fourni. Le ViewModel contient les informations essentielles du titre, telles que le libellé et le nom de l'artiste, pour permettre à l'utilisateur de confirmer la suppression.
/// </summary>
/// <param name="id">L'identifiant du titre à supprimer, utilisé pour récupérer les données du titre à partir de la liste des titres générés.</param>
/// <returns>La vue de confirmation de suppression avec le ViewModel contenant les détails du titre à supprimer, ou une réponse NotFound si le titre n'existe pas.</returns>
public ActionResult Delete(int id)
/// <returns>La vue de confirmation de suppression avec le ViewModel contenant les détails du titre à supprimer.</returns>
public IActionResult Delete(int id)
{
var titre = _titres.First(t => t.IdTitre == id);
@@ -180,20 +140,4 @@ public class TitreController : Controller
return View(model);
}
/// <summary>
/// Traite la confirmation de suppression d'un titre. En cas de succès, redirige vers la liste des titres après avoir supprimé le titre de la liste. En cas d'erreur, affiche à nouveau la vue de confirmation avec le message d'erreur.
/// </summary>
/// <param name="model">Le ViewModel contenant les détails du titre à supprimer, utilisé pour identifier le titre à supprimer et pour afficher les informations en cas d'erreur.</param>
/// <returns>Redirection vers la liste des titres en cas de succès, ou la vue de confirmation avec le message d'erreur en cas d'échec.</returns>
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(AdminTitreDelete model)
{
var titre = _titres.First(t => t.IdTitre == model.Id);
_titres.Remove(titre);
return RedirectToAction(nameof(Index));
}
}

View File

@@ -1,14 +0,0 @@
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Artiste
{
using Webzine.Entity;
/// <summary>
/// ViewModel pour afficher un groupe d'artiste.
/// </summary>
public class GroupeArtisteViewModel
{
/// <summary>
/// Liste d'artistes.
/// </summary>
public IEnumerable<Artiste> Artistes { get; set; } = new List<Artiste>();
}
}

View File

@@ -8,49 +8,49 @@ public class DashboardViewModel
/// <summary>
/// Définit le nombre total d'artistes chroniqués dans le webzine.
/// </summary>
public int ArtistCount { get; set; }
public int NombreArtistes { get; set; }
/// <summary>
/// Définit le nom de l'artiste le plus chroniqué dans le webzine.
/// </summary>
public string MostChronicledArtistName { get; set; }
public string ArtisteLePlusChronique { get; set; }
/// <summary>
/// Définit le nom de l'album le plus chroniqué dans le webzine.
/// </summary>
public string TopArtistAlbumsName { get; set; }
public string AlbumLePlusChronique { get; set; }
/// <summary>
/// Définit le nombre total de biographies d'artistes dans le webzine.
/// </summary>
public int BiographyCount { get; set; }
public int NombreBiographies { get; set; }
/// <summary>
/// Définit l'identifiant de la biographie d'artiste la plus lue dans le webzine.
/// </summary>
public int MostPlayedTrackId { get; set; }
public int IdMusiqueLaPlusJouee { get; set; }
/// <summary>
/// Définit le nom de la biographie d'artiste la plus lue dans le webzine.
/// </summary>
public string MostPlayedTrack { get; set; }
public string MusiqueLaPlusJouee { get; set; }
/// <summary>
/// Définit le nombre total de titres chroniqués dans le webzine.
/// </summary>
public int TrackCount { get; set; }
public int NombreTitres { get; set; }
/// <summary>
/// Définit le nombre total de genres musicaux chroniqués dans le webzine.
/// </summary>
public int GenreCount { get; set; }
public int NombreGenres { get; set; }
/// <summary>
/// Définit le nombre total de chroniques d'albums dans le webzine.
/// </summary>
public int TotalPlays { get; set; }
public int NombreLectures { get; set; }
/// <summary>
/// Définit le nombre total de likes sur les chroniques d'albums dans le webzine.
/// </summary>
public int TotalLikes { get; set; }
public int NombreLikes { get; set; }
}

View File

@@ -1,4 +1,4 @@
@model Webzine.WebApplication.Areas.Administration.ViewModels.Artiste.GroupeArtisteViewModel
@model IEnumerable<Webzine.Entity.Artiste>
@{
ViewData["Title"] = "Artiste";
@@ -21,7 +21,7 @@
</thead>
<tbody>
@foreach (var artiste in Model.Artistes)
@foreach (var artiste in Model)
{
<tr class="align-middle">
<td class="p-2">
@@ -29,13 +29,11 @@
</td>
<td class="text-center p-2">
<a asp-action="Edit" asp-route-id="@artiste.IdArtiste"
class="text-primary">
<a asp-action="Edit" asp-route-id="@artiste.IdArtiste">
<i class="fa fa-edit"></i>
</a>
<a asp-action="Delete" asp-route-id="@artiste.IdArtiste"
class="text-primary">
<a asp-action="Delete" asp-route-id="@artiste.IdArtiste">
<i class="fa fa-trash"></i>
</a>

View File

@@ -16,11 +16,11 @@
<div class="mb-4">
<h4>@Model.Contenu</h4>
<div class="text-muted">
<blockquote>
— <strong>@Model.Auteur</strong>
le @Model.DateCreation.ToString("dd/MM/yyyy HH:mm:ss")
le @Model.DateCreation.ToString("dd/MM/yyyy HH:mm:ss")
sur <em>@Model.TitreLibelle</em>
</div>
</blockquote>
</div>
<form asp-action="Delete" method="post">

View File

@@ -2,7 +2,7 @@
<h1 class="mb-4">Tableau de bord</h1>
<hr />
<hr/>
<div class="container">
@@ -11,17 +11,16 @@
<!-- ARTISTS -->
<div class="col-md-4">
<a asp-area="Administration"
asp-controller="Artiste"
class="text-decoration-none">
asp-controller="Artiste">
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
<i class="fa fa-users fa-3x text-primary mb-3"></i>
<h3 class="text-primary">
@Model.ArtistCount
<h3>
@Model.NombreArtistes
</h3>
<p class="text-primary">
<p>
artistes
</p>
</div>
@@ -29,23 +28,20 @@
</a>
</div>
<!-- L'ARTIST LE PLUS CHRONICLED -->
<!-- L'ARTIST LE PLUS CHRONIQUE -->
<div class="col-md-4">
<a asp-area=""
asp-controller="Artiste"
asp-route-nom="@Model.MostChronicledArtistName"
class="text-decoration-none">
asp-route-nom="@Model.ArtisteLePlusChronique">
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
<i class="fa fa-user fa-3x text-primary mb-3"></i>
<h3 class="text-primary">
@Model.MostChronicledArtistName
<h3>
@Model.ArtisteLePlusChronique
</h3>
<p class="text-primary">
artiste le plus chroniqué
</p>
<p>artiste le plus chroniqué</p>
</div>
</a>
@@ -55,17 +51,16 @@
<div class="col-md-4">
<a asp-area=""
asp-controller="Artiste"
asp-route-nom="@Model.TopArtistAlbumsName"
class="text-decoration-none">
asp-route-nom="@Model.AlbumLePlusChronique">
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
<i class="fa fa-trophy fa-3x text-primary mb-3"></i>
<h3 class="text-primary">
@Model.TopArtistAlbumsName
<h3>
@Model.AlbumLePlusChronique
</h3>
<p class="text-primary">
<p>
artiste avec le plus d'albums distincts
</p>
</div>
@@ -76,17 +71,16 @@
<!-- BIOGRAPHIES -->
<div class="col-md-4">
<a asp-area="Administration"
asp-controller="Titre"
class="text-decoration-none">
asp-controller="Titre">
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
<i class="fa fa-book fa-3x text-primary mb-3"></i>
<h3 class="text-primary">
@Model.BiographyCount
<h3>
@Model.NombreBiographies
</h3>
<p class="text-primary">
<p>
biographies d'artistes
</p>
</div>
@@ -99,17 +93,16 @@
<a asp-area=""
asp-controller="Titre"
asp-action="Details"
asp-route-id="@Model.MostPlayedTrackId"
class="text-decoration-none">
asp-route-id="@Model.IdMusiqueLaPlusJouee">
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
<i class="fa fa-compact-disc fa-3x text-primary mb-3"></i>
<h4 class="text-primary">
@Model.MostPlayedTrack
<h4>
@Model.MusiqueLaPlusJouee
</h4>
<p class="text-primary">
<p>
titre le plus lu
</p>
</div>
@@ -120,17 +113,16 @@
<!-- TITRE NOMBRE -->
<div class="col-md-4">
<a asp-area="Administration"
asp-controller="Titre"
class="text-decoration-none">
asp-controller="Titre">
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
<i class="fa fa-music fa-3x text-primary mb-3"></i>
<h3 class="text-primary">
@Model.TrackCount
<h3>
@Model.NombreTitres
</h3>
<p class="text-primary">
<p>
titres
</p>
</div>
@@ -141,17 +133,16 @@
<!-- GENRES -->
<div class="col-md-4">
<a asp-area="Administration"
asp-controller="Styles"
class="text-decoration-none">
asp-controller="Styles">
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
<i class="fa fa-tags fa-3x text-primary mb-3"></i>
<h3 class="text-primary">
@Model.GenreCount
<h3>
@Model.NombreGenres
</h3>
<p class="text-primary">
<p>
styles de musique
</p>
</div>
@@ -165,7 +156,7 @@
<i class="fa fa-eye fa-3x text-dark mb-3"></i>
<h3>
@Model.TotalPlays
@Model.NombreLectures
</h3>
<p>
@@ -180,7 +171,7 @@
<i class="fa fa-thumbs-up fa-3x text-dark mb-3"></i>
<h3>
@Model.TotalLikes
@Model.NombreLikes
</h3>
<p>

View File

@@ -17,10 +17,6 @@
@* On affiche le Libellé en gros *@
<h4>@Model.Libelle</h4>
@* On affiche l'ID discrètement en dessous *@
<div class="text-muted">
Identifiant technique : @Model.IdStyle
</div>
</div>
<form asp-action="Delete" method="post">

View File

@@ -36,7 +36,7 @@
<a asp-action="Edit" asp-route-id="@style.IdStyle" class="text-primary me-2" title="Éditer">
<i class="fas fa-edit"></i>
</a>
<a asp-action="Delete" asp-route-id="@style.IdStyle" class="text-primary" title="Supprimer">
<a asp-action="Delete" asp-route-id="@style.IdStyle" title="Supprimer">
<i class="fas fa-trash"></i>
</a>
</td>

View File

@@ -1,37 +1,44 @@
using Microsoft.AspNetCore.Mvc;
using Webzine.Repository.Fake;
using Webzine.WebApplication.ViewModels.Accueil;
// <copyright file="AccueilController.cs" company=" Equipe 1 - ">
// Copyright (c) Equipe 1 - . All rights reserved.
// </copyright>
namespace Webzine.WebApplication.Controllers
{
using Microsoft.AspNetCore.Mvc;
using Webzine.Repository.Fake;
using Webzine.WebApplication.ViewModels.Accueil;
/// <summary>
/// Permet de retourner la page d'accueil avec tous les éléments.
/// </summary>
public class AccueilController : Controller
{
// Injection du logger via le constructeur
private readonly ILogger<AccueilController> _logger;
private readonly IConfiguration _configuration;
private readonly ILogger<AccueilController> logger;
private readonly IConfiguration configuration;
/// <summary>
/// Initialise une nouvelle instance du <see cref="AccueilController"/> avec un service de journalisation et de configuration injectés.
/// Initialise une nouvelle instance de la classe <see cref="AccueilController"/>.
/// </summary>
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
/// <param name="configuration">Service d'injection de configuration pour accéder aux paramètres de l'application.</param>
public AccueilController(ILogger<AccueilController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
this._logger.LogDebug(1, "initialisation du AccueilController");
this.logger = logger;
this.configuration = configuration;
this.logger.LogDebug(1, "initialisation du AccueilController");
}
/// <summary>
/// Affiche la page d'accueil du webzine, présentant les derniers titres et les titres les plus populaires.
/// </summary>
/// <returns>La vue Index avec le ViewModel contenant les listes de titres à afficher.</returns>
public ActionResult Index()
public IActionResult Index()
{
_logger.LogInformation("Arrivée sur la page d'accueil");
this.logger.LogInformation("Arrivée sur la page d'accueil");
var derniereChronique = _configuration.GetValue<int>("Webzine:NombreDerniereChronique");
var topTitres = _configuration.GetValue<int>("Webzine:NombreDeTopTitres");
var derniereChronique = this.configuration.GetValue<int>("Webzine:NombreDerniereChronique");
var topTitres = this.configuration.GetValue<int>("Webzine:NombreDeTopTitres");
var titres = FakeDataFactory.GetTitres();
var vm = new AccueilIndexViewModel
@@ -44,10 +51,10 @@ namespace Webzine.WebApplication.Controllers
TopTitres = titres
.OrderByDescending(t => t.NbLikes)
.Take(topTitres)
.ToList()
.ToList(),
};
return View(vm);
return this.View(vm);
}
}
}

View File

@@ -1,7 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Webzine.Entity.Fixtures;
using Webzine.WebApplication.ViewModels;
using Webzine.WebApplication.ViewModels.Artiste;
namespace Webzine.WebApplication.Controllers
{
@@ -16,7 +14,7 @@ namespace Webzine.WebApplication.Controllers
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
public ArtisteController(ILogger<ArtisteController> logger)
{
_logger = logger;
this._logger = logger;
this._logger.LogDebug(1, "initialisation du ArtisteController");
}
@@ -28,9 +26,13 @@ namespace Webzine.WebApplication.Controllers
[HttpGet("/artiste/{nom}")]
public IActionResult Index(string nom)
{
_logger.LogInformation("Tentative d'accès à l'artiste avec le nom : {NomArtiste}", nom);
this._logger.LogInformation("Tentative d'accès à l'artiste avec le nom : {NomArtiste}", nom);
if (string.IsNullOrEmpty(nom)) return RedirectToAction("Index", "Accueil");
if (string.IsNullOrEmpty(nom))
{
this._logger.LogWarning("Nom de l'artiste manquant dans la requête.");
return RedirectToAction("Index", "Accueil");
}
// On transforme "fatal-bazooka" en "Fatal Bazooka" pour la factory
string nomPropre = System.Globalization.CultureInfo.CurrentCulture.TextInfo
@@ -41,20 +43,13 @@ namespace Webzine.WebApplication.Controllers
if (artiste == null)
{
_logger.LogWarning("Artiste non trouvé pour le nom : {NomArtiste}", nomPropre);
return NotFound();
this._logger.LogWarning("Artiste non trouvé pour le nom : {NomArtiste}", nomPropre);
return RedirectToAction("Index");
}
_logger.LogInformation("Artiste trouvé : {NomArtiste}", nom);
this._logger.LogInformation("Artiste trouvé : {NomArtiste}", nom);
// On remplit le ViewModel
var viewModel = new ArtisteModel
{
Artiste = artiste,
Titres = artiste.Titres
};
return View(viewModel);
return View(artiste);
}
}
}

View File

@@ -16,7 +16,7 @@ namespace Webzine.WebApplication.Controllers
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
public ContactController(ILogger<ContactController> logger)
{
_logger = logger;
this._logger = logger;
this._logger.LogDebug(1, "initialisation du ContactController");
}

View File

@@ -13,14 +13,14 @@ public class RechercheController : Controller
public RechercheController(ILogger<RechercheController> logger, ITitreRepository titreRepository)
{
_logger = logger;
_titreRepository = titreRepository;
this._logger = logger;
this._titreRepository = titreRepository;
}
[HttpPost("")]
public IActionResult Index(string mot)
{
_logger.LogInformation("Recherche artistes/titres pour le mot : {Mot}.", mot);
this._logger.LogInformation("Recherche artistes/titres pour le mot : {Mot}.", mot);
var titres = _titreRepository.Search(mot)
.Concat(_titreRepository.SearchByStyle(mot))

View File

@@ -23,10 +23,10 @@ public class TitreController : Controller
/// <param name="titreRepository">Repository des titres injecte.</param>
public TitreController(ILogger<TitreController> logger, ITitreRepository titreRepository)
{
_logger = logger;
_titreRepository = titreRepository;
this._logger = logger;
this._titreRepository = titreRepository;
_logger.LogInformation("Initialisation du controleur TitreController.");
this._logger.LogInformation("Initialisation du controleur TitreController.");
}
/// <summary>
@@ -37,14 +37,14 @@ public class TitreController : Controller
[HttpGet("{id}")]
public IActionResult Details(int id)
{
_logger.LogInformation("Demande d'affichage du detail pour le titre ID {Id}.", id);
this._logger.LogInformation("Demande d'affichage du detail pour le titre ID {Id}.", id);
var titre = FindById(id);
var titre = this._titreRepository.Find(id);
if (titre == null)
{
_logger.LogWarning("Titre avec ID {Id} introuvable.", id);
return NotFound();
this._logger.LogWarning("Titre avec ID {Id} introuvable.", id);
return RedirectToAction("Index");
}
var vm = new TitreDetail
@@ -79,7 +79,7 @@ public class TitreController : Controller
[HttpGet("style/{style}")]
public IActionResult Style(string style)
{
_logger.LogInformation("Recherche des titres pour le style : {Style}.", style);
this._logger.LogInformation("Recherche des titres pour le style : {Style}.", style);
var titresFiltres = _titreRepository.SearchByStyle(style).ToList();
@@ -100,14 +100,14 @@ public class TitreController : Controller
[HttpPost("like")]
public IActionResult Like(TitreLike model)
{
_logger.LogInformation("Ajout d'un like pour le titre ID {Id}.", model.IdTitre);
this._logger.LogInformation("Ajout d'un like pour le titre ID {Id}.", model.IdTitre);
var titre = FindById(model.IdTitre);
var titre = this._titreRepository.Find(model.IdTitre);
if (titre == null)
{
_logger.LogWarning("Impossible d'ajouter un like. Titre ID {Id} introuvable.", model.IdTitre);
return NotFound();
this._logger.LogWarning("Impossible d'ajouter un like. Titre ID {Id} introuvable.", model.IdTitre);
return RedirectToAction("Index");
}
titre.NbLikes++;
@@ -125,16 +125,16 @@ public class TitreController : Controller
{
if (!ModelState.IsValid)
{
_logger.LogWarning("Echec de validation du modele de commentaire pour le titre ID {Id}.", model.IdTitre);
this._logger.LogWarning("Echec de validation du modele de commentaire pour le titre ID {Id}.", model.IdTitre);
return RedirectToAction("Details", new { id = model.IdTitre });
}
var titre = FindById(model.IdTitre);
var titre = this._titreRepository.Find(model.IdTitre);
if (titre == null)
{
_logger.LogWarning("Impossible d'ajouter le commentaire. Titre ID {Id} introuvable.", model.IdTitre);
return NotFound();
this._logger.LogWarning("Impossible d'ajouter le commentaire. Titre ID {Id} introuvable.", model.IdTitre);
return RedirectToAction("Index");
}
var commentaire = new Commentaire
@@ -147,16 +147,11 @@ public class TitreController : Controller
titre.Commentaires.Add(commentaire);
_logger.LogInformation("Commentaire ajoute avec succes au titre ID {Id}.", model.IdTitre);
this._logger.LogInformation("Commentaire ajoute avec succes au titre ID {Id}.", model.IdTitre);
return RedirectToAction("Details", new { id = model.IdTitre });
}
private Titre? FindById(int id)
{
return _titreRepository.Find(id);
}
private static TitreStyleItem MapTitreItem(Titre titre)
{
return new TitreStyleItem

View File

@@ -0,0 +1 @@
*.sqlite*

View File

@@ -1,5 +1,7 @@
using NLog;
using NLog.Web;
using Microsoft.EntityFrameworkCore;
using Webzine.EntitiesContext;
using Webzine.Repository;
using Webzine.Repository.Contracts;
@@ -25,6 +27,9 @@ try
builder.Services.AddScoped<ITitreRepository, DbTitreRepository>();
builder.Services.AddScoped<IStyleRepository, DbStyleRepository>();
builder.Services.AddDbContext<WebzineDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));
// NLog: Setup NLog for Dependency injection
builder.Logging.ClearProviders();
builder.Host.UseNLog();
@@ -35,6 +40,12 @@ try
// le dossier wwwroot.
app.UseStaticFiles();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<WebzineDbContext>();
db.Database.EnsureCreated();
}
// Active le middleware permettant le routage des requetes entrantes.
app.UseRouting();

View File

@@ -1,6 +1,4 @@
using Webzine.Entity;
namespace Webzine.WebApplication.ViewModels.Accueil
namespace Webzine.WebApplication.ViewModels.Accueil
{
/// <summary>
/// ViewModel pour la page d'accueil du webzine, affichant les derniers titres et les titres les plus populaires.

View File

@@ -1,20 +0,0 @@
using Webzine.Entity;
namespace Webzine.WebApplication.ViewModels.Artiste
{
/// <summary>
/// ViewModel pour afficher les détails d'un artiste, incluant les informations de l'artiste et la liste de ses titres.
/// </summary>
public class ArtisteModel
{
/// <summary>
/// Artiste dont on affiche les détails.
/// </summary>
public Entity.Artiste Artiste { get; set; }
/// <summary>
/// Liste des titres de l'artiste.
/// </summary>
public List<Entity.Titre> Titres { get; set; }
}
}

View File

@@ -5,48 +5,22 @@
<h1>Derniers titres chroniqués</h1>
@* TEMPLATE *@
@* <div class="container">
<div class="container bg-light row p-3 mt-3">
<div class="col-auto">
<img class="img-thumbnail"
src="" />
</div>
<div class="col">
<a class="text-primary text-decoration-none fw-light h4">Justice - D.A.N.C.E</a>
<p class="mt-2 mb-3 text-muted ">
Insérer texte
</p>
<div class="d-flex flex-wrap align-items-center gap-3">
<a class="btn btn-primary btn-sm">Lire la suite</a>
<div class="d-flex align-items-center text-muted small">
<i class="fa-solid fa-calendar"></i>
Date :
17/12/2022 11:08:08
</div>
<div class="d-flex align-items-center text-muted small">
<i class="fa-solid fa-tags"></i>
<a class="text-decoration-none m-1">Insérer style</a>
</div>
</div>
</div>
</div>
</div> *@
<div class="container">
@foreach (var titre in Model.DerniersTitres)
{
<div class="container bg-light row p-3 mt-3">
<div class="col-auto">
<img class="img-thumbnail img-fluid"
style="max-width:200px;"
@* UrlJaquette *@
src="@titre.UrlJaquette" />
<div class="row bg-light p-3 mt-3 align-items-center">
<!-- Image -->
<div class="col-12 col-md-3 text-center mb-3 mb-md-0">
<img class="img-fluid img-thumbnail"
src="@titre.UrlJaquette"
alt="@titre.Libelle" />
</div>
<div class="col">
@* Artiste - Titre @titre.Artiste - @titre.Libelle*@
<!-- Contenu -->
<div class="col-12 col-md-9">
<!-- Artiste - Titre -->
<div class="fw-light h4 text-primary">
<a asp-action="Index"
asp-controller="Artiste"
@@ -60,62 +34,68 @@
@titre.Libelle
</a>
</div>
@* Chronique *@
<p class="mt-2 mb-3 text-muted ">
<!-- Chronique -->
<p class="mt-2 mb-3 text-muted">
@titre.Chronique
</p>
<!-- Footer -->
<div class="d-flex flex-wrap align-items-center gap-3">
<a asp-action="Details" asp-controller="Titre" asp-route-id="@titre.IdTitre" class="btn btn-primary btn-sm">Lire la suite</a>
<a asp-action="Details"
asp-controller="Titre"
asp-route-id="@titre.IdTitre"
class="btn btn-primary btn-sm">
Lire la suite
</a>
<div class="d-flex align-items-center text-muted small">
<i class="fa-solid fa-calendar me-1"> </i>
@* Date de création *@
<i class="fa-solid fa-calendar me-1"></i>
@titre.DateCreation
</div>
<div class="d-flex align-items-center text-muted small">
<i class="fa-solid fa-tags"></i>
@* Style *@
<a asp-controller="Titre" asp-action="Style" asp-route-id="Pop" class="text-decoration-none m-1">Pop</a>
<i class="fa-solid fa-tags me-1"></i>
<a asp-controller="Titre"
asp-action="Style"
asp-route-id="Pop"
class="text-decoration-none">
Pop
</a>
</div>
</div>
</div>
</div>
}
<!-- Bouton -->
<div class="row justify-content-end">
<button class="btn btn-secondary col-auto mt-3">Titres plus anciens >></button>
<button class="btn btn-secondary col-auto mt-3">
Titres plus anciens >>
</button>
</div>
</div>
@* TEMPLATE *@
@* <div class="container">
<div class="row">
<div class="card col m-1" style="width: 18rem;">
<img class="card-img-top"
src="" alt="Alternate Text" />
<div class="card-body">
<a class="card-link" href="#">Album</a><br />
par <a class="card-link">Artiste</a>
</div>
</div>
</div>
</div> *@
<div class="container">
<h1 class="mt-5">Titres les plus populaires</h1>
<div class="row">
@foreach (var titre in Model.TopTitres)
{
<div class="card col m-1" style="width: auto;">
<img class="card-img-top"
src="@titre.UrlJaquette" />
<div class="row g-3">
@foreach (var titre in Model.TopTitres)
{
<div class="col-12 col-md-6 col-lg-4">
<div class="card h-100">
<img class="card-img-top" src="@titre.UrlJaquette" alt="@titre.Album" />
<div class="card-body">
<a asp-controller="Titre" asp-action="Details" asp-route-id="@titre.IdTitre" class="card-link">@titre.Album</a><br />
par <a asp-controller="Artiste" asp-action="Index" asp-route-nom="@titre.Artiste.Nom" class="card-link">@titre.Artiste.Nom</a>
<div class="card-body">
<a asp-controller="Titre" asp-action="Details" asp-route-id="@titre.IdTitre" class="card-link">
@titre.Album
</a>
<br />
par
<a asp-controller="Artiste" asp-action="Index" asp-route-nom="@titre.Artiste.Nom" class="card-link">
@titre.Artiste.Nom
</a>
</div>
</div>
</div>
</div>
}
}
</div>
</div>

View File

@@ -1,4 +1,4 @@
@model Webzine.WebApplication.ViewModels.Artiste.ArtisteModel
@model Webzine.Entity.Artiste
@{
ViewData["Title"] = "Artiste";
@@ -6,11 +6,11 @@
<div class="container">
<h1>@Model.Artiste.Nom</h1>
<h1>@Model.Nom</h1>
<hr class="mb-5" />
<p class="lead">@Model.Artiste.Biographie</p>
<p class="lead">@Model.Biographie</p>
<h2 class="mt-5 mb-4">Albums</h2>
<hr class="mb-5" />

View File

@@ -21,42 +21,42 @@
<h2>Suivez-nous</h2>
<div class="row g-4 text-center">
<div class="col-md-4">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle">
<i class="fa-solid fa-link fa-3x text-primary mb-3"></i>
<div class="fw-bold text-primary">Site officiel du DIIAGE</div>
</a>
</div>
<div class="col-md-4">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle">
<i class="fa-brands fa-facebook fa-3x text-primary mb-3"></i>
<div class="fw-bold text-primary">Facebook</div>
</a>
</div>
<div class="col-md-4">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle">
<i class="fa-brands fa-instagram fa-3x text-primary mb-3"></i>
<div class="fw-bold text-primary">Instagram</div>
</a>
</div>
<div class="col-md-4">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle">
<i class="fa-brands fa-linkedin fa-3x text-primary mb-3"></i>
<div class="fw-bold text-primary">LinkedIn</div>
</a>
</div>
<div class="col-md-4">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle">
<i class="fa-solid fa-map fa-3x text-primary mb-3"></i>
<div class="fw-bold text-primary">Google Maps</div>
</a>
</div>
<div class="col-md-4">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle">
<i class="fa-brands fa-twitter fa-3x text-primary mb-3"></i>
<div class="fw-bold text-primary">Twitter</div>
</a>

View File

@@ -2,7 +2,6 @@
@{
ViewData["Title"] = "Recherche";
Layout = "_Layout";
}
<div class="container mt-4">

View File

@@ -3,11 +3,6 @@
*@
@{
}
<div class="site-footer text-bg-light mt-auto">
<footer class="d-flex flex-wrap justify-content-between align-items-center py-3">
<div class="col-md-4 d-flex align-items-center">
<span class="mb-3 mb-md-0 ms-5 text-body-secondary">&copy; ASP .NET Core - DIIAGE 2025 - 2026</span>
</div>
</footer>
</div>
<footer class="py-3 text-bg-light">
<p class="ms-5">&copy; ASP .NET Core - DIIAGE 2025 - 2026</p>
</footer>

View File

@@ -6,29 +6,25 @@
<title>@ViewData["Title"] - Webzine</title>
@* Ajout de bootstrap *@
<script src="~/js/bootstrap.min.js" defer></script>
<script src="~/js/bootstrap.bundle.js" defer></script>
<link rel="stylesheet" href="~/css/app.css">
<link rel="stylesheet" href="~/css/bootstrap.min.css">
<link rel="stylesheet" href="~/css/all.min.css">
<link rel="stylesheet" href="~/css/app.css">
@* Ajout de font-awesome *@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<script src="~/js/bootstrap.bundle.js" defer></script>
</head>
<body>
<div class="site-shell">
<partial name="_Header"/>
<div class="container-fluid flex-grow-1 py-4">
<div class="row g-0">
<main class="col mx-3">
@RenderBody()
</main>
@if(ViewContext.RouteData.Values["area"]?.ToString() != "Administration")
{
<partial name="_Sidebar" />
}
</div>
<partial name="_Header"/>
<div class="container-fluid flex-grow-1 py-4">
<div class="row">
<main class="col mx-3">
@RenderBody()
</main>
@if(ViewContext.RouteData.Values["area"]?.ToString() != "Administration")
{
<partial name="_Sidebar" />
}
</div>
<partial name="_Footer" />
</div>
<partial name="_Footer" />
</body>
</html>

View File

@@ -3,7 +3,7 @@
*@
@{
}
<aside class="col-3">
<aside class="col-lg-3 d-none d-lg-block">
<div>
<h2>À propos</h2>
<p>Retrouvez les dernières pépites sur notre webzine.</p>

View File

@@ -40,7 +40,7 @@
{
var style = Model.Details.Styles[i];
<a class="text-primary text-decoration-none fw-semibold"
<a class="text-primary fw-semibold"
asp-controller="Titre"
asp-action="Style"
asp-route-style="@style.Libelle">

View File

@@ -2,7 +2,6 @@
@{
ViewData["Title"] = $"Titres - {Model.StyleName}";
Layout = "_Layout";
}
<div class="container mt-4">
<div class="row">

View File

@@ -11,12 +11,13 @@
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
<Content Include="..\Webzine.Documentation\StyleCop\stylecop.json">
<AdditionalFiles Include="..\Webzine.Documentation\StyleCop\stylecop.json">
<Link>stylecop.json</Link>
</Content>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<Folder Include="Data\" />
<Folder Include="wwwroot\data\" />
<Folder Include="wwwroot\lib\" />
</ItemGroup>
@@ -24,6 +25,8 @@
<ItemGroup>
<PackageReference Include="Faker.Net" Version="2.0.163" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.5" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.*" />
<PackageReference Include="NLog" Version="6.1.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
@@ -33,6 +36,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Webzine.EntitiesContext\Webzine.EntitiesContext.csproj" />
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
<ProjectReference Include="..\Webzine.Repository\Webzine.Repository.csproj" />
</ItemGroup>

View File

@@ -7,7 +7,10 @@
},
"Webzine": {
"NombreDerniereChronique": 3,
"NombreDeTopTitres" : 3
"NombreDeTopTitres": 3
},
"ConnectionStrings": {
"DefaultConnection": "Data Source=Data/webzine.sqlite"
},
"AllowedHosts": "*"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long