##### Code-source ##### .. captionup:: ./package.json .. code-block:: javascript { "name": "forum", "version": "1.0.0", "dependencies": { "express": "4.13.4", "express-session": "1.13.0", "ejs": "2.4.1", "crypto": "0.0.3", "base64url": "1.0.6", "node-mysql": "0.4.2", "socket.io": "1.4.5", "mime-types": "2.1.10" } } Code côté serveur ===== .. captionup:: ./app.js .. code-block:: javascript var http = require('http'); var path = require('path'); var crypto = require('crypto'); var base64url = require('base64url'); var mysql = require('mysql'); var express = require('express'); var app = express(); var server = http.Server(app); var io = require('socket.io')(server); var express_session = require('express-session'); var routes = require('./serveur/routes.js'); var session = require('./serveur/session.js'); var socket = require('./serveur/socket.js'); var sql = require('./serveur/sql.js'); routes.f(app, session, express, path, __dirname, mysql, sql, express_session, crypto, base64url); socket.f(io, mysql, sql, session, crypto, base64url); server.listen(8888); .. captionup:: serveur/routes.js .. code-block:: javascript exports.f = function( app, session, express, path, dossier, mysql, sql, express_session, crypto, base64url ) { // le chiffre 7 pourrait être n'importe quel autre chiffre ou lettre var session_secret = base64url(crypto.randomBytes(20)).replace('-', '7') session.session_init(app, express_session, session_secret); // la session est initiée dans express.js app.get('/token/:token', function(req, res) { session.conv_jeton(mysql, sql, req.params.token, function(nom_utilisateur) { session.login(res, req.session, nom_utilisateur, function() { res.redirect(301, '/'); }); }); }); // page de login app.get('/login', function(req, res) { res.render('login.ejs', { nom_utilisateur: session.session_active(req.session, req) }); }); // page de logout app.get('/logout', function(req, res) { session.logout(req.session); // redirection vers login res.redirect(301, '/login'); }); // page de nouveau compte app.get('/nouveau_compte', function(req, res) { res.render('nouveau_compte.ejs', { nom_utilisateur: session.session_active(req.session, req) }); }); // page de profil app.get(/^\/profil_([a-z0-9]{4,10})/i, function(req, res) { sql.profil(mysql, sql, req.params[0], function(result) { res.render('profil.ejs', { nom_utilisateur: session.session_active(req.session, req), profil : result }); }); }); // page d'accueil app.get(/^\/home|\/$/, function(req, res) { sql.chargement_discussions(mysql, sql, true, function(discussions) { res.render('index.ejs', { nom_utilisateur: session.session_active(req.session, req), discussions: discussions }); }); }); // dernière discussion - utilisée par des requêtes AJAX app.get('/derniere_discussion', function(req, res) { /* ici, la variable tout_charger vaut false, car nous ne voulons que la dernière discussion */ sql.chargement_discussions(mysql, sql, false, function(discussion) { res.render('chargement_discussions.ejs', { discussions: discussion }); }); }); // page de discussions app.get(/^\/([0-9]{1,9})$/, function(req, res) { sql.chargement_messages(req.params[0], mysql, sql, true, function(discussion_existante, sujet_discussion, messages) { if (!discussion_existante) { res.render('erreur_discussion.ejs'); } else { res.render('discussion.ejs', { nom_utilisateur: session.session_active(req.session, req), sujet_discussion: sujet_discussion, m : messages }); } } ); }); // dernier message (d'une discussion) - utilisé par des requêtes AJAX app.get(/^\/dernier_([0-9]{1,9})$/, function(req, res) { sql.chargement_messages(req.params[0], mysql, sql, false, function(discussion_existante, sujet_discussion, messages) { res.render('chargement_messages.ejs', { nom_utilisateur: session.session_active(req.session, req), m: messages }); } ); }); // page de nouvelle discussion app.get('/new', function(req, res) { res.render('new.ejs', { nom_utilisateur: session.session_active(req.session, req) }); }); // fichiers statiques app.get(/static\/([0-9a-z\.\/_-]+)$/i, function(req, res) { res.sendFile(dossier + '/static/' + req.params[0]); }); // page d'erreur app.use(function(req, res, next) { res.render('error.ejs'); }); }; .. captionup:: ./serveur/session.js .. code-block:: javascript // initialise la session exports.session_init = function(app, express_session, session_secret) { app.use(express_session({ secret: session_secret, name: 'utilisateur', resave: true, saveUninitialized: true })); }; // crée une nouvelle session pour un utilisateur exports.login = function(res, sess, nom_utilisateur, callback) { sess.nom_utilisateur = nom_utilisateur; callback(); }; // supprime la session exports.logout = function(sess) { sess.destroy(function(err) { }); }; // retourne la session en cours exports.session_active = function(sess, req) { if (sess.nom_utilisateur) { return sess.nom_utilisateur; } else { return 'session inexistante'; } }; // crée un jeton d'authentification, le met dans la base de données, le retourne exports.creation_jeton = function(mysql, sql, base64url, crypto, socket, nom_utilisateur) { var token = base64url(crypto.randomBytes(20)).replace('-', '_'); var requete_sql = '\ INSERT INTO session(nom_utilisateur, token) VALUES(??, "??")'; var inserts = [nom_utilisateur, token]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function() { socket.emit('redirection', '/token/'+token); }); } // convertit un jeton en nom d'utilisateur et supprime le jeton de la base de données exports.conv_jeton = function(mysql, sql, token, callback) { var requete_sql = 'SELECT nom_utilisateur FROM session WHERE token = "??"'; var inserts = [token]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { var requete_sql = '\ DELETE FROM session WHERE nom_utilisateur = "'+results[0].nom_utilisateur+'"'; sql.requete(mysql, sql, requete_sql); callback(results[0].nom_utilisateur); }); } .. captionup:: ./serveur/socket.js .. code-block:: javascript exports.f = function(io, mysql, sql, session, crypto, base64url) { io.on('connection', function (socket) { // vérifie si le mot de passe est correct socket.on('login', function (login) { var requete_sql = '\ SELECT mot_de_passe \ FROM users \ WHERE nom_utilisateur = ??'; var inserts = [login.nom_utilisateur]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { try { /* ce bloc try-catch sert à détecter une erreur pouvant survenir lorsqu'on demande le mot de passe d'un utilisateur inexistant */ if (login.password == results[0].mot_de_passe) { session.creation_jeton( mysql, sql, base64url, crypto, socket, login.nom_utilisateur ); } else { socket.emit('erreur_login'); } } catch(e) { socket.emit('erreur_login'); } }); }); // crée un nouveau compte socket.on('nouveau_compte', function(compte) { var requete_sql = 'SELECT \ COUNT(nom_utilisateur) AS "user_exists"\ FROM users WHERE nom_utilisateur = ??'; var inserts = [compte.nom_utilisateur]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(result) { if (result[0].user_exists !== 0) { socket.emit('utilisateur_existant'); return; } else { var requete_sql = '\ INSERT INTO \ users(nom_utilisateur, mot_de_passe, email, nom, prenom, avatar) \ VALUES(??, ??, ??, ??, ??, ?)'; var inserts = [ compte.nom_utilisateur, compte.password, compte.mail, compte.nom, compte.prenom, Math.floor((Math.random() * 100) + 1) ]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql); session.creation_jeton( mysql, sql, base64url, crypto, socket, compte.nom_utilisateur); } }); }); // modifie les coordonnées d'un utilisateur socket.on('modifier_compte', function(compte) { var requete_sql = '\ UPDATE users SET email = ??, nom = ??, prenom = ?? \ WHERE nom_utilisateur = ??'; var inserts = [ compte.mail, compte.nom, compte.prenom, compte.utilisateur ]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { socket.emit('mod_compte_ok', { mail : compte.mail, nom : compte.nom, prenom : compte.prenom, utilisateur : compte.utilisateur, broadcast : false }); socket.broadcast.emit('mod_compte_ok', { mail : compte.mail, nom : compte.nom, prenom : compte.prenom, utilisateur : compte.utilisateur, broadcast : true }); }); }); // modifie le mot de passe d'un utilisateur socket.on('modifier_password', function(mod) { var requete_sql = '\ SELECT mot_de_passe \ FROM users \ WHERE nom_utilisateur = ??'; var inserts = [mod.nom_utilisateur]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { if (mod.ancien_password == results[0].mot_de_passe) { var requete_sql = 'UPDATE users SET mot_de_passe = ?? \ WHERE nom_utilisateur = ??'; var inserts = [mod.nouveau_password, mod.nom_utilisateur]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql); socket.emit('mod_password_ok'); } else { socket.emit('ancien_password_incorrect'); } }); }); // publie un nouveau message et l'enregistre dans la base de données socket.on('nouveau_message', function (message) { var requete_sql = '\ INSERT INTO messages(nom_utilisateur, contenu, \ date_ecriture, id_discussion, likes)\ VALUES(??, ??, NOW(), ?, 0)'; var inserts = [ message.nom_utilisateur, message.contenu, message.id_discussion ]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql); socket.emit('charger_dernier_message'+message.id_discussion); socket.broadcast.emit('charger_dernier_message'+message.id_discussion); }); // modifie un message et l'enregistre dans la base de données socket.on('modification_message', function (d) { var requete_sql = '\ UPDATE messages SET contenu = ??, date_modification=NOW() WHERE id_message = ?'; var inserts = [d.contenu.replace(/'/g, "\\'"), d.id_message]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { var requete_sql = '\ SELECT * FROM messages WHERE id_message = ?'; var inserts = [d.id_message]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { socket.emit('update_message'+d.id_discussion, { id_message : results[0].id_message, date_modification : results[0].date_modification, contenu : results[0].contenu }); socket.broadcast.emit('update_message'+d.id_discussion, { id_message : results[0].id_message, date_modification : results[0].date_modification, contenu : results[0].contenu }); }); }); }); // supprime un message (dans la base de données) socket.on('suppression_message_serveur', function (r) { var requete_sql = 'DELETE FROM messages WHERE id_message = ?'; var inserts = [r.id_message]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql); /* la concaténation de 'charger_dernier_message' avec l'id de la discussion permet d'éviter que le message se charge chez tous les utilisateurs ayant une discussion ouverte */ socket.emit('suppression_message_client_'+r.id_discussion, r.id_message); socket.broadcast.emit('suppression_message_client_'+r.id_discussion, r.id_message); }); /* ajoute un "like" à un message tout en vérifiant que l'utilisateur n'aie pas déjà aimé le message (chaque utilisateur ne peut aimer qu'une seule fois un message) */ socket.on('like_message', function(d) { // id_message, nom_utilisateur if (d.nom_utilisateur !== 'session inexistante') { var requete_sql = 'SELECT nom_utilisateur FROM likes WHERE id_message = ?'; var inserts = [d.id_message]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { for (var i = 0; i < results.length; i++) { if (results[i].nom_utilisateur === d.nom_utilisateur) { var deja_like = true; return; } } if (!deja_like) { var requete_sql = 'SELECT likes FROM messages WHERE id_message = ?'; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { var likes_actuels = results[0].likes + 1; var requete_sql = 'UPDATE messages SET likes = ? WHERE id_message = ?'; var inserts = [likes_actuels, d.id_message]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql); var requete_sql = 'INSERT INTO likes(id_message, nom_utilisateur) \ VALUES (?, ??)'; var inserts = [d.id_message, d.nom_utilisateur]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql); socket.emit('update_likes'+d.id_discussion, { id_message : d.id_message, nombre_likes : likes_actuels }); socket.broadcast.emit('update_likes'+d.id_discussion, { id_message : d.id_message, nombre_likes : likes_actuels }); }); } }); } }); // change l'avatar d'un utilisateur socket.on('changement_avatar', function(d) { var requete_sql = 'UPDATE users SET avatar = ? WHERE nom_utilisateur = ??'; var inserts = [d.avatar, d.nom_utilisateur]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql); }); socket.on('nouvelle_discussion', function(discussion) { // sélectionne le dernier ID de discussion var requete_sql = '\ SELECT id_discussion FROM messages \ ORDER BY id_discussion DESC \ LIMIT 0,1'; sql.requete(mysql, sql, requete_sql, function(results) { /* incrémente le dernier ID de 1, ce qui détermine l'ID de la nouvelle discussion */ var id_nouvelle_discussion = results[0].id_discussion+1; // insert le premier message de la discussion var requete_sql = '\ INSERT INTO \ messages(nom_utilisateur, contenu, date_ecriture, id_discussion, likes)\ VALUES(??, ??, NOW(), ?, 0)'; var inserts = [ discussion.nom_utilisateur, discussion.message, id_nouvelle_discussion ]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql); // attribue le sujet à la discussion dans la table "discussions" var requete_sql = '\ INSERT INTO discussions(sujet, id_discussion)\ VALUES(??, ?)'; var inserts = [discussion.sujet.replace(/'/g, "\\'"), id_nouvelle_discussion]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function() { // redirige l'utilisateur vers la nouvelle discussion socket.emit('redirection', id_nouvelle_discussion); // requête pour charger la dernière discussion dans la page d'accueil socket.broadcast.emit('charger_derniere_discussion'); }); }); }); }); } .. captionup:: ./serveur/sql.js .. code-block:: javascript /* crée une connection toujours active entre le serveur et la base de données */ exports.pool = function(mysql) { var pool = mysql.createPool({ host : 'localhost', user : 'root', password : 'root', database : 'forum', charset : 'UTF8_UNICODE_CI', multipleStatements : true }); return pool; } // exécute une requête SQL exports.requete = function(mysql, sql, requete_sql, callback) { sql.pool(mysql).getConnection(function(err, connection) { connection.query(requete_sql, function(err, results) { sql.query_error(err); if(callback) { callback(results); } connection.destroy(); }); }); } exports.query_error = function(erreur) { if (erreur) { console.log('query error : ' + erreur.stack); } } // prépare une requête SQL exports.preparer = function(mysql, requete_sql, inserts) { requete_sql = mysql.format(requete_sql, inserts) .replace(/`/g, "'") .replace(/'\.'/g, "."); return requete_sql; } // retourne toutes les informations pour afficher un profil exports.profil = function(mysql, sql, utilisateur, callback) { var requete_sql = 'SELECT * FROM users WHERE nom_utilisateur = ??'; var inserts = [utilisateur]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { callback(results[0]); }); } // charge les discussions sur la page d'accueil exports.chargement_discussions = function(mysql, sql, tout_charger, callback) { if (tout_charger) { // toutes les discussions sont chargées var requete_sql = '\ SELECT id_discussion\ FROM discussions\ ORDER BY id_discussion'; } else { // seule la dernière discussion est chargée var requete_sql = '\ SELECT id_discussion\ FROM discussions\ ORDER BY id_discussion DESC\ LIMIT 0,1'; } sql.requete(mysql, sql, requete_sql, function(results) { var requete_sql = ''; for (var i = 0; i < results.length; i++) { var requete_temp = '\ SELECT\ COUNT(messages.id_message) AS "nombre_messages",\ discussions.sujet,\ discussions.id_discussion,\ messages.nom_utilisateur AS "utilisateur_createur",\ messages.date_ecriture\ FROM discussions, messages\ WHERE discussions.id_discussion = messages.id_discussion\ AND messages.id_discussion = ?\ ORDER BY messages.date_ecriture\ LIMIT 0,1;'; var inserts = [results[i].id_discussion]; requete_sql = requete_sql + sql.preparer(mysql, requete_temp, inserts); } sql.requete(mysql, sql, requete_sql, function(results) { if (tout_charger) { callback(results); } else { callback([results]); } }); }); } // retourne le sujet d'une discussion exports.sujet_discussion = function(mysql, sql, id_discussion, callback) { var requete_sql = 'SELECT sujet FROM discussions WHERE id_discussion = ?'; var inserts = [id_discussion]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { callback(results[0].sujet); }); } // charge tous ou le dernier des messages d'une discussionl exports.chargement_messages = function( id_discussion, mysql, sql, tout_charger, callback) { // requête SQL pour vérifier si la discussion existe var requete_sql = '\ SELECT id_discussion\ FROM discussions\ WHERE id_discussion = ?'; var inserts = [id_discussion]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { // on vérifie si la variable results[0] contient l'id de discussion if (!results[0]) { // on retourne false à l'argument discussion_existante // ceci est ensuite traité par routes.js et envoie une page d'erreur callback(false); } else { // on récupère d'abord le sujet de la discussion sql.sujet_discussion(mysql, sql, id_discussion, function(sujet_discussion) { // une fois qu'on l'a on récupère tous les messages var requete_sql = '\ SELECT\ messages.id_message AS "id_message",\ messages.nom_utilisateur AS "nom_utilisateur",\ messages.contenu AS "contenu",\ messages.date_ecriture AS "date_ecriture",\ messages.date_modification AS "date_modification",\ messages.id_discussion AS "id_discussion",\ messages.likes AS "likes",\ users.avatar AS "avatar"\ FROM users, messages\ WHERE messages.id_discussion = ?\ AND messages.nom_utilisateur = users.nom_utilisateur\ ORDER BY messages.date_ecriture'; // si on ne veut que le dernier message... if (!tout_charger) { requete_sql += ' DESC LIMIT 0,1'; } var inserts = [id_discussion]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { // enfin, on peut envoyer le sujet de discussion et les messages callback(true, sujet_discussion, results); }); } ); } } ); } Code côté client ===== .. captionup:: ./static/js/conversion_dates.js .. code-block:: javascript convertir_date = function(date, id_html) { date = new Date(date); var jour_mois = date.getDate(); var jour = date.getDay(); var mois = date.getMonth(); var annee = date.getFullYear(); var heure = date.getHours(); var minutes = date.getMinutes(); switch(jour) { case 1: jour = 'lundi'; break; case 2: jour = 'mardi'; break; case 3: jour = 'mercredi'; break; case 4: jour = 'jeudi'; break; case 5: jour = 'vendredi'; break; case 6: jour = 'samedi'; break; case 0: jour = 'dimanche'; break; } switch(mois) { case 0: mois = 'janvier'; break; case 1: mois = 'février'; break; case 2: mois = 'mars'; break; case 3: mois = 'avril'; break; case 4: mois = 'mai'; break; case 5: mois = 'juin'; break; case 6: mois = 'juillet'; break; case 7: mois = 'août'; break; case 8: mois = 'septembre'; break; case 9: mois = 'octobre'; break; case 10: mois = 'novembre'; break; case 11: mois = 'décembre'; break; } if(/^[0-9]$/.test(minutes)) { date_francais = jour+' '+jour_mois+' '+mois+' '+annee+' à '+heure+'h0'+minutes; } else { date_francais = jour+' '+jour_mois+' '+mois+' '+annee+' à '+heure+'h'+minutes; } if(typeof(id_html) === 'undefined') { return date_francais; } else { byId(id_html).innerHTML = date_francais; } } .. captionup:: ./static/js/discussion.js .. code-block:: javascript var id_discussion = window.location.href.replace(/.+\/([0-9]+)/, '$1'); /* fonction permettant de formatter les guillemets simples et doubles ainsi que les backslash pour éviter un bug lors de la requête SQL */ function echap(chaine) { return chaine .replace(/"/g, "#g_doubles") .replace(/'/g, "#g_simples") .replace(/\\/g, "#backslash") } // fonction qui réalise l'inverse de la précédente function unechap(chaine) { return chaine .replace(/#g_doubles/g, '"') .replace(/#g_simples/g, "'") .replace(/#backslash/g, "\\"); } function publier_message(nom_utilisateur) { if (tinyMCE.get('emplacement_nouveau_message').getContent() !== '') { socket.emit('nouveau_message', { nom_utilisateur : nom_utilisateur, contenu : echap(tinyMCE.get('emplacement_nouveau_message').getContent()), id_discussion : id_discussion }); tinyMCE.get('emplacement_nouveau_message').setContent(''); } } function supprimer_message(id_message) { socket.emit('suppression_message_serveur', { id_discussion : id_discussion, id_message : id_message }); } /* "like" d'un message à noter que cette fonction envoie également le nom d'utilisateur pour l'insérer dans la table "likes" de la base de données et ainsi éviter qu'un utilisateur puisse aimer 2X un message */ function aimer(id_message, nom_utilisateur) { socket.emit('like_message', { id_message : id_message, nom_utilisateur : nom_utilisateur, id_discussion : id_discussion }); } /* retourne simplement l'objet javascript de la classe "zone_message" à partir d'un objet contenant une propriété "id_message" */ function zone_message(classe, d) { return byId(d.id_message) .getElementsByClassName('zone_message')[0] .getElementsByClassName(classe)[0]; } /* affiche l'éditeur TinyMCE à la place du contenu du message masque le bouton "modifier" et affiche le bouton "enregistrer" */ function modifier_message(id_message) { var d = { id_message : id_message }; byId(id_message+'_bouton_modifier').style.display = 'none'; byId(id_message+'_bouton_modifier_mobile').style.display = 'none'; byId(id_message+'_bouton_enregistrer').style.display = 'block'; byId(id_message+'_bouton_enregistrer_mobile').style.display = 'inline'; zone_message('affichage', d).style.display = 'none'; zone_message('editeur', d).style.display = 'block'; // récupère l'ID aléatoire de TinyMCE var id_tinymce = zone_message('editeur', d).getElementsByTagName('textarea')[0].id; init_editeur(id_tinymce); } /* enregistre le message, envoie un événement socket, masque le bouton "enregistrer", affiche le bouton "modifier". A noter que le message est modifié sur la page une fois que socket.io retourne l'événement "update_message" */ function enregistrer_message(id_message) { var d = { id_message : id_message }; byId(id_message+'_bouton_modifier').style.display = 'block'; byId(id_message+'_bouton_modifier_mobile').style.display = 'inline'; byId(id_message+'_bouton_enregistrer').style.display = 'none'; byId(id_message+'_bouton_enregistrer_mobile').style.display = 'none'; zone_message('affichage', d).style.display = 'block'; zone_message('editeur', d).style.display = 'none'; var id_tinymce = zone_message('editeur', d).getElementsByTagName('textarea')[0].id; socket.emit('modification_message', { contenu: echap(tinyMCE.get(id_tinymce).getContent()), id_message: id_message, id_discussion : id_discussion }); } /* télécharge le dernier message à l'aide d'une requête AJAX s'exécute lorsqu'un utilisateur a publié un message sur la discussion */ socket.on('charger_dernier_message'+id_discussion, function() { var xhr = new XMLHttpRequest(); xhr.open('GET', 'dernier_'+id_discussion); xhr.addEventListener('readystatechange', function() { if (xhr.readyState === 4 && xhr.status === 200) { byId('messages').innerHTML += xhr.responseText; var dates_ecriture = byId('messages').getElementsByClassName('date_ecriture'); var id = dates_ecriture[dates_ecriture.length-1] .getElementsByTagName('span')[0].id; convertir_date(Date.now(), id); } }); xhr.send(null); }); // supprime un message du côté client socket.on('suppression_message_client_'+id_discussion, function(id_message) { byId(id_message).style.display = 'none'; }); // met à jour les likes d'un message socket.on('update_likes'+id_discussion, function(m) { byId(m.id_message).getElementsByClassName('likes')[0].innerHTML = m.nombre_likes; byId(m.id_message).getElementsByClassName('likes')[1].innerHTML = m.nombre_likes; }) /* met à jour le contenu d'un message, change l'ID de la textarea pour que le message puisse toujours être modifié */ socket.on('update_message'+id_discussion, function(d) { zone_message('date_modification', d).innerHTML = 'modifié le ' + convertir_date(Date.now()); zone_message('editeur', d).innerHTML = ''; zone_message('affichage', d).innerHTML = unechap(d.contenu); }); .. captionup:: ./static/js/general.js .. code-block:: javascript var socket = io.connect('http://' + window.location.host); function byId(id) { return document.getElementById(id); } function byClass(classe) { return document.getElementsByClassName(classe); } function init_editeur(id_editeur) { var tinymce_toolbar = "undo redo | \ n eqneditor | \ n underline bold italic | \ n alignleft aligncenter alignright alignjustify | \ n bullist numlist outdent indent | \ n image link code | \ n forecolor backcolor emoticons"; var tinymce_plugins = "eqneditor textcolor image code emoticons"; tinyMCE.init({ selector: '#'+id_editeur, plugins: tinymce_plugins, toolbar: tinymce_toolbar, language: "fr_FR" }); } function navbar_mobile() { if (byId('contenu_nav').className === 'navbar-collapse collapse') { byId('contenu_nav').className = 'navbar-collapse collapse in'; byId('navbar').style.height = '100%'; } else { byId('contenu_nav').className = 'navbar-collapse collapse'; byId('navbar').style.height = '50px'; } } .. captionup:: ./static/js/index.js .. code-block:: javascript socket.on('charger_derniere_discussion', function() { var xhr = new XMLHttpRequest(); xhr.open('GET', 'derniere_discussion'); xhr.addEventListener('readystatechange', function() { if (xhr.readyState === 4 && xhr.status === 200) { byId('retour_ajax').innerHTML += xhr.responseText; } }); xhr.send(null); }); function rechercher() { byId('chargement_discussions').innerHTML = ''; byId('retour_ajax').innerHTML = ''; var recherche = byId('recherche_discussion').value; var discussions = byClass('discussion'); for (var i = 0; i < discussions.length; i++) { discussions[i].style.display = 'none'; } for (var i = 0; i < discussions.length; i++) { var sujet = discussions[i].getElementsByClassName('sujet_texte')[0].innerHTML; var regex = new RegExp(recherche, 'i'); if (regex.test(sujet)) { discussions[i].style.display = 'block'; } } } .. captionup:: ./static/js/login.js .. code-block:: javascript // cette fonction envoie l'événement socket "login" function login() { socket.emit('login', { nom_utilisateur : byId('login_utilisateur').value, password : byId('login_password').value }); } function verifier_enter(event, form, callback) { // fonction tirée de http://stackoverflow.com/questions/14251676/ var code = (event.keyCode ? event.keyCode : event.which); if(code === 13) { callback(); } } // met les zones de texte en rouge et affiche le message d'erreur socket.on('erreur_login', function() { byId('form_login_utilisateur').className += ' has-error'; byId('form_login_password').className += ' has-error'; byId('login_incorrect').style.display = 'inline'; }); // redirige l'utilisateur vers l'URL indiquée dans l'événement socket // (en l'occurence, la page "token") socket.on('redirection', function(url) { window.location.assign(url); }); .. captionup:: ./static/js/new.js .. code-block:: javascript // publie un message depuis une nouvelle discussion function publier_message(nom_utilisateur) { if (byId('input_sujet').value !== '' && tinyMCE.get('emplacement_nouveau_message').getContent() !== '') { socket.emit('nouvelle_discussion', { sujet : byId('input_sujet').value, message : tinyMCE.get('emplacement_nouveau_message').getContent(), nom_utilisateur : nom_utilisateur }); } } // redirige l'utilisateur vers la nouvelle discussion socket.on('redirection', function(id_discussion) { window.location.assign(id_discussion); }); .. captionup:: ./static/js/nouveau_compte.js .. code-block:: javascript // crée un nouveau compte function nouveau_compte() { if (verifier('utilisateur') && verifier('prenom') && verifier('nom') && verifier('mail') && verifier('password')) { socket.emit('nouveau_compte', { nom_utilisateur : byId('nc_utilisateur').value, password : byId('nc_password').value, prenom : byId('nc_prenom').value, nom : byId('nc_nom').value, mail : byId('nc_mail').value }); } } // affiche un message d'erreur si l'utilisateur existe socket.on('utilisateur_existant', function() { byId('utilisateur_existant').style.display = 'block'; }); .. captionup:: ./static/js/profil.js .. code-block:: javascript /* change de classe bootstrap la balise
![]() |
<%= discussions[i][0].sujet %>
créé par <%= discussions[i][0].utilisateur_createur %> le <% convertir_date(discussions[i][0].date_ecriture); %> |
<% if (nom_utilisateur === 'session inexistante') { %>
<% include chargement_discussions %>
Nom d'utilisateur : <%= profil.nom_utilisateur %>
Prénom : <%= profil.prenom %>
Nom : <%= profil.nom %>
E-mail : <%= profil.email %>