Merge pull request 'Jalon 1' (#67) from dev into main
Reviewed-on: http://10.4.0.131/DI1-P4-E1/Webzine/pulls/67 Reviewed-by: j.vetu <josephine.vetu@diiage.org>
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,4 +4,6 @@ obj/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
[Ll]ogs/
|
||||
.idea/
|
||||
.idea/
|
||||
/vs/
|
||||
/.vs/
|
||||
|
||||
21
Webzine.Documentation/Rapport/equipe 1 - rapport.md
Normal file
21
Webzine.Documentation/Rapport/equipe 1 - rapport.md
Normal file
@@ -0,0 +1,21 @@
|
||||
**Loïc Masi** :
|
||||
|
||||
04/03 :
|
||||
- Création de 'AccueilController'
|
||||
- Création de la fonction Index() -> afficher l'accueil du webzine
|
||||
- Ajout de la vue 'Views/Accueil/Index.cshtml'
|
||||
- Mise en place d'un Header dans 'Views/Shared/_Header.cshtml'
|
||||
- Mise en place de la Sidebar dans 'Views/Shared/_Sidebar.cshtml'
|
||||
|
||||
05/03 :
|
||||
- Mise en place de fausse données dans 'Webzine.Repository' à l'aide de Faker
|
||||
- Ajout du ViewModel pour afficher les informations nécessaire sur la page d'accueil
|
||||
- Adaptation de quelques éléments sur la page (Bootstrap)
|
||||
- Mise en place du parametrage du nombre d'elements a afficher sur la page dans appsettings
|
||||
- Modifiaction du header pour ajouter le Dropdown (Administration) et ajout de quelques redirections
|
||||
|
||||
06/03 :
|
||||
- Récupération des modifications depuis 'dev'
|
||||
- Ajout des redirections vers les pages 'Administration'
|
||||
- Adaptation du layout principal pour adaptation entre public et administration
|
||||
- Ajout du Footer (sur toutes les pages)
|
||||
@@ -8,7 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest" Version="4.0.1"/>
|
||||
<PackageReference Include="MSTest" Version="4.0.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -16,7 +16,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting"/>
|
||||
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -25,4 +25,8 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
39
Webzine.Entity/Artiste.cs
Normal file
39
Webzine.Entity/Artiste.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
using System.Timers;
|
||||
|
||||
namespace Webzine.Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// Classe représentant un artiste.
|
||||
/// Lien avec l'entité <see cref="Titre"/> : un artiste peut avoir plusieurs titres, mais un titre n'a qu'un seul artiste.
|
||||
/// </summary>
|
||||
public class Artiste
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifiant unique de l'artiste.
|
||||
/// </summary>
|
||||
public int IdArtiste { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nom de l'artiste. Doit être compris entre 2 et 50 caractères et est obligatoire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(2)]
|
||||
[MaxLength(50)]
|
||||
[Display(Name = "Nom de l'artiste")]
|
||||
public string Nom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Biographie de l'artiste, qui peut contenir une description de sa carrière, de son style musical, etc. Doit être compris entre 10 et 4000 caractères et est obligatoire.
|
||||
/// </summary>
|
||||
public string Biographie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Liste des titres associés à cet artiste. Un artiste peut avoir plusieurs titres, mais un titre n'a qu'un seul artiste.
|
||||
/// </summary>
|
||||
public List<Titre> Titres { get; set; }
|
||||
}
|
||||
}
|
||||
54
Webzine.Entity/Commentaire.cs
Normal file
54
Webzine.Entity/Commentaire.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
|
||||
namespace Webzine.Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// Classe représentant un commentaire laissé par un utilisateur sur un titre.
|
||||
/// Lien avec l'entité <see cref="Titre"/> : un titre peut avoir plusieurs commentaires, mais un commentaire n'a qu'un seul titre.
|
||||
/// </summary>
|
||||
public class Commentaire
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifiant unique du commentaire.
|
||||
/// </summary>
|
||||
public int IdCommentaire { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contenu du commentaire laissé par l'utilisateur. Doit être compris entre 10 et 1000 caractères et est obligatoire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(10)]
|
||||
[MaxLength(1000)]
|
||||
[Display(Name = "Commentaire")]
|
||||
public string Contenu { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nom de l'auteur du commentaire. Doit être compris entre 2 et 30 caractères et est obligatoire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(2)]
|
||||
[MaxLength(30)]
|
||||
[Display(Name = "Nom")]
|
||||
public string Auteur { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Date de création du commentaire. Représente la date à laquelle le commentaire a été laissé par l'utilisateur. Doit être une date valide et est obligatoire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Display(Name = "Date de création")]
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifiant du titre associé à ce commentaire. Clé étrangère vers l'entité <see cref="Titre"/>.
|
||||
/// </summary>
|
||||
public int IdTitre { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Référence au titre associé à ce commentaire. Permet d'accéder aux informations du titre à partir du commentaire.
|
||||
/// </summary>
|
||||
public Titre Titre { get; set; }
|
||||
}
|
||||
}
|
||||
53
Webzine.Entity/Fixtures/ArtisteFactory.cs
Normal file
53
Webzine.Entity/Fixtures/ArtisteFactory.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Bogus;
|
||||
|
||||
namespace Webzine.Entity.Fixtures
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory pour générer des artistes avec des titres associés, à l'aide de la bibliothèque Bogus.
|
||||
/// </summary>
|
||||
public class ArtisteFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Récupère un artiste par son nom, en générant des données fictives pour ses titres associés.
|
||||
/// </summary>
|
||||
/// <param name="nom">Le nom de l'artiste à générer.</param>
|
||||
/// <returns>Un objet Artiste avec des titres associés générés de manière aléatoire.</returns>
|
||||
public static Artiste SeedArtisteByName(string nom)
|
||||
{
|
||||
// On définit nos albums "bouchonnés"
|
||||
var albumsData = new[]
|
||||
{
|
||||
new { Nom = "Bohemian Rhapsody", Image = "https://upload.wikimedia.org/wikipedia/en/9/9f/Bohemian_Rhapsody.png" },
|
||||
new { Nom = "Born This Way", Image = "https://static.wikia.nocookie.net/ladygaga/images/2/2d/BornThisWay-DeluxeEdition.jpg/revision/latest/scale-to-width-down/3500?cb=20111120030308" }
|
||||
};
|
||||
|
||||
var faker = new Bogus.Faker("fr");
|
||||
var tousLesTitres = new List<Titre>();
|
||||
|
||||
// Pour chaque album, on génère un paquet de titres
|
||||
foreach (var album in albumsData)
|
||||
{
|
||||
var nombreDeTitres = faker.Random.Number(3, 6);
|
||||
|
||||
var titresDeLalbum = new Faker<Titre>("fr")
|
||||
.RuleFor(t => t.IdTitre, f => f.IndexFaker + 1 + tousLesTitres.Count)
|
||||
.RuleFor(t => t.UrlJaquette, _ => album.Image)
|
||||
.RuleFor(t => t.Album, _ => album.Nom)
|
||||
.RuleFor(t => t.Duree, f => f.Random.Number(90, 180))
|
||||
.RuleFor(t => t.Libelle, f => f.Music.Genre() + " - " + f.Commerce.ProductName())
|
||||
.Generate(nombreDeTitres);
|
||||
|
||||
tousLesTitres.AddRange(titresDeLalbum);
|
||||
}
|
||||
|
||||
// On crée l'artiste final
|
||||
var artisteFaker = new Faker<Artiste>("fr")
|
||||
.RuleFor(a => a.IdArtiste, f => f.IndexFaker + 1)
|
||||
.RuleFor(a => a.Nom, _ => nom)
|
||||
.RuleFor(a => a.Biographie, f => f.Lorem.Paragraphs(2))
|
||||
.RuleFor(a => a.Titres, _ => tousLesTitres);
|
||||
|
||||
return artisteFaker.Generate();
|
||||
}
|
||||
}
|
||||
}
|
||||
192
Webzine.Entity/Fixtures/DataFactory.cs
Normal file
192
Webzine.Entity/Fixtures/DataFactory.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
namespace Webzine.Entity.Fixtures;
|
||||
|
||||
using Entity;
|
||||
using Faker;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public class DataFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionnaire contenant des données de musique réelles pour les titres générés.
|
||||
/// </summary>
|
||||
public Dictionary<string, (string TrackUrl, string ImageUrl)> RealMusicData { get; set; }
|
||||
= new Dictionary<string, (string TrackUrl, string ImageUrl)>
|
||||
{
|
||||
{ "juliana_chahayed_1", ("https://open.spotify.com/intl-fr/track/0qYLUdJQMhrCFA9dNZGcnm?si=b4fd45727a354a31",
|
||||
"https://i.scdn.co/image/ab67616d0000b2738e8e7b8f8f8f8f8f8f8f8f8") },
|
||||
|
||||
{ "mister_v_1", ("https://youtu.be/JeqUw7sGUK8?si=FnmFR2EgkVY6MhqQ",
|
||||
"https://img.youtube.com/vi/JeqUw7sGUK8/maxresdefault.jpg") },
|
||||
|
||||
{ "compagnie_creole_1", ("https://youtu.be/wfxt1SGWAI8",
|
||||
"https://img.youtube.com/vi/wfxt1SGWAI8/maxresdefault.jpg") },
|
||||
|
||||
{ "femto_1", ("https://open.spotify.com/intl-fr/track/0qYLUdJQMhrCFA9dNZGcnm?si=b4fd45727a354a31",
|
||||
"https://i.scdn.co/image/ab67616d0000b2738e8e7b8f8f8f8f8f8f8f8f8") },
|
||||
|
||||
{ "chat_noir_1", ("https://youtu.be/OTi4-q-_Tj0?si=SNnLd-6Y893nL5Au",
|
||||
"https://img.youtube.com/vi/OTi4-q-_Tj0/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_2", ("https://youtu.be/X-rJ01EyiAI?si=gH9m_U8oXI35OgWu",
|
||||
"https://img.youtube.com/vi/X-rJ01EyiAI/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_3", ("https://youtu.be/7lIM0wSx7kQ?si=S-RAsLzd4SiCQhE4",
|
||||
"https://img.youtube.com/vi/7lIM0wSx7kQ/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_4", ("https://youtu.be/dbxyKR1P8vA?si=aRzdYBhwvKptV8Ff",
|
||||
"https://img.youtube.com/vi/dbxyKR1P8vA/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_5", ("https://youtu.be/DDHvKo5NnII?si=NFRkdVQL2mELP0yn",
|
||||
"https://img.youtube.com/vi/DDHvKo5NnII/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_6", ("https://youtu.be/J9LgHNf2Qy0?si=YGO1ggiLkefa9901",
|
||||
"https://img.youtube.com/vi/J9LgHNf2Qy0/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_7", ("https://youtu.be/oadhHk2xs6c?si=mbnJCA6SGsoYXnUK",
|
||||
"https://img.youtube.com/vi/oadhHk2xs6c/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_8", ("https://youtu.be/6K1zCgkBaoE?si=quq9vQLJ-AmzjRJJ",
|
||||
"https://img.youtube.com/vi/6K1zCgkBaoE/maxresdefault.jpg") },
|
||||
|
||||
{ "german_rapper_1", ("https://www.youtube.com/watch?v=DWpg71HJt24",
|
||||
"https://img.youtube.com/vi/DWpg71HJt24/maxresdefault.jpg") },
|
||||
|
||||
{ "nizard_1", ("https://youtu.be/1fjA68k8DAU?si=2PuZSquVQGvfmQkZ",
|
||||
"https://img.youtube.com/vi/1fjA68k8DAU/maxresdefault.jpg") },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste d'artistes de musique en utilisant la bibliothèque Faker pour créer des noms d'artistes et des biographies réalistes.
|
||||
/// </summary>
|
||||
/// <param name="count">Nombre d'artistes à générer</param>
|
||||
/// <returns>Liste d'artistes de musique générés</returns>
|
||||
public List<Artiste> GenerateArtists(int count)
|
||||
{
|
||||
var artists = new List<Artiste>();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
artists.Add(new Artiste
|
||||
{
|
||||
IdArtiste = i + 1,
|
||||
Nom = Name.FullName(),
|
||||
Biographie = Lorem.Paragraph(),
|
||||
Titres = new List<Titre>()
|
||||
});
|
||||
}
|
||||
|
||||
return artists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste de styles de musique à partir d'une liste prédéfinie de noms de styles.
|
||||
/// </summary>
|
||||
/// <param name="count">Nombre de styles à générer (maximum 15, car il y a 15 styles prédéfinis)</param>
|
||||
/// <returns>Liste de styles de musique générés</returns>
|
||||
public List<Style> GenerateStyles(int count)
|
||||
{
|
||||
var styleNames = new[] { "Rock", "Pop", "Rap", "Électro", "Jazz", "Classique", "Reggae", "Blues", "Metal", "Folk", "Chanson française", "Hip-Hop", "R&B", "Soul", "Punk" };
|
||||
var styles = new List<Style>();
|
||||
|
||||
for (int i = 0; i < Math.Min(count, styleNames.Length); i++)
|
||||
{
|
||||
styles.Add(new Style
|
||||
{
|
||||
IdStyle = i + 1,
|
||||
Libelle = styleNames[i],
|
||||
Titres = new List<Titre>(),
|
||||
});
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste de titres de musique en utilisant des données de musique réelles pour les URL d'écoute et les jaquettes.
|
||||
/// </summary>
|
||||
/// <param name="count">Nombre de titres à générer</param>
|
||||
/// <param name="artists">Liste des artistes à associer aux titres générés</param>
|
||||
/// <param name="styles">Liste des styles à associer aux titres générés</param>
|
||||
/// <returns>Liste de titres de musique générés</returns>
|
||||
public List<Titre> GenerateTitres(int count, List<Artiste> artists, List<Style> styles)
|
||||
{
|
||||
var titres = new List<Titre>();
|
||||
var random = new Random();
|
||||
var musicItems = RealMusicData.Values.ToList();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var randomMusic = musicItems[random.Next(musicItems.Count)];
|
||||
var randomArtist = artists[random.Next(artists.Count)];
|
||||
|
||||
int numberOfStyles = random.Next(1, 4);
|
||||
var selectedStyles = styles.OrderBy(x => random.Next()).Take(numberOfStyles).ToList();
|
||||
|
||||
var titre = new Titre
|
||||
{
|
||||
IdTitre = i + 1,
|
||||
IdArtiste = randomArtist.IdArtiste,
|
||||
Artiste = randomArtist,
|
||||
Libelle = string.Join(" ", Lorem.Words(3)),
|
||||
Chronique = Lorem.Paragraph(),
|
||||
DateCreation = DateTime.Now.AddDays(-random.Next(1, 365)),
|
||||
Duree = random.Next(120, 360), // 2 à 6 minutes en secondes
|
||||
DateSortie = DateTime.Now.AddYears(-random.Next(0, 5)).AddDays(-random.Next(1, 365)),
|
||||
UrlJaquette = randomMusic.ImageUrl,
|
||||
UrlEcoute = randomMusic.TrackUrl,
|
||||
NbLectures = random.Next(1000, 1000000),
|
||||
NbLikes = random.Next(100, 100000), // Likes entre 100 et 100 000
|
||||
Album = $"Album {random.Next(1, 10)}",
|
||||
Commentaires = new List<Commentaire>(),
|
||||
Styles = new List<Style>(),
|
||||
};
|
||||
|
||||
foreach (var style in selectedStyles)
|
||||
{
|
||||
titre.Styles.Add(style);
|
||||
if (!style.Titres.Contains(titre))
|
||||
{
|
||||
style.Titres.Add(titre);
|
||||
}
|
||||
}
|
||||
|
||||
titres.Add(titre);
|
||||
randomArtist.Titres.Add(titres.Last());
|
||||
}
|
||||
|
||||
return titres;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste de commentaires pour les titres de musique générés, en associant chaque commentaire à un titre aléatoire.
|
||||
/// </summary>
|
||||
/// <param name="count">Nombre de commentaires à générer</param>
|
||||
/// <param name="titres">Liste des titres de musique à associer aux commentaires générés</param>
|
||||
/// <returns>Liste de commentaires générés</returns>
|
||||
public List<Commentaire> GenerateCommentaires(int count, List<Titre> titres)
|
||||
{
|
||||
var commentaires = new List<Commentaire>();
|
||||
var random = new Random();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var randomTitre = titres[random.Next(titres.Count)];
|
||||
|
||||
commentaires.Add(new Commentaire
|
||||
{
|
||||
IdCommentaire = i + 1,
|
||||
Contenu = Lorem.Paragraph(),
|
||||
Auteur = Name.FullName(),
|
||||
DateCreation = DateTime.Now.AddDays(-random.Next(1, 30)), // Commentaires créés dans les 30 derniers jours
|
||||
IdTitre = randomTitre.IdTitre,
|
||||
Titre = randomTitre
|
||||
});
|
||||
|
||||
randomTitre.Commentaires.Add(commentaires.Last());
|
||||
}
|
||||
|
||||
return commentaires;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Webzine.EntitiesContext;
|
||||
namespace Webzine.Entity.Fixtures;
|
||||
|
||||
public class SeedDataLocal
|
||||
{
|
||||
|
||||
}
|
||||
76
Webzine.Entity/Fixtures/TitreFactory.cs
Normal file
76
Webzine.Entity/Fixtures/TitreFactory.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Faker;
|
||||
using Webzine.Entity;
|
||||
|
||||
namespace Webzine.Repository.Fake
|
||||
{
|
||||
/// <summary>
|
||||
/// Classe de fabrique pour générer des données factices (fake data) pour les entités Artiste et Titre.
|
||||
/// </summary>
|
||||
public static class FakeDataFactory
|
||||
{
|
||||
//https://cdn-images.dzcdn.net/images/cover/311bba0fc112d15f72c8b5a65f0456c1/1900x1900-000000-80-0-0.jpg",
|
||||
/// <summary>
|
||||
/// Génère une liste d'artistes avec des données factices, incluant des titres associés à chaque artiste.
|
||||
/// </summary>
|
||||
/// <param name="count">Le nombre d'artistes à générer. Par défaut, 10 artistes seront générés.</param>
|
||||
/// <returns>Une liste d'objets Artiste avec des titres associés, générés de manière aléatoire.</returns>
|
||||
public static List<Artiste> GetArtistes(int count = 10)
|
||||
{
|
||||
var artistes = new List<Artiste>();
|
||||
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
artistes.Add(new Artiste
|
||||
{
|
||||
IdArtiste = i,
|
||||
Nom = Name.FullName(),
|
||||
Biographie = Lorem.Paragraph(),
|
||||
Titres = new List<Titre>()
|
||||
});
|
||||
}
|
||||
|
||||
return artistes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste de titres avec des données factices, en associant chaque titre à un artiste existant.
|
||||
/// </summary>
|
||||
/// <param name="count">Le nombre de titres à générer. Par défaut, 40 titres seront générés.</param>
|
||||
/// <returns>Une liste d'objets Titre avec des données factices, associés à des artistes générés de manière aléatoire.</returns>
|
||||
public static List<Titre> GetTitres(int count = 40)
|
||||
{
|
||||
var artistes = GetArtistes();
|
||||
var titres = new List<Titre>();
|
||||
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
var artiste = artistes[RandomNumber.Next(0, artistes.Count - 1)];
|
||||
|
||||
var titre = new Titre
|
||||
{
|
||||
IdTitre = i,
|
||||
IdArtiste = artiste.IdArtiste,
|
||||
Artiste = artiste,
|
||||
Libelle = Lorem.Sentence(3),
|
||||
Chronique = Lorem.Paragraph(),
|
||||
DateCreation = DateTime.Now.AddDays(-RandomNumber.Next(1, 100)),
|
||||
DateSortie = DateTime.Now.AddYears(-RandomNumber.Next(1, 20)),
|
||||
Duree = RandomNumber.Next(120, 420),
|
||||
UrlJaquette = "https://picsum.photos/300",
|
||||
UrlEcoute = Internet.Url(),
|
||||
NbLectures = RandomNumber.Next(0, 500),
|
||||
NbLikes = RandomNumber.Next(0, 200),
|
||||
Album = Lorem.Sentence(2),
|
||||
Commentaires = new List<Commentaire>()
|
||||
};
|
||||
|
||||
titres.Add(titre);
|
||||
artiste.Titres.Add(titre);
|
||||
}
|
||||
|
||||
return titres;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Webzine.Entity/Style.cs
Normal file
32
Webzine.Entity/Style.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
|
||||
namespace Webzine.Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// Classe représentant un style de musique.
|
||||
/// </summary>
|
||||
public class Style
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifiant unique du style.
|
||||
/// </summary>
|
||||
public int IdStyle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Libellé du style de musique. Doit être compris entre 2 et 50 caractères et est obligatoire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(2)]
|
||||
[MaxLength(50)]
|
||||
[Display(Name = "Libellé")]
|
||||
public string Libelle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Liste des titres associés à ce style de musique. Un style peut être associé à plusieurs titres, mais un titre n'est associé qu'à un seul style.
|
||||
/// </summary>
|
||||
public List<Titre> Titres { get; set; } = new List<Titre>();
|
||||
}
|
||||
}
|
||||
113
Webzine.Entity/Titre.cs
Normal file
113
Webzine.Entity/Titre.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
|
||||
namespace Webzine.Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// Classe représentant un titre de musique.
|
||||
/// Lien avec l'entité <see cref="Artiste"/> : un artiste peut avoir plusieurs titres, mais un titre n'a qu'un seul artiste.
|
||||
/// Lien avec l'entité <see cref="Commentaire"/> : un titre peut avoir plusieurs commentaires, mais un commentaire n'a qu'un seul titre.
|
||||
/// </summary>
|
||||
public class Titre
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifiant unique du titre.
|
||||
/// </summary>
|
||||
public int IdTitre { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifiant de l'artiste associé à ce titre. Clé étrangère vers l'entité <see cref="Artiste"/>.
|
||||
/// </summary>
|
||||
public int IdArtiste { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Référence à l'artiste associé à ce titre. Permet d'accéder aux informations de l'artiste à partir du titre.
|
||||
/// </summary>
|
||||
public Artiste Artiste { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Titre de la musique. Doit être compris entre 1 et 200 caractères.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
[MaxLength(200)]
|
||||
[Display(Name = "Titre")]
|
||||
public string Libelle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chronique du titre, qui peut contenir une description ou une critique de la musique. Doit être compris entre 10 et 4000 caractères.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(10)]
|
||||
[MaxLength(4000)]
|
||||
public string Chronique { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Date de création du titre. Représente la date à laquelle le titre a été ajouté à la base de données. Doit être une date valide et est obligatoire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Display(Name = "Date de création")]
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Durée du titre en secondes. Doit être un entier positif et est obligatoire.
|
||||
/// </summary>
|
||||
[Display(Name = "Durée en secondes")]
|
||||
public int Duree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Date de sortie du titre. Représente la date à laquelle le titre a été publié ou rendu disponible au public. Doit être une date valide et est obligatoire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Display(Name = "Date de sortie")]
|
||||
public DateTime DateSortie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// URL de la jaquette de l'album associé au titre. Doit être une chaîne de caractères comprise entre 1 et 250 caractères, et est obligatoire. Représente l'image de couverture de l'album auquel le titre appartient.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(250)]
|
||||
[Display(Name = "Jaquette de l'album")]
|
||||
public string UrlJaquette { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// URL d'écoute du titre. Doit être une chaîne de caractères comprise entre 13 et 250 caractères, et est obligatoire. Représente le lien vers la plateforme ou le service où le titre peut être écouté en ligne.
|
||||
/// </summary>
|
||||
[MinLength(13)]
|
||||
[MaxLength(250)]
|
||||
[Display(Name = "URL d'écoute")]
|
||||
public string UrlEcoute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre de lectures du titre. Représente le nombre de fois que le titre a été écouté ou joué. Doit être un entier positif et est obligatoire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Display(Name = "Nombre de lectures")]
|
||||
public int NbLectures { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre de likes du titre. Représente le nombre de fois que le titre a été aimé ou apprécié par les utilisateurs. Doit être un entier positif et est obligatoire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Display(Name = "Nombre de likes")]
|
||||
public int NbLikes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nom de l'album associé au titre. Doit être une chaîne de caractères comprise entre 1 et 200 caractères, et est obligatoire. Représente le nom de l'album auquel le titre appartient.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Album { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Liste des commentaires associés à ce titre. Permet d'accéder à tous les commentaires laissés par les utilisateurs sur ce titre. Un titre peut avoir plusieurs commentaires, mais un commentaire n'a qu'un seul titre.
|
||||
/// </summary>
|
||||
public List<Commentaire> Commentaires { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Liste des styles associés à ce titre. Permet d'accéder à tous les styles musicaux auxquels ce titre appartient. Un titre peut appartenir à plusieurs styles, et un style peut être associé à plusieurs titres (relation many-to-many).
|
||||
/// </summary>
|
||||
public List<Style> Styles { get; set; } = new List<Style>();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Bogus" Version="35.6.5" />
|
||||
<PackageReference Include="Faker.Net" Version="2.0.163" />
|
||||
<PackageReference Include="NLog" Version="6.1.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// using Webzine.Entity;
|
||||
using Webzine.Entity;
|
||||
|
||||
namespace Webzine.Repository.Contracts
|
||||
{
|
||||
@@ -10,20 +10,20 @@ namespace Webzine.Repository.Contracts
|
||||
|
||||
// void Delete(Titre titre);
|
||||
|
||||
// Titre Find(int idTitre);
|
||||
Titre? Find(int idTitre);
|
||||
|
||||
// IEnumerable<Titre> FindTitres(int offset, int limit);
|
||||
|
||||
// IEnumerable<Titre> FindAll();
|
||||
IEnumerable<Titre> FindAll();
|
||||
|
||||
// void IncrementNbLectures(Titre titre);
|
||||
|
||||
// void IncrementNbLikes(Titre titre);
|
||||
|
||||
// IEnumerable<Titre> Search(string mot);
|
||||
IEnumerable<Titre> Search(string mot);
|
||||
|
||||
// IEnumerable<Titre> SearchByStyle(string libelle);
|
||||
IEnumerable<Titre> SearchByStyle(string libelle);
|
||||
|
||||
// void Update(Titre titre);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,4 +21,8 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,71 @@
|
||||
namespace Webzine.Repository;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
public class LocalEntityRepository
|
||||
namespace Webzine.Repository;
|
||||
|
||||
/// <summary>
|
||||
/// Classe qui permet d'initialiser un jeu de données
|
||||
/// pour tester l'application
|
||||
/// </summary>
|
||||
public class LocalEntityRepository : ITitreRepository
|
||||
{
|
||||
}
|
||||
private readonly ILogger<LocalEntityRepository> _logger;
|
||||
private readonly List<Titre> _titres;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="LocalEntityRepository"/> avec un service de journalisation injecte.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecte pour suivre les operations du repository.</param>
|
||||
public LocalEntityRepository(ILogger<LocalEntityRepository> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_logger.LogDebug(1, "NLog injected into LocalEntityRepository");
|
||||
|
||||
var factory = new DataFactory();
|
||||
var artistes = factory.GenerateArtists(10);
|
||||
var styles = factory.GenerateStyles(10);
|
||||
|
||||
_titres = factory.GenerateTitres(30, artistes, styles);
|
||||
factory.GenerateCommentaires(50, _titres);
|
||||
}
|
||||
|
||||
public IEnumerable<Titre> Search(string mot)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(mot))
|
||||
{
|
||||
return Enumerable.Empty<Titre>();
|
||||
}
|
||||
|
||||
return _titres
|
||||
.Where(t => !string.IsNullOrWhiteSpace(t.Libelle)
|
||||
&& t.Libelle.Contains(mot, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(t => t.Libelle)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public Titre? Find(int idTitre)
|
||||
{
|
||||
return _titres.FirstOrDefault(t => t.IdTitre == idTitre);
|
||||
}
|
||||
|
||||
public IEnumerable<Titre> FindAll()
|
||||
{
|
||||
return _titres;
|
||||
}
|
||||
|
||||
public IEnumerable<Titre> SearchByStyle(string libelle)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(libelle))
|
||||
{
|
||||
return Enumerable.Empty<Titre>();
|
||||
}
|
||||
|
||||
return _titres
|
||||
.Where(t => t.Styles.Any(s => !string.IsNullOrWhiteSpace(s.Libelle)
|
||||
&& s.Libelle.Contains(libelle, StringComparison.OrdinalIgnoreCase)))
|
||||
.OrderBy(t => t.Libelle)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Faker.Net" Version="2.0.163" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.3" />
|
||||
<PackageReference Include="NLog" Version="6.1.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -21,4 +22,9 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
|
||||
<ProjectReference Include="..\Webzine.Repository.Contracts\Webzine.Repository.Contracts.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
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;
|
||||
|
||||
[Area("Administration")]
|
||||
public class ArtisteController : Controller
|
||||
{
|
||||
// Injection du logger via le constructeur
|
||||
private readonly ILogger<ArtisteController> _logger;
|
||||
private readonly List<Artiste> _artistes;
|
||||
|
||||
|
||||
public ArtisteController(ILogger<ArtisteController> logger)
|
||||
{
|
||||
_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.
|
||||
/// </summary>
|
||||
/// <returns>Redirection.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
|
||||
var _artistes_ordre = _artistes.OrderBy(t => t.Nom).ToList();
|
||||
|
||||
_logger.LogInformation("Initialisation du contrôleur TitreController pour l'Administration.");
|
||||
|
||||
GroupeArtisteViewModel groupeArtisteModel = new GroupeArtisteViewModel
|
||||
{
|
||||
Artistes = _artistes_ordre
|
||||
};
|
||||
|
||||
return View(groupeArtisteModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renvoie à la page pour créer un artiste.
|
||||
/// </summary>
|
||||
/// <returns>Redirection.</returns>
|
||||
public IActionResult Create()
|
||||
{
|
||||
var model = new AdminArtisteForm
|
||||
{
|
||||
Id = 0,
|
||||
Nom = string.Empty,
|
||||
Biographie = string.Empty
|
||||
};
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renvoie à la page modifier un artiste.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant de l'artiste à modifier. </param>
|
||||
/// <returns>Redirection.</returns>
|
||||
public IActionResult Edit(int id)
|
||||
{
|
||||
var artiste = _artistes.First(t => t.IdArtiste == id);
|
||||
|
||||
var model = new AdminArtisteForm
|
||||
{
|
||||
Id = artiste.IdArtiste,
|
||||
Nom = artiste.Nom,
|
||||
Biographie = artiste.Biographie
|
||||
};
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renvoie à la page supprimer un artiste.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant de l'artiste à supprimer. </param>
|
||||
/// <returns>Redirection.></returns>
|
||||
public IActionResult Delete(int id)
|
||||
{
|
||||
var artiste = _artistes.First(t => t.IdArtiste == id);
|
||||
var model = new AdminArtisteForm
|
||||
{
|
||||
Id = id,
|
||||
Nom = artiste.Nom,
|
||||
Biographie = artiste.Biographie
|
||||
};
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.Controllers
|
||||
{
|
||||
[Area("Administration")]
|
||||
public class CommentaireController : Controller
|
||||
{
|
||||
private readonly ILogger<CommentaireController> _logger;
|
||||
private readonly List<Commentaire> _commentaires;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="CommentaireController"/>.
|
||||
/// Les données sont générées dynamiquement via <see cref="DataFactory"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté.</param>
|
||||
public CommentaireController(ILogger<CommentaireController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
_logger.LogInformation("Initialisation du contrôleur CommentaireController.");
|
||||
|
||||
var factory = new DataFactory();
|
||||
|
||||
var _artistes = factory.GenerateArtists(10);
|
||||
var _styles = factory.GenerateStyles(10);
|
||||
var _titres = factory.GenerateTitres(30, _artistes, _styles);
|
||||
|
||||
_commentaires = factory.GenerateCommentaires(50, _titres);
|
||||
|
||||
_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()
|
||||
{
|
||||
// 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
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
{
|
||||
var commentaire = _commentaires
|
||||
.FirstOrDefault(c => c.IdCommentaire == id);
|
||||
|
||||
if (commentaire == null)
|
||||
return NotFound();
|
||||
|
||||
var vm = new CommentaireDeleteViewModel
|
||||
{
|
||||
IdCommentaire = commentaire.IdCommentaire,
|
||||
Auteur = commentaire.Auteur,
|
||||
Contenu = commentaire.Contenu,
|
||||
DateCreation = commentaire.DateCreation,
|
||||
TitreLibelle = commentaire.Titre?.Libelle
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.Controllers;
|
||||
|
||||
[Area("Administration")]
|
||||
public class DashboardController : Controller
|
||||
{
|
||||
private readonly ILogger<DashboardController> _logger;
|
||||
private readonly List<Titre> _titres;
|
||||
private readonly List<Style> _styles;
|
||||
private readonly List<Artiste> _artistes;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="DashboardController"/>.
|
||||
/// Les données sont générées dynamiquement via <see cref="DataFactory"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté.</param>
|
||||
public DashboardController(ILogger<DashboardController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
_logger.LogInformation("Initialisation du contrôleur TitreController.");
|
||||
|
||||
var factory = new DataFactory();
|
||||
|
||||
_artistes = factory.GenerateArtists(10);
|
||||
_styles = factory.GenerateStyles(10);
|
||||
_titres = factory.GenerateTitres(30, _artistes, _styles);
|
||||
|
||||
factory.GenerateCommentaires(50, _titres);
|
||||
|
||||
_logger.LogInformation("Données fictives générées avec succès.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche le tableau de bord de l'administration.
|
||||
/// </summary>
|
||||
/// <returns>La vue Index du tableau de bord.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
var mostChronicledArtist = _titres
|
||||
.GroupBy(t => t.Artiste)
|
||||
.OrderByDescending(g => g.Count())
|
||||
.FirstOrDefault();
|
||||
|
||||
var topArtistAlbums = _titres
|
||||
.GroupBy(t => t.Artiste)
|
||||
.OrderByDescending(g => g.Select(t => t.Album).Distinct().Count())
|
||||
.FirstOrDefault();
|
||||
|
||||
var mostPlayedTrack = _titres
|
||||
.OrderByDescending(t => t.NbLectures)
|
||||
.FirstOrDefault();
|
||||
|
||||
var model = new DashboardViewModel
|
||||
{
|
||||
ArtistCount = _artistes.Count,
|
||||
|
||||
MostChronicledArtistName = mostChronicledArtist?.Key.Nom,
|
||||
|
||||
TopArtistAlbumsName = topArtistAlbums?.Key.Nom,
|
||||
|
||||
BiographyCount = _artistes.Count(a => !string.IsNullOrEmpty(a.Biographie)),
|
||||
|
||||
MostPlayedTrackId = mostPlayedTrack?.IdTitre ?? 0,
|
||||
MostPlayedTrack = mostPlayedTrack?.Libelle,
|
||||
|
||||
TrackCount = _titres.Count,
|
||||
|
||||
GenreCount = _styles.Count,
|
||||
|
||||
TotalPlays = _titres.Sum(t => t.NbLectures),
|
||||
|
||||
TotalLikes = _titres.Sum(t => t.NbLikes)
|
||||
};
|
||||
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Style;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.Controllers
|
||||
{
|
||||
[Area("Administration")]
|
||||
public class StyleController : Controller
|
||||
{
|
||||
private readonly ILogger<StyleController> _logger;
|
||||
private readonly List<Style> _styles;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="StyleController"/>.
|
||||
/// Les données sont générées dynamiquement via <see cref="DataFactory"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté.</param>
|
||||
public StyleController(ILogger<StyleController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
_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.");
|
||||
}
|
||||
|
||||
// GET: Administration/Styles
|
||||
public ActionResult 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",
|
||||
},
|
||||
};
|
||||
|
||||
// Initialisation du ViewModel
|
||||
var viewModel = new StyleViewModel
|
||||
{
|
||||
Styles = listeStyles,
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
// GET: Administration/Styles/Create
|
||||
public ActionResult 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)
|
||||
{
|
||||
var style = this._styles
|
||||
.FirstOrDefault(c => c.IdStyle == id);
|
||||
|
||||
if (style == null)
|
||||
{
|
||||
return this.NotFound();
|
||||
}
|
||||
|
||||
var vm = new StyleDeleteViewModel
|
||||
{
|
||||
IdStyle = style.IdStyle,
|
||||
Libelle = style.Libelle,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
// Recherche du style (simulation avec la liste _styles)
|
||||
var style = _styles.FirstOrDefault(s => s.IdStyle == id);
|
||||
|
||||
if (style == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// Mapping vers le ViewModel
|
||||
var model = new StyleEditViewModel
|
||||
{
|
||||
IdStyle = style.IdStyle,
|
||||
Libelle = style.Libelle
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Titre;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.Controllers;
|
||||
|
||||
[Area("Administration")]
|
||||
public class TitreController : Controller
|
||||
{
|
||||
private readonly ILogger<TitreController> _logger;
|
||||
private readonly List<Titre> _titres;
|
||||
private readonly List<Style> _styles;
|
||||
private readonly List<Artiste> _artistes;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="TitreController"/>.
|
||||
/// Les données sont générées dynamiquement via <see cref="DataFactory"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté.</param>
|
||||
public TitreController(ILogger<TitreController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
_logger.LogInformation("Initialisation du contrôleur TitreController pour l'Administration.");
|
||||
|
||||
var factory = new DataFactory();
|
||||
|
||||
_artistes = factory.GenerateArtists(10);
|
||||
_styles = factory.GenerateStyles(10);
|
||||
_titres = factory.GenerateTitres(30, _artistes, _styles);
|
||||
|
||||
factory.GenerateCommentaires(50, _titres);
|
||||
|
||||
_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()
|
||||
{
|
||||
var model = _titres.Select(t => new AdminTitreList
|
||||
{
|
||||
Id = t.IdTitre,
|
||||
Artiste = t.Artiste?.Nom,
|
||||
Titre = t.Libelle,
|
||||
Duree = TimeSpan.FromSeconds(t.Duree).ToString(@"mm\:ss"),
|
||||
DateSortie = t.DateSortie,
|
||||
NbLectures = t.NbLectures,
|
||||
NbLikes = t.NbLikes,
|
||||
NbCommentaires = t.Commentaires?.Count ?? 0
|
||||
}).ToList();
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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()
|
||||
{
|
||||
var model = new AdminTitreForm
|
||||
{
|
||||
Artistes = _artistes.Select(a => new SelectListItem
|
||||
{
|
||||
Value = a.IdArtiste.ToString(),
|
||||
Text = a.Nom
|
||||
}).ToList(),
|
||||
|
||||
AllStyles = _styles.Select(s => new SelectListItem
|
||||
{
|
||||
Value = s.IdStyle.ToString(),
|
||||
Text = s.Libelle
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
var titre = _titres.First(t => t.IdTitre == id);
|
||||
|
||||
var model = new AdminTitreForm
|
||||
{
|
||||
Id = titre.IdTitre,
|
||||
IdArtiste = titre.IdArtiste,
|
||||
Libelle = titre.Libelle,
|
||||
Album = titre.Album,
|
||||
Chronique = titre.Chronique,
|
||||
DateSortie = titre.DateSortie,
|
||||
Duree = titre.Duree,
|
||||
UrlJaquette = titre.UrlJaquette,
|
||||
UrlEcoute = titre.UrlEcoute,
|
||||
NbLectures = titre.NbLectures,
|
||||
NbLikes = titre.NbLikes,
|
||||
Styles = titre.Styles.Select(s => s.IdStyle).ToList(),
|
||||
|
||||
Artistes = _artistes.Select(a => new SelectListItem
|
||||
{
|
||||
Value = a.IdArtiste.ToString(),
|
||||
Text = a.Nom
|
||||
}).ToList(),
|
||||
|
||||
AllStyles = _styles.Select(s => new SelectListItem
|
||||
{
|
||||
Value = s.IdStyle.ToString(),
|
||||
Text = s.Libelle
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
var titre = _titres.First(t => t.IdTitre == id);
|
||||
|
||||
var model = new AdminTitreDelete
|
||||
{
|
||||
Id = titre.IdTitre,
|
||||
Titre = titre.Libelle,
|
||||
Artiste = titre.Artiste?.Nom
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Artiste
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la création et la modification d'un artiste dans l'administration.
|
||||
/// </summary>
|
||||
public class AdminArtisteForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit l'identifiant de l'artiste.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste.
|
||||
/// </summary>
|
||||
public string Nom { get; set; }
|
||||
/// <summary>
|
||||
/// Définit la biographie de l'artiste.
|
||||
/// </summary>
|
||||
public string Biographie { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour la suppression d'un commentaire, contenant les informations nécessaires pour afficher les détails du commentaire à supprimer.
|
||||
/// </summary>
|
||||
public class CommentaireDeleteViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit l'identifiant du commentaire à supprimer.
|
||||
/// </summary>
|
||||
public int IdCommentaire { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'auteur du commentaire à supprimer.
|
||||
/// </summary>
|
||||
public string? Auteur { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le contenu du commentaire à supprimer.
|
||||
/// </summary>
|
||||
public string? Contenu { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la date de création du commentaire à supprimer.
|
||||
/// </summary>
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le titre du commentaire à supprimer.
|
||||
/// </summary>
|
||||
public string? TitreLibelle { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// <copyright file="CommentaireViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour afficher la liste des commentaires en administration.
|
||||
/// </summary>
|
||||
public class CommentaireViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Obtient ou définit la liste des commentaires.
|
||||
/// </summary>
|
||||
public IEnumerable<Entity.Commentaire> Commentaires { get; set; } = new List<Entity.Commentaire>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour le tableau de bord de l'administration du webzine.
|
||||
/// </summary>
|
||||
public class DashboardViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit le nombre total d'artistes chroniqués dans le webzine.
|
||||
/// </summary>
|
||||
public int ArtistCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste le plus chroniqué dans le webzine.
|
||||
/// </summary>
|
||||
public string MostChronicledArtistName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'album le plus chroniqué dans le webzine.
|
||||
/// </summary>
|
||||
public string TopArtistAlbumsName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de biographies d'artistes dans le webzine.
|
||||
/// </summary>
|
||||
public int BiographyCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit l'identifiant de la biographie d'artiste la plus lue dans le webzine.
|
||||
/// </summary>
|
||||
public int MostPlayedTrackId { get; set; }
|
||||
/// <summary>
|
||||
/// Définit le nom de la biographie d'artiste la plus lue dans le webzine.
|
||||
/// </summary>
|
||||
public string MostPlayedTrack { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de titres chroniqués dans le webzine.
|
||||
/// </summary>
|
||||
public int TrackCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de genres musicaux chroniqués dans le webzine.
|
||||
/// </summary>
|
||||
public int GenreCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de chroniques d'albums dans le webzine.
|
||||
/// </summary>
|
||||
public int TotalPlays { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de likes sur les chroniques d'albums dans le webzine.
|
||||
/// </summary>
|
||||
public int TotalLikes { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// <copyright file="StyleDeleteViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. Tout droit réservé.
|
||||
// </copyright>
|
||||
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la création d'un style en administration.
|
||||
/// </summary>
|
||||
|
||||
public class StyleCreateViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
|
||||
public string Libelle { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// <copyright file="StyleDeleteViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. Tout droit réservé.
|
||||
// </copyright>
|
||||
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la suppression d'un style en administration.
|
||||
/// </summary>
|
||||
|
||||
public class StyleDeleteViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Obtient ou définit l'identifiant du style à supprimer.
|
||||
/// </summary>
|
||||
|
||||
public int IdStyle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
|
||||
public string Libelle { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// <copyright file="StyleDeleteViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. Tout droit réservé.
|
||||
// </copyright>
|
||||
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la modification d'un style en administration.
|
||||
/// </summary>
|
||||
|
||||
public class StyleEditViewModel
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
|
||||
public int IdStyle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
|
||||
public string Libelle { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// <copyright file="StyleViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. Tout droit réservé.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour afficher la liste des commentaires en administration.
|
||||
/// </summary>
|
||||
public class StyleViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Obtient ou définit la liste des commentaires.
|
||||
/// </summary>
|
||||
public IEnumerable<Entity.Style> Styles { get; set; } = new List<Entity.Style>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Titre;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour la suppression d'un titre dans l'administration.
|
||||
/// </summary>
|
||||
public class AdminTitreDelete
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit l'identifiant du titre à supprimer.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le titre du titre à supprimer.
|
||||
/// </summary>
|
||||
public string Titre { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste associé au titre à supprimer.
|
||||
/// </summary>
|
||||
public string Artiste { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Titre;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour la création et la modification d'un titre dans l'administration.
|
||||
/// </summary>
|
||||
public class AdminTitreForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit l'identifiant du titre.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit l'identifiant de l'artiste associé au titre.
|
||||
/// </summary>
|
||||
public int IdArtiste { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le titre du titre.
|
||||
/// </summary>
|
||||
public string Libelle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'album associé au titre.
|
||||
/// </summary>
|
||||
public string Album { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la chronique du titre, peut-être une critique ou une description du titre.
|
||||
/// </summary>
|
||||
public string Chronique { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la date de sortie du titre.
|
||||
/// </summary>
|
||||
public DateTime DateSortie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la durée du titre en secondes.
|
||||
/// </summary>
|
||||
public int Duree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit l'URL de la jaquette de l'album associé au titre.
|
||||
/// </summary>
|
||||
public string UrlJaquette { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit l'URL d'écoute du titre.
|
||||
/// </summary>
|
||||
public string UrlEcoute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre de lectures du titre.
|
||||
/// </summary>
|
||||
public int NbLectures { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre de likes du titre.
|
||||
/// </summary>
|
||||
public int NbLikes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la liste des identifiants des styles associés au titre.
|
||||
/// </summary>
|
||||
public List<int> Styles { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Définit la liste des artistes disponibles pour la sélection dans le formulaire de création ou de modification d'un titre.
|
||||
/// </summary>
|
||||
public List<SelectListItem> Artistes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la liste des styles disponibles pour la sélection dans le formulaire de création ou de modification d'un titre.
|
||||
/// </summary>
|
||||
public List<SelectListItem> AllStyles { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Titre
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la liste des titres dans l'administration, affichant les informations essentielles pour chaque titre.
|
||||
/// </summary>
|
||||
public class AdminTitreList
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit l'identifiant du titre.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste associé au titre.
|
||||
/// </summary>
|
||||
public string Artiste { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le titre du titre.
|
||||
/// </summary>
|
||||
public string Titre { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'album associé au titre.
|
||||
/// </summary>
|
||||
public string Duree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la date de sortie du titre, affichée dans un format lisible pour les utilisateurs de l'administration.
|
||||
/// </summary>
|
||||
public DateTime DateSortie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre de lectures du titre.
|
||||
/// </summary>
|
||||
public int NbLectures { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre de likes du titre.
|
||||
/// </summary>
|
||||
public int NbLikes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre de commentaires associés au titre.
|
||||
/// </summary>
|
||||
public int NbCommentaires { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Artiste.AdminArtisteForm
|
||||
|
||||
<h1>Créer un artiste</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<form asp-action="Create" method="post">
|
||||
|
||||
<partial name="_Form" />
|
||||
|
||||
</form>
|
||||
@@ -0,0 +1,32 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Artiste.AdminArtisteForm
|
||||
|
||||
<div class="container mt-4">
|
||||
|
||||
<h1 class="mb-3">Supprimer un artiste</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
Etes-vous sûr de vouloir supprimer l'artiste
|
||||
"@Model.Nom" ?
|
||||
</p>
|
||||
|
||||
<form asp-action="Delete" method="post">
|
||||
|
||||
<input type="hidden" asp-for="Id" />
|
||||
|
||||
<button type="submit" class="btn btn-danger">
|
||||
Supprimer
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
||||
<a asp-action="Index">
|
||||
Retour à l'administration des artistes
|
||||
</a>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Artiste.AdminArtisteForm
|
||||
|
||||
<h1>Editer un artiste</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<form asp-action="Edit" method="post">
|
||||
|
||||
<input type="hidden" asp-for="Id"/>
|
||||
|
||||
<partial name="_Form" />
|
||||
|
||||
</form>
|
||||
@@ -0,0 +1,48 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Artiste.GroupeArtisteViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Artiste";
|
||||
}
|
||||
|
||||
|
||||
<div class="container mt-4">
|
||||
<h1 class="mb-4">Artiste</h1>
|
||||
<hr />
|
||||
<a asp-action="Create" class="btn btn-primary mb-3">
|
||||
<i class="fa fa-plus"></i> Ajouter un nouvel artiste
|
||||
</a>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover table-bordered table-sm">
|
||||
<thead class="table-active">
|
||||
<tr>
|
||||
<th scope="col" class="p-2">Nom</th>
|
||||
<th scope="col" class="text-center p-2" style="width: 100px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@foreach (var artiste in Model.Artistes)
|
||||
{
|
||||
<tr class="align-middle">
|
||||
<td class="p-2">
|
||||
@artiste.Nom
|
||||
</td>
|
||||
<td class="text-center p-2">
|
||||
|
||||
<a asp-action="Edit" asp-route-id="@artiste.IdArtiste"
|
||||
class="text-primary">
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
<a asp-action="Delete" asp-route-id="@artiste.IdArtiste"
|
||||
class="text-primary">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,38 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Artiste.AdminArtisteForm
|
||||
|
||||
<div class="container">
|
||||
<!-- ARTISTE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Nom de l'artiste<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Nom" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BIOGRAPHIE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Biographie<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Biographie" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- BOUTONS -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-9 offset-md-3">
|
||||
|
||||
<button type="submit" class="btn btn-primary me-2">
|
||||
Sauvegarder
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<a asp-action="Index"
|
||||
class="btn text-primary">
|
||||
Retour à l'administration des artistes
|
||||
</a>
|
||||
</div>
|
||||
@@ -0,0 +1,42 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire.CommentaireDeleteViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Supprimer un commentaire";
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
|
||||
<h1 class="mb-3">Supprimer un commentaire</h1>
|
||||
<hr />
|
||||
|
||||
<p class="mb-4">
|
||||
Êtes-vous sûr de vouloir supprimer le commentaire suivant ?
|
||||
</p>
|
||||
|
||||
<div class="mb-4">
|
||||
<h4>@Model.Contenu</h4>
|
||||
|
||||
<div class="text-muted">
|
||||
— <strong>@Model.Auteur</strong>
|
||||
le @Model.DateCreation.ToString("dd/MM/yyyy HH:mm:ss")
|
||||
sur <em>@Model.TitreLibelle</em>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form asp-action="Delete" method="post">
|
||||
<input type="hidden" asp-for="IdCommentaire" />
|
||||
|
||||
<button type="submit" class="btn btn-danger">
|
||||
Supprimer
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<a asp-action="Index"
|
||||
class="btn-link">
|
||||
Retour à l'administration des commentaires
|
||||
</a>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire.CommentaireViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Commentaires";
|
||||
}
|
||||
|
||||
|
||||
<div class="container mt-4">
|
||||
<h1 class="mb-4">Commentaires</h1>
|
||||
<hr />
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover table-bordered">
|
||||
<thead class="table-active">
|
||||
<tr>
|
||||
<th scope="col">Titre</th>
|
||||
<th scope="col">Auteur</th>
|
||||
<th scope="col">Commentaire</th>
|
||||
<th scope="col">Date de création</th>
|
||||
<th scope="col" class="text-center p-2" style="width: 100px" ;>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (Webzine.Entity.Commentaire commentaire in Model.Commentaires)
|
||||
{
|
||||
<tr class="align-middle">
|
||||
<td>
|
||||
@commentaire.Titre.Libelle
|
||||
</td>
|
||||
<td>
|
||||
@commentaire.Auteur
|
||||
</td>
|
||||
<td>
|
||||
@commentaire.Contenu
|
||||
</td>
|
||||
<td>
|
||||
@commentaire.DateCreation.ToString("dd/MM/yyyy HH:mm:ss")
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a asp-action="Delete" asp-route-id="@commentaire.IdCommentaire" class="d-inline btn btn-link text-primary">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,194 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.DashboardViewModel
|
||||
|
||||
<h1 class="mb-4">Tableau de bord</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="row g-4 text-center">
|
||||
|
||||
<!-- ARTISTS -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area="Administration"
|
||||
asp-controller="Artiste"
|
||||
class="text-decoration-none">
|
||||
|
||||
<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>
|
||||
|
||||
<p class="text-primary">
|
||||
artistes
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- L'ARTIST LE PLUS CHRONICLED -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area=""
|
||||
asp-controller="Artiste"
|
||||
asp-route-nom="@Model.MostChronicledArtistName"
|
||||
class="text-decoration-none">
|
||||
|
||||
<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>
|
||||
|
||||
<p class="text-primary">
|
||||
artiste le plus chroniqué
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- LE PLUS D'ALBUMS -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area=""
|
||||
asp-controller="Artiste"
|
||||
asp-route-nom="@Model.TopArtistAlbumsName"
|
||||
class="text-decoration-none">
|
||||
|
||||
<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>
|
||||
|
||||
<p class="text-primary">
|
||||
artiste avec le plus d'albums distincts
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- BIOGRAPHIES -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area="Administration"
|
||||
asp-controller="Titre"
|
||||
class="text-decoration-none">
|
||||
|
||||
<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>
|
||||
|
||||
<p class="text-primary">
|
||||
biographies d'artistes
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- LA TRACK AVEC LE PLUS D'ÉCOUTE -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area=""
|
||||
asp-controller="Titre"
|
||||
asp-action="Details"
|
||||
asp-route-id="@Model.MostPlayedTrackId"
|
||||
class="text-decoration-none">
|
||||
|
||||
<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>
|
||||
|
||||
<p class="text-primary">
|
||||
titre le plus lu
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- TITRE NOMBRE -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area="Administration"
|
||||
asp-controller="Titre"
|
||||
class="text-decoration-none">
|
||||
|
||||
<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>
|
||||
|
||||
<p class="text-primary">
|
||||
titres
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- GENRES -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area="Administration"
|
||||
asp-controller="Styles"
|
||||
class="text-decoration-none">
|
||||
|
||||
<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>
|
||||
|
||||
<p class="text-primary">
|
||||
styles de musique
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- NOMBRE DE LECTURES -->
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-4 bg-light h-100">
|
||||
<i class="fa fa-eye fa-3x text-dark mb-3"></i>
|
||||
|
||||
<h3>
|
||||
@Model.TotalPlays
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
lectures
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TOTAL LIKES -->
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-4 bg-light h-100">
|
||||
<i class="fa fa-thumbs-up fa-3x text-dark mb-3"></i>
|
||||
|
||||
<h3>
|
||||
@Model.TotalLikes
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
likes
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,46 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Style.StyleCreateViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Créer un style";
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
<h1 class="mb-3">Créer un style</h1>
|
||||
<hr />
|
||||
|
||||
<form asp-action="Create" method="post" class="mt-4">
|
||||
|
||||
@* Utilisation de d-flex pour aligner les éléments sur une ligne *@
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
@* Label *@
|
||||
<div class="me-3">
|
||||
<label asp-for="Libelle" class="col-form-label fw-bold">
|
||||
Libellé<span class="text-danger">*</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@* Input *@
|
||||
<div class="me-3">
|
||||
<input asp-for="Libelle" class="form-control" style="width: 250px;" />
|
||||
</div>
|
||||
|
||||
@* Bouton *@
|
||||
<div>
|
||||
<button type="submit" class="btn btn-primary">Sauvegarder</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Zone d'erreur en dessous *@
|
||||
<div class="mt-2">
|
||||
<span asp-validation-for="Libelle" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="mt-4">
|
||||
<a asp-action="Index" class="btn-link">Retour à l'administration des styles</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Style.StyleDeleteViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Supprimer un style";
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
|
||||
<h1 class="mb-3">Supprimer un style</h1>
|
||||
<hr />
|
||||
|
||||
<p class="mb-4">
|
||||
Êtes-vous sûr de vouloir supprimer le style suivant ?
|
||||
</p>
|
||||
|
||||
<div class="mb-4">
|
||||
@* 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">
|
||||
@* Champ caché indispensable pour transmettre l'ID au contrôleur en POST *@
|
||||
<input type="hidden" asp-for="IdStyle" />
|
||||
|
||||
<button type="submit" class="btn btn-danger">
|
||||
Supprimer
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<a asp-action="Index"
|
||||
class="btn-link">
|
||||
Retour à l'administration des styles
|
||||
</a>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,47 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Style.StyleEditViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Editer un style";
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
<h1 class="mb-3">Editer un style</h1>
|
||||
<hr />
|
||||
|
||||
<form asp-action="Edit" method="post" class="mt-4">
|
||||
|
||||
@* Champ caché pour l'ID *@
|
||||
<input type="hidden" asp-for="IdStyle" />
|
||||
|
||||
@* Conteneur Flex pour alignement horizontal (Label - Input - Bouton) *@
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
@* Label *@
|
||||
<div class="me-3">
|
||||
<label asp-for="Libelle" class="col-form-label fw-bold">
|
||||
Libellé<span class="text-danger">*</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@* Input *@
|
||||
<div class="me-3">
|
||||
<input asp-for="Libelle" class="form-control" style="width: 250px;" />
|
||||
</div>
|
||||
|
||||
@* Bouton *@
|
||||
<div>
|
||||
<button type="submit" class="btn btn-primary">Sauvegarder</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Zone d'erreur spécifique au champ *@
|
||||
<div class="mt-2">
|
||||
<span asp-validation-for="Libelle" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="mt-4">
|
||||
<a asp-action="Index" class="btn-link">Retour à l'administration des styles</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,55 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Style.StyleViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Styles";
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
|
||||
<h1 class="mb-3">Styles</h1>
|
||||
<hr />
|
||||
|
||||
<div class="mb-3">
|
||||
<a asp-action="Create" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i> Ajouter un nouvel style
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover table-bordered table-sm">
|
||||
<thead class="table-active">
|
||||
<tr>
|
||||
<th scope="col" class="p-2">Libellé</th>
|
||||
<th scope="col" class="text-center p-2" style="width: 100px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (Model.Styles != null && Model.Styles.Any())
|
||||
{
|
||||
@foreach (Webzine.Entity.Style style in Model.Styles)
|
||||
{
|
||||
<tr class="align-middle">
|
||||
<td class="p-2">
|
||||
@style.Libelle
|
||||
</td>
|
||||
<td class="text-center p-2">
|
||||
<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">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<tr>
|
||||
<td colspan="2" class="text-center p-2">Aucun style disponible.</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Titre.AdminTitreForm
|
||||
|
||||
<h1>Créer un titre</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<form asp-action="Create" method="post">
|
||||
|
||||
<partial name="_Form"/>
|
||||
|
||||
</form>
|
||||
@@ -0,0 +1,34 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Titre.AdminTitreDelete
|
||||
|
||||
<div class="container mt-4">
|
||||
|
||||
<h1 class="mb-3">Supprimer un titre</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
Etes-vous sûr de vouloir supprimer le titre
|
||||
"@Model.Titre"
|
||||
de
|
||||
@Model.Artiste ?
|
||||
</p>
|
||||
|
||||
<form asp-action="Delete" method="post">
|
||||
|
||||
<input type="hidden" asp-for="Id"/>
|
||||
|
||||
<button type="submit" class="btn btn-danger">
|
||||
Supprimer
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<a asp-action="Index">
|
||||
Retour à l'administration des titres
|
||||
</a>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Titre.AdminTitreForm
|
||||
|
||||
<h1>Editer un titre</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
<form asp-action="Edit" method="post">
|
||||
|
||||
<input type="hidden" asp-for="Id"/>
|
||||
|
||||
<partial name="_Form"/>
|
||||
|
||||
</form>
|
||||
@@ -0,0 +1,63 @@
|
||||
@model IEnumerable<Webzine.WebApplication.Areas.Administration.ViewModels.Titre.AdminTitreList>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Titres";
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
|
||||
<h1 class="mb-3">Titres</h1>
|
||||
<hr />
|
||||
|
||||
<a asp-action="Create" class="btn btn-primary mb-3">
|
||||
<i class="fa fa-plus"></i> Ajouter un nouveau titre
|
||||
</a>
|
||||
<div class="table-responsive">
|
||||
|
||||
<table class="table table-striped table-hover table-bordered">
|
||||
<thead class="table-active">
|
||||
<tr>
|
||||
<th>Artiste</th>
|
||||
<th>Titre</th>
|
||||
<th>Durée</th>
|
||||
<th>Date de sortie</th>
|
||||
<th class="text-center"><i class="fa fa-eye"></i></th>
|
||||
<th class="text-center"><i class="fa fa-thumbs-up"></i></th>
|
||||
<th class="text-center"><i class="fa fa-comments"></i></th>
|
||||
<th class="text-center action-column">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
@foreach (var item in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.Artiste</td>
|
||||
<td>@item.Titre</td>
|
||||
<td>@item.Duree</td>
|
||||
<td>@item.DateSortie.ToString("dd/MM/yyyy")</td>
|
||||
|
||||
<td class="text-center">@item.NbLectures</td>
|
||||
<td class="text-center">@item.NbLikes</td>
|
||||
<td class="text-center">@item.NbCommentaires</td>
|
||||
|
||||
<td class="text-center action-column">
|
||||
|
||||
<a asp-action="Edit" asp-route-id="@item.Id"
|
||||
class="btn btn-sm text-primary">
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
<a asp-action="Delete" asp-route-id="@item.Id"
|
||||
class="btn btn-sm text-primary">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,135 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Titre.AdminTitreForm
|
||||
|
||||
<div class="container">
|
||||
<!-- ARTISTE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Nom de l'artiste<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<select asp-for="IdArtiste"
|
||||
asp-items="Model.Artistes"
|
||||
class="form-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TITRE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Titre<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Libelle" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ALBUM -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Album<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Album" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CHRONIQUE -->
|
||||
<div class="row mb-3">
|
||||
<label class="col-md-3 col-form-label">Chronique<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<textarea asp-for="Chronique"
|
||||
class="form-control"
|
||||
rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DATE + DUREE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Date de sortie<span class="text-danger">*</span></label>
|
||||
<div class="col-md-3">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="DateSortie"
|
||||
pattern="\d{2}/\d{2}/\d{4}"
|
||||
value="@Model.DateSortie.ToString("d")"/>
|
||||
</div>
|
||||
|
||||
<label class="col-md-3 col-form-label">Durée en secondes<span class="text-danger">*</span></label>
|
||||
<div class="col-md-3">
|
||||
<div class="input-group">
|
||||
<input asp-for="Duree"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0" />
|
||||
<span class="input-group-text text-muted">seconds</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JAQUETTE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Jaquette<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="UrlJaquette"
|
||||
class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- URL ECOUTE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">URL d'écoute</label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="UrlEcoute"
|
||||
class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- STYLES -->
|
||||
<div class="row mb-4">
|
||||
<label class="col-md-3 col-form-label">Styles</label>
|
||||
<div class="col-md-9">
|
||||
<div class="row">
|
||||
@foreach (var style in Model.AllStyles)
|
||||
{
|
||||
<div class="col-md-4 form-check">
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
name="Styles"
|
||||
value="@style.Value"
|
||||
@(Model.Styles.Contains(int.Parse(style.Value)) ? "checked" : "") />
|
||||
|
||||
<label class="form-check-label">
|
||||
@style.Text
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LECTURES / LIKES (AFFICHAGE UNIQUEMENT) -->
|
||||
<div class="row mb-4 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Nb de lectures<span class="text-danger">*</span></label>
|
||||
<div class="col-md-3">
|
||||
@Model.NbLectures
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Nb de likes<span class="text-danger">*</span></label>
|
||||
<div class="col-md-3">
|
||||
@Model.NbLikes
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BOUTONS -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-9 offset-md-3">
|
||||
|
||||
<button type="submit" class="btn btn-primary me-2">
|
||||
Sauvegarder
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<a asp-action="Index"
|
||||
class="btn text-primary">
|
||||
Retour à l'administration des titres
|
||||
</a>
|
||||
</div>
|
||||
@@ -0,0 +1,2 @@
|
||||
@* Permet de factoriser les imports de tag helpers *@
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@@ -0,0 +1,4 @@
|
||||
@{
|
||||
// Modification du Layout pour r<>cup<75>rer celui de base
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
53
Webzine.WebApplication/Controllers/AccueilController.cs
Normal file
53
Webzine.WebApplication/Controllers/AccueilController.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Repository.Fake;
|
||||
using Webzine.WebApplication.ViewModels.Accueil;
|
||||
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
{
|
||||
public class AccueilController : Controller
|
||||
{
|
||||
// Injection du logger via le constructeur
|
||||
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.
|
||||
/// </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");
|
||||
}
|
||||
|
||||
/// <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()
|
||||
{
|
||||
_logger.LogInformation("Arrivée sur la page d'accueil");
|
||||
|
||||
var derniereChronique = _configuration.GetValue<int>("Webzine:NombreDerniereChronique");
|
||||
var topTitres = _configuration.GetValue<int>("Webzine:NombreDeTopTitres");
|
||||
var titres = FakeDataFactory.GetTitres();
|
||||
|
||||
var vm = new AccueilIndexViewModel
|
||||
{
|
||||
DerniersTitres = titres
|
||||
.OrderByDescending(t => t.DateCreation)
|
||||
.Take(derniereChronique)
|
||||
.ToList(),
|
||||
|
||||
TopTitres = titres
|
||||
.OrderByDescending(t => t.NbLikes)
|
||||
.Take(topTitres)
|
||||
.ToList()
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,30 @@ public class ApiController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<ApiController> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="ApiController"/> avec un service de journalisation injecté.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
|
||||
public ApiController(ILogger<ApiController> logger)
|
||||
{
|
||||
this._logger = logger;
|
||||
this._logger.LogDebug(1, "NLog injected into VersionController");
|
||||
this._logger.LogDebug(1, "initialisation du ApiController");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Endpoint de test pour vérifier que l'API fonctionne correctement. Retourne une chaîne de caractères "Hello World !".
|
||||
/// </summary>
|
||||
/// <returns>Une chaîne de caractères "Hello World !".</returns>
|
||||
[HttpGet]
|
||||
public string HelloWorld()
|
||||
{
|
||||
return "Hello World !";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Endpoint de test pour vérifier que l'API fonctionne correctement. Retourne un objet JSON contenant le nom et la version de l'application.
|
||||
/// </summary>
|
||||
/// <returns>Un objet JSON avec les propriétés "nom" et "version".</returns>
|
||||
[HttpGet]
|
||||
public IActionResult Version()
|
||||
{
|
||||
|
||||
60
Webzine.WebApplication/Controllers/ArtisteController.cs
Normal file
60
Webzine.WebApplication/Controllers/ArtisteController.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.WebApplication.ViewModels;
|
||||
using Webzine.WebApplication.ViewModels.Artiste;
|
||||
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
{
|
||||
public class ArtisteController : Controller
|
||||
{
|
||||
// Injection du logger via le constructeur
|
||||
private readonly ILogger<ArtisteController> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="ArtisteController"/> avec un service de journalisation injecté.
|
||||
/// </summary>
|
||||
/// <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.LogDebug(1, "initialisation du ArtisteController");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prend en paramètre le nom de l'artiste (ex: "fatal-bazooka"), utilise la factory pour trouver l'artiste correspondant, et affiche sa page dédiée.
|
||||
/// </summary>
|
||||
/// <param name="nom">Le nom de l'artiste à rechercher, formaté en kebab-case (ex: "fatal-bazooka").</param>
|
||||
/// <returns>La vue de l'artiste avec son ViewModel, ou une redirection vers l'accueil si le nom est vide, ou une erreur 404 si l'artiste n'est pas trouvé.</returns>
|
||||
[HttpGet("/artiste/{nom}")]
|
||||
public IActionResult Index(string nom)
|
||||
{
|
||||
_logger.LogInformation("Tentative d'accès à l'artiste avec le nom : {NomArtiste}", nom);
|
||||
|
||||
if (string.IsNullOrEmpty(nom)) return RedirectToAction("Index", "Accueil");
|
||||
|
||||
// On transforme "fatal-bazooka" en "Fatal Bazooka" pour la factory
|
||||
string nomPropre = System.Globalization.CultureInfo.CurrentCulture.TextInfo
|
||||
.ToTitleCase(nom.Replace("-", " "));
|
||||
|
||||
// On appelle la factory pour obtenir l'artiste unique
|
||||
var artiste = ArtisteFactory.SeedArtisteByName(nomPropre);
|
||||
|
||||
if (artiste == null)
|
||||
{
|
||||
_logger.LogWarning("Artiste non trouvé pour le nom : {NomArtiste}", nomPropre);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_logger.LogInformation("Artiste trouvé : {NomArtiste}", nom);
|
||||
|
||||
// On remplit le ViewModel
|
||||
var viewModel = new ArtisteModel
|
||||
{
|
||||
Artiste = artiste,
|
||||
Titres = artiste.Titres
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Webzine.WebApplication/Controllers/ContactController.cs
Normal file
32
Webzine.WebApplication/Controllers/ContactController.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Controller pour la page contact.
|
||||
/// </summary>
|
||||
public class ContactController : Controller
|
||||
{
|
||||
// Injection du logger via le constructeur
|
||||
private readonly ILogger<ContactController> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="ContactController"/> avec un service de journalisation injecté.
|
||||
/// </summary>
|
||||
/// <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.LogDebug(1, "initialisation du ContactController");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche la page de contact du webzine.
|
||||
/// </summary>
|
||||
/// <returns>La vue Index de la page de contact.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
63
Webzine.WebApplication/Controllers/RechercheController.cs
Normal file
63
Webzine.WebApplication/Controllers/RechercheController.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Recherche;
|
||||
using Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
namespace Webzine.WebApplication.Controllers;
|
||||
|
||||
[Route("recherche")]
|
||||
public class RechercheController : Controller
|
||||
{
|
||||
private readonly ILogger<RechercheController> _logger;
|
||||
private readonly ITitreRepository _titreRepository;
|
||||
|
||||
public RechercheController(ILogger<RechercheController> logger, ITitreRepository titreRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
_titreRepository = titreRepository;
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
public IActionResult Index(string mot)
|
||||
{
|
||||
_logger.LogInformation("Recherche artistes/titres pour le mot : {Mot}.", mot);
|
||||
|
||||
var titres = _titreRepository.Search(mot)
|
||||
.Concat(_titreRepository.SearchByStyle(mot))
|
||||
.DistinctBy(t => t.IdTitre)
|
||||
.OrderBy(t => t.Libelle)
|
||||
.Select(t => new TitreStyleItem
|
||||
{
|
||||
IdTitre = t.IdTitre,
|
||||
Libelle = t.Libelle,
|
||||
ArtisteNom = t.Artiste?.Nom,
|
||||
UrlJaquette = t.UrlJaquette,
|
||||
Duree = t.Duree
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var artistes = _titreRepository.FindAll()
|
||||
.Select(t => t.Artiste)
|
||||
.Where(a => a != null
|
||||
&& !string.IsNullOrWhiteSpace(a.Nom)
|
||||
&& !string.IsNullOrWhiteSpace(mot)
|
||||
&& a.Nom.Contains(mot, StringComparison.OrdinalIgnoreCase))
|
||||
.DistinctBy(a => a!.IdArtiste)
|
||||
.OrderBy(a => a!.Nom)
|
||||
.Select(a => new RechercheArtisteItem
|
||||
{
|
||||
Nom = a!.Nom,
|
||||
NombreDeTitres = a.Titres?.Count ?? 0
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var vm = new RechercheIndexViewModel
|
||||
{
|
||||
Mot = mot,
|
||||
Artistes = artistes,
|
||||
Titres = titres
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
171
Webzine.WebApplication/Controllers/TitreController.cs
Normal file
171
Webzine.WebApplication/Controllers/TitreController.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
namespace Webzine.WebApplication.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controleur responsable de la gestion des titres musicaux :
|
||||
/// affichage des details, filtrage par style,
|
||||
/// ajout de likes, commentaires et recherche.
|
||||
/// </summary>
|
||||
[Route("titre")]
|
||||
public class TitreController : Controller
|
||||
{
|
||||
private readonly ILogger<TitreController> _logger;
|
||||
private readonly ITitreRepository _titreRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="TitreController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecte.</param>
|
||||
/// <param name="titreRepository">Repository des titres injecte.</param>
|
||||
public TitreController(ILogger<TitreController> logger, ITitreRepository titreRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
_titreRepository = titreRepository;
|
||||
|
||||
_logger.LogInformation("Initialisation du controleur TitreController.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche le detail d'un titre specifique.
|
||||
/// </summary>
|
||||
/// <param name="id">Identifiant du titre.</param>
|
||||
/// <returns>Vue des details ou 404 si introuvable.</returns>
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult Details(int id)
|
||||
{
|
||||
_logger.LogInformation("Demande d'affichage du detail pour le titre ID {Id}.", id);
|
||||
|
||||
var titre = FindById(id);
|
||||
|
||||
if (titre == null)
|
||||
{
|
||||
_logger.LogWarning("Titre avec ID {Id} introuvable.", id);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var vm = new TitreDetail
|
||||
{
|
||||
Details = new TitreContent
|
||||
{
|
||||
IdTitre = titre.IdTitre,
|
||||
Libelle = titre.Libelle,
|
||||
Chronique = titre.Chronique,
|
||||
DateSortie = titre.DateSortie,
|
||||
NbLikes = titre.NbLikes,
|
||||
UrlJaquette = titre.UrlJaquette,
|
||||
UrlEcoute = titre.UrlEcoute,
|
||||
ArtisteNom = titre.Artiste?.Nom,
|
||||
Styles = titre.Styles,
|
||||
Commentaires = titre.Commentaires
|
||||
},
|
||||
CommentForm = new TitreComment
|
||||
{
|
||||
IdTitre = titre.IdTitre
|
||||
}
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche les titres correspondant a un style musical donne.
|
||||
/// </summary>
|
||||
/// <param name="style">Nom du style musical.</param>
|
||||
/// <returns>Vue contenant la liste filtree.</returns>
|
||||
[HttpGet("style/{style}")]
|
||||
public IActionResult Style(string style)
|
||||
{
|
||||
_logger.LogInformation("Recherche des titres pour le style : {Style}.", style);
|
||||
|
||||
var titresFiltres = _titreRepository.SearchByStyle(style).ToList();
|
||||
|
||||
var vm = new TitreStyle
|
||||
{
|
||||
StyleName = style,
|
||||
Titres = titresFiltres.Select(MapTitreItem).ToList()
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ajoute un like a un titre.
|
||||
/// </summary>
|
||||
/// <param name="model">Modele contenant l'identifiant du titre.</param>
|
||||
/// <returns>Redirection vers la page detail.</returns>
|
||||
[HttpPost("like")]
|
||||
public IActionResult Like(TitreLike model)
|
||||
{
|
||||
_logger.LogInformation("Ajout d'un like pour le titre ID {Id}.", model.IdTitre);
|
||||
|
||||
var titre = FindById(model.IdTitre);
|
||||
|
||||
if (titre == null)
|
||||
{
|
||||
_logger.LogWarning("Impossible d'ajouter un like. Titre ID {Id} introuvable.", model.IdTitre);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
titre.NbLikes++;
|
||||
|
||||
return RedirectToAction("Details", new { id = model.IdTitre });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ajoute un commentaire a un titre.
|
||||
/// </summary>
|
||||
/// <param name="model">Donnees du commentaire.</param>
|
||||
/// <returns>Redirection vers la page detail.</returns>
|
||||
[HttpPost("comment")]
|
||||
public IActionResult Comment(TitreComment model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
_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);
|
||||
|
||||
if (titre == null)
|
||||
{
|
||||
_logger.LogWarning("Impossible d'ajouter le commentaire. Titre ID {Id} introuvable.", model.IdTitre);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var commentaire = new Commentaire
|
||||
{
|
||||
Auteur = model.Auteur,
|
||||
Contenu = model.Contenu,
|
||||
DateCreation = DateTime.Now,
|
||||
IdTitre = model.IdTitre
|
||||
};
|
||||
|
||||
titre.Commentaires.Add(commentaire);
|
||||
|
||||
_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
|
||||
{
|
||||
IdTitre = titre.IdTitre,
|
||||
Libelle = titre.Libelle,
|
||||
ArtisteNom = titre.Artiste?.Nom,
|
||||
UrlJaquette = titre.UrlJaquette,
|
||||
Duree = titre.Duree
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using NLog;
|
||||
using NLog.Web;
|
||||
using Webzine.Repository;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
// Early init of NLog to allow startup and exception logging, before host is built
|
||||
// Initiation du logger NLog pour la classe courante afin de pouvoir l'utiliser pour logger des messages d'information, d'erreur, etc avant la construction de l'application.
|
||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||
logger.Debug("init main");
|
||||
|
||||
@@ -9,44 +11,50 @@ try
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Ajoute les services n<EFBFBD>cessaires pour permettre l'utilisation des
|
||||
// Ajoute les services necessaires pour permettre l'utilisation des
|
||||
// controllers avec des vues.
|
||||
builder.Services.AddControllersWithViews()
|
||||
// Ajoute la compilation des vues lors de l'ex<EFBFBD>cution de l'application.
|
||||
// Cela nous <EFBFBD>vite de recompiler l'application <EFBFBD> chaque modification de vue.
|
||||
// N<EFBFBD>cessite le package Nuget Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.
|
||||
// Ajoute la compilation des vues lors de l'execution de l'application.
|
||||
// Cela nous evite de recompiler l'application a chaque modification de vue.
|
||||
// Necessite le package Nuget Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.
|
||||
.AddRazorRuntimeCompilation();
|
||||
|
||||
builder.Services.AddSingleton<ITitreRepository, LocalEntityRepository>();
|
||||
|
||||
// NLog: Setup NLog for Dependency injection
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Host.UseNLog();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Active la possibilit<EFBFBD> de servir des fichiers statiques pr<EFBFBD>sents dans
|
||||
// Active la possibilite de servir des fichiers statiques presents dans
|
||||
// le dossier wwwroot.
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Active le middleware permettant le routage des requ<EFBFBD>tes entrantes.
|
||||
// Active le middleware permettant le routage des requetes entrantes.
|
||||
app.UseRouting();
|
||||
|
||||
// Ajoute une route pour les zones (Areas) comme Admin
|
||||
app.MapControllerRoute(
|
||||
name: "areas",
|
||||
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
|
||||
|
||||
// Ajoute un endpoint permettant de router les urls
|
||||
// avec la forme /controller/action/id(optionnel).
|
||||
// Equivalent <20> app.MapDefaultControllerRoute()
|
||||
app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
pattern: "{controller=Accueil}/{action=Index}/{id?}");
|
||||
|
||||
app.Run();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// NLog: catch setup errors
|
||||
// NLog: attrape les exceptions non gerees et les logge.
|
||||
logger.Error(exception, "Stopped program because of exception");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
|
||||
// Assure que NLog flush tous les messages de log avant de fermer l'application.
|
||||
NLog.LogManager.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Webzine.Entity;
|
||||
|
||||
namespace Webzine.WebApplication.ViewModels.Accueil
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la page d'accueil du webzine, affichant les derniers titres et les titres les plus populaires.
|
||||
/// </summary>
|
||||
public class AccueilIndexViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit la liste des derniers titres ajoutés au webzine.
|
||||
/// </summary>
|
||||
public List<Entity.Titre> DerniersTitres { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Définit la liste des titres les plus populaires du webzine.
|
||||
/// </summary>
|
||||
public List<Entity.Titre> TopTitres { get; set; } = [];
|
||||
}
|
||||
}
|
||||
20
Webzine.WebApplication/ViewModels/Artiste/ArtisteModel.cs
Normal file
20
Webzine.WebApplication/ViewModels/Artiste/ArtisteModel.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Webzine.WebApplication.ViewModels.Recherche;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour afficher un artiste dans les resultats de recherche.
|
||||
/// </summary>
|
||||
public class RechercheArtisteItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Nom de l'artiste.
|
||||
/// </summary>
|
||||
public string? Nom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre de titres associes a l'artiste.
|
||||
/// </summary>
|
||||
public int NombreDeTitres { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
namespace Webzine.WebApplication.ViewModels.Recherche;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour afficher les resultats de recherche d'artistes et de titres.
|
||||
/// </summary>
|
||||
public class RechercheIndexViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Mot saisi dans le formulaire.
|
||||
/// </summary>
|
||||
public string? Mot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Artistes trouves.
|
||||
/// </summary>
|
||||
public List<RechercheArtisteItem> Artistes { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Titres trouves.
|
||||
/// </summary>
|
||||
public List<TitreStyleItem> Titres { get; set; } = new();
|
||||
}
|
||||
31
Webzine.WebApplication/ViewModels/Titre/TitreComment.cs
Normal file
31
Webzine.WebApplication/ViewModels/Titre/TitreComment.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
/// <summary>
|
||||
/// Classe représentant un commentaire sur un titre, utilisée pour la validation des données lors de la soumission d'un commentaire.
|
||||
/// </summary>
|
||||
public class TitreComment
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit l'identifiant du titre auquel le commentaire est associé.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int IdTitre { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'auteur du commentaire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(2)]
|
||||
[MaxLength(30)]
|
||||
public string Auteur { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le contenu du commentaire.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(10)]
|
||||
[MaxLength(1000)]
|
||||
public string Contenu { get; set; }
|
||||
}
|
||||
59
Webzine.WebApplication/ViewModels/Titre/TitreContent.cs
Normal file
59
Webzine.WebApplication/ViewModels/Titre/TitreContent.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Webzine.Entity;
|
||||
|
||||
namespace Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
/// <summary>
|
||||
/// Contient les détails d'un titre, ainsi que les commentaires associés.
|
||||
/// </summary>
|
||||
public class TitreContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit l'identifiant du titre.
|
||||
/// </summary>
|
||||
public int IdTitre { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le titre du titre.
|
||||
/// </summary>
|
||||
public string Libelle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la chronique du titre.
|
||||
/// </summary>
|
||||
public string Chronique { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la date de sortie du titre.
|
||||
/// </summary>
|
||||
public DateTime DateSortie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre de likes du titre.
|
||||
/// </summary>
|
||||
public int NbLikes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit l'url de la jaquette de l'album associé au titre.
|
||||
/// </summary>
|
||||
public string UrlJaquette { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit l'url d'écoute du titre.
|
||||
/// </summary>
|
||||
public string UrlEcoute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste associé au titre.
|
||||
/// </summary>
|
||||
public string ArtisteNom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'album associé au titre.
|
||||
/// </summary>
|
||||
public List<Style> Styles { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Définit la liste des commentaires associés au titre.
|
||||
/// </summary>
|
||||
public List<Commentaire> Commentaires { get; set; } = new();
|
||||
}
|
||||
17
Webzine.WebApplication/ViewModels/Titre/TitreDetail.cs
Normal file
17
Webzine.WebApplication/ViewModels/Titre/TitreDetail.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour la page de détails d'un titre, contenant les informations du titre et le formulaire de commentaire.
|
||||
/// </summary>
|
||||
public class TitreDetail
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit les détails du titre à afficher, y compris les informations sur le titre et les commentaires associés.
|
||||
/// </summary>
|
||||
public TitreContent Details { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le formulaire de commentaire pour permettre aux utilisateurs de laisser un commentaire sur le titre affiché.
|
||||
/// </summary>
|
||||
public TitreComment CommentForm { get; set; }
|
||||
}
|
||||
12
Webzine.WebApplication/ViewModels/Titre/TitreLike.cs
Normal file
12
Webzine.WebApplication/ViewModels/Titre/TitreLike.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour représenter un "like" sur un titre, contenant l'identifiant du titre aimé.
|
||||
/// </summary>
|
||||
public class TitreLike
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit l'identifiant du titre qui a reçu le "like".
|
||||
/// </summary>
|
||||
public int IdTitre { get; set; }
|
||||
}
|
||||
17
Webzine.WebApplication/ViewModels/Titre/TitreStyle.cs
Normal file
17
Webzine.WebApplication/ViewModels/Titre/TitreStyle.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour représenter une collection de titres associés à un style musical spécifique, avec le nom du style et une liste d'items de titre.
|
||||
/// </summary>
|
||||
public class TitreStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit le nom du style musical associé à la collection de titres.
|
||||
/// </summary>
|
||||
public string? StyleName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la liste des items de titre associés au style musical.
|
||||
/// </summary>
|
||||
public List<TitreStyleItem> Titres { get; set; } = new();
|
||||
}
|
||||
29
Webzine.WebApplication/ViewModels/Titre/TitreStyleItem.cs
Normal file
29
Webzine.WebApplication/ViewModels/Titre/TitreStyleItem.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour représenter un titre dans une liste de titres, contenant les informations essentielles telles que l'identifiant, le libellé, le nom de l'artiste, l'URL de la jaquette et la durée du titre.
|
||||
/// </summary>
|
||||
public class TitreStyleItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit l'identifiant du titre.
|
||||
/// </summary>
|
||||
public int IdTitre { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le libellé du titre.
|
||||
/// </summary>
|
||||
public string? Libelle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste associé au titre.
|
||||
/// </summary>
|
||||
public string? ArtisteNom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit l'URL de la jaquette du titre.
|
||||
/// </summary>
|
||||
public string? UrlJaquette { get; set; }
|
||||
|
||||
public int Duree { get; set; }
|
||||
}
|
||||
121
Webzine.WebApplication/Views/Accueil/Index.cshtml
Normal file
121
Webzine.WebApplication/Views/Accueil/Index.cshtml
Normal file
@@ -0,0 +1,121 @@
|
||||
@model Webzine.WebApplication.ViewModels.Accueil.AccueilIndexViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Accueil";
|
||||
}
|
||||
|
||||
<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>
|
||||
<div class="col">
|
||||
@* Artiste - Titre @titre.Artiste - @titre.Libelle*@
|
||||
<div class="fw-light h4 text-primary">
|
||||
<a asp-action="Index"
|
||||
asp-controller="Artiste"
|
||||
asp-route-nom="@titre.Artiste.Nom">
|
||||
@titre.Artiste.Nom
|
||||
</a>
|
||||
-
|
||||
<a asp-action="Details"
|
||||
asp-controller="Titre"
|
||||
asp-route-id="@titre.IdTitre">
|
||||
@titre.Libelle
|
||||
</a>
|
||||
</div>
|
||||
@* Chronique *@
|
||||
<p class="mt-2 mb-3 text-muted ">
|
||||
@titre.Chronique
|
||||
</p>
|
||||
<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>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small">
|
||||
<i class="fa-solid fa-calendar me-1"> </i>
|
||||
@* Date de création *@
|
||||
@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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row justify-content-end">
|
||||
<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="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>
|
||||
73
Webzine.WebApplication/Views/Artiste/Index.cshtml
Normal file
73
Webzine.WebApplication/Views/Artiste/Index.cshtml
Normal file
@@ -0,0 +1,73 @@
|
||||
@model Webzine.WebApplication.ViewModels.Artiste.ArtisteModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Artiste";
|
||||
}
|
||||
|
||||
|
||||
<div class="container">
|
||||
<h1>@Model.Artiste.Nom</h1>
|
||||
|
||||
<hr class="mb-5" />
|
||||
|
||||
<p class="lead">@Model.Artiste.Biographie</p>
|
||||
|
||||
<h2 class="mt-5 mb-4">Albums</h2>
|
||||
<hr class="mb-5" />
|
||||
|
||||
@* On groupe les titres par nom d'album *@
|
||||
@{
|
||||
var albumsGroupes = Model.Titres
|
||||
.OrderBy(t => t.Libelle) // Trie les titres par ordre alphabétique au sein de chaque groupe futur
|
||||
.GroupBy(t => t.Album) // Groupe par nom d'album
|
||||
.OrderBy(g => g.Key); // Trie les albums par ordre alphabétique (la clé du groupe)
|
||||
}
|
||||
|
||||
@foreach (var groupe in albumsGroupes)
|
||||
{
|
||||
// On récupère le premier titre du groupe pour afficher l'image de l'album
|
||||
var premierTitre = groupe.First();
|
||||
|
||||
<div class="row mb-5 align-items-start">
|
||||
<div class="col-md-3 mb-3">
|
||||
<img src="@premierTitre.UrlJaquette"
|
||||
class="img-fluid shadow-sm rounded border"
|
||||
alt="Pochette de @groupe.Key" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-9">
|
||||
<h3 class="h4 pb-2">@groupe.Key</h3>
|
||||
|
||||
<table class="table table-hover table-sm mt-3">
|
||||
<thead class="text-muted">
|
||||
<tr>
|
||||
<th style="width: 80px">Durée</th>
|
||||
<th>Titre</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var titre in groupe)
|
||||
{
|
||||
// Conversion des secondes en format MM:SS
|
||||
var minutes = titre.Duree / 60;
|
||||
var secondes = titre.Duree % 60;
|
||||
var dureeFormatee = $"{minutes}:{secondes:D2}";
|
||||
|
||||
<tr>
|
||||
<td class="text-secondary font-monospace">@dureeFormatee</td>
|
||||
<td>
|
||||
<a asp-controller="Titre"
|
||||
asp-action="Details"
|
||||
asp-route-id="@titre.IdTitre"
|
||||
class="text-primary fw-bold">
|
||||
@titre.Libelle
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
66
Webzine.WebApplication/Views/Contact/Index.cshtml
Normal file
66
Webzine.WebApplication/Views/Contact/Index.cshtml
Normal file
@@ -0,0 +1,66 @@
|
||||
@{
|
||||
ViewData["Title"] = "Contact";
|
||||
}
|
||||
|
||||
<div class="container">
|
||||
<h1>Contact</h1>
|
||||
<div>
|
||||
C.U.C.D.B - DIIAGE <br />
|
||||
69 Avenue Aristide Briand<br />
|
||||
21000 Dijon
|
||||
</div>
|
||||
<div>
|
||||
<i class="fa-solid fa-phone"></i> Phone : 03 80 40 50 60<br />
|
||||
<i class="fa-solid fa-envelope"></i> secretariat@cucdb.fr
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="container mt-5">
|
||||
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<i class="fa-brands fa-twitter fa-3x text-primary mb-3"></i>
|
||||
<div class="fw-bold text-primary">Twitter</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
85
Webzine.WebApplication/Views/Recherche/Index.cshtml
Normal file
85
Webzine.WebApplication/Views/Recherche/Index.cshtml
Normal file
@@ -0,0 +1,85 @@
|
||||
@model Webzine.WebApplication.ViewModels.Recherche.RechercheIndexViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Recherche";
|
||||
Layout = "_Layout";
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h1 class="mb-4">Resultats pour "@Model.Mot"</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
@if (string.IsNullOrWhiteSpace(Model.Mot))
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
Saisissez un mot-cle pour lancer une recherche.
|
||||
</div>
|
||||
}
|
||||
else if (!Model.Artistes.Any() && !Model.Titres.Any())
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
Aucun artiste ni titre ne correspond a votre recherche.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (Model.Artistes.Any())
|
||||
{
|
||||
<h2 class="h4 mt-4">Artistes</h2>
|
||||
|
||||
@foreach (var artiste in Model.Artistes)
|
||||
{
|
||||
<div class="my-3">
|
||||
<a asp-controller="Artiste"
|
||||
asp-action="Index"
|
||||
asp-route-nom="@artiste.Nom">
|
||||
@artiste.Nom
|
||||
</a>
|
||||
<span class="text-muted">(@artiste.NombreDeTitres titre(s))</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@if (Model.Titres.Any())
|
||||
{
|
||||
<h2 class="h4 mt-4">Titres</h2>
|
||||
|
||||
@foreach (var titre in Model.Titres)
|
||||
{
|
||||
<div class="d-flex align-items-start my-3">
|
||||
<a asp-controller="Titre"
|
||||
asp-action="Details"
|
||||
asp-route-id="@titre.IdTitre"
|
||||
class="me-3 text-black">
|
||||
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70" height="70" class="object-fit-cover" />
|
||||
</a>
|
||||
|
||||
<div class="justify-content-center d-flex flex-column">
|
||||
<div>
|
||||
<a asp-controller="Artiste"
|
||||
asp-action="Index"
|
||||
asp-route-nom="@titre.ArtisteNom">
|
||||
@titre.ArtisteNom
|
||||
</a>
|
||||
-
|
||||
<a asp-controller="Titre"
|
||||
asp-action="Details"
|
||||
asp-route-id="@titre.IdTitre">
|
||||
@titre.Libelle
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Duree : @TimeSpan.FromSeconds(titre.Duree).ToString(@"mm\:ss")
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
13
Webzine.WebApplication/Views/Shared/_Footer.cshtml
Normal file
13
Webzine.WebApplication/Views/Shared/_Footer.cshtml
Normal file
@@ -0,0 +1,13 @@
|
||||
@*
|
||||
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
*@
|
||||
@{
|
||||
}
|
||||
<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">© ASP .NET Core - DIIAGE 2025 - 2026</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
81
Webzine.WebApplication/Views/Shared/_Header.cshtml
Normal file
81
Webzine.WebApplication/Views/Shared/_Header.cshtml
Normal file
@@ -0,0 +1,81 @@
|
||||
@*
|
||||
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
*@
|
||||
@{
|
||||
}
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Logo -->
|
||||
<a class="navbar-brand" href="#">Webzine</a>
|
||||
|
||||
<!-- bouton mobile -->
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarWebzine">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarWebzine">
|
||||
|
||||
<!-- Menu -->
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
@* Laisser le asp-area vide pour indiqué une valeur null *@
|
||||
<a class="nav-link" asp-area="" asp-controller="Accueil" asp-action="Index">
|
||||
<i class="fa-solid fa-house"></i> Accueil
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@* TODO : Modifier, il s'agit d'une liste *@
|
||||
<li class="nav-item">
|
||||
@* <a class="nav-link" href="#">
|
||||
<i class="fa-solid fa-screwdriver-wrench"></i> Administration
|
||||
</a> *@
|
||||
<div class="dropdown">
|
||||
<a class="nav-link dropdown-toggle"
|
||||
href="#"
|
||||
role="button"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<i class="fa-solid fa-screwdriver-wrench"></i> Administration
|
||||
</a>
|
||||
|
||||
<ul class="dropdown-menu">
|
||||
@* Modifier la route 'Dashboard' ? *@
|
||||
<li><a class="dropdown-item" asp-area="Administration" asp-controller="Dashboard" asp-action="Index"><i class="fa-solid fa-gauge-high"></i> Tableau de bord</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" asp-area="Administration" asp-controller="Artiste" asp-action="Index"><i class="fa-solid fa-users"></i> Artistes</a></li>
|
||||
<li><a class="dropdown-item" asp-area="Administration" asp-controller="Commentaire" asp-action="Index"><i class="fa-solid fa-comments"></i> Commentaires</a></li>
|
||||
<li><a class="dropdown-item" asp-area="Administration" asp-controller="Style" asp-action="Index"><i class="fa-solid fa-tags"></i> Styles</a></li>
|
||||
<li><a class="dropdown-item" asp-area="Administration" asp-controller="Titre" asp-action="Index"><i class="fa-solid fa-music"></i> Titres</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" asp-area="" asp-controller="Contact" asp-action="Index">
|
||||
<i class="fa-solid fa-envelope"></i> Contact
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<!-- Barre de recherche -->
|
||||
<form class="d-flex" asp-controller="Recherche" asp-action="Index" method="post">
|
||||
<div class="input-group">
|
||||
<div class="form-outline">
|
||||
<input class="form-control me-2"
|
||||
type="search"
|
||||
name="mot"
|
||||
placeholder="Trouver un artiste, un titre ou un style">
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<i class="fa-solid fa-magnifying-glass"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -1,15 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@ViewData["Title"] - Mon Application</title>
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<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">
|
||||
|
||||
@* Ajout de font-awesome *@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
@RenderBody()
|
||||
</main>
|
||||
<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>
|
||||
</div>
|
||||
<partial name="_Footer" />
|
||||
</div>
|
||||
</body>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
51
Webzine.WebApplication/Views/Shared/_Sidebar.cshtml
Normal file
51
Webzine.WebApplication/Views/Shared/_Sidebar.cshtml
Normal file
@@ -0,0 +1,51 @@
|
||||
@*
|
||||
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
*@
|
||||
@{
|
||||
}
|
||||
<aside class="col-3">
|
||||
<div>
|
||||
<h2>À propos</h2>
|
||||
<p>Retrouvez les dernières pépites sur notre webzine.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Styles</h2>
|
||||
<ul>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Acid house">Acid house</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Ambient">Ambient</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Deep house">Deep house</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Disco">Disco</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Downtempo">Downtempo</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Drum n bass">Drum n bass</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Dub Techno">Dub Techno</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Electro">Electro</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Electronic">Electronic</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Experimental">Experimental</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Funk">Funk</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Garage">Garage</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Hardcore">Hardcore</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Hardstyle">Hardstyle</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Hip hop">Hip hop</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="House">House</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Indie">Indie</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Industrial">Industrial</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Jazz">Jazz</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Latin">Latin</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Metal">Metal</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Minimal">Minimal</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Pop">Pop</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Progressive">Progressive</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Punk">Punk</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="R&B">R&B</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Rap">Rap</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Reggae">Reggae</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Rock">Rock</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Soul">Soul</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Techno">Techno</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Trance">Trance</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Trip hop">Trip hop</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="UK garage">UK garage</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="World">World</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
196
Webzine.WebApplication/Views/Titre/Details.cshtml
Normal file
196
Webzine.WebApplication/Views/Titre/Details.cshtml
Normal file
@@ -0,0 +1,196 @@
|
||||
@model Webzine.WebApplication.ViewModels.Titre.TitreDetail
|
||||
|
||||
@{
|
||||
ViewData["Title"] = Model.Details.Libelle;
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
|
||||
<div class="mb-3">
|
||||
<h2>
|
||||
<a asp-area=""
|
||||
asp-controller="Artiste"
|
||||
asp-action="Index"
|
||||
asp-route-nom="@Model.Details.ArtisteNom">@Model.Details.ArtisteNom</a>
|
||||
- @Model.Details.Libelle
|
||||
</h2>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center pb-2 mb-3">
|
||||
|
||||
<div class="text-muted small d-flex align-items-center gap-4">
|
||||
|
||||
<!-- Date -->
|
||||
<span class="text-dark">
|
||||
<i class="fa fa-calendar me-1"></i>
|
||||
Le @Model.Details.DateSortie.ToString("dd/MM/yyyy 'à' HH:mm")
|
||||
</span>
|
||||
|
||||
<!-- Likes -->
|
||||
<span>
|
||||
<i class="fa fa-heart text-dark me-1"></i>
|
||||
@Model.Details.NbLikes
|
||||
</span>
|
||||
|
||||
<!-- Styles -->
|
||||
<span class="text-dark">
|
||||
<i class="fa fa-tags me-1"></i>
|
||||
Styles :
|
||||
|
||||
@for (int i = 0; i < Model.Details.Styles.Count; i++)
|
||||
{
|
||||
var style = Model.Details.Styles[i];
|
||||
|
||||
<a class="text-primary text-decoration-none fw-semibold"
|
||||
asp-controller="Titre"
|
||||
asp-action="Style"
|
||||
asp-route-style="@style.Libelle">
|
||||
@style.Libelle
|
||||
</a>
|
||||
|
||||
@if (i < Model.Details.Styles.Count - 1)
|
||||
{
|
||||
<span>, </span>
|
||||
}
|
||||
}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ACTION BUTTONS -->
|
||||
<div class="d-flex gap-2">
|
||||
|
||||
<form asp-action="Like" method="post">
|
||||
<input type="hidden" name="IdTitre" value="@Model.Details.IdTitre" />
|
||||
<button type="submit" class="btn btn-outline-primary btn-sm">
|
||||
<i class="fa fa-thumbs-up me-1"></i> Like
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<a asp-area="Administration" asp-controller="Titre" asp-action="Edit" asp-route-id="@Model.Details.IdTitre" class="btn text-primary btn-sm">
|
||||
<i class="fa fa-pen-to-square me-1"></i> Editer
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CONTENU -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-8">
|
||||
<p class="text-justify">
|
||||
@Model.Details.Chronique
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 text-center">
|
||||
<img src="@Model.Details.UrlJaquette"
|
||||
class="img-fluid rounded shadow"
|
||||
alt="Jaquette" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LECTEUR -->
|
||||
@if (!string.IsNullOrEmpty(Model.Details.UrlEcoute))
|
||||
{
|
||||
<div class="mt-4">
|
||||
<iframe width="100%" height="315"
|
||||
src="@Model.Details.UrlEcoute"
|
||||
title="Lecteur"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- FORMULAIRE COMMENTAIRE -->
|
||||
<div class="mt-4 mb-4">
|
||||
<div>
|
||||
|
||||
<h4 class="mb-4">Donne ton avis sur le titre</h4>
|
||||
|
||||
<form asp-action="Comment" method="post">
|
||||
<input type="hidden" name="IdTitre" value="@Model.Details.IdTitre"/>
|
||||
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
Nom<span class="text-danger">*</span>
|
||||
</label>
|
||||
<div class="col">
|
||||
<input name="Auteur"
|
||||
class="form-control input-full"
|
||||
placeholder="Votre nom"
|
||||
required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3 align-items-start">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
Commentaire<span class="text-danger">*</span>
|
||||
</label>
|
||||
<div class="col">
|
||||
<textarea name="Contenu"
|
||||
rows="3"
|
||||
class="form-control input-full"
|
||||
placeholder="Votre commentaire..."
|
||||
required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6 offset-sm-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Envoyer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- COMMENTAIRES -->
|
||||
<div class="mt-4">
|
||||
|
||||
<h4 class="mb-4">Commentaires</h4>
|
||||
|
||||
@if (Model.Details.Commentaires.Any())
|
||||
{
|
||||
foreach (var comment in Model.Details.Commentaires.OrderByDescending(c => c.DateCreation))
|
||||
{
|
||||
<div class="row mb-4">
|
||||
|
||||
<div class="col-sm-1"></div>
|
||||
|
||||
<div class="col-sm-6 d-flex">
|
||||
<img src="/images/avatar.png"
|
||||
width="50"
|
||||
height="50"
|
||||
class="rounded-circle me-3 shadow-sm"
|
||||
alt="avatar" />
|
||||
|
||||
<div>
|
||||
<strong>@comment.Auteur</strong>,
|
||||
<span class="text-muted small">
|
||||
le @comment.DateCreation.ToString("dd/MM/yyyy HH:mm")
|
||||
</span>
|
||||
|
||||
<p class="mb-0">@comment.Contenu</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-sm-6 offset-sm-2">
|
||||
<p class="text-muted">Aucun commentaire pour le moment.</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
61
Webzine.WebApplication/Views/Titre/Style.cshtml
Normal file
61
Webzine.WebApplication/Views/Titre/Style.cshtml
Normal file
@@ -0,0 +1,61 @@
|
||||
@model Webzine.WebApplication.ViewModels.Titre.TitreStyle
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Titres - {Model.StyleName}";
|
||||
Layout = "_Layout";
|
||||
}
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
|
||||
<!-- COLUMN -->
|
||||
<div class="col-md-8">
|
||||
|
||||
<h1 class="mb-4">Titres ayant le style @Model.StyleName</h1>
|
||||
|
||||
<hr />
|
||||
|
||||
@if (!Model.Titres.Any())
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
Aucun titre trouvé.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var titre in Model.Titres)
|
||||
{
|
||||
<div class="d-flex align-items-start my-3">
|
||||
|
||||
<!-- Image -->
|
||||
<a asp-action="Details"
|
||||
asp-route-id="@titre.IdTitre"
|
||||
class="me-3 text-black">
|
||||
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70px" height="70px" class="object-fit-cover"/>
|
||||
</a>
|
||||
|
||||
<!-- Infos -->
|
||||
<div class="justify-content-center d-flex flex-column">
|
||||
<div>
|
||||
<a asp-action="Index"
|
||||
asp-controller="Artiste"
|
||||
asp-route-nom="@titre.ArtisteNom">
|
||||
@titre.ArtisteNom
|
||||
</a>
|
||||
-
|
||||
<a asp-action="Details"
|
||||
asp-route-id="@titre.IdTitre">
|
||||
@titre.Libelle
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Durée : @TimeSpan.FromSeconds(titre.Duree).ToString(@"mm\:ss")
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\data\" />
|
||||
<Folder Include="wwwroot\js\" />
|
||||
<Folder Include="wwwroot\lib\" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -26,11 +25,21 @@
|
||||
<PackageReference Include="Faker.Net" Version="2.0.163" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.3" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.*" />
|
||||
<PackageReference Include="NLog" Version="5.*" />
|
||||
<PackageReference Include="NLog" Version="6.1.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
|
||||
<ProjectReference Include="..\Webzine.Repository\Webzine.Repository.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="Areas\Administration\Views\Shared\_Header.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Areas\Administration\Views\Shared\_Layout.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
13
Webzine.WebApplication/Webzine.WebApplication.csproj.user
Normal file
13
Webzine.WebApplication/Webzine.WebApplication.csproj.user
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ActiveDebugProfile>https</ActiveDebugProfile>
|
||||
<Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID>
|
||||
<Controller_SelectedScaffolderCategoryPath>root/Common/MVC/Controller</Controller_SelectedScaffolderCategoryPath>
|
||||
<View_SelectedScaffolderID>RazorViewEmptyScaffolder</View_SelectedScaffolderID>
|
||||
<View_SelectedScaffolderCategoryPath>root/Common/MVC/View</View_SelectedScaffolderCategoryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -5,5 +5,9 @@
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"Webzine": {
|
||||
"NombreDerniereChronique": 3,
|
||||
"NombreDeTopTitres" : 3
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
6
Webzine.WebApplication/wwwroot/css/bootstrap.min.css
vendored
Normal file
6
Webzine.WebApplication/wwwroot/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
Webzine.WebApplication/wwwroot/css/bootstrap.min.css.map
Normal file
1
Webzine.WebApplication/wwwroot/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
6312
Webzine.WebApplication/wwwroot/js/bootstrap.bundle.js
vendored
Normal file
6312
Webzine.WebApplication/wwwroot/js/bootstrap.bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
Webzine.WebApplication/wwwroot/js/bootstrap.min.js
vendored
Normal file
7
Webzine.WebApplication/wwwroot/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
Webzine.WebApplication/wwwroot/js/bootstrap.min.js.map
Normal file
1
Webzine.WebApplication/wwwroot/js/bootstrap.min.js.map
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user