20-100.ch // Prog, news et autres trucs de geek...

Aller au contenu | Aller au menu | Aller à la recherche

dimanche 14 mars 2010

Désactiver le layout et/ou la vue pour une action donnée dans le Framework Zend

Le Framework Zend est un framework MVC en PHP que j'affectionne particulièrement. Dans certains cas, il arrive que l'on veuille avec une action dans un contrôleur donné qui n'affiche rien, ou alors simplement du texte brut (des données JSON par exemple). Dans ce cas, plutôt que de créer un fichier vide pour la vue, il est plus simple de désactiver la vue (et le layout le cas échéant) pour cette action donnée, à l'aide du code suivante :

$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);

mardi 29 décembre 2009

Activer Intellisense pour CUDA dans Visual Studio

Petite parenthèse dans mes articles sur la configuration d'un serveur pour vous parler de CUDA. Je suis actuellement en train de développer avec cette API de nVidia pour faire tourner du code sur les processeurs graphiques, et j'utilise pour ça Visual Studio 2008. La première étape fût d'activer la coloration syntaxique pour le code CUDA (qui est une extension du C, et dont les fichiers finissent en .cu/.cuh).

Pour cela, il suffit d'aller dans Tools / Options / Text Editor / File Extension, et d'ajouter une règle pour les fichiers cu et cuh, afin qu'ils soient édités avec Microsoft Visual C++.

Mais cela ne va pas activer Intellisense, l'auto-complétion, et surtout les aides à la navigation dans le code (les petits menus déroulants en haut de l'éditeur qui permettent d'aller directement à une fonction). Pour activer tout ça, il faut aller dans l'éditeur de registre, dans le dossier:

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\Languages\Language Services\C/C++

Là, modifiez la clé NCB Default C/C++ Extensions et ajoutez-y les extensions .cu et .cuh, et le tour est joué !

Trouvé sur Home of easter wizards via les forums nVidia.

mercredi 23 juillet 2008

Attribut DataFormatString pas pris en compte dans un BoundField

Dans un GridView, on a la possibilité de formater le contenu d'un BoundField avec un pattern équivalent à ceux utilisés par String.Format(). C'est donc ce que j'ai voulu faire, en formatant une de mes colonnes contenant un prix pour qu'elle s'affiche joliment, avec un séparateur des milliers et la monnaie :

<asp:BoundField DataField="Price" DataFormatString="CHF {0:0,0}" SortExpression="Price" />


Mais le problème, c'est que ça ne fonctionne pas : le "CHF" est bien affiché, mais aucun séparateur des milliers ! Cela vient du fait que pour éviter l'injection de code, ASP.Net encode en HTML tous les champs avant de les afficher, et avant de les formatter ! Je ne sais pas pourquoi, mais cela semble le rendre incapable de séparer les milliers. Pour résoudre le problème, il suffit donc de désactiver l'encodage HTML de ce champ, si vous êtes sûr de ne rien risquer (dans mon cas, c'est un int qui vient directement de ma base de données, peu de risques d'injection donc ;-)...), en ajoutant l'attribut HtmlEncode="false" :

<asp:BoundField DataField="Price" DataFormatString="CHF {0:0,0}" HtmlEncode="false" SortExpression="Price" />

mardi 22 juillet 2008

Method error 12030 avec une CascadingDropDown

Le composant CascadingDropDown de l'AjaxToolkit ASP.Net permet de créer des liste déroulantes inter-dépendantes, c'est-à-dire que ce n'est que lorsqu'on a sélectionné une valeur de la première que le contenu de la seconde est chargé. Ce genre de comportement est par exemple utile pour les voitures : on sélectionne la marque, et ensuite seulement la liste des modèles correspondants est chargée.

Ce composant fonctionne avec des services web, qu'il appelle lorsqu'une valeur est sélectionnée pour charger les données de la liste suivante. J'ai voulu l'implémenter, et ai obtenu l'erreur suivante lors du chargement des données de la première liste : "Method error 12030".

Comme son nom l'indique ;-), cette erreur veut dire qu'il y a un problème avec le service web appelé. Dans mon cas, il manquait la mention

[ScriptService()]

en dessus de la classe définissant mon service web, et

[ScriptMethod]

en dessus de la méthode correspondante. Il suffit d'ajouter ça, et ça fonctionne !

mercredi 4 juin 2008

Débugger du JavaScript sous Internet Explorer

Tout développeur web qui un jour a dû écrire du JavaScript un peu compliqué s'est heurté au problème du débuggage du code JS, et s'est tourné vers l'outil ultime pour Firefox : Firebug. Celui-ci permet de mettre des breakpoints dans le code JS, de l'exécuter ligne par ligne, de voir le contenu des variables à tout moment, mais a aussi de nombreuses autres fonctionnalités très utiles, comme l'inspection et la manipulation DOM, et la modification en direct du CSS. Il prend aussi en charge les diverses fonctions de débuggage de Firefox : console.log(), console.error(), etc..., et les affiche de manière lisible.

Mais le problème, c'est qu'il faut des fois débugger sous Internet Explorer ! Et le pâle équivalent de Firebug sous ce dernier, la Developer Toolbar, ne le permet pas. Pire encore, les appels à console.log() si utiles sous Firefox provoquent des erreurs dans IE ! Qu'à cela ne tienne, vous avez maintenant la possiblité d'utiliser Firebug Lite.

Il existe en deux versions, qu'il faut inclure dans vos pages. La première permet, en appuyant sur F12, d'afficher un semblant de console JavaScript , dans laquelle le résultat des console.log(), etc... aparaît. La seconde, quant à elle, permet juste d'empêcher les appels à ces fonctions de causer des erreurs dans Internet Explorer.

Si vous ne voulez pas l'installer sur toutes vos pages web, mais que vous voulez juste l'utiliser comme outil pour débugger, il existe aussi en bookmarklet : Firebug Lite Bookmarklet

mercredi 12 mars 2008

Centrer uniquement une cellule dans un tableau en LaTeX

Lorsqu'on travaille avec les tableau en LaTeX, il est facile de spécifier l'alignement de chaque colonne : c'est l'argument de la commande \begin{tabular}. Par exemple, pour avoir 3 colonnes, séparées par une bordure et respectivement alignée à droite, centrée et alignée à gauche, il suffit de faire :

\begin{tabular}{r|c|l}
cellule alignée à droite & cellule centrée & cellule alignée à gauche \\
\end{tabular}


Cependant, comment faire si l'on veut modifier uniquement l'alignement d'une cellule ? L'astuce est d'utiliser la commande \multicolumn, qui permet de fusionner plusieurs cellules et de définir l'alignement du texte dans les cellules fusionnées, et lui demander de fusionner... une seule cellule ! Ainsi, on pourra choisir l'alignement à sa guise. Cependant, cette technique a un désavantage, c'est qu'il faut redéfinir à nouveau les bordure de la nouvelle cellule :

\begin{tabular}{|l|l|l}
\multicolumn{1}{|c}{Titre 1} & \multicolumn{1}{|c}{Titre 2} & \multicolumn{1}{|c}{Titre 3} \\
cellule 1 & cellule 2& cellule 3 \\
\end{tabular}

vendredi 7 mars 2008

Définir quel bouton sera activé lors de l'appui sur la Enter en ASP.Net

Si, dans une page ASP.NET, vous avez plusieurs formulaire qui cohabitent, vous avez peut-être remarqué que le fait d'appuyer sur Enter envoie toujours le même, à savoir le premier présent dans la page. Ca peut être assez dérangeant, si par exemple vous avez un formulaire de connexion à votre site situé en-dessous d'un champ de recherche : dans ce cas, lorsque l'utilisateur aura saison son mot de passe et appuyé sur Enter (sur son clavier, donc), ce sera le formulaire de recherche qui sera transmis !

Pour contrôler ce genre de scénarii, ASP.NET met à disposition l'attribut DefaultButton sur les balises <asp:Form> et <asp:Panel>. Elle permet d'indiquer, pour respectivement le formulaire ou le panel concerné, quelle bouton sera activé lors de l'appui sur la touche Enter. Par exemple :

<html>
   <body>
      <form id="Form2" runat="server">
         <asp:Panel ID="Panel1" DefaultButton="Button1" RunAt="server">
            <asp:Button ID="Button1" RunAt="server"/>
         </asp:Panel>
         <asp:Panel ID="Panel2" DefaultButton="Button2" RunAt="server">
           <asp:Button ID="Button2" RunAt="server"/>
         </asp:Panel>
      </form>
   </body>
</html>


Et si vous n'avez pas de bouton, mais une balise <asp:Login> que vous voulez activer lors de l'appui sur la touche Enter ? Rien de plus simple, il suffit de donner IdDuForm$LoginButton comme DefaultButton :

<html>
   <body>
      <form id="Form2" runat="server">
         <asp:Panel ID="Panel1" DefaultButton="Login$LoginButton" RunAt="server">
            <asp:Login ID="Login1" RunAt="server"/>
         </asp:Panel>
      </form>
   </body>
</html>

dimanche 2 mars 2008

Encoder les headers personnalisé en JavaScript et les décoder en PHP

Dans la suite de mon post précédent, expliquant comment accéder à des headers HTTP personnalisés en PHP, j'ai rencontré le problème suivant : les headers sont bien transmis pour des données "simples", mais cela ne fonctionnait pas pour des caractères spéciaux ou accentués !

Vu que mes headers sont créés lors d'une requête AJAX, via le framework Opensocial, il faut d'abord pourvoir les "encoder", ou du moins "escaper" les caractères spéciaux, en JavaScript, et pouvoir les "décoder" du côté serveur, dans mon cas en PHP.

Pour ce faire, j'utilise la fonction JavaScript encodeURIComponent() du côté client :

var customHeaders = { "x-question-name" : nameValue, "x-question-text" : textValue };

var params = {};
params[opensocial.ContentRequestParameters.HEADERS] = customHeaders;

opensocial.makeRequest(url, createQuestionCB, params);


et la fonction PHP rawurldecode() du côté serveur !

$name = rawurldecode($_SERVER["HTTP_X_QUESTION_NAME"]);
$text = rawurldecode($_SERVER["HTTP_X_QUESTION_TEXT"]);

Comment récupérer des headers HTTP personnalisés en PHP

Lorsqu'on travaille avec des requêtes AJAX, une manière de passer des paramètres au serveur est via des headers HTTP personnalisés. De cette manière, on n'est pas limités par la taille maximale de 255 caractères pour une URL. Cependant, comment récupérer ces headers personnalisés du côté du serveur en PHP ?

Comme on s'y attend, ils sont bien dans l'objet $_SERVER, mais pas sous le nom escompté : il faut ajouter HTTP_ au nom de son header personnalisé, passer tous les caractères en majuscule et remplacer les - par des _ pour obtenir le nom sous lequel on trouvera ce header dans le tableau $_SERVER.

Ainsi, le header My-Header sera répertorié sous la clé HTTP_MY_HEADER dans le tableau $_SERVER !

mercredi 13 février 2008

Commenter du code ASP.NET pour éviter qu'il ne soit interprété

L'ASP.NET étant un language de balise similaire au (X)HTML, mon premier réflexe lorsque j'ai voulu commenter mon code (soit pour expliquer une partie peu claire, soit pour "désactiver" temporairement une partie de la page pour débugger) a été d'utiliser les commentaires HTML :

<!-- Commentaire explicatif -->
<img src="test.gif" alt="test" />
<!--
<asp:HyperLink ID="lien" runat="server" NavigateUrl="http://20-100.ch/blog/" Text="Lien désactivé" />
-->


Cependant, cela n'empêche pas le serveur d'interpréter les balises ASP.NET ! Ainsi, le lien ci-dessus sera bien visible dans la source de la page, même si rien ne sera affiché dans le navigateur.

Il existe en fait des balises de commentaires spécifiques à l'ASP.NET qui font ce que l'on veut :

<%-- Commentaire explicatif --%>
<img src="test.gif" alt="test" />
<%--
<asp:HyperLink ID="lien" runat="server" NavigateUrl="http://20-100.ch/blog/" Text="Lien désactivé" />
--%>


Pratique !

dimanche 10 février 2008

Comment déclarer une chaîne de caractères sur plusieurs lignes en JavaScript

Deux techniques à choix pour déclarer une chaîne sur plusieurs lignes en JavaScript :

var s = (<r><![CDATA[

ligne 3
ligne 4

]]></r>).toString();


ou alors :

var s = "\
ligne 1\n\
ligne 2\n\
"
;


A vous de choisir celle que vous voulez utiliser (ma préférence va à la seconde, la première faisant un peu trop "hack" à mon goût, en plus d'être sensiblement plus longue à écrire et moins claire à relire). En connaissez-vous d'autres ?

jeudi 7 février 2008

Impossible de charger le type 'Microsoft.Web.Services.ScriptHandlerFactory'

En voulant utiliser des services web en ASP.Net en même temps que la librairie ASP.Net AJAX (anciennement Atlas Library), je suis tombé sur l'erreur suivante :

Impossible de charger le type 'Microsoft.Web.Services.ScriptHandlerFactory'.

Cette erreur fait référence à la ligne suivante du fichier web.config :

<httpHandlers>
    <add verb="*" path="*.asmx" type="Microsoft.Web.Services.ScriptHandlerFactory" validate="false"/>
</httpHandlers>

Pourtant, dans mon cas, cette ligne a été générée automatiquement, et ne devrait donc pas être fausse. J'ai essayé d'inclure moult références à mon projet web, pensant qu'il m'en manquait une mais en vain. Il suffit en fait de changer le type, qui doit être System.Web.Script.Services.ScriptHandlerFactory.

Le fichier web.config dont donc contenir la ligne suivante :

<add verb="*" path="*.asmx" type=System.Web.Script.Services.ScriptHandlerFactory" validate="false"/>

jeudi 8 novembre 2007

Permettre l'upload de fichier dans un UpdatePanel

Il arrive souvent en ASP.Net d'inclure toute sa page dans un UpdatePanel, ce qui a pour effet de rendre asynchrone le fonctionnement de celle-ci. Cependant, si vous mettez un champ FileUpload (l'équivalent en ASP.Net du <input type="file">) dans un UpdatePanel, vous vous retrouverez avec une taille de 0 byte lors du traitement du postback.

La raison est que l'UpdatePanel fait les requêtes directement en asynchrone et de manière transparent, sans passer par un POST normal, et n'upload pas les fichiers. Pour que votre FileUpload fonctionne dans un UpdatePanel, il suffit d'ajouter un trigger à ce dernier, lui indiquant d'effectuer un POST normal lorsque le formulaire contenant votre fichier est soumis.

Par exemple, si votre UpdatePanel est dans la Master Page et que votre FileUpload se trouve dans un FormView, voici comment ajouter le trigger en C# :

UpdatePanel panel = (UpdatePanel)Page.Master.FindControl("__myUpdatePanel");

PostBackTrigger trigger = new PostBackTrigger();
trigger.ControlID = this.__myFormView.UniqueID;

panel.Triggers.Add(trigger);

vendredi 31 août 2007

Une ligne de code CSS/HTML pour faire planter IE6

Un blogeur japonais a découvert un bug d'Internet Explorer 6 qui permet de faire planter l'explorateur, voire la machine complète, à l'aide d'une seule ligne de code HTML/CSS :

<style>*{position:relative}</style></code><table><input></table>


A vous de jouer ;)...

mardi 14 août 2007

Comment débugger plus facilement ses scripts JavaScript

Lorsqu'on développe, on a souvent besoin de consulter des valeurs lors de l'exécution, pour débugger son code et vérifier que tout se déroule comme prévu. Alors que la plupart des "vrais" langages de programmation (Java, .Net, etc...) bénéficient d'un débugger pour faciliter cette tâche, JavaScript est un peu le parent pauvre. Les développeurs sont souvent réduits à devoir afficher les valeurs dans des alert(), ou en plein milieu de leur belle page web !

Hé bien, ce temps est fini ! Il est en effet possible, en JavaScript, d'afficher des valeurs dans la console à l'aide de l'instruction suivante :

console.debug("Hello World !");


Le texte sera affiché directement dans la console JavaScript, disponible via Outils -> Console d'erreurs sour Firefox. A noter que deux autres méthodes sont disponibles, respectivement pour tracer le déroulement du script et pour rapporter des erreurs :

console.log("Opération effectuée.");
console.error("Problème !");


Merci à Trefex pour cette astuce ;) !

vendredi 27 juillet 2007

Changer dynamiquement l'attribut onclick d'un élément HTML

Il arrive parfois de vouloir changer dynamiquement l'attribut onlcick d'un élément HTML. Supposons que l'on veuille attribuer la fonction suivante au clic sur un élément :

function newFunction() {
    alert('Hello World !');
}


L'astuce est qu'il ne faut pas passer une chaîne de caractère, ou un appel à la fonction, mais un pointeur vers celle-ci ! Ainsi, les deux notations suivantes sont fausses :

element.onclick = newFunction(); // faux
element.onclick = 'newFunction();'; // faux aussi !


La bonne manière de faire est :

element.onclick = newFunction;


Cependant, on ne peux pas passer d'arguments à notre fonction en procédant de cette manière. Il faut alors passer par une "fonction anonyme" :

function newFunction2(name) {
    alert('Hello ' + name + ' !');
}

element.onclick = function () {
    newFunction2('World');
}

mercredi 4 juillet 2007

Insérer des images à l'URL relative au thème en .Net

Une des nombreuses fonctionnalités du .Net, pour la création de sites web, est la possibilité d'utiliser des thèmes. Ceux-ci permettent d'avoir à disposition plusieurs jeux de fichiers CSS et d'images, ainsi qu'un moyen, via des "skins", de définir le style CSS par défaut des composants ASP.Net.

Cependant, un problème subsiste : on ne peut pas, en dehors des fichiers CSS et skins qui sont situés dans le dossier du thème, définir l'url d'une image de manière relative au dossier du thème (par exemple, "Images/logo.jpg" qui pointera vers "/App_Themes/Theme2/Images/logo.css" selon le thème choisi).

Ce problème peut heureusement être contourner très facilement, en étendant le composant Image pour lui faire remplacer dynamiquement l'url de l'image ! Il suffit de créer une nouvelle classe, de la faire étendre System.Web.UI.WebControls.Image, et de lui ajouter la méthode suivante :

protected override void Render(HtmlTextWriter writer) {
    string oldUrl = this.ImageUrl;
    this.ImageUrl = String.Format("~/App_Themes/{0}/{1}", this.Page.Theme, this.ImageUrl);

    base.Render(writer);

    this.ImageUrl = oldUrl;
}


Ensuite, dans votre page ASPX, vous l'utilisez de la manière suivante :

<VotrePrefixe:ThemeImage ID="ImageLogo" runat="server" ImageUrl="Images/logo.jpg"/>


Un grand merci à http://blog.iridescence.no pour son article sur la gestion des images dans les thèmes !

mardi 3 juillet 2007

Problème avec le PATH_INFO pour Dotclear 2

Une des nombreuses fonctionnalités de Dotclear 2 est de permettre l'utilisation du PATH_INFO (au lieu de QUERY_STRING) comme méthode pour générer les liens. En pratique, cela veut dire que vous aurez des liens du type :

  • http://20-100.ch/blog/index.php/post/2007/07/02/Lancement-du-blog

au lieu de :

  • http://20-100.ch/blog/index.php?post/2007/07/02/Lancement-du-blog.

Quelle différence ? Hé bien, tout d'abord, c'est plus joli ;) ! Ensuite, il paraît que c'est mieux pour le référencement (une URL en mode QUERY_STRING verrait parfois la partie située après le ? être ignorée... à confirmer, mais c'est toujours ça de gagné !). Et pour finir, ça apporte un grand plus pour les statistiques de votre blog : en mode QUERY_STRING, toutes les requêtes sont considérées comme étant sur la même page (index.php), tandis qu'en PATH_INFO, chaque poste sera considéré comme ayant sa propre adresse ! Ce qui permet d'avoir des statistiques plus détaillées...

Toujours est-il qu'au lancement de ce blog, il m'était impossible d'utiliser ce mode ! En regardant un peu de plus près comment le PATH_INFO fonctionne, voici ce que j'ai trouvé :

  1. Le serveur web (Apache) extrait les données situées après le nom de la page (le /post/2007/07/02/Lancement-du-blog) pour les mettre dans un variable système
  2. Le script PHP récupère cette valeur via la variable $_SERVER["PATH_INFO"]

Je suis donc allé voir dans mon phpinfo(), si cette variable était bien définie, et je ne l'ai pas trouvée. A la place, une variable $_SERVER["ORIG_PATH_INFO"] contenait la valeur attendue. J'ai donc poursuivi mes investigation, pour découvrir que ce cas de figure apparaît sur les serveurs où PHP est en mode CGI (ce qui est le cas chez Wrackweb). J'ai aussi découvert ces lignes dans le fichier inc/config.php de Dotclear 2 :

// If you have PATH_INFO issue, uncomment following lines
// if (!isset($_SERVER['ORIG_PATH_INFO'])) {
//      $_SERVER['ORIG_PATH_INFO'] = '';
// }
// $_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO'];


Il suffisait donc de décommenter ces lignes pour que le PATH_INFO fonctionne...

Moralité : RTFM, comme diraient mes "amis" de la mailing-list Typo3 ;-)...

lundi 2 juillet 2007

Forcer le téléchargement d'un fichier

Il peut arriver certaines fois que l'on veuille force le navigateur à télécharger un fichier plutôt que de l'afficher directement dans la fenêtre. Pour ce faire, il suffit de spécifier des headers HTTP indiquant au navigateur ce qu'il doit faire.

Dans notre cas, les headers qui nous intéressent sont les suivants :

  • Content-Type : application/force-download (indique au navigateur que le contenu doit être téléchargé et non affiché)
  • Content-Disposition : attachment; filename="Nom de votre fichier.pdf" (indique au navigateur que la réponse comprend un attachment, et spécifie le nom de celui-ci)

Ces headers s'ajoutent généralement dans le code côté serveur. Voici comment faire en PHP :

header('Content-Type : application/force-download');
header('Content-Disposition : attachment; filename="Nom de votre fichier.pdf"');


et en C# :

protected void Page_Load(object sender, EventArgs e) {
   string fileName = "Nom de votre fichier.pdf";
   this.Response.ContentType = "application/force-download";
   this.Response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + "\"");
}