Catégorie : développement Internet

Drupal : comment faire fonctionner la dernière version de CKEditor

Télécharger la dernière version ici : CKEditor Version 4.3.1 – 10 Dec 2013.

Ensuite cherchez dans le répertoire de Drupal, le fichier ckeditor.inc.
Éditez ce fichier et aux alentours de la ligne 77, dans la fonction
vous devriez trouver une ligne qui ressemble fortement à ceci :

while ($max_lines && $line = fgets($library)) {

(A la différence près qu’il ya a un paramètre supplémentaire à fgets();.
Remplacez le code existant par celui qui est ici.

Ensuite 3 à 4 lignes en dessous, il y a un filtre pour chercher la version de ckeditor. Ce filtre n’est plus valide parce qu’il filtrait sur des nombres, mais ils ont ajouté des lettres à la version de CKEditor. Il faut donc le modifier par ce qui suit :

if (preg_match('@version:\"(?:CKEditor )?([\d\.]+)(?:.+revision:\"([\da-z]+))?@', $line, $version)) {

Et tout cela pourrait presque fonctionner, mais il y a quelque chose de très déroutant : même si vous avez suivi tout cela pas à pas, l’éditeur n’affichera pas les « boutons », mais juste une petite ligne : c’est parce que par défaut, rien n’est coché, et donc aucun bouton n’est visible.

Éditez donc les options de l’éditeur et cochez les boutons que vous voulez voir apparaître dans l’éditeur. Personnellement, je coche tout.

Et voilà, vous aurez un éditeur qui s’intègrera parfaitement, de manière agréable, toutes les fois où vous voudrez ajouter du contenu.

Cours optimisation Internet – Ecole d’ingénieurs des Mines

Après avoir fait ma présentation, cela fait toujours plaisir de savoir qu’on est suffisamment intéressé pour demander ma présentation !

Cliquez sur le lien pour la récupérer, et n’hésitez pas à laisser une appréciation sur ce qui était bien et ce qui manquait éventuellement, sachant que j’ai fait ce que je pouvais dans un laps de temps aussi court 😉

Cliquez ici.

jQuery Mobile : comment forcer le rechargement d’une page ?

Si vous faites des boutons de navigation, votre code jQuery Mobile va ressembler à ceci :

<a href="/">Home</a>

Le seul hic, c’est qu’en cliquant dessus, le navigateur ne va pas recharger réellement la page, il va se baser sur son cache, en supposant que la page n’a pas changé. Ce qui peut poser problème. Par exemple, sur mon site mobile, lorsqu’un commande est validée, je vide le panier côté serveur et je fais un lien vers la page racine, le « home ». Avec le code précédent, le navigateur ne recharge pas réellement la page, et garde le panier toujours « rempli » car il garde les informations et variables JavaScript en mémoire. Dans ce cas, il faut forcer au rechargement de la page. Comment faire ?

Grâce à l’astuce de ce site, voici la solution : il suffit d’ajouter cette balise : rel="external".

Et la page sera rechargée.

<a href="/" rel="external">Home</a>

En espérant que cela aide du monde !

JavaScript, jQuery et closure: petit exercice à faire

Ci suit un petit exercice JavaScript: copiez collez les deux morceaux de code qui suivent dans une page jsfiddle.net

Si vous examinez le code JavaScript, il est très simple : l’objectif de l’exercice semble simple : il faut juste faire « flasher » les libellés les uns après les autres (c’est là que la difficulté se trouve).

Seul problème, avec le code qui suit et semble correct : ça ne fonctionne pas. L’exercice est… de trouver la solution en JavaScript.

Ne regardez pas la fin, car il y a la solution !.

Code HTML :

<table id="s-inscrire-infos">
<tbody>
<tr>
<td id="label-nomprenom"> <span><label for="label-nomprenom"><b>Nom et prénom&nbsp;:&nbsp;</b></label></span> </td>
<td> <span> <input type="text" name="nomprenom" id="nomprenom" class="form-nom-prenom" size="50" maxlength="50" /> </span> </td>
</tr>
<tr class="erreur" id="erreur-nomprenom" style="display:none">
<td colspan="2">* Il faut renseigner votre nom et prénom</td>
</tr>
<tr>
<td id="label-adresse1"> <span> <label for="label-adresse"> <b>Adresse ligne 1 :&nbsp;</b> <br /> <font size="-2">(ou nom de la société)&nbsp;</font> </label> </span> </td>
<td> <span><input type="text" name="adresse1" id="adresse1" class="form-adresse" size="50" maxlength="60" /></span> <br /> <span class="tiny">Rue, voie, boîte postale, nom de société</span> </td>
</tr>
<tr class="erreur" id="erreur-adresse1" style="display:none">
<td colspan="2">* Il faut au moins renseigner une ligne</td>
</tr>
<tr>
<td id="label-adresse2"> <span><label for="label-adresse"><b>Adresse ligne 2 :&nbsp;</b> <br /> <font size="-2">(facultatif)&nbsp;</font> </label></span> </td>
<td> <span><input type="text" name="adresse2" id="adresse2" class="form-adresse" size="50" maxlength="60" /></span> <br /> <span class="tiny">Appartement, bâtiment, étage, digicode, cedex, etc.</span> </td>
</tr>
<tr>
<td id="label-cp"> <span><label for="label-cp"><b>Code postal&nbsp;:&nbsp;</b></label></span> </td>
<td> <span> <input type="text" name="cp" id="cp" class="form-cp" size="20" maxlength="20" /> </span> </td>
</tr>
<tr class="erreur" id="erreur-cp" style="display:none">
<td colspan="2">* Il faut renseigner votre code postal</td>
</tr>
<tr>
<td id="label-ville"> <span><label for="label-ville"><b>Ville&nbsp;:&nbsp;</b></label></span> </td>
<td> <span> <input type="text" name="ville" id="ville" class="form-ville" size="25" maxlength="50" /> </span> </td>
</tr>
<tr class="erreur" id="erreur-ville" style="display:none">
<td colspan="2">* Il faut renseigner votre ville</td>
</tr>
<tr>
<td id="label-tel"> <span> <label for="tel"><b>Numéro de téléphone&nbsp;:&nbsp;</b></label> </span> </td>
<td> <span> <input type="text" name="tel" id="tel" class="form-tel" size="15" maxlength="20" /> </span> </td>
</tr>
<tr class="erreur" id="erreur-tel" style="display:none">
<td colspan="2">* Il faut renseigner votre téléphone</td>
</tr>
<tr>
<td colspan="2" class="form-submit"> <input type="submit" value="Valider" /> </td>
</tr>
</tbody>
</table>

Code JavaScript :

jQuery.fn.getBg = function() {
return $(this).parents().filter(function() {
var color = $(this).css('background-color');
return color != 'transparent' && color != 'rgba(0, 0, 0, 0)';
}).eq(0).css('background-color');
};

function flash(id, font_color, bg_color, nb) {
var bc = $(id).getBg();
var cl = $(id).css('color');
var mx = parseInt(nb);
if (mx <= 0) {
mx = 1;
}
for (var i = 0; i < mx; i++) {
$(id).animate({
backgroundColor: bg_color,
color: font_color
}, 200).animate({
backgroundColor: bc,
color: cl
});
};
}

dataMessage = new Array("#erreur-nomprenom", "#erreur-adresse1", "#erreur-cp", "#erreur-ville", "#erreur-tel");
var theQueue = $({});
for (key in dataMessage) {
var m = dataMessage[key];
if (m.indexOf('#erreur') == 0) {
console.log('should add:'+m);
theQueue.queue('flash', function(next) {
console.log('i will flash:'+m);
$(m).fadeIn('slow', function() {
flash('#label-' + this.id.substr(7), "#ffffff", "#aa0000", 3);
next();
});
});
}
}
theQueue.dequeue('flash');

Solution :

var theQueue = $({});
for (key in dataMessage) {
var m = dataMessage[key];
if (m.indexOf('#erreur') == 0) {
console.log('should add:' + m);
var toFlash = (function(m) {
return function(next) {
console.log('i will flash:' + m);
$(m).fadeIn('slow', function() {
flash('#label-' + this.id.substr(7), "#ffffff", "#aa0000", 3);
next();
});
}
})(m);
theQueue.queue('flash', toFlash);
}
}
theQueue.dequeue('flash');

Eh oui pas si évident que ça à trouver !

Merci à Julien pour son aide !

Si vous avez d’autres solutions n’hésitez pas à les mettre dans un commentaire en bas !

Sencha / ExtJS : comment garder une colonne triée avec une grid ?

Quand on fait une grille de données (datagrid) et qu’on la lie avec un magasin (store) c’est facile.

On peut autoriser à trier par colonnes dans la datagrid.

Seul problème : si le store est un stocké sur le serveur, il fait une seule fois l’appel et ensuite c’est la datagrid qui gère les tris.

Quand on modifie un enregistrement, il est envoyé au serveur, le serveur l’enregistre, et renvoie le résultat de ce qu’il a enregistré. Généralement, il renvoie exactement ce qu’il a reçu. Le seul problème, c’est qu’au retour, la grille ne rafraichit pas l’ordre de tri selon les colonnes qu’on a choisies.

Exemple concret : vous avez une grille avec plein de noms. Vous cliquez sur la colonne « nom », pour la trier par ordre alphabétique. Vous changez le nom « Albert » par « Zoé ». Voici ce qu’il se passe :

  • Le store envoie id=54, nom="Zoé" au serveur ;
  • Le serveur fait la modification en base, et renvoie id=54, nom="Zoé" en retour ;
  • Le store reçoit id=54, nom="Zoé", fait son changement en interne et le transmet à la datagrid ;
  • La datagrid se rafraichit mais ne change pas le tri et laisse "Zoé" à la même place.

La solution : dans le store, lors de l’événement qui signale que le résultat de l’écriture a été intégré (« write« ) il faut forcer l’appel à sort(); qui sera répercuté sur la datagrid automatiquement.

Voici mon code (raccourci à l’extrême sur ma classe de store qui gère les exceptions et plein d’autres choses) :

Ext.define('Ext.data.StoreHandleErrors', {
    extend: 'Ext.data.Store',
    alias: 'data.storehandleerrors',

    constructor: function(config) {
        this.callParent([config]);

        this.on(
            'write',
            function(me, opts) {
                this.sort();
            },
            this
        );
    }
});

ExtJs : dériver un Store « générique » et s’en servir

Ça fait plus d’une journée que je cherche comment faire un Store générique, c’est à dire que j’ai plusieurs Store qui sont tous basés sur le même modèle avec le même type de proxy, etc.

Donc au lieu de faire un copier coller pour chaque Store, j’ai cherché comment en faire un « générique » auquel je pourrai appliquer une configuration « par défaut ».

Voilà le code complet résultat, avec les fonctions qui gère les erreurs possibles renvoyées par Php (session expirée, problème d’écriture en base de données, etc).

Ce qui m’a pris le plus de temps à trouver c’est que pour « surcharger » le constructeur, ça n’est pas la fonction classique « initComponent: function(){ } » mais la fonction de base "constructor: function(config) { }".

Il ne faut, de plus, surtout pas oublier d’appeler le parent, non pas via this.callParent(); mais via this.callParent([config]);.

Ci suit du code, le code de plus d’une journée de travail, en deux parties (j’espère qu’il sauvera du temps à des personnes, ou qu’il les mettra sur la bonne voie !) :

  • première partie = la surcharge
  • seconde partie = exemple d’utilisation de cette surcharge

Première partie : code de la classe

Ext.define('Ext.data.StoreHandleErrors', {
    extend: 'Ext.data.Store',
    alias: 'data.storehandleerrors',

    constructor: function(config) {
        /* (!!) Réécriture par dessus certaines propriétés
         *      du proxy : si jamais elles existent déjà,
         *      elles vont être réécrites.
         */
        config.autoLoad= true;
        config.autoSync= true;
        config.proxy.type= 'ajax';
        config.proxy.reader= {
            type: 'json',
            successProperty: 'success',
            root: 'data',
            messageProperty: 'message'
        };  
        config.proxy.writer= {
            type: 'json',
            writeAllFields: true,
            root: 'data'
        };  
        config.proxy.listeners= {

            exception: function(proxy, response, operation) {

                var error=operation.getError(),
                    title='Erreur du serveur';

                if (error instanceof Array) {
                  error=error.join("
"); } switch(response.status) { case 200: if (response.responseText!='') { var b = Ext.JSON.decode(response.responseText); if (b.title) { title=b.title; } if (b.success==false) { if (b.timeout==true) { windowLoginPanel.show(); } } } break; case -1: var error= 'Le serveur met trop de temps à répondre'+ '

'+ 'On ne peut rien faire, essayez '+ 'd\'actualiser la page.'; break; case 500: var error= 'Le serveur a une erreur interne.'+ '

'+ 'On ne peut rien faire, essayez '+ 'd\'actualiser la page.'; break; default: var error= 'Erreur renvoyée par le serveur non gérée.
'+ 'Détails :
'+ response.statusText+ '

'+ 'On ne peut rien faire, essayez '+ 'd\'actualiser la page.'; break; } Ext.MessageBox.show({ title: title, msg: error, icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK }); } }; this.callParent([config]); this.on( 'write', function(proxy, operation) { if ( (operation.action == 'create') || (operation.action == 'update') ) { var m = this.getById( parseInt( operation.resultSet.records[0].internalId ) ); } else if (operation.action == 'destroy') { var m = this.getAt(0); } if (m) { this.panelGridEtEdit.gsGrid.getSelectionModel().select(m); } else { this.panelGridEtEdit.gsGrid.getSelectionModel().deselectAll(); } Ext.example.msg( Ext.String.capitalize(operation.action), operation.resultSet.message ); }, this ); } });

Seconde partie : utilisation de la classe

    var storeAdresses = Ext.create('Ext.data.StoreHandleErrors', {
        model: 'Intranet.Adresse',
        proxy: {
            api: {
                read: '/json/intranet/liste/adresses/',
                create:  '/json/intranet/item/adresse/?mode=create',
                update:  '/json/intranet/item/adresse/?mode=update',
                destroy: '/json/intranet/item/adresse/?mode=destroy'
            }   
        }   
    }); 

ExtJS et grids : double click, comment faire, howto ?

Cela fait une bonne heure que je cherche comment avoir le double click sur une grid générée dynamiquement.

C’est très simple :

Ce code ne fonctionnait pas, donc si vous êtes dans le même cas que moi, n’ayez pas peur :

this.gridAttributs = Ext.create('Ext.grid.Panel', { 
    border: 0,
    store: this.store,
    columns: [ 
        { text: "id", dataIndex: 'id', sortable: true },
        { ... },
        { ... }
    ],
    celldblclick: function(evt, elem, opts ) { 
        console.log('dblclick');
    }
});

Voici le code qui fonctionne :

this.gridAttributs = Ext.create('Ext.grid.Panel', { 
    border: 0,
    store: this.store,
    columns: [ 
        { text: "id", dataIndex: 'id', sortable: true },
        { ... },
        { ... }
    ]
});
this.gridAttributs.on('cellDblClick', function(evt, elem, opts ) {
    console.log('dblclick');
});

J’espère vous avoir évité de perdre l’heure que moi même j’ai perdu ! 😉

jQuery et changement de css / class / classname

Encore la petite astuce pratique qui vous évitera de chercher des heures :

Très souvent, on veut changer la propriété d’un div. C’est facile via la fonction jQuery

$('#mondiv').css('propriété', 'nouvelle valeur');

Mais si on veut applique carrément tout une classe ?

C’est très simple, c’est la fonction .toggleClass() :

$('#mondiv').toggleClass('nouvelle classe');

jQuery : différence entre visible et hidden

Je voulais tester si un élément est visible, ou non, en jQuery.

Après quelques recherches sur le net, on tombe souvent sur des exemples comme cela :

if ( $("#monid").is(':visible')) {
...
}

C’est une grossière erreur.

Voici l’explication, et le principe qu’il faut avoir en tête :

Lorsqu’on cache ou qu’on montre un élément avec jQuery ou jQueryUI, cela va presque toujours modifier la propriété display (display:none, display:block, etc).

La propriété visible est complètement différente, et est utilisée pour cacher un élément, mais en gardant la place qu’elle occupe.

Pour vérifier si quelque chose est « caché » (notez la subtilité avec la différence de « pas visible« ) il faut vérifier la propriété css display.

Exemple concret :

if ( $("#monid").css('display')!='none') {
...
}