Protégé : Git why not full local

Cette publication est protégée par un mot de passe. Pour la voir, veuillez saisir votre mot de passe ci-dessous :

Publié dans Blog, Développement | Saisissez votre mot de passe pour accéder aux commentaires.

Web Development TP 1

Using green resources of the computer

At first, to preserve resources, close any other applications except web development browser, eclipse and tomcat server.

You can decrease the CPU frequency by change the level of power save, reduce temporary the light of the screen at a comfortable level for your eyes.

NOTE: You should do this after finish the whole exercise on this page. You can save with CTRL+S files and deactivate Automatic building for Eclipse J2EE with the menu « Project » > « Build Automatically » and decide to trigger the building with the shortcut CTRL+B.

At the end reduce the number of tabs and windows from your browser.

1/ Description Application Todo

Our main application will be to manage a list of thing to do, as for example the shopping list :

The Model part of the MVC will be simplest to let us work on the MVC with Web Development tools. Only one business rule is provided « no double entry », for example if someone try to add again « chocolat » at the list, there are no error but only one « chocolat » will stay into the list (case-insensitive)

Please find below a « very very » simple Class Diagram at starting point, you will have to add content and correct it later :

To manage easily one unique instance of the application, a Singleton Design Pattern is provided as this draft :

public class Application {
   private static Application instance = null;
   
   public static Application getInstance() {
      if (instance == null) {
           instance = new Application ();
      }
      return instance;
     }

  private Application() {
    // Initialization of the List 
  }

}

So please think about to instanciate a new application without access to the private constructor ?

How to initialize the list of todo items call « lesTodos » with the draft code

Could you please use the Interface List from java.util.List for futur integration and evolution.

NOTE: une interface do not provide all the code only signature of methods as show into the code :

Do not hesitate to open Java code by CTRL+left click on a class of a function, to study the difference between ArrayList and List.

Keep in mind that a future evolution of the project could use a LinkedList or a Stream result as we will see later.

Please study mainly methods contains, get and indexOf into ArrayList class.

2/ Project creation with only business/model part

Create a new project « Dynamic Web Project » ( File > New > Dynamic Web project), then deploy your project into Tomcat 9 installed previously with righ clic > « Add and Remove … » to add this project to Tomcat.

Create a new package into the « Java Ressources » part of the project :

We need a top package call main (name of package must be in lowercase at the opposite for class with the first capital letter) with insides packages model, control and test.

Code classes Application and Todo with additional methods depending your need withe righ clic on the package model and menu New > Class, you can tick to implement « public static void main (String[] args) » to test the class as a simple Java program.

You can generate Getter and Setter with right click mouse on the code and Menu Source > Generate Getters and Setters…

Use the generation of method hashCode() and equals() using only the field « texte » because comparing 2 instances of todo only this field is mandatory (business rule).

You can also generate the method toString() on the class Application but I recommend to you to learn to debug this class.

Create additional constructors with all fields, only texte field and with no fields with the Menu Source > Generate Constructor using Fields.

Please consider test comparing class with the main method :

public static void main (String[] args){
  Todo todo1 = new Todo( "lait", false );
  Todo todo2 = new Todo( "beurre", true);

   if ( todo1.equals( todo2 ) ) {
    System.out.println(" lait equals beurre ");
   }

   todo2.setText( "lait" );
   if ( todo1.equals( todo2 ) ) { 
     System.out.println(" lait equals lait ");
   }
}

To test a class, just right-click on the code and choose Menu > Debug As > Java Application, output is displayed in « Console » tab, or you can add breakpoint to understand features of equals() method.

NOTE: hashCode() return a value to organize instance of the same class by order. In our application the information will not be used but in case of extreme large number of instance, it could be useful to group instance for speed up processing.

Again, use the « public static void main (String[] args) » method to debug call of Application singleton by using this code:

public static void main(String[] args){
  Application.getInstance().addTodo("lait");
  Application.getInstance().addTodo("beurre");
  Application.getInstance().addTodo("lait");

}

Then you can also try to add additional method to Application for example several methods call findTodo() should be very useful.

3/ Unit Test with Junit4

In fact, the calling on « public static void main (String[] args) » method to test our program is not the best way to test the model part. We will use a Test Framework call JUnit to test Application class.

By the past, I let students start by testing Todo class first, but they find testing getters and setters less interesting, so let’s start with ApplicationTest. You will find an example of basic TodoTest application below, you could study particlary the testEqualsObject() method design to check the model business rule :

class TodoTest {
	@Test
	void testTodo() {
	    Todo todo = new Todo("chocolat" , false);
	    assertNotNull( todo );
	}
	@Test
	void testTodoNoParameters() {
	    Todo todo = new Todo();
 	    assertNotNull( todo.getTexte() );
	}
	@Test
	void testGetTexte() {
	     Todo todo = new Todo("chocolat" , false);
	     assertEquals ( todo.getTexte() , "chocolat");
	}
	@Test
	void testSetTexte() {
	    Todo todo = new Todo("chocolat", false);
	     todo.setTexte("ananas");
	     assertEquals ( todo.getTexte() , "ananas");
	}
	@Test
	void testSetActif() {
	     Todo todo = new Todo("chocolat" , false);
	     todo.setActif(true);
	     assertTrue( todo.isActif());
	}
	@Test
	void testIsActif() {
	     Todo todo = new Todo("chocolat" , false);
	     assertFalse( todo.isActif());
	}
	@Test
	void testEqualsObject() {
	     Todo todo1 = new Todo("chocolat" , false);
	     Todo todo2 = new Todo("chocolat" , true);
	     assertTrue( todo1.equals(todo2));
	}
}

On the class Application, right click to open Menu « New > Other > Junit Test Case » then fill the windows by providing the package model to store this new ApplicationTest class.

Please check methods @BeforeAll @BeforeEach, @AfterAll, @AfterEach and on the next windows, check method you want to test, obviously all method your create into Application.

One dialog will ask you to « Add Junit 5 library to the build path », please confirm by pressing OK to download and install Junit library to create test.

NOTE: if you forget one step, just delete the ApplicationTest class and start over.

NOTE: if for any reason, the ApplicationTest class is stored into the wrong package, just move into the package test.

To execute a Unit Test Case, just right-click on the code and chose Menu Debug As > Junit Test and you can check un green bar to display if all tests pass:

If a test fail, you will find a red bar, the console message, the number of failure test and of course which test fail :

You can debug with a breakpoint any test to understand why this test failed as any other Java code. Keep in mind, the writer of the code is not usually the person who writes the test code.

NOTE: a different way of wriing code is to write test code then write the missing code so in our case writing ApplicationTest then writing Application class.

Now we want to test at least :

  • Singleton is never null
  • 2 references of Application are equals
  • list of todos is empty at start
  • add a todo to the list and that the list increase by one
  • add a todo and find it
  • add a todo and it is the last todo of the list
  • delete a todo and do not find it into the list

You should discover that the order of executions of each of the test methods are not in the same orders and are not independent. It could be useful to empty the list before each test with the method setUp().

So please code and test everything to check tat the model part is working perfectly. Each time, you will add a new feature, consider adding a new test and check there is no regression.

NOTE: setUpBeforeClass() is trigger before starting all test methods, tearDownAfterClass() is trigger after passing all test methods, setUp() is trigger before each test and tearDown() after each test method. Those methods are useful to configure a test by creating a context with data or in the opposite, no data.

4/ Link between Model Part et one view/JSP

At first, create a JSP page to display 4 todo items :

This is an example of code to create Application with data and avoid an empty list. You can note the building of the list is made into the constructor method :

private Application() {
     lesTodos = new ArrayList<Todo>();
     create4TestTodos();
 }
 private void create4TestTodos(){
     addTodo( "Test1" , false );
     addTodo( "Test2" , false );
     addTodo( "Test1actif" , true );
     addTodo( "Test2actif" , true );
 }

Create a JSP page todos.jsp and import the package model into, you will find an example of code to start :

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="main.model.*" %>
Foreach list of todo itemps :
<%
for(Todo todo : Application.getInstance().getLesTodos() ){
%>
<p>
   <%=todo.getTexte() %>
</p>
<%
}
%>

This is very basic JSP to rescue from Application each todo item and display then into <p> tag for learning purpose, of course checkboxes are missing.

Start the JSP as we did into TP 0 to check everything is OK, but it is not a classical way to call directly a JSP with MVC design pattern

NOTE, you can create an index.jsp with auto redirection to call the futur Control Servlet:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
    response.sendRedirect("./Control?action=list");
%>

NOTE: to remove 404 error and debug console information on the downloading favinco.ico, you can add this tag to <head> HTML part :

<link rel="icon" href="data:,">

5/ Servlet as the Control part of design pattern MVC

Calling JSP pages as a spaghetti chain of application make web application complex to improve and debug. We will use the idea of state transition diagram instead direct calling from one JSP to an other.

Suppose un application to display email, the call from the list of emails to a detail of one email should be with MVC design pattern with an intermediary call to Servlet Control :

The URL to the Servlet pass one parameter call action to ask the controller if it is possible to transit to detail-email.jsp page with an additional parameter to identify the email :

Why using a controller, because we can load information, check information, logs the transit and even redirect to a different page according to routing requirement of our web application.

Create Servlet by right-clicking on the package control then Menu New > Servlet, please provide the name of the Servlet Class, check the java package if necessary and check also the URL mappings /Control and click button Finish

IMPORTANT NOTE: the mapping is the URL routing information if Tomcat find into URL /Control it will call the doGet() or doPost() method of this Servlet Class.

It is possible d’add several mapping et event use joker mapping as for example *.pdf to call a Servlet on every .pdf URL.

It is also classical to create a new servlet class as Control2 and change only the mapping to make an evolution on the web application.

We will modify the doGet() method with this piece of code :

protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {

String vueFinale = "todos.jsp";

// TODO: a future doAction() method will be call from here to choose which JSP page to display  
     
request.getRequestDispatcher(vueFinale).forward(request, response);

}

The purpose of vueFinale variable is to store the final JSP name as view to display using the getRequestDispatcher. We create route from information receive from doGet() parameters and the redirection will ne manage into this place.

We can image maybe display an error.jsp page if the program needs to route the user to an error page.

Into the Control class we will code a method call doAction() with request parameters and mainly the parameter action. Using the value of this parameter action, the code will check informations receive from GET/POST forms, manage operations on the model classes and return the value for the next JSP page to display.

private String doAction(HttpServletRequest request) {

  String vueFinale = "erreur.jsp";

   String action = request.getParameter("action");

   // Action1 addTodo
   if ("addTodo".equals(action)) {

       String texte = request.getParameter("todo_texte");
       Application.getInstance().addTodo(texte, false);

       vueFinale = "todos.jsp";
    }

     // Action2 Update list ...


    // ... 
    return vueFinale;
}

So calling this URL ./Control?action=addTodo&todo_texte_chocolat should trigger the piece of code to add a new instance of todo into the model. Then the control servlet class redirect the user to display the view todos.jsp listing all the todo items (inclus the new one).

This code is really partial and you should understand it by implement and debug the core of MVC Web application.

NOTE: if you need to rescue a list of parameters name from this kind of URL http://127.0.0.1:8080/Control?nom=jean&prenom=pierre&civilite=Mr as a list [ nom , prenom , civilite ], try :

List<String> keys = Collections.list(request.getParameterNames());

The next step is to develop a JSP and add features to the servlet to present a todo item list, un HTML horizontal bar (with tag <hr />) and un form to add new todo as :

NOTE: You should know a checkbox input feald is only trigger if the checkbox is check

You can read the following diagram showing sending data from HTML form, viewing information into the URL (the GET method is wonderful for this feature) , then data are send to the Servlet with the submit button and then you should debug the Servlet :

You can open into a separate tab this image to understand links between the HTML form and then Servlet variables.

Add a form to update the checkbox status if some todos are checked or not :

To create n HTML list please use this HTML :

<ul>
<li>chocolat</li>
<li>ananas</li>
</ul>

And to give you some code about tags with HTML form :

<form method="get" action="./Control">
<ul>
<li><input type="checkbox" name="checkbox_chocolat" /> chocolat</li>
<li><input type="checkbox" name="checkbox_ananas" /> ananas</li>
</ul>
<!-- missing some part about submit -->
</form>

You should know to tick the checkbox by default you should add an checked attribut as :

<input type="checkbox" name="checkbox_chocolat" checked />

or not checked by default with nothing :

<input type="checkbox" name="checkbox_chocolat"  />

Of course you should code something like this code :

<%
for(Todo todo : Application.getInstance().getLesTodos() ){
%>
<p>
   <%if ( todo.isActif() { %>
      <input type="checkbox" name="checkbox_" checked />
    <% } else { %>
       <input type="checkbox" name="checkbox_" />
    <% } %>
</p>
<%
}
%>

But maybe there is a way to merge the two part of if/else block to simulate a HTML tag construction with a new checked variable ?

Cliquez ici pour afficher une proposition de solution pour gérer checked:

6/ New controller action: update todo

Usually, it is better to try to code your own personal way to update todo status. Please, consider only as basic structure and make your own version.

We can image 3 different strategies with the HTML part :

  • First is to consider a form for a whole list of todo items and a separate form to add new todo :
<form method="get" action="./Control"> 
<ul> 
   <li>
      <input type="checkbox" name="checkbox_chocolat" /> 
      chocolat 
   </li> 
   <li>
     <input type="checkbox" name="checkbox_ananas" /> 
      ananas
    </li> 
 </ul> 
 <input type="submit" name="action" value="Update list" /> 
</form>

<hr />
<form method="get" action="./Control"> 
   New Todo: <input type="text" name="todo_texte" />
   <input type="submit" name="action" value="Add todo" />
 </form>
  • The second solution is also possible to use a form for each item list
<form method="get" action="./Control">
<ul>
  <li>
   input type="checkbox" name="checkbox_chocolat" /> 
   chocolat 
   <input type="submit" name="action" value="Update liste" /> 
  </li>
</ul>
</form>

<form method="get" action="./Control">
<ul>
  <li>
   input type="checkbox" name="checkbox_ananas" /> 
   chocolat 
   <input type="submit" name="action" value="Update liste" /> 
  </li>
</ul>
</form>

<hr />
<form method="get" action="./Control"> 
   New Todo: <input type="text" name="todo_texte" />
   <input type="submit" name="action" value="Add todo" />
 </form>
  • A third choice is to create a hyperlink to simulate the form to update status
<ul> 
   <li>
      <input type="checkbox" name="checkbox_chocolat" /> 
      chocolat
      <a href="./Control?action=Update%20liste&checkbox_chocolat=on">update</a>
   </li> 
   <li>
     <input type="checkbox" name="checkbox_ananas" /> 
      ananas
      <a href="./Control?action=Update%20liste&checkbox_ananas=on">update</a>
    </li> 
 </ul> 
 <input type="submit" name="action" value="Update list" /> 

Any kind of solution is valid, some are easier to maintain in time.

Server side with the servlet, the action part code Update list, I recommend you to think about uncheck all the todo items then update the list of items send by the form as this way :

if ( "Update list".equals( action ) ){
  // TODO: you should create this function is needed and the Junit test
   Application.getInstance().uncheckAll(); 
   List<String> keys = Collections.list(request.getParameterNames());   
   for(String key: keys) { 
      if ( key.startsWith("checkbox_")) { 
          String todoTexte = key.replace("checkbox_", "");      
          Application.getInstance().updateTodo(todoTexte, true); 
       } 
    } 
}

By the way why this line of code should be better than the second one :

if ( "Update list".equals( action ) ){

// Or 

if ( action.equals( "Update list" ) ){

You should also improve your code with this version :

if ( "Update list".equals( action ) ){ 
   // TODO: create a method to invalid the todo items and then update according parameters
   Application.getInstance().updateListTodo( Collections.list(request.getParameterNames() ); 
}

But it is more important for me to first extract list of parameter into the Servlet and give to Application only verified data. It is just a way of thinking, not a mandatory way of coding.

Another way to improve is to change the checkbox name as :

<form method="get" action="./Control">
<ul>
  <li>
     <input type="checkbox" name="todo" value="chocolat" />
     chocolat
   </li>
   <li>
      <input type="checkbox" name="todo" value="ananas" />
       ananas
    </li>

  <input type="submit" name="action" value="Update list" />
</form>

The URL created by the form submit action will be

http://127.0.0.1:8080/DevWeb/Control&todo=chocolat&todo=ananas&action=Update list

You should rescue the whole list of checkbox parameters with GetParametValues() as :

String[] valuesCheckboxTodo = request.getParameterValues("todo");

7/ Delete todo item

Please provide the last action to delete and item and delivery the code by email. It is not and evaluation, just a milestone of the project before go on.

8/ Add action JSON

Could you add a new action on the controller as the action=json to create a different JSP page not into HTML but in a format exchange to another program.

By the past, we use XML format as this way :

<?xml version="1.0" encoding="utf-8"?> 
  <todos> 
    <todo id="1" texte="chocolat" actif="false" /> 
    <todo id="2" texte="ananas" actif="true" /> 
    <todo id="3" texte="kiwi" actif="false" /> 
  </todos>

But this format is less and less used despite the JSON format :

{ 
 "todos": 
  [ 
   { 
      "id": "1", 
      "texte": "chocolat", 
      "actif": "false" 
    },
   { 
      "id": "2", 
      "texte": "ananas",
      "actif": "true" 
    }, 
    { 
       "id": "3", 
       "texte": "kiwi",
       "actif": "false" 
     }
   ]
 }

This format is also used with JavaScript code to communicate with the web server with AJAX request.

Please code the JSP page and change the content type from text/html to :

<%@ page language="java" contentType="application/json; charset=UTF-8" pageEncoding="UTF-8"%>

You can use an online tool to check JSON format as Online JSON Viewer. Some browser could reject JSON file using single quote instead of double quote or extra coma.

9/ Optional: write a client to use your JSON as an API Restful service

You can develop a Java client program (called a desktop client program or a rich client) to rescue the data via JSON. This program is a simple Java project to develop independently as the Dev Web Tomcat project.

This program is as a rich client provide by OneDrive/DropBox/GoogleDrive/etc. whih call the API to receive only data. We can also use wget/curl command to simulate access to an API with and HTTP GET request.

We can also imagine an IOS/Android application which make HTTP GET request and receive JSON list of items from your DevWeb application.

In Java, we can use one of many libraries to request a JSON and create instances (Google Gson, Jackson, Json-Lib, etc.). I find easier to use json-simple from https://cliftonlabs.github.io/json-simple/, download the JAR from the website and add it to the project (json-simple-4.0.1.jar is copy into a lib folder) :

First step if to create a valid URL for our JSON page

HttpURLConnection urlConnection = null;
try {
     URL url = new URL("http://localhost:8080/DevWeb/json.jsp");
     urlConnection = (HttpURLConnection) url.openConnection();
} catch (MalformedURLException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
}

Then a script to read line and regroup them into a stringBuilder variable :

StringBuilder stringBuilder = new StringBuilder();
if (urlConnection != null) {
  try (
     BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) 
     {
          String line;
          while ((line = reader.readLine()) != null) {
               stringBuilder.append(line);
          }
      }catch (IOException e) {
           e.printStackTrace();
      }
}

At least, we will use json-simple JAR classes to parse JSON node elements and create Todo new instance :

JsonObject  deserializedObject = Jsoner.deserialize(stringBuilder.toString(), new JsonObject());		

JsonArray todos = (JsonArray) deserializedObject.get("todos");

for(int i=0; i < todos.size(); i ++) {
   JsonObject todo = (JsonObject) todos.get(i);
   String texte = (String) todo.get("texte");
    boolean actif = ( "true".equalsIgnoreCase( todo.get("actif").toString() )); 
    Todo newTodo = new Todo( texte , actif );
}
			

This way is far from very verbose way to code, just to have a view of serialization/deserialization of instance, but other libraries hide all the complexity make the code more readable.

10/ Optional: using a JSP Tag

To be clear, this is a new template language named ‘Expression Language’ or EL. This language use a simple expression to call getters and setters as {todo.texte} to call todo.getTexte().

Suppose we want to improve the item todo part of the JSP page managing the checkbox and the text display into the <il> HTML tag.

First create a folder WebContent/WEB-INF/tags/, then create a new file « JSP tag » named todo.tag as :

The content of todo.tag will be a short part of the HTML/JSP :

<%@tag language="java" pageEncoding="ISO-8859-1"%>
<%@tag import="main.model.Todo" %>
<%@attribute name="todo" required="true" type="main.model.Todo"  %>

<li>
	<input type="checkbox" name="todo_${todo.texte}" value="${todo.texte}"/> 
${todo.texte}   
		
</li>

A todo item is required to use this JSP Tag is the type main.model.Todo is import into the JSP Tag.

Return to the todos.jsp listing item todo, add the import of the JSP tag with :

<%@taglib prefix="projetDevWeb" tagdir="/WEB-INF/tags" %>

Using a prefix is a way to avoid tag name conflicts if you import many tags, the inside you loop on all item todo, you should call the tag with the variable storing item todo information :

<projetDevWeb:todo todo="${currentTodo}" > </projetDevWeb:todo>

But the variable currentTodo must be backup explicitely into the page context (or into the session context if you want to), because the execution content of the JSP tag is different than the contexte of the JSP page. Variables create into the JSP page are not visible by default into the JSP tag to avoid conflict. So adding this liste will create a variable stored into the context page :

pageContext.setAttribute("currentTodo", todo , PageContext.PAGE_SCOPE);

Our final code should be close to this example :

<%
for(Todo todo : Application.getInstance().getLesTodos()){	
   pageContext.setAttribute("currentTodo", todo , PageContext.PAGE_SCOPE);
   %>
        <projetDevWeb:todo todo="${currentTodo}" >
	</projetDevWeb:todo>
   <%
}
%>

You have to understand that the EL language use the coding way ${instance.property} as a simple way to call getters and setters. This simple version of using taglib and JSTL is really basic way to divide the rendering of the HTML page into several sub files.

One more advanced way to use EL language is to add JSTL libraries to Tomcat 9 : jakarta.servlet.jsp.jstl-1.2.6.jar et jakarta.servlet.jsp.jstl-api-1.2.7.jar

Then you can add a new tags’ library by default by add this line of code at the beginning of your JSP page :

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

The using the foreach JSTL tag to iterate on the application list of todo items :

<c:forEach 
    items="${Application.getInstance().getLesTodos()}" 
    var="todo" >  	
    	<projetDevWeb:todo todo="${todo}" > </projetDevWeb:todo> 
</c:forEach>   

Using Application singleton allow us to work with the variable into the JSP context page and into the JSP tag.

Publié dans IMT Mines Alès | Laisser un commentaire

Web Development TP 2

1/ Add a CSS style sheet

With Eclipse, add a new folder css into the folder webapp as (the screenshot show WebContent folder which was the past name of webapp folder) :

Menu New > Other then filter the very long list file with css then choose CSS file. Then create a style.css file with the content below :

div#central{
  position:absolute;
  left: 50%;
  top: 10%;
  width: 800px;
  height: 800px;
  margin-top: -50px;
  margin-left: -400px;
  color: black;
  background-color:rgb(200,220,200);
  padding: 20px;
}
ul {
  list-style: none;
  margin: 0;
  padding: 4px 4px 4px 4px;
}
ul > li{
   display: inline;
}
div#menu{
  position:relative;
  left:10%;
  width:80%;
  background-color:rgb(200,240,220);
}

To use this css your code should containts 2 tag div with « id » as central and another « id » as menu. Consider include the div menu inside the div central and add the link between your JSP and the style.css into the <head> tag :

<link rel="stylesheet" type="text/css" href="css/style.css">
<div id="central">
 <div id="menu">
      <!-- Menu area -->
 </div>

    <!-- Content area -->
</div>

2/ Area menu

Into the menu area, we will use a list of hyperlink to present a menu, consult the website alsacreation to create step by step a simple css menu. Please stop before using graphic menu with rollover and without javascript).

Our purpose here is to add a simple menu with some CSS features include the modification of a simple list tag element :

<ul><li><a href="#">Menu 1</a></li><li><a href="#">Menu 2</a></li></ul>

After the creation of the menu with the CSS style, place de code into a div menu as the image below shows.

Please change colors and design with CSS, it is definitely ugly to let your change as your witch.

To understand the organization between div and other HTML tag, please use the inspector include into Dev Web tools of your browser.

You can inspect the website www.mines-ales.fr, open dev web tools and follow steps as you see below or you can right-click on un HTML element and Menu > Inspector :

On the right part of Inspector, you can see CSS rules and test adding some new CSS. On a local website, you can use Chrome/Chromium with option to save directly your css modification with –user-data-dir switch on the browser command line (see TP 0).

You can create a basic menu with hyperlink to call your application controller as this way :

<ul>
  <li>
   <a href="./Controlaction=pageAddTodo">Add Todo</a>
  </li>
  <li>
      <a href="./Control?action=xml">Liste xml</a>
  </li>
  <li>
    <a href="./Control?action=json">Liste Json</a>
  </li>
</ul>

This form to create a menu is a very HTML way to avoid inject Javascript as we will do later.

3/ First step into Boostrap CSS framework

As start, we will use a so classical Boostrap template named Jumbotron. It is a classical top menu with a central area for title/small text paragraph/one button, then a line with 3 columns of combined title/paragraph/button and a footnote.

To understand the way Boostrap is degin, think about one line with a left area of 1/3 of the width (in green) and a second area same line but 2/3 of the width (in blue) :

<div class="row">
    <div class="col-md-3" style="background-color:green;padding:50px;">
             Left green area
     </div>

     <div class="col-md-9" style="background-color:blue;padding:50px;color:white">
             Right blue area
     </div>
</div>

The purpose of the next step is to rescue the Jumbotron and place our todo items list into the left column.

At first study with Inspector tool the HTML structure and decte the div which manage the left area. That should be a tag as below :

<div class="col-md-4"> 

We should think adding an ID attribute to this tag in the future to identify easily this important DIV.

To copy HTML just right-click on the HTML page and display HTML, select all, copy and paste. Or Open Tab Network with Dev Web Tools, select CSS and JS elements and choose to save each CSS/JS files :

NOTE: Firefox do not save, so instead open into a separate tab and save each element. You don’t need e10-viewport-bug-workaround CSS or JS, we don’t need to support Internet Explorer 10 by now.

4/ Using logs

Using logs is one skill to manage 2 classical problems :

  • I want several levels of display technical information, for example at least INFO and CRITICAL
  • I want several sources and destination of technical information, for example: store information into files, send information to the console, send information to an external server.

A rookie in computer science usualy tend to write code with output to the console :

System.out.println("Error " + e);

This way to work with Tomcat will work, but all output will go into a file named catalina.out even if your web server as 10 different applications into one single file. A mess event if you use the second output System.err.println.

Thinking an application in production mode, you should want to store only critical information at the opposite in development mode, you should want verbose log.

Log4J is a Log framework tool managing several destination to logs (console and/or files and/or database and/or dedicated log server). We could start a program with a certain log level and even change it by program.

To install log4j, just download my library version log4j-1.2.17.jar (very simple to learn) and place it into sub folder lib (to create if not exist) into WEB-INF folder (inside webapp folder).

Add the new jar to the build path by right click on the jar Menu Build Path > Add to Build Path :

NOTE: we can add many jar libraries to deploy with our web project so each web application store its specifics jars independently on another dev web project as you see below :

NOTE: you should also use a tomcat specific lib folder to store libraries in common for all dev web projects on the Tomcat web server. The strategic is always to choose to fix a specific library for all the project to have a standard version, or in the opposite to let project autonomous. That a strategic in production decision.

To configure log4j, we need to create a log4j.properties file into Java Resources > src folder (no need package, this is not a java file just a configuration file). Create a new file, righ click on the folder New > Other …> General > Untitled Text File.

Fill the file with information to output logs to the console and to a file store into a folder created by you into the Dev Web project (C:/DEV/workspace/DevWeb/logs/)

log4j.rootLogger=DEBUG,stdout,file

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n


log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=C:/DEV/workspace/DevWeb/logs/out.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%-7p %d [%t] %c %x - %m%n

NOTE: we will use a folder available to Eclipse after Refresh (F5 key) to make it simple to see new content, but it is more common to place files outside the computer on a separate drive.

NOTE2: if you find any troubles that Tomcat not found any appenders (WARNING: No appenders could be found for logger) into the console. You could copy the log4J.properties into the following folder : src/main/webapp/WEB-INF/classes/.

The first line is to set the log level to DEBUG and there are 2 loggers: stdout and file (named as you want but do not forget to change everywhere). We can later add more loggers to a database, a log server, emails, log rotation, etc.

Each line starting by log4j.appender.stdout sets settings for a logger which display information into the console as a System.out.println() did.

Each line starting by log4j.appender.file sets settings for a log file so the log file name and the pattern form of each line as :

Date: %d{ABSOLUTE}
Log level: %5p
Logger name: %c{1}
Log line number: %L
Message: %m

More pattern settings for log4j

Now to use a logger into a class, we will create a log instance as a property of you class :

private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger("DevWebProject");

NOTE: I use the absolute name of the class Logger (org.apache.log4j) because Tomcat include others log framework (org.apache.commons.logging) for internal purpose and it could create conflict. Here is an example of strength/weakness of Java with several solutions for the feature of logging system, but the variety of framework create multiples situations not so easy to manage. At this opposite, the competition between framework create synergy to make better product.

Now, to log an information, we can call instance with the level of severity. There are usually 6 levels, from the most critical to the lowest :

  1. fatal
  2. error
  3. warm
  4. info
  5. debug
  6. trace

If the log4j properties file set the log level to info, logs messages at level fatal, error, warm and info will be sent.

Keep in mind to set the level accorded to the importance as explain it in this article: https://dzone.com/articles/logging-best-practices

As an example, if I ant to log a new created todo item, level will be very low debug level because in production I do not want this level of information.

// Action1 addTodo 
if ("addTodo".equals(action)) { 
  logger.debug( "addTodo " + texte );
}

Or this pattern way as exxample by Setu Patani in his article :

// Action1 addTodo 
if ("addTodo".equals(action)) { 
  logger.debug( String.format( "addTodo %s", texte ) );
}

Log4j new version accept pattern as this form :

// Action1 addTodo 
if ("addTodo".equals(action)) { 
  logger.debug( "addTodo {}", texte );
}

Now we can create messages with the log level according to the criticizes:

logger.trace("Very not important message");
logger.debug("May be useful in debug mode");
logger.info("Classical message level");
logger.warm("Just warning, but the code should not stop");
logger.error("Classical error level");
logger.fatal("Very important error");

So code several logs messages into your program and make it work to create many logs. Check console and the log file (Refresh folder with F5 key in Eclipse to update folder content)

The first line of log4j.properties file set the minimum error level :

log4j.rootLogger=DEBUG // display FATAL,ERROR,WARM, INFO et DEBUG

or

log4j.rootLogger=ERROR// display FATAL et ERROR

Into the program, I can change the level as I wish :

log.setLevel( org.apache.log4j.Level.INFO );

Now add a tird logger to display HTML report logs, with a new log :

log4j.appender.html=org.apache.log4j.DailyRollingFileAppender
log4j.appender.html.DatePattern='.'yyyy-MM-dd-HH-mm
log4j.appender.html.layout=org.apache.log4j.HTMLLayout
log4j.appender.HTML.layout.LocationInfo=true
log4j.appender.HTML.layout.Title=Application logs
log4j.appender.html.File=C:/Dev/workspace/logs/application.html

Do not forget to add it on the first line of log4j.properties and start again your web application to create new logs messages.

This logger will create an independent HTML file according to the date format. We can also create a Rolling file with a separate level call requete with a max log file and a max number of rolling log files with :

log4j.rootLogger=DEBUG,stdout, file
log4j.logger.requete=INFO, requete

log4j.appender.requete=org.apache.log4j.RollingFileAppender
log4j.appender.requete.File=c:/Dev/workspace/DevWeb/logs/requete.log log4j.appender.requete.layout=org.apache.log4j.PatternLayout 
log4j.appender.requete.layout.ConversionPattern=%-7p %d [%t] %c %x - %m%n
log4j.appender.requete.MaxFileSize=10KB
log4j.appender.requete.MaxBackupIndex=5

This log could be use to log only SQL request but limit the number of file to preserve a certain amount of logs. This way several files requetes.log.X will be created until 10Kb for one file. After 5 files created, the first one will be deleted to store new logs.

5/ Install small plugin Eclipse Log Viewer

To display logs, you can use a small Eclipse plugin call Log Viewer. Just drag and drop the « Install » button into Eclipse to popup the windows to confirm installation (acceptation of license, acceptation of certificate, etc.)

Then you can display into your Eclipse J2EE perspective this new plugin via Menu Windows > Show View > Other… and search Log Viewer :

At the top right of Log Viewer panel, you can open log file, chose colorization, refresh display, etc.

At first, open a log file newly created, press Open Logfile button :

Then you can apply rules to display element with Show rules preferences:

So then we can set if word INFO, display background color is green, if word is ERROR, background color should be red :

You can display your log to follow the most critical part.

6/ Helper Class

Design pattern Helper is a concept of a « helping class ». As an example, suppose a class Book storing classical information about a book (the term used POJO: Plain-Old Java Object). Instances of book purpose is to store properties (title, page number, author, editor, ISBN, Series name, etc.).

Suppose we want to code several methods to check information as a control of ISBN value, we could imagine create a checkISBN() and checkAuthor() methods into a separate class named BookHelper to avoid including those methods into the Book class. Think the point Book purpose is to only store and provide properties only with getters and setters. So we need to store technical functions into a separate class.

We can imagine create a utility class to store methods which help to check ISBN and Author but fast this class will be full of methods from other class than Book. So let separate methods for class Book into a class BookHelper.

In our project, we can create a class ControlHelper to store method to help Control to log parameters pass to Control class :

private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger( "BoostrapSample.main.ControlerHelper" );

public static void logAllParameters(HttpServletRequest request){

 Map<String, String[]> parameters = request.getParameterMap();
 for(String parameter : parameters.keySet()) { 
    String[] values = parameters.get(parameter);
    String value = Arrays.stream( values ).collect(Collectors.joining(","));

    log.info( "Parametre " + parameter + " " + value ); 
 }

}

Into this method, we use a « new » feature from Java called stream. Streams are a modern way to speed up classical list data processing. Serialization of data processing with stream could be execute on list structures as classes/interfaces Set, List, ArrayList, Map, Array, Collection, etc.

To convert an « old » String[] values into a stream to speed up data processing and create more visible chain of treatment with inline method :

Arrays.stream( values )

NOTE: using inline method is also used in Javascript as we see later

Calling an inline method or anonymous is a programming technic to create an instance and call a method in one line. Suppose we want to code a new call to action method on a button into a graphical interface :

Button b=new Button("Click Here"); 

b.addActionListener(
   new ActionListener(){ 
        public void actionPerformed(ActionEvent e){ 
            System.out.println("Hello World!"); 
    } 
});

In this case the class ActionListerner only purpose is to execute one unique method actionPerformed. The inline version with Java lamba solution should be more visible :

b.addActionListener( e  -> System.out.println("Hello World!"));

The simplification is obvious and time to time called syntaxes sugar to symbolize as easy you could read this line of code.

Into our application, we will use a stream onto our lesTodos list :

lesTodos.stream()

Now we want to use a filter method to return only todos items with actif true value. The symbol -> use at left what is the parameter name on each stream element, then it will apply the method on each right part :

todo -> todo.isActif()

You can compare with the anonymous version :

Class anonymous{
   public boolean method_call_by-default(Todo todo){
       return todo.isActif();
   }
}

So combine previous part, the stream will be :

lesTodos.stream().filter( todo -> todo.isActif())

This feature, mainly used with Data mining and Big Data, is to combine and chain operation to use JVM parallelization capabilities.

Combine todo to rescue only texte of todo actif will be :

lesTodos.stream().filter( todo -> todo.isActif()).map( todo -> todo.getTexte() )

We can then combien and merge data with a separator symbol en transform the final result into a String with :

String reponse = lesTodos.stream().filter( todo -> todo.isActif()).map( todo -> todo.getTexte() ).collect(Collectors.joining(","));

Online, you will find many samples using streams to work, Oracle website shows a sum of integers as a good example.

7/ Conditional debug breakpoint

A common feature with modern IDE is to create a conditional debug breakpoint. Right click in Eclipse at the left of line number into the source code interface et select into the Menu « Breakpoint Properties…« , then you can tick « Conditional« , « Suspend when true » :

Using conditional breakpoint while debug is a way to speed up development. Time to time I debug from the beginning line by line to understand a process, later it will be more valuable to only debug into a certain case. Try it…

8/ Database part

To use a database we will add a JDBC connector linking our Java code to this database. You can use a Mysql JDBC connector if you want to use an already install Mysql server. I use Mysql regulary but for a training course, to manage security, you should consider using a simple Sqlite database.

Using Sqlite with Java is just adding a new JAR sqlite-jdbc-3.8.11.2.jar (or if broken here) into lib directory and create into dao package a new DaoManager class.

The folowing code is an partial example of a singleton DaoManager class, you should improve it with your previous code write until now.

public class DaoManager {
  private DaoManager() {
    try {
      Class.forName("org.sqlite.JDBC");
      connection = DriverManager.getConnection("jdbc:sqlite:c:/DEV/Workspace/todo.db");
     } catch (Exception e) {
      log.severe( String.format( "Sqlite error %s" , e.getMessage() ) );
      }
     log.info("Opened database successfully");

   createDatabase();
  } 


  private void createDatabase() {
    try {
      Statement stmt = connection.createStatement();
      String sql = "CREATE TABLE IF NOT EXISTS TODO ( ID INTEGER PRIMARY KEY AUTOINCREMENT, TEXTE TEXT NOT NULL, ACTIF BOOLEAN NOT NULL)";

      stmt.executeUpdate(sql);
      stmt.close();
     } catch (Exception e) {
       log.severe( e.getClass().getName() + ": " + e.getMessage() );
     }
    addTodo( new Todo("ananas" , false ) );
    addTodo( new Todo("chocolat" , false ) );
  }

public void addTodo(Todo todo){

  try {
    PreparedStatement preparedStatment = connection.prepareStatement("insert into TODO(TEXTE,ACTIF) values( ? , ? )" );

    preparedStatment.setString(1, todo.getTexte() );
    preparedStatment.setBoolean(2, todo.isActif() );

    preparedStatment.execute();
    preparedStatment.close();

  } catch (Exception e) {
     log.severe( e.getClass().getName() + ": " + e.getMessage() );
   }
}

public List<Todo> getAllTodo(){
   List<Todo> returnListTodo = new ArrayList<Todo>();
   try {
      Statement statement = connection.createStatement();

      if ( statement.execute( "Select TEXTE,ACTIF FROM TODO " ) ){
        ResultSet resultSet = statement.getResultSet();
        while ( resultSet.next() ) {
          String texte = resultSet.getString("TEXTE");
          boolean actif = resultSet.getBoolean("ACTIF");

          returnListTodo.add( new Todo( texte , actif ));
        }
       }
       statement.close();

    } catch (Exception e) {
        log.severe( e.getClass().getName() + ": " + e.getMessage() );
    }
     return returnListTodo 
  }

}

There are 2 strategies to call DaoManager singleton, one from the Control class and one from the Application. In my mind, I think more logical to call DaoManager from Control class with « have the hand  » over Application class and DaoManager to do the job.

After calling DaoManager, you should create the database sqlite file c:/DEV/Workspace/todo.db , to check content and structure, I think https://sqlitebrowser.org/dl/ tool it is perfect to open and manage database as phpMyAdmin does for Mysql.

The file c:/DEV/Workspace/todo.db storing the database is locked if you open it with Sqlitebrowser so close the access before restart your web application.

Install Sqlite with DB Browser Desktop check :

Then start DBBrowser and open c:/DEV/Workspace/todo.db :

You can browse your data as you wish :

You can use this tool to check todo items are stored into the database. We will load the list of todo items each time Control class is called. There are solutions to avoid calling on each HTTP request, but at start we are close as standard 3-tiers architectures.

Now there are an important modification of your application. Add a new property call id (of type int) to your Todo class. This id manage a unique identification number for your todo item.

DaoManager should create a table Todo with an auto increment column ID. The database will generate this unique ID et your code should rescue it with a method call getGeneratedKeys() after an SQL command INSERT INTO… :

ResultSet rs = preparedStatment.getGeneratedKeys();
if (rs.next()) {
   autoGeneratedIdFromDatabase = rs.getInt(1);
   todo.setId( autoGeneratedIdFromDatabase );
} else {
 logger.error( String.format("Error exception %s ", ex.getMessage() ) );
}

Now you can use this new ID value to manage your todo for update/delete everywhere into your application. An important refactoring work to check you can manage this evolution of the project.

Publié dans IMT Mines Alès | Laisser un commentaire

Web Development TP 3

Time to time WordPress change double quote into a nice quotation mark, so I use single quote because it is more common, but you should use double quote instead.

1/ JavaScript

JavaScript is a programming language at first dedicated to be executed inside the internet browser. To start a JavaScript piece of code (usually a function), you should create a link between an HTML event and JavaScript. Classical events are onload white loading an HTML page, onclick while ckick on an HTML button, onchange if text is added/suppressed into a text field <input type=’texte’ />.

You can find online, for example on Mozilla website, classical events : https://developer.mozilla.org/fr/docs/Web/Events.

Very basic sample to trigger a dialog box windows while counting plus one each time the button is pressed :

<html>
<head>
<script> 
   // By the passe var count = 0;
   let count = 0;
   function clickHandler() { 
      count = count+ 1;
      alert("Hello, World! " + count );
   }
</script> 
</head>

<body>
<span onclick="clickHandler();">Click here</span>
</body>
</html>

You can try inline with https://jsfiddle.net/ and press « Run » button top left.

When you click on the span area (add green border with CSS) then test your JavaScript.

But JavaScript is time to time not so compatible depending on browser constructors and mobile/desktop version, so try and test a lot.

A classical HTML form validation before submit content, it is classical feature with JavaScript :

<html>
<head>
   <script src="js/script.js"></script> 
</head>
<body>
  <form id="the_form" method="get" action="./Control">
    New Todo: <input type="text" name="todo_text" id="todo_text">

    <input type="button" name="bouton" value="Add Todo"
        onclick="checkTodo()">
    <input type="hidden" name="action" value="Add Todo">
  </form>
</body>
</html>

Ok in this situation, JavaScript code is inside a separate file script.js include into the folder js. Browsers will load the external JavaScript file, as we saw for a CSS file.

We can see we remove the <input type=’submit’ /> button and replace by a standard <input type=’button />. Why, because HTML submit button will submit HTTP GET request and trigger JavaScript function and we want only submit HTTP GET request only if the JavaScript function validate the form.

So we must add a hidden field to send the field action to the Servlet Control.

So the script.js will check is there is some text into the field todo_text (and no jusst empty spaces) :

// Form button trigger checkTodo method
function checkTodo(){
   // Variable to reference a field by the ID todo_text
   let todo_text = document.getElementById("todo_text");

   // content of the text field value
   let todo_textValue = todo_text.value;

   // Remove addition space at the beginning and the end of the value
   todo_textValue = todo_textValue.trim();
   
   // If not text is present, change the text into the field as a warning message and do not submit the form
   if ( todo_textValue === "" ){
       todo_text.value = "Mandatory!";
    }else{
       // Store a reference on the HTML form  
       let form = document.getElementById("the_form");
       // Trigger the submit event on this HTML form as usual
       form.submit();
   }
}

This is a very classical way to work with JavaScript, check fields before sending form. It could be more classical to display the « Mandatory » message just right of the incorrect field.

NOTE: there are regularly evolutions for JavaScript, so function should be write a different ways :

// Classical wat
function a_sum(a,b){
  return a+b;
}

// Create a const variable to reference the function 
const a_sum = function(a,b){
  return a+b;
}

// Anonymous function but store the reference into const variable
const a_sum= ((a,b) => a + b );

New operations on array elements also are created :

[1,2,3].map( x => x * 2 );  
   [ 2 , 4 , 6 ]


[1,2,3,4].filter( x => x > 2 ); 
    [ 3 , 4 ]


[1,2,3,4].indexOf( 2 );
  1 

[1,2,3,4].reduce( (x,y) => x + y );
  10

[1,2,3,4].find( x => x > 3 );
   4

[2,4,6].findIndex( x => x == 3 );
   -1

[2,4,6].findIndex( x => x == 4 );
  1

[2,4,6].some( x => x == 5 );
  false

[1,4,6,7].some( x => x%2===0 );
  true

[1,3,5,7].some( x => x%2===0 );
  false

[1,3,5,7].concat( [1,4,6,7] );
   [ 1, 3, 5, 7, 1, 4, 6, 7 ]

[1,3,5,7].push( 9 );
   [1,3,5,7,9]

[1,3,5,7].join( ";" );
  1;3;5;7

[1,2,5,8].forEach( x => {
  if ( x%2 == 0 ) console.log( x )
  } 
);
     2
     8


[1,2,5,8].every( x => x%2==0)
   false


[2,4,8].every( x => x%2==0)
  true

You can also test inside the JavaScript console of Dev Web browser tools :

For decades, JavaScript additional functions create features as the classical calendar field https://jqueryui.com/datepicker/ or slider https://jqueryui.com/slider/

Group of functions will also became so structured and importants, that we call them Javascript Framework.

2/ jQuery a JavaScript Framework

Vanilla JavaScript (=classical JavaScript) missing so a good deal of syntax flexibility, high level function, libraries and framework was created to manage large number of code in JavaScript.

jQuery is an important one and stills be present or keep and influence on a bunch of JavaScript modern framework. The classical jQuery function to search an HTML tag is really common now : $(‘img’)

With jQuery and JavaScript we want to strike out text if the checkbox of a todo item is checked.

NOTE: You will find JavaScript debug in chapter 3

To strike out a text, we create a new CSS rule into style.css :

.strike{
    text-decoration: line-through;
}

This video displays the Dev Web Tools to understand how to manage HTML, CSS and JavaScript relationships (The video is slow).

CSS editor usage with HTML Inspector into Dev Web Tools

You can test CSS and JavaScript directly into the browser to check and test.

Suppose, we have got this HTML code at start created by JSP :

<p>
<input type="checkbox" name="checkbox_chocolat" > Chocolat
</p>

At first, we need a span tag to manage the text displayed at the right of the HTML field.

<p>
<input type="checkbox" name="checkbox_chocolat" >
<span id="checkbox_chocolat"> Chocolat </span>
</p>

We want to link a click event on the checkbox to trigger an HTML modification adding a new class strike.

<p>
<input type="checkbox" name="checkbox_chocolat" >
<span id="checkbox_chocolat" class="strike"> Chocolat </span>

We need to import jQuery inside our JSP page from a CDN server with this additional HTML tag to load a JavaScript library.

<script
 src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>

We can also download the jquery.min.js and place it into webapp folder to be independant from jQuery server and avoid bugs from jQuery updates.

<script src="js/jquery-latest.min.js"></script>

Now we can us jQuery and JavaScript into our HTML page and start with a basic initial code :

<script language="javascript">

 $(document).ready(function() {
    $("input[type=checkbox]").click(
         function(event) { 
            var name = $(this).attr("name");
            var checked = $(this).prop('checked');;
            console.log( $(this).attr("name") );
           // ... go on woth javascript
         });
   
 });
// 
</script>

Close lookup :

$(document).ready(function() {
    // This JavaScript is trigger when the HTML page is loaded
});

jQuery uses a function named $ to search inside the HTML DOM page on or severals elements/tags, for example :

$("h1") // search h1 HTML tags
$("#header") // search HTML tags with id="header"
$("input[type=checkbox]") // search <input> tags input with attribut input egual to checkbox

So we can add a click event on all checkbox easily :

$("input[type=checkbox]").click( 
    // JavaScript Code 
 );

You can find the starting of the code to create and debug the strike out of the text if a checbox is checked :

        function(event) { 
            let name = $(this).attr("name");
            let checked = $(this).prop('checked');
            console.log( $(this).attr("name") );
           // ... go on with JavaScript
         });

The console.log method display information into JavaScript console tab into the browser.

Now using functions to search tag from its ID and using method addClass(‘strick’), you can add a class to the correct text :

$("span#checkbox_chocolat").addClass("strike");

So try to find by yourself before read the solution…






























$("input[type=checkbox]").click( 
        function(event){
            let name = $(this).attr("name");
            let checked = $(this).prop('checked');
            $("span#checkox_"+name).addClass("barre");
        });

Of course, you should only strike text if the checkbox is checked and unstrike it if checkbox is not checked. So try to look .toogleClass() function in jQuery to manage nicely.

3/ JavaScript debug

Our browsers are capable to debug JavaScript , just open Dev Web Tools.

Open this image into a separate tab to see the animation :

Javascript debugger with chrome

Firefox version, check the breakpoint at line 65 with the blue arro :

JavaScript allows to create a breakpoint with a line of code :

debugger;

This option is really powerful to use with advanced JavaScript frameworks which generate a lot of JavaScript and lost time to time the link between the initial code and the generated one.

NOTE: some framework needs also a $ function so you can also use the jQuery function name as an alias :

jQuery(document).ready(function(jQuery){
            // do not use $() but jQuery()
        });

jQuery allows also to use for each function with jQuery HTML DOM objects :

 $( "p > span" ).each(function() {
         $( this ).removeClass("strike");
   });

4/ React

React is more than a JavaScript library with massive Facebook contributions and a component way of thinking. The main idea is a state variable of a component is modified, then React has to render it to refresh display of this component.

React is a Framework using file extension .jsx using JavaScript to link with. Usually, you can use NodeJs to use React but there are a more simply way to understand. At first, install for Chrome browser family extension for React developer tools.

You have to switch from Developer Tool tabs : console, debug and components.

At first, create a simple react.jsp page to receive this code and using React:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en"><head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>React Local</title>
  <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> 
  <script type="application/javascript" src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
</head><body>

<div id="main"></div>
<script type="text/babel" src="js/react.js" />

</body></html>

You can see we are using the development library of React, in production we should use the minified production version and a library named babel for transcompilation process.

NOTE: you can use the Eclipse plugin React from Codemix available from the Eclipse Marketplace : https://marketplace.eclipse.org/content/react-codemix#group-details. To install, just drag and drop as Log Viewer and validates some dialog windows.

Then you should create a JavaScript file with the sub menu from Codemix as :

Into the HTML page, we will have to create a <div id=’main’> as the top level node component for React. Hierrachy of component will be (don’t forget single quote => double quote) :

  1. Page HTML
    1. <div id=’main’>
      1. App
        1. Liste
          1. Todo
          2. Todo
          3. Todo

    Our first piece of JavaScript will be inside react.js, but in fact this file will be created by concatenation of several jsx files. But to begin, let starts in JavaScript/Babel code react.js :

    const rootElement = document.getElementById('main')
    
    function App(){
       return( <div> newApp </div> )
    }
    
    ReactDOM.render( <App />, rootElement )

    React first line set the top level element will be the div identified by ID main (the jQuery notation should be $(‘div#main’) ).

    The first React component created by App or called by React <App /> tag to be closed to the HTML style of writing code. This component included a hidden method call render. This method is called and create the <div> newApp </div> part.

    The last line create the link from the rootElement from HTML page and the first component.

    Try this code :

    It is important to understand that we are inside a React component with React notation not so close time to time from HTML notation include trouble with double quote.

    Then we will create a second component List called by component App. List will be managed component TodoItem :

    The link will be from App component :

    function App(){
      return( <div> <Liste name="liste1" /> </div> )
    }
    
    ReactDOM.render( <App />, rootElement )

    Then we will create 3 jsx files for 3 different components installed into a folder jsx inside the webapp folder.

    Here the beginning for each React component :

    App.jsx

    function App(){
       return( <div> <Liste name="liste1" /> </div> )
    }
    
    ReactDOM.render( <App />, rootElement )

    Liste.jsx

    class Liste extends React.Component {
            // state variable store the fact to refresh react component
            // 
    	constructor(props) {
    		super(props);
    		this.state = {
    			error: null,
    			isLoaded: false,
    			todos:[{"key":15,"texte":"Beurre","actif":"" },{ "key":16,"texte":"Lait","actif":"checked" },{ "key":17,"texte":"Yaourt","actif":""}]
    		};
    	}
        // Called when component is ready to display into the DOM
        componentDidMount() {
          this.setState({
              isLoaded: true
            });
        }
       // Called when component display is trigger
       render() {
    	const { error, isLoaded, todos} = this.state;
    	if (error) {
    	  return <div>Error: {error.message}</div>;
    	} else if (!isLoaded) {
    	   return <div>Loading...</div>;
    	} else {
    	   return (
    		<div className="list">
    		<h1>List for {this.props.name}</h1>
    		<ul>
    		{todos.map(todo => (
    		  <TodoItem key={todo.key} name={todo.texte} actif={todo.actif} />
              	))}	
    		</ul>
    		</div>
    		);
    	  }
    	}
    }

    In this example, loading data from JSOn is not created from componentDidMount() function, I let you find the code to trigger an Ajax request to laod data. In the same way, you have to send back information from TodoItem to the web server to complete the update cycle of informations.

    TodoItem.jsx

    class TodoItem extends React.Component {
    // add actif variable and record method checkboxHandler on the button
    constructor(props){
       super(props);
       this.state = { actif: props.actif };
       this.checkboxHandler = this.checkboxHandler.bind(this);
    }
    
    // Method called from the checkbox click
    checkboxHandler(){
        // Use console to display information
        console.log("Event");
        // A breakpoint for debug 
        debugger;
        if ( this.state.actif == "checked"){
           this.setState( {actif: ""} );
        }else{
           this.setState( {actif: "checked"} );
        }
    }
    
     // Render the component when needed
     render() { 
      return (
       <li>
         <input type="checkbox" name={this.props.name} checked={this.state.actif} 
    onChange={this.checkboxHandler} />
             {this.props.name}
       </li>
       );
      }
    
     
    }

    At present each React component is inside a separate file, now we need to combine files, and it is possible to trigger from Eclipse to combine those files (and if need to minify content).

    We will use a langage created for Java project called ANT by creating into the src folder a build.xml file :

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <project default="concat_react" name="Concat several babel/react files into an unique file">
    <property name="dir.workspace" value="${dir.buildfile}/.."/>
    <target name="concat_react"> 
    <concat destfile="${dir.workspace}/main/webapp/js/react.js" append="false" >
    const rootElement = document.getElementById('main');
    </concat> 
    <concat destfile="${dir.workspace}/main/webapp/js/react.js" append="true">
    <fileset dir="${dir.workspace}/main/webapp/jsx" includes="**/*.jsx" excludes="App.jsx"/>
    <filelist dir="${dir.workspace}/main/webapp/jsx" files="App.jsx"/>
    </concat>
    </target>
    </project>

    This ANT script will combine reacts files with APP.jsx at first into a new file, webapp/js/react.js.

    To let Eclipse calls the file build.xml, right-click on the Projet > Properties > Builder:

    Press « New… » button, select « Ant Builder ».

    You nee to set a task name, the localtion of the build file should be ${workspace_loc:/DevWeb/src/build.xml} and the directory base ${workspace_loc:/DevWeb/src}.

    Open the Refresh tab, set the folder to refresh with the generated file react.js inside webapp/js/ folder :

    Each time one of 3 React components will be changed inside jsx files, you can force to Build via CTRL+M shortcut (or Menu Project > Build All) and then create the new js file.

    If you want an automatic building (I think not really useful), open third tab « Target » into the Ant task and after « Main » and « Refresh », in front of « Auto Build » click on « Set Targets… » button and tick the default task (the only one you have).

    We can also think about an ANT task to trigger a group of unit test or a minification of files removing comments.

    You can debug ANT script as any kind of Java program, just Right click menu Debug as …> Ant Build.

    5/ Tomcat rewrite mode

    From many years, Apache Web Server used a module calle « Rewriting » whiwh allows to change URL from :
    http://website.com/2018/02/27/
    to
    http://website.com/index.php?year=2018&month=02&day=27

    Usually, a rule rewrite an URL from one forme to an other using Regular expression, with our example :

    RewriteRule ^[^/]*/(.*)/(.*)/(.*)$ index.php?year=$1&mont=$2&day=3 [L,QSA]

    Tomcat could use rewrite rule if you modify 2 files :

    At first, open server.xml file into folder « Servers » ( to display code, change tab at the bottom of the main windows from « Design » to « Source », used « Source):

    Inside tag <Context> of your application, here my application is called HibernateEmacs2018. Change the individual tag <Context /> into an open/closed tag <Context> </Context> then insert a new line with settings to activate RewriteValve:

    <Context docBase="HibernateEmacs2018" path="/HibernateEmacs2018" reloadable="true" source="org.eclipse.jst.jee.server:HibernateEmacs2018">
       <Valve className="org.apache.catalina.valves.rewrite.RewriteValve" />
    </Context>

    The, you will have to create rewrite.config file into WEB-INF folder with one or severals rewriting rules:

    The first rule will be to detect /Control/ and convert to the URL Control?action=getLesTodos to display by default all the todo items :

    RewriteRule ^/Control/$ /Control?action=getLesTodos [R,NE]

    The second rule will convert URL from /Control/delTodo/chocolat/ to /Control?action=delTodo&texte=chocolat :

    RewriteRule /Control/(.*)/(.*)/ /Control?action=$1&texte=$2 [R,NE]

    You can then add rules to improve visibility of URLs, using rewrite mode to use RESTful web services, improve search engine indexation, etc.

    6/ Using a Java Record instead a Java POJO

    A new type of class is available at present is the record type. Ours Java class Todo could be rewrite as a record :

    public record TodoItem(int id, String texte, boolean actif) {
    }

    This record class TodoItem will manage all getters (no setters see below), constructor and classical methods toString() and hashCode(). We can so create a very simple instance as :

    TodoItem todo1 = new TodoItem(0,"chocolat",false);
    System.out.println(todo1.texte);

    You can overwrite a method equals() with our specific business requirement to compare only texte property:

    public record TodoItem(int id, String texte, boolean actif) {
    	@Override
    	public boolean equals(Object obj) {
    		if (this == obj) return true;
    		if (obj == null) return false;
    		if (getClass() != obj.getClass()) return false;
    		TodoItemother = (TodoItem) obj;
    		return Objects.equals(texte, other.texte);
    	}
    }

    The main information about record compare to POJO class, is all properties are considerate to be final as this code should be :

    public record TodoItem(int id, String texte, boolean actif) {
        private finale int id; 
        private finale String texte;
        private finale boolean actif;
    
    }

    So you can absolutely not make a modification on the instance, this code will not work :

    void changeTodoItem(String oldTexte, String newTexte) {
      TodoItem findedTodoItem = Appplication.getInstance().findAfaire(oldTexte);
      findedTodoItem.texte = newTexte;
    }

    The way to use in this case the record it to copie the old record into a new one :

    public void changeTodoItem(String oldTexte, String newTexte) {
     TodoItem findedTodoItem = Appplication.getInstance().findAfaire(oldTexte);
     TodoItem newTodoItem = new TodoItem ( findedTodoItem.id, newTexte, findedTodoItem.actif);
     Appplication.getInstance().removeTodoItem(oldTexte);
     Appplication.getInstance().addTodoItem( newTodoItem );				
    }

    Using a record could be useful for convert data from a JSON to record and exchange data between layer without modification.

    Publié dans IMT Mines Alès | Laisser un commentaire

    Web Development TP 0

    At first, you can download slides from Web Development course with detailed explanations of Servlet/JSP development in MVC. Do not hesitate to read it often to remind features.

    Development tools and software are free and/or open source. Tomcat 9 and Eclipse J2EE are provide regarding of your OS on this website for speed up download http://www.dev.lgi2p.mines-ales.fr/install/, but you can download online anyway :

    TP 0 Installation Eclipse and Tomcat to adapt regards your OS and specific configuration

    Please at first install an Oracle Java JDK version 17 for compatibility purpose, and check the folder where installation is done. JDK update process could also change the folder path.

    If needed, look on your computer to find a software call javac.exe or javac to be sure to find a Java JDK not the Java JRE. The Java JRE is not design to manage Tomcat, and errors will be find late into the process. So best to check and write down the path to a full standard Oracle Java JDK.

    Download and unzip Apache Tomcat 9 core version into a subfolder without space or not classical characters as accents or special characters. Usually with Windows OS, I am please to find a folder call c:\Developpement or c:\DEV to easy managed software programs. With Tomcat is common to unzip C:\DEV\apache-tomcat-9.0.75\ (but it is better to avoid C:\DEV\apache-tomcat-9.0.75\apache-tomcat-9.0.75\)

    Download and unzip the Eclipse IDE for Enterprise Java and Web Development, later called Eclipse J2EE into a specific folder without space or not classical characters as accents or special characters. Usually with Windows OS, I am please to find a folder call c:\Developpement or c:\DEV to easy managed software programs. I usually unzip everything into C:\DEV\eclipse-jee-2023-12-R-win32-x86_64\ (but it is better to avoid C:\DEV\eclipse-jee-2023-12-R-win32-x86_64\eclipse\)

    NOTE: don’t use another Eclipse software provide by another courses, Eclipse Plugins could be in conflict and version of settings could lead to troubles.

    Start Eclipse with eclipse.exe or eclipse program to answer firsts settings as bellow :

    • The folder Worskspace (as Windows path C:\DEV\Workspace) will store all settings and projects, so it is important to backup this place regularly.
    • Tick « Use this as the default and do not ask again » prevent Eclipse to popup this dialog each time Eclipse start. You can switch Workspace in case with Menu File > Switch Workspace.

    You can then close the « Welcome » tab windows, not really useful at this moment.

    The next step is to remove the default Java JRE from Eclipse J2EE and provide our Oracle Java JDK. Technically, Eclipse J2EE could manage several Java versions but we will keep simple with only one JDK.

    • Menu « Windows » > « Preferences » > « Java » > « Installed JREs »
    • Press buton « Add » to provide « Standard VM » with « Installed JRE Home directory is the full path of the initial Oracle Java JDK noted at the first step.
    • Remove previous JDK to be sure that Oracle Java JDK is the default one.
    The default and unique Oracle Java JDK provide

    Eclipse IDE provide several tabs organizations call perspectives. We will use mainly Java EE perspective and Debug Perspective. Please use Menu « Window » > « Open Perspective »> > »Other » and activate « Java EE » perspective (top right button).

    Java EE Perspective is the only perspective with « Server » Tab so if you do not find this tab please switch perspective or right click on « Java EE » perspective button to reset this perspective to its initial configuration.

    Then click on « No servers are available. Click this link to create a new server »

    The first step is to choose the « Tomcat 9 Server » version

    Please provide, the full path to find the Apache Tomcat previously download and unzip as below. I like to choose the correct JDK installed with the previous step to be informed by Eclipse if any update change the JDK provide by default:

    On tab « Server » right click to open the menu and start in Debug Mode this Apache Tomcat server from Eclipse.

    On Windows, the firewall dialog should be open to ask you the authorization to give access to the network to Java and Eclipse, so please answer yes.

    Open « Console » tab to consult all output from Tomcat server, the last sentence should be « The server startup took [xxx] milliseconds » to inform you the configuration is correct.

    To confirm, please open a web browser and consult http://127.0.0.1:8080/ , you could be surprise by :8080, the default settings for Tomcat in development is to listen the 8080 port instead the default http port 80 or the default https port 443.

    The front page will be 404 page because no content will be at present deploy by Tomcat but this page with « Apache Tomcat/9.0.75 » shows everything is fine just there is no content:

    So now, we will create a new project to deploy content into Tomcat. With Eclipse, use Menu File > New > Other > Web > Dynamic Web Project then provide a Project Name without space or specific characters as accents. DevWeb will be ok and press finish button. Then you have to add this project to our fresh new Tomcat server.

    With the « Server » tab, right clic on the Tomcat Server and select « Add and Remove », with the new dialog windows, press « Add all>> » to add this project then « Finish » button :

    Tomcat will automatically deploy the Dev Web project on Tomcat Server. So now download the IMT Mines Ales logo PNG image (https://upload.wikimedia.org/wikipedia/commons/thumb/2/2e/IMT_Mines_Al%C3%A8s.svg/640px-IMT_Mines_Al%C3%A8s.svg.png) into the desktop.

    You can drag and drop the image from the desktop to the src/main/webapp folder and choose « Copy files » or you can place the image into the disk at C:\DEV\Workspace\DevWeb\src\main\webapp\ but after you have to « Refresh » the project by pressing F5 key :

    Now try to correct the URL http://127.0.0.1:8080/ by adding the project name and the image filename to display this image into the browser. The default form should be « http://127.0.0.1:8080/DevWeb/<image_name>.png »

    By default, some Web server could display the content of the folder, but the default configuration of Tomcat is to hide the folder content.

    Now we can add HTML and CSS content from the CSS Zen Garden website, you can download the HTLM file and the CSS file and place those file into the webapp folder. You should restart Tomcat Server to force deployment of file 217.css and « CSS Zen Garden The Beauty of CSS Design.htm » file and consult the URL http://127.0.0.1:8080/DevWeb/CSS%20Zen%20Garden%20The%20Beauty%20of%20CSS%20Design.htm

    You can see with the DevWeb tool of Firefox/Chrome Browser (CTRL+ALT+I key) there are trouble with the 211.css file. The Network tab show the browser is not able to download the 211.css file. You have to correct the « CSS Zen Garden The Beauty of CSS Design.htm » file to match the URL.

    NOTE: anytime you find %20 is the encoding URL value for space, please correct the HTML file to help the browser to download and apply the stylesheet file. The way to open « Network » tab with DevWeb Tools into the Web Browser is the correct way to look of trouble with Web Development, key this way of working in your mind.

    Next step is to create index.jsp page with menu File > New > JSP File, you have to provide the filename as index.jsp and the parent folder should be src/main/webapp by default. Add the content to display number from 0 to 9 :

    You can add a title to your page and click on the left of line number to add (or remove if needed) a breakpoint, then click on the source code to open the menu to Debug As > « Debug on Server » then choose « Tomcat 9 server » and check « Alway use this server when running this project » to avoid this step later, then button Finish.

    You should have an Eclipse dialog to ask you to switch to the Debug Perspective :

    You should check « Remember my decision » and button Switch to prevent this step later.

    Eclipse then open the default browser for your OS (we can change easily with Menu Windows > Preferences) and way to debug the JSP at the line marks by the breakpoint. Press F6 key to step over, or F8 key to Resume to the next break point.

    You can find more details about Eclipse Debug with « Eclipse debogage et points d’arrets« .

    The next step is to create sub folders « css » and « img » to the webapp folder and place contents and find URLs to locate thoses contents.

    You can define a default browser as Chromium, with very helpful settings for development only Menu Windows > Preferences, then General > Web Browser

    I use options for this specific Chromium browser dedicated to Web Development only :

    –disable-web-security –disable-gpu –user-data-dir=c:\DEV\worskpace –auto-open-devtools-for-tabs –remote-debugging-port=9222 -incognito –single-process  "%URL"

    –disable-web-security : allow to open contents from severals web server for Framework development as Node.js and tomcat
    –disable-gpu: desactivation of GPU to test a very basic browser
    –user-data-dir : provide a folder to edit CSS and Javascript from Chromium
    –auto-open-devtools-for-tab: open DevWeb tools by default
    –remote-debugging-port: remote javascript debug mainly use with Typescript
    –incognito: default incognito browser
    –single-process: limit the CPU usage of this browser enough to do the job

    Publié dans IMT Mines Alès | Laisser un commentaire

    Azure Spring deployment

    This article is divided into two parts. First about Spring Boot Web Application and the second part is Tomcat Web Application without database.


    Spring boot Web Application

    1/ Create Web app with Azure account

    Then choose to create a Web App of type Web App + Database :

    Then provide information at first :

    The « Ressource Group » is just a group of several « tools » provide by Azure in ours cases the already created empty group « sftp-test-europe » just provide networks settings, virtual network, database server, web app server and App Service plan.

    We need to provide web app name here ceris1 as application name. This information will be used to create URL, database name, etc.

    We provide the runtime stack of Java 17, and the Java web server stack  » Java SE (Embedded Web Server) ».

    Then we choose MySql database and check « Hosting Basic for hobby or research » to avoid to paid too much for a test.

    The second interface provides information to keep about MySQL settings include server host, login, password, and default database name ceris1-database for me. The default MySQL port should be 3306.

    2/ Modification Spring application

    Change the file applications.properties to provide the azure MySQL settings as :

    I change the default Web server port from 8080 to 80 to create classical URL :

    http://ceris1.azure.com/ 

    We need to generate the final jar of our application, but we also need to avoid tests from Spring. Why, we do not have access to the Azure MySQL server to test our application, so the command will be :

    mvn clean install spring-boot:repackage -DskipTests to create 

    If the creation is OK, you will find a new jar file into :

    C:\Dev23\Workspace-vscode-182.2\todo\target\todo-0.0.1-SNAPSHOT.jar

    Then we need FTPS settings to upload the jar file, with « Deployement Center » we can find several methods but historical FTPS is simple :

    We need FTPs server name (FTPS endpoint), FTP Username and Password to upload file.

    Open Filezilla or any ftps client with settings, remove the default file into « /site/wwwroot » folder and drag and drop the newly created jar file :

    You can test running the project with the Azure SSH console with :

    The first time, open the consol is really slow, but I use it a lot to understand which settings is not OK about MySQL, port, java version, etc. That really useful.

    Enter the command to start spring server with our development after may be a change directory to /home/site/wwwroot/ default folder :

    java -jar todo-0.0.1-SNAPSHOT.jar

    You should find the classical deployment information :

    You should have a port 80 conflit at the end because the default web start application is still alive. Just « Restart » the web app from the Azure interface to let remove it after remove the default file with Filezilla or the Azure console.

    Restart the application with the command line and test it online : https://ceris1.azurewebsites.net/.

    NOTE: the automatic deployment from git server should be used be the purpose here is to understand the basic deployment from a simple jar application.


    Tomcat War Web Application

    From Eclipse Java EE, we suppose to have a Dynamic Web Project with servlet and JSP. At present, we will not include a database, but the process will be identical as Spring boot Web Application.

    1/ Create Web App with Azure account

    Then choose to create a Web App of type Web App :

    Then provide information at first :

    • Resource Group : a group of applications of azure, but in our case it will a group of only this Web App
    • Name: Name of the instance just for URL, and to identify this Web App
    • Runtime stack: Java 17 for our web application
    • Java web start stack: Apache Tomcat 9.0
    • Pricing plan: free F1 (Shared infrastructure) to test this application for free

    After the creation of the instance of web application (could take several minutes), we will consult the « deployment Center » and « FTPS credentials » that we will use to upload with FTP.

    2/ Create and deploy war file

    Open Eclipse Java JEE to create a war file from your application :

    Right-click on your project, the menu Export > war file then provide the destination to create the file : c:/Dev24/app.war. You have to teep the app.war as the name of the war file.

    Then use Filezilla to upload the file, but I find a bug from FTPS credential : use a FTP connection NOT FTPS to simply upload the file from C:\Dev24\ to the /site/wwwroot/ :

    I delete the initial hostingstart.html welcome file installed by default by Azure Web App.

    Then you can consult your application, which will deployed after a short delay (several minutes) :

    The Web application will be slow because we only have a free container for purpose. We can consult the Azure ssh console into the browser to manage logs and information about tomcat.

    Publié dans Développement, IMT Mines Alès | Laisser un commentaire

    Oh my git

    Using GIT command is an important skill to acquire for a developer and even all « code user » generally speaking. I truly recommend the software/serious game, Oh my git ! to learn step by step from the beginning as not an expert but a quiet good deeper into GIT’s mechanisms.

    Before create a French translation, I write here my vision of a solute and some additional explanation about this incredible software. On each lesson, you can choose to write the git command line on the top right as a computer terminal or to play one of card which represent git command :

    You can find the courses from the level zero at top to advanced level at the bottom and event a sandbox area to train. You do not need to install git, it will package into a subfolder where you install the program. Version is compatible from Windows, Linux and MacOs.

    List of lessons by category, first one is green because I succeed, and the small terminal symbol is to show I use no cards only terminal :

    The final sandbox category offer you just to practice with no specific goal.

    Time to work, it could be useful to use, Oh my git! not in full screen, you can create a text file named override.cfg with the following contains:

    display/window/size/fullscreen = false

    The game will be into a separate classical window.

    1/ Living dangerously (levels\intro\risky)

    The first lesson will just to show you how to edit a file into the game :

    Only one file « form.txt » where you can edit the content and at any new line content to « win ».

    The big dark grey area will be used to display a list of project version. A project with git is usually a folder contains files and even other subfolders. Into this lesson, there are no previous versions.

    On the top right, the context and the goal of the lesson is detailed. There are usually not linked of any programming context, just small story on chains of operations on file.

    At the bottom, the dark blue area will display cards (none at present) and on the bottom right, black are will display output from git commands (even if you use cards or terminal).

    The most bottom right are is to enter git command, usually starting by git. Some additional commands are used commons on Linux system and macOS and most supported by Windows.

    The red area just below instructions and goal will turn green if you succeed.

    At the top left, 3 buttons to got « back » to the menu, « reload » the exercise, and « Toggle music ». If you succeed, a new button will appear « Next level », we are into a serious game ;- )

    2/ Making backups (levels\intro\copies)

    The next level is just to show 4 versions of a file, you should recognize a situation into a project with copies of the same growing content with a basic way to label versions.

    Just add a content to the last form2_really_final.txt file, and you will learn how GIT will help you to avoid the mess if the number of version grows with collaboration.

    3/ Enter the time machine (levels\intro\init)

    First playing part, you can even play the « git init » card or enter the « git init » command to start creating a hidden folder .git into your project and start using git. Let’s try :

    The result display a « blue squirrel » which represent an important concept of GIT: the HEAD.

    Suppose you have 3 versions of one project : V1, V2 and V3. GIT is supposed to reference one version with the HEAD. This is the version that your hard drive will store. If you change the HEAD from V3 to V2, you will see store into the folder of your hard drive files from the V2.

    We can say it the cursor of the time machine slider.

    Output command of « git init » confirm everything is fine and working.

    4/ The command line (levels\intro\cli)

    At the bottom right, you can enter command line, and the interface will list you classical command you could use. Here you can see « git init » and « git add » command show. The TAB key will fill the terminal with the first choose.

    This level is just to enter command by hand if you want to succeed.

    NOTE: you can press UP KEY to browse into the command history list of previous commands to avoid enter and enter command.

    5/ Your first commit (levels\intro\commit)

    The next level starts with an already initialized with « git init » command project (HEAD / blue squirrel visible). You are supposed to have a file named glass with one line of text :

    The glass is full of water.

    NOTE: even if the project included the glass file and even if the « git init » command is executed into this folder, GIT do not supervise the glass file.

    So the card shows us in fact 2 commands: « git add . » and « git commit ». Those commands are used often together, but let dig in :

    • git add . : will ask GIT to supervise all files and folders inside the project folder. In our case we can execute « git add glass » command to ask GIT to supervise the only glass file but in other lessons we will have different filenames and not only one file, the dot is the common way to ask GIT all files.
    • git commit : This command is not enough, the correct command should be : git commit -m « Description message to this project version/commit version »

    We can enter separate commands, but the one line version (separate by semicolon) is a good way to use GIT :

    git add . ; git commit -m "Water inside the glass"

    Now we will have a stored version of our project called a commit, that’s the yellow square and commit message will be display if you move your mouse over « Water inside the glass » :

    So now, I can edit the glass file, change content/remove everything, save it and GIT know how to bring me back the glass file at the moment I create the « Water inside the glass » commit.

    NOTE: the dark blue label « main » symbolize the main branch of versions and HEAD reference the last version of this branch.

    Now, I will edith the glass file, change content (« The glass is empty of water. »), save it and open again to check the content is really not the same :

    I can write the command to create a new commit version of the project with glass file modified :

    git add . ; git commit -m "Glass is empty"

    Then, we can see the first version of the project and the second version (only one message will be display with mouse over). The HEAD reference still moved to the last version.

    You can use GIT on your computer to keep several versions of files without other command.

    6/ Working together (levels\intro\remote)

    Now we will work with a main feature to synchronize your project with a futur students file and an other GIT user named teacher into this example. The game will play the teacher, you are supposed to receive a students file, add you name, commit this version and send it back to the teacher :

    You can see a concept not

    The first command is « git pull » to copy from the remote teacher repository the students file.

    We have a teacher version of his main branch, include the students file.

    The file content now :

    ~ List of current students ~
    
    - Sam
    - Alex

    So edit and save the students file by adding your name and commit

    git add . ; git commit -m "3 students"

    You see below the file content already save, and the command GIT prepared to be executed :

    Now, you see below, your local version of the students file is the last version and this different of the teacher version. We can imagine got back for example to the previous version, the teacher one. In fact, we will send our version to the teacher repository.

    Now, the next command « git push » send your version to the teacher who automatically accept the modification. You see the result, the new version of the students file will be available to the teacher.

    This the end of the first part of the game, let’s move to files catagory.

    7/ Unexpected Roommates (levels\files\files-delete)

    This level is just to learn how to remove files, option one enter command to remove all files with _web at the end of the filename :

    rm *_web 

    Drag and drop the rm card on each file with _web into the filename.

    You should still have bed file.

    8/ Interior design (levels\files\files-delete)

    This level is to learn how to create file, you have a bed file contains « A yellow cozy bed. ». you are supposed to create 2 additional files with the word « yellow » inside.

    The command will be just :

    touch lamp

    Or you can drag and drop the touch card and fill the table name as see below:

    The edit content to add yellow word and save it. The following command creates a file with content :

    echo "A yellow lamp" > lamp

    You can also create a yellow chair :

    echo "A yellow chair " > chair 

    The next category branches is really important.

    9/ Moving through time (levels\branches\checkout-commit)

    The « git checkout » command it is the most GIT important command to change the HEAD and navigate from version to version.

    We have 3 commits of the project, with sometime one file, sometime two files.

    First commit where the HEAD reference at start :

    • Commit message « The beginning »
    • a piggy_bank file with :
    This piggy bank belongs to the big sister.
    It contains 10 coins.

    Second commit

    • Commit message « Little sister comes in »
    • a piggy_bank file with :
    This piggy bank belongs to the big sister.
    It contains 10 coins.
    • a little_sister file with :
    A young girl with brown, curly hair.

    Third commit :

    • Commit message « Little sister does something »
    • a piggy_bank file with :
    This piggy bank belongs to the big sister.
    It is empty.
    • a little_sister file with :
    A young girl with brown, curly hair.
    Has 10 coins.

    So we understand the story and steps. Now the most important thing to understand with GIT, each commit version create has got a unique number call « chawouane ».

    In my screenshot the « git log » command show the « chawouane » ddd8b8ca6fc2891c6311d3ed620d1a2acbb1b74f for the first commit (the HEAD commit)

    You can also right-click on the yellow square commit symbol to paste the « chawouane » code into the terminal.

    NOTE: you can use the « chawouane » short version, indeed the 7 first characters « ddd8b8c » as equal as the long version.

    Now to move forward, we can enter the « git checkout » command and right-click on the next commit to paste the « chawouane » code or play the card on the next commit :

    The now, the HEAD is on the second commit, we will see the second file little_sister inside this commit :

    Of course, we can move to the last commit with the same method : « git checkout » command with the last « chawouane » code or again drag and drop the card on the rightest yellow square.

    Then into this commit version, we will remove the « 10 coins » from the little_sister file and place the phrase « 10 coins » inside piggy_bank file (don’t forget to save file).

    Then to validate this version, the command will be :

    git add . ; git commit -m "Restore peace"

    GIT shows several important information:

    • 2 files changes with this new commit version
    • HEAD reference move to commit « chawouane » code 67db068

    10/ Make parallel commits (levels\branches\fork)

    This level will let you move back and front into the timeline with « git checkout » command but you can also move backward with « git checkout HEAD^ » command. There is no move forward equivalence, you will have to use « chawouane » code.

    Here the timeline of commit message to understand when something goes wrong:

    So we can move back from HEAD, as proposed the command could be :

    git checkout HEAD~2

    Now we have to edit, cage/child file with this content :

    A small child.
    It's really loves cats.
    It's give a lion a lollipop.

    Then edit also the cage/lion file with content :

    Looks very happy with a lollipop.

    Now time to commit to create a new story with :

    git add . ; git commit -m "Happy ending"

    A new branch is created with a diferent story.

    11/ Creating branches (levels\branches\branch-create)

    Now we will manage the fork/branch to identify each way individually. We have 3 commits, the initial one where HEAD is placed, here is the name of other commits to understand :

    We want to create a name of each branch to easily checkout from one to an other. So move to the « Go to the concert » commit with the « chawouane » code then at this place create a branch :

    That will create a branch name concert reference this branch of versions :

    NOTE: HEAD is not linked to the « concert » branch only on the last commit of this branch. That could be tricky as we see later. The recent version of git accept a creation and a move of the HEAD with git switch — create new_branch, but we will see later also.

    Now go to the birthday branch and create it as we did :

    NOTE: we can also delete the concert branch with « git branch -D concert » if needed

    ADVANCED TRICK: if you really want all the commit event at the starting point of this level when you are on the first one :

    git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$'

    This command will just display all the « chawouane »code then you can loop on to display comment on :

    for chawouane in $(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$' | cut -f1 -d' '); do     git log --format='%s %h' -n1 $chawouane; done 

    In fact the first part, will extract the « chawouane » code from commit (and not for blob or three files of git) and remove the word « commit » :

    git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$' | cut -f1 -d' '

    Then we will loop with $chavouane variable do execute on each « chawouane » code

    git log --format='%s %h' -n1 $chawouane

    This will display the comment and the sort version of the « chawouane » code as :

    Evening preparation 2090c72
    Go to the birthday  24d1d4
    Got to the concert  792b42f

    12/ Branches grow with you! (levels\branches\grow)

    This level use the previous configuration with 2 branches « concert » and « birthday ».

    Do you reminder that we have to find the « chawouane » code to move at the end of a list of commit versions, now with named branches, to go at the end of the concert, just :

    git checkout concert

    To win this level, just edit the you file and add one line for example : « This is great » and commit as usual :

    git add . ; git commit -m "This is great"

    The HEAD reference is linked to the concert branch name, so a commit on the HEAD will let move branch name also.

    Then you could go to the last birthday commit but not with the branch name only on the last commit, change you file and create the new commit :

    git checkout <chawouane code>

    Then edit you file and commit the new project :

    git add . ; git commit -m "Go to birthday party"

    In this situation, our HEAD moves independently with the birthday branch name, creating a top commit not linked directly to the branch birthday.

    So in the end, we can succeed the level with this kind of operation :

    git checkout concert
    echo "It is great to go to concert" >> you
    git add . ; git commit -m "This is great"
    
    git checkout birthday
    git checkout $(git rev-parse HEAD)
    echo "It is great to go to birthday party" >> you
    git add . ; git commit -m "Go to birthday"

    Command git rev-parse HEAD display the « chawouane » code of the commit referenced by the HEAD

    To simulate an edition of the you file by adding content, we can use the following command :

    echo "This is great" >> you

    This example will use a detached HEAD to concert branch and explain how to managed detached HEAD

    We will try something different to learn to manage an interesting case when your HEAD reference is not linked to the concert branch name :

    Suppose, we use the « chawouane » code to checkout the last commit version (git checkout <chawouane code>). Then if we create a new commit with the following command :

    git add . ; git commit -m "concert alternative"

    There will be into this situation with an HEAD reference creating a commit not really inside de concert branch :

    To correct the situation, we have to move HEAD from « concert alternative » to « concert » branch with :

    git checkout concert

    But now we have to move « concert » branch name and HEAD to the last commit unconnected to any branch. There is a new command using with the « chawouane » code of this lonely commit :

    git reset --hard 9b58db3

    NOTE: We should usually not use git reset –hard because into Oh my git software we are able to find any commit from the graphic interface. Usually, move the HEAD and the branch name on a new commit could lead to lost a whole part. Or, you could maybe use git reflog to display the list of everywhere HEAD was, but it could be long to identify links between commits.

    Yes, I used the small « chawouane » code and that will modify at the end HEAD reference and branch name :

    Try to avoid detach HEAD from the branch name is a classical way to work with GIT.

    14/ Deleting branches (levels\branches\branch-remove)

    In this level, you have to find the only way where you arrive at school and delete other branch.

    To help you avoid checkout, all commit and open file to find the correct way. The correct one is just leap branch.

    Just delete other branch with :

    git branch -D friend
    git branch -D ice-cream
    git branch -D music

    You can see, delete branch do not delete commit project version, you still can move HEAD to any commit with the correct « chawouane » code :

    15/ Moving branches around (levels\branches\reorder)

    This level will learn you to move branch name, at the begining ,the you file :

    You do not have a baguette.
    
    You do not have coffee.
    
    You do not have a donut.

    On each branch, there are one line slightly different :

    • you file from baguette branch contains « You drank coffee. »
    • you file from coffee branch contains « You ate a baguette. « 
    • you file from donut branch contains « You have a donut. « 

    So let’s start with just a modification for donut branch, using command sed to change content of the file (-i operation on the same file, ‘s/have/ate/’ regular expression to substitute have a donut by ate a donut ) :

    git checkout donut 
    sed -i 's/have a donut/ate a donut/' you
    git add . ; git commit -m "Donut eaten"

    Then switch branch name with classical command, just store the initial « chawaoum » code of « coffee » branch into a chawaoum.txt (because the oh my git bash do not create variable) :

    git checkout coffee
    git rev-parse HEAD > chawaoum.txt
    git reset --hard baguette
    
    git checkout baguette
    git reset --hard $(cat chawaoum.txt) 
    rm chawaoum.txt

    Ok if we can use a simple bash environment variable, we could simplefy with :

    git checkout coffee
    chawaoum=$(git rev-parse HEAD)
    git reset --hard baguette
    
    git checkout baguette
    git reset --hard $chawaoum
    

    This is the end of the branch part of the game, let’s move to merge catagory.

    16/ Merging timelines (levels\merge\merge)

    After split and move branch, we now learn to combine branches. The level start with the well-known 3 parallels situation:

    • You eat the baguette
    • You drink the coffee
    • You eat the donut (HEAD pointing here)

    You can merge without branch name, but it will be more common to at first create branches. It is OK for this training to use only chawaoum code.

    The structure of the you file into the initial node ( via command git checkout HEAD~2 ):

    You do not have a baguette. 
    
    You do not have coffee. 
    
    You do not have a donut.

    You understand on each way, you eat or drink something and each file will have a different line. The baguette branch will be :

    You ate a baguette. 
    
    You do not have coffee. 
    
    You do not have a donut.

    The purpose of timelines is to regularly merge two branches into a new one. Of course, we hope that each different line on each separate file will be merged.

    The most simple solution is just merge all the last commit of each « branch » into the HEAD:

    git merge <chawouane of 'You eat the baguette'>
    git merge <chawouane of 'You drink a coffee'>

    But we will try to find a « chawouane » code from the comment message of a commit. I think at first use this command but that work only into HEAD ancestror :

    git log --all --oneline --grep="baguette"

    We need to extract all the « chawouane » code from the git and cycle on each of them to display message, so find all « chawouane » code

    git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$' | cut -f1 -d' '

    Suppose we have a « chawouane » code we can display the message and the « chawouane » code with git log command only with the message contains baguette:

    git log --format="%s %h" --grep="baguette" -n1 <chawoane code>

    This will display the message (with %s) and the short chawouane code (with %h) only with baguette (with –grep) , -n1 switch is is just to avoid duplication.

    Then loop on everything :

    for chawouane in $(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$' | cut -f1 -d' '); do 
        git log --format="%s %h"  --grep="baguette" -n1 $chawouane; done

    This will display :

    You buy a baguette 43e1279
    You eat the baguette 6a6e2a1

    Now we know which « chawouane » code is associated to the final commit message, and you see the importance of a clear and explicit commit message.

    So just merge HEAD with the final commit which « chawouane » code is 6a6e2a1 :

    git merge 6a6e2a1 

    If you open the you file, you find the content merge with the final line changed :

    You ate a baguette. 
    
    You do not have coffee. 
    
    You ate a donut.

    Now merge with the coffee « branch » (no branch name) by finding the correct commit :

    for chawouane in $(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$' | cut -f1 -d' '); do 
        git log --format="%s %h"  --grep="drink.*coffee" -n1 $chawouane; done

    I use again regular expression to find the commit message « drink<zero to n caracters>coffee » and then find the only « chawouane » code and merge with :

    git merge fa4af5a

    Then the final merged file is :

    You ate a baguette. 
    
    You drank coffee. 
    
    You ate a donut.

    The final situation with merge commit default message, a merge create a new commit :

    Of course, you can set a custom message with :

    git merge fa4af5a -m "Merge coffee with baguette and donut"

    17/ Contradictions (levels\merge\conflict)

    This level has 2 different timelines muesli and pancakes, HEAD is on the main branch. For french user, I should rename chocolatine and pain au chocolat.

    The sam file will store :

    • At the beginning : « Just woke up. Is hungry. »
    • At Muesli! : « Had muesli with oats and strawberries for breakfast. »
    • At Pancakes! : « Had blueberry pancakes with maple syrup for breakfast. »
    • At Go to work: previous message plus « Is at work. »

    We can not merge as we did in the previous level, the sam file will be in conflict.

    At first, I need to explain something about 2 very old Linux commands, diff and patch. Suppose a 1-3.txt file with :

    one 
    two
    three

    Suppose a 0-2.txt file with :

    zero
    one
    two

    The diff command with -u for common line will be :

    diff -u 0-2.txt  1-3.txt
    
    
    --- 0-2.txt
    +++ 1-3.txt
    @@ -1,3 +1,3 @@
    -zero
     one
     two
    +three

    Symbol minus is linked at 0-2.txt file and symbol + to the other file 1-3.txt. So we can understand the difference between two files and the common lines. If we remove the commons line and redirect to a file :

    diff 0-2.txt 1-3.txt > patch-from-0-2-to-1-3.txt

    We create in fact a file that the patch command could apply to patch the 0-2.txt with the same content as 1-3.txt file.

    patch 0-2.txt  < patch-from-0-2-to-1-3.txt
    
    cat 0-2.txt
    one
    two
    three
    

    So the GIT merging process is really similar, you can try git diff on the initial files 0-2.txt file and 1-3.txt to display the same output as diff :

    diff --git a/0-2.txt b/1-3.txt
    index af9184c..4cb29ea 100644
    --- a/0-2.txt
    +++ b/1-3.txt
    @@ -1,3 +1,3 @@
    -zero
     one
     two
    +three

    a/ and b/ are simply generic value to manage file with identical name a/sam and b/sam will be used in our future case.

    So return to our case with a git diff to show difference on the unique sam file :

    git diff muesli pancakes 
    
    diff --git a/sam b/sam
    --- a/sam
    +++ b/sam
    @@ -1,3 +1,3 @@
    -Had muesli with oats and strawberries for breakfast.
    +Had blueberry pancakes with maple syrup for breakfast.

    So we know the conflict that will create the git merge command, now how to manage :

    git checkout muesli
    git merge pancakes

    A message explain the conflict, we can open sam file and try to understand this weird notation :

    <<<<<<<< HEAD
    Had muesli with oats and strawberries for breakfast.
    =======
    Had blueberry pancakes with maple syrup for breakfast.
    >>>>>>> pancakes
    
    Is a work.

    So the HEAD at present is equal to muesli branch then just below HEAD you could see the line in conflict into HEAD then multiples equals separate the line from pancakes branch in conflict.

    At the end, the sentence « Is a work. » is common, so no conflict.

    GIT ask us to manage the situation, so delete/write and replace everything as and save the file :

    Had muesli with oats and strawberries and blueberry pancakes with maple syrup for breakfast.
    
    Is a work.

    Then we need to commit the merge :

    git add . ; git commit -m "Muesli and pancakes"

    Of course, we need to move main branch to the final muesli commit with :

    git checkout main
    git reset --hard muesli

    NOTE: It is clearly possible to at first move the main branch to the muesli with :

    git reset --hard muesli
    git merge pancakes
    echo "Had muesli with oats and strawberries for breakfast." > sam
    echo "Had blueberry pancakes with maple syrup for breakfast." >> sam
    echo "Is at work." >> sam
    git add . ; git commit -m "Muesli and pancakes"

    18/ Abort the merge (levels\merge\merge-abort) Optional

    My Oh my git version include into the folder levels/merge/ a level not visible by default, you have to edit the sequence file into this folder, then add merge-abort at the end of this file and restart the program.

    This level is just to checkout to a « Main Lane » commit or to « Side Lane » commit then try to merge but a the end just cancel the operation with :

    git merge --abort

    This situation is just to understand how to undo the git merge in conflict :

    You can use the previous level : Contradictions to undo the merge conflict with :

    git checkout muesli
    git merge pancakes
    cat sam
    git merge --abort

    This will cancel the merge. Now a new category : index.

    19/ Step by step (levels\index\conflict)

    The next level will go deeper into GIT command :

    But in fact, I think there is a problem with this exercise, to win we have to change the content of smoke_detector file into step-by-step branch, remove « absolutely silent » and create a new commit.

    git checkout step-by-step
    sed -i 's/absolutely silent/absolutely noisy/' smoke_detector
    git add . ; git commit -m "remove silent"

    I think the purpose is to show several commits with step-by-step branch is better than the short lonely commit with all-at-once branch.

    20/ Add new files to the index (levels\index\new)

    The next level will learn how to add a file to the initial HEAD main. You can also find the label refs/heads/main, in fact into the hidden .git folder, a subfolder named refs store all references include branches, tags and all heads, the subfolder heads will store heads and branches names, then a file main. This file store the commit chawouane code.

    This level contains only one candle file, so add it to the index and commit :

    git add candle
    git commit -m "Initial commit"

    We can look into the .git folder, with command to see the content of file. But Git provides tools to manage some troubles, so we will use then on this basic sample. Restart the whole level and at first we will find the content of the folder .git/objects/ with this command :

    ls .git/objects/

    So the output, will display only two folders : info and pack. Then just git add candle and list again the folder

    ls .git/objects/
    pack
    info
    70

    The new folder 70 will store the content of our candle file so we can output the ls content of this 70 folder into a candle_code.txt file with (note the folder name ) :

    ls .git/objects/70/ > candle_code.txt

    So now open the candle_code.txt file to discover the « chawouane » code of our file in my case 47e0416f37d1ce42a87358f2d7aa2aeb443af3 that can copy. So now I can open a content file with the concatenation of folder name (70) and the « chawouane » (47e0416f37d1ce42a87358f2d7aa2aeb443af3 ):

    git cat-file -p 7047e0416f37d1ce42a87358f2d7aa2aeb443af3 

    This will display the content of the candle file.

    NOTE: you can think to add any files and folder from the root git file with :

    git add :/

    21/ Update files in the index (levels\index\change)

    The next exercise is to index the candle file with a simple modificaiton change the color blue into yellow :

    sed -i 's/blue/yellow/' candle
    git add candle
    git commit -m "Initial commit"

    22/ Resetting files in the index (levels\index\reset)

    This level works with 3 files : blue_candle, green_candle and red_candle contains at first « It’s burning! » then into the second commit « It’s been blown out. ».

    From the beginning, we used to follow files states from commit to commit. But suppose, we work on blue_candle and green_candle adding information :

    echo "The small rope (the wick) of the candle is red" >> blue_candle
    echo "The small rope (the wick) of the candle is red" >> green_candle

    Now we want to come back to the last commit, but we are AT the last commit, so git checkout HEAD could not work, but we can restore files :

    git restore blue_candle green_candle

    But sometime, you want to keep the content of your working project folder but go back to a previous commit version, so let’s start again same level :

    echo "The small rope (the wick) of the candle is red" >> blue_candle
    echo "The small rope (the wick) of the candle is red" >> green_candle
    git add . ; git commit -m "Blue and green candle wicks are red"
    git reset HEAD^

    This situation will be as a « move backward » into history of commits (green arrow) but if you open blue_candle and green_candle files, you fill find the new line about the wick.

    The git reset command work only on branches and HEAD index and preserve file content.

    But we work only on the full project files, the level asks us to only preserve some files. Suppose a modification on red_candle, blue_candle and green_candle files :

    echo "The small rope (the wick) of the candle is red" >> blue_candle
    echo "The small rope (the wick) of the candle is red" >> green_candle
    echo "The small rope (the wick) of the candle is red" >> red_candle
    git reset green_candle blue_candle
    git add red_candle; git commit -m "Only red candle will versionning"

    But now move to the previous commit with git checkout HEAD^ :

    cat blue_candle 
    
    It's been blown out.
    The small rope (the wick) of the candle is red
    
    
    cat red_candle
    
    It's burning.
    
    
    cat green_candle
    It's been blown out.
    The small rope (the wick) of the candle is red
    

    So even if we move back into the commit history, we still have unsupervised blue_candle and green_candle files, which still the same content.

    This is a situation, you work on a new feature and change all the 3 candles files, but the feature is canceled, and you want to go back and keep the blue_candle and green_candle files content.

    As you can see, the git reset remove the commit supervision on a file, red_candle file still supervised and its contain history could be checkout.

    To solve the level, just again :

    echo "The small rope (the wick) of the candle is red" >> blue_candle
    echo "The small rope (the wick) of the candle is red" >> green_candle
    echo "The small rope (the wick) of the candle is red" >> red_candle
    git reset green_candle blue_candle
    git add red_candle; git commit -m "Only red candle will versionning"

    If you want to restore un-hide blue_candle and green_candle file , you can restore them with a basic git checkout HEAD^, you will see only the red_candle version of file is changed.

    NOTE: to understand the git reset and the git checkout difference, please read the Stackoverflow content and study the diagram below :

    By my way :

    • git reset –hard HEAD~3 : move the HEAD/master flag to 3 commits in the past, the working directory is from b325c, so it is git checkout HEAD~3 and move HEAD/master flag a true time machine.
    • git reset HEAD~3 : same but also the list of managed/supervision files by git, so the working directory is from ed489 commit, but the list of files will be from b325c. So if you add a new file between c10b9 and ed489, the tracking by git into its index will be forgotten but the new file still exist
    • git reset –soft HEAD~3: just keep the file from ed489 and keep the list of managed/supervision files by git from ed489 including a new file creates from c10b9 and ed489
    • git reset file: this command reverse gid add file, should replace by git restore –staged file
    • git reset –keep file: should be used instead of git reset –hard because preserve the working directory from ed489
    • git reset –merge file: same of keep but also preserve not supervised file

    If you want the list of managed/supervised files use on main branch :

    git ls-tree -r main --name-only

    NOTE: suppose your work on many files in a commit, and you want to reset to the initial situation, git reset –hard HEAD will cancel everything for this commit.

    23/ Adding changes step by step (levels\index\steps)

    This level contains 3 files :

    • hammer file : « A hammer, balancing on its handle. »
    • bottle : « A bottle, containing a clear liquid. »
    • sugar_white : « A white sugar cube. »

    In fact, our story misses several element included a candle_added file

    echo "A candle burns the rope" > candle_added
    echo "Hammer smashes the bottle" >> hammer
    echo "Bottle explodes" >> bottle
    echo "The sugar melts" >> sugar_cube
     
    git add candle_added
    git commit -m "First accident"
    
    
    echo "The hammer explodes the windows " >> hammer
    git add hammer
    git commit -m "Second accident"
    
    echo "The candle burns the carpet" >> candle_added
    git add candle_added
    git commit -m "Third accident"
    

    But if you git checkout HEAD~2 (commit message is First accident), you will not find « Hammer smatchs the bottle » into the hammer file :

    A hammer, balancing on its handle.

    But you find inside the next commit :

    git checkout main 
    git checkout HEAD^
    cat hammer 
    
    A hammer, balancing on its handle. 
    Hammer smatchs the bottle
    The hammer exploses the windows
    

    The content of the file is not stores if you do not specify it each time with a git add command.

    NOTE: Usually it is really common to add everything with git add . command to be sure to avoid different version control of file with index of git.

    The new catagory will be remotes.

    24/ Friend (levels\remotes\friend)

    In this level, we find out again a remote friend repository as GitHub server.

    Always start with a git pull command if you work with a friend who add regularly content.

    git pull 
    
    cat essay 
    
    Line 1
    Line 2, gnihihi

    We need to correct the file and add a third line :

    echo "Line 3," >> essay
    git add . ; git commit -m "3 lines"
    git push 
    

    Now the program will simulate a friend adding a fourth line, so :

    git pull
    
    cat essay 
    
    Line 1
    Line 2, gnihihi
    Line 3,
    Line 4, blurbblubb

    Add content and push again:

    echo "Line 5," >> essay
    git add . ; git commit -m "5 lines"
    git push 

    You are lucky, you friend and you are not working on the same line of the same file.

    25/ Problems (levels\remotes\problems)

    There are two ways to manage this level:

    • First option, you are supposed to finish edit the file and want to commit your new version
    • Second option, start with a « git pull » before commit your work.

    Let’s start with the first option, we are supposed to be in a middle of modification and change the file content from « The bike shed should be ??? » to your proposal : « The bike shed should green ».

    So at first, create a commit of your version with :

    git add . ; git commit -m "green"
    

    Then you can try to push :

    git push 

    But, you have got an error because, your local branch call friend/main is not update, so now :

    git pull

    So our local main branch and our local friend/main branch are in conflict. A git pull included a git merge command, so you have a try to merge from the local version to your version.

    You can compare from your version with git diff command :

    git diff main:file friend/main:file
    
    diff --git a/file b/file
    index 076b9c8..60823c8 100644
    --- a/file
    +++ b/file
    @@ -1 +1 @@
    -The bike shed should be green
    +The bike shed should be blue

    Or of course open the file :

    <<<<<<< HEAD
    The bike shed should be green
    =======
    The bike shed should be blue
    >>>>>>> fb733ac9b8d350147249588e849c4686b85c6a78
    

    Now, it is not technical, it is a compromise with your friend to yellow :

    echo "The bike shed should be yellow" > file

    Then commit this new version :

    git add . ; git commit -m "yellow"

    The git push to upload your proposal and the compromise :

    git push 

    You can see there are all the proposal, your friend says blue, you say green and the final version is yellow. But you can also imagine going back because blue is OK.

    Restart the level to try the second option. When you work with someone, you can at first get the latest version from the remote repository with :

    git pull

    And then, you know the local friend/main version is different of your working version.

    You can compare the last commit file content from main branch to the remote friend/main branch with :

    git diff main:file friend/main:file
    diff --git a/file b/file
    index 076b9c8..60823c8 100644
    --- a/file
    +++ b/file
    @@ -1 +1 @@
    -The bike shed should be ???
    +The bike shed should be blue

    So change yellow to your color :

    sed -i 's/green/yellow/' file
    git add . ; git commit -m "yellow"
    git merge friend/main
    

    At this moment, the conflict about bike color appear :

    cat file 
    <<<<<<< HEAD
    The bike shed should be yellow
    =======
    The bike shed should be blue
    >>>>>>> 9e15575465664addhbsjdsbhjqbsdhjdsq

    Now we have to set our color, so edit the conflict file as you which, merge and push to the server :

    echo "The bike shed should you be yellow" > file
    git add . ; git commit -m "yellow"
    git push 

    Now new category changing-the-past :

    26/ Rebasing (levels\changing-the-past\rebase)

    At the beginning, you file is close to lesson 16 with coffee, donut and baguette branch name :

    You do not have a baguette. 
    
    You do not have coffee. 
    
    You do not have a donut.

    On each branch, you eat or drink the corresponding element, but in this level we DO NOT want to merge. This is what we do NOT want to do :

    The git rebase command will try to merge but on the same HEAD branch, suppose we check out to donut branch and rebase with branch coffeee, the situation will be :

    git checkout donut
    git rebase coffee
    

    The donut branch is added to the coffee branch, adding content inside and of course the you file content is :

    You do not have a baguette.
    
    You drank coffee
    
    You ate donut.

    Again, git checkout and git rebase with baguette branch, to create one big donut or baguette branch with you file :

    You ate a baguette.
    
    You drank coffee
    
    You ate donut.

    Now, 2 ways to correct other branch names:

    • create a final branch name, return to main commit and rebase it : git branch final ; git checkout main ; git rebase donut;
    • Rename the main branch as the new branch: git branch final; git checkout main; git branch –move main initial; git checkout final; git branch main;
    • Or move the main branch at the beginning as the future commit branch with git checkout main ; git reset –hard donut

    27/ Reordering events (levels\changing-the-past\reorder)

    This level is to learn how to use git cherry-pick command or git rebase -i command. The you file at the main branch commit contains :

    You just woke up.
    
    You are wearing underwear.
    
    You are wearing pants. 
    
    You are wearing a shirt.
    
    You are wearing shoes. 

    But the trouble is more with the commit order :

    We are supposed to start with underwear then pants then shoes (shirt is not checked).

    As explain, you can use git cherry-pick command with list of command and use the notation branch name~# to identify easily the order :

    • main~1 : Put on underwear
    • main~2: Put on pants
    • main~3: Put on shoes
    • main: Put on shirt
    git checkout HEAD~4
    git cherry-pick main~1
    git cherry-pick main~2
    git cherry-pick main
    git cherry-pick main~3
    git branch main-correct-order
    git checkout main; git reset --hard  main-correct-order

    The final solution creates a copy of each commit into a separate branch and move at the end the main branch name to the new created main-correct-order branch name.

    IS like if we copy/paste from on commit to another commit on a separate branch, just a the end we move the branch main and abandon the old one.

    NOTE: to use the second version, Oh-my-git software misses somes setting, so we have to open the folder from outisde of the software with a git command. At first find the location on your drive of the correct folder with pwd command. in my case, it will be into the following folder:

    C:\Users\pierre.jean\AppData\Roaming\Oh My Git\tmp\repos\yours

    Then check the git log classical command with :

    "C:\Program Files\Git\cmd\git.exe" log --oneline --graph --all --decorate

    So you can work outside of the Oh-my-git software to manage additional operations.

    Option number 2, use git rebase -i to open a file contains the list of operation so again, same starting point :

    So our command will start on the very first commit to restart from scratch :

    git rebase HEAD~4 -i 

    This command will display an editor to manage the content, you will see (chawaouan numbers are difference) the following content and remeber each line starting with # is a comment :

    pick 044e7b9 Put on shoes
    pick b4fcfbe Put on pants
    pick 22446fb Put on underwear
    pick 8f8d77c Put on shirt
    
    # Rebase instructions ... 

    Just change the order from top to bottom as following example :

    pick 22446fb Put on underwear
    pick b4fcfbe Put on pants
    pick 044e7b9 Put on shoes
    pick 8f8d77c Put on shirt

    Force update of Oh-my-git with a git log command and then you will have (without branch name main-correct-order) :

    You can read instructions into the interactive editor of git rebase to learn how to change commits.

    NOTE: instead of moving the main reference with git reset –hard, you can rename the branch with :

    git branch -m main incorrect-order-main
    git branch -m inccorrect-order main

    Then a new category sh**-happens

    28/ Restore a deleted file (levels\shit-happens\restore-a-file)

    This level is really easy now you know git, just git restore essay to reload file from the last commit.

    29/ Restore a file from the past (levels\shit-happens\restore-a-file-from-the-past)

    This level is really easy now you know git, several way to reload file from the first commit.

    You can also :

    • git checkout HEAD^ essay; git add essay; git commit -m « Rescue good version »
    • git checkout HEAD^ — essay ; git add essay; git commit -m « Rescue good version »
    • git checkout HEAD^ ; git branch rescue ; git checkout rescue; git checkout main; git reset –hard rescue
    • git branch -m main main_bad ; git checkout HEAD^ ; git branch main
    • git reset –hard HEAD~1
    • git reset –hard HEAD^

    The first version is to rescue from the previous version the file into the actual working directory then add this version to the index then commit. The double dash — is to manage this example working with a file named main : git checkout — main will work.

    The third version is go back into the past, create a new branch called rescue, then replace the flag main on the same rescue branch.

    The fourth version is the best, rename the branch from main to main_bad then go back and create a new branch call main.

    Fifth and sith versions are lightly worse but use the git reset –hard trick to move the reference main and HEAD on another commit.

    30/ Undo a bad commit (levels\shit-happens\bad-commit)

    In this level, the numbers file into the actual commit contains :

    1 2 3 4 5 6 7 8 9 11

    The last commit contains a typo « More numberrrrrs ». We want to preserve the numbers file content from the last commit, but we want to go back to the previous commit to start over to a next commit.

    To go back preserve the file content with git reset HEAD^:

    The solution would be git reset the previous commit, then correct the numbers file and at the end create a new commit :

    git reset HEAD^ 
    sed -i 's/11/10/' numbers
    git commit -a -m "More numbers"

    This example used a switch -a to automatically « git add » any previous file already follow by GIT. Many times we used git add . command to let know to GIT any new files into the current folder. I could be important to keep a git add <list of file or folder> command to be precise into the list of files and folder supervised by GIT. But if you know that you will work all the time with the same list of files, that could be better to use -a -m or -am switches.

    31/ I pushed something broken (levels\shit-happens\pushed-something-broken)

    In this level, a local and a team repository are used with 4 commits and only on text file.

    • First commit with message « fine » : file contains « this is fine » and empty line, new line with question mark and empty line, new line with question mark, and empty line, new line with question mark ( so 4 individual questions marks with an empty line previsouly)
    • Second commit with message « also fine »: same contains of the previous commit file, but the first question mark is replaced by « this is also fine »
    • Third commit with message « very bad » : same contains of the previous commit file but the next avalaible question mark is substitute by « this is very bad »
    • Final commit with message « fine again », the text file contains :
    this is fine 
    
    also fine
    
    this is very bad
    
    this is fine again

    So we want to undo only the modification into text file the line with « this is very bad », we want the following file :

    this is fine 
    
    also fine
    
    ?
    
    this is fine again

    The reverse of the merge processing will be :

    git revert --no-edit HEAD~1 

    The option –no-edit avoid to open the interactive editor and create a new commit with the modification :

    So now the text file is reverted as and you just push to the team repository.

    We do not have on the git revert command line the option -m to edit the commit message (-m switch is for something different), but we can change the commit message with :

    git commit --amend -m "Change the line 'very bad' into ? "

    This will create a new commit with the new message « Change the line ‘very bad’ into ? « . If you do not push already to the team repository, you can go on with a simple git push. But if you did this scenario :

    git revert --no-edit HEAD~1 
    git push 
    git commit --amend -m "Change the line 'very bad' into ? "

    You are working into a previous version of the project but because this last commit is just a light « amend » commit, you can force the team repository with git push –force.

    The « wrong message » commit is an orphan without impact but avoid to force for other users.

    32/ Go back to where you were before (levels\shit-happens\reflog)

    This level contains a very long list of commits (10 commits) and each commit has a diffrent branch name (same number) but no file :

    The git reflog command just shows the list of the previous position of the HEAD :

    git reflog 
    
    d087659 (HEAD -> main, 10) HEAD@{0}: checkout: moving from 3 to main
    64ef148 (3) HEAD@{1}: checkout: moving from main to 3
    d087659 (HEAD -> main, 10 HEAD@{2}: commit:10
    9ea5730 (9) HEAD@{3}: commit: 9
    411e7a1 (8) HEAD@{4}: commit: 8
    b7a475f (7) HEAD@{5}: commit: 7
    a464650 (6) HEAD@{6}: commit: 6
    75c829d (5) HEAD@{7}: commit: 5
    64ef148 (4) HEAD@{8}: commit: 4
    c372d04 (3) HEAD@{9}: commit: 3
    046a26a (2) HEAD@{10}: commit: 2
    75c829d (1) HEAD@{11}: commit (initial): 1
    

    We have the history of operation on the HEAD reference, so just git checkout 3 to return to the commit reference by the « 3 » branch name as shows at the first line. You can also :

    git HEAD@{1}

    If you open the file levels\shit-happens\reflog into the software installation folder, you will find the list of operation to create the level into [setup] category :

    [setup]
    
    for i in {1..10}; do
        git commit --allow-empty -m $i
        git branch $i
    done
    git checkout 3
    git checkout main

    The setup will create from 1 to 10 commit, git checkout 3 to finish the level.

    The next category is workflows.

    33/ Cloning a repo (levels\workflows\pr)

    This level is quite simple but include one important feature, cloning someone else repository.

    But this level supposes that you can access to the repository directly from your hard drive. This is not the common situation, usually you access remotely to someone repository.

    I try to copy everything (including .git folder) from the friend project folder, and it’s works until you have to send back to your friend modifications. The only option will be to erase everything I work to put your folder. This is not a very collaborative way.

    The git clone will keep the link between the 2 repositories and will manage exchange. The clone will be from one folder ../friend to your actual folder ./ as a copy but link to update from and to each folder.

    git clone ../friend ./

    You can see origin/main and origin/HEAD into your git projet. The word origin symbolize this initial git folder from the clone folder was created .

    Now we can correct the file content, commit into your folder and create a separate branch.

    sed -i 's/= /= 5/' file
    git commit -am "Solution is 5"
    git branch "solution"

    This lesson needs a final command, that could be uses sometime just to make a kind of label associated to a commit. This is not a branch, just a label to symbolize a very important commit as a milestone of your project.

    git tag pr

    It could also be possible to push a tag remotely :

    git push origin v1.0.0

    NOTE: One additional lesson exists into the levels/workflows to manage .gitignore file, this lesson is just to add a chicken file into the .gitignore file. Then git add . command will ignore this file, that classical to avoid to import temporary generated files as .class .so, etc.

    Next category will be bisect.

    34/Yellow brick road (levels\bisect\bisect)

    This level show 30 commits on the main branch with HEAD on the final commit. Each commit message is a number from 1 to 30 include a

    Commits from 1 to 11 include a you file contains : « You still have your key. »

    12th commit include 2 files :

    • you file contains : « Your pocket is empty. »
    • key file contains : « Is on the ground. »

    13th commit include 2 files:

    • you file contains : « Your pocket is empty. »
    • bird file contains : « Is holding a key i its beak. »

    Commits from 14 to 30 include a you file contains : « Your pocket is empty. »

    We can image go front and back until finding the moment when something « wrong happen » that the mechanism includes into git.

    Let suppose we know something bad happen at the 20th commit, so we want to start searching by bijection from there:

    git checkout HEAD~10
    git bisect start

    Now we declare this git commit as bad with :

    git bisect bad

    Then we will go to 10th commit because we remember this commit is OK.

    git checkout HEAD~10
    git bisect good

    The command git bisect good will check out to 15th commit in the middle between 20th commit and 10th commit. We see the actual HEAD between the 10th and 20th commits.

    So the 15th commit the you file contains : « Your pocket is empty. », so we enter git bisect bad and check out another version :

    We can see the new refs/bisect/bad label is now on the 15th commit, not on the 20th commit.

    The 12th commit is not event good the you file contains « Your pocket is empty. » so git bisect bad command again.

    We are on the 11th git commit with a valid you file contains « You still have your key. »

    Now we can move the main branch name to this commit, but we have threes options to mark the good place (because we will not easily use the generated name from the git bisect good (refs/bisect/good-04448cd8cd7cd614cd7f42635959946f9946270)).

    We will create a new branch named main-key-is-ok (with option -b to attach HEAD to this new created branch), then rename main branch as main-lost-key and rename main-key-is-ok as main :

    git checkout -b main-key-is-ok
    
    git branch -m main main-lost-key
    git branch -m main-key-is-ok  main

    or we can use the new git switch –create main-key-is-ok to create a new branch and place HEAD on and I the end I will use it again to keep two branch main and main-key-is-ok for historical information

    git switch --create main-key-is-ok
    git branch -m main main-lost-key
    git switch --create main

    or we can use a tag to flag the position with :

    git tag key-into-pocket
    git branch -m main main-lost-key
    git switch --create main

    If you do not want to preserve the main branch, you can just move the main and HEAD with git reset –hard using the reference

    git checkout main 
    git reset --hard refs/bisect/bad~1
    

    or before return the HEAD to the main branch :

    git tag key-into-pocket
    git checkout main
    git reset --hard key-into-pocket

    Next category is « stash »

    35/Stashing (levels\stash\stash)

    This level begin to let you use the concept of stash, you can imagine that you start from initial commit and work on files, but you are far from the moment you want to commit. You have to think that commit with a project into a team could trigger deployment and unit test, so you do not commit often.

    You have a recipe file with the following content :

    Apple Pie:
    - 4 Apples
    - 500g Flour

    Add a new line like :

    Apple Pie:
    - 4 Apples
    - 500g Flour
    - Secret ingredient

    The command, git stash push, will create two temporaries commits to let you work on something different.

    The important element is the HEAD on the main branch contains only recipe file with the following content as the initial commit was created:

    Apple Pie:
    - 4 Apples

    You can use the git stash command as well

    36/Pop from Stash (levels\stash\stash-pop)

    The next level works with the previous example (without the secret ingredient line).

    The git stash pop command is just the « undo » from git to reverse the situation to the initial one but preserve commits create to temporary stash.

    The git stash apply command will just keep the branch reference of the stash in case of need:

    You can remove the stash with git stash drop command to keep the initial situation ( the green area is supposed to be black to hide the previous refs/stash temporary branch):

    37/Clear the Stash (levels\stash\stash-clear)

    If you have so many stashes, you can list then, clear them or clear one :

    • git stash list: to list all stashes
    • git stash clear: to clear all stashes
    • git stash drop : this command must be follow with the stash id stash@{0} for the last one, stash@{1} for the previous, etc

    I think it is better to preserve one stash and avoir this situation.

    38/Branch from stash (levels\stash\stash-branch)

    A more common situation is to transform a stash commit into a new alternative branch to go on with :

    git stash branch alternative

    39/Merging popped stash (levels\stash\stash-merge)

    A classical situation, you want to merge content from the recipe main branch commit and the recipe main into the stash commit :

    The git stash pop command will create a conflict into the recipe file :

    Apple Pie: 
    - 4 Apples
    <<<<<<<<< Updated upstream
    - Pinch of Salt
    ========
    - 500g Flour
    >>>>>>>>>> Stashed changes

    Just edit the file to correct the situation, as for example :

    Apple Pie: 
    - 4 Apples
    - Pinch of Salt
    - 500g Flour
    

    The following command will create a classical situation:

     git add . ; git commit -m "Include salt and flour"

    Do not forget to clear the stash with git stash drop command.

    The next category will work with tags.

    40/Creating tags (levels\tags\add-tag)

    This level is just to create a tag with the classical git command. The feature-list file contains :

    Adding feature 1
    Adding feature 2
    Adding feature 3
    git tag v3

    I suggest creating a tag to indicate version is 3

    41/Removing tags (levels\tags\remove-tag)

    The next level contains 3 tags to remove as :

    git tag -d v1
    git tag -d v2
    git tag -d v3

    42/Tagging later (levels\tags\add-tag-later)

    This level is just to understand that you can tag previous commit into the history of commit with :

    git tag v1 HEAD~1

    43/Remote Tags (levels\tags\add-tag-later)

    This level manage to push your tag that are not usually suppose to be share (branches are supposed), but this is the initial situation :

    If you create on local HEAD a new tag v2 you can share with the remote friend repository :

    git tag v2
    git push friend v2

    Then you can delete it remotely :

    git push friend --delete v2

    You can also rescue the v1 tag from friend repository with :

    git fetch friend 

    If you wan to delete you local tags if there are not into your friend repository :

    git fetch friend --prune --prune-tags

    The next category is sandbox.

    44/ Sandbox (levels\sandbox\)

    There are 3 levels without goals just to work on git with 3 differents situations. Name of exercises are explicit :

    • Empty sandbox
    • Sandbox with a remote
    • Sandbox with three commits

    Do not forget you can add your own exercice into Oh my git a very good tool to create situation to learn git.


    Using Github with Oh-My-git

    The Oh-my-git software is design to train you using git on your computer, but you can manage to let it works with the GitHub.com website. Github is not the only website providing git server storage, Gitlab software could be installed, Amazon, Sourcesup (Renater), Bitbucket, etc. provide also git server storage.

    At first, you have to create a Github account, then open You Menu > « settings » > Developer settings > Personal access tokens > Tokens (classic) or just follow https://github.com/settings/tokens , then « Generate a personal access token » :

    Then you can provide a name of this token, give an expiration date (not perpetual), give right at least on repo rights and press the bottom button « Generate token » :

    Then you have to copy the generated token, please be sure to copy the token because there is no way to give you back this one. If you lost it, you have to create a new one.

    Github website provides the basic start we need on the first page you see after login creation.

    At first, create a new private repository to practice :

    Then you have the quick setup listing commands to Oh-my-git could use with the token modification :

    Let’s start to use GitHub with Oh-My-git, open the « Empty sandbox » level and delete the initial configuration :

    rm -rf .git 

    Then you can enter the quick setup commands, except adding your token into the URL :

    echo "# hello1" >> README.md
    git init
    git add . ; git commit -m "First commit"
    git branch -M main
    git remote add origin https://<token>@github.com/pierrejean/hello1.git
    git push -u origin main

    This will create the README.md file into your Github repository.

    More important information about remote connection :

    The following command creates an alias origin to link git push command to a server :

    git remote add origin https://<token>@github.com/pierrejean/hello1.git

    We saw the remote repository as friend or teacher in previous levels:

    git remote add friend https://<token>@github.com/pierrejean/hello1.git

    If you want to change the URL to use origin as classical alias, the command is :

    git remote set-url origin https://<token>@github.com/pierrejean/hello2.git

    In fact, Oh-Myt-git do not provide a secure way to login/password from https/ssh default solution with Git/GitHub so using a token is a simple solution also for integration but keep the token as safe and secret as any password.

    But please add default user and email for your local account :

    git config --global user.name "pierrejean"
    git config --global user.email "pierre.jean@mines-ales.fr"

    You can create prefill a new github repository from your browser with this URL :

    https://<token>@github.com/new?name=hello2&owner=pierrejean&visibility=private

    More stuff

    After train with Oh-my-git, you should use a more advanced tool to work with Git as GitHub Desktop, Git Kraken, IDE plugin for git, but it is important to know what GIT commands do.

    But if you want to display into the terminal as close as Oh my git, you could use this command :

    git log --oneline --graph --all --decorate

    To output branches, commits, commits messages in parentheses, tags (here all version vX ), etc.

    * 2a6cf7f (deadend) v6
    * 06c61e4 (HEAD -> main) v5
    | * b8b1318 (alternative) v4
    |/
    *   1fe4f31 v3
    |\
    | * 7dc37f1 (first) v2
    * | 58d7bb0 v2.a
    |/
    * 598c50e v1

    To avoid to all the time call git log with all options, you can create a Windows Powershell function to call easily :

    function gitlog { git log --oneline --graph --all --decorate}

    Create an alias with Linux and MacOS :

    alias gitlog="git log --oneline --graph --all --decorate"

    In cas of need into a historical cmd/dos version, create a gitlog.bat file into a PATH registered folder as c:\Program Files\Git\cmd\ then add the content :

    git log --oneline --graph --all --decorate

    Two more stuff

    You can add one file or one folder with git add . or git add README.md or you can :

    git add :/

    This will add all file from the top folder where you enter git init command.

    And more important, two commands to compare difference on one file, suppose one file equipe1.txt file, one version is :

    Equipe1
    ********

    The other version is :

    Equipe1
    ------

    The classical conflict will show :

    Equipe1
    <<<<<<< HEAD
    **********
    =======
    -------
    >>>>>>> 50b69dcb7523fbe3e051e695ae5f3ffb21068013

    To show difference on the HEAD and the 50b69dc, I can :

    git diff HEAD 50b69dc .\equipe1.txt

    to show difference :

    diff --git a/equipe1.txt b/equipe1.txt
    index c62584c..8856277 100644
    --- a/equipe1.txt
    +++ b/equipe1.txt
    @@ -1,2 +1,2 @@
     Equipe1
    -**********
    +-------

    Or we can use a word difference on the same line to display more nicely :

    git diff --word-diff=color HEAD 50b69dc .\equipe1.txt

    Then on the same line difference are visible :

    diff --git a/equipe1.txt b/equipe1.txt
    index c62584c..8856277 100644
    --- a/equipe1.txt
    +++ b/equipe1.txt
    @@ -1,2 +1,2 @@
     Equipe1
    **********-------

    We can see the difference more easily, but a better way is to show in parallel :

     git difftool -y -x sdiff HEAD 50b69dc .\equipe1.txt

    To display just side by side difference :

    Equipe1                      Equipe1
    **********                  | -------

    Evolution of git with git switch

    The initial git checkout command covers so many different operations that git try to create new commands that cover some as an alternative. This link provides some corresponding conversion: https://stackoverflow.com/questions/57265785/whats-the-difference-between-git-switch-and-git-checkout-branch

    Mainly, you can understand :

    git initial commandnew git alternative command
    git branch new_branch && git checkout new_branchgit switch –create new_branch
    git checkout — <file>git restore — <file>
    Publié dans Blog, Développement | Laisser un commentaire

    Master m-Rehab

    Cliquer pour télécharger la présentation du cours

    Installation des logiciels :

    NOTE Mysql installation avec Windows:

    • Windows 64-bit, ZIP Archive puis ne pas « créer de compte » juste « No thanks, just start my download. »
    • Installation si nécessaire de « Redistribuable Visual C++ pour Visual Studio 2012 » fichiers vcredist_x64.exe et vcredist_x86.exe depuis https://www.microsoft.com/fr-fr/download/details.aspx?id=30679
    • En cas de soucis, possibilité d’installer MAMP : https://www.mamp.info/en/downloads/

    Accès aux fichiers pour TP

    Liens vers le fichier SQL ci-dessous pour importation dans une base de données :


    Accès à la base de données depuis R

    install.packages("RMySQL")
    library(RMySQL)

    L’accès à un serveur de base de données Mysql suppose plusieurs informations techniques, l’adresse du serveur de base de données (ici 127.0.0.1), un compte sur cette base de données (ici root), un mot de passe correspondant à ce compte (ici «  » pour indiquer mot de passe vide), un port de base de données (ici et par défaut 3306 mais sur Mamp cela peut être 8889) et enfin une base de données (ici sante)

    DB <- dbConnect(MySQL(), user="root", host="127.0.0.1", password="",dbname="sante" , port=3306)

    Une fois la connexion réalisée, on peut lister les tables dans cette base de données sante.

    dbListTables(DB)

    Et enfin poser des questions en SQL

    resultat <- dbGetQuery(DB, "SELECT * FROM data")
    print( resultat )

    Importation en CSV

    La première étape est de trouver le dossier dans lequel Mysql accepte l’importation d’un fichier CSV, la requête SQL est la suivante :

    SHOW VARIABLES LIKE "secure_file_priv"
    Dossier Mysql autorisé pour importation de fichier CSV

    Dans mon cas le dossier est dans c:\Developpement\Wamp64\tmp\ du coup je peux placer mon fichier analyse.csv dans ce dossier et lancé cette requête SQL après avoir créé la table analyse :

    LOAD DATA INFILE 'c:/Developpement/wamp64/tmp/analyse.csv' 
    INTO TABLE analyse 
    FIELDS TERMINATED BY ',' 
    ENCLOSED BY '"'
    LINES TERMINATED BY '\r\n'
    IGNORE 1 ROWS;

    Publié dans Médecine | Laisser un commentaire

    WordPress nouveau bloc version simple

    La constitution d’un nouveau bloc permet de rapidement produire un contenu répétable pour inclure par exemple une signature automatiquement disponible ou même un short code.

    Dans le dossier wp-content\plugins\, en premier lieu, il faut créer un dossier, par exemple block-twitter avec la liste des fichiers suivants :

    • block.asset.php
    • block.js
    • block.json
    • index.php

    Le premier fichier block.asset.php est standard avec ce code :

    <?php return
        array( 'dependencies' =>
            array(
                'wp-blocks',
                'wp-element',
                'wp-polyfill'
            ),
            'version' => '0.1'
        );

    Le second fichier block.js contient le JavaScript pour présenter le contenu du bloc côté front et côté backoffice (côté éditeur). On note dans cette première version que le bloc ne fait que présenter un message côté backoffice légèrement différent de la version frontoffice :

    ( function ( blocks, element ) {
        var el = element.createElement;
    
        blocks.registerBlockType( 'block-twitter/basic', {
            edit: function () {
                return el( 'p', {}, 'Signature du Master CTN' );
            },
            save: function () {
                return el( 'p', {}, 'Suivez le Master CTN sur Twitter http://twitter.com/MasterCTN' );
            },
        } );
    } )( window.wp.blocks, window.wp.element );

    Par la suite, nous mettrons un short code pour déclencher l’appel à un programme :

    ( function ( blocks, element ) {
        var el = element.createElement;
    
        blocks.registerBlockType( 'block-twitter/basic', {
            edit: function () {
                return el( 'p', {}, '[twitter_short_code_plugin]' );
            },
            save: function () {
                return el( 'p', {}, '[twitter_short_code_plugin]' );
            },
        } );
    } )( window.wp.blocks, window.wp.element );

    Le troisième fichier block.json contient la description des informations du bloc notamment son titre, pour le chercher dans la liste des blocs noter la valeur de « name » qui a été utilisée dans le fichier block.js :

    {
        "apiVersion": 2,
        "title": "Block Twitter",
        "name": "block-twitter/basic",
        "category": "layout",
        "icon": "universal-access-alt",
        "editorScript": "file:./block.js"
    }
    

    Le quatrième fichier est index.php qui permet de fabriquer un plugin dont l’activation dans la liste des plugins de WordPress permettra de lier notre nouveau bloc avec l’éditeur de WordPress :

    <?php
    /**
     * Plugin Name: Block Twitter
     */
    
    function block_twitter_register_block() {
        register_block_type( __DIR__ );
    }
    add_action( 'init', 'block_twitter_register_block' );

    Pour faire la gestion du short code, ce fichier contiendra ensuite la gestion du code appelé par ce short code, ici juste un simple message :

    <?php
    /**
     * Plugin Name: Block Twitter
     */
    
    
    function twitter_short_code_plugin(){
        return "<p>Le compte Twitter</p>";
    }
    
    add_shortcode('twitter_short_code_plugin', 'twitter_short_code_plugin');
    
    
    function block_twitter_register_block() {
        register_block_type( __DIR__ );
    }
    add_action( 'init', 'block_twitter_register_block' );

    Après avoir activé le plugin dans le backoffice de WordPress :

    Plugin avant activation pour ajouter le nouveau bloc

    Nous pouvons disposer de ce bloc dans la liste des blocs disponibles pour l’utiliser :

    Bloc Twitter disponible pour ajouter du contenu

    Finalement, avec la solution de gestion du short code on obtient ce rendu dans l’éditeur :

    Ajout du short code dans la liste des blocs de la page d’exemple

    Et le rendu côté front office remplace le short code par le message prévu entouré en vers juste pour rendre la zone plus visible :

    Rendu du short code sur la page d’exemple côté frontoffice

    Maintenant, il faut essayer d’ajouter la fonctionnalité de rendu dans l’éditeur du backoffice de WordPress plutôt que d’avoir le short code.

    En premier lieu, modifions le fichier block.js qui doit faire un appel au serveur pour afficher le résultat d’un appel « ServerSide » et non plus le message (l’ancien code est en commentaire avec // ):

            edit: function () {
                // return el( 'p', {}, '[twitter_short_code_plugin]' );
                return wp.element.createElement(
                    wp.serverSideRender,
                    {
                        block: 'block-twitter/editeur'
                    }
                );
    
    
            },

    Dans le fichier index.php il faut que la fonction block_twitter_register_block contiennent l’action à faire suite à cet appel « ServerSide » :

    function block_twitter_register_block() {
        register_block_type( __DIR__ );
        register_block_type(
            'block-twitter/editeur',
            [
                'render_callback' => 'block_twitter_render_callback',
            ]
        );
    }

    On enregistre un appel à la fonction block_twitter_render_callback quand une URL de cette forme sera émise pour afficher le bloc dans l’éditeur http://<serveur web wordpress>/wp-json/wp/v2/block-renderer/block-twitter/editeur?context=edit&post_id=2&_locale=user .

    La fonction block_twitter_render_callback est à ajouter dans le fichier index.php pour permettre quand un bloc est affiché d’avoir le rendu au lieu du shortcode :

    function block_twitter_render_callback( $atts ) {
     
        return "<p>Compte TWITTER visible Editor</p>";
    }
    Rendu du bloc dans l’éditeur dans le back office de WordPress

    On peut voir qu’au lieu du short code, le HTML prévu dans la fonction block_twitter_render_callback est affiché. Le passage en HTML affiche le short code pour bien rappeler le fonctionnement par défaut de bloc.

    Publié dans CTN | Laisser un commentaire

    Protégé : Accès serveur calcul CERIS

    Cette publication est protégée par un mot de passe. Pour la voir, veuillez saisir votre mot de passe ci-dessous :

    Publié dans Développement | Saisissez votre mot de passe pour accéder aux commentaires.