IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

MVC Music Store


précédentsommairesuivant

XVI. Introduction

Dans le précédent chapitre, nous avons chargé des données depuis notre base et les avons affichées. Dans ce chapitre, nous allons aussi permettre l'édition de données.

XVII. Création du StoreManagerController

Nous allons commencer par créer un nouveau contrôleur appelé StoreManagerController. Pour ce contrôleur, nous allons tirer profit des options d'échafaudage disponibles dans ASP.NET MVC 3 Tools Update. Configurez les options pour la boite de dialogue Add Controller dialog comme ci-dessous.

Image non disponible

Lorsque vous cliquez sur le bouton Add, vous pouvez constater que le mécanisme d'échafaudage d'ASP.NET MVC 3 vous a mâché une bonne quantité de travail :

  • Il crée le nouveau StoreManagerController avec une variable locale Entity Framework
  • Il ajoute un dossier StoreManager au dossier Views du projet.
  • Il ajoute les vues Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml, et Index.cshtml, fortement typées à la classe Album
Image non disponible

Le nouveau contrôleur StoreManager inclut les opérations CRUD (create, read, update, delete) qui savent comment fonctionne la classe modèle Album et utilisent notre contexte Entity Framework pour les accès en base.

XVIII. Modification d'une Vue échafaudée

Il est important de se rappeler que, ce code qui a été généré pour nous, c'est la norme de codage d'ASP.NET MVC, tout comme nous l'avons écrit tout au long du tutoriel. C'est destiné à vous faire économiser le temps que vous passez sur l'écriture du code du contrôleur et la création des vues fortement typées manuellement, mais ce n'est pas le genre de code généré que vous avez déjà pu voir précédé d'avertissements dans les commentaires vous expliquant que vous ne devez rien changer au code. Ceci est votre code, et attendez-vous à le modifier.

Donc, nous allons commencer par une rapide modification de la vue Index du StoreManager (/Views/StoreManager/Index.cshtml). Cette vue va afficher une table qui liste les albums de notre store avec des liens d'édition / détails / suppression, et inclut les propriétés publiques de l'album. Nous allons retirer le champ AlbumArtUrl, vu qu'il n'est pas très utile dans cet écran. Dans la section <table> du code de la vue, enlevez les éléments <th> et <td> entourant les références faites à AlbumArtUrl, comme indiqué ci-dessous :

 
Sélectionnez
<table>
    <tr>
        <th>
            Genre
        </th>
        <th>
            Artist
        </th>
        <th>
            Title
        </th>
        <th>
            Price
        </th>
        <th>
            AlbumArtUrl
        </th>
        <th></th>
    </tr>
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Genre.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Artist.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Title)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Price)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.AlbumArtUrl)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
            @Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
        </td>
    </tr>
}
</table>

Le code de la vue modifié devrait apparaître comme suit :

 
Sélectionnez
@model IEnumerable<MvcMusicStore.Models.Album>
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
    @Html.ActionLink("Create
New", "Create")
</p>
<table>
    <tr>
        <th>
            Genre
        </th>
        <th>
            Artist
        </th>
        <th>
            Title
        </th>
        <th>
            Price
        </th>
        <th></th>
    </tr>
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Genre.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Artist.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Title)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Price)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
            @Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
        </td>
    </tr>
}
</table>

XIX. Un premier regard sur le Store Manager

Maintenant, exécutez l'application et allez sur l'url /StoreManager/. Cela affiche l'Index du Store Manager que nous venons juste de modifier, affichant une liste des albums du store avec des liens d'éditions, de détails, et suppression.

Image non disponible

Le fait de cliquer sur le lien d'édition affiche un formulaire avec des champs pour modifier l'album, incluant des dropdownlists pour le genre et l'artiste.

Image non disponible

Cliquez sur le lien « Back to List » en bas, puis cliquez sur le lien Details pour un album. Cela va afficher les informations pour un album.

Image non disponible

Une nouvelle fois, cliquez sur le lien « Back to List », puis cliquez sur le lien Delete. Une fenêtre de confirmation va s'afficher, affichant les détails de l'album et nous demandant si nous sommes sûrs de vouloir le supprimer.

Image non disponible

Le fait de cliquer sur le bouton Delete en bas va supprimer l'album et vous renvoyer à la page d'Index, qui montre bien que l'album a été supprimé.

Nous n'avons pas encore fini avec le Store Manager, mais nous avons déjà un code fonctionnel dans le contrôleur et la vue pour les opérations CRUD.

XX. Regard sur le code du StoreManagerController

Le StoreManagerController contient une bonne quantité de code. Passons-le en revue de haut en bas. Le contrôleur inclut quelques espaces de noms standards pour un contrôleur MVC, ainsi qu'une référence pour notre espace de noms Models. Le contrôleur a une instance privée du MusicStoreEntities, utilisée par chaque action du contrôleur pour les accès à la base.

 
Sélectionnez
using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Data.Entity;
 using System.Linq;
 using System.Web;
 using System.Web.Mvc;
 using MvcMusicStore.Models;
 
namespace MvcMusicStore.Controllers
{ 
    public class StoreManagerController : Controller
    {
        private MusicStoreEntities db = new MusicStoreEntities();

XX-A. Les actions Index et Details du Store Manager

La vue index récupère une liste d'albums, y compris les informations liées au genre et à l'artiste pour chaque album, comme nous l'avons vu précédemment lorsque nous avons travaillé sur la méthode Browse du Store. La vue Index suit les références aux objets liés afin d'afficher le nom du genre et le nom de l'artiste de chaque album, de sorte que le contrôleur puisse être efficace et interroge cette information dans la demande initiale.

 
Sélectionnez
//
// GET: /StoreManager/
public ViewResult Index()
{
  var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist);
  return View(albums.ToList());
}

L'action Details du StoreManagerController fonctionne exactement comme l'action Details du StoreController que nous avons écrite précédemment - elle recherche l'album par ID en utilisant la méthode Find(), puis le renvoie à la vue.

 
Sélectionnez
  //
  // GET: /StoreManager/Details/5
  public ViewResult Details(int id)
  {
    Album album = db.Albums.Find(id);
    return View(album);
  }

XX-B. Les actions Create

Les actions Create sont un peu différentes de celles que nous avons vues jusqu'ici, parce qu'elles gèrent les saisies dans le formulaire. Lorsque les utilisateurs visitent /StoreManager/Create/ ils verront un formulaire vide. Cette page HTML va contenir un élément <form> qui contient des dropdownlists et textboxs où l'on peut saisir les détails de l'album.

Une fois que les utilisateurs ont rempli les valeurs du formulaire, ils peuvent appuyer sur le bouton « Save » pour soumettre ces changements à notre application afin de les sauvegarder dans notre base de données. Lorsque les utilisateurs appuient sur le bouton « save » le <form> va effectuer un postback vers le /StoreManager/Create/ URL et soumettre les valeurs du <form> dans le cadre du protocole HTTP-POST.

ASP.NET MVC nous permet de facilement séparer la logique de ces deux scénarios d'invocations d'URL en nous permettant de mettre en œuvre deux méthodes « Create » séparées dans notre classe StoreManagerController - une qui gère le protocole initial HTTP-GET afin de naviguer vers l'URL /StoreManager/Create/, et une autre pour gérer le protocole HTTP-POST des changements soumis.

XX-C. Passage d'informations à la vue en utilisant le ViewBag

Nous avons utilisé le ViewBag plus tôt dans ce tutoriel, mais je n'en ai pas beaucoup parlé. Le ViewBag nous permet de passer des informations à la vue sans utiliser un objet modèle fortement typé. Dans ce cas, notre action HTTP-GET Edit a besoin de passer une liste de genres et d'artistes au formulaire pour remplir les dropdownlists, et la façon la plus simple de le faire est de les renvoyer comme des éléments ViewBag.

Le ViewBag est un objet dynamique, ce qui signifie que vous pouvez taper ViewBag.Foo ou ViewBag.VotreNomIci sans écrire du code pour définir ces propriétés. Dans notre cas, le code du contrôleur utilise ViewBag.GenreId et ViewBag.ArtistId de sorte que les valeurs des dropdownlists soumises avec le formulaire soient GenreId et ArtistId, qui sont les propriétés de l'album qui vont être mises.

Ces valeurs des dropdownlists sont renvoyées au formulaire en utilisant l'objet SelectList, qui est construit juste pour ce but. Ceci est fait en utilisant le code comme ceci :

 
Sélectionnez
ViewBag.GenreId = new SelectList(db.Genres, "GenreId",
"Name");

Comme vous pouvez le voir depuis le code de la méthode, trois paramètres sont utilisés pour créer cet objet :

  • La liste des éléments du dropdownlist est affichée. Notez que ce n'est pas juste un string ? nous passons une liste de genres.
  • Le paramètre suivant passé au SelectList est la valeur sélectionnée. C'est de cette façon que le SelectList sait comment présélectionner un élément dans la liste. Ce sera plus facile à comprendre lorsque nous verrons le formulaire d'édition, qui est assez similaire.
  • Le dernier paramètre est la propriété qui va être affichée. Dans notre cas, cela indique la propriété Genre.Name qui est celle que l'on va montrer à l'utilisateur.

Sachant cela, alors, l'action HTTP-GET Create est assez simple - deux SelectLists sont ajoutées au ViewBag, et aucun objet modèle n'est passé au formulaire (car il n'a pas encore été créé).

 
Sélectionnez
//
// GET: /StoreManager/Create
public ActionResult Create()
{
    ViewBag.GenreId = new SelectList(db.Genres, "GenreId",
"Name");
    ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId",
"Name");
    return View();
}

XX-D. HTML Helpers pour afficher les DropDownLists dans la vue Create

Puisque nous avons parlé de la façon dont les valeurs des dropdownlists sont passées à la vue, nous allons jetons un coup d'œil à la vue pour voir comment ces valeurs sont affichées. Dans la vue (/Views/StoreManager/Create.cshtml), vous verrez que l'appel suivant est fait pour afficher le dropdownlist Genre.

 
Sélectionnez
@Html.DropDownList("GenreId",
String.Empty)

Ceci est connu comme étant un HTML Helper - une méthode utilitaire qui effectue une tâche de vue commune. Les HTML Helpers sont très utiles pour conserver le code des vues concis et lisible. Le Html.DropDownList helper est fourni par ASP.NET MVC, mais comme nous le verrons plus tard, il est possible de créer nos propres helpers pour les vues, que nous réutiliserons dans notre application.

L'appel Html.DropDownList a juste besoin de dire deux choses - où récupérer la liste à afficher, et quelle valeur (le cas échéant) doit être présélectionnée. Le premier paramètre, GenreId, dit au DropDownList de rechercher une valeur appelée GenreId dans le modèle ou ViewBag. Le deuxième paramètre est utilisé pour indiquer la valeur à afficher initialement dans le dropdownlist. Puisque le formulaire est un formulaire Create, il n'y a pas de valeur présélectionnée et String.Empty est passé.

XX-E. Gestion des valeurs postées

Comme nous l'avons évoqué précédemment, il y a deux méthodes associées à chaque formulaire. La première gère la requête HTTP-GET et affiche le formulaire. La seconde gère la requête HTTP-POST, qui contient les valeurs soumises du formulaire. Notez que l'action du contrôleur a un attribut [HttpPost], qui dit à ASP.NET MVC qu'il ne devrait répondre qu'aux requêtes HTTP-POST.

 
Sélectionnez
//
// POST: /StoreManager/Create
[HttpPost]
 public ActionResult Create(Album album)
 {
    if (ModelState.IsValid)
    {
        db.Albums.Add(album);
        db.SaveChanges();
        return RedirectToAction("Index");  
    }
    ViewBag.GenreId = new SelectList(db.Genres, "GenreId",
"Name", album.GenreId);
    ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId",
"Name", album.ArtistId);
    return View(album);
 }

Cette action a quatre responsabilités :

  1. Lire les valeurs du formulaire
  2. Vérifie que les valeurs du formulaire passent les règles de validation
  3. Si la soumission du formulaire est valide, enregistre les données et affiche la liste mise à jour
  4. Si la soumission du formulaire n'est pas valide, réaffiche le formulaire avec les erreurs de validation

XX-F. Lecture des valeurs du formulaire avec liaison du modèle

L'action du contrôleur traite la soumission du formulaire qui inclut les valeurs GenreId et ArtistId (venant des dropdownlists) et les valeurs des textboxs pour le Title, le Price, et l'AlbumArtUrl. Bien qu'il soit possible d'accéder directement aux valeurs, une meilleure approche serait d'utiliser les capacités de la liaison de modèle intégrée dans ASP.NET MVC. Lorsqu'une action de contrôleur prend un type modèle en paramètre, ASP.NET MVC va tenter de peupler un objet de ce type utilisé dans le formulaire (ainsi que les valeurs de route et querystring). Elle le fait en recherchant les valeurs dont les noms correspondent aux propriétés du modèle, par exemple, lorsque l'on met la valeur GenreId d'un nouvel objet Album, l'action recherche une entrée ayant le nom GenreId. Lorsque vous créez des vues utilisant les méthodes standards dans ASP.NET MVC, les formulaires seront toujours affichés en utilisant les propriétés de noms en tant que noms de champ de saisie, c'est donc les noms de champs qui vont être comparés.

XX-G. Validation du modèle

Le modèle est validé avec un simple appel à ModelState.IsValid. Nous n'avons pas encore ajouté de règles de validation à notre classe Album - nous le ferons d'ici peu - pour l'instant ces vérifications n'ont pas beaucoup de choses à faire. Ce qui est important, c'est que le ModelStat.IsValid s'adapte aux règles de validation que nous avons mises dans notre modèle, ainsi les futurs changements de ces règles ne nécessiteront aucune mise à jour du code de l'action.

XX-H. Sauvegarde des valeurs soumises

Si la soumission du formulaire passe les validations, il est temps d'enregistrer les valeurs dans la base de données. Avec Entity Framework, qui nécessite simplement d'ajouter le modèle à la collection d'albums et appeler SaveChanges.

 
Sélectionnez
db.Albums.Add(album);
 db.SaveChanges();

Entity Framework génère les commandes SQL appropriées pour persister la valeur. Après avoir sauvegardé les données, nous sommes redirigés vers la liste d'albums et nous pouvons constater nos mises à jour. Cela est fait en renvoyant RedirectToAction avec le nom de l'action du contrôleur que nous voulons afficher. Dans notre cas, il s'agit de la méthode Index.

XX-I. Affichage de la soumission du formulaire invalide avec des erreurs de validation

Dans le cas de saisies invalides du formulaire, les valeurs des dropdownlists sont ajoutées au ViewBag (comme dans le cas de HTTP-GET) et les valeurs du modèle lié sont retransmises à la vue pour l'affichage. Les erreurs de validation sont automatiquement affichées en utilisant le HTML Helper @Html.ValidationMessageFor.

XX-J. Test du formulaire Create

Pour tester ceci, exécutez l'application et aller sur /StoreManager/Create/ - cela vous affichera un formulaire vide qui est retourné par la méthode HTTP-GET Create du StoreController.

Remplissez quelques valeurs et cliquez sur le bouton Create pour soumettre le formulaire.

Image non disponible
Image non disponible

XX-K. Gestion de l'édition

La paire d'actions d'édition (HTTP-GET et HTTP-POST) sont très similaires aux méthodes de création que nous avons vues. Comme le scénario d'édition consiste à travailler avec un album existant, la méthode HTTP-GET d'édition charge l'album en se basant sur le paramètre « id », passé via la route. Ce code pour retrouver un album par l'AlbumId est le même que celui que nous avons vu précédemment dans l'action Details du contrôleur. Comme avec la méthode HTTP-GET Create, les valeurs des dropdownlists sont renvoyées via le ViewBag. Cela nous permet de renvoyer un album en tant qu'objet modèle à la vue (qui est fortement typée à la classe Album) en passant des données supplémentaires (ex. : une liste de Genres) via le ViewBag.

 
Sélectionnez
//
// GET: /StoreManager/Edit/5
public ActionResult Edit(int id)
 {
    Album album = db.Albums.Find(id);
    ViewBag.GenreId = new SelectList(db.Genres, "GenreId",
"Name", album.GenreId);
    ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId",
"Name", album.ArtistId);
    return View(album);
 }

L'action HTTP-POST Edit est très similaire à l'action HTTP-POST Create. La seule différence est qu'au lieu d'ajouter un nouvel album à la collection db.Albums, nous recherchons l'instance actuelle de l'album en utilisant db.Entry(album) et en mettant son statut à Modified. Cela indique à Entity Framework que nous modifions un album existant au lieu d'en créer un.

 
Sélectionnez
//
// POST: /StoreManager/Edit/5
[HttpPost]
 public ActionResult Edit(Album album)
 {
    if (ModelState.IsValid)
    {
        db.Entry(album).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    ViewBag.GenreId = new SelectList(db.Genres, "GenreId",
"Name", album.GenreId);
    ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId",
"Name", album.ArtistId);
    return View(album);
 }

Nous pouvons tester cela en exécutant l'application et en naviguant vers /StoreManger/, puis en cliquant sur le lien Edit d'un album.

Image non disponible

Ceci affiche un formulaire d'édition montré par la méthode HTTP-GET Edit. Remplissez quelques valeurs et cliquez sur le bouton Save.

Image non disponible

Cette action poste le formulaire, enregistre les valeurs, et nous renvoie à la liste des albums, montrant que les valeurs ont été mises à jour.

Image non disponible

XX-L. Gestion de la suppression

La suppression suit le même schéma que l'édition et la création, utilisant une action qui affiche un formulaire de confirmation, et une autre action qui manipule la soumission du formulaire.

L'action HTTP-GET Delete est exactement la même que notre précédente action Details du Store Manager.

 
Sélectionnez
//
// GET: /StoreManager/Delete/5
 
public ActionResult Delete(int id)
 {
    Album album = db.Albums.Find(id);
    return View(album);
 }

On affiche un formulaire qui est fortement typé à un type d'album, utilisant le contenu de la vue Delete.

Image non disponible

Le template Delete montre tous les champs du modèle, mais nous pouvons simplifier cela un peu. Modifiez le code de la vue /Views/StoreManager/Delete.cshtml comme suit.

 
Sélectionnez
@model MvcMusicStore.Models.Album
@{
    ViewBag.Title = "Delete";
}
<h2>Delete Confirmation</h2>
<p>Are you sure you want to delete the album titled 
   <strong>@Model.Title</strong>?
</p>
@using (Html.BeginForm()) {
    <p>
        <input type="submit" value="Delete" />
    </p>
    <p>
        @Html.ActionLink("Back to
List", "Index")
    </p>
}

Cela affiche un simple message de confirmation de la suppression.

Image non disponible

Cliquer sur le bouton Delete permet la publication du formulaire su le serveur, ce qui exécute l'action DeleteConfirmed.

 
Sélectionnez
//
// POST: /StoreManager/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
 {            
    Album album = db.Albums.Find(id);
    db.Albums.Remove(album);
    db.SaveChanges();
    return RedirectToAction("Index");
}

Notre action HTTP-POST Delete effectue les actions suivantes :

  • Charge l'album par ID
  • Supprime l'album et sauvegarde les modifications
  • Redirige vers l'Index, montre que l'album a été retiré de la liste

Afin de tester ceci, exécutez l'application et rendez vous sur /StoreManager. Sélectionnez un album de la liste et cliquez sur le lien Delete.

Image non disponible

Cela affiche notre écran de confirmation de suppression.

Image non disponible

Cliquer sur le bouton Delete supprime l'album et nous renvoie sur la page Index du Store Manager, ce qui montre bien que l'album a été supprimé.

Image non disponible

XX-M. Utilisation d'un HTML Helper personnalisé pour tronquer du texte

Nous avons un potentiel problème avec notre page Index du Store Manager. Nos titres d'albums et noms d'artistes peuvent tous deux être suffisamment longs pour « casser » la mise en forme de notre tableau. Nous allons créer un HTML Helper personnalisé afin de nous permettre de les tronquer facilement ainsi que d'autres propriétés de nos vues.

Image non disponible

La syntaxe @helper de Razor fait qu'il est assez facile de créer vos propres helpers à utiliser dans vos vues. Ouvrez la vue /Views/StoreManager/Index.cshtml et ajoutez le code suivant directement après la ligne @model.

 
Sélectionnez
@helper Truncate(string
input, int length)
 {
    if (input.Length <= length) {
        @input
    } else {
        @input.Substring(0, length)<text>...</text>
    }
}

Ce helper prend un string et une longueur maximale à autoriser. Si le texte fourni est plus court que la longueur spécifiée, le helper le ressort tel quel. S'il est plus long, alors il tronque le texte et renvoie « … » pour le reste.

Maintenant nous pouvons utiliser notre helper Truncate pour s'assurer que le titre de l'album et le nom de l'artiste valent moins de 25 caractères. Le code complet de la vue utilisant notre nouvel helper Truncate apparaît ci-dessous.

 
Sélectionnez
@model IEnumerable<MvcMusicStore.Models.Album>
@helper Truncate(string input, int length)
 {
    if (input.Length <= length) {
        @input
    } else {
        @input.Substring(0, length)<text>...</text>
    }
}
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
    @Html.ActionLink("Create
New", "Create")
</p>
<table>
    <tr>
        <th>
            Genre
        </th>
        <th>
            Artist
        </th>
        <th>
            Title
        </th>
        <th>
            Price
        </th>
        <th></th>
    </tr>
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Genre.Name)
        </td>
        <td>
            @Truncate(item.Artist.Name, 25)
        </td>
        <td>
            @Truncate(item.Title, 25)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Price)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
            @Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
        </td>
    </tr>
}
</table>

Maintenant lorsque nous naviguons sur l'url /StoreManager/, les albums et titres sont conservés sous nos longueurs maximales.

Image non disponible

Note: Cela montre le cas simple de création et d'utilisation d'un helper dans une vue. Pour en savoir un peu plus sur la création des helpers que vous pourriez utiliser sur votre site, vous pouvez voir mon blog : http://bit.ly/mvc3-helper-options

Vous pouvez utiliser les discussions disponibles sur http://mvcmusicstore.codeplex.com pour toutes questions ou tous commentaires.


précédentsommairesuivant

Copyright © 2012 Jon Galloway. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.