From 8ee1535f5594573931ddaebee77bf6148a5358cb Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Mon, 6 Oct 2008 08:21:00 +0000 Subject: [PATCH 1/1] REPORT de la branche 1.1 (459->476) --- js/betterjs.js | 1 + js/client.js | 6 +- js/formateur.js | 4 +- js/fragment.js | 8 +- js/pageMinichat/commandes.js | 41 +++- js/pageMinichat/conversation.js | 43 +++- js/pageMinichat/conversations.js | 6 +- js/pageMinichat/pageMinichat.js | 32 ++- js/util.js | 18 +- modules/Makefile | 6 +- modules/erl/euphorik_bd.erl | 116 +++++------ modules/erl/euphorik_bd_admin.erl | 194 +++++++++++++----- .../erl/euphorik_minichat_conversation.erl | 53 +++-- modules/erl/euphorik_protocole.erl | 2 +- modules/erl/euphorik_test.erl | 54 +++-- modules/include/euphorik_bd.hrl | 12 +- styles/1/euphorik.css | 10 + styles/1/pageMinichat.css | 20 ++ styles/2/euphorik.css | 9 + styles/2/pageMinichat.css | 12 ++ tools/mise_en_prod.erl | 40 +++- tools/start_yaws.sh | 3 +- tools/tools.rb | 47 +++-- 23 files changed, 488 insertions(+), 249 deletions(-) diff --git a/js/betterjs.js b/js/betterjs.js index 69788d8..d982030 100644 --- a/js/betterjs.js +++ b/js/betterjs.js @@ -1,3 +1,4 @@ +// coding: utf-8 // tout un tas d'améliorations de JavaScript ;) diff --git a/js/client.js b/js/client.js index 49f7749..c378075 100644 --- a/js/client.js +++ b/js/client.js @@ -26,7 +26,7 @@ euphorik.Client = function(util, communication) { this.communication = communication; this.cookie = null; - this.regexCookie = /^cookie=([^;]*)/; + this.regexCookie = /cookie=([^;]*)/; // données personnels this.resetDonneesPersonnelles(); @@ -295,7 +295,7 @@ euphorik.Client.prototype.getJSONEnregistrement = function(login, password) { }; /** - * Connexion. Réalisée de manière synchrone. + * Connexion. Réalisé de manière synchrone. */ euphorik.Client.prototype.connexion = function(action, messageJson) { var thisClient = this; @@ -369,7 +369,7 @@ euphorik.Client.prototype.flush = function(async) { if (!this.authentifie()) { return false; } - + var thisClient = this; var ok = true; diff --git a/js/formateur.js b/js/formateur.js index 4b74d44..eba6c5a 100644 --- a/js/formateur.js +++ b/js/formateur.js @@ -28,7 +28,7 @@ euphorik.Formateur = function() { this.regexUrl = new RegExp("(?:(?:" + this.protocoles + ")://|www\\.)[^ ]*", "gi"); this.regexImg = new RegExp("^.*?\\.(gif|jpg|png|jpeg|bmp|tiff)$", "i"); - this.regexDomaine = new RegExp("^(?:(?:" + this.protocoles + ")://|www\\.).*?([^/.]+\\.[^/.]+)(?:$|/).*$", "i"); + this.regexDomaine = new RegExp("^(?:(?:" + this.protocoles + ")://)(.*?)(?:$|/).*$", "i"); this.regexTestProtocoleExiste = new RegExp("^(?:" + this.protocoles + ")://.*$", "i"); this.regexNomProtocole = new RegExp("^(.*?)://"); }; @@ -124,7 +124,7 @@ euphorik.Formateur.prototype.traiterWikiSyntaxe = function(m) { /** * Renvoie une version courte de l'url. - * par exemple : http://en.wikipedia.org/wiki/Yakov_Smirnoff devient wikipedia.org + * par exemple : http://en.wikipedia.org/wiki/Yakov_Smirnoff devient en.wikipedia.org */ euphorik.Formateur.prototype.getShort = function(url) { var estUneImage = false; diff --git a/js/fragment.js b/js/fragment.js index ef087f4..4ffbf8f 100644 --- a/js/fragment.js +++ b/js/fragment.js @@ -21,12 +21,18 @@ */ Fragment = function() { var thisFragment = this; + // remplacement des codes de type %22 (en hexa) + var replaceHtmlCode = function(str) { + return str.replace(/%(\d\d)/g, function(text, code) { + return String.fromCharCode(parseInt(code, 16)); + }); + }; this.fragments = {}; if (!window.location.hash) { return; } try { - var fragmentsStr = window.location.hash.slice(1).split(";"); + var fragmentsStr = replaceHtmlCode(window.location.hash.slice(1)).split(";"); fragmentsStr.each(function(i, tuple) { tuple = tuple.split("="); thisFragment.fragments[tuple[0]] = JSON.parse(tuple[1]); diff --git a/js/pageMinichat/commandes.js b/js/pageMinichat/commandes.js index f49ce9a..014b9bb 100644 --- a/js/pageMinichat/commandes.js +++ b/js/pageMinichat/commandes.js @@ -27,14 +27,30 @@ * /nick * Modifie le pseudo courant */ -euphorik.Commandes = function(client) { +euphorik.Commandes = function(client, pageMinichat, util, formateur) { + var thisCommandes = this; + this.client = client; + this.pageMinichat = pageMinichat; + this.util = util; + this.formateur = formateur; + + // construction du texte d'aide (liste des commandes) de manière statique + this.texteAide = "

Commandes

    "; + objectEach( + euphorik.Commandes.liste, + function(nom, commande) { + thisCommandes.texteAide += "
  • " + thisCommandes.formateur.traitementComplet(commande.usage) + " : " + thisCommandes.formateur.traitementComplet(commande.description) + "
  • "; + } + ); + this.texteAide += "
"; }; euphorik.Commandes.statut = {ok : 0, pas_une_commande : 1, erreur_commande : 2}; euphorik.Commandes.liste = { "nick" : { + description : "Change le pseudo courant", usage : "/nick ", exec : function(args, client) { @@ -47,7 +63,15 @@ euphorik.Commandes.liste = { return [euphorik.Commandes.statut.ok, '']; } - } + }, + "cpf" : { + description : "Envoie le message \"C'est pas faux\"", + usage : "/cpf", + exec : function(args, client, pageMinichat) { + pageMinichat.envoyerMessage("C'est pas faux"); + return [euphorik.Commandes.statut.ok, '']; + } + } }; /** @@ -68,9 +92,20 @@ euphorik.Commandes.prototype.exec = function(chaine) { if (nomCommande === "") { return [euphorik.Commandes.statut.erreur_commande, 'La commande est vide']; } + // commandes spéciales pour afficher l'aide : "?", "h", "help", "aide" + if (nomCommande === "?" || nomCommande === "h" || nomCommande === "help" || nomCommande === "aide") { + this.util.messageDialogue( + this.texteAide, + euphorik.Util.messageType.informatif, + {"fermer" : function(){}}, + false, + -1 + ); + return [euphorik.Commandes.statut.ok, '']; + } if (euphorik.Commandes.liste.hasOwnProperty(nomCommande)) { - return euphorik.Commandes.liste[nomCommande].exec(args, this.client); + return euphorik.Commandes.liste[nomCommande].exec(args, this.client, this.pageMinichat); } return [euphorik.Commandes.statut.erreur_commande, 'La commande /' + nomCommande + ' est inconnue']; diff --git a/js/pageMinichat/conversation.js b/js/pageMinichat/conversation.js index 2810b1b..acd7d67 100644 --- a/js/pageMinichat/conversation.js +++ b/js/pageMinichat/conversation.js @@ -244,15 +244,22 @@ euphorik.Conversation.prototype.flush = function() { var messagePair = (this.idDernierMessageAffiche === 0 ? true : ($("#" + this.getId() + " .messages div:" + (reverse ? "first" : "last")).attr("class").search("messagePair") === -1) - ); + ); + + // permet d'itérer sur les nouveaux messages à afficher + var pourChaqueNouveauMessage = function(f) { + thisConversation.messages.each(function(i, mess) { + if (mess.id > thisConversation.idDernierMessageAffiche) { + f(mess); + } + }); + }; // construction de l'XHTML des messages - var XHTML = ""; - this.messages.each(function(i, mess) { - if (mess.id > thisConversation.idDernierMessageAffiche) { - XHTML += mess.XHTML(messagePair, thisConversation.getId()); - messagePair = !messagePair; - } + var XHTML = ""; + pourChaqueNouveauMessage(function(mess) { + XHTML += mess.XHTML(messagePair, thisConversation.getId()); + messagePair = !messagePair; }); var DOM = $(XHTML); @@ -274,11 +281,25 @@ euphorik.Conversation.prototype.flush = function() { } else { $("#" + this.getId() + " .messages .message").slice(0, nbMessagesAffiche - this.nbMessageMax).remove(); } - } + } + + // met à jour la classe des messages auquels repondent les nouveaux messages + // dans le cas où ce message appartient au client courant (c'est un peu de la triche) TODO : ya mieux ? + pourChaqueNouveauMessage(function(mess) { + if (mess.auteurId === thisConversation.client.id) { + objectEach(mess.repondA, function(messId) { + var mess = thisConversation.messagesParId[messId]; + if (mess) { + mess.clientARepondu = true; + $("#conversations #" + mess.getId(thisConversation.getId())).addClass("repondu") + } + }); + } + }); if (this.messages.length > 0) { this.idDernierMessageAffiche = this.messages[this.messages.length-1].id; - } + } // met à jour la racine de la conversation this.majRacine(); @@ -326,7 +347,7 @@ euphorik.Conversation.prototype.attacherEventsSurMessage = function(element) { // donne le focus à la ligne de saisie $("form input.message").focus(); }).hover(function() { // affiche les outils liées au message - var top = $(this).offset().top + var top = $(this).offset().top; var left = $(this).offset().left + $(this).outerWidth() - thisConversation.util.outilsMessage.outerWidth(); $(".extraire", thisConversation.util.outilsMessage).unbind(); $(".extraireCompletement", thisConversation.util.outilsMessage).unbind(); @@ -336,7 +357,7 @@ euphorik.Conversation.prototype.attacherEventsSurMessage = function(element) { thisConversation.util.infoBulle("Extraction de la conversation complète", $(".extraireCompletement", thisConversation.util.outilsMessage)); thisConversation.util.outilsMessage.css("top", top).css("left", left).prependTo(this).show(); }, function() { - thisConversation.util.outilsMessage.hide() + thisConversation.util.outilsMessage.hide(); }); // mise en évidence de la conversation diff --git a/js/pageMinichat/conversations.js b/js/pageMinichat/conversations.js index b99e092..94236c1 100644 --- a/js/pageMinichat/conversations.js +++ b/js/pageMinichat/conversations.js @@ -63,11 +63,11 @@ euphorik.Conversations.prototype.toggleMessageRepond = function(mess) { euphorik.Conversations.prototype.mettreAJourFragment = function() { conv = []; for(var i = 1; i < this.conversations.length; i++) { - conv.push(this.conversations[i].racine.id) + conv.push(this.conversations[i].racine.id); } this.fragment.setVal("conv", conv); this.fragment.write(); -} +}; /** * Enlève tous les messages auquel l'utilisateur souhaite répondre. @@ -195,7 +195,7 @@ euphorik.Conversations.prototype.getJSONrafraichirMessages = function() { }; euphorik.Conversations.prototype.getJSONConversations = function() { - var thisConversations = this + var thisConversations = this; var clientConv = []; this.client.conversations.each(function(i, conv) { diff --git a/js/pageMinichat/pageMinichat.js b/js/pageMinichat/pageMinichat.js index f11dea4..b71efea 100755 --- a/js/pageMinichat/pageMinichat.js +++ b/js/pageMinichat/pageMinichat.js @@ -25,7 +25,7 @@ euphorik.PageMinichat = function(client, formateur, util, communication) { this.formateur = formateur; this.util = util; this.communication = communication; - this.commandes = new euphorik.Commandes(this.client); + this.commandes = new euphorik.Commandes(this.client, this, this.util, this.formateur); // permet d'éviter d'envoyer plusieurs messages simultanément en pressant // rapidement sur "enter" par exemple @@ -163,7 +163,7 @@ euphorik.PageMinichat.prototype.charger = function() { var retCommandes = thisPage.commandes.exec(message); switch (retCommandes[0]) { case euphorik.Commandes.statut.pas_une_commande : - thisPage.envoyerMessage($("form#posterMessage input.pseudo").val(), message); + thisPage.envoyerMessage(message); break; case euphorik.Commandes.statut.erreur_commande : thisPage.util.messageDialogue(retCommandes[1], euphorik.Util.messageType.erreur); @@ -213,18 +213,23 @@ euphorik.PageMinichat.prototype.chargerConversationsFragment = function() { } catch(e) { ;; console.log(e) } -} +}; euphorik.PageMinichat.prototype.decharger = function() { this.conversations.comet.stopAttenteCourante(); $("body #smiles").remove(); - this.fragment.delVal("conv") + this.fragment.delVal("conv"); }; -euphorik.PageMinichat.prototype.envoyerMessage = function(pseudo, message) { - var thisPageMinichat = this; +/** + * Envoie un nouve message donné, le pseudo utilisé est celui se trouvant + * dans la zone de saisie (form#posterMessage input.pseudo). + */ +euphorik.PageMinichat.prototype.envoyerMessage = function(message) { + var thisPageMinichat = this; + var pseudo = $("form#posterMessage input.pseudo").val(); // (un pseudo vide est autorisé) pseudo = this.formateur.filtrerInputPseudo(pseudo); @@ -239,6 +244,8 @@ euphorik.PageMinichat.prototype.envoyerMessage = function(pseudo, message) { this.util.messageDialogue("Le message est vide"); return; } + + this.client.pseudo = pseudo; if (!this.client.authentifie()) { if (!this.client.enregistrement()) { @@ -247,8 +254,6 @@ euphorik.PageMinichat.prototype.envoyerMessage = function(pseudo, message) { } } - this.client.pseudo = pseudo; - // évite le double post if (this.envoieMessageEnCours) { this.util.messageDialogue("Message en cours d'envoie..."); @@ -260,17 +265,6 @@ euphorik.PageMinichat.prototype.envoyerMessage = function(pseudo, message) { "put_message", this.getJSONMessage(pseudo, message), function() { - // TODO : revoir cette partie - // met à jour la classe des messages auquel repond celui ci (c'est un peu de la triche) TODO : ya mieux ? - objectEach(thisPageMinichat.conversations.messagesRepond, function(messId) { - thisPageMinichat.conversations.conversations.each(function(i, conv) { - var mess = conv.messagesParId[messId]; - if (mess) { - mess.clientARepondu = true; - $("#conversations #" + mess.getId(conv.getId())).addClass("repondu") - } - }); - }); $("form#posterMessage input.message").val(""); thisPageMinichat.conversations.enleverMessagesRepond(); thisPageMinichat.envoieMessageEnCours = false; diff --git a/js/util.js b/js/util.js index c859831..f5fca3f 100644 --- a/js/util.js +++ b/js/util.js @@ -43,14 +43,16 @@ euphorik.Util.messageType = {informatif: 0, question: 1, erreur: 2}; * @param message le message (string) * @param type voir 'messageType'. par défaut messageType.informatif * @param les boutons sous la forme d'un objet ou les clefs sont les labels des boutons - * et les valeurs les fonctions executées lorsqu'un bouton est activé. - * @param formate faut-il formaté le message ? true par défaut + * et les valeurs les fonctions executées lorsqu'un bouton est activé. + * Lorsqu'un bouton est activé le message se ferme. + * @param formate faut-il formaté le message ? true par défaut + * @param temps le temps d'affichage du message en seconde, -1 pour une durée infinie */ -euphorik.Util.prototype.messageDialogue = function(message, type, boutons, formate) { +euphorik.Util.prototype.messageDialogue = function(message, type, boutons, formate, temps) { var thisUtil = this; type = type || euphorik.Util.messageType.informatif; - formate = formate || true; + formate = formate === undefined ? true : formate; if (this.timeoutMessageDialogue) { clearTimeout(this.timeoutMessageDialogue); @@ -58,7 +60,7 @@ euphorik.Util.prototype.messageDialogue = function(message, type, boutons, forma var fermer = function() { $("#info").slideUp(100); }; fermer(); - + $("#info .message").html(!thisUtil.formateur || !formate ? message : thisUtil.formateur.traitementComplet(message)); switch(type) { @@ -72,8 +74,10 @@ euphorik.Util.prototype.messageDialogue = function(message, type, boutons, forma $("#info .boutons").append("
" + nom + "
").find("div:last").click(bouton).click(fermer); }); - $("#info").slideDown(200); - this.timeoutMessageDialogue = setTimeout(fermer, euphorik.conf.tempsAffichageMessageDialogue); + $("#info").slideDown(200); + if (temps !== -1) { + this.timeoutMessageDialogue = setTimeout(fermer, temps || euphorik.conf.tempsAffichageMessageDialogue); + } }; euphorik.Util.positionTypeX = {gauche: 0, gaucheRecouvrement: 1, centre: 2, droiteRecouvrement: 3, droite: 4}; diff --git a/modules/Makefile b/modules/Makefile index 0a77892..859cc56 100755 --- a/modules/Makefile +++ b/modules/Makefile @@ -41,7 +41,7 @@ $(rep_ebin)/euphorik_bd.beam: $(rep_erl)/euphorik_bd.erl $(rep_include)/euphorik erlc $(erlc_params) # Module pour la mise à jour de la BD -$(rep_ebin)/euphorik_bd_admin.beam: $(rep_erl)/euphorik_bd_admin.erl $(rep_erl)/euphorik_bd.erl $(rep_include)/euphorik_bd.hrl $(rep_include)/euphorik_defines.hrl +$(rep_ebin)/euphorik_bd_admin.beam: $(rep_erl)/euphorik_bd_admin.erl $(rep_include)/euphorik_bd.hrl $(rep_include)/euphorik_defines.hrl erlc $(erlc_params) # Module permettant l'extraction des conversations du minichat @@ -61,7 +61,7 @@ $(rep_ebin)/euphorik_protocole.beam: $(rep_erl)/euphorik_protocole.erl $(rep_inc # erlc $(erlc_params) # Module effectuant periodiquement certaines tâches -$(rep_ebin)/euphorik_daemon.beam: $(rep_erl)/euphorik_daemon.erl $(rep_include)/euphorik_defines.hrl $(rep_erl)/euphorik_bd_admin.erl +$(rep_ebin)/euphorik_daemon.beam: $(rep_erl)/euphorik_daemon.erl $(rep_include)/euphorik_defines.hrl erlc $(erlc_params) # Module avec plein de bordel dedant @@ -69,7 +69,7 @@ $(rep_ebin)/euphorik_common.beam: $(rep_erl)/euphorik_common.erl erlc $(erlc_params) # Module dédié au tests -$(rep_ebin)/euphorik_test.beam: $(rep_erl)/euphorik_test.erl $(rep_erl)/euphorik_bd.erl $(rep_include)/euphorik_bd.hrl +$(rep_ebin)/euphorik_test.beam: $(rep_erl)/euphorik_test.erl $(rep_include)/euphorik_bd.hrl erlc $(erlc_params) # Suppression des modules compilés diff --git a/modules/erl/euphorik_bd.erl b/modules/erl/euphorik_bd.erl index 81a36a0..4fecb73 100755 --- a/modules/erl/euphorik_bd.erl +++ b/modules/erl/euphorik_bd.erl @@ -46,7 +46,6 @@ % messages : nouveau_message/3, nouveau_message_sys/1, - nouveau_message_sys/2, messages/1, messages/2, messages/3, @@ -78,7 +77,6 @@ troll_by_id/1, current_troll/0, elire_troll/0, - message_id_associe/1, % utiles : resultat_transaction/1 @@ -277,13 +275,16 @@ user_by_mess(Id) -> nouveau_message(Mess, Auteur_id, Repond_A_ids) -> % regarde si les id 'Repond_A' existent F = fun() -> - Repond_a = lists:map( - fun(Repond_a_id) -> - [M] = mnesia:wread({minichat, Repond_a_id}), - M - end, + Repond_a = lists:foldr( + fun(Repond_a_id, Acc) -> + case mnesia:read({minichat, Repond_a_id}) of + [M] -> [M | Acc]; + _ -> Acc % le message n'est pas trouvé + end + end, + [], Repond_A_ids - ), + ), Racine_id = case Repond_a of [] -> undefined; [M | _] -> @@ -295,16 +296,16 @@ nouveau_message(Mess, Auteur_id, Repond_A_ids) -> _ -> {erreur, "Les messages ne font pas partie de la même conversation"} end - end, - case Racine_id of - {erreur, E} -> {erreur, E}; - _ -> - % est-ce que l'auteur existe ? - case mnesia:wread({user, Auteur_id}) of - [#user{profile = Profile} = Auteur] -> - if length(Repond_a) =/= length(Repond_A_ids) -> - {erreur, "Un ou plusieurs messages introuvable"}; - true -> + end, + if length(Repond_a) =/= length(Repond_A_ids) -> + {erreur, "Un ou plusieurs messages introuvable"}; + true -> + case Racine_id of + {erreur, E} -> {erreur, E}; + _ -> + % est-ce que l'auteur existe ? + case mnesia:wread({user, Auteur_id}) of + [#user{profile = Profile} = Auteur] -> % comparaison entre la date du dernier poste et maintenant (gestion du flood) Now = now(), Delta = euphorik_common:delta_date_ms(Auteur#user.date_derniere_connexion, Now), @@ -332,11 +333,11 @@ nouveau_message(Mess, Auteur_id, Repond_A_ids) -> racine_id = if Racine_id =:= undefined -> Id; true -> Racine_id end }), Id - end - end; - _ -> - {erreur, "L'auteur du message est introuvable"} - end + end; + _ -> + {erreur, "L'auteur du message est introuvable"} + end + end end end, resultat_transaction(mnesia:transaction(F)). @@ -352,16 +353,11 @@ inserer_reponses(_, []) -> % Permet de créer un message système. % Renvoie l'id du message système nouveau_message_sys(Mess) -> - nouveau_message_sys(Mess, undefined). - - -% Création d'un message système lié à un troll. -nouveau_message_sys(Mess, Troll_id) -> {ok, #user{profile = Profile}} = user_by_id(0), resultat_transaction(mnesia:transaction( fun() -> Id = nouvel_id(minichat), - mnesia:write(#minichat{id = Id, auteur_id = 0, date = now(), pseudo = Profile#profile.pseudo, contenu = Mess, troll_id = Troll_id, racine_id = Id}), + mnesia:write(#minichat{id = Id, auteur_id = 0, date = now(), pseudo = Profile#profile.pseudo, contenu = Mess, racine_id = Id}), Id end )). @@ -372,26 +368,29 @@ messages(N) -> messages(N, 1). -% Renvoie N messages se trouvant sur la page P +% Renvoie N messages se trouvant sur la page P +% TODO FIXME : fonction en O(N * P) ! messages(N, P) -> - F = fun() -> - C = cursor( - qlc:keysort( - #minichat.id, - q([E#minichat{contenu = contenu_message(E)} || E <- mnesia:table(minichat)]), - [{order, descending}] - ), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ), - if P > 1 -> qlc:next_answers(C, N * (P - 1)); - true -> ok - end, - R = qlc:next_answers(C, N), - qlc:delete_cursor(C), - R + F = fun() -> + % % #minichat{contenu = contenu_message(E)} + get_tuples_avant(minichat, reculer(minichat, mnesia:last(minichat), N * (P - 1)), N) end, - lists:reverse(resultat_transaction(mnesia:transaction(F))). - + resultat_transaction(mnesia:transaction(F)). + +get_tuples_avant(Table, Id, N) -> + get_tuples_avant(Table, Id, N, []). +get_tuples_avant(_, '$end_of_table', _, Tuples) -> Tuples; +get_tuples_avant(_, _, 0, Tuples) -> + Tuples; +get_tuples_avant(Table, Id, N, Tuples) -> + [T] = mnesia:read({Table, Id}), + get_tuples_avant(Table, mnesia:prev(Table, Id), N - 1, [T | Tuples]). + +reculer(_, '$end_of_table' = Fin, _) -> Fin; +reculer(_, Id, 0) -> Id; +reculer(Table, Id, N) -> + reculer(Table, mnesia:prev(Table, Id), N - 1). + % Renvoie les messages manquants pour la page P en sachant qu'il y a N message % par page et que le dernier message que l'on possède est Id @@ -412,9 +411,9 @@ message_by_id(Id) -> % Renvoie le contenu d'un message donnée en fonction du troll associé, à utiliser à l'intérieur d'une transaction. -% TODO : Cette fonction pourrait être remplacé par un "outer-join", est-ce possible avec qlc ? +% TODO : Cette fonction pourrait être remplacée par un "outer-join", est-ce possible avec qlc ? contenu_message(E) -> - case mnesia:read({troll, E#minichat.troll_id}) of + case mnesia:index_read(troll, E#minichat.id, #troll.id_minichat) of [] -> E#minichat.contenu; [T] -> E#minichat.contenu ++ T#troll.content end. @@ -796,26 +795,13 @@ elire_troll() -> plus_de_trolls; Trolls -> Troll = lists:nth(random:uniform(length(Trolls)), Trolls), - Troll2 = Troll#troll{date_post = now()}, - mnesia:write(Troll2), - nouveau_message_sys("Troll de la semaine : ", Troll2#troll.id) + Id_message = nouveau_message_sys("Troll de la semaine : "), + Troll2 = Troll#troll{date_post = now(), id_minichat = Id_message}, + mnesia:write(Troll2) end end ). - -% Renvoie l'id du message associé au troll dont l'id est donnée. -% Renvoie undefined si il n'y en a pas. -message_id_associe(Troll_id) -> - resultat_transaction(mnesia:transaction( - fun() -> - case e(q([M#minichat.id || M <- mnesia:table(minichat), M#minichat.troll_id =:= Troll_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [Id] -> Id; - _ -> undefined - end - end - )). - % Renvoie le résultat d'une transaction (en décomposant le tuple fournit) resultat_transaction({_, T}) -> diff --git a/modules/erl/euphorik_bd_admin.erl b/modules/erl/euphorik_bd_admin.erl index 62ab563..076a570 100644 --- a/modules/erl/euphorik_bd_admin.erl +++ b/modules/erl/euphorik_bd_admin.erl @@ -34,8 +34,9 @@ reset/0, update/0, - backup_text/1, - restore_text/1, + backup/1, + restore/1, + change_node_name/4, toggle_ek_master/1, print_users/0, @@ -58,7 +59,7 @@ version_bd() -> % Instructions pour créer une nouvelle base : -% $erl -sname yaws -mnesia dir '"/projets/euphorik/var/BD"' +% $erl -sname yaws -mnesia dir '"/projets/euphorik/BD"' % voir doc/installation.txt % >l(euphorik_bd). % >euphorik_bd:create(). @@ -84,6 +85,7 @@ create_tables() -> {disc_copies, [node()]} ]), mnesia:create_table(minichat, [ + {type, ordered_set}, {attributes, record_info(fields, minichat)}, {disc_copies, [node()]} ]), @@ -109,12 +111,22 @@ create_tables() -> % mis à part car lors de la reprise de données avec load_textfile les indexes ne sont pas recréés creer_indexes() -> + % commence par supprimer les anciens indexes + lists:foreach(fun(T) -> + lists:foreach(fun(P) -> + mnesia:del_table_index(T, P) + end, + mnesia:table_info(T, index) + ) + end, + ?TABLES + ), mnesia:add_table_index(minichat, auteur_id), - mnesia:add_table_index(minichat, troll_id), mnesia:add_table_index(reponse_minichat, cible), mnesia:add_table_index(user, cookie), mnesia:add_table_index(user, login), - mnesia:add_table_index(troll, date_post). + mnesia:add_table_index(troll, date_post), + mnesia:add_table_index(troll, id_minichat). % Connexion à la base de données de yaws sur overnux @@ -203,9 +215,28 @@ update(Version) -> % Applique une modification de la BD pour passer d'une version à la suivante. % crée un backup avant l'application du patch -% dans var/BD/backups nommé "backup" où et le numéro de la version. +% dans BD/backups nommé "backup" où et le numéro de la version. % 1 -> 2 patch(1) -> + % Prend un chemin vers la feuille de style de type "css/1/euphorik.css" + % et renvoie "styles/1/euphorik.css" + Transforme_css = fun("css" ++ Reste) -> + "styles" ++ Reste; + (F) -> F + end, + Traiter_message = fun(M, Racine) -> + F = fun(F, M2) -> % seul moyen à ma connaissance pour faire de la récursion dans une lambda fonction, voir : http://www.nabble.com/Auto-generated-functions-td15279499.html + % met à jour la racine de chaque message qui répond à M + lists:foreach( + fun(M3) -> + mnesia:write(M2#minichat{racine_id = Racine}), + F(F, M3) + end, + euphorik_bd:enfants(M#minichat.id) + ) + end, + F(F, M, Racine) + end, mnesia:create_table(texte, [ {attributes, record_info(fields, texte)}, {disc_copies, [node()]} @@ -215,78 +246,133 @@ patch(1) -> mnesia:transform_table( user, fun({user, Id, Cookie, Pseudo, Login, Password, Email, Date_creation, Date_derniere_connexion, Css, Nick_format, View_times, View_tooltips, Indice_flood, _Page_principale, Conversations, Ek_master, Last_ip}) -> - {user, Id, Cookie, Login, Password, {profile, Pseudo, Email, patch1_transforme_css(Css), Nick_format, View_times, View_tooltips, light, reverse, lists:map(fun({R, _}) -> {R, false} end, Conversations)}, Date_creation, Date_derniere_connexion, Indice_flood, Ek_master, Last_ip} + {user, Id, Cookie, Login, Password, {profile, Pseudo, Email, Transforme_css(Css), Nick_format, View_times, View_tooltips, light, reverse, lists:map(fun({R, _}) -> {R, false} end, Conversations)}, Date_creation, Date_derniere_connexion, Indice_flood, Ek_master, Last_ip} end, - record_info(fields, user), - user + record_info(fields, user) ), mnesia:transform_table( minichat, fun({minichat, Id, Auteur_id, Date, Pseudo, Contenu, Troll_id}) -> {minichat, Id, Auteur_id, Date, Pseudo, Contenu, Troll_id, Id} end, - record_info(fields, minichat), - minichat + record_info(fields, minichat) ), case mnesia:transaction( fun() -> % met à jour les enfants des racines % idéalement : utiliser un cursor mais je crois qu'il n'est pas possible de faire des modifs en itérant en même temps avec un cursor, a voir.. Messages = e(q([M || M <- mnesia:table(minichat), euphorik_bd:parents(M#minichat.id) =:= []]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]), - lists:foreach(fun(M) -> patch_1_traiter_message(M, M#minichat.id) end, Messages) + lists:foreach(fun(M) -> Traiter_message(M, M#minichat.id) end, Messages) end ) of {aborted, Raison} -> {erreur, Raison}; {atomic, _} -> ok - end. - + end; +% 2 -> 3 +patch(2) -> + % première étape : changer le type de la table minichat de set à ordered_set + % TODO : trouver un meilleur moyen que de passer par un backup + backup("tmp"), + create(), + restore("tmp"), + file:delete(dossier_backups() ++ "tmp"), + mnesia:transform_table( + troll, + fun({troll, Id_troll, Id_user, Date_create, Date_post, Content}) -> + % recherche le message associé s'il existe + Id_minichat = case e(q([M || M <- mnesia:table(minichat), element(7, M) =:= Id_troll]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [M] -> element(2, M); + _ -> undefined + end, + {troll, Id_troll, Id_user, Id_minichat, Date_create, Date_post, Content} + end, + record_info(fields, troll) + ), + %mnesia:del_table_index(minichat, troll_id), + mnesia:transform_table( + minichat, + fun({minichat, Id, Auteur_id, Date, Pseudo, Contenu, _Troll_id, Racine_id}) -> + {minichat, Id, Auteur_id, Date, Pseudo, Contenu, Racine_id} + end, + record_info(fields, minichat) + ), + creer_indexes(). % uniquement pour l'indice sur id_minichat de la table troll -% Prend un chemin vers la feuille de style de type "css/1/euphorik.css" -% et renvoie "styles/1/euphorik.css" -patch1_transforme_css("css" ++ Reste) -> - "styles" ++ Reste; -patch1_transforme_css(F) -> - F. - -patch_1_traiter_message(M, Racine) -> - % met à jour la racine de chaque message qui répond à M - lists:foreach( - fun(M2) -> - mnesia:write(M2#minichat{racine_id = Racine}), - patch_1_traiter_message(M2, Racine) - end, - euphorik_bd:enfants(M#minichat.id) - ). +% Renvoie le dossier dans lequel les backups sont effectué, ce dossier doit être en écriture. +dossier_backups() -> + mnesia:system_info(directory) ++ "/backups/". -% crée un backup dont le nom est fournit dans le repertoire backups qui se trouve dans le repertoire de la BD. -%backup(Nom) -> -% mnesia:backup(mnesia:system_info(directory) ++ "/backups/" ++ Nom). - -% Reviens à une version précédente de la base de données -% (les données insérées durant les versions plus récentes sont perdues) -%restore(N) -> -% mnesia:restore(fichier_backup(N), [{default_op, recreate_tables}]). +% Renvoie le fichier (avec le chemin) correspondant à la version Version, par exemple : "/var/euphorik/BD/backups/backup1" +fichier_backup(Version) when is_integer(Version) -> + dossier_backups() ++ "backup" ++ integer_to_list(Version). + +% crée un backup dont le nom est fournit dans le repertoire backups qui se trouve dans le repertoire de la BD. +backup(Fichier) -> + mnesia:backup(dossier_backups() ++ Fichier). + + +% Restaure un backup de la BD. +restore(Fichier) when is_list(Fichier) -> + mnesia:restore(dossier_backups() ++ Fichier, []); +% Reviens à une version précédente de la base de données. +% (les données insérées durant les versions plus récentes sont perdues). +restore(Version) when is_integer(Version) -> + mnesia:restore(fichier_backup(Version), []). % [{default_op, recreate_tables}]). + -% Renvoie le fichier (avec le chemin) correspondant à la version Version, par exemple : "/var/euphorik/var/BD/backups/backup1" -fichier_backup(Version) -> - mnesia:system_info(directory) ++ "/backups/" ++ if is_integer(Version) -> "backup" ++ integer_to_list(Version); true -> Version end. +% Change le nom du noeud d'un backup. +% provient d'ici : http://www.erlang.org/doc/apps/mnesia/Mnesia_chap7.html#6.9 +% From : l'ancien nom +% To : le nouveau nom +% Source : le nom du fichier de backup +% Target : le nom du fichier du nouveau backup +change_node_name(From, To, Source, Target) -> + Switch = + fun(Node) when Node == From -> To; + (Node) when Node == To -> throw({error, already_exists}); + (Node) -> Node + end, + Convert = + fun({schema, db_nodes, Nodes}, Acc) -> + {[{schema, db_nodes, lists:map(Switch,Nodes)}], Acc}; + ({schema, version, Version}, Acc) -> + {[{schema, version, Version}], Acc}; + ({schema, cookie, Cookie}, Acc) -> + {[{schema, cookie, Cookie}], Acc}; + ({schema, Tab, CreateList}, Acc) -> + Keys = [ram_copies, disc_copies, disc_only_copies], + OptSwitch = + fun({Key, Val}) -> + case lists:member(Key, Keys) of + true -> {Key, lists:map(Switch, Val)}; + false-> {Key, Val} + end + end, + {[{schema, Tab, lists:map(OptSwitch, CreateList)}], Acc}; + (Other, Acc) -> + {[Other], Acc} + end, + mnesia:traverse_backup(Source, Target, Convert, switched). -backup_text(_) -> todo. -restore_text(File) -> - mnesia:stop(), - mnesia:delete_schema([node()]), - mnesia:start(), - case mnesia:load_textfile(File) of - {atomic, ok} -> - update(), - creer_indexes(); - Erreur -> - Erreur - end. +% Obsolète +%~ backup_text(File) -> + %~ mnesia:dump_to_textfile(File). +%~ restore_text(File) -> + %~ mnesia:stop(), + %~ mnesia:delete_schema([node()]), + %~ mnesia:start(), + %~ create_tables(), + %~ case mnesia:load_textfile(File) of + %~ {atomic, ok} -> + %~ update(), + %~ creer_indexes(); + %~ Erreur -> + %~ Erreur + %~ end. toggle_ek_master(User_id) -> @@ -369,4 +455,4 @@ print_user(Id) when is_integer(Id) -> _ -> {erreur, "Id pas trouvé : " ++ integer_to_list(Id)} end. - \ No newline at end of file + diff --git a/modules/erl/euphorik_minichat_conversation.erl b/modules/erl/euphorik_minichat_conversation.erl index 2f932e2..4d0b3a8 100755 --- a/modules/erl/euphorik_minichat_conversation.erl +++ b/modules/erl/euphorik_minichat_conversation.erl @@ -31,8 +31,7 @@ % Un message est un tuple représentant le message et la liste des id % des messages auquels il répond % @type Message() = {#minichat, [int()]} -% - + -module(euphorik_minichat_conversation). -export([ @@ -40,10 +39,8 @@ ]). -include("../include/euphorik_bd.hrl"). -include("../include/euphorik_defines.hrl"). --include_lib("stdlib/include/qlc.hrl"). -import(lists, [reverse/1, any/2, map/2, sublist/3, filter/2]). -import(euphorik_bd, [resultat_transaction/1]). --import(qlc, [e/2, q/1]). -import(mnesia, [table/1, transaction/1]). @@ -111,26 +108,22 @@ mise_en_forme_conversation(Messages) -> % @spec conversations_detailees([{integer(), integer()}], integer(), integer(), integer()) -> [[{integer(), bool()}] | Conversation_detailee()] conversations_detailees(Racines, N, D, P) -> Conversations = map(fun({Racine, P_conv, Dernier}) -> conversation(Racine, N, Dernier, P_conv) end, Racines), - Conversation_principale = resultat_transaction(transaction(fun() -> - Curseur = qlc:cursor( - qlc:sort(q([E#minichat.id || E <- table(minichat)]), [{order, descending}]), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ), - {CP, Plus} = conversation_principale(Curseur, Conversations, N, P), - qlc:delete_cursor(Curseur), + Conversation_principale = resultat_transaction(transaction(fun() -> + Dernier_id = mnesia:last(minichat), + {CP, Plus} = conversation_principale(Dernier_id, Conversations, N, P), {[M || M <- CP, M > D], Plus} % filtre en fonction de D end)), [Conversation_principale | Conversations]. -% Construit la conversation principale en fonction d'un curseur C initialement placé sur le dernier message +% Construit la conversation principale en fonction d'un id de message initialement placé sur le dernier message % et la liste de conversations. % N est le nombre de messages que l'on souhaite. % P est le numéro de la page (1, 2, 3...) -% @spec conversation_principale(qlc:QueryCursor(), [Conversation_detailee()], integer(), integer()) -> {[integer()], bool()} -conversation_principale(C, Conversations, N, P) -> +% @spec conversation_principale(integer(), [Conversation_detailee()], integer(), integer()) -> {[integer()], bool()} +conversation_principale(Id, Conversations, N, P) -> % on prend en message de plus pour savoir s'il y en a plus que ce que l'on désire - CP = reverse(conversation_principale2(C, lists:flatten(map(fun({C2, _, X, _}) -> C2 -- X end, Conversations)), N + 1, (P - 1) * N)), + CP = reverse(conversation_principale2(Id, lists:flatten(map(fun({C2, _, X, _}) -> C2 -- X end, Conversations)), N + 1, (P - 1) * N)), Plus = length(CP) =:= N + 1, { if Plus -> @@ -143,25 +136,25 @@ conversation_principale(C, Conversations, N, P) -> }. -% C est le curseur (voir ci dessus) +% Id est l'id d'un message, voir ce dessus % 'Messages' sont les messages que l'on doit enlever de la conversation % S est le nombre de messages qu'il faut sauter. -% @spec conversation_principale2(qlc:QueryCursor(), [integer()], integer(), integer()) -> [integer()] +% @spec conversation_principale2(integer(), [integer()], integer(), integer()) -> [integer()] conversation_principale2(_, _, 0, _) -> + []; +conversation_principale2('$end_of_table', _, _, _) -> []; -conversation_principale2(C, Messages, N, S) -> - case qlc:next_answers(C, 1) of - [] -> []; - [M] -> % traitement message par message (pas des plus performant :/) - Doit_etre_saute = any(fun(E) -> E == M end, Messages), - if Doit_etre_saute -> - conversation_principale2(C, Messages, N, S); % le message ne fait pas partie de la conversation - S =:= 0 -> - [M | conversation_principale2(C, Messages, N - 1, S)]; % ok : le message fait partie de la conversation - true -> - conversation_principale2(C, Messages, N, S - 1) % on n'a pas encore atteint le début de la page - end - end. +conversation_principale2(Id, Messages, N, S) -> + % traitement message par message (pas des plus performant :/) + Id_prev = mnesia:prev(minichat, Id), + Doit_etre_saute = any(fun(E) -> E == Id end, Messages), + if Doit_etre_saute -> + conversation_principale2(Id_prev, Messages, N, S); % le message ne fait pas partie de la conversation + S =:= 0 -> + [Id | conversation_principale2(Id_prev, Messages, N - 1, S)]; % ok : le message fait partie de la conversation + true -> + conversation_principale2(Id_prev, Messages, N, S - 1) % on n'a pas encore atteint le début de la page + end. % Renvoie un tuple {C, Cn, X, Plus} où diff --git a/modules/erl/euphorik_protocole.erl b/modules/erl/euphorik_protocole.erl index a34eae6..1fb3cc1 100755 --- a/modules/erl/euphorik_protocole.erl +++ b/modules/erl/euphorik_protocole.erl @@ -283,7 +283,7 @@ wait_event_page_chat(User, Racines_conversations, Message_count, Last_message_id {struct, [ {reply, "new_troll"}, {troll_id, Current#troll.id}, - {message_id, euphorik_bd:message_id_associe(Current#troll.id)}, + {message_id, Current#troll.id_minichat}, {content, Current#troll.content} ]}; _ -> diff --git a/modules/erl/euphorik_test.erl b/modules/erl/euphorik_test.erl index c1bf49b..2cbc1ec 100644 --- a/modules/erl/euphorik_test.erl +++ b/modules/erl/euphorik_test.erl @@ -22,6 +22,7 @@ -module(euphorik_test). -export([ + bench_write_minichat/1, start/2, stop/1, bench_get_messages/0, @@ -34,6 +35,7 @@ -define(INTERVALLE_MIN, 2). -define(INTERVALLE_MAX, 5). + % N est le nombre d'utilisateur % M est le nombre de message que chaque utilisateur va poster start(N, M) -> @@ -55,7 +57,7 @@ start(N, M) -> stop(Pids) -> lists:foreach(fun(Pid) -> exit(Pid, kill) end, Pids). - +% des trucs qui trainent bench_get_messages() -> T = [ {page,"chat"}, @@ -67,8 +69,6 @@ bench_get_messages() -> {conversations,{array,[]}} ], moyenne_temps(euphorik_protocole, wait_event, [T], 20). - - bench_get_messages_avec_2_conversations() -> T = [ {page,"chat"}, @@ -91,8 +91,6 @@ bench_get_messages_avec_2_conversations() -> ]}} ], moyenne_temps(euphorik_protocole, wait_event, [T], 20). - - moyenne_temps(Module, Fun, Args, N) -> moyenne_temps(Module, Fun, Args, N, N, 0). moyenne_temps(_, _, _, 0, Total, Temps_acc) -> @@ -129,9 +127,9 @@ mot_rand(L, Mot) -> % Tire au hasard de 0 à 3 messages sur les 10 derniers postés, renvoie une liste de int() % répartition : % 0 : 0.1 -% 1 : 0.7 -% 2 : 0.15 -% 3 : 0.05 +% 1 : 0.95 +% 2 : 0.04 +% 3 : 0.01 messages_id_rand() -> R = random:uniform(), if R =< 0.1 -> @@ -139,9 +137,9 @@ messages_id_rand() -> true -> Messages = lists:map(fun(#minichat{id = Id}) -> Id end, euphorik_bd:messages(8)), if - R > 0.1 andalso R =< 0.8 -> + R > 0.1 andalso R =< 0.95 -> tire_element_rand(1, Messages); - R > 0.8 andalso R =< 0.95 -> + R > 0.95 andalso R =< 0.99 -> tire_element_rand(2, Messages); true -> tire_element_rand(3, Messages) @@ -170,9 +168,8 @@ loop(User_id, M) -> % attend un temp aléatoire compris entre INTERVALLE_MIN sec et INTERVALLE_MAX sec timer:sleep(1000 * (random:uniform(?INTERVALLE_MAX - ?INTERVALLE_MIN + 1) + ?INTERVALLE_MIN - 1)), % poste un message aléatoire par une personne aléatoire répondant à des messages aléatoires - {Message, Repond_a} = {message_rand(), messages_id_rand()}, - %{Message, Repond_a} = {"blablablablablabla", []}, - io:format("~p poste ~p et repond a ~w~n", [User_id, Message, Repond_a]), + {Message, Repond_a} = {message_rand(), messages_id_rand()}, + % io:format("~p poste ~p et repond a ~w~n", [User_id, Message, Repond_a]), case euphorik_bd:nouveau_message(Message, User_id, Repond_a) of {erreur, E} -> io:format("~p : erreur : ~p~n", [User_id, E]), @@ -181,4 +178,35 @@ loop(User_id, M) -> loop(User_id, M - 1) end. + +% Permet de tester la vitesse d'écriture en fonction de la +% taille de la BD +% voir : http://erlang.org/pipermail/erlang-questions/2008-October/038697.html +bench_write_minichat(Filename) -> + Times = bench_write_minichat(1, []), + {ok, File} = file:open(Filename, [write]), + lists:foreach( + fun({Id, Time}) -> + io:format(File, "~w ~w~n", [Id, Time]) + end, + Times + ), + file:close(File). +bench_write_minichat(100000, Temps) -> Temps; +bench_write_minichat(N, Temps) -> + {T, _} = timer:tc(mnesia, transaction, [fun() -> + Id = mnesia:dirty_update_counter(counter, minichat, 1), + mnesia:write(#minichat{ + id = Id, + auteur_id = random:uniform(10000), + date = now(), + pseudo = "Test", + contenu = "Blabla blabla bla.", + racine_id = random:uniform(10000) + }) + end]), + bench_write_minichat(N + 1, if N rem 500 =:= 0 -> [{N, T} | Temps]; true -> Temps end). + + + \ No newline at end of file diff --git a/modules/include/euphorik_bd.hrl b/modules/include/euphorik_bd.hrl index 6713830..0b04c4b 100755 --- a/modules/include/euphorik_bd.hrl +++ b/modules/include/euphorik_bd.hrl @@ -1,3 +1,4 @@ +% coding: utf-8 % Copyright 2008 Grégory Burri % % This file is part of Euphorik. @@ -19,7 +20,7 @@ % Version de la BD --define(VERSION_BD, 2). +-define(VERSION_BD, 3). -define(TABLES, [counter, proprietes, minichat, reponse_minichat, user, ip_table, troll]). @@ -55,7 +56,6 @@ date, % erlang:now() pseudo, % chaine de caractère contenu, % chaine de caractère - troll_id = undefined, % l'id du troll associé correspondant racine_id = undefined % la racine, par défaut correspond à l'id du message }). @@ -69,7 +69,6 @@ }). - -record(profile, % attention : pas une table ! { pseudo = [], % string() @@ -81,7 +80,9 @@ ostentatious_master = light, % peut valoir invisible, light ou heavy. seulement pour ek_master chat_order = reverse, % peut valoir chrono ou reverse conversations = [] % [{integer(), bool}], la liste des messages correspondant au conversation {racine, reduite?} - }). + }). + + -record(user, { id, @@ -113,7 +114,8 @@ -record(troll, { id, - id_user, + id_user, + id_minichat = undefined, % l'id du message associé date_create, % erlang:now() date_post = undefined, % date à laquelle le troll est affiché sur la page principale. undefined initialement puis erlang:now() quand affiché content % chaine de caractère diff --git a/styles/1/euphorik.css b/styles/1/euphorik.css index 4db3de1..54aef62 100755 --- a/styles/1/euphorik.css +++ b/styles/1/euphorik.css @@ -22,6 +22,15 @@ body { margin: 0px; } +/***** Textile *****/ +em.leger { + font-style: italic +} +em.fort { + font-style: normal; + font-weight: bold +} + /***** Menu *****/ ul#menu { background-image: url(img/logo_fond.png); @@ -221,6 +230,7 @@ div#info .boutons div:hover { form input, form button, form select { + color: #841919; background-color: #f0df95; border: #841919 1px solid; } diff --git a/styles/1/pageMinichat.css b/styles/1/pageMinichat.css index ebfadcf..de908dd 100755 --- a/styles/1/pageMinichat.css +++ b/styles/1/pageMinichat.css @@ -9,6 +9,18 @@ vertical-align: middle; } +/***** L'aide sur les commandes *****/ +#aideCommandes h1 { + font-size: 14px; + font-weight: bold; +} +#aideCommandes .usage { + font-weight: bold; +} +#aideCommandes li { + list-style-type: none; +} + /***** La boite de sélection des smiles *****/ #smiles { text-align: center; @@ -59,6 +71,14 @@ top: 77px; line-height: 32px; } + +#page.minichat #trollCourant a:link, #page.minichat #trollCourant a:visited { + color: #f85b31; +} + #page.minichat #trollCourant a:hover, #page.minichat #trollCourant a:active { + color: #f87e5d; +} + #page.minichat #trollCourant .troll { cursor: pointer; font-style: italic diff --git a/styles/2/euphorik.css b/styles/2/euphorik.css index 52f959f..261d8b3 100755 --- a/styles/2/euphorik.css +++ b/styles/2/euphorik.css @@ -25,6 +25,15 @@ body { margin-top: 40px; } +/***** Textile *****/ +em.leger { + font-style: italic +} +em.fort { + font-style: normal; + font-weight: bold +} + /***** Menu *****/ ul#menu { padding-left: 300px; diff --git a/styles/2/pageMinichat.css b/styles/2/pageMinichat.css index 633ff61..67b4d58 100755 --- a/styles/2/pageMinichat.css +++ b/styles/2/pageMinichat.css @@ -11,6 +11,18 @@ vertical-align: middle; } +/***** L'aide sur les commandes *****/ +#aideCommandes h1 { + font-size: 14px; + font-weight: bold; +} +#aideCommandes .usage { + font-weight: bold; +} +#aideCommandes li { + list-style-type: none; +} + /***** La boite de sélection des smiles *****/ #smiles { text-align: center; diff --git a/tools/mise_en_prod.erl b/tools/mise_en_prod.erl index 2b9cb17..933aceb 100755 --- a/tools/mise_en_prod.erl +++ b/tools/mise_en_prod.erl @@ -5,13 +5,39 @@ % Recharge les modules de euphorik et met à jour la BD. % TODO : construire le nom du noeud en fonction du nom de l'host -main(_) -> +hote() -> + '@overnux'. + +% le premier argument est le nom du noeud est peut valoir : +% - yaws : noeud de production +% - yaws_dev : noeud de pre-production +main([Nom_node]) when Nom_node =:= "yaws"; Nom_node =:= "yaws_dev" -> + Node = list_to_atom(Nom_node ++ atom_to_list(hote())), net_kernel:start([flynux, shortnames]), io:format("rechargement des modules..~n"), - _Pid = spawn_link(yaws@overnux, euphorik_daemon, reload_euphorik, []), - receive - {'EXIT', _, _} -> - io:format("mise à jour de la BD..~n"), - spawn(yaws@overnux, euphorik_bd_admin, update, []) - end. + rpc:call(Node, euphorik_daemon, reload_euphorik, []), + if Nom_node =:= "yaws_dev" -> copier_bd(Node); + true -> true + end, + io:format("mise à jour de la BD..~n"), + rpc:call(Node, euphorik_bd_admin, update, []); +main(_) -> + io:format("Usage : mise_en_prod.erl "), + halt(1). +% Copie la bd du noeud de production +copier_bd(Node) -> + io:format("Copie de la BD de production vers le noeude pre-production~n"), + Fichier = "/tmp/backup_ek_tmp", + Fichier2 = "/tmp/backup_ek_tmp2", + rpc:call(yaws@overnux, mnesia, backup, [Fichier]), + rpc:call(Node, euphorik_bd_admin, change_node_name, [yaws@overnux, yaws_dev@overnux, Fichier, Fichier2]), + rpc:call(Node, mnesia, restore, [Fichier2, [{default_op, recreate_tables}]]), + rpc:call(yaws@overnux, file, delete, [Fichier]), + rpc:call(Node, file, delete, [Fichier2]). + + + + + + diff --git a/tools/start_yaws.sh b/tools/start_yaws.sh index 8b9311e..d812137 100755 --- a/tools/start_yaws.sh +++ b/tools/start_yaws.sh @@ -1,3 +1,4 @@ #!/bin/bash +# coding: utf-8 # screen est utilisé par exemple pour lancé une version de preproduction et permettre de la faire tourner après un délog -yaws --conf ./yaws.conf --sname yaws_dev --mnesiadir "../var/BD/" -I debian_yaws_dev +yaws --conf ./yaws.conf --sname yaws_dev --mnesiadir "../var/BD/" -I debian_yaws_dev diff --git a/tools/tools.rb b/tools/tools.rb index 11570dd..b437687 100644 --- a/tools/tools.rb +++ b/tools/tools.rb @@ -49,15 +49,16 @@ class VerifJS def verifierRecur(dossier) Dir.foreach(dossier){|fichier| - if fichier != '.' and fichier != '..' and File.directory?(fichier) and fichier != 'dirs' - if not verifierRecur(dossier + '/' + fichier) + cheminComplet = "#{dossier}/#{fichier}" + if fichier[0,1] != '.' and File.directory?(cheminComplet) and fichier != 'libs' + if not verifierRecur(cheminComplet) return false end elsif fichier[-3, 3] == '.js' - puts "== Vérification de #{dossier}/#{fichier} ==" + puts "== Vérification de #{cheminComplet} ==" # TODO : mettre un if pour la version windows si dessous - #system("java org.mozilla.javascript.tools.shell.Main jslint.js #{dossier}/#{fichier}") - system("rhino ./tools/jslint.js #{dossier}/#{fichier}") + #system("java org.mozilla.javascript.tools.shell.Main jslint.js #{cheminComplet}") + system("rhino ./tools/jslint.js #{cheminComplet}") # puts $?.exitstatus if $?.exitstatus > 0 return false @@ -116,7 +117,7 @@ class MiseEnProd # Effectue la mise en production. def miseEnProd copierFichiers() - maj() + maj('yaws') end # Effectue la mise en préproduction. @@ -124,6 +125,7 @@ class MiseEnProd copierFichiers() copierVAR() lancerYaws() + maj('yaws_dev') end def copierFichiers @@ -144,7 +146,7 @@ class MiseEnProd creer_rep("tools") system("rsync tools/yaws.conf #{@uri}:#{@rep}/tools") system("rsync tools/start_yaws.sh #{@uri}:#{@rep}/tools") - # TODO + system("ssh #{@uri} \"cd #{@rep}/tools; screen -d -m -S yaws_dev ./start_yaws.sh\"") end def exec(commande) @@ -159,6 +161,7 @@ class MiseEnProd end def compiler_partie_serveuse + log "compilation des modules serveur" Dir.chdir('modules') system("make") if $?.exitstatus != 0 @@ -178,6 +181,7 @@ class MiseEnProd # css, images, html, etc.. def copier_partie_statique + log "copie de la partie statique" uri = "#{@uri}:#{@rep}" system("awk '$0 !~ /prod=\"delete\"/' index.yaws | ssh #{@uri} \" cat > #{@rep}/index.yaws\"") system("rsync favicon.ico #{uri}") @@ -188,6 +192,7 @@ class MiseEnProd # minification et package des fichiers js dans euphorik.js def pack_js + log "minification, assemblage et copie du javascript" rep_js = 'js' creer_rep(rep_js) # jquery.js et euphorik.js doivent se trouve en premier @@ -217,6 +222,7 @@ class MiseEnProd end def copie_modules_serveurs + log "copie des modules du serveur" # copie des modules erlang creer_rep('modules') system("rsync -r --exclude 'euphorik_test.beam' modules/ebin #{@uri}:#{@rep}/modules") @@ -224,14 +230,24 @@ class MiseEnProd end def set_droits_fichiers + log "attribution des droits sur les fichiers" # attribution des droits exec("chmod -R g+rx .") end - def maj + # noeud : le nom du noeud sur lequel le script de mise en prod est exécuté + # Execute le script 'mise_en_prod.erl' sur le serveur afin de : + # - Recharger les modules + # - Mettre à jour la base de données + def maj(noeud) + log "rechargement des modules serveur et mise à jour de la base de données" # execution du script de mise à jour system("cat tools/mise_en_prod.erl | ssh #{@uri} \"cat > /tmp/mise_en_prod.erl\"") - system("ssh #{@uri} \"chmod u+x /tmp/mise_en_prod.erl; /tmp/mise_en_prod.erl; rm /tmp/mise_en_prod.erl\"") + system("ssh #{@uri} \"chmod u+x /tmp/mise_en_prod.erl; /tmp/mise_en_prod.erl #{noeud}; rm /tmp/mise_en_prod.erl\"") + end + + def log(message) + puts "----- #{message} -----" end end @@ -264,18 +280,7 @@ class Commande @verifJS.verifier() when 'version' @version.maj() - end - -=begin - Net::SSH.start('euphorik.ch', 'gburri') {|ssh| - output = ssh.exec!("hostname") - stdout = "" - ssh.exec!("ls -l /tmp"){|channel, stream, data| - stdout << data if stream == :stdout - } - puts stdout - } -=end + end end def afficherUsage -- 2.45.2