fév 17 2011

[Post-it] cPanel : fichier php.ini custom pour un compte

Catégorie(s): DéveloppementSébastien @ 19:28

Je viens d’avoir à mettre en place un compte sous cPanel qui nécessitait une configuration PHP particulière. Ne souhaitant pas la voir impacter la configuration de base du serveur, j’ai pensé à mettre en place une configuration PHP personnalisée pour celui-ci . N’ayant pas trouvé dans cPanel un outil permettant de créer un fichier php.ini par compte qui hériterait des propriétés de celui par défaut, j’ai trouvé une autre façon de faire en trois étapes. Vous aurez besoin d’un accès root et du nom du compte (_USER_ dans la suite).

Etape 1 : Copier le fichier php.ini

copy /usr/local/lib/php.ini /home/_USER_/php.ini

Etape 2 : Editer le fichier php.ini du compte

vim /home/_USER_/php.ini

Etape 3 : Utiliser ce fichier php.ini pour ce compte seulement

vim /usr/local/apache/conf/php.conf

Ajouter :

<Directory /home/_USER_>
	suPHP_ConfigPath /home/_USER_
</Directory>

Redémarrer Apache, et voilà, c’est fait. Pour vérifier, un simple phpinfo() vous montrera que le fichier php.ini utilisé est « /home/_USER_/php.ini » et pas « /usr/local/lib/php.ini » comme sur les autres comptes. Si quelqu’un a une autre solution, n’hésitez pas à partager dans les commentaires.


jan 03 2011

[Post-it] Convertir un WSDL (RPC-Encoding) en fichier Java

Catégorie(s): DéveloppementSébastien @ 21:14

Pour interroger un webservice SOAP, il m’a été nécessaire de générer tout un ensemble de classes à l’aide d’un fichier WSDL (RPC). J’ai tout d’abord tenté d’utiliser Axis2 et sa classe WSDL2Java mais j’ai été confronté à plusieurs erreurs du type :

org.apache.axis2.schema.SchemaCompilationException:
can not find type {http://schemas.xmlsoap.org/soap/encoding/}
Array from the parent schema urn:Interface

La raison est simple : Axis2 n’accepte pas les WSDL encodés suivant le type RPC. RPC est en effet une vieille spécification de SOAP 1.1 qui rencontre de gros soucis de performances dès que la charge augmente (cf. Discover SOAP encoding’s impact on Web service performance).

Dans mon cas, je n’ai pas le choix et je dois obligatoirement d’utiliser ce fichier pour interroger un webservice tiers. Il m’a donc fallu utiliser Axis 1.4 et au final ça c’est avéré hyper simple vu que ça se résume à une ligne de commande :

java -classpath "lib\*" org.apache.axis.wsdl.WSDL2Java -av -p [PAKAGE NAME] [WSDL FILE] -D

Donc on commence par passer le dossier « lib » comme classpath. On appelle ensuite la classe WSDL2Java avec les arguments suivants :

  • -a = générer le code pour tous les éléments ;
  • -v = Afficher ce que WSDL2Java génère lorsqu’elle le fait ;
  • -p = Nom du package dans lequel seront contenu les classes générées ;
  • -D = Afficher les informations de débogage.

Le dernier n’est pas obligatoire mais j’en ai eu besoin parce que le fichier WSDL fournit n’était pas valide (2 erreurs que j’ai dû corriger manuellement).

Et voilà vos classes Java générées. C’était pas si compliqué que ça quand on oublie pas de faire attention à l’encodage du fichier WSDL. Seul bémol : le code généré n’est pas ce qu’il y a de plus propre et vous risquez de devoir mettre le nez dedans pour corriger certains trucs.

Sources et infos complémentaires :


oct 22 2010

[Post-it] Supprimer les fichiers de plus de X jours sous *nix

Catégorie(s): DéveloppementSébastien @ 19:34

Les fichiers de cache peuvent consommer beaucoup d’espace disque surtout lorsque le site a un traffic important. Comment faire pour supprimer les fichiers obsolètes sous *nix ? Avec une simple ligne de commande :

find -type f -mtime +2 -delete

Et hop, supprimés les fichiers qui n’ont pas été modifiés depuis plus de deux jours ! Reste plus qu’à planifier cette commande une fois par jour/semaine/etc via un CRON et le problème du dossier de cache qui bouffe tout l’espace disque est réglé. Pensez également à changer le 2 par la durée de conservation des fichiers (en jour).

Note : Il peut être possible de faire réaliser cette opération par le gestionnaire du cache en lui-même (genre avant de créer le cache, on supprime tout ce qui est périmé). Suivant comment c’est codé, ça risque de bouffer énormément de temps et donc ralentir le chargement des pages. Sur un site à petit traffic, ça peut ne pas posser de problème autrement oui.

PS : Si vous avez d’autres idées liées à la gestion des fichiers de cache, hésitez pas à m’en faire part dans les commentaires. :)


juil 22 2010

[Java] HttpClient 4 et authentification HTTP

Catégorie(s): DéveloppementSébastien @ 22:24

Tavailler avec différentes API permet souvent de faire des découvertes assez intéressantes. Celle d’aujourd’hui concerne l’authentification HTTP en utilisant la librairie HttpClient v4.0.1.

Pour appeler un webservice qui génère un rapport, on m’a donné une simple url :

http://user:pass@domain.com/get_report

Le nom d’utilisateur et le mot de passe comme ça, j’avoue que c’est pas ce que j’aurai fait mais étant donné que c’est pas un webservice interne, j’ai pas le choix. Un petit bout de code accompagnait cette url :

<?php
echo file_get_contents('http://user:pass@domain.com/get_report');
?>

Je teste et ça fonctionne correctement. Etant donné que mon outil est en Java et vu la facilité du truc, la mirgration depuis PHP ne risque, à première vue, pas poser trop trop de problèmes vu que j’ai déjà pas mal bossé avec la librairie HttpClient. Je m’attèle donc à la création d’une classe toute bête pour tester rapidement :

package net.delistage.httpauth;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

public class HttpAuthTest1 {
	private static final String URL = "http://user:pass@domain.com/get_report";

	/**
	 * We simply get the report from URL.
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			DefaultHttpClient client = new DefaultHttpClient();

			// Our request method
			HttpGet httpGet = new HttpGet(HttpAuthTest1.URL);

			// Try to get the report
			HttpResponse response = client.execute(httpGet);
			HttpEntity entity = response.getEntity();
			String content = IOUtils.toString(entity.getContent());
			System.out.println(content);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Comme vous pouvez le constater, rien de bien compliqué ou exotique : c’est basique à souhait. Et là bien sûr… ça ne fonctionne pas. Le contenu récupéré n’est pas le rapport mais le formulaire de login au webservice (parce que oui, il dispose d’un accès web en plus de l’API). Je ne me décourage pas et je tente d’envoyer le nom d’utilisateur et de mot de passe plus proprement en utilisant le CredentialsProvider :

package net.delistage.httpauth;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

public class HttpAuthTest2 {
	private static final String URL = "http://domain.com/get_report";
	private static final String USER = "user";
	private static final String PASS = "pass";

	/**
	 * We simply get the report from URL after being authenticated using simple
	 * credentials.
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			DefaultHttpClient client = new DefaultHttpClient();

			// Our request method
			HttpGet httpGet = new HttpGet(HttpAuthTest2.URL);

			// Set credentials
			UsernamePasswordCredentials creds = new UsernamePasswordCredentials(
					HttpAuthTest2.USER, HttpAuthTest2.PASS);
			client.getCredentialsProvider()
					.setCredentials(AuthScope.ANY, creds);

			// Try to get the report again
			HttpResponse response = client.execute(httpGet);
			HttpEntity entity = response.getEntity();
			String content = IOUtils.toString(entity.getContent());
			System.out.println(content);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

C’est déjà mieux en théorie. Je re-tente et là bingo : ça ne fonctionne toujours pas. Ca serait trop simple non ? Bon réfléchissons à la cause du problème : on dirait que le serveur tente de faire la requête sur la page avant de se connecter avec les identifiants que je lui fourni. Direction la documentation de la librairie concernant l’authentication HTTP et plus particulièrement le paragraphe sur l’authentification préventive. Pour cela, il faut utiliser une classe implémentant l’interface HttpRequestInterceptor. Le code d’exemple est assez clair mais plutôt que de l’implémenter dans une méthode, je préfère le faire dans une classe dédiée :

package net.delistage.httpauth;

import java.io.IOException;

import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;

public class BasicAuthRequestInterceptor implements HttpRequestInterceptor {
	public void process(final HttpRequest request, final HttpContext context)
			throws HttpException, IOException {

		AuthState authState = (AuthState) context
				.getAttribute(ClientContext.TARGET_AUTH_STATE);
		CredentialsProvider credsProvider = (CredentialsProvider) context
				.getAttribute(ClientContext.CREDS_PROVIDER);
		HttpHost targetHost = (HttpHost) context
				.getAttribute(ExecutionContext.HTTP_TARGET_HOST);

		// If not auth scheme has been initialized yet
		if (authState.getAuthScheme() == null) {
			AuthScope authScope = new AuthScope(targetHost.getHostName(),
					targetHost.getPort());
			// Obtain credentials matching the target host
			Credentials creds = credsProvider.getCredentials(authScope);
			// If found, generate BasicScheme preemptively
			if (creds != null) {
				authState.setAuthScheme(new BasicScheme());
				authState.setCredentials(creds);
			}
		}
	}
}

Il reste maintenant à l’utiliser dans ma classe et voir comment ça se comporte :

package net.delistage.httpauth;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

public class HttpAuthFinal {
	private static final String URL = "http://domain.com/get_report";
	private static final String USER = "user";
	private static final String PASS = "pass";

	/**
	 * We simply get the report from URL after being authenticated.
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			DefaultHttpClient client = new DefaultHttpClient();

			// Simple interceptor set as first interceptor in the protocol chain
			HttpRequestInterceptor preemptiveAuth = new BasicAuthRequestInterceptor();
			client.addRequestInterceptor(preemptiveAuth, 0);

			// Our request method
			HttpGet httpGet = new HttpGet(HttpAuthFinal.URL);

			// Set credentials
			UsernamePasswordCredentials creds = new UsernamePasswordCredentials(
					HttpAuthFinal.USER, HttpAuthFinal.PASS);
			client.getCredentialsProvider()
					.setCredentials(AuthScope.ANY, creds);

			// We can get the report now !
			HttpResponse response = client.execute(httpGet);
			HttpEntity entity = response.getEntity();
			String content = IOUtils.toString(entity.getContent());
			System.out.println(content);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Et cette fois, le test est concluant : mon rapport s’affiche bien correctement ! Par contre il reste un problème annoncé par la documentation : utiliser un intercepteur est un risque de sécurité car il est susceptible d’envoyer en clair les identifiants de connexion à des tiers non authorisés. Dans la mesure où je n’ai pas encore trouvé mieux ou une autre solution pour que ça fonctionne, je vais devoir faire avec malgré cela. Si des lecteurs passent par là et ont une autre solution ou idée, je suis tout ouïe.


jan 23 2010

Réaliser un panel à onglet simple avec Mootools

Catégorie(s): Développement,JavascriptSébastien @ 10:34

Objectifs

Je souhaitais réaliser un système de panel à onglet (ou tab panel in english) pour un site sur lequel je travaille. Le but est d’avoir peu de code, que ce soit facilement utilisable et implémentable mais également compatible avec la majorité des navigateurs. Sachant que je bosse avec le framework javascript Mootools, je vais l’utiliser pour me faciliter tout ça. Je souhaite également pouvoir mettre sur la même page plusieurs panels à onglets. Par contre, je tiens à ce que ça reste très simple, donc pas d’animation inutiles ou de fonctionnalités de-la-mort-qui-tue-mais-qui-servent-pas-à-grand-chose.

Le plus rapide aurait été de reprendre un code existant sur le net, mais c’est pas drôle. Alors amusé à le faire tout seul comme un grand.

Résultat

On commence par la démo disponible à cette adresse : http://delistage.net/tutos/tab-panel-simple-mootools/.

Et on passe maintenant au détail des différents fichiers et scripts nécessaires :

index.html

<div class="panel">
  <ul class="panel-header">
    <li class="panel-active"><a href="#" rel="panel-content-1">Content 1</a></li>
    <li><a href="#" rel="panel-content-2">Content 2</a></li>
    <li><a href="#" rel="panel-content-3">Content 3</a></li>
  </ul>

  <div id="panel-content-1" class="panel-content panel-display">
    panel-content-1
  </div><!-- #panel-content-1 -->

  <div id="panel-content-2" class="panel-content">
    panel-content-2
  </div><!-- #panel-content-2 -->

  <div id="panel-content-3" class="panel-content">
    panel-content-3
  </div><!-- #panel-content-3 -->
</div><!-- Panel 1 -->

Conteneur : un div ayant la classe panel, à ne pas changer.

La liste des onglets (ul) sans contrainte au niveau du nom de la classe. Chaque onglet est contenu dans une ligne (li) dont celui à sélectionner par défaut doit avoir la classe panel-active. On a ensuite l’onglet en lui-même sous forme de lien. L’attribut rel de cette balise doit contenir l’id de la div de contenu correspondant.

Différentes DIV de contenu doivent posséder un id (identique à celui rentré dans la balise rel de l’onglet) et la classe panel-content. Si vous souhaitez afficher un des panels par défaut, il suffit de lui ajouter la classe panel-display.

panels.js

Ce fichier contient la fonction en charge de la gestion des différents panels. Pas besoin de plus de commentaires que ceux qui sont dans le code je pense.

panelManager = function() {
  // On parcourt chaque panel
  $$('.panel').each(function(panelElt) {
    // On récupère les onglets ...
    var tabs = panelElt.getFirst('ul').getElements('a');

    // Et on les parcourt à leur tour
    tabs.each(function(tab) {
      /*
       * Ajout de l'évèment click
       */
      tab.addEvent('click', function() {
        // On regarde si le panel-content lié à cet onglet existe
        var contentDiv = $(this.rel);
        if(contentDiv != null) {
          // Si c'est le cas, on masque les panel-content
          panelElt.getElements('div').removeClass('panel-display');
          // On déselectionne tous les onglets
          panelElt.getFirst('ul').getElements('li').removeClass('panel-active');
          // On affiche le panel-content sélectionné
          contentDiv.addClass('panel-display');
          // On sélectionne l'onglet
          this.getParent().addClass('panel-active');
        }

        // Etant donné que notre onglet est un lien (a),
        // on retourne faux pour annuler son action
        return false;
      });
    });
  });
}

Sur les pages contenant un ou plusieurs panels, il vous suffit d’appeler cette fonction très simplement :

<script type="text/javascript" src="panels.js"></script>
<script type="text/javascript">
  window.addEvent('domready', function() {
    panelManager();
  });
</script>

panels.css

.panel { margin-bottom: 15px; } 

.panel-header { border-bottom: 1px solid #333333; height: 30px; }

.panel-header li {
  float: left;
  height: 24px;
  margin-right: 10px;
  padding: 0 10px;
  padding-top: 5px;
  border: 1px solid #666666;
  border-bottom: 1px solid #333333;
  background-color: #fafafa;
}

.panel-header li a { color: #555555; text-decoration: none; }

.panel-header li:first-child { margin-left: 5px; }

.panel-header li.panel-active {
  background-color: #eeeeee;
  border: 1px solid #333333;
  border-bottom: 1px dashed #333333;
  color: #333333;
}

.panel-header li.panel-active a {
  color: #333333;
  text-decoration: none;
  font-weight: bold;
}

.panel-content {
  clear: both;
  background-color: #eeeeee;
  border: 1px solid #333333;
  border-top: 0;
  padding: 5px;
  display: none;
}

.panel-display { display: block; }

Conclusion

Et voilà, vous avez là une petite fonction qui s’occupe d’afficher de la gestion des panels très simplement. Ça fonctionne avec Firefox 3.5, IE8 et Chrome 3.0 pour ceux que j’ai testé, mais étant donné que ça utilise Mootools, c’est censé être compatible avec la majorité des navigateurs. En cas de bugs ou de choses pas claires, les commentaires sont ouverts.


« Page précédentePage suivante »