Code-source¶
{
"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¶
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);
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');
});
};
// 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);
});
}
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');
});
});
});
});
}
/* 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¶
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;
}
}
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 = '<textarea id="'+Date.now()+'">' +
unechap(d.contenu)+'</textarea>';
zone_message('affichage', d).innerHTML = unechap(d.contenu);
});
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';
}
}
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';
}
}
}
// 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);
});
// 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);
});
// 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';
});
/* change de classe bootstrap la balise <div> contenant
les informations sur l'utilisateur pour que l'avatar puisse
pendre toute la largeur de l'écran */
function responsive() {
if (window.matchMedia("(max-width: 400px)").matches) {
byId('infos_profil').className = 'col-xs-12';
}
else {
byId('infos_profil').className = 'col-xs-8';
}
if (!window.matchMedia("(min-width: 399px)").matches) {
byId('infos_profil').className = 'col-xs-12';
}
else {
byId('infos_profil').className = 'col-xs-8';
}
}
// vérifie le formulaire de modification de données
// entoure les champs correspondants en vert
function verifier_form() {
verifier('prenom');
verifier('nom');
verifier('mail');
}
// affiche le formulaire de modification des coordonnées
function afficher_mod() {
byId('mod_password').style.display = 'none';
byId('mod_avatar').style.display = 'none';
if (byId('mod_coordonnees').style.display === 'none') {
byId('mod_coordonnees').style.display = 'block';
}
else {
byId('mod_coordonnees').style.display = 'none';
}
}
// affiche le formulaire de modification du mot de passe
function afficher_mod_password() {
byId('mod_coordonnees').style.display = 'none';
byId('mod_avatar').style.display = 'none';
if (byId('mod_password').style.display === 'none') {
byId('mod_password').style.display = 'block';
}
else {
byId('mod_password').style.display = 'none';
}
}
// affiche le choix d'avatars
function afficher_mod_avatar() {
byId('mod_password').style.display = 'none';
byId('mod_coordonnees').style.display = 'none';
if (byId('mod_avatar').style.display === 'none') {
byId('mod_avatar').style.display = 'block';
}
else {
byId('mod_avatar').style.display = 'none';
}
}
// envoie un événement socket pour modifier les coordonnées
function modifier_compte(utilisateur) {
if (verifier('prenom') &&
verifier('nom') &&
verifier('mail'))
{
socket.emit('modifier_compte', {
utilisateur : utilisateur,
prenom : byId('nc_prenom').value,
nom : byId('nc_nom').value,
mail : byId('nc_mail').value,
});
}
}
// envoie un événement socket pour modifier le mot de passe
function modifier_password(utilisateur) {
if (byId('nc_ancien_password').value !== '' && verifier('nouveau_password')) {
socket.emit('modifier_password', {
nom_utilisateur : utilisateur,
ancien_password : byId('nc_ancien_password').value,
nouveau_password : byId('nc_nouveau_password').value
});
}
}
// télécharge les petits avatars et les affiche sur la page
function avatars_small(nom_utilisateur) {
for (var i = 1; i < 101; i++) {
byId('mod_avatar').innerHTML +=
'<image src="static/avatars_petits/'+i+
'.jpg" onclick="changer_avatar('+i+', \''+nom_utilisateur+'\')">';
}
}
// change l'avatar d'un utilisateur sur la page et envoie un événement socket
function changer_avatar(n, nom_utilisateur) {
byClass('avatar_profil_desktop')[0].innerHTML =
'<image src="static/avatars_grands/'+n+'.jpg" class="avatar_profil"></image>';
byClass('avatar_profil_mobile')[0].innerHTML =
'<image src="static/avatars_grands/'+n+'.jpg" class="avatar_profil"></image>';
socket.emit('changement_avatar', {
avatar : n,
nom_utilisateur : nom_utilisateur
});
}
// affiche un message d'erreur - mot de passe incorrect
socket.on('ancien_password_incorrect', function() {
byId('form_nc_ancien_password').className = 'form-group has-error';
byId('ancien_password_incorrect').style.display = 'inline';
});
/* réinitialise les champs des mots de passe et affiche un message confirmant
la modification du mot de passe */
socket.on('mod_password_ok', function() {
byId('form_nc_ancien_password').className = 'form-group';
byId('nc_ancien_password').value = '';
byId('nc_nouveau_password').value = '';
byId('ancien_password_incorrect').style.display = 'none';
byId('mod_password').style.display = 'none';
verifier('nouveau_password');
byId('password_success').style.display = 'block';
byId('coordonnees_success').style.display = 'none';
});
/* actualise les nouvelles coordonnées et affiche un message confirmant
la modification des coordonnées chez l'utilisateur qui les a modifiées */
socket.on('mod_compte_ok', function(nouveau) {
byId('info_prenom').innerHTML = 'Prénom : ' + nouveau.prenom;
byId('info_nom').innerHTML = 'Nom : ' + nouveau.nom;
byId('info_email').innerHTML = 'E-mail : ' + nouveau.mail;
if (!nouveau.broadcast) {
byId('nc_prenom').value = nouveau.prenom;
byId('nc_nom').value = nouveau.nom;
byId('nc_prenom_mobile').value = nouveau.prenom;
byId('nc_nom_mobile').value = nouveau.nom;
byId('nc_mail').value = nouveau.mail;
byId('mod_coordonnees').style.display = 'none';
byId('password_success').style.display = 'none';
byId('coordonnees_success').style.display = 'block';
}
});
function get_regex(data) {
switch(data) {
case 'utilisateur':
return /^[a-z0-9]{4,10}$/i;
case 'password': case 'nouveau_password':
return /^.{4,20}$/i;
case 'prenom': case 'prenom_mobile': case 'nom': case 'nom_mobile':
return /^[a-zâäàéèùêëîïôöçñ\ ]{2,50}$/i;
case 'mail':
return /^[a-z0-9\.-_]+\@[a-z0-9\.-_]+\.[a-z]{2,10}$/i;
}
}
function get_html_class(data, type) {
if (data === 'prenom' || data === 'nom') {
switch(type) {
case 'success':
return 'has-success';
case 'nothing':
return '';
case 'error':
return 'has-error';
}
} else {
switch(type) {
case 'success':
return 'form-group has-success';
case 'nothing':
return 'form-group';
case 'error':
return 'form-group has-error';
}
}
}
function verifier(data, stop) {
var return_value;
if (get_regex(data).test(byId('nc_'+data).value)) {
byId('form_nc_'+data).className = get_html_class(data, 'success');
byId(data+'_incorrect').style.display = 'none';
return_value = true;
} else if (byId('nc_'+data).value === '') {
byId('form_nc_'+data).className = get_html_class(data, 'nothing');
byId(data+'_incorrect').style.display = 'none';
return_value = false;
} else {
byId('form_nc_'+data).className = get_html_class(data, 'error');
byId(data+'_incorrect').style.display = 'inline';
return_value = false;
}
if ((data === 'prenom' || data == 'nom') && !stop) {
byId('nc_'+data+'_mobile').value = byId('nc_'+data).value;
verifier(data+'_mobile', true);
}
if ((data === 'prenom_mobile' || data == 'nom_mobile') && !stop) {
byId('nc_'+data.replace(/_mobile/, '')).value = byId('nc_'+data).value;
verifier(data.replace(/_mobile/, ''), true);
}
return return_value;
}
@media(max-width:991px) {
.discussions {
padding-bottom: 15px;
}
}
@media(max-width:767px) {
.desktop {
display: none;
}
.avatar {
display: none;
}
.ligne {
position:relative;
bottom: 10px;
}
.sujet_discussion {
margin-bottom: 15px;
}
.nom_utilisateur {
position: relative;
bottom: 4px;
right: 3px;
}
.editeur {
margin-right: 10px;
}
.form-group {
padding-left: 17px;
}
.ligne {
margin-bottom: -8px;
}
}
@media(min-width: 768px) {
.mobile {
display: none;
}
.nom_utilisateur {
display: block;
text-align: center;
}
.ligne {
margin-bottom: 24px;
}
}
/*avatar du profil*/
.avatar_profil_mobile {
display: none;
}
@media(max-width: 572px) {
.avatar_profil {
max-width: 110%;
vertical-align: middle;
}
}
@media(min-width: 570px) {
#mod_avatar {
position: relative;
bottom: 25px;
}
}
@media(max-width: 500px) {
.avatar_profil {
position: relative;
top: 5px;
}
}
@media(max-width: 400px) {
.avatar_profil_desktop {
display: none;
}
.avatar_profil_mobile {
display: block;
position: relative;
bottom: 35px;
text-align: center;
}
}
body {
background-color: rgb(245, 245, 245);
}
.blanc {
background-color: rgb(255, 255, 255);
margin-top: -4px;
padding-top: 4px;
}
table {
position: relative;
top: -2px;
}
.sujet {
padding-top: 10px;
padding-bottom: 10px;
}
.sujet_texte {
font-size: 18px;
font-weight: bold;
position: relative;
top: -1px;
color:black;
}
.sujet_texte:hover {
text-decoration: underline;
color:black;
}
.infos_sujet {
position: relative;
top: 2px;
}
.nombre_messages {
position: relative;
top: 2px;
font-weight: bold;
}
.dm {
color: rgb(150,150,150);
}
.td_message {
padding-left: 10px;
padding-right: 10px;
}
.act_message {
vertical-align: text-top;
}
.btn {
margin-bottom: 10px;
}
.ecrire_message {
padding-top: 10px;
}
.help-block {
position: relative;
top: -8px;
}
.row {
position: relative;
top: -15px;
}
.modification_message {
min-height: 60px;
overflow-y: auto;
word-wrap:break-word;
}
.publication_message {
float:right;
position:relative;
top:5px;
}
.dates {
color: grey;
position: relative;
top: -3px;
}
#input_sujet {
width: 100%;
font-size: 20px;
margin-top: 10px;
text-align: center;
}
.zone_message {
max-height: 400px;
overflow: auto;
margin-right: -11px;
}
.message_erreur {
position: relative;
top: 2px;
margin-bottom: -15px;
display: none;
}
.gros_titre {
font-size:30px;
text-decoration:bold;
text-align:center;
margin-bottom: 25px;
}
.sujet_discussion {
font-size:30px;
text-decoration:bold;
text-align:center;
margin-bottom: 12px;
position: relative;
bottom: 5px;
}
.avatar {
max-width: 80%;
display: block;
margin-left: auto;
margin-right: auto;
}
.avatar_profil {
max-width:180px;
margin-top: 5px;
position: relative;
bottom: 20px;
}
.ligne {
border-top: solid 3px rgb(235,235,235);
margin-left: -15px;
margin-right: -15px;
}
.btn-nouveau_compte {
margin-bottom:4px
margin-top:-15px;
position: relative;
bottom: 9px;
}
#mod_coordonnees, #mod_password {
padding-right:20px;
display:none;
}
.editeur {
margin-top: 10px;
}
HTML et EJS¶
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Forum tm 15-16</title>
<link rel="stylesheet" href="static/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="static/style.css">
<link rel="stylesheet" href="static/responsive.css">
<script src="/socket.io/socket.io.js"></script>
<script src="static/tinymce/tinymce.min.js"></script>
<script src="static/js/general.js"></script>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<nav class="navbar navbar-default" id="navbar" style="height:50px;">
<div class="navbar-header">
<a class="navbar-brand" href="/">Forum</a>
<a href="#"
class="navbar-toggle"
onclick="navbar_mobile();">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
</div>
<div class="navbar-collapse collapse" id="contenu_nav">
<ul class="nav navbar-nav">
<li><a href="/">Accueil</a></li>
</ul>
<% if (nom_utilisateur !== 'session inexistante') { %>
<form class="navbar-form navbar-left">
<a href="new" class="btn btn-default">
<span class="glyphicon glyphicon-pencil"></span>
Nouvelle discussion
</a>
</form>
<% } %>
<ul class="nav navbar-nav navbar-right" style="padding-right:15px;">
<li>
<% if (nom_utilisateur !== 'session inexistante') { %>
<li>
<a href="profil_<%= nom_utilisateur %>" style="color:black;">
<%= nom_utilisateur %>
</a>
</li>
<% } %>
<% if (nom_utilisateur === 'session inexistante') { %>
<a href="nouveau_compte">
Créer un compte
</a>
<% } %>
<li>
<% if (nom_utilisateur === 'session inexistante') { %>
<a href="login">
Login
</a>
<% } else { %>
<a href="logout">
Logout
</a>
<% } %>
</li>
</ul>
</div>
</nav>
<% convertir_date = function(date) {
var id_temp = Math.random().toString(36).substring(7); %>
<span id="<%= id_temp %>"></span>
<script>convertir_date("<%= date %>","<%= id_temp %>");</script>
<% }
for (var i = 0; i < discussions.length; i++) { %>
<div class="sujet discussion" id="<%= discussions[i][0].id_discussion %>">
<table>
<tr>
<td style="width:50px;">
<img src="static/avatar.png" style="max-width:75%;">
</td>
<td>
<a href="<%= discussions[i][0].id_discussion %>" class="sujet_texte">
<%= discussions[i][0].sujet %>
</a><br>
<span class="infos_sujet">
créé par <a href="profil_<%= discussions[i][0].utilisateur_createur %>">
<%= discussions[i][0].utilisateur_createur %>
</a> le
<% convertir_date(discussions[i][0].date_ecriture); %>
</span>
<span class="nombre_messages">
<%= discussions[i][0].nombre_messages %>
<% if (discussions[i][0].nombre_messages === 1) { %> message <% } %>
<% if (discussions[i][0].nombre_messages > 1) { %> messages <% } %>
</span>
</td>
</tr>
</table>
</div>
<% } %>
<!-- cette fonction sert à lancer la conversion d'une date formatée par mysql.
elle utilise le fichier conversion_dates.js -->
<% convertir_date = function(date) {
var id_temp = Math.random().toString(36).substring(7); %>
<span id="<%= id_temp %>"></span>
<script>convertir_date("<%= date %>","<%= id_temp %>");</script>
<% }
<!-- cette boucle parcourt tous les messages et applique à chacun un formattage -->
for (var i = 0; i < m.length; i++) { %>
<!-- les lignes suivantes traitent quelques caractères spéciaux (' " \)
qui créent des bugs s'ils ne sont pas "encodés" -->
<% m[i].contenu = m[i].contenu
.replace(/#g_doubles/g, '"')
.replace(/#g_simples/g, "'")
.replace(/#backslash/g, "\\"); %>
<!-- cet attribut ID permet de sélectionner plus facilement
et supprimer les messages -->
<div id="<%= m[i].id_message %>">
<p class="ligne"></p>
<div class="row">
<div class="col-sm-2 zone_utilisateur">
<image src="static/avatars_grands/<%= m[i].avatar %>.jpg" class="avatar"><br>
<div>
<a href="profil_<%= m[i].nom_utilisateur %>" class="nom_utilisateur">
<%= m[i].nom_utilisateur %>
</a>
<!-- affichage sur les appareils dont la largeur de la fenêtre est petite -->
<span class="mobile">
<a onclick="aimer(<%= m[i].id_message %>, '<%= nom_utilisateur %>')"
class="btn btn-sm btn-success">
<span class="glyphicon glyphicon-heart"></span>
<span class="likes">
<%= m[i].likes %>
</span>
</a>
<!-- les boutons suivants s'affichent seulement si l'utilisateur connecté
a écrit le message -->
<% if (nom_utilisateur === m[i].nom_utilisateur) { %>
<!-- bouton "modifier" -->
<span id="<%= m[i].id_message %>_bouton_modifier_mobile">
<span onclick="modifier_message(<%= m[i].id_message %>)"
class="btn btn-sm btn-default">
<span class="glyphicon glyphicon-pencil"></span>
modifier
</span>
</span>
<!-- bouton "enregistrer", qui s'affichera après un clic sur "modifier" -->
<span id="<%= m[i].id_message %>_bouton_enregistrer_mobile"
style="display: none;">
<span onclick="enregistrer_message(<%= m[i].id_message %>)"
class="btn btn-sm btn-default">
<span class="glyphicon glyphicon-pencil"></span>
enregistrer
</span>
</span>
<span>
<!-- bouton "supprimer" -->
<span onclick="supprimer_message(<%= m[i].id_message %>)"
class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-remove"></span>
supprimer
</span><br>
</span>
<% } %>
</span>
</div>
<center>
<!-- affichage sur les appareils dont la largeur de la fenêtre
est plus grande -->
<span class="desktop">
<a onclick="aimer(<%= m[i].id_message %>, '<%= nom_utilisateur %>')"
class="btn btn-sm btn-success"
style="margin-top: 5px;">
<span class="glyphicon glyphicon-heart"></span>
<span class="likes">
<%= m[i].likes %>
</span>
</a><br>
</span>
</center>
</div>
<!-- si les boutons s'affichent, la balise prend la classe bootstrap col-sm-8 -->
<% if (nom_utilisateur === m[i].nom_utilisateur) { %>
<div class="td_message col-sm-8">
<% } else { %>
<!-- dnas le cas contraire, on peut se permettre
d'avoir une zone de texte plus grande -->
<div class="td_message col-sm-10">
<% } %>
<div class="zone_message">
<div class="dates">
<span class="date_ecriture">
écrit le <% convertir_date(m[i].date_ecriture); %><br>
</span>
<span class="date_modification">
<% if (m[i].date_modification !== '0000-00-00 00:00:00') { %>
modifié le <% convertir_date(m[i].date_modification); %><br>
<% } %>
</span>
</div>
<div class="editeur mod_message" style="display: none;">
<!-- la textarea contient un attribut ID généré au hasard
pour permettre à TinyMCE de générer l'éditeur plusieurs fois
(nous verrons ceci lorsque nous nous occuperons d'afficher TinyMCE) -->
<textarea
id="<%= m[i].id_message + Math.random().toString(36).substring(7); %>">
<%= m[i].contenu %>
</textarea>
</div>
<span class="affichage">
<%- m[i].contenu %>
</span>
</div>
</div>
<% if (nom_utilisateur === m[i].nom_utilisateur) { %>
<!-- affichage des boutons sur les fenêtres plus larges -->
<div class="desktop">
<!-- l'attribut "style" force le texte à s'afficher en haut -->
<div class="col-sm-2" style="vertical-align: text-top;">
<center>
<!-- bouton "modifier" -->
<div id="<%= m[i].id_message %>_bouton_modifier">
<span onclick="modifier_message(<%= m[i].id_message %>)"
class="btn btn-sm btn-default">
<span class="glyphicon glyphicon-pencil"></span>
modifier
</span><br>
</div>
</center>
<center>
<!-- bouton "enregistrer" -->
<div id="<%= m[i].id_message %>_bouton_enregistrer"
style="display: none;">
<span onclick="enregistrer_message(<%= m[i].id_message %>)"
class="btn btn-sm btn-default">
<span class="glyphicon glyphicon-pencil"></span>
enregistrer
</span><br>
</div>
</center>
<center>
<div>
<!-- bouton "supprimer" -->
<span onclick="supprimer_message(<%= m[i].id_message %>)"
class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-remove"></span>
supprimer
</span><br>
</div>
</center>
</div>
</div>
<% } %>
</div>
</div>
<% } %>
<!DOCTYPE html>
<html lang="fr">
<head>
<% include balise_head %>
<script src="static/js/conversion_dates.js"></script>
<script src="static/js/discussion.js"></script>
</head>
<body>
<% include barre_navigation %>
<div class="container">
<div class="row">
<div class="col-lg-offset-2 col-md-offset-1 col-md-10 col-lg-8
blanc discussions">
<p>
<% if (nom_utilisateur === 'session inexistante') { %>
<div class="alert alert-info" style="text-align:justify">
Vous n'êtes actuellement pas connecté.
Par conséquent, vous ne pouvez pas aimer ou publier des messages.
<a href="login">
Connectez-vous
</a>
pour participer à la discussion.
</div>
<% } %>
</p>
<div class="sujet_discussion">
<%= sujet_discussion %>
</div>
<div id="messages">
<% include chargement_messages %>
</div>
<% if (nom_utilisateur !== 'session inexistante') { %>
<% include editeur %>
<% } %>
</div>
<div class="col-lg-offset-2 col-md-offset-1"></div>
</div>
</div>
</body>
<html>
<p class="editeur">
<textarea id="emplacement_nouveau_message" class="form-control">
</textarea>
<script>
init_editeur('emplacement_nouveau_message');
</script>
<div class="help-block">Vous pouvez agrandir la fenêtre
<span onclick="publier_message('<%= nom_utilisateur %>');"
class="btn btn-primary publication_message" >
Publier
</span>
</div>
</p>
Cette discussion n'existe pas
Cette page n'existe pas
<div class="row">
<div class="form-group" id="form_nc_utilisateur">
<label for="nc_utilisateur" class="col-lg-3 col-sm-3 control-label">
Nom d'utilisateur
</label>
<div class="col-lg-9 col-sm-9">
<input type="text" class="form-control" id="nc_utilisateur"
onkeyup="verifier('utilisateur');">
<span class="help-block message_erreur" id="utilisateur_incorrect">
Le nom d'utilisateur doit contenir au minimum 4 caractères,
au maximum 10 caractères, et peut être uniquement composé de chiffres
et de lettres non-accentuées.
</span>
</div>
</div>
</div>
<div class="row desktop">
<div class="form-group">
<div id="form_nc_prenom">
<label for="nc_prenom" class="col-sm-3 control-label">Prénom</label>
<div class="col-lg-4 col-sm-4">
<input type="text" class="form-control" id="nc_prenom"
onkeyup="verifier('prenom');">
<span class="help-block message_erreur" id="prenom_incorrect">
Ce prénom est invalide.
</span>
</div>
</div>
<div id="form_nc_nom">
<label for="nc_nom" class="col-sm-1 control-label">Nom</label>
<div class="col-lg-4 col-sm-4">
<input type="text" class="form-control" id="nc_nom"
onkeyup="verifier('nom');">
<span class="help-block message_erreur" id="nom_incorrect">
Ce nom est invalide.
</span>
</div>
</div>
</div>
</div>
<div class="row mobile">
<div class="form-group" id="form_nc_prenom_mobile">
<label for="nc_prenom_mobile" class="col-sm-4 control-label">
Prénom
</label>
<div class="col-lg-8 col-sm-8">
<input type="text" class="form-control" id="nc_prenom_mobile"
onkeyup="verifier('prenom_mobile');">
<span class="help-block message_erreur" id="prenom_mobile_incorrect">
Ce prénom est invalide.
</span>
</div>
</div>
</div>
<div class="row mobile">
<div class="form-group" id="form_nc_nom_mobile">
<label for="nc_nom_mobile" class="col-sm-4 control-label">Nom</label>
<div class="col-lg-8 col-sm-8">
<input type="text" class="form-control" id="nc_nom_mobile"
onkeyup="verifier('nom_mobile');">
<span class="help-block message_erreur" id="nom_mobile_incorrect">
Ce nom est invalide.
</span>
</div>
</div>
</div>
<div class="row">
<div class="form-group" id="form_nc_mail">
<label for="nc_mail" class="col-lg-2 col-sm-2 control-label">Mail</label>
<div class="col-lg-10 col-sm-10">
<input type="text" class="form-control" id="nc_mail"
onkeyup="verifier('mail');">
<span class="help-block message_erreur" id="mail_incorrect">
Cette adresse e-mail est invalide.
</span>
</div>
</div>
</div>
<div class="row">
<div class="form-group" id="form_nc_password">
<label for="nc_password" class="col-lg-3 col-sm-3 control-label">
Mot de passe
</label>
<div class="col-lg-9 col-sm-9">
<input type="password" class="form-control" id="nc_password"
onkeypress="verifier_enter(event, this, nouveau_compte);"
onkeyup="verifier('password');">
<span class="help-block message_erreur" id="password_incorrect">
Le mot de passe doit contenir au minimum 4 caractères,
au maximum 20 caractères, et peut être uniquement
composé de chiffres et de lettres.
</span>
</div>
</div>
</div>
<form class="form-horizontal" id="mod_coordonnees">
<div class="form-group">
<h4 class="gros_titre">Modifier les coordonnées</h4>
</div>
<div class="row desktop">
<div class="form-group">
<div id="form_nc_prenom">
<label for="nc_prenom" class="col-sm-3 control-label">Prénom</label>
<div class="col-lg-4 col-sm-4">
<input type="text" class="form-control" id="nc_prenom"
onkeyup="verifier('prenom');">
<span class="help-block message_erreur" id="prenom_incorrect">
Ce prénom est invalide.
</span>
</div>
</div>
<div id="form_nc_nom">
<label for="nc_nom" class="col-sm-1 control-label">Nom</label>
<div class="col-lg-4 col-sm-4">
<input type="text" class="form-control" id="nc_nom"
onkeyup="verifier('nom');">
<span class="help-block message_erreur" id="nom_incorrect">
Ce nom est invalide.
</span>
</div>
</div>
</div>
</div>
<div class="row mobile">
<div class="form-group" id="form_nc_prenom_mobile">
<label for="nc_prenom_mobile" class="col-sm-4 control-label">Prénom</label>
<div class="col-lg-8 col-sm-8">
<input type="text" class="form-control" id="nc_prenom_mobile"
onkeyup="verifier('prenom_mobile');">
<span class="help-block message_erreur" id="prenom_mobile_incorrect">
Ce prénom est invalide.
</span>
</div>
</div>
</div>
<div class="row mobile">
<div class="form-group" id="form_nc_nom_mobile">
<label for="nc_nom_mobile" class="col-sm-4 control-label">Nom</label>
<div class="col-lg-8 col-sm-8">
<input type="text" class="form-control" id="nc_nom_mobile"
onkeyup="verifier('nom_mobile');">
<span class="help-block message_erreur" id="nom_mobile_incorrect">
Ce nom est invalide.
</span>
</div>
</div>
</div>
<div class="row">
<div class="form-group" id="form_nc_mail">
<label for="nc_mail" class="col-lg-3 col-sm-3 control-label">Mail</label>
<div class="col-lg-9 col-sm-9">
<input type="text" class="form-control" id="nc_mail"
onkeyup="verifier('mail');">
<span class="help-block message_erreur" id="mail_incorrect">
Cette adresse e-mail est invalide.
</span>
</div>
</div>
</div>
<div class="form-group">
<span class="pull-right btn btn-primary btn-nouveau_compte"
onclick="modifier_compte('<%= profil.nom_utilisateur %>');">
Enregistrer
</span>
</div>
</form>
<form class="form-horizontal" id="mod_password">
<div class="form-group">
<h4 class="gros_titre">Modifier le mot de passe</h4>
</div>
<div class="row">
<div class="form-group" id="form_nc_ancien_password">
<label for="form_nc_ancien_password" class="col-lg-3 col-sm-4 control-label">
Ancien mot de passe
</label>
<div class="col-lg-9 col-sm-8">
<input type="password" class="form-control" id="nc_ancien_password">
<span class="help-block message_erreur" id="ancien_password_incorrect">
Le mot de passe est incorrect.
</span>
</div>
</div>
</div>
<div class="row">
<div class="form-group" id="form_nc_nouveau_password">
<label for="form_nc_nouveau_password" class="col-lg-3 col-sm-4 control-label">
Nouveau mot de passe
</label>
<div class="col-lg-9 col-sm-8">
<input type="password" class="form-control" id="nc_nouveau_password"
onkeyup="verifier_nouveau_password();">
<span class="help-block message_erreur" id="nouveau_password_incorrect">
Le mot de passe doit contenir au minimum 4 caractères,
au maximum 20 caractères, et peut être uniquement
composé de chiffres et de lettres.
</span>
</div>
</div>
</div>
<div class="form-group">
<span class="pull-right btn btn-primary btn-nouveau_compte"
onclick="modifier_password('<%= profil.nom_utilisateur %>');">
Enregistrer
</span>
</div>
</form>
<!DOCTYPE html>
<html>
<head>
<% include balise_head %>
<script src="static/js/index.js"></script>
<script src="static/js/conversion_dates.js"></script>
</head>
<body>
<% include barre_navigation %>
<div class="container">
<div class="row">
<div class="col-lg-offset-2 col-md-offset-1 col-md-10 col-lg-8 blanc">
<p style="padding-top:6px;">
<input
id="recherche_discussion"
placeholder="rechercher une discussion"
onkeyup="rechercher();"
class="form-control">
</input>
</p>
<p>
<span id="chargement_discussions">
<% include chargement_discussions %>
</span>
<span id="retour_ajax"></span>
</p>
<div class="col-lg-offset-2 col-md-offset-1"></div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="fr">
<head>
<% include balise_head %>
<script src="static/js/login.js"></script>
</head>
<body>
<% include barre_navigation %>
<div class="container">
<div class="row">
<div class="col-lg-offset-2 col-md-offset-1 col-md-10 col-lg-8
blanc corps_page">
<center>
<div class="alert alert-info">
Vous n'avez pas encore de compte ?
<a href="nouveau_compte">Inscrivez-vous</a> maintenant !
</div>
</center>
<form class="form-horizontal" style="padding-right:20px;">
<div class="form-group">
<h4 class="gros_titre">Se connecter</h4>
</div>
<div class="row">
<div class="form-group" id="form_login_utilisateur">
<label for="login_utilisateur"
class="col-lg-3 col-sm-4 control-label">
Nom d'utilisateur
</label>
<div class="col-lg-9 col-sm-8">
<input type="text" class="form-control" id="login_utilisateur">
<span class="help-block message_erreur"
id="login_incorrect"
style="position: relative; top: 6px;">
Le nom d'utilisateur ou le mot de passe est incorrect.
</span>
</div>
</div>
</div>
<div class="row">
<div class="form-group" id="form_login_password">
<label for="login_password" class="col-lg-3 col-sm-4 control-label">
Mot de passe
</label>
<div class="col-lg-9 col-sm-8">
<input type="password"
class="form-control"
id="login_password"
onkeypress="verifier_enter(event, this, login);">
</div>
</div>
</div>
<div class="form-group">
<span
class="pull-right btn btn-primary"
style="margin-top: -8px; margin-bottom: 16px;"
onclick="login();">
Login
</span>
</div>
</form>
<div class="col-lg-offset-2 col-md-offset-1"></div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="fr">
<head>
<% include balise_head %>
<script src="static/js/new.js"></script>
</head>
<body>
<% include barre_navigation %>
<div class="container">
<div class="row">
<div class="col-lg-offset-2 col-md-offset-1 col-md-10 col-lg-8
blanc corps_page">
<% if (nom_utilisateur !== 'session inexistante') { %>
<input type="text" id="input_sujet" class="e"
placeholder="Sujet de la discussion">
<% include editeur %>
<% } else { %>
<script>
window.location.assign('login');
</script>
<% } %>
</div>
<div class="col-lg-offset-2 col-md-offset-1"></div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<% include balise_head %>
<script src="static/js/login.js"></script>
<script src="static/js/nouveau_compte.js"></script>
<script src="static/js/verification_form.js"></script>
</head>
<body>
<% include barre_navigation %>
<div class="container">
<div class="row">
<div class="col-lg-offset-2 col-md-offset-1 col-md-10 col-lg-8 blanc">
<center class="alert alert-danger" id="utilisateur_existant"
style="display: none;">
Ce nom d'utilisateur existe déjà. Veuillez en choisir un autre.
</center>
<form class="form-horizontal" style="padding-right:20px;">
<div class="form-group">
<h4 class="gros_titre">Créer un compte</h4>
</div>
<% include form_nouveau_compte %>
<div class="form-group">
<span class="pull-right btn btn-primary btn-nouveau_compte"
onclick="nouveau_compte();">
Créer un compte
</span>
</div>
</form>
<div class="col-lg-offset-2 col-md-offset-1"></div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="fr">
<% if (!profil) { %>
<script>
window.location.assign('error');
</script>
<% } else { %>
<head>
<% include balise_head %>
<script src="static/js/verification_form.js"></script>
<script src="static/js/profil.js"></script>
<script> // ces fonctions javascript ont besoin de variables EJS
function inserer_form() {
byId('nc_prenom').value = '<%= profil.prenom %>';
byId('nc_nom').value = '<%= profil.nom %>';
byId('nc_prenom_mobile').value = '<%= profil.prenom %>';
byId('nc_nom_mobile').value = '<%= profil.nom %>';
byId('nc_mail').value = '<%= profil.email %>';
}
</script>
</head>
<body>
<% include barre_navigation %>
<div class="container">
<div class="row">
<div class="col-lg-offset-2 col-md-offset-1 col-md-10 col-lg-8
blanc corps_page">
<div class="alert alert-success" style="text-align:center; display:none"
id="coordonnees_success">
Les coordonnées ont été modifiées avec succès
</div>
<div class="alert alert-success" style="text-align:center; display:none"
id="password_success">
Le mot de passe a été modifié avec succès
</div>
<h4 class="gros_titre">Profil</h4>
<div class="avatar_profil_mobile">
<img src="static/avatars_grands/<%= profil.avatar %>.jpg"
class="avatar_profil">
</div>
<div class="row">
<div class="col-xs-4 avatar_profil_desktop">
<img src="static/avatars_grands/<%= profil.avatar %>.jpg"
class="avatar_profil"><br>
</div>
<div class="col-xs-8" id="infos_profil">
<p style="font-size:16px; margin-top:15px;">
<span id="info_nom_utilisateur">
Nom d'utilisateur : <%= profil.nom_utilisateur %>
</span><br>
<span id="info_prenom">
Prénom : <%= profil.prenom %>
</span><br>
<span id="info_nom">
Nom : <%= profil.nom %>
</span><br>
<span id="info_email">
E-mail : <%= profil.email %>
</span><br>
</p>
<% if (nom_utilisateur === profil.nom_utilisateur) { %>
<span class="btn btn-default" onclick="afficher_mod();">
<span class="glyphicon glyphicon-pencil"></span>
Coordonnées
</span>
<span class="btn btn-default" onclick="afficher_mod_password();">
<span class="glyphicon glyphicon-pencil"></span>
Mot de passe
</span>
<span class="btn btn-default" onclick="afficher_mod_avatar();">
<span class="glyphicon glyphicon-pencil"></span>
Avatar
</span>
<% } else { %>
<a class="btn btn-primary" href="mailto:<%= profil.email %>">
<span class="glyphicon glyphicon-pencil"></span>
Envoyer un courriel
</a>
<% } %>
</div>
</div>
<% include form_profil %>
<div id="mod_avatar" style="display:none;">
<script> avatars_small('<%= nom_utilisateur %>'); </script>
</div>
</div>
<div class="col-lg-offset-2 col-md-offset-1"></div>
</div>
</div>
<script> /* ces fonctions doivent attendre que la page soit chargée
pour être exécutées */
responsive();
inserer_form();
verifier_form();
afficher_mod();
</script>
</body>
<% } %>
<html>