JavaRush /Blog Java /Random-FR /Création d'une application Web simple à l'aide de servlet...
Стас Пасинков
Niveau 26
Киев

Création d'une application Web simple à l'aide de servlets et de jsp (partie 2)

Publié dans le groupe Random-FR
Création d'une application web simple à l'aide de servlets et jsp (partie 1) Niveau de connaissances requis pour comprendre l'article : vous avez déjà plus ou moins compris Java Core et souhaitez vous intéresser aux technologies JavaEE et à la programmation web. Cela a plus de sens si vous étudiez actuellement la quête Java Collections, qui couvre des sujets proches de l'article.
Création d'une application Web simple à l'aide de servlets et de jsp (partie 2) - 1

Création d'entités

Dans le package d'entités , nous allons créer une classe User , et il y aura deux variables de chaîne privées name et password . Créons des constructeurs (par défaut et celui qui accepterait les deux valeurs), des getters/setters, remplaçons la méthode toString() juste au cas où, ainsi que les méthodes equals() et hashCode() . Autrement dit, nous ferons tout ce qu'un développeur Java décent fait lors de la création d'une classe.
public class User {
    private String name;
    private String password;

    public User() {
    }

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (name != null ? !name.equals(user.name) : user.name != null) return false;
        return password != null ? password.equals(user.password) : user.password == null;

    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (password != null ? password.hashCode() : 0);
        return result;
    }
}
Nous pouvons maintenant commencer à créer une liste d'utilisateurs. Nous y ajouterons des utilisateurs et d'où nous les prendrons pour les afficher. Cependant, il y a un problème. Nous ne créons pas nos objets servlets, Tomcat le fait pour nous . Les méthodes que nous y remplaçons sont également déjà définies pour nous et nous ne pouvons pas ajouter de paramètre. Comment alors pouvons-nous créer une liste partagée visible par nos deux servlets ? Si nous créons simplement notre propre objet de liste dans chaque servlet, il s'avérera que nous ajouterons des utilisateurs à une liste et afficherons la liste des utilisateurs utilisant le servlet ListServlet à une autre. Il s’avère que nous avons besoin d’un objet commun aux deux servlets. De manière générale, nous avons besoin d'un objet qui serait commun à toutes les classes de notre programme ; le seul objet pour tout le programme. J'espère que vous avez entendu quelque chose sur les modèles de conception. Et, peut-être, pour certains, c'est le premier véritable besoin d'utiliser le modèle Singleton dans leur programme. Vous pouvez vous pervertir et créer un Singleton sympa avec des doubles vérifications et une synchronisation (oui, nous avons une application multithread, puisque Tomcat exécute des servlets dans différents threads), mais j'utiliserai l'option avec initialisation précoce, car elle est tout à fait appropriée ici cela suffit et cela correspond à nos objectifs.

Création d'un modèle

Créons une classe (et implémentons le modèle Singleton dedans ) dans le package modèle et appelons-la quelque chose d'inhabituel. Disons Modèle . Créons un objet liste d'utilisateurs privé dans notre classe et implémentons deux méthodes : une pour pouvoir ajouter un utilisateur et la seconde pour renvoyer une liste de chaînes (noms d'utilisateur). Puisque notre objet utilisateur est constitué d’un nom et d’un mot de passe et que nous ne souhaitons pas « révéler » les mots de passe des utilisateurs, nous n’aurons qu’une liste de noms.
public class Model {
    private static Model instance = new Model();

    private List<User> model;

    public static Model getInstance() {
        return instance;
    }

    private Model() {
        model = new ArrayList<>();
    }

    public void add(User user) {
        model.add(user);
    }

    public List<String> list() {
        return model.stream()
                .map(User::getName)
                .collect(Collectors.toList());
    }
}

Un peu sur mvc

Depuis que vous avez entendu parler de singleton , vous avez probablement entendu parler d'un autre modèle de conception - MVC (model-view-controller, en russe model-view-controller, ou tout comme en anglais model-view-controller). Son essence est de séparer la logique métier de la présentation. Autrement dit, séparez le code qui détermine quoi faire du code qui détermine comment afficher. La vue (vue ou simplement vues) est responsable de la forme sous laquelle certaines données sont présentées. Dans notre cas, les vues sont nos pages JSP. C'est pourquoi je les ai mis dans un dossier appelé vues . Le modèle correspond aux données réelles avec lesquelles le programme fonctionne. Dans notre cas, il s'agit d'utilisateurs (liste d'utilisateurs). Eh bien, les contrôleurs sont le lien qui les relie. Ils prennent les données du modèle et les transmettent aux vues (ou reçoivent des données de Tomcat, les traitent et les transmettent au modèle). La logique métier (ce que le programme doit faire exactement) doit y être décrite, et non dans le modèle ou dans la vue. Ainsi, chacun fait ce qu'il veut :
  • le modèle stocke les données ;
  • les vues dessinent une belle représentation des données ;
  • les responsables du traitement gèrent le traitement des données.
Cela permet au programme d'être assez simple et maintenable, plutôt qu'un vidage monstrueux de tout le code dans une seule classe. MVC convient non seulement à la programmation Web, mais c'est dans ce domaine qu'on le retrouve particulièrement souvent (presque toujours). Dans notre cas, les servlets feront office de contrôleurs. Il s'agit d'une description très superficielle et brève du modèle, mais MVC n'est pas le sujet principal de cet article. Qui veut en savoir plus – Google à la rescousse ! Créer un formulaire pour ajouter un utilisateur Ajoutons au fichier add.jsp un formulaire composé de deux champs de saisie de texte (un normal, l'autre un mot de passe) et un bouton pour envoyer des données au serveur.
<form method="post">
    <label>Name:
        <input type="text" name="name"><br />
    </label>

    <label>Password:
        <input type="password" name="pass"><br />
    </label>
    <button type="submit">Submit</button>
</form>
Ici, le formulaire a un attribut de méthode avec la valeur post . Cela signifie que les données de ce formulaire seront envoyées au serveur sous la forme d'une requête POST. L' attribut action n'est pas spécifié, ce qui signifie que la demande sera envoyée à la même adresse où nous sommes allés sur cette page ( /add ). Ainsi, notre servlet, liée à cette adresse, à réception d'une requête GET, renvoie cette jsp avec le formulaire d'ajout d'utilisateurs, et si elle reçoit une requête POST, cela signifie que le formulaire y a envoyé ses données (que nous extrairons du demander l'objet dans la méthode doPost() Il est à noter que les champs de saisie ont un paramètre name (pour un champ avec un nom, il a la valeur name, et pour un champ avec un mot de passe, il a la valeur pass ). C'est un point assez important. Puisque pour obtenir ces données (nom et mot de passe qui seront saisis) à partir de la requête (déjà à l'intérieur du servlet), nous utiliserons exactement ces nom et pass . Mais plus là-dessus plus tard. Le bouton d'envoi des données lui-même se présente à nouveau sous la forme d' un bouton , et non d'un champ de sortie, comme c'est généralement le cas. Je ne sais pas à quel point cette option est universelle, mais elle fonctionne pour moi (navigateur Chrome).

Traiter une requête POST avec un servlet

Revenons à la servlet AddServlet . Je vous rappelle : pour que notre servlet puisse « capter » les requêtes GET, nous avons remplacé la méthode doGet() de la classe HttpServlet . Pour apprendre à notre servlet à intercepter également les requêtes POST, nous devons également remplacer la méthode doPost() . Il reçoit des objets de requête et de réponse similaires de Tomcat , avec lesquels nous travaillerons. Tout d’abord, extrayons de la requête le nom et transmettons les paramètres envoyés par le formulaire (si vous les avez nommés différemment dans le formulaire, alors ce sont les noms que vous écrivez). Après cela, nous créerons notre objet utilisateur en utilisant les données reçues. Ensuite, nous obtiendrons l'objet modèle et ajouterons l'utilisateur créé au modèle.
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String name = req.getParameter("name");
    String password = req.getParameter("pass");
    User user = new User(name, password);
    Model model = Model.getInstance();
    model.add(user);
}

Transmission de données à afficher

Passons à la servlet ListServlet . La méthode doGet() a déjà été implémentée ici , qui transfère simplement le contrôle à la vue list.jsp . Si vous ne l'avez pas encore, faites-le par analogie avec la même méthode du servlet AddServlet . Maintenant, ce serait bien d'obtenir une liste de noms d'utilisateur du modèle et de les transmettre à la vue, qui les recevra et les affichera joliment. Pour ce faire, nous utiliserons à nouveau l'objet de requête que nous avons reçu de Tomcat . Nous pouvons ajouter un attribut à cet objet, en lui donnant un nom, et, en fait, l'objet lui-même que nous aimerions transférer en vue . Du fait que lors du transfert du processus d'exécution d'un servlet vers une vue, nous y passons les mêmes objets de requête et de réponse que le servlet lui-même a reçu, puis en ajoutant notre liste de noms à l'objet de requête, nous pouvons alors à partir de cette requête objet dans la vue, créez notre liste de noms d'utilisateurs et obtenez. Nous en avons terminé avec la classe ListServlet , voici donc le code de la classe entière :
package app.servlets;

import app.model.Model;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

public class ListServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Model model = Model.getInstance();
        List<String> names = model.list();
        req.setAttribute("userNames", names);

        RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/list.jsp");
        requestDispatcher.forward(req, resp);
    }
}

Exécuter du code Java dans des fichiers jsp

Il est temps de commencer à travailler sur le fichier list.jsp . Il ne s'exécutera que lorsque le ListServlet aura réussi le processus d'exécution ici. De plus, dans cette servlet, nous avons déjà préparé une liste de noms d'utilisateurs à partir du modèle et l'avons transmise ici dans l'objet de requête. Puisque nous avons une liste de noms, nous pouvons la parcourir et imprimer chaque nom. Comme je l'ai déjà dit, les fichiers jsp peuvent exécuter du code java (ce qui les différencie des pages HTML statiques). Afin d'exécuter du code, il suffit de mettre la construction suivante à l'endroit dont nous avons besoin :
<!-- html code -->
<%
    // java code
%>
<!-- html code -->
À l'intérieur de cette construction, nous avons accès à plusieurs variables :
  • request est notre objet de requête, que nous avons transmis depuis le servlet, où il était simplement appelé req ;
  • responce - objet de réponse, appelé resp dans le servlet ;
  • out est un objet de type JspWriter (hérité du Writer habituel ), à l'aide duquel nous pouvons « écrire » quelque chose directement dans la page html elle-même. L' entrée out.println("Hello world!") est très similaire à l' entrée System.out.println("Hello world!") , mais ne confondez pas les deux !
    out.println() « écrit » sur la page HTML et System.out.println écrit sur la sortie système. Si vous appelez la méthode jsp System.out.println() à l'intérieur de la section avec du code Java , vous verrez les résultats dans la console Tomcat , et non sur la page.

Vous pouvez rechercher d'autres objets disponibles dans jsp ici . En utilisant l' objet request , nous pouvons obtenir la liste des noms qui ont été transmis par le servlet (nous avons attaché l'attribut correspondant à cet objet), et en utilisant l' objet out , nous pouvons afficher ces noms. Faisons ceci (pour l'instant juste sous la forme d'une liste html) :
<ul>
    <%
        List<String> names = (List<String>) request.getAttribute("userNames");

        if (names != null && !names.isEmpty()) {
            for (String s : names) {
                out.println("<li>" + s + "</li>");
            }
        }
    %>
</ul>
Si vous avez besoin d'afficher une liste uniquement s'il y a des utilisateurs, et sinon d'afficher un avertissement indiquant qu'il n'y a pas encore d'utilisateurs, nous pouvons réécrire un peu cette section :
<%
    List<String> names = (List<String>) request.getAttribute("userNames");

    if (names != null && !names.isEmpty()) {
        out.println("<ui>");
        for (String s : names) {
            out.println("<li>" + s + "</li>");
        }
        out.println("</ui>");
    } else out.println("<p>There are no users yet!</p>");
%>
Maintenant que nous pouvons transmettre les données des servlets aux vues, nous pouvons légèrement améliorer notre AddServlet afin qu'une notification s'affiche lorsqu'un utilisateur a été ajouté avec succès. Pour ce faire, dans la méthode doPost() , après avoir ajouté un nouvel utilisateur au modèle, nous pouvons ajouter le nom de cet utilisateur aux attributs de l' objet req et redonner le contrôle à la vue add.jsp . Et dedans, créez déjà une section avec du code Java dans laquelle une vérification est effectuée pour voir si un tel attribut est dans la demande, et si tel est le cas, affichez un message indiquant que l'utilisateur a été ajouté avec succès. Après ces modifications, le code complet du servlet AddServlet ressemblera à ceci :
package app.servlets;

import app.entities.User;
import app.model.Model;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class AddServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/add.jsp");
        requestDispatcher.forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        String password = req.getParameter("pass");
        User user = new User(name, password);
        Model model = Model.getInstance();
        model.add(user);

        req.setAttribute("userName", name);
        doGet(req, resp);
    }
}
Ici, à la fin de la méthode doPost() , nous définissons un attribut avec le nom de l'utilisateur ajouté au modèle, après quoi nous appelons la méthode doGet() , à laquelle nous transmettons la requête et la réponse actuelles. Et la méthode doGet() transfère déjà le contrôle à la vue, où elle envoie un objet de requête avec le nom de l'utilisateur ajouté attaché comme attribut. Il ne reste plus qu'à corriger add.jsp pour qu'il affiche une telle notification si un tel attribut est présent. Le add.jsp final est :
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Add new user</title>
    </head>

    <body>
        <div>
            <h1>Super app!</h1>
        </div>

        <div>
            <%
                if (request.getAttribute("userName") != null) {
                    out.println("<p>User '" + request.getAttribute("userName") + "' added!</p>");
                }
            %>
            <div>
                <div>
                    <h2>Add user</h2>
                </div>

                <form method="post">
                    <label>Name:
                        <input type="text" name="name"><br />
                    </label>
                    <label>Password:
                        <input type="password" name="pass"><br />
                    </label>
                    <button type="submit">Submit</button>
                </form>
            </div>
        </div>

        <div>
            <button onclick="location.href='/'">Back to main</button>
        </div>
    </body>
</html>
Le corps de la page est composé de :
  • div-a avec en-tête ;
  • Conteneur div pour le contenu, il vérifie si un attribut avec le nom d'utilisateur existe ;
  • div avec un formulaire pour ajouter des utilisateurs ;
  • et à la fin il y a un pied de page avec un bouton pour revenir à la page principale.
Il peut sembler qu'il y a trop de divs, mais nous les utiliserons plus tard lorsque nous ajouterons des styles. Le fichier list.jsp final est :
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Users</title>
    </head>

    <body>
        <div>
            <h1>Super app!</h1>
        </div>

        <div>
            <div>
                <div>
                    <h2>Users</h2>
                </div>
                <%
                    List<String> names = (List<String>) request.getAttribute("userNames");

                    if (names != null && !names.isEmpty()) {
                        out.println("<ui>");
                        for (String s : names) {
                            out.println("<li>" + s + "</li>");
                        }
                        out.println("</ui>");
                    } else out.println("<p>There are no users yet!</p>");
                %>
            </div>
        </div>

        <div>
            <button onclick="location.href='/'">Back to main</button>
        </div>
    </body>
</html>
Ainsi, nous disposons d'une application Web entièrement fonctionnelle qui peut stocker et ajouter des utilisateurs, ainsi qu'afficher une liste de leurs noms. Il ne reste plus qu'à l'embellir... :)

Ajout de styles. Nous utilisons le framework W3.CSS

Pour le moment, notre application fonctionne, mais absolument folle. Par conséquent, nous ajouterons un arrière-plan, la couleur du texte et des boutons, des listes de styles, effectuerons l'alignement, ajouterons des retraits, etc. Si vous écrivez des styles manuellement, cela peut prendre beaucoup de temps et de nerfs. Par conséquent, je suggère d'utiliser le framework CSS W3.CSS. Il dispose déjà de classes prêtes à l'emploi avec des styles, il ne reste plus qu'à placer les classes CSS que l'on souhaite appliquer aux bons endroits. Afin de les ajouter à nos pages, nous inclurons d’abord un fichier avec des styles. Ceci peut être fait de deux façons:
  1. parcourez nos pages et dans la section d'en-tête insérez un lien direct vers le fichier avec les styles

    <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">

    Cette option convient si vous disposez d’une connexion Internet constante. Ensuite, lorsque vous ouvrirez vos pages sur un serveur local, les styles seront extraits d’Internet.


  2. Si vous souhaitez avoir tous les styles localement et ne pas dépendre d'une connexion Internet, téléchargez le fichier avec les styles et placez-le quelque part dans le dossier Web (par exemple, web/styles/w3.css ), puis parcourez tous nos pages ( index.html, add.jsp, list.jsp ) et entrez un lien vers ce fichier avec des styles à l'intérieur de la section d'en-tête

    <link rel="stylesheet" href="styles/w3.css">

    Après cela, parcourez simplement les balises et ajoutez les styles que vous aimez. Je ne m'étendrai pas là-dessus en détail, mais donnerai immédiatement mes versions toutes faites de mes trois fichiers avec des classes de style arrangées.

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Super app!</title>
        <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
    </head>

    <body class="w3-light-grey">
        <div class="w3-container w3-blue-grey w3-opacity w3-right-align">
            <h1>Super app!</h1>
        </div>

        <div class="w3-container w3-center">
            <div class="w3-bar w3-padding-large w3-padding-24">
                <button class="w3-btn w3-hover-light-blue w3-round-large" onclick="location.href='/list'">List users</button>
                <button class="w3-btn w3-hover-green w3-round-large" onclick="location.href='/add'">Add user</button>
            </div>
        </div>
    </body>
</html>

ajouter.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Add new user</title>
        <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
    </head>

    <body class="w3-light-grey">
        <div class="w3-container w3-blue-grey w3-opacity w3-right-align">
            <h1>Super app!</h1>
        </div>

        <div class="w3-container w3-padding">
            <%
                if (request.getAttribute("userName") != null) {
                    out.println("<div class=\"w3-panel w3-green w3-display-container w3-card-4 w3-round\">\n" +
                            "   <span onclick=\"this.parentElement.style.display='none'\"\n" +
                            "   class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-green w3-border w3-border-green w3-hover-border-grey\">×</span>\n" +
                            "   <h5>User '" + request.getAttribute("userName") + "' added!</h5>\n" +
                            "</div>");
                }
            %>
            <div class="w3-card-4">
                <div class="w3-container w3-center w3-green">
                    <h2>Add user</h2>
                </div>
                <form method="post" class="w3-selection w3-light-grey w3-padding">
                    <label>Name:
                        <input type="text" name="name" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
                    </label>
                    <label>Password:
                        <input type="password" name="pass" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
                    </label>
                    <button type="submit" class="w3-btn w3-green w3-round-large w3-margin-bottom">Submit</button>
                </form>
            </div>
        </div>

        <div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
            <button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
        </div>
    </body>
</html>

liste.jsp

<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Users list</title>
        <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
    </head>

    <body class="w3-light-grey">
        <div class="w3-container w3-blue-grey w3-opacity w3-right-align">
            <h1>Super app!</h1>
        </div>

        <div class="w3-container w3-center w3-margin-bottom w3-padding">
            <div class="w3-card-4">
                <div class="w3-container w3-light-blue">
                    <h2>Users</h2>
                </div>
                <%
                    List<String> names = (List<String>) request.getAttribute("userNames");

                    if (names != null && !names.isEmpty()) {
                        out.println("<ul class=\"w3-ul\">");
                        for (String s : names) {
                            out.println("<li class=\"w3-hover-sand\">" + s + "</li>");
                        }
                        out.println("</ul>");

                    } else out.println("<div class=\"w3-panel w3-red w3-display-container w3-card-4 w3-round\">\n"
+
                            "   <span onclick=\"this.parentElement.style.display='none'\"\n" +
                            "   class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-red w3-border w3-border-red w3-hover-border-grey\">×</span>\n" +
                            "   <h5>There are no users yet!</h5>\n" +
                            "</div>");
                %>
            </div>
        </div>

        <div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
            <button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
        </div>
    </body>
</html>
C'est tout :) Si vous avez encore des questions ou des commentaires, ou au contraire, si quelque chose ne fonctionne pas, laissez un commentaire. UPD : si vous rencontrez des problèmes avec les erreurs 404 lorsque vous cliquez sur des boutons, même si tout a été fait correctement, vous devriez peut-être corriger la configuration de déploiement dans l'idée. Pour ce faire, vous devez vous rendre dans Modifier les configurations (en haut près du bouton démarrer), aller dans l'onglet Déploiement à droite de la fenêtre et vous assurer que dans le contexte Application c'est simplement indiqué / Eh bien, je' Je joindrai quelques captures d’écran de ce qui est ressorti de tout cela.
Création d'une application Web simple à l'aide de servlets et de jsp (partie 2) - 2
Création d'une application Web simple à l'aide de servlets et de jsp (partie 2) - 3
Création d'une application Web simple à l'aide de servlets et de jsp (partie 2) - 4
Et enfin , si vous souhaitez vous entraîner avec ce projet, vous pouvez essayer :
  • créez un servlet et un jsp pour supprimer un utilisateur et quelques autres pour modifier/éditer un utilisateur existant. Vous obtiendrez une véritable application web CrUD :) sur les servlets));
  • remplacez la liste (List) par le travail avec une base de données afin que les utilisateurs ajoutés ne disparaissent pas après le redémarrage du serveur :)
Bonne chance!
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION