Compare commits
30 Commits
324f310e38
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ce835fb93c | |||
| 88af050bea | |||
| 2373233899 | |||
| 8239d8024d | |||
| 1586d02094 | |||
| 2fe4bab887 | |||
| eb792edb4d | |||
| 1a450491c6 | |||
| a472e34bbe | |||
| 0490924e3d | |||
| db7896d2de | |||
| bd2243e28a | |||
| 1c7d7f636a | |||
| 1fe20f3f3b | |||
| 7f4884a90d | |||
| 532f73309c | |||
| 129a0c321c | |||
| ef4ddb1420 | |||
| da6a720f46 | |||
| 6d7a783d73 | |||
| 6cb1a005fd | |||
| d99fa137ec | |||
| 534fc8ace1 | |||
| 783e91969c | |||
| 226c55aca3 | |||
| 755c3d4e2e | |||
| c875a397ad | |||
| 5034bf1248 | |||
| 322c2a7e7f | |||
| 7aa9c51104 |
BIN
R1.02/cours/js.pdf
Normal file
43
R3.01/README.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Programmation WEB coté serveur R3.01
|
||||||
|
Ce dépôt contient les ressources utilisées
|
||||||
|
(cours/td/tp) dans la ressource R3.01.
|
||||||
|
|
||||||
|
**Les objectifs**
|
||||||
|
|
||||||
|
> Savoir développer une application Web côté serveur, en utilisant le langage PHP.
|
||||||
|
|
||||||
|
Les notions suivantes seront abordées :
|
||||||
|
|
||||||
|
- Le langage php
|
||||||
|
On se limitera aux bases du langages. La couche objet sera présenté très succintement.
|
||||||
|
Toute la suite sera illustré avec PHP.
|
||||||
|
|
||||||
|
- Interaction avec le client
|
||||||
|
URL (Uniform Resource Locator), requêtes, formulaires, transmission des paramètres, des données, etc.
|
||||||
|
|
||||||
|
- Applications Web à états
|
||||||
|
Cookies et sessions
|
||||||
|
|
||||||
|
- Organisation de l’accès aux données
|
||||||
|
Bases de données, annuaires, services Web, etc.
|
||||||
|
|
||||||
|
- Introduction à la programmation objet en PHP
|
||||||
|
Introduction au principe MVC
|
||||||
|
|
||||||
|
- Sensibilisation à la sécurité des applications web
|
||||||
|
|
||||||
|
## Calendrier
|
||||||
|
|
||||||
|
| Semaine | Cours | TD/TP |
|
||||||
|
| ------------------ | -------------------------------------------------------- | ------------------ |
|
||||||
|
| 1 : 16/03 - 20/03| [Bases du langages](./cours/cm_bases_php.pdf) | |
|
||||||
|
|
||||||
|
## Les TPS
|
||||||
|
|
||||||
|
#### TP1 : Bases du langage PHP
|
||||||
|
Le [tp1](./tp/tp1)
|
||||||
|
permet de se familiariser avec le langage PHP.
|
||||||
|
À chaque exercice correspond un sous répertoire avec
|
||||||
|
des fichiers à compléter.
|
||||||
|
|
||||||
|
|
||||||
BIN
R3.01/cours/cm_bases_php.pdf
Normal file
@@ -6,4 +6,17 @@
|
|||||||
#### Semaine 2
|
#### Semaine 2
|
||||||
[DOM](cours/dom.pdf), [tp2](./td_tp/tp2), [tp2mvc](td_tp/tp2mvc)
|
[DOM](cours/dom.pdf), [tp2](./td_tp/tp2), [tp2mvc](td_tp/tp2mvc)
|
||||||
|
|
||||||
|
#### Semaine 3
|
||||||
|
[DOM](cours/dom.pdf), [tp3](./td_tp/tp3)
|
||||||
|
|
||||||
|
#### Semaine 4
|
||||||
|
[Promesses, Ajax, API de données](cours/ajax.pdf), [tp4](./td_tp/tp4)
|
||||||
|
|
||||||
|
#### Semaine 5
|
||||||
|
[Programmation déclarative, RIOT.js](cours/riot.pdf), [tp5](./td_tp/tp5)
|
||||||
|
#### Semaine 6
|
||||||
|
[API REST](cours/api.pdf), service firebase [tp6](./td_tp/tp6)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
R4.01_R4.A.10/cours/ajax.pdf
Normal file
BIN
R4.01_R4.A.10/cours/api.pdf
Normal file
BIN
R4.01_R4.A.10/cours/riot.pdf
Normal file
54
R4.01_R4.A.10/miniprojet/README.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
## Thème
|
||||||
|
|
||||||
|
Écrire une application web en javascript qui permet de consulter les données de
|
||||||
|
parcours sup (vœux de poursuite d'études et de réorientation dans l'enseignement
|
||||||
|
supérieur et réponses des établissements) afin d'aider un(e) lycéén(e) à faire ses choix.
|
||||||
|
|
||||||
|
|
||||||
|
- La partie cliente devra être réaliser au moyen du framework [riotjs](https://riot.js.org/).
|
||||||
|
- Les données seront consommées au moyen de [l'api](https://data.enseignementsup-recherche.gouv.fr/explore/dataset/fr-esr-parcoursup/api/?timezone=Europe%2FBerlin&sort=tri) opendata. (cf [méthodologie](https://data.enseignementsup-recherche.gouv.fr/api/datasets/1.0/fr-esr-parcoursup/attachments/methodologie_opendata_2025pdf/))
|
||||||
|
- La localisation utilisera openstreet map et l'api [Leaflet](https://leafletjs.com/).
|
||||||
|
- Les graphiques seront réalisés au moyen de [chart css](https://chartscss.org/).
|
||||||
|
|
||||||
|
## Fonctionnalités
|
||||||
|
#### Recherche des formations par mots clés, avec localisation sur une carte.
|
||||||
|
|
||||||
|
[<a href="img/search.png">exemple</a>]
|
||||||
|
#### Vue d'une formation particulière, avec accès aux données de parcourssup, entre autres :
|
||||||
|
- les données lors de la phase d'admission,
|
||||||
|
- les données lors de la phase complémentaire,
|
||||||
|
- le profils des admis,
|
||||||
|
- l'évolution de la selectivité, des mentions au bac depuis 2020.
|
||||||
|
|
||||||
|
[<a href="img/formation.png">exemple</a>, <a href="img/profil.png">exemple</a>,
|
||||||
|
<a href="img/profil1.png">exemple</a>]
|
||||||
|
|
||||||
|
#### Aide au choix d'orientation
|
||||||
|
|
||||||
|
L'utilisateur doit pouvoir selectionner des formations/filières afin de les comparer et estimer
|
||||||
|
ses chances d'intégrations en fonction de la série de son bac et de sa moyenne en terminale.
|
||||||
|
|
||||||
|
[<a href="img/selection1.png">exemple</a>
|
||||||
|
, <a href="img/selection2.png">exemple</a>]
|
||||||
|
|
||||||
|
Les choix de l'utilisateur devront au moins être persistant localement. Vous pouvez, losque tout fonctionne,
|
||||||
|
mettre en oeuvre une persistance avec [firebase](https://firebase.com).
|
||||||
|
|
||||||
|
## Réalisation
|
||||||
|
1. Une partie de votre travail consiste à comprendre comment fonctionne l'api (entrées/sorties), et comment l'utiliser. N'hésitez pas à encapsuler/abstraire les informations de l'api dans votre propre modèle.
|
||||||
|
2. Le but est d'utiliser la programmation déclarative et par composants de RIOT.js. Il faut donc l'architecturer et l'organiser en
|
||||||
|
conséquence. En particulier, il faut profiter pleinement de l'utilisation de composants le plus génériques possibles.
|
||||||
|
3. Il faut utiliser des urls routables. Riot.js vient avec un [router](https://github.com/riot/route) très simple d'utilisation.
|
||||||
|
Je vous expliquerai comment mettre en oeuvre la réécriture d'url avec apache.
|
||||||
|
4. Il est possible d'utiliser le pattern observable, avec une bibliothèque adhoc ([exemple](https://github.com/riot/observable))
|
||||||
|
5. Dans le depot GIT de votre projet, il faut une description des composants, leurs entrées, leurs fonctions, et comment
|
||||||
|
ils communiquent avec le reste du monde.
|
||||||
|
|
||||||
|
## Attendus
|
||||||
|
Un mail (sujet S4WEB) m'indiquant les informations suivantes :
|
||||||
|
|
||||||
|
- l'url de votre projet sous la forme : http://dwarves.iut-fbleau.fr/~login/parcoursup
|
||||||
|
- l'url du dépôt GIT avec vos sources sur dwarves.iut-fbleau.fr
|
||||||
|
- les noms du binôme.
|
||||||
|
|
||||||
|
Le tout est à finaliser avant le Vendredi 03 avril 2025, 18 heures. Une soutenance aura lieu la semaine du 30/03.
|
||||||
17
R4.01_R4.A.10/miniprojet/groupes.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
## Groupes projet
|
||||||
|
- Rayan Lankechari / Ibrahima Bah
|
||||||
|
- Demrici-Özmen Canpolat / Eliot Maxime
|
||||||
|
- Ozvann ABRAHAM / Karl FRAMERY
|
||||||
|
- SOLAR Dimitri / ZAABAY Tarehi
|
||||||
|
- Nicolas Miekisiak / Loïc Sainton
|
||||||
|
- Torreblanca Mathys / Poitou Enzo / Haffa BAKHOUCHE
|
||||||
|
- WIECZOREK Valentin / PLOUVIER Luka
|
||||||
|
- Kouami Kpeglo / Alexis Fort
|
||||||
|
- Ugo Faye / Mathis Leclere
|
||||||
|
- Nathan Couchot / Théo Anastasio / Adrien Rabot
|
||||||
|
- Pierre COURTEHOUX / Lukas SIMOES
|
||||||
|
- CAMILLE Cléo / Warrhen Roland
|
||||||
|
- Aylane Sehl / Séri-Khane / ??
|
||||||
|
- Arwa Ben Fraj / Bounni Loubelo Benicia
|
||||||
|
- Hicham Cherifi / Wael Atik
|
||||||
|
- DERQSI BILAL / CAMILLE LECHAVLIER
|
||||||
BIN
R4.01_R4.A.10/miniprojet/img/formation.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/profil.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/profil1.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/search.png
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/selection1.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/selection2.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
100
R4.01_R4.A.10/td_tp/tp4/README.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# TP javascript : Ajax, promesses.
|
||||||
|
|
||||||
|
Vous disposez tous, à la racine de votre compte, d'un répertoire `public_html` qui est servi (http/https) par le serveur dwarves.
|
||||||
|
Pour accéder à vos pages, utilisez l'url :
|
||||||
|
|
||||||
|
> http(s)://dwarves.[arda|iut-fbleau.fr]/~login
|
||||||
|
|
||||||
|
Pour tester les différentes api http, vous pouvez utilisez la commande `curl`.
|
||||||
|
|
||||||
|
|
||||||
|
#### Ex1
|
||||||
|
Le but est de faire une recherche de film en utilisant l'api [OMDb](https://www.omdbapi.com/) (Open Movie Database).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Son utilisation nécessite une clé. Vous pouvez utiliser la mienne `2fcb2848`. Si
|
||||||
|
la limite de requêtes est atteinte, générez votre propre clé.
|
||||||
|
|
||||||
|
|
||||||
|
Complétez le [code](./src/ex1).
|
||||||
|
- J'ai volontairement utilisé des modules. Il vous faut donc tester l'application avec
|
||||||
|
http.
|
||||||
|
|
||||||
|
- Écrire la fonction du modèle
|
||||||
|
```js
|
||||||
|
getMovies(search)
|
||||||
|
```
|
||||||
|
Cette fonction renvoie une promesse qui permet de faire une requête ajax vers l'api OMDb. Pour ajax,
|
||||||
|
utilisez d'abord l'interface `fetch`, ~~puis l'objet `XMLHttpRequest`~~.
|
||||||
|
|
||||||
|
- Écrire la fonction du contrôleur
|
||||||
|
```js
|
||||||
|
async search(searchMovie)
|
||||||
|
```
|
||||||
|
qui permet de faire une recherche. Cette fonction utilise évidemment la fonction précédente `getMovies`.
|
||||||
|
|
||||||
|
- Écrire la fonction de la vue
|
||||||
|
```js
|
||||||
|
renderList(movies)
|
||||||
|
```
|
||||||
|
qui construit la liste des films affichés. La structure attendue est
|
||||||
|
```html
|
||||||
|
<li>
|
||||||
|
<a href="http://www.imdb.com/title/tt0068646" target="_blank">The Godfather</a><span>1972</span>
|
||||||
|
</li>
|
||||||
|
```
|
||||||
|
#### Ex2
|
||||||
|
Le but, dans un premier temps, est d'écrire une interface, avec ajax, qui permette de jouer au jeu suivant :
|
||||||
|
|
||||||
|
- On a une grille dans laquelle sont cachés des renards (on en connait le nombre initial).
|
||||||
|
|
||||||
|
- A chaque tour, on choisit de tirer sur un case. On a l'alternative suivante :
|
||||||
|
- on tue un renard,
|
||||||
|
- on récupére les nombres de renards sur la grille sur la ligne, colonne et diagonales de la case.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="img/renard1.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Le jeu utilise un serveur http à l'url suivante
|
||||||
|
|
||||||
|
```
|
||||||
|
https://dwarves.iut-fbleau.fr/foxes/foxes.php
|
||||||
|
```
|
||||||
|
|
||||||
|
Voici les urls du jeu :
|
||||||
|
|
||||||
|
```
|
||||||
|
https://dwarves.iut-fbleau.fr/foxes/foxes.php?new&size=10&foxes=10
|
||||||
|
```
|
||||||
|
|
||||||
|
initialise la partie , avec la taille de la grille et le nombre de renards
|
||||||
|
dont la position est aléatoire (le script utilise des sessions de cinq minutes).
|
||||||
|
|
||||||
|
La réponse normale, en json :
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"status":"ok","foxes":10,"tries":0}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
https://dwarves.iut-fbleau.fr/foxes/foxes.php?X=1&Y=3
|
||||||
|
```
|
||||||
|
|
||||||
|
permet de tirer sur une case. Le serveur répond
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"status":"ok","foxes":2,"tries":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
`foxes` vaut -1 si le coup est gagnant, sinon donne le nombre de renards de la colonne, ligne et diagonales de la case.
|
||||||
|
|
||||||
|
|
||||||
|
En cas de victoire, le status est `win`. En cas d'erreur, le status est `nok`.
|
||||||
|
|
||||||
|
|
||||||
|
**Remarque**
|
||||||
|
|
||||||
|
Ajouter l'option `credentials` à vos requêtes pour utiliser la session en cours.
|
||||||
35
R4.01_R4.A.10/td_tp/tp4/aide.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#### XMLHttpRequest
|
||||||
|
```js
|
||||||
|
let xhr = new XMLHttpRequest()
|
||||||
|
xhr.open("GET","mon/url")
|
||||||
|
|
||||||
|
xhr.onload = ...
|
||||||
|
xhr.onerror = ...
|
||||||
|
|
||||||
|
xhr.send()
|
||||||
|
```
|
||||||
|
#### Promise
|
||||||
|
```js
|
||||||
|
|
||||||
|
let promise = new Promise ((resolve,reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve("OK")
|
||||||
|
},1000)
|
||||||
|
})
|
||||||
|
promise.then(data => ....)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### async/await
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
function getUser(email){
|
||||||
|
return new Promise(...)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateUser(){
|
||||||
|
|
||||||
|
let user = await getUser(...)
|
||||||
|
console.log(user)
|
||||||
|
}
|
||||||
|
```
|
||||||
BIN
R4.01_R4.A.10/td_tp/tp4/img/renard1.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
R4.01_R4.A.10/td_tp/tp4/img/search.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
86
R4.01_R4.A.10/td_tp/tp4/src/ex1/css/style.css
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
main {
|
||||||
|
position: absolute;
|
||||||
|
top: 2rem;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
}
|
||||||
|
.loader {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
margin: 6rem 0 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #FFFAAA;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
label span {
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin: 1rem 0 0;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: 300;
|
||||||
|
padding: 0.8rem 1rem;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-shadow: 1px 1px 2px rgba(0,0,0, 0.3);
|
||||||
|
-moz-appearance:none;
|
||||||
|
-webkit-appearance:none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
input:focus {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
margin: 1rem 0 2rem;
|
||||||
|
}
|
||||||
|
ul li {
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
margin: 1px 0;
|
||||||
|
line-height: 1.4rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
align-content: space-between;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
box-shadow: 0 0 2px rgba(0,0,0, 0.3);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
ul li:hover,
|
||||||
|
ul li:active,
|
||||||
|
ul li:focus {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
ul li a {
|
||||||
|
margin: 0 0.6rem 0 0;
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
ul li span {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Helvetica Neue', Helvetica, Arial;
|
||||||
|
font-weight: 300;
|
||||||
|
background-size: cover;
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-image: -webkit-radial-gradient(ellipse farthest-corner at top, #661141, #000000);
|
||||||
|
background-image: radial-gradient(ellipse farthest-corner at top, #661141, #000000);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
13
R4.01_R4.A.10/td_tp/tp4/src/ex1/img/puff.svg
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
|
||||||
|
<svg width="44" height="44" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke-width="2">
|
||||||
|
<circle cx="22" cy="22" r="1">
|
||||||
|
<animate attributeName="r" begin="0s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="stroke-opacity" begin="0s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
<circle cx="22" cy="22" r="1">
|
||||||
|
<animate attributeName="r" begin="-0.9s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="stroke-opacity" begin="-0.9s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
28
R4.01_R4.A.10/td_tp/tp4/src/ex1/index.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="initial-scale=1,width=device-width">
|
||||||
|
<link rel="stylesheet" type="text/css" href="./css/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- vue html -->
|
||||||
|
<main>
|
||||||
|
<label>
|
||||||
|
<span>Search a movie</span>
|
||||||
|
<input type="input" placeholder="28 Days Later..">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="loader">
|
||||||
|
<img src="./img/puff.svg">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="error"></p>
|
||||||
|
<div id="list-movies">
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
<script type="module" src="./module/app.js"></script>
|
||||||
|
</html>
|
||||||
7
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/app.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
import model from '../module/model.js'
|
||||||
|
import Controller from '../module/controller.js'
|
||||||
|
import View from '../module/view.js'
|
||||||
|
|
||||||
|
const view = new View()
|
||||||
|
const app = new Controller(view,model)
|
||||||
26
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/controller.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
class Controller {
|
||||||
|
constructor(view,model){
|
||||||
|
this.view = view
|
||||||
|
this.model = model
|
||||||
|
|
||||||
|
this.loading = false
|
||||||
|
this.lastSearch = null
|
||||||
|
this.error = null
|
||||||
|
this.results = []
|
||||||
|
|
||||||
|
this.view.setLoading(false)
|
||||||
|
this.view.bindSearch(this.search.bind(this))
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
this.loading = false
|
||||||
|
this.error = null
|
||||||
|
this.results = []
|
||||||
|
}
|
||||||
|
|
||||||
|
async search(searchMovie) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Controller
|
||||||
|
|
||||||
12
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/helpers.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Returns a function, that, as long as it continues to be invoked, will not
|
||||||
|
// be triggered. The function will be called after it stops being called for
|
||||||
|
// N milliseconds.
|
||||||
|
function debounce(fn, wait) {
|
||||||
|
let timeout
|
||||||
|
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = setTimeout(() => fn(...args), wait)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default debounce
|
||||||
9
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/model.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
let apiKey = '2fcb2848'
|
||||||
|
|
||||||
|
let model = {
|
||||||
|
getMovies(search){
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default model
|
||||||
44
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/view.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import debounce from "./helpers.js"
|
||||||
|
|
||||||
|
class View {
|
||||||
|
constructor(){
|
||||||
|
this.listMovies = document.querySelector("#list-movies")
|
||||||
|
this.inputSearch = document.querySelector("input")
|
||||||
|
this.loader = document.querySelector(".loader")
|
||||||
|
this.message = document.querySelector("p.error")
|
||||||
|
}
|
||||||
|
|
||||||
|
#getInput(){
|
||||||
|
return this.inputSearch.value
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(loading){
|
||||||
|
if (loading)
|
||||||
|
this.loader.style.display = "block"
|
||||||
|
else
|
||||||
|
this.loader.style.display = "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMessage(error){
|
||||||
|
this.message.style.display = "block"
|
||||||
|
this.message.textContent = error
|
||||||
|
}
|
||||||
|
|
||||||
|
renderList(movies){
|
||||||
|
let ul = document.createElement("ul")
|
||||||
|
|
||||||
|
movies.forEach((movie)=>{
|
||||||
|
// TODO
|
||||||
|
})
|
||||||
|
|
||||||
|
this.listMovies.replaceChildren(ul)
|
||||||
|
}
|
||||||
|
|
||||||
|
bindSearch(handler){
|
||||||
|
this.inputSearch.addEventListener("input",debounce((e)=>{
|
||||||
|
handler(this.#getInput())
|
||||||
|
},500))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default View
|
||||||
36
R4.01_R4.A.10/td_tp/tp4/src/ex2/app.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import loader from './loader.js';
|
||||||
|
const board = document.querySelector("div.board");
|
||||||
|
const form = document.querySelector("form");
|
||||||
|
const message = document.querySelector("#message");
|
||||||
|
const ld = new loader(document.getElementById("loader"));
|
||||||
|
|
||||||
|
let url = 'https://dwarves.iut-fbleau.fr/foxes/foxes.php';
|
||||||
|
|
||||||
|
let foxes;
|
||||||
|
let size;
|
||||||
|
|
||||||
|
let newBoard = ( (size ) => {
|
||||||
|
let frag = document.createDocumentFragment();
|
||||||
|
for (let i = 0; i< size; i++){
|
||||||
|
let div = document.createElement("div");
|
||||||
|
for(let j = 0; j < size; j++){
|
||||||
|
let span = document.createElement("span");
|
||||||
|
span.dataset.num = size*i + j;
|
||||||
|
div.appendChild(span);
|
||||||
|
}
|
||||||
|
frag.appendChild(div);
|
||||||
|
}
|
||||||
|
board.replaceChildren(frag);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
form.addEventListener("submit", (async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
// TODO
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
board.addEventListener("click", ( async (ev) => {
|
||||||
|
// TODO
|
||||||
|
}))
|
||||||
54
R4.01_R4.A.10/td_tp/tp4/src/ex2/css/style.css
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
article {
|
||||||
|
text-align : center;
|
||||||
|
}
|
||||||
|
.board > div {
|
||||||
|
display : flex;
|
||||||
|
}
|
||||||
|
article .board {
|
||||||
|
display : inline-block;
|
||||||
|
}
|
||||||
|
.board span {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align:baseline;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
/*padding : 1em;*/
|
||||||
|
margin : 0.1em;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #444;
|
||||||
|
/*background: radial-gradient(rgb(150,250,150), #00ee00);*/
|
||||||
|
}
|
||||||
|
.board span:hover {
|
||||||
|
background: #3AB903;
|
||||||
|
background: radial-gradient(rgb(66, 255, 66), #065006);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board span.on {
|
||||||
|
background: #009900;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.is-loading {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 999;
|
||||||
|
overflow: show;
|
||||||
|
margin: auto;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
div.is-loading:after {
|
||||||
|
animation: spinAround 500ms infinite linear;
|
||||||
|
border: 2px solid hsl(0deg, 0%, 86%);
|
||||||
|
border-radius: 9999px;
|
||||||
|
border-right-color: transparent;
|
||||||
|
border-top-color: transparent;
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
top: calc(50% - 5em);
|
||||||
|
left: calc(50% - 5em);
|
||||||
|
width: 10em;
|
||||||
|
height: 10em;
|
||||||
|
border-width: 0.25em;
|
||||||
|
}
|
||||||
35
R4.01_R4.A.10/td_tp/tp4/src/ex2/foxes.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"
|
||||||
|
>
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div id="loader" ></div>
|
||||||
|
|
||||||
|
<hgroup>
|
||||||
|
<h4>Foxes</h4>
|
||||||
|
<p>Le but est de tuer tous les renards en minimisant le nombre de coups joués.</p>
|
||||||
|
</hgroup>
|
||||||
|
<p id="message"></p>
|
||||||
|
<article>
|
||||||
|
<div class="board">
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<form>
|
||||||
|
<fieldset role="group">
|
||||||
|
<input type="text" name="size" placeholder="Size">
|
||||||
|
<input type="text" name="foxes" placeholder="Foxes">
|
||||||
|
<input type="submit" value="New Game">
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
<script type="module" src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
12
R4.01_R4.A.10/td_tp/tp4/src/ex2/loader.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class loader {
|
||||||
|
constructor(node){
|
||||||
|
this.node = node
|
||||||
|
}
|
||||||
|
set(loading){
|
||||||
|
if (loading)
|
||||||
|
this.node.classList.add("is-loading")
|
||||||
|
else
|
||||||
|
this.node.classList.remove("is-loading")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default loader
|
||||||
39
R4.01_R4.A.10/td_tp/tp5/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# TP javascript : riot.js
|
||||||
|
|
||||||
|
La [documentation](https://riot.js.org/) de riot.js
|
||||||
|
|
||||||
|
#### Ex1
|
||||||
|
Voici un [exemple](./src/ex1) d'application (todo list) écrite avec riot.js.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="./img/todo.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Le but est de comprendre le code pour prendre en main Riot.js
|
||||||
|
|
||||||
|
- Complétez la fonction `add` qui permet d'ajouter une tâche.
|
||||||
|
- Faites en sorte que le bouton `clear done` soit desactivé quand il n'y a pas de tâches accomplies.
|
||||||
|
- Complétez la fonction `clear` qui retire les tâches accomplies.
|
||||||
|
- Mettez en oeuvre les filtres `All`, `Active` et `Done`.
|
||||||
|
- Complétez le code qui permet de supprimer une tâche.
|
||||||
|
- Ajoutez le nombre de tâches restantes.
|
||||||
|
- Modifiez le code pour que les tâches soient stockées localement (utilisez l'objet `localStorage`).
|
||||||
|
|
||||||
|
#### Ex2
|
||||||
|
Voici un [exemple](./src/ex2) d'application (radar chart) écrite avec riot.js.
|
||||||
|
<div align="center">
|
||||||
|
<img src="./img/radar.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- Complétez les méthodes `add` et `remove`.
|
||||||
|
- Créez un nouveau composant `slider.riot`, qui vous utiliserez dans app.riot
|
||||||
|
|
||||||
|
#### Ex3
|
||||||
|
L'api DummyJSON permet de manipuler des données fictives. Ecrire, avec riotjs, une application qui permet :
|
||||||
|
|
||||||
|
- de récuperer depuis l'url `https://dummyjson.com/products` une liste de produits
|
||||||
|
- pour chaque produit, afficher le titre, prix et image.
|
||||||
|
- ajouter un filtre `prix < 100`, et un bouton `voir détails`.
|
||||||
|
|
||||||
BIN
R4.01_R4.A.10/td_tp/tp5/img/radar.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
R4.01_R4.A.10/td_tp/tp5/img/todo.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
28
R4.01_R4.A.10/td_tp/tp5/src/ex1/index.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Riot todo</title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<link rel="stylesheet" href="todo.css">
|
||||||
|
<script src="todo.riot" type="riot"></script>
|
||||||
|
<script src="https://unpkg.com/riot@10.1.2/riot+compiler.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<todo />
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
riot.compile().then(() => {
|
||||||
|
riot.mount('todo', {
|
||||||
|
title: 'I want to behave!',
|
||||||
|
todos: [
|
||||||
|
{ title: 'Avoid excessive caffeine', done: false ,id : 0},
|
||||||
|
{ title: 'Be less provocative', done: true , id:1},
|
||||||
|
{ title: 'Be nice to people', done: true, id:2}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
112
R4.01_R4.A.10/td_tp/tp5/src/ex1/todo.css
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'myriad pro', sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
todo {
|
||||||
|
display: block;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 5% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input {
|
||||||
|
font-size: 85%;
|
||||||
|
padding: .4em;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #1FADC5;
|
||||||
|
border: 1px solid rgba(0,0,0,.2);
|
||||||
|
font-size: 75%;
|
||||||
|
color: #fff;
|
||||||
|
padding: .4em 1.2em;
|
||||||
|
border-radius: 2em;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0 .23em;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[disabled] {
|
||||||
|
background-color: #ddd;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: .2em 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.completed {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.filters {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
/* position: absolute;*/
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a,.filters li span {
|
||||||
|
color: inherit;
|
||||||
|
/* margin: 3px;*/
|
||||||
|
margin-right:10px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size : 0.75em;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.filters li a:hover {
|
||||||
|
border-color: rgba(175, 47, 47, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected {
|
||||||
|
border-color: rgba(175, 47, 47, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
li .destroy {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
/* width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: auto 0;
|
||||||
|
font-size: 30px;*/
|
||||||
|
color: #cc9a9a;
|
||||||
|
/* margin-bottom: 11px;*/
|
||||||
|
transition: color 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .destroy:hover {
|
||||||
|
color: #af5b5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .destroy:after {
|
||||||
|
content: '\274c';
|
||||||
|
}
|
||||||
|
|
||||||
|
li:hover .destroy {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
63
R4.01_R4.A.10/td_tp/tp5/src/ex1/todo.riot
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<todo>
|
||||||
|
<h3>{ props.title }</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li each={ todo in state.todos } key = {todo.id}>
|
||||||
|
<label class={ todo.done ? 'completed' : '' }>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked = { todo.done }
|
||||||
|
onclick = { () => toggle(todo) } />
|
||||||
|
{ todo.title }
|
||||||
|
</label>
|
||||||
|
<span class="destroy" onclick={() => remove(todo)}></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form onsubmit={ add }>
|
||||||
|
<input oninput={ edit } />
|
||||||
|
<button disabled={ !state.text }>
|
||||||
|
Add #{ state.todos.length + 1 }
|
||||||
|
</button>
|
||||||
|
<button onclick={ clear }>Clear done</button>
|
||||||
|
<ul class="filters">
|
||||||
|
<li><span>0 todos left</span> </li>
|
||||||
|
<li> <a href="#"> All</a></li>
|
||||||
|
<li> <a href="#">Active</a></li>
|
||||||
|
<li> <a href="#">Done</a></li>
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
onBeforeMount(props, state) {
|
||||||
|
// initial state
|
||||||
|
this.state = {
|
||||||
|
todos: props.todos,
|
||||||
|
text: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
edit(e) {
|
||||||
|
// update only the text state
|
||||||
|
this.update({
|
||||||
|
text: e.target.value
|
||||||
|
})
|
||||||
|
},
|
||||||
|
clear(e) {
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
},
|
||||||
|
add(e) {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
toggle(todo) {
|
||||||
|
todo.done = !todo.done
|
||||||
|
this.update()
|
||||||
|
},
|
||||||
|
remove(todo){
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</todo>
|
||||||
18
R4.01_R4.A.10/td_tp/tp5/src/ex2/README.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Riot4 Radar Chart
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
First of all, plz execute this commands.
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
$ cd ../
|
||||||
|
$ yarn install
|
||||||
|
$ yarn radar
|
||||||
|
```
|
||||||
|
|
||||||
|
Perhaps the browser will open automatically and the application should start.
|
||||||
|
|
||||||
|
URL: [http://127.0.0.1:8080/](http://127.0.0.1:8080/)
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
demo: [CodeSandbox](https://codesandbox.io/embed/riot4-template-0f6i0)
|
||||||
62
R4.01_R4.A.10/td_tp/tp5/src/ex2/app.riot
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<app>
|
||||||
|
<h2>{ props.title }</h2>
|
||||||
|
<div id="radar">
|
||||||
|
|
||||||
|
<!-- Use the component -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
is="polygraph"
|
||||||
|
stats={ state.stats } />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- controls -->
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div each={ (stat, index) in state.stats }>
|
||||||
|
<label>{ stat.label }</label>
|
||||||
|
<input type="range"
|
||||||
|
onchange={ changeValue }
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
data-index={ index }
|
||||||
|
value={ stat.value }>
|
||||||
|
<span>{ stat.value }</span>
|
||||||
|
<button onclick={ remove } class="remove" value={ index }>X</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- add item -->
|
||||||
|
<form id="add">
|
||||||
|
<input type="text"
|
||||||
|
name="newlabel"
|
||||||
|
value={ state.newLabel }
|
||||||
|
oninput={ inputStat }>
|
||||||
|
<button onclick={ add }>Add a Stat</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
state: {
|
||||||
|
newLabel: '',
|
||||||
|
stats: []
|
||||||
|
},
|
||||||
|
onBeforeMount(props, state) {
|
||||||
|
state.stats = props.stats
|
||||||
|
},
|
||||||
|
add(e) {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
remove(stat) {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
inputStat(e) {
|
||||||
|
this.state.newLabel = e.target.value
|
||||||
|
},
|
||||||
|
changeValue(e) {
|
||||||
|
this.state.stats[e.target.dataset.index].value = e.target.value
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</app>
|
||||||
31
R4.01_R4.A.10/td_tp/tp5/src/ex2/components/polygraph.riot
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<polygraph>
|
||||||
|
<g>
|
||||||
|
<polygon points={ points() }></polygon>
|
||||||
|
<circle cx="200" cy="200" r="160"></circle>
|
||||||
|
<text
|
||||||
|
each={ (stat, index) in state.stats }
|
||||||
|
index={ index }
|
||||||
|
stat={ stat }
|
||||||
|
total={ state.stats.length }
|
||||||
|
is="text" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
state: {
|
||||||
|
stats: []
|
||||||
|
},
|
||||||
|
onBeforeMount(props, state) {
|
||||||
|
state.stats = props.stats
|
||||||
|
},
|
||||||
|
// a computed property for the polygon's points
|
||||||
|
points() {
|
||||||
|
const total = this.state.stats.length
|
||||||
|
return this.state.stats.map((stat, i) => {
|
||||||
|
const point = valueToPoint(stat.value, i, total)
|
||||||
|
return point.x + ',' + point.y
|
||||||
|
}).join(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</polygraph>
|
||||||
25
R4.01_R4.A.10/td_tp/tp5/src/ex2/components/text.riot
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<text x={ state.point.x } y={ state.point.y }>
|
||||||
|
{ props.stat.label }
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
state: {
|
||||||
|
point: ''
|
||||||
|
},
|
||||||
|
onBeforeMount(props, state) {
|
||||||
|
state.point = valueToPoint(
|
||||||
|
+props.stat.value + 10,
|
||||||
|
props.index,
|
||||||
|
props.total
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onBeforeUpdate(props, state) {
|
||||||
|
state.point = valueToPoint(
|
||||||
|
+props.stat.value + 10,
|
||||||
|
props.index,
|
||||||
|
props.total
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</text>
|
||||||
13
R4.01_R4.A.10/td_tp/tp5/src/ex2/helper.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const valueToPoint = (value, index, total) => {
|
||||||
|
const x = 0
|
||||||
|
const y = -value * 1.6
|
||||||
|
const angle = Math.PI * 2 / total * index
|
||||||
|
const cos = Math.cos(angle)
|
||||||
|
const sin = Math.sin(angle)
|
||||||
|
const tx = x * cos - y * sin + 200
|
||||||
|
const ty = x * sin + y * cos + 200
|
||||||
|
return {
|
||||||
|
x: tx,
|
||||||
|
y: ty
|
||||||
|
}
|
||||||
|
}
|
||||||
42
R4.01_R4.A.10/td_tp/tp5/src/ex2/index.html
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Riot.js Radar Chart</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/riot/10.0.1/riot+compiler.min.js"></script>
|
||||||
|
<script src="helper.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- app -->
|
||||||
|
|
||||||
|
<app></app>
|
||||||
|
|
||||||
|
<!-- tags -->
|
||||||
|
<script data-src="app.riot" type="riot"></script>
|
||||||
|
<script data-src="components/polygraph.riot" type="riot"></script>
|
||||||
|
<script data-src="components/text.riot" type="riot"></script>
|
||||||
|
<script>
|
||||||
|
riot
|
||||||
|
.compile()
|
||||||
|
.then(() => {
|
||||||
|
riot.mount('app', {
|
||||||
|
title: 'Riot.js Radar Chart',
|
||||||
|
stats: [
|
||||||
|
{ label: 'A', value: 100 },
|
||||||
|
{ label: 'B', value: 100 },
|
||||||
|
{ label: 'C', value: 100 },
|
||||||
|
{ label: 'D', value: 100 },
|
||||||
|
{ label: 'E', value: 100 },
|
||||||
|
{ label: 'F', value: 100 }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
46
R4.01_R4.A.10/td_tp/tp5/src/ex2/style.css
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
body {
|
||||||
|
font-family: Helvetica Neue, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
#radar{
|
||||||
|
display : flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width:400px;
|
||||||
|
height:400px;
|
||||||
|
}
|
||||||
|
polygon {
|
||||||
|
fill: #ED1646;
|
||||||
|
opacity: .75;
|
||||||
|
}
|
||||||
|
|
||||||
|
circle {
|
||||||
|
fill: transparent;
|
||||||
|
stroke: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-family: Futura, Helvetica Neue, Arial, sans-serif;
|
||||||
|
font-size: 10px;
|
||||||
|
fill: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 10px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#raw {
|
||||||
|
/* position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 300px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#add {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
109
R4.01_R4.A.10/td_tp/tp6/README.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# TP : Une api rest pour la todo list.
|
||||||
|
|
||||||
|
Le but de l'exercice est d'écrire une api de données restful pour l'application todolist du
|
||||||
|
[tp2](../tp2mvc) ou [tp5](../tp5).
|
||||||
|
|
||||||
|
|
||||||
|
Les routes de notre api :
|
||||||
|
|
||||||
|
```
|
||||||
|
get /todo/(:id)
|
||||||
|
post /todo
|
||||||
|
delete /todo/:id
|
||||||
|
put /todo/:id
|
||||||
|
```
|
||||||
|
|
||||||
|
- Pour routage et les entrées/sorties http, on utilise [flight php](https://flightphp.com/).
|
||||||
|
- Pour les entrées/sorties avec la base données, je vous donne un son utilise l'orm [readbean php](https://www.redbeanphp.com/index.php).
|
||||||
|
|
||||||
|
Copiez le fichier `.htaccess` à la racine de vos sources pour activer la réécriture d'urls.
|
||||||
|
|
||||||
|
```apache
|
||||||
|
Require method GET POST PUT DELETE OPTIONS
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||||
|
```
|
||||||
|
|
||||||
|
Votre api pour l'instant
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require 'flight/Flight.php';
|
||||||
|
require 'model/model.php';
|
||||||
|
Flight::route('GET /todo(/@id)','getTodos');
|
||||||
|
Flight::route('POST /todo','addTodo');
|
||||||
|
Flight::route('DELETE /todo/@id','deleteTodo');
|
||||||
|
Flight::route('PUT /todo/@id','updateTodo');
|
||||||
|
|
||||||
|
function deleteTodo($id)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTodo($id)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTodo()
|
||||||
|
{
|
||||||
|
$todo = [
|
||||||
|
"title" => Flight::request()->data->title ,
|
||||||
|
"done" => Flight::request()->data->done
|
||||||
|
];
|
||||||
|
|
||||||
|
$id = Todo::create($todo);
|
||||||
|
Flight::response()->header("Location",Flight::request()->url.$id);
|
||||||
|
$todo['id'] = $id;
|
||||||
|
Flight::json($todo,201);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTodos($id = null)
|
||||||
|
{
|
||||||
|
$filter = Flight::request()->query->filter ?? "all";
|
||||||
|
|
||||||
|
if ($id === null){
|
||||||
|
switch($filter){
|
||||||
|
case "done":
|
||||||
|
$todos = Todo::findCompleted();
|
||||||
|
break;
|
||||||
|
case "active":
|
||||||
|
$todos = Todo::findUnCompleted();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$todos = Todo::findAll();
|
||||||
|
}
|
||||||
|
Flight::json(
|
||||||
|
[
|
||||||
|
"results" => $todos
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$todo = Todo::find($id);
|
||||||
|
if ($todo)
|
||||||
|
Flight::json($todo);
|
||||||
|
else
|
||||||
|
Flight::halt(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Flight::start();
|
||||||
|
```
|
||||||
|
|
||||||
|
Le modèle utilise PDO, en php. Il vous faudra créér une table avec les attributs nécessaires.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1. Complétez le fichier index.php
|
||||||
|
2. Testez votre api à la ligne de commandes en utilisant `curl`.
|
||||||
|
3. Connectez votre application todolist avec l'api.
|
||||||
|
4. Connectez votre api à l'application [todo](./src/todo-riot). Il faut
|
||||||
|
complétez le fichier api.js. Prenez soin de modifier le fichier `htaccess`.
|
||||||
911
R4.01_R4.A.10/td_tp/tp6/src/api_php/flight/Engine.php
Normal file
144
R4.01_R4.A.10/td_tp/tp6/src/api_php/flight/Flight.php
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use flight\Engine;
|
||||||
|
use flight\net\Request;
|
||||||
|
use flight\net\Response;
|
||||||
|
use flight\net\Router;
|
||||||
|
use flight\template\View;
|
||||||
|
use flight\net\Route;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/autoload.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Flight class is a static representation of the framework.
|
||||||
|
*
|
||||||
|
* @license MIT, http://flightphp.com/license
|
||||||
|
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||||
|
*
|
||||||
|
* # Core methods
|
||||||
|
* @method static void start() Starts the framework.
|
||||||
|
* @method static void path(string $path) Adds a path for autoloading classes.
|
||||||
|
* @method static void stop(?int $code = null) Stops the framework and sends a response.
|
||||||
|
* @method static void halt(int $code = 200, string $message = '', bool $actuallyExit = true)
|
||||||
|
* Stop the framework with an optional status code and message.
|
||||||
|
* @method static void register(string $name, string $class, array $params = [], ?callable $callback = null)
|
||||||
|
* Registers a class to a framework method.
|
||||||
|
* @method static void unregister(string $methodName)
|
||||||
|
* Unregisters a class to a framework method.
|
||||||
|
* @method static void registerContainerHandler(callable|object $containerHandler) Registers a container handler.
|
||||||
|
*
|
||||||
|
* # Routing
|
||||||
|
* @method static Route route(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||||
|
* Maps a URL pattern to a callback with all applicable methods.
|
||||||
|
* @method static void group(string $pattern, callable $callback, callable[] $group_middlewares = [])
|
||||||
|
* Groups a set of routes together under a common prefix.
|
||||||
|
* @method static Route post(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||||
|
* Routes a POST URL to a callback function.
|
||||||
|
* @method static Route put(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||||
|
* Routes a PUT URL to a callback function.
|
||||||
|
* @method static Route patch(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||||
|
* Routes a PATCH URL to a callback function.
|
||||||
|
* @method static Route delete(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||||
|
* Routes a DELETE URL to a callback function.
|
||||||
|
* @method static Router router() Returns Router instance.
|
||||||
|
* @method static string getUrl(string $alias, array<string, mixed> $params = []) Gets a url from an alias
|
||||||
|
*
|
||||||
|
* @method static void map(string $name, callable $callback) Creates a custom framework method.
|
||||||
|
*
|
||||||
|
* @method static void before(string $name, Closure(array<int, mixed> &$params, string &$output): (void|false) $callback)
|
||||||
|
* Adds a filter before a framework method.
|
||||||
|
* @method static void after(string $name, Closure(array<int, mixed> &$params, string &$output): (void|false) $callback)
|
||||||
|
* Adds a filter after a framework method.
|
||||||
|
*
|
||||||
|
* @method static void set(string|iterable<string, mixed> $key, mixed $value) Sets a variable.
|
||||||
|
* @method static mixed get(?string $key) Gets a variable.
|
||||||
|
* @method static bool has(string $key) Checks if a variable is set.
|
||||||
|
* @method static void clear(?string $key = null) Clears a variable.
|
||||||
|
*
|
||||||
|
* # Views
|
||||||
|
* @method static void render(string $file, ?array<string, mixed> $data = null, ?string $key = null)
|
||||||
|
* Renders a template file.
|
||||||
|
* @method static View view() Returns View instance.
|
||||||
|
*
|
||||||
|
* # Request-Response
|
||||||
|
* @method static Request request() Returns Request instance.
|
||||||
|
* @method static Response response() Returns Response instance.
|
||||||
|
* @method static void redirect(string $url, int $code = 303) Redirects to another URL.
|
||||||
|
* @method static void json(mixed $data, int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
|
||||||
|
* Sends a JSON response.
|
||||||
|
* @method static void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
|
||||||
|
* Sends a JSONP response.
|
||||||
|
* @method static void error(Throwable $exception) Sends an HTTP 500 response.
|
||||||
|
* @method static void notFound() Sends an HTTP 404 response.
|
||||||
|
*
|
||||||
|
* # HTTP caching
|
||||||
|
* @method static void etag(string $id, ('strong'|'weak') $type = 'strong') Performs ETag HTTP caching.
|
||||||
|
* @method static void lastModified(int $time) Performs last modified HTTP caching.
|
||||||
|
*/
|
||||||
|
class Flight
|
||||||
|
{
|
||||||
|
/** Framework engine. */
|
||||||
|
private static Engine $engine;
|
||||||
|
|
||||||
|
/** Whether or not the app has been initialized. */
|
||||||
|
private static bool $initialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't allow object instantiation
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forbid cloning the class
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function __clone()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles calls to static methods.
|
||||||
|
*
|
||||||
|
* @param string $name Method name
|
||||||
|
* @param array<int, mixed> $params Method parameters
|
||||||
|
*
|
||||||
|
* @return mixed Callback results
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static function __callStatic(string $name, array $params)
|
||||||
|
{
|
||||||
|
return self::app()->{$name}(...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Engine Application instance */
|
||||||
|
public static function app(): Engine
|
||||||
|
{
|
||||||
|
if (!self::$initialized) {
|
||||||
|
require_once __DIR__ . '/autoload.php';
|
||||||
|
|
||||||
|
self::setEngine(new Engine());
|
||||||
|
self::$initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the engine instance
|
||||||
|
*
|
||||||
|
* @param Engine $engine Vroom vroom!
|
||||||
|
*/
|
||||||
|
public static function setEngine(Engine $engine): void
|
||||||
|
{
|
||||||
|
self::$engine = $engine;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
R4.01_R4.A.10/td_tp/tp6/src/api_php/flight/autoload.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use flight\core\Loader;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/Flight.php';
|
||||||
|
require_once __DIR__ . '/core/Loader.php';
|
||||||
|
|
||||||
|
Loader::autoload(true, [dirname(__DIR__)]);
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace flight\commands;
|
||||||
|
|
||||||
|
use Nette\PhpGenerator\ClassType;
|
||||||
|
use Nette\PhpGenerator\PhpFile;
|
||||||
|
use Nette\PhpGenerator\PhpNamespace;
|
||||||
|
|
||||||
|
class ControllerCommand extends AbstractBaseCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Construct
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $config JSON config from .runway-config.json
|
||||||
|
*/
|
||||||
|
public function __construct(array $config)
|
||||||
|
{
|
||||||
|
parent::__construct('make:controller', 'Create a controller', $config);
|
||||||
|
$this->argument('<controller>', 'The name of the controller to create (with or without the Controller suffix)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the function
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function execute(string $controller)
|
||||||
|
{
|
||||||
|
$io = $this->app()->io();
|
||||||
|
if (isset($this->config['app_root']) === false) {
|
||||||
|
$io->error('app_root not set in .runway-config.json', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/Controller$/', $controller)) {
|
||||||
|
$controller .= 'Controller';
|
||||||
|
}
|
||||||
|
|
||||||
|
$controllerPath = getcwd() . DIRECTORY_SEPARATOR . $this->config['app_root'] . 'controllers' . DIRECTORY_SEPARATOR . $controller . '.php';
|
||||||
|
if (file_exists($controllerPath) === true) {
|
||||||
|
$io->error($controller . ' already exists.', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_dir(dirname($controllerPath)) === false) {
|
||||||
|
$io->info('Creating directory ' . dirname($controllerPath), true);
|
||||||
|
mkdir(dirname($controllerPath), 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = new PhpFile();
|
||||||
|
$file->setStrictTypes();
|
||||||
|
|
||||||
|
$namespace = new PhpNamespace('app\\controllers');
|
||||||
|
$namespace->addUse('flight\\Engine');
|
||||||
|
|
||||||
|
$class = new ClassType($controller);
|
||||||
|
$class->addProperty('app')
|
||||||
|
->setVisibility('protected')
|
||||||
|
->setType('flight\\Engine')
|
||||||
|
->addComment('@var Engine');
|
||||||
|
$method = $class->addMethod('__construct')
|
||||||
|
->addComment('Constructor')
|
||||||
|
->setVisibility('public')
|
||||||
|
->setBody('$this->app = $app;');
|
||||||
|
$method->addParameter('app')
|
||||||
|
->setType('flight\\Engine');
|
||||||
|
|
||||||
|
$namespace->add($class);
|
||||||
|
$file->addNamespace($namespace);
|
||||||
|
|
||||||
|
$this->persistClass($controller, $file);
|
||||||
|
|
||||||
|
$io->ok('Controller successfully created at ' . $controllerPath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the class name to a file
|
||||||
|
*
|
||||||
|
* @param string $controllerName Name of the Controller
|
||||||
|
* @param PhpFile $file Class Object from Nette\PhpGenerator
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function persistClass(string $controllerName, PhpFile $file)
|
||||||
|
{
|
||||||
|
$printer = new \Nette\PhpGenerator\PsrPrinter();
|
||||||
|
file_put_contents(getcwd() . DIRECTORY_SEPARATOR . $this->config['app_root'] . 'controllers' . DIRECTORY_SEPARATOR . $controllerName . '.php', $printer->printFile($file));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace flight\commands;
|
||||||
|
|
||||||
|
use Flight;
|
||||||
|
use flight\net\Route;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property-read ?bool $get
|
||||||
|
* @property-read ?bool $post
|
||||||
|
* @property-read ?bool $delete
|
||||||
|
* @property-read ?bool $put
|
||||||
|
* @property-read ?bool $patch
|
||||||
|
*/
|
||||||
|
class RouteCommand extends AbstractBaseCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Construct
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $config JSON config from .runway-config.json
|
||||||
|
*/
|
||||||
|
public function __construct(array $config)
|
||||||
|
{
|
||||||
|
parent::__construct('routes', 'Gets all routes for an application', $config);
|
||||||
|
|
||||||
|
$this->option('--get', 'Only return GET requests');
|
||||||
|
$this->option('--post', 'Only return POST requests');
|
||||||
|
$this->option('--delete', 'Only return DELETE requests');
|
||||||
|
$this->option('--put', 'Only return PUT requests');
|
||||||
|
$this->option('--patch', 'Only return PATCH requests');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the function
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$io = $this->app()->io();
|
||||||
|
|
||||||
|
if (isset($this->config['index_root']) === false) {
|
||||||
|
$io->error('index_root not set in .runway-config.json', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->bold('Routes', true);
|
||||||
|
|
||||||
|
$cwd = getcwd();
|
||||||
|
|
||||||
|
$index_root = $cwd . '/' . $this->config['index_root'];
|
||||||
|
|
||||||
|
// This makes it so the framework doesn't actually execute
|
||||||
|
Flight::map('start', function () {
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
include($index_root);
|
||||||
|
$routes = Flight::router()->getRoutes();
|
||||||
|
$arrayOfRoutes = [];
|
||||||
|
foreach ($routes as $route) {
|
||||||
|
if ($this->shouldAddRoute($route) === true) {
|
||||||
|
$middlewares = [];
|
||||||
|
if (!empty($route->middleware)) {
|
||||||
|
try {
|
||||||
|
$middlewares = array_map(function ($middleware) {
|
||||||
|
$middleware_class_name = explode("\\", get_class($middleware));
|
||||||
|
return preg_match("/^class@anonymous/", end($middleware_class_name)) ? 'Anonymous' : end($middleware_class_name);
|
||||||
|
}, $route->middleware);
|
||||||
|
} catch (\TypeError $e) {
|
||||||
|
$middlewares[] = 'Bad Middleware';
|
||||||
|
} finally {
|
||||||
|
if (is_string($route->middleware) === true) {
|
||||||
|
$middlewares[] = $route->middleware;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$arrayOfRoutes[] = [
|
||||||
|
'Pattern' => $route->pattern,
|
||||||
|
'Methods' => implode(', ', $route->methods),
|
||||||
|
'Alias' => $route->alias ?? '',
|
||||||
|
'Streamed' => $route->is_streamed ? 'Yes' : 'No',
|
||||||
|
'Middleware' => !empty($middlewares) ? implode(",", $middlewares) : '-'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$io->table($arrayOfRoutes, [
|
||||||
|
'head' => 'boldGreen'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to add the route based on the request
|
||||||
|
*
|
||||||
|
* @param Route $route Flight Route object
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function shouldAddRoute(Route $route)
|
||||||
|
{
|
||||||
|
$boolval = false;
|
||||||
|
|
||||||
|
$showAll = !$this->get && !$this->post && !$this->put && !$this->delete && !$this->patch;
|
||||||
|
if ($showAll === true) {
|
||||||
|
$boolval = true;
|
||||||
|
} else {
|
||||||
|
$methods = [ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH' ];
|
||||||
|
foreach ($methods as $method) {
|
||||||
|
$lowercaseMethod = strtolower($method);
|
||||||
|
if (
|
||||||
|
$this->{$lowercaseMethod} === true &&
|
||||||
|
(
|
||||||
|
$route->methods[0] === '*' ||
|
||||||
|
in_array($method, $route->methods, true) === true
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$boolval = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $boolval;
|
||||||
|
}
|
||||||
|
}
|
||||||