Portfolio webapp basée sur Solid avec Vue.js

DFaveris
13 min readDec 27, 2020

--

ce tuto est la première partie. Si vous souhaitez passer à la deuxième qui vous montre comment publier une webapplication à moindre frais sur gh-pages de github, c’est ici https://dfaveris.medium.com/publier-une-application-vuejs-sur-github-ee7662c94667 .

Et pour savoir comment visualiser le portfolio et les images postées, c’est ici https://dfaveris.medium.com/portfolio-2-cr%C3%A9er-la-page-de-pr%C3%A9sentation-du-portfolio-2aad0304d6ad

Ceci est la traduction du tutoriel en anglais posté sur l’un de mes PODS Solid à l’adresse https://spoggy-test6.solidcommunity.net/public/blog/portfolio.html

Le résultat (en cours de construction actuellement) https://scenaristeur.github.io/portfolio/

Un portfolio est un dossier personnel dans lequel les acquis de formation et les acquis de l’expérience d’une personne sont définis et démontrés en vue d’une reconnaissance par un établissement d’enseignement ou un employeur. En France, on voit parfois le terme de portefeuille de compétences. Avec le développement du numérique, on parle maintenant de portfolio numérique ou de cyberfolio. — Wikipedia

Une application web (aussi appelée web application, de l’anglais) est une application manipulable directement en ligne grâce à un navigateur web et qui ne nécessite donc pas d’installation sur les machines clientes, contrairement aux applications mobiles

Le projet Solid de Sir Tim Berners-Lee propose une manière différente d’utiliser Internet, en dissociant les données & les applications. Chacun peut choisir où il stocke ses données, à qui il les partage, & quelles applications y ont accès. Vos données sont structurées de manière à pouvoir être liées les unes aux autres. Il est également possible de les lier aux données partagées par d’autres utilisateurs, ou à des bases de données sémantiques telles que Semapps, offrant ainsi une infinité de connexions ! Solid sur Wikipedia.

Un POD Solid est un espace de stockage personnel.

Pour ce tutoriel, vous aurez besoin d’installer dans un premier temps NodeJs, C’est un environnement de développement JavaScript qui nous permettra de fabriquer notre webapp.

La version LTS (Long-term support) fera très bien l’affaire.

Commencez donc par installer Nodejs si vous ne l’avez pas déjà en le téléchargeant à cette adresse https://nodejs.org/en/

Nous aurons également besoin d’autres composants mais nous les installerons ensemble en temps voulu :

  • VueJs pour l’architecture de l’application et des composants,
  • bootstrap-vue pour produire une interface utilisateur adaptable sur mobile,
  • solid-auth-client pour se connecter/deconnecter sur un POD Solid,
  • query-ldflex pour facilement effectuer de simples requêtes sur notre POD Solid,
  • solid-file-client pour gérer les fichiers et les dossiers

NB : pour simplifier le code, j’ai volontairement omis dans ce tuto la gestion des erreurs ou la fonctionnalité trackSession de la bibliothèque solid-auth-client qui surveille les changement de session.

Sans plus attendre, nous allons commencer en installant vue-cli tool afin de créer la base de notre application vue.js. Si vous avez installé nodejs, l’outil npm de gestion de packages nous permet d’installer @vue/cli en global. Ouvrez une fenêtre de terminal (cmd sous Windows, ou shell sous Linux) et tapez ces deux commandes :

npm install -g @vue/cli
vue create portfolio

Ensuite choisissez ‘manually select features’ et cochez les options suivantes :

  • Choose Vue version
  • Babel
  • Progressive Web App
  • Router
  • Vuex
  • Linter / Formatter

Selectionnez ensuite la version 2.x de vuejs car la version 3.x n’est pas encore finalisée.

A la question ‘use history mode for router’ choisissez ‘Yes’
puis ‘Eslint with error prevention only / Lint on save / In dedicated config files’
save preset : yes pour pouvoir réutiliser ces préréglages plus tard

En résumé, vous devriez avoir quelque chose comme ça :

A ce point, si vous tapez sur la touche ‘Entrée’ votre application Vue.js basique va être créée dans le dossier /portfolio.

Une fois que l’application est créée, suivez les instructions indiquées en changeant de dossier avec les commandes ‘cd portfolio’ puis npm run serve . Vous pourrez alors voir votre application en ouvrant un navigateur et en tapant dans la barre d’adresse https://localhost:8080 (ou un autre port si vous avez déjà quelque chose qui tourne sur le port 8080)

https://spoggy-test6.solidcommunity.net/public/blog/images/portfolio-created.png

En ouvrant un navigateur web à l’adresse http://localhost:8080, vous devriez voir votre application :

Si tout est OK jusque là, prenons le temps de regarder à quoi ressemble le code notre application…

Ouvrez votre éditeur de code favori

Dans l’image qui suit, vous pouvez voir à quoi ressemble une application vuejs de base :

  1. sur la gauche l’architecture de l’application avec tous les dossiers et fichiers
  2. ensuite, le fichier /src/main.js qui est le point d’entrée de notre application. Il fait appel au fichier vuejs /src/App.vue et le monte dans le div possédant l’id #app du fichier/public/index.html
  3. le fichier App.vue qui implémente les liens du router ‘Home’ & ‘About’ que nous pouvons voir sur l’image précédente
  4. les liens du router permettent de gérer les pages de notre application et sont définies dans le fichier /src/router/index.js en faisant correspondre le lien Home à la vue /src/view/Home.vue
  5. la vue Home.vue à son tour importe le composant /src/components/HelloWorld.vue ( et comme il est indiqué, @ est un alias pour /src )

NB : les vues/views et les components/composants Vue.js ont la même structure, les composants étant des petits éléments ‘atomiques’ alors que les vues sont plus complexes et regroupent plusieurs composants.

Si vous jetez un oeil plus attentif au composant /src/components/HelloWorld.vue, vous pourrez noter l’architecture de base d’un composant vue.js :

  • une balise template où l’on mettre notre code html
  • une balise script où nous mettrons notre code JavaScript
  • une balise style où l’on pourra ajouter… du style css

Connecter votre application au POD de l’utilisateur

Pour connecter votre application au POD de l’utilisateur, nous allons utiliser la librtairie solid-auth-client. Pour ce faire, laissez la fenêtre où vous avez lancé votre application avec ‘ npm run serve’ ouverte et ouvrez une deuxième fenêtre d’invite de commande et placez vous dans le dossier /folder de votre application. Installez ensuite la librairie solid-auth-client grâce à la commande

npm install --save solid-auth-client

Ensuite nous pouvons créer un composant /src/components/solid/SolidLoginButton.vue et l’importer dans notre vue /src/views/Home.vue

Nous pouvons le construire très simplement avec le code suivant :

/src/components/solid/SolidLoginButton.vue

<template>
<div>
<button>Login</button>
<button>Logout</button>
</div>
</template>
<script>
export default {
name: 'SolidLoginButton',
}
</script>

/src/components/Home.vue

<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<SolidLoginButton />
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'

export default {
name: 'Home',
components: {
HelloWorld,
'SolidLoginButton': () => import('@/components/solid/SolidLoginButton'),
},
}
</script>

Notez ici l’alternative que j’ai utilisé pour importer un composant. HelloWorld est toujours importé alors que SolidLoginButton est lazy loaded ( chargé paresseusement ). Cela n’a pas vraiment d’importance ici, mais j’ai pris l’habitude de ne charger les composants que lorsqu’ils sont vraiment nécessaires. Notez aussi que vous pouvez ommettre ici l’extension ‘.vue’

Si vous avez stoppé le serveur de développement, relancez-le avec npm run serve sinon, vous devriez voir votre application se rafraichir automatiquement, et se mettre à jour dans le navigateur avec votre nouveau composant et ses deux boutons Login/Logout (Connexion / Deconnexion).

https://spoggy-test6.solidcommunity.net/public/blog/images/solid-login-button-localhost.png

Maintenant que nous avons nos deux boutons Login/Logout, nous allons utiliser la librairie solid-auth-client pour donner la possibilité à l’utilisateur de se connecter.

D’abord, il nous faut importer la librairie avec la ligne import auth from 'solid-auth-client'; que l’on placera juste après la balise script dans le composant /src/components/solid/SolidLoginButton.vue

Ensuite, ajoutez deux methodes asynchrones login() et logout() qui engloberont les methodes de connexion et de deconnexion de la librairie solid-auth-client

Puis liez les deux boutons à ces deux méthodes que vous venez de créer en leur ajoutant un attribut @click

/src/components/solid/SoldLoginButton.vue

<template>
<div>
<button @click="login">Login</button>
<button @click="logout">Logout</button>
</div>
</template>
<script>
import auth from 'solid-auth-client';
export default {
name: 'SolidLoginButton',
methods: {
async login() {
let session = await auth.currentSession();
let popupUri = 'https://solidcommunity.net/common/popup.html';
if (!session)
session = await auth.popupLogin({ popupUri });
alert(`Logged in as ${session.webId}`);
},
async logout(){
await auth.logout()
},
}
}
</script>

Vous pouvez maintenant tester le fonctionnement des boutons : En cliquant sur le bouton ‘Login’, une fenêtre de connexion Solid devrait s’ouvrir et vous devriez pouvoir vous connecter en utilisant votre WebId Solid. Si vous n’avez pas encore de POD Solid, je vous invite à en créer un en utilisant l’un des fournisseurs de POD

Maintenant que nous pouvons nous connecter et nous déconnecter, nous pouvons optimiser et améliorer l’affichage de nos boutons : Le bouton Logout ne devrait être affiché que lorsque l’utilisateur est connecté et le bouton Login seulement lorsque l’utilisateur n’est pas connecté.

Nous allons gérer ça en créant une variable webId dans la section data du composant /src/components/solid/SoldLoginButton.vue et en testant la valeur de cette variable avec la fonctionnalité vue.js ‘v-if’ v-if="webId == null" pour le bouton Login et v-else pour le bouton Logout. Nous modifierons aussi la valeur de webId dans la fonction login() avec this.webId = session.webId et dans la fonction logout() avec this.webId = null .

Du coup, la ligne alert(`Logged in as ${session.webId}`); dans la fonction login() n’est plus nécessaire et bloque parfois la fermeture de la fenêtre de connexion, donc vous pouvez la supprimer.

Avec ce nouveau code, un seul bouton devrait être visible à la fois, en fonction du statut de connexion de l’utilisateur

/src/components/solid/SoldLoginButton.vue

<template>
<div>
<button v-if="webId == null" @click="login">Login</button>
<button v-else @click="logout">Logout</button>
</div>
</template>
<script>
import auth from 'solid-auth-client';
export default {
name: 'SolidLoginButton',
data: function () {
return {
webId: null
}
},
methods: {
async login() {
let session = await auth.currentSession();
let popupUri = 'https://solidcommunity.net/common/popup.html';
if (!session){
session = await auth.popupLogin({ popupUri });
}
this.webId = session.webId
},
async logout(){
await auth.logout()
this.webId = null
},
}
}
</script>

REndons tout ça un peu plus cool en utilisant un framework CSS. On pourrait certainement faire ça avec vue/ui ou vuetify , personnelement je trouve facile d’utiliser bootstrap-vue, donc utilisons à nouveau npm pour l’installer

npm install vue bootstrap-vue bootstrap --save

Ensuite, on va devoir enregistrer BootstrapVue dans notre point d’entrée (main.js)

import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
Vue.use(BootstrapVue)
Vue.use(IconsPlugin)

et importer les feuilles de style CSS :

import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Notre nouveau fichier /src/main.js devrait maintenant ressembler à ceci :

/src/main.js

import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'

Vue.use(BootstrapVue)
Vue.use(IconsPlugin)

import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Vue.config.productionTip = false

new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')

Ceci nous permet maintenant d’utiliser bootstrap-vue dans n’importe lequel de nos composants et donc de personnaliser nos boutons Login / Logout en modifiant la balise button en b-button et en ajoutant un attribut variant.

<template>
<div>
<b-button variant="success" v-if="webId == null" @click="login">Login</b-button>
<b-button variant="danger" v-else @click="logout">Logout</b-button>
</div>
</template>
<script>
import auth from 'solid-auth-client';
export default {
name: 'SolidLoginButton',
data: function () {
return {
webId: null
}
},
methods: {
async login() {
let session = await auth.currentSession();
let popupUri = 'https://solidcommunity.net/common/popup.html';
if (!session){
session = await auth.popupLogin({ popupUri });
}
this.webId = session.webId
},
async logout(){
await auth.logout()
this.webId = null
},
}
}
</script>

Si tout est bon, vous devriez maintenant voir le bouton Login en vert et le bouton Logout en rouge en fonction du statut de connexion de l’utilisateur.

C’est comme ça que je démarre beaucoup de mes applications Solid comme poPock et vous pouvez réutiliser ce modèle pour créer tout type d’application Solid…

Envoyer une image sur le POD de l’utilisateur

N’oublions pas que notre but est de créer une application portfolio, et donc de permettre aux utilisateurs d’enregistrer des images sur leur POD…

On va encore une fois utiliser bootstrap-vue en prenant cette fois le formulaire d’uplod de fichier bootstrap-vue form-file . Maintenant que vous avez compris la structure d’un composant, le code du composant /src/components/portfolio/Upload.vue (paramétré pour autoriser l’upload multiple et n’acceptant que les images) ne devrait pas vous faire peur .

/src/components/portfolio/Upload.vue

<template>
<div class="container">
<b-form-file multiple
accept="image/*"
v-model="files"
:state="Boolean(files)"
placeholder="Choose a file or drop it here..."
drop-placeholder="Drop file here..."
@input="onInput"
></b-form-file>
</div>
</template>
<script>
export default {
name: 'Upload',
data() {
return {
files: null
}
},
methods: {
onInput(files) {
console.log(files)
}
}
}
</script>

Nous pouvons comme précédement importer ce nouvau composant dans notre vue /src/views/Home.vue

/src/views/Home.vue

<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<SolidLoginButton />
<Upload />
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
components: {
HelloWorld,
'SolidLoginButton': () => import('@/components/solid/SolidLoginButton'),
'Upload': () => import('@/components/portfolio/Upload'),
},
}
</script>

Maintenant, nous pouvons envoyer plusieurs images simultanément à notre application et voir les fichiers dans la console JavaScript (Ctrl+Maj+i)

Maintenant que nous pouvons charger des images, nous avons besoin d’un endroit pour les stocker. Etant donné que nous construisons un portfolio, un bon endroit pour stocker nos images dans le dossier /public du Pod de l’utilisateur, dans un sous-dossier /portfolio.

D’abord, nous avons besoin de récupérer l’espace de stockage de l’utilisateur d’après le profile fourni par son WebId. La manière la plus simple pour y arriver est d’utiliser la librairie query-ldflex

Le moyen le plus simple pour utiliser query-ldflex est d’ajouter dans notre fichier /public/index.html deux lignes d’import de script à la fin de la balise head. Une pour la librairie rdflib et une pour query-ldflex

/public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<script src="https://cdn.jsdelivr.net/npm/solid-auth-client@2.3.0/dist-lib/solid-auth-client.bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rdflib/dist/rdflib.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@solid/query-ldflex/dist/solid-query-ldflex.rdflib.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

VOUS DEVEZ MAINTENANT RAFRAICHIR LA PAGE DANS LE NAVIGATEUR POUR ÊTRE CERTAIN QUE CES LIBRAIRIES SONT CORRECTEMENT CHARGEES.

A ce stade, il y aura un message d’avertissement dans la console indiquant que plusieurs versions de solid-auth-client sont chargées, mais ce n’est pas gênant.

Maintenant, nous pouvons accéder à la librairie ldflex partout dans notre application avec le ligne let ldflex = window.solid et ldflex récupère automatiquement le webId de l’utilisateur connecté avec solid-auth-client, ce qui nous donne un moyen simple pour récupérer l’adresse de l’espace de stockage de l’utilisateur avec let storage = await ldflex.data.user.storage .

Ainsi notre composant /src/components/portfolio/Upload.vue pourrait ressembler à quelque chose comme ça :

/src/components/portfolio/Upload.vue

<template>
<div class="container">
<b-form-file multiple
accept="image/*"
v-model="files"
:state="Boolean(files)"
placeholder="Choose a file or drop it here..."
drop-placeholder="Drop file here..."
@input="onInput"
></b-form-file>
</div>
</template>

<script>
let ldflex = window.solid

export default {
name: 'Upload',
data() {
return {
files: null
}
},
methods: {
async onInput(files) {
console.log(files)
let storage = await ldflex.data.user.storage
let path = `${storage}`+'public/portfolio/'
console.log(path)
}
}
}
</script>

Maintenant, nous avons nos fichiers image et un chemin (path) pour les stocker (jetez un oeil dans la console JavaScript)

Enregistrer les fichiers images sur le POD de l’utilisateur

Avec ce code, nous savons où enregistrer nos fichiers images… Pas mal… On avance…

Pour continuer, on va maintenant utiliser la magnifique librairie solid-file-client pour enregistrer les fichiers sur le Pod de l’utilisateur à l’endroit voulu…

On peut installer la librairie avec la commande

npm install --save solid-file-client

Et nous pouvons l’utiliser avec

import FC from 'solid-file-client'
const fc = new FC( window.solid.auth )

Donc maintenant notre composant /src/componenets/portfolio/Upload.vue ressemble à ça

/src/componenets/portfolio/Upload.vue

<template>
<div class="container">
<b-form-file multiple
accept="image/*"
v-model="files"
:state="Boolean(files)"
placeholder="Choose a file or drop it here..."
drop-placeholder="Drop file here..."
@input="onInput"
></b-form-file>
</div>
</template>

<script>
let ldflex = window.solid
import FC from 'solid-file-client'
const fc = new FC( window.solid.auth )
export default {
name: 'Upload', data() {
return {
files: null
}
},
methods: {
async onInput(files) {
console.log(files)
let storage = await ldflex.data.user.storage
let path = `${storage}`+'public/portfolio/'
console.log(path)
await files.forEach(async function(f) {
console.log(f)
let uri = f.webkitRelativePath.length > 0 ? path+f.webkitRelativePath : path+f.name
console.log(encodeURI(uri))
await fc.createFile(encodeURI(uri), f, f.type)
})
}
}
}
</script>

A CE STADE, ESSAYER VOTRE CODE DEVRAIT VOUS RENVOYER UNE ERREUR, car votre application est hébergée à l’adresse http://localhost:8080 et cette adresse n’est pas connue dans les “applications de confiance” (trusted apps) du POD

Pour régler ce problème, vous devez ajouter l’adresse http://localhost:8080 dans les ‘trusted apps’ de l’onglet ‘Preferences’ de votre POD. (Il y a possibilité de modifier la fenêtre de connexion pour que ces manipulations ne soient pas nécessaires par un utilisateur non averti, mais il est bon de savoir comment cela fonctionne ;-))

Une fois que c’est fait, vous pouvez télécharger les photos sur le POD ;-)

FIN DE LA PREMIERE PARTIE

La deuxièmle étape maintenant que nous avons une application est de la publier sur internet pour qu’elle soit accessible, et c’est l’objet du deuxième tuto https://dfaveris.medium.com/publier-une-application-vuejs-sur-github-ee7662c94667

Sourtce originale https://spoggy-test6.solidcommunity.net/public/blog/portfolio.html

--

--

DFaveris
DFaveris

Written by DFaveris

main Project : "un robot qui range ma chambre" robot that tidy my room. Working on "how can robots share informations ?" how can we tell them what we want...