<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>.: chiroux.com :. &#187; Informatique</title>
	<atom:link href="http://www.chiroux.com/category/informatique/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.chiroux.com</link>
	<description></description>
	<lastBuildDate>Fri, 09 Oct 2009 21:47:56 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=abc</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Installation d&#8217;un couple de serveurs dhcp et dns redondants</title>
		<link>http://www.chiroux.com/installation-dun-couple-de-serveurs-dhcp-et-dns-redondants/</link>
		<comments>http://www.chiroux.com/installation-dun-couple-de-serveurs-dhcp-et-dns-redondants/#comments</comments>
		<pubDate>Tue, 29 Sep 2009 22:15:04 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Administration Système]]></category>
		<category><![CDATA[bind]]></category>
		<category><![CDATA[configuration]]></category>
		<category><![CDATA[dhcp]]></category>
		<category><![CDATA[jaunty]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[redondant]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/?p=499</guid>
		<description><![CDATA[<p class="wp-caption-text">Networked - CC - auteur: http://www.flickr.com/photos/superkimbo/1727117782/</p>
Cette fois ci, on s&#8217;oriente plus du côté des petites entreprises qui ont rapidement besoin d&#8217;une gestion interne du réseau, simple, efficace et sécurisée.
Le but de cet article est donc de montrer pas à pas comment monter deux serveurs dhcp+dns qui se backupent l&#8217;un l&#8217;autre de manière transparente. Ces [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_528" class="wp-caption alignleft" style="width: 510px"><img src="http://www.chiroux.com/wp-content/uploads/2009/09/network_servers.jpg" alt="Networked - CC - auteur: http://www.flickr.com/photos/superkimbo/1727117782/" title="network_servers" width="400" height="266" class="size-full wp-image-528" /><p class="wp-caption-text">Networked - CC - auteur: http://www.flickr.com/photos/superkimbo/1727117782/</p></div><br />
Cette fois ci, on s&#8217;oriente plus du côté des petites entreprises qui ont rapidement besoin d&#8217;une gestion interne du réseau, simple, efficace et sécurisée.<br />
Le but de cet article est donc de montrer pas à pas comment monter deux serveurs dhcp+dns qui se backupent l&#8217;un l&#8217;autre de manière transparente. Ces deux serveurs seront les piliers du réseau naissant de la petite entreprise.</p>
<p>Pour l&#8217;installation de base, il suffit de suivre le <a href="http://www.chiroux.com/installation-dun-serveur-web-securise-sous-ubuntu-9-04server/">tuto d&#8217;installation d&#8217;un serveur ubuntu</a>, en s&#8217;arrêtant juste avant l&#8217;install de lighttpd qui n&#8217;est pas nécessaire ici (mais cela marche aussi avec bien sûr).<br />
<span id="more-499"></span><br />
Le tuto a l&#8217;air assez long mais il est relativement simple et rapide à mettre en oeuvre : en partant de deux serveurs vides, non installés, suivre les deux tutos d&#8217;install générique et celui ci de dhcp+dns, il suffit d&#8217;a peine 2H pour tout terminer et avoir ses serveurs fonctionnels.</p>
<p></p>
<span id="Table_des_matires"><h2>Table des matières</h2></span>
<p><div class='toc wptoc'>
<h2>Contents</h2>
<ol class='toc-odd level-1'>
	<li>
		<a href="#Table_des_matires">Table des matières</a>
	</li>
	<li>
		<a href="#Introduction">Introduction</a>
	</li>
	<li>
		<a href="#Installation_de_base_et_commune_aux_deux_serveurs">Installation de base et commune aux deux serveurs</a>
		<ol class='toc-even level-2'>
			<li>
				<a href="#Installation_des_paquets">Installation des paquets</a>
				<ol class='toc-odd level-3'>
					<li>
						<a href="#dhcp">dhcp</a>
					</li>
					<li>
						<a href="#dns">dns</a>
					</li>
				</ol>
			<li>
				<a href="#Prparation_du_firewall">Préparation du firewall</a>
			</li>
			<li>
				<a href="#prparation_du_dns_:_configuration_du_logging_en_mode_debug">préparation du dns : configuration du logging en mode debug</a>
			</li>
		</ol>
	<li>
		<a href="#sur_le_matre_ns1">sur le maître (ns1)</a>
		<ol class='toc-even level-2'>
			<li>
				<a href="#travaux_prparatoire_:_gnration_des_clefs_partages">travaux préparatoire : génération des clefs partagées</a>
				<ol class='toc-odd level-3'>
					<li>
						<a href="#rndc">rndc</a>
					</li>
					<li>
						<a href="#clef_pour_mise__jour_venant_du_dhcp">clef pour mise à jour venant du dhcp</a>
					</li>
				</ol>
			<li>
				<a href="#dhcp_1">dhcp</a>
			</li>
			<li>
				<a href="#dns_1">dns</a>
				<ol class='toc-odd level-3'>
					<li>
						<a href="#named.conf">named.conf</a>
					</li>
					<li>
						<a href="#named.conf.options">named.conf.options</a>
					</li>
					<li>
						<a href="#named.conf.local">named.conf.local</a>
					</li>
					<li>
						<a href="#les_fichiers_de_zones">les fichiers de zones</a>
					</li>
				</ol>
			<li>
				<a href="#redmarrage">redémarrage</a>
			</li>
		</ol>
	<li>
		<a href="#sur_lesclave_ns2">sur l'esclave (ns2)</a>
		<ol class='toc-even level-2'>
			<li>
				<a href="#dhcp_2">dhcp</a>
			</li>
			<li>
				<a href="#dns_2">dns</a>
				<ol class='toc-odd level-3'>
					<li>
						<a href="#named.conf_1">named.conf</a>
					</li>
					<li>
						<a href="#named.conf.options_1">named.conf.options</a>
					</li>
					<li>
						<a href="#named.conf.local_1">named.conf.local</a>
					</li>
				</ol>
			<li>
				<a href="#redmarrage_1">redémarrage</a>
			</li>
		</ol>
	<li>
		<a href="#Debugging">Debugging</a>
		<ol class='toc-even level-2'>
			<li>
				<a href="#analyser_les_logs">analyser les logs</a>
			</li>
			<li>
				<a href="#jai_beau_changer_mes_conf_dns_mon_problme_persiste">j'ai beau changer mes conf dns, mon problème persiste</a>
			</li>
</ol>
</ol>
</ol>
</div>
<div class='wptoc-end'>&nbsp;</div>
<span id="Introduction"><h2>Introduction</h2></span>
<ul>
<li>Nous allons utiliser, classiquement, les serveurs bind et dhcpd 3</li>
<li>L&#8217;installation s&#8217;effectue sur deux machines, que l&#8217;on nommera respectivement ns1 et ns2</li>
<li>Le domaine installé est volontairement local et sera appelé : monentreprise.local</li>
<li>Le réseau local sera la classe C :  192.168.1.0/24</li>
<li>et les machines ns1 et ns2 auront respectivement les ips : 192.168.1.10 et 192.168.1.11</li>
<li>la passerelle internet sera 192.168.1.1</li>
</ul>
<p>toutes ces valeurs sont des hypothèses et doivent être adaptées en fonction de votre config.</p>
<p>A la fin de l&#8217;installation, nous devrions avoir deux serveurs, un &#8216;maitre&#8217; dhcp et dns (ns1) et son &#8216;esclave&#8217; (ns2).<br />
Les baux dhcp délivrés par le maitre étant mis à jour automatiquement dans le dns, le tout répliqué automatiquement sur l&#8217;esclave.<br />
L&#8217;esclave prenant le relais automatiquement si le maître ne marche plus.</p>
<span id="Installation_de_base_et_commune_aux_deux_serveurs"><h2>Installation de base et commune aux deux serveurs</h2></span>
<p>Comme son nom l&#8217;indique, il faut donc executer ces lignes de façon identique sur chacun des deux serveurs.</p>
<span id="Installation_des_paquets"><h3>Installation des paquets</h3></span>
<span id="dhcp"><h4>dhcp</h4></span>
<pre class="brush: bash;">
sudo apt-get install dhcp3-server
</pre>
<p>on stoppe le serveur tant qu&#8217;il n&#8217;est pas configuré pour éviter de mettre la grouille sur le réseau :</p>
<pre class="brush: bash;">
sudo /etc/init.d/dhcp3-server stop
</pre>
<span id="dns"><h4>dns</h4></span>
<pre class="brush: bash;">
sudo apt-get install bind9
</pre>
<span id="Prparation_du_firewall"><h3>Préparation du firewall</h3></span>
<p>Dans le firewall, il est nécessaire d&#8217;ouvrir explicitement quelques ports pour :</p>
<ul>
<li>le dns</li>
<li>la mise a jour dynamique des dns suite aux baux délivrés par le dhcp</li>
<li>la synchronisation des dhcps entre eux
<li>
</ul>
<p>Editer le fichier du firewall :</p>
<pre class="brush: bash;">
sudo vim /etc/init.d/server_iptables
</pre>
<p>et ajouter les lignes suivantes :</p>
<pre class="brush: bash;">
# Autorise les requetes DNS
iptables -A INPUT -s 192.168.1.0/24 -p tcp -i eth0 --dport 53 -j ACCEPT
iptables -A INPUT -s 192.168.1.0/24 -p udp -i eth0 --dport 53 -j ACCEPT
# Autorise les MAJ DDNS
iptables -A INPUT -s 192.168.1.0/24 -p tcp -i eth0 --dport 953 -j ACCEPT
# Autorise le failover dhcp
iptables -A INPUT -s 192.168.1.0/24 -p tcp -i eth0 --dport 647 -j ACCEPT
</pre>
<p>On remarque ici qu&#8217;on autorise ces ports uniquement depuis les adresses de notre réseau local. On pourrait encore durir les règles en autorisant les maj ddns et les synchros dhcp uniquement depuis nos deux serveurs.</p>
<p>Relancer les règles du Firewall:</p>
<pre class="brush: bash;">
sudo /etc/init.d/server_iptables
</pre>
<span id="prparation_du_dns_:_configuration_du_logging_en_mode_debug"><h3>préparation du dns : configuration du logging en mode debug</h3></span>
<pre class="brush: bash;">
sudo vim /etc/bind/named.conf.debug.log
</pre>
<pre class="brush: bash;">
logging {
  category &quot;default&quot; { &quot;debug&quot;; };
  category &quot;general&quot; { &quot;debug&quot;; };
  category &quot;database&quot; { &quot;debug&quot;; };
  category &quot;security&quot; { &quot;debug&quot;; };
  category &quot;config&quot; { &quot;debug&quot;; };
  category &quot;resolver&quot; { &quot;warning&quot;; };
  category &quot;xfer-in&quot; { &quot;debug&quot;; };
  category &quot;xfer-out&quot; { &quot;debug&quot;; };
  category &quot;notify&quot; { &quot;debug&quot;; };
  category &quot;client&quot; { &quot;debug&quot;; };
  category &quot;unmatched&quot; { &quot;debug&quot;; };
  category &quot;network&quot; { &quot;debug&quot;; };
  category &quot;update&quot; { &quot;debug&quot;; };
  category &quot;queries&quot; { &quot;warning&quot;; };
  category &quot;dispatch&quot; { &quot;debug&quot;; };
  category &quot;dnssec&quot; { &quot;debug&quot;; };
  category &quot;lame-servers&quot; { &quot;debug&quot;; };

  channel &quot;debug&quot; {
    file &quot;/var/log/bind9/nameddbg&quot; versions 2 size 50m;
    print-time yes;
    print-category yes;
    print-severity yes;
  };

  channel &quot;warning&quot; {
    file &quot;/var/log/bind9/nameddbg&quot; versions 2 size 50m;
    severity warning;
    print-time yes;
    print-category yes;
    print-severity yes;
  };
};
</pre>
<p>Cette conf est très verbose, il sera peut-être nécessaire de la réduire une fois l&#8217;installation achevée et fonctionnelle.</p>
<p>Par défault, le répertoire /var/log/bind9 n&#8217;existe pas et est bloqué par apparmor, il faut donc le créer et l&#8217;autoriser :</p>
<pre class="brush: bash;">
sudo mkdir /var/log/bind9
sudo chown bind:bind /var/log/bind9
</pre>
<p>Configurer apparmor pour autoriser l&#8217;écriture dans le repertoire du log: </p>
<pre class="brush: bash;">
sudo vim /etc/apparmor.d/usr.sbin.named
</pre>
<p>et ajouter à la fin (dans la zone sur les logs): </p>
<pre class="brush: bash;">
/var/log/bind9/** rw,
   /var/log/bind9/ rw,
</pre>
<p>redémarrer apparmor (on relancera bind à la fin de la config)</p>
<pre class="brush: bash;">
sudo /etc/init.d/apparmor restart
</pre>
<span id="sur_le_matre_ns1"><h2>sur le maître (ns1)</h2></span>
<span id="travaux_prparatoire_:_gnration_des_clefs_partages"><h3>travaux préparatoire : génération des clefs partagées</h3></span>
<p>des clefs seront nécessaires pour la mise à jour du dns par le dhcp, ainsi que pour la configuration rndc (rndc est un outil de configuration pour bind, optionnel, mais bind aime bien qu&#8217;il soit là). On va donc aussi configurer rndc sur le maitre.</p>
<span id="rndc"><h4>rndc</h4></span>
<pre class="brush: bash;">
cd /etc/bind
sudo dnssec-keygen -a hmac-md5 -b 256 -n HOST ns1
</pre>
<p>ce programme va générer deux fichier nommés Kns1.xxxxxxxx.key et Kns1.xxxxxxxx.private<br />
le fichier .key va devenir notre clef rndc :</p>
<pre class="brush: bash;">
sudo mv Kns1.xxxxxxxxx.key rndc.key
</pre>
<p>(remplacer les xxxxxxxx par le bon nom)</p>
<p>ensuite afficher le contenu du fichier private :</p>
<pre class="brush: bash;">
sudo cat Kns1.xxxxxxxxx.private
</pre>
<p>et copier le texte après &#8216;Key:&#8217;<br />
ensuite créer le fichier de conf rndc :</p>
<pre class="brush: bash;">
sudo vim /etc/bind/rndc.conf
</pre>
<p>et coller les éléments suivants:</p>
<pre class="brush: bash;">
key rdnc-key {
  algorithm hmac-md5;
  secret &quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&quot;;
};

options {
  // what host should rndc attempt to control by default
  default-server 127.0.0.1;
  // and what key should it use to communicate with named
  default-key &quot;rdnc-key&quot;;
};

server 127.0.0.1 {
  // always use this key with this host
  key &quot;rdnc-key&quot;;
};
</pre>
<p>et remplacer les XXXXX du secret par ce qu&#8217;on a copié après &#8216;Key:&#8217; du fichier Kns1.xxxxxxxxx.private</p>
<p>on peut maintenant effacer le fichier .private :</p>
<pre class="brush: bash;">
sudo rm Kns1.xxxxxxxxx.private
</pre>
<span id="clef_pour_mise__jour_venant_du_dhcp"><h4>clef pour mise à jour venant du dhcp</h4></span>
<pre class="brush: bash;">
cd /etc/bind
sudo dnssec-keygen -a hmac-md5 -b 128 -n USER dhcpupdate
</pre>
<p>Dans le fichier .key genéré, copier la clef : La clef est la dernière chaine de caractère du fichier .key, par exemple ici : dhcpupdate. IN KEY 0 3 157 Zihefb3NqqepA/5RgzbicM==<br />
la clef est : Zihefb3NqqepA/5RgzbicM==</p>
<p>avec cette clef, on a généré des directives de configuration pour le dhcp et pour le dns, elles auront l&#8217;aspect suivant :<br />
pour le dns :</p>
<pre class="brush: bash;">
key dhcpupdate {
  algorithm hmac-md5;
  secret &quot;ICICOLLERLACLEFSECRETEGENEREE&quot;;
};
</pre>
<p>pour le dhcp (pareil que pour le dns, mais sans les guillemets) :</p>
<pre class="brush: bash;">
key dhcpupdate {
  algorithm hmac-md5;
  secret ICICOLLERLACLEFSECRETEGENEREE;
};
</pre>
<span id="dhcp_1"><h3>dhcp</h3></span>
<pre class="brush: bash;">
sudo vim /etc/dhcp3/dhcpd.conf
</pre>
<p>et mettre le fichier de conf suivant :</p>
<pre class="brush: bash;">
#
# Sample dhcpd.conf file
#

# ======== Mise a jour DDNS ========
ddns-domainname &quot;monentreprise.local&quot;;
ddns-rev-domainname &quot;1.168.192.in-addr.arpa&quot;;
#Mehode de mise a  jour du DNS
ddns-update-style interim;
#Mise a  jour autorisee
ddns-updates on;
#ici on force la maj par le dhcp et non par le client
ignore client-updates;
#on force la maj des ipfixes
update-static-leases on;
# Clef partagee dhcpd et bind9
key dhcpupdate {
    algorithm hmac-md5;
    secret ICICOLLERLACLEFSECRETEGENEREE;
};

# ======== Option Generales du dhcp ========

# Server name
server-name &quot;dhcp.monentreprise.local&quot;;

# option definitions common to all supported networks...
option domain-name &quot;monentreprise.local&quot;;
option domain-name-servers 192.168.1.10, 192.168.1.11;

default-lease-time 3600;
max-lease-time 7200;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

# No service will be given on this subnet, but declaring it helps the
# DHCP server to understand the network topology.
subnet 192.168.1.0 netmask 255.255.255.0 {
}

#Zones
zone 1.168.192.in-addr.arpa. {
  primary 127.0.0.1;
  key dhcpupdate;
}

zone linkcareservices.local. {
  primary 127.0.0.1;
  key dhcpupdate;
}

# ======== Failover configuration ========
failover peer &quot;dhcp-failover&quot; {
  primary; # declare this to be the primary server
  address 192.168.1.10;
  port 647;
  peer address 192.168.1.11;
  peer port 647;
  max-response-delay 30;
  max-unacked-updates 10;
  load balance max seconds 3;
  mclt 1800;
  split 128;
}

# ======== Reseaux ========
## déclaration sous réseau 192.168.1.*
subnet 192.168.1.0 netmask 255.255.255.0 {
  # Si vous voulez spécifier un domaine différent de celui par défaut :
  #option domain-name &quot;mon_domaine.qqc&quot;;
  ## Adresse de diffusion
  option broadcast-address 192.168.1.255;
  ## routeur par défaut
  option routers 192.168.1.1;
        ## Plage d'attribution d'adresse
        ## Ici on ouvre pour l'instant une 'petite' plage entre .50 et .99, c'est un exemple, on peut mettre plus.
  pool {
    failover peer &quot;dhcp-failover&quot;;
    range 192.168.1.50 192.168.1.99;
  }
  # évalue si l'adresse est déjà attribuée
  ping-check = 1;
}

host ns1 {
  hardware ethernet 00:00:00:00:00:00;
  fixed-address 192.168.1.10;
}

host ns2 {
  hardware ethernet 00:00:00:00:00:00;
  fixed-address 192.168.1.11;
}
</pre>
<p>Pour que le fichier de configuration soit complet, il faudra remplacer les ICICOLLERLACLEFSECRETEGENEREE de la clef par la clef générée précedemment.<br />
Il y a également deux baux statiques dans le fichier de configuration pour nos serveurs ns1 et ns2, il faut remplacer les 00:00&#8230; des adresses MAC par les vraies adresses mac de vos machines.</p>
<span id="dns_1"><h3>dns</h3></span>
<span id="named.conf"><h4>named.conf</h4></span>
<p>ce fichier représente la configuration principale du dns, on va juste ajouter quelques directives en début de fichier :</p>
<pre class="brush: bash;">
sudo vim /etc/bind/named.conf
</pre>
<p>et ajouter en début de fichier les éléments suivants:</p>
<pre class="brush: bash;">
acl internals { 127.0.0.0/8; 192.168.1.0/24; };

controls {
  inet 127.0.0.1 allow { 127.0.0.1; localhost; } keys { &quot;rdnc-key&quot;; };
};

key rdnc-key {
  algorithm hmac-md5;
  secret &quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&quot;;
};

key dhcpupdate {
  algorithm hmac-md5;
  secret &quot;ICICOLLERLACLEFSECRETEGENEREE&quot;;
};
</pre>
<p>en remplaçant XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX par la clef rndc générée précedemment, et en remplaçant ICICOLLERLACLEFSECRETEGENEREE par la clef dhcpupdate générée plus haut.</p>
<span id="named.conf.options"><h4>named.conf.options</h4></span>
<pre class="brush: bash;">
sudo vim /etc/bind/named.conf.options
</pre>
<p>Comme on est en train de construire des serveurs pour un petit réseau interne, nous n&#8217;avons pas besoin que les dns résolvent tout internet, on va donc les configurer pour faire relais vers d&#8217;autre dns. L&#8217;avantage, c&#8217;est qu&#8217;on peut choisir ceux qu&#8217;on veut, et pas obligatoirement ceux de son ISP, même si au final il est quand même préférable d&#8217;en choisir des pas trop loin et si possible performants. Les DNS des ISP répondent souvent à ces problématiques.<br />
Vous pouvez aussi mettre simplement le dns de votre routeur/box en relais, nos dns internes servant au final à gérer les zones internes.</p>
<p>Le fichier complet ressemble donc à ceci :</p>
<pre class="brush: bash;">
options {
        directory &quot;/var/lib/bind&quot;;

        // If there is a firewall between you and nameservers you want
        // to talk to, you may need to fix the firewall to allow multiple
        // ports to talk.  See http://www.kb.cert.org/vuls/id/800113

        // If your ISP provided one or more IP addresses for stable
        // nameservers, you probably want to use them as forwarders.
        // Uncomment the following block, and insert the addresses replacing
        // the all-0's placeholder.

        // forwarders {
        //      0.0.0.0;
        // };
  forwarders {
    aa.bb.cc.dd;
    ee.ff.gg.hh;
    192.168.1.1;
  };

        auth-nxdomain no;    # conform to RFC1035
        listen-on-v6 { none; };
  listen-on { 127.0.0.1; 192.168.1.10; 192.168.1.11; };

  // transférer les informations de zones aux DNS secondaires
  allow-transfer { 192.168.1.11; };

  // Accepter les requêtes pour le réseau interne uniquement
  allow-query { internals; };

  // Autoriser les requêtes récursives pour les hôtes locaux
  allow-recursion { internals; };

  // Ne pas rendre publique la version de BIND
  version none; 

};
</pre>
<p>dans la zone &#8216;forwarders&#8217;, vous pouvez donc remplacer les ips aa.bb.cc.dd et ee.ff.gg.hh par deux dns publics ou ceux de votre isp, ou vous pouvez enlever les lignes pour ne garder que le dns du routeur.<br />
Dans cette config, on autorise le transfert des infos de DNS vers notre futur secondaire.</p>
<p>Il y a un autre élément important dans ce fichier de config : le répertoire par défaut de travail de bind qui doit être /var/lib/bind : sous ubuntu 9.04, par défaut, seul ce répertoire autorise bind à écrire dans le fichier et c&#8217;est nécessaire pour la maj ddns venant du dhcp.</p>
<span id="named.conf.local"><h4>named.conf.local</h4></span>
<pre class="brush: bash;">
sudo vim /etc/bind/named.conf.local
</pre>
<p>insérer les zones et les reverses :</p>
<pre class="brush: bash;">
///
// Do any local configuration here
//

// Consider adding the 1918 zones here, if they are not used in your
// organization
//include &quot;/etc/bind/zones.rfc1918&quot;;

include &quot;/etc/bind/named.conf.debug.log&quot;; 

zone &quot;monentreprise.local&quot; {
    type master;
    notify yes;
    allow-transfer { 192.168.1.11; } ;
    file &quot;monentreprise.local.hosts&quot;;
};

zone &quot;1.168.192.in-addr.arpa&quot; {
    type master;
    notify yes;
    allow-transfer { 192.168.1.11; } ;
    file &quot;1.168.192.in-addr.arpa.zone&quot;;
};
</pre>
<p>Cette config indique que ce dns est &#8216;master&#8217; pour les deux zones et qu&#8217;il notifie et transfère les infos de zones vers le secondaire.<br />
De plus, il pointe sur une configuration de debug particulière qui est utile pour l&#8217;analyse des problème de la configuration et que l&#8217;on a paramétré précédemment.</p>
<span id="les_fichiers_de_zones"><h4>les fichiers de zones</h4></span>
<p>Il faut maintenant créer les fichier de la zone et du reverse. On va les créer dans /etc/bind, puis les lier dans /var/lib/bind/ où va vraiment aller les chercher bind, suite à notre config dans options. </p>
<pre class="brush: bash;">
sudo vim /etc/bind/monentreprise.local.hosts
</pre>
<p>et mettre les infos suivantes (c&#8217;est un exemple, mais qui est paramétré avec deux serveurs DNS pour préparer la conf/maitre-esclave) </p>
<pre class="brush: bash;">
@      IN     SOA     ns1.monentreprise.local. email.monentreprise.com. (
            20092909003 ; serial
            600 ; refresh after 10 minutes (for testing purpose)
            3600 ; retry after 1 hour
            604800 ; expires after 1 week
            86400 ) ; minimum TTL of 1 day
@     IN     NS     ns1.monentreprise.local.
@     IN     NS     ns2.monentreprise.local.
gw          IN  A       192.168.1.1
ns1         IN  A       192.168.1.10
ns2         IN  A       192.168.1.11
dhcp        IN  CNAME   ns1
dhcp2         IN  CNAME   ns2
</pre>
<p>Dans la premiere ligne, il faut indiquer un email après le serveur (ne pas oublier les &#8216;.&#8217; à la fin). bizarrement, l&#8217;email est de la forme email.domaine.suffixe. alors que cela veut dire email@domaine.suffixe</p>
<p>faire de même avec le reverse: </p>
<pre class="brush: bash;">
sudo vim /etc/bind/1.168.192.in-addr.arpa.zone
</pre>
<pre class="brush: bash;">
@       IN    SOA     ns1.monentreprise.local. email.monentreprise.com. (
            20090929002 ; serial
            600 ; refresh after 10 minutes (for testing purpose)
            3600 ; retry after 1 hour
            604800 ; expires after 1 week
            86400 ) ; minimum TTL of 1 day
@       IN     NS     ns1.monentreprise.local.
@       IN     NS     ns2.monentreprise.local.
1       IN     PTR    gw.monentreprise.local.
10     IN     PTR    ns1.monentreprise.local.
11     IN     PTR    ns2.monentreprise.local.
</pre>
<p>Pour ces deux fichiers, si vous les modifiez à la main, il est important de faire évoluer le sérial à chaque modification. Sinon le DNS principal n&#8217;ira pas notifier le secondaire.</p>
<p>maintenant on va faire en sorte que ces fichiers soient dispos dans /var/lib/bind et modifiables par bind: </p>
<pre class="brush: bash;">
sudo chown bind:bind /etc/bind/monentreprise.local.hosts
sudo chown bind:bind /etc/bind/1.168.192.in-addr.arpa.zone
sudo ln -s /etc/bind/monentreprise.local.hosts  /var/lib/bind/monentreprise.local.hosts
sudo ln -s /etc/bind/1.168.192.in-addr.arpa.zone  /var/lib/bind/1.168.192.in-addr.arpa.zone
</pre>
<span id="redmarrage"><h3>redémarrage</h3></span>
<p>on peut enfin redémarrer bind et démarrer le dhcp</p>
<pre class="brush: bash;">
sudo /etc/init.d/bind9 restart
sudo /etc/init.d/dhcp3-server start
</pre>
<p><b>Attention :</b> il ne faut pas oublier de désactiver le ou les précédents serveurs dhcp sur le réseau (celui du routeur/de la box par exemple).</p>
<span id="sur_l8217esclave_ns2"><h2>sur l&#8217;esclave (ns2)</h2></span>
<p>La conf sur l&#8217;esclave est plus simple car il n&#8217;y a pas de système de maj ddns et les fichiers de zones dns sur récupérées automatiquement du maitre.</p>
<span id="dhcp_2"><h3>dhcp</h3></span>
<pre class="brush: bash;">
sudo vim /etc/dhcp3/dhcpd.conf
</pre>
<p>et mettre la conf suivante :</p>
<pre class="brush: bash;">
#
# Sample dhcpd.conf file
#

# ======== Mise a jour DDNS ========
ddns-update-style none;

# ======== Option Generales du dhcp ========
# Server name
server-name &quot;dhcp2.monentreprise.local&quot;;

# option definitions common to all supported networks...
option domain-name &quot;monentreprise.local&quot;;
option domain-name-servers 192.168.1.10, 192.168.1.11;

default-lease-time 3600;
max-lease-time 7200;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

# No service will be given on this subnet, but declaring it helps the
# DHCP server to understand the network topology.
subnet 192.168.1.0 netmask 255.255.255.0 {
}

# ======== Failover configuration ========
failover peer &quot;dhcp-failover&quot; {
  secondary; # declare this to be the secondary server
  address 192.168.1.11;
  port 647;
  peer address 192.168.1.10;
  peer port 647;
  max-response-delay 30;
  max-unacked-updates 10;
  load balance max seconds 3;
}

# ======== Reseaux ========
## déclaration sous réseau 192.168.1.*
subnet 192.168.1.0 netmask 255.255.255.0 {
  # Si vous voulez spécifier un domaine différent de celui par défaut :
  #option domain-name &quot;mon_domaine.qqc&quot;;
  ## Adresse de diffusion
  option broadcast-address 192.168.1.255;
  ## routeur par défaut
  option routers 192.168.1.1;
        ## Plage d'attribution d'adresse
        # Ici on ouvre pour l'instant une 'petite' plage entre .50 et .99, c'est un exemple, on peut mettre plus.
  pool {
    failover peer &quot;dhcp-failover&quot;;
    range 192.168.1.50 192.168.1.99;
  }
  # évalue si l'adresse est déjà attribuée
  ping-check = 1;
}

host ns1 {
  hardware ethernet 00:00:00:00:00:00;
  fixed-address 192.168.1.10;
}

host ns2 {
  hardware ethernet 00:00:00:00:00:00;
  fixed-address 192.168.1.11;
}
</pre>
<p>Comme pour le dhcp maître, il y a deux baux statiques dans le fichier de configuration pour nos serveurs ns1 et ns2, il faut remplacer les 00:00… des adresses MAC par les vraies adresses mac de vos machines.</p>
<span id="dns_2"><h3>dns</h3></span>
<span id="named.conf_1"><h4>named.conf</h4></span>
<pre class="brush: bash;">
sudo vim /etc/bind/named.conf
</pre>
<p>ajouter juste, en début de fichier, l&#8217;acl internals :</p>
<pre class="brush: bash;">
acl internals { 127.0.0.0/8; 192.168.1.0/24; };
</pre>
<span id="named.conf.options_1"><h4>named.conf.options</h4></span>
<pre class="brush: bash;">
sudo vim /etc/bind/named.conf.options
</pre>
<p>Ce fichier est très proche de celui du maitre, sauf qu&#8217;il n&#8217;a plus la directive &#8216;allow-transfer&#8217;:</p>
<pre class="brush: bash;">
options {
        directory &quot;/var/lib/bind&quot;;

        // If there is a firewall between you and nameservers you want
        // to talk to, you may need to fix the firewall to allow multiple
        // ports to talk.  See http://www.kb.cert.org/vuls/id/800113

        // If your ISP provided one or more IP addresses for stable
        // nameservers, you probably want to use them as forwarders.
        // Uncomment the following block, and insert the addresses replacing
        // the all-0's placeholder.

        // forwarders {
        //      0.0.0.0;
        // };
  forwarders {
    aa.bb.cc.dd;
    ee.ff.gg.hh;
    192.168.1.1;
  };

        auth-nxdomain no;    # conform to RFC1035
        listen-on-v6 { none; };
  listen-on { 127.0.0.1; 192.168.1.10; 192.168.1.11; };

  // transférer les informations de zones aux DNS secondaires
  allow-transfer { 192.168.1.11; };

  // Accepter les requêtes pour le réseau interne uniquement
  allow-query { internals; };

  // Autoriser les requêtes récursives pour les hôtes locaux
  allow-recursion { internals; };

  // Ne pas rendre publique la version de BIND
  version none; 

};
</pre>
<span id="named.conf.local_1"><h4>named.conf.local</h4></span>
<pre class="brush: bash;">
sudo vim /etc/bind/named.conf.local
</pre>
<p>Ici la conf est sensiblement différente, car on précise que les zones sont &#8216;esclaves&#8217; :</p>
<pre class="brush: bash;">
//
// Do any local configuration here
//

// Consider adding the 1918 zones here, if they are not used in your
// organization
//include &quot;/etc/bind/zones.rfc1918&quot;;

include &quot;/etc/bind/named.conf.debug.log&quot;; 

zone &quot;linkcareservices.local&quot; {
    type slave;
    masters {192.168.1.10;} ;
    file &quot;linkcareservices.local.hosts&quot;;
};

zone &quot;1.168.192.in-addr.arpa&quot; {
    type slave;
    masters {192.168.1.10;} ;
    file &quot;1.168.192.in-addr.arpa.zone&quot;;
};
</pre>
<p>Il n&#8217;y a pas besoin de créer à la main les fichiers de zones : ils seront transférés automatiquement depuis le maitre.</p>
<span id="redmarrage_1"><h3>redémarrage</h3></span>
<p>on peut enfin redémarrer bind et démarrer le dhcp</p>
<pre class="brush: bash;">
sudo /etc/init.d/bind9 restart
sudo /etc/init.d/dhcp3-server start
</pre>
<span id="Debugging"><h2>Debugging</h2></span>
<p>Ce tuto est censé marcher directement, mais vous rencontrez des problèmes, voici quelques infos / trucs pour débugger les problèmes. J&#8217;essaierais de faire évoluer cette zone au fur et à mesure.</p>
<span id="analyser_les_logs"><h3>analyser les logs</h3></span>
<p>les logs du dhcp sont dans /var/log/syslog<br />
les logs du dns sont dans  /var/log/bind9/nameddbg </p>
<p>Avant tout, il faut bien regarder ces logs à la recherce de problèmes</p>
<span id="j8217ai_beau_changer_mes_conf_dns_mon_problme_persiste"><h3>j&#8217;ai beau changer mes conf dns, mon problème persiste</h3></span>
<p>Je me suis cassé la tête des heures durant sur des problèmes de MAJ ddns du dhcp vers le dns qui ne marchaient pas.<br />
Au final, le pb était qu&#8217;il y avait deux dns qui tournaient sur la machine.<br />
Ce pb a l&#8217;air plus courant qu&#8217;il n&#8217;y parait : ça m&#8217;est déjà arrivé deux fois (et deux fois je suis tombé dans le panneau). C&#8217;est peut-être un pb dans le restart du serveur dans init.d qui ne marche pas bien.<br />
Donc si vous avez un doute :</p>
<pre class="brush: bash;">
sudo /etc/init.d/bind9 stop
</pre>
<p>puis :</p>
<pre class="brush: bash;">
ps -ef | grep named
</pre>
<p>et si le ps montre un process named qui tourne encore, alors ne pas hésiter à le killer sauvagement :</p>
<pre class="brush: bash;">
sudo kill -9 XXXXXX
</pre>
<p>XXXXXX étant le numéro du process.<br />
puis :</p>
<pre class="brush: bash;">
sudo /etc/init.d/bind9 start
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/installation-dun-couple-de-serveurs-dhcp-et-dns-redondants/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Ma liste de BDs, version iphone</title>
		<link>http://www.chiroux.com/ma-liste-de-bds-version-iphone/</link>
		<comments>http://www.chiroux.com/ma-liste-de-bds-version-iphone/#comments</comments>
		<pubDate>Sun, 06 Sep 2009 01:39:29 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Bande Dessinee]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[bd]]></category>
		<category><![CDATA[bdgest]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[iwebkit]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/?p=470</guid>
		<description><![CDATA[<p>J&#8217;avais déjà, il y a quelque années, fait un petit script php pour générer ma liste de BDs en html ou rss (voir cet article).</p>
<p>Mais le parcours de la liste complète (ce que j&#8217;ai souvent besoin de faire quand je suis en train d&#8217;acheter des BDs pour m&#8217;assurer que je ne suis pas en train [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.chiroux.com/wp-content/uploads/2009/09/Illustration_BD-1.jpg" alt="Illustration_BD-1" title="Illustration_BD-1" width="400" height="188" class="alignleft size-full wp-image-495" />J&#8217;avais déjà, il y a quelque années, fait un petit script php pour générer ma liste de BDs en html ou rss (<a href="http://www.chiroux.com/bdlisting-un-newsfeed-de-ma-liste-de-bd/">voir cet article</a>).</p>
<p>Mais le parcours de la liste complète <span style="font-size: x-small;">(ce que j&#8217;ai souvent besoin de faire quand je suis en train d&#8217;acheter des BDs pour m&#8217;assurer que je ne suis pas en train d&#8217;acheter une BD que j&#8217;ai déjà&#8230;)</span> est long et peu pratique sur l&#8217;iphone.</p>
<p>J&#8217;ai donc décidé de faire évoluer ce script et l&#8217;adapter à l&#8217;iphone.<br />
<span id="more-470"></span><br />
<img src="http://www.chiroux.com/wp-content/uploads/2006/05/iphone-bds.png" alt="iphone-bds" title="iphone-bds" width="336" height="778" class="alignright size-full wp-image-489" /><br />
<br/><br />
<br/></p>
<p>Il existe plusieurs frameworks qui facilitent le développement d&#8217;applis web pour iphone, et après en avoir regardé quelques uns, les deux qui ont retenu mon attention sont :</p>
<ul>
<li><a href="http://www.iwebkit.net/">http://www.iwebkit.net/</a></li>
<li><a href="http://www.jqtouch.com/">http://www.jqtouch.com/</a></li>
</ul>
<p>Des deux, jqtouch est le plus joli, mais aussi le moins performant. J&#8217;ai donc choisi la sobriété et la rapidité avec iwebkit (qui a l&#8217;heure ou j&#8217;écris cet article est en version 4.6.2).</p>
<p>L&#8217;utilisation est hyper simple, il suffit d&#8217;inclure dans ses pages les css et javascript du kit et d&#8217;organiser ses pages comme indiqué dans la doc.<br />
J&#8217;ai beaucoup utilisé les listes pour cette petite appli, qui d&#8217;abord propose de sélectionner une lettre de l&#8217;alphabet, puis une série et enfin un album.</p>
<p>Comme pour BDlisting précédemment, l&#8217;appli se plugge sur <a href="http://bdgweb.free.fr/">BDGweb </a> et utilise la même base de donnée et donc le même export.</p>
<p>Pour l&#8217;installer, c&#8217;est tout simple, il suffit de déposer le répertoire iphone issu de l&#8217;archive directement dans votre répertoire où se situe bdgweb.</p>
<span id="Dmo"><h2>Démo</h2></span>
<ul>
<li><a href="http://bd.chiroux.com/iphone/" target="_blank">Voici ce que cela donne avec ma propre liste</a></li>
</ul>
<p>(si vous ne la visionnez pas directement sur un iphone, sachez qu&#8217;elle rend un peu mieux sous safari, mais qu&#8217;elle marche aussi sous firefox, seuls les liens retours sont un peu moches sous ff)</p>
<span id="Tlcharger_le_source"><h2>Télécharger le source</h2></span>
<p>Ce source est directement utilisable, il contient iwebkit en version 4.6.2<br />
Note: There is a file embedded within this post, please visit this post to download the file.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/ma-liste-de-bds-version-iphone/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Installation d&#8217;un serveur web sécurisé sous Ubuntu 9.04server</title>
		<link>http://www.chiroux.com/installation-dun-serveur-web-securise-sous-ubuntu-9-04server/</link>
		<comments>http://www.chiroux.com/installation-dun-serveur-web-securise-sous-ubuntu-9-04server/#comments</comments>
		<pubDate>Mon, 17 Aug 2009 22:58:11 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Administration Système]]></category>
		<category><![CDATA[9.04]]></category>
		<category><![CDATA[configuration]]></category>
		<category><![CDATA[installation]]></category>
		<category><![CDATA[jaunty]]></category>
		<category><![CDATA[lighttpd]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/?p=419</guid>
		<description><![CDATA[<p>Cet article est le premier d&#8217;une série sur l&#8217;admin système linux. On va donc commencer par les bases : installer un ubuntu server avec une sécurité correcte et on en profite pour installer un LLMP dessus (c&#8217;est LAMP mais avec Lighttpd à la place de apache  )</p>
<p>Pour ce tuto, j&#8217;ai choisi d&#8217;utiliser un serveur [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.chiroux.com/wp-content/uploads/2009/08/ubuntu-logo.png" alt="ubuntu-logo" title="ubuntu-logo" width="300" height="277" class="alignright size-full wp-image-444" />Cet article est le premier d&#8217;une série sur l&#8217;admin système linux. On va donc commencer par les bases : installer un ubuntu server avec une sécurité correcte et on en profite pour installer un LLMP dessus (c&#8217;est LAMP mais avec Lighttpd à la place de apache <img src='http://www.chiroux.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> )</p>
<p>Pour ce tuto, j&#8217;ai choisi d&#8217;utiliser <a href="https://www.gandi.net/hebergement/" class="broken_link"  target="_blank">un serveur virtuel de chez gandi</a> : je profite d&#8217;une offre d&#8217;été qui propose un serveur gratuit pendant un mois.<br />
J&#8217;avais beta testé cette offre il y a un an et demi, mais les trop gros problèmes d&#8217;accès disque (lenteurs notamment) m&#8217;avaient incité à partir prendre un RPS chez OVH.<br />
Maintenant la situation change : l&#8217;offre de gandi est plus mûre (et l&#8217;archi disque a notamment changée : c&#8217;est maintenant du SAS directement sur les serveurs) et les <a href="http://forum.ovh.com/showthread.php?t=49747" target="_blank">RPS d&#8217;ovh vont disparaitre</a>.<br />
Je vais donc profiter de cette installation pour faire quelques benchs sur l&#8217;offre gandi et voir si je (re)bascule dessus ou pas ensuite.<br />
En attendant, voici de quoi installer le serveur<br />
<span id="more-419"></span></p>
<span id="Table_des_matires"><h2>Table des matières</h2></span>
<div class='toc wptoc'>
<h2>Contents</h2>
<ol class='toc-odd level-1'>
	<li>
		<a href="#Table_des_matires">Table des matières</a>
	</li>
	<li>
		<a href="#Du_ct_de__gandi">Du côté de  gandi</a>
	</li>
	<li>
		<a href="#Organiser_les_users">Organiser les users</a>
		<ol class='toc-even level-2'>
			<li>
				<a href="#On_va_donc_dabord_autoriser_le_sudo_pour_testuser_:">On va donc d'abord autoriser le sudo pour testuser :</a>
			</li>
			<li>
				<a href="#Ensuite_on_va_ajouter_testuser_dans_le_groupe_admin_:">Ensuite on va ajouter testuser dans le groupe admin :</a>
			</li>
			<li>
				<a href="#Enfin_on_change_le_mot_de_passe_root">Enfin on change le mot de passe root</a>
			</li>
		</ol>
	<li>
		<a href="#Mise__jour_du_systme">Mise à jour du système</a>
	</li>
	<li>
		<a href="#Installer_et_configurer_de_quoi_bien_travailler">Installer et configurer de quoi bien travailler</a>
		<ol class='toc-even level-2'>
			<li>
				<a href="#Lheure_et_ntp">L'heure et ntp</a>
			</li>
		</ol>
	<li>
		<a href="#Scurit">Sécurité</a>
		<ol class='toc-even level-2'>
			<li>
				<a href="#ssh_:_autoriser_uniquement_certains_users_en_ssh">ssh : autoriser uniquement certains users en ssh</a>
			</li>
			<li>
				<a href="#Fail2ban">Fail2ban</a>
			</li>
			<li>
				<a href="#IPtables">IPtables</a>
			</li>
			<li>
				<a href="#Scuriser_le_kernel">Sécuriser le kernel</a>
			</li>
		</ol>
	<li>
		<a href="#Installation_Lighttpd_et_php">Installation Lighttpd et php</a>
	</li>
	<li>
		<a href="#Installation_Mysql">Installation Mysql</a>
	</li>
	<li>
		<a href="#Configuration_de_lighttpd_pour_un_site_de_test">Configuration de lighttpd pour un site de test</a>
		<ol class='toc-even level-2'>
			<li>
				<a href="#mettre_un_fichier_de_test">mettre un fichier de test</a>
			</li>
		</ol>
	<li>
		<a href="#Conclusion">Conclusion</a>
	</li>
</ol>
</ol>
</div>
<div class='wptoc-end'>&nbsp;</div>
<span id="Du_ct_de__gandi"><h2>Du côté de  gandi</h2></span>
<ul>
<li>Administration / Hebergement : cliquer sur créer un serveur</li>
<li>Valider le nombre de parts</li>
<li>Choisir Installation expert et Ubuntu 9.04</li>
<li>Choisir un hostname, un nom d&#8217;utilisateur et un mot de passe [si possible, choisir un nom de user non standard, pour améliorer la sécurité]</li>
<li>Valider, et attendre le mail de confirmation avec l&#8217;@IP du serveur</li>
<li>Ensuite, on peut se connecter en SSH sur le serveur avec le login et pass entré juste avant.</li>
</ul>
<p>Ajouter un disque de données :</p>
<ul>
<li>sur l&#8217;interfance gandi, onglet Gestion des disque, cliquer sur Créer un disque</li>
<li>choisir un nom pour le disque, la taille souhaitée et le filesystem</li>
<li>enfin, une fois le disque créé, cliquer sur le petit logo &#8216;link&#8217; attacher le disque à un serveur</li>
<li>choisir le serveur et valider</li>
<li>le volume est dynamiquement ajouté au serveur dans /srv/nomduvolumechoisi</li>
</ul>
<span id="Organiser_les_users"><h2>Organiser les users</h2></span>
<p>Le login créé (ici: testuser) n&#8217;est pas par défaut dans la liste des sudoers, donc pas de sudo possible.</p>
<p>Le mot de passe root par défaut est le même que celui du user testuser</p>
<span id="On_va_donc_d8217abord_autoriser_le_sudo_pour_testuser_:"><h3>On va donc d&#8217;abord autoriser le sudo pour testuser :</h3></span>
<p>en tant que root :</p>
<pre class="brush: bash;">
visudo</pre>
<p>et ajouter à la fin du fichier :</p>
<pre class="brush: bash;">
# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
</pre>
<span id="Ensuite_on_va_ajouter_testuser_dans_le_groupe_admin_:"><h3>Ensuite on va ajouter testuser dans le groupe admin :</h3></span>
<p>toujours en tant que root :</p>
<pre class="brush: bash;">
usermod -G admin testuser
</pre>
<span id="Enfin_on_change_le_mot_de_passe_root"><h3>Enfin on change le mot de passe root</h3></span>
<p>en tant que root :</p>
<pre class="brush: bash;">
passwd
</pre>
<p>et entrer un bon mot de passe bien sécurisé</p>
<p>A partir de maintenant, sauf mention spécifique, le reste des commandes sera réalisée avec le user testuser</p>
<span id="Mise__jour_du_systme"><h2>Mise à jour du système</h2></span>
<p>Afin de démarrer sur un système tout propre</p>
<pre class="brush: bash;">
sudo apt-get update
sudo apt-get upgrade
</pre>
<span id="Installer_et_configurer_de_quoi_bien_travailler"><h2>Installer et configurer de quoi bien travailler</h2></span>
<pre class="brush: bash;">
sudo apt-get install vim-python
</pre>
<p>puis</p>
<pre class="brush: bash;">
vim ~/.vimrc
</pre>
<p>et coller le texte suivant:</p>
<pre class="brush: bash;">
syntax on
set number
set background=dark
set tabstop=2
set shiftwidth=2
set softtabstop=2
set expandtab
set autoindent
autocmd BufRead *.py set smartindent cinwords=if,elif,else,for,while,try,except,finally,def,class
autocmd BufWritePre *.py normal m`:%s/\s\+$//e ``
map &lt;F12&gt; :set number!&lt;CR&gt;
map &lt;F10&gt; :set paste!&lt;CR&gt;
</pre>
<p>enfin :</p>
<pre class="brush: bash;">
vim ~/.bashrc
</pre>
<p>et vers la fin du fichier, décommenter les 3 lignes suivantes :</p>
<pre class="brush: bash;">
alias ll='ls -l'
alias la='ls -A'
alias l='ls -CF'
</pre>
<span id="L8217heure_et_ntp"><h3>L&#8217;heure et ntp</h3></span>
<p>Dans mon système livré par gandi, l&#8217;heure est par défaut en heure UTC, si on veut la mettre à l&#8217;heure locale (Paris, France pour moi):</p>
<pre class="brush: bash;">
sudo dpkg-reconfigure tzdata
</pre>
<p>et choisir : Europe, puis Paris</p>
<p>Pour NTP :<br />
[édit: Comme le remarque justement Daniel dans les commentaires, paramétrer ntp pour un serveur virtuel n'est vraisemblablement pas nécessaire car il va hériter de la date de son hôte. Je laisse ici l'info au cas où votre serveur n'est pas un hébergement gandi]</p>
<pre class="brush: bash;">
sudo vim /etc/cron.daily/ntpdate
</pre>
<p>et coller les commandes suivantes :</p>
<pre class="brush: bash;">
#!/bin/sh
#On lance une synchro ntp
ntpdate fr.pool.ntp.org
</pre>
<p>enfin</p>
<pre class="brush: bash;">
sudo chmod a+x /etc/cron.daily/ntpdate
</pre>
<p>Ainsi notre serveur resynchronisera son horloge tous les jours automatiquement.</p>
<span id="Scurit"><h2>Sécurité</h2></span>
<span id="ssh_:_autoriser_uniquement_certains_users_en_ssh"><h3>ssh : autoriser uniquement certains users en ssh</h3></span>
<p>ici, on va limiter le ssh uniquement pour notre user : testuser</p>
<pre class="brush: bash;">
sudo vim /etc/ssh/sshd_config
</pre>
<p>Bin vérifier que dans la zone &#8216;Authentication&#8217; on a bien :</p>
<pre class="brush: bash;">
PermitRootLogin  without-password
</pre>
<p>sinon modifier le paramètre.</p>
<p>ensuite, en bas du fichier, ajouter :</p>
<pre class="brush: bash;">
# Allow only a certain list of users
AllowUsers testuser
</pre>
<p><span style="color: #ff0000;"><strong>Attention, il faut être vigilant en insérant cette ligne : une erreur dans le nom du user interdirait toute connexion ultérieure en ssh et bloquerait l&#8217;accès au serveur. Il est conseillé de garder sa session actuelle ouverte et de tester une nouvelle connexion supplémentaire pour bien vérifier qu&#8217;on puisse encore se connecter.</strong></span></p>
<p>On recharge la config ssh</p>
<pre class="brush: bash;">
sudo /etc/init.d/ssh reload
</pre>
<span id="Fail2ban"><h3>Fail2ban</h3></span>
<p>Fail2ban analyse les logs (ssh notament) et banni les IP qui attaque en force brute sur ssh via des configurations automatiques dans iptables.</p>
<pre class="brush: bash;">
sudo apt-get install fail2ban
</pre>
<p>modification des paramètres de fail2ban:</p>
<pre class="brush: bash;">
sudo vim /etc/fail2ban/jail.conf
</pre>
<p>pour ma part, j&#8217;ai juste changé deux paramètres : le bantime et le nombre d&#8217;essais :</p>
<pre class="brush: bash;">
bantime  = 1800
maxretry = 3
</pre>
<pre class="brush: bash;">
sudo /etc/init.d/fail2ban restart
</pre>
<span id="IPtables"><h3>IPtables</h3></span>
<p>Iptable a été installé avec fail2ban, on va maintenant le configurer pour nos applications</p>
<p>Pour cela, on va créer un fichier de commande qui se lancera au boot du serveur et que l&#8217;on peut relancer à volonté pour reconfigurer le firewall.</p>
<pre class="brush: bash;">
sudo vim /etc/init.d/server_iptables
</pre>
<p>et coller les commandes suivantes :</p>
<pre class="brush: bash;">
#!/bin/bash
# reset iptables
iptables -F

# Autorise les connections sortantes et sur l'interface &quot;loopback&quot;
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -d 127.0.0.0/8 -i ! lo -j DROP

# Autorise les connections deja etablies
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Autorise HTTP, SSH, ICMP-ping
iptables -A INPUT -p tcp -i eth0 --dport ssh -j ACCEPT
iptables -A INPUT -p tcp -i eth0 --dport 80 -j ACCEPT
iptables -A INPUT -p icmp -i eth0 -j ACCEPT

#  Refuse a priori ce qui vient de l'exterieur
iptables -P INPUT DROP
iptables -P FORWARD DROP
</pre>
<p>Ensuite on le rend executable et on l&#8217;installe au boot :</p>
<pre class="brush: bash;">
sudo chmod +x /etc/init.d/server_iptables
sudo update-rc.d server_iptables defaults
</pre>
<span id="Scuriser_le_kernel"><h3>Sécuriser le kernel</h3></span>
<p>ça doit être lié à Xen, mais le kernel de gandi est assez ancien (à la date de cet article c&#8217;est un 2.6.18, alors que le kernel en cours est un 2.6.28</p>
<p>On va modifier quelques paramètres du kernel pour le rendre plus solide aux attaques<br />
Ici, il faut se mettre directement en root pour que ces commandes passent :</p>
<pre class="brush: bash;">
sudo su
</pre>
<span id="Se_protger_contre_les_Smurf_Attack:"><h4>Se protéger contre les Smurf Attack:</h4></span>
<p>celui ci est déjà bon dans l&#8217;install que j&#8217;ai testé:</p>
<pre class="brush: bash;">
echo &quot;1&quot; &gt; /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
</pre>
<span id="Eviter_le_source_routing:_"><h4>Eviter le source routing: </h4></span>
<p>celui ci est déjà bon dans l&#8217;install que j&#8217;ai testé:</p>
<pre class="brush: bash;">
echo &quot;0&quot; &gt; /proc/sys/net/ipv4/conf/all/accept_source_route
</pre>
<span id="Se_protger_des_attaques_de_type_Syn_Flood:_"><h4>Se protéger des attaques de type Syn Flood: </h4></span>
<p>celui ci est déjà bon dans l&#8217;install que j&#8217;ai testé:</p>
<pre class="brush: bash;">
echo &quot;1&quot; &gt; /proc/sys/net/ipv4/tcp_syncookies
echo &quot;1024&quot; &gt; /proc/sys/net/ipv4/tcp_max_syn_backlog
echo &quot;1&quot; &gt; /proc/sys/net/ipv4/conf/all/rp_filter
</pre>
<span id="Dsactiver_lautorisation_des_redirections_ICMP:"><h4>Désactiver l’autorisation des redirections ICMP:</h4></span>
<pre class="brush: bash;">
echo &quot;0&quot; &gt; /proc/sys/net/ipv4/conf/all/accept_redirects
echo &quot;0&quot; &gt; /proc/sys/net/ipv4/conf/all/secure_redirects
</pre>
<span id="Eviter_le_log_des_paquets_icmp_erron:_"><h4>Eviter le log des paquets icmp erroné: </h4></span>
<p>celui ci est déjà bon dans l&#8217;install que j&#8217;ai testé:</p>
<pre class="brush: bash;">
echo &quot;1&quot; &gt; /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses
</pre>
<span id="Active_le_logging_des_packets_aux_adresses_sources_falficies_ou_non_routables:"><h4>Active le logging des packets aux adresses sources falficiées ou non routables:</h4></span>
<pre class="brush: bash;">
echo &quot;1&quot; &gt; /proc/sys/net/ipv4/conf/all/log_martians
</pre>
<p>on quitte le user root</p>
<pre class="brush: bash;">
exit
</pre>
<span id="Installation_Lighttpd_et_php"><h2>Installation Lighttpd et php</h2></span>
<pre class="brush: bash;">
sudo apt-get install lighttpd
sudo apt-get install php5-cgi
sudo apt-get install php5-mysql
sudo lighty-enable-mod cgi
sudo /etc/init.d/lighttpd force-reload
</pre>
<p>edit le fichier de conf de lighttpd et ajouter le mod fastcgi :</p>
<pre class="brush: bash;">
sudo vim /etc/lighttpd/lighttpd.conf
</pre>
<p>les modules activés sont les suivants (j&#8217;ai mis mod_rewrite et mod_redirect en plus, car cela sert toujours finalement)</p>
<pre class="brush: bash;">
 server.modules              = (
             &quot;mod_access&quot;,
             &quot;mod_alias&quot;,
             &quot;mod_accesslog&quot;,
             &quot;mod_compress&quot;,
             &quot;mod_fastcgi&quot;,
             &quot;mod_rewrite&quot;,
             &quot;mod_redirect&quot;,
 #           &quot;mod_evhost&quot;,
 #           &quot;mod_usertrack&quot;,
 #           &quot;mod_rrdtool&quot;,
 #           &quot;mod_webdav&quot;,
 #           &quot;mod_expire&quot;,
 #           &quot;mod_flv_streaming&quot;,
 #           &quot;mod_evasive&quot;
 )
</pre>
<p>puis aller chercher dans le fichier la variable : server.dir-listing<br />
et la passer à disable : </p>
<pre class="brush: bash;">
server.dir-listing          = &quot;disable&quot;
</pre>
<span id="Installation_Mysql"><h2>Installation Mysql</h2></span>
<pre class="brush: bash;">
apt-get install mysql-server-5.0
</pre>
<p>Quand il le demande, entrer un mot de passe root pour mysql (choisir un vrai mot de passe bien sécurisé)</p>
<p>On va &#8216;forcer&#8217; l&#8217;utf8 partout :</p>
<pre class="brush: bash;">
sudo vim /etc/mysql/conf.d/caractersencoding.cnf
</pre>
<p>et coller la configuration suivante :</p>
<pre class="brush: bash;">
[mysqld]
  #Set the default character set.
  default-character-set=utf8
  #Set the default collation.
  default-collation=utf8_general_ci
  #
  character-set-server=utf8
  skip-character-set-client-handshake
  init-connect='SET NAMES utf8'
</pre>
<p>on redémarre pour vérifier que tout est bon</p>
<pre class="brush: bash;">
sudo /etc/init.d/mysql restart
</pre>
<p>Mysql est maintenant installé dans ses répertoires par défaut, et notamment les bases seront crées dans /var/lib/mysql<br />
[ceci est une spécificité liée à l'installation gandi, vous pouvez passer ce point si votre serveur est 'normal'] /var/lib est dans le disque &#8217;système&#8217; de gandi qui fait uniquement 3Go. En fonction de ce qu&#8217;on prévoi de faire avec son serveur mysql, il est peut-etre judicieux de le déplacer sur le disque de données :</p>
<pre class="brush: bash;">
sudo /etc/init.d/mysql stop
sudo mv /var/lib/mysql /srv/nomdurepertoiregandi/mysql
sudo ln -s /srv/nomdurepertoiregandi/mysql /var/lib/mysql
sudo /etc/init.d/mysql start
</pre>
<span id="Configuration_de_lighttpd_pour_un_site_de_test"><h2>Configuration de lighttpd pour un site de test</h2></span>
<p>Afin de voir si tout va bien, on va créer un site de test </p>
<pre class="brush: bash;">
sudo mkdir /srv/nomdurepertoiregandi/www
sudo chown www-data:www-data /srv/nomdurepertoiregandi/www
sudo -u www-data mkdir /srv/nomdurepertoiregandi/www/sitetest1
</pre>
<p>ensuite on va configurer lighty :</p>
<pre class="brush: bash;">
sudo vim /etc/lighttpd/lighttpd.conf
</pre>
<p>ajouter sous la conf des modules :</p>
<pre class="brush: bash;">
 # Config pour sitetest
$HTTP[&quot;host&quot;] == &quot;111.111.111.111&quot; {
     server.document-root       = &quot;/srv/nomdurepertoiregandi/www/sitetest1/&quot;

     # FAST CGI POUR PHP
     fastcgi.server = ( &quot;.php&quot; =&gt; ((
                         &quot;bin-path&quot; =&gt; &quot;/usr/bin/php-cgi&quot;,
                         &quot;socket&quot; =&gt; &quot;/tmp/php.socket&quot;,
                         &quot;max-procs&quot; =&gt; 1,
                         &quot;bin-environment&quot; =&gt; (
                             &quot;PHP_FCGI_CHILDREN&quot; =&gt; &quot;4&quot;,
                             &quot;PHP_FCGI_MAX_REQUESTS&quot; =&gt; &quot;10000&quot;
                         ),
                         &quot;bin-copy-environment&quot; =&gt; (
                             &quot;PATH&quot;, &quot;SHELL&quot;, &quot;USER&quot;
                         ),
                         &quot;broken-scriptfilename&quot; =&gt; &quot;enable&quot;
     )))
}
</pre>
<p>en remplaçant l&#8217;ip 111.111.111.111 par l&#8217;ip ou le nom de domaine du site que vous voulez ajouter<br />
ensuite on redémarre lighty</p>
<pre class="brush: bash;">
sudo /etc/init.d/lighttpd restart
</pre>
<span id="mettre_un_fichier_de_test"><h3>mettre un fichier de test</h3></span>
<pre class="brush: bash;">
sudo -u www-data vim /srv/nomdurepertoiregandi/www/sitetest1/testinfo.php
</pre>
<p>et coller le code suivant :</p>
<pre class="brush: php;">
&lt;?php phpinfo(); ?&gt;
</pre>
<p>et voilà, le site est normalement accessible sur l&#8217;IP (ou le nom de domaine) que vous avez précisé dans la conf et l&#8217;url (en prenant mon exemple ci-dessus) : http://111.111.111.111/testinfo.php<br />
affiche toutes les infos de php.</p>
<span id="Conclusion"><h2>Conclusion</h2></span>
<p>On a maintenant un serveur ubuntu jaunty prêt à fonctionner, léger et sécurisé. Il prends peu de ressources, très peu de ram et pourra servir de base aux reste des tutos à venir.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/installation-dun-serveur-web-securise-sous-ubuntu-9-04server/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>Une review rapide de la sonde colorimétrique Spider 3 Elite</title>
		<link>http://www.chiroux.com/une-review-rapide-de-la-sonde-colorimtrique-spider-3-elite/</link>
		<comments>http://www.chiroux.com/une-review-rapide-de-la-sonde-colorimtrique-spider-3-elite/#comments</comments>
		<pubDate>Mon, 12 Nov 2007 21:27:20 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Gadgets]]></category>
		<category><![CDATA[Informatique]]></category>
		<category><![CDATA[calibration]]></category>
		<category><![CDATA[couleur]]></category>
		<category><![CDATA[review]]></category>
		<category><![CDATA[test]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/review-sonde-datacolor-spyder-3.html</guid>
		<description><![CDATA[<p>J&#8217;ai reçu ce jour la nouvelle sonde Spyder 3 Elite. Voici donc une petite revue de la bête et du logiciel livré avec. </p>
<p>La première chose qui m&#8217;a surpris est la petite taille de la boîte. Je n&#8217;avais pas de sonde avant, mais j&#8217;avais vu les boites des spyder 2. Celle ci est significativement plus [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.chiroux.com/wp-content/uploads/2007/11/datacolor_spyder3.jpg" alt="datacolor_spyder3" title="datacolor_spyder3" width="212" height="124" class="alignright size-full wp-image-331" />J&#8217;ai reçu ce jour la nouvelle sonde Spyder 3 Elite. Voici donc une petite revue de la bête et du logiciel livré avec. </p>
<p>La première chose qui m&#8217;a surpris est la petite taille de la boîte. Je n&#8217;avais pas de sonde avant, mais j&#8217;avais vu les boites des spyder 2. Celle ci est significativement plus petite et plus légère. A tel point que je croyait que le colis envoyé par miss numérique était vide&#8230;<span id="more-22"></span></p>
<span id="Le_dballage"><h1>Le déballage</h1></span>
<p>Voici quelques photos du déballage avec ma DS comme point de repère (désolé de la qualité médiocre des photos.. je suis entre 2 appareils en ce moment et j&#8217;ai donc juste un petit compact en backup).</p>
<p>La boîte :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/Spyder3_1_boite.jpg" alt="" width="500" height="375" /></p>
<p>Le contenu de la boite :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/Spyder3_2_eclate.jpg" alt="" width="500" height="375" /></p>
<ul>
<li>la sonde (à noter le gros truc ovale et lourd sur le câble USB, nous le reverrons plus tard)</li>
<li>un socle</li>
<li>un CD</li>
<li>une doc&#8230; ultra minimaliste</li>
<li>deux lingettes (une humide et une sèche) pour nettoyer son écran</li>
</ul>
<p>&#8230; et c&#8217;est tout&#8230; franchement un peu déçu pour 250€. Ok je sais ce qui est important c&#8217;est la sonde et -surtout- le soft, mais bon une bonne petite doc avec n&#8217;aurait pas fait de mal.</p>
<p>La sonde à côté de ma DS :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/Spyder3_3_capteur.jpg" alt="" width="500" height="375" /></p>
<p>Vue de profil :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/Spyder3_4_capteur_profil.jpg" alt="" width="500" height="667" /></p>
<p>Et sur son socle :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/Spyder3_5_socle.jpg" alt="" width="500" height="666" /></p>
<span id="L8217install_du_soft"><h1>L&#8217;install du soft</h1></span>
<p>Comme toujours sur PC, et même sous vista, il faut installer le soft avant de brancher le périphérique en USB :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/1.png" alt="" width="532" height="296" /><br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/2.png" alt="" width="380" height="289" /><br />
Bon il est disponible en français, c&#8217;est déjà ça&#8230;</p>
<p><img src="/wp-content/uploads/Import/Blog/Spyder3/3.png" alt="" width="572" height="373" /><br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/4.png" alt="" width="572" height="373" /><br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/5.png" alt="" width="572" height="373" /><br />
Le coup habituel sous windows&#8230;</p>
<p><img src="/wp-content/uploads/Import/Blog/Spyder3/6.png" alt="" width="572" height="373" /><br />
Le CLUF&#8230; mais oui je l&#8217;ai lu&#8230;. heuu en fait non. Vous les lisez vous les Clufs ?</p>
<p><img src="/wp-content/uploads/Import/Blog/Spyder3/7.png" alt="" width="572" height="373" /><br />
Bon, etc..etc.. (je coupe un peu l&#8217;install c&#8217;est assez classique)</p>
<p><img src="/wp-content/uploads/Import/Blog/Spyder3/9.png" alt="" width="572" height="373" /><br />
Celle ci j&#8217;aime bien&#8230; Des rumeurs circulent dans votre PC qui disent que peut-être vous auriez Adobe Gamma ou éventuellement autre chose qui serait peut-être, mais sans être bien sûr (normal c&#8217;est une rumeur) présent et installé dans vos articles startup&#8230;<br />
La deuxième phrase pour le coup, je n&#8217;ai rien compris&#8230;.</p>
<p>Bon bref pour résumé il me conseille de virer Adobe Gamma, ce qui est une bonne idée que je m&#8217;empresse de faire.</p>
<p><img src="/wp-content/uploads/Import/Blog/Spyder3/10.png" alt="" width="572" height="373" /><br />
J&#8217;ai cherché pendant 1h la disquette, mais je n&#8217;ai pas trouvé. Le pire c&#8217;est que je ne sait même comment j&#8217;ai pu la mettre, vu que je n&#8217;ai pas de lecteur de disquette. Mince, alors&#8230; la galette ronde qui ressemble à un vynil argenté, ce serait ça la disquette ?!? Arf&#8217;</p>
<p>Allez, un petit coup de reboot.. bon cette fois ci je ne râle pas trop puisqu&#8217;il fallait virer Adobe Gamma du démarrage.</p>
<span id="La_calibration"><h1>La calibration</h1></span>
<p>Après le reboot j&#8217;ai branché la sonde. Comme c&#8217;est la mode, elle arbore une *jolie* LED bleue dont la brillance varie lentement (un peut à la manière de la WII quand vous avez un message).<br />
Ensuite on lance l&#8217;appli Spyder3 Elite :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/11.png" alt="" width="609" height="449" /><br />
Il a détecté mon écran principal, c&#8217;est bien.<br />
Je profite de ce moment pour expliquer rapidement ma config :</p>
<ul>
<li>PC Core2Duo X6800 sous Vista Ultimate 32bits, 4 GO de RAM (3,2 utilisable car 32bits&#8230; il faudra que je passe à 64 bits un jour ou un autre, mais j&#8217;ai encore trop de pb de drivers)</li>
<li>Carte Graphique Nvidia 8800 GTX</li>
<li>2 écrans
<ul>
<li>le principal : DELL 24&nbsp;&raquo; 2407WFP</li>
<li>le secondaire : mon vieux 17&nbsp;&raquo; Hyundai Q17</li>
</ul>
</li>
<li>Le reste n&#8217;a pas beaucoup d&#8217;importance dans cette revue</li>
</ul>
<p>Je vais donc &#8211; et c&#8217;était un des points principaux justifiant l&#8217;achat de cette sonde &#8211; qualibrer mes deux écrans.</p>
<p>Nous commençons donc avec l&#8217;écran Dell.<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/12.png" alt="" width="609" height="449" /></p>
<p>Il faut ici entrer certaines caractéristiques de réglage de son écran :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/13.png" alt="" width="609" height="449" /><br />
Le mien n&#8217;a que le réglage de luminosité.</p>
<p><img src="/wp-content/uploads/Import/Blog/Spyder3/14.png" alt="" width="609" height="235" /><br />
Ah ! C&#8217;est une bonne question et cela me met le doute. Heurement la page d&#8217;aide est bien faite et me permet de derterminer qu&#8217;à priori mon réglage de luminosité est bien un vrai réglage de luminosité.</p>
<p>Ne pas oublier de tout remettre en valeur usine :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/15.png" alt="" width="609" height="449" /></p>
<p>Si j&#8217;ai bien compris, ici on peut jouer sur la luminosité pour optimiser le réglage. Cela me parraissait bien sur mon écran, j&#8217;ai laissé tel quel.<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/16.png" alt="" width="609" height="449" /></p>
<p>Même question sur les réglages de couleur. J&#8217;ai coché juste préréglages Kelvin. Le réglage RVB n&#8217;est pas possible par défaut, il faut aller dans les options avancées du soft pour le débloquer. Comme je ne maitrise pas encore trop ce genre de réglage, je laisse par défaut :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/17.png" alt="" width="609" height="449" /></p>
<p>On règle l&#8217;écran à 6500K (réglage en mode &#8216;PC NORMAL&#8217; du menu color settings sur mon DELL) :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/18.png" alt="" width="609" height="449" /></p>
<p>On va ensuite fixer la sonde. C&#8217;est ici qu&#8217;intervient le gros truc ovale sur le câble USB, c&#8217;est en fait un contrepoid qui permet de &#8216;poser&#8217; la sonde devant l&#8217;écran au lieu d&#8217;utiliser la ventouse.<br />
J&#8217;ai préférer tester avec la ventouse.<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/19.png" alt="" width="609" height="449" /></p>
<p>Suprise ! Le soft ne me demande pas de poser la sonde : d&#8217;abord nous allons nous occuper des paramètres du second écran :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/20.png" alt="" width="609" height="449" /></p>
<p>Je vous passe les écrans du second écran, ce sont les mêmes que le premier.<br />
A noter un petit détail sympa : le soft s&#8217;est déplacé tout seul sur le deuxième écran pour poser la question ci-dessus et tout le reste du processus est sur le second écran. Pratique !</p>
<p>Une fois les réglages du second écran terminés, le soft propose un choix pour étalonner l&#8217;un ou l&#8217;autre. Je suppose donc que les étalonnages futurs commenceront directement ici.<br />
Pour noter un peu le temps, il a fallu environ 15 minutes pour répondres aux questions jusqu&#8217;ici pour les 2 écrans, copies d&#8217;écrans comprises. C&#8217;est donc assez rapide.</p>
<p><img src="/wp-content/uploads/Import/Blog/Spyder3/21.png" alt="" width="609" height="449" /></p>
<p>Le soft affiche un résumé des paramètres de l&#8217;écran. Un peu rébarbatif, d&#8217;autant plus qu&#8217;on vient de les défnir, mais cela peut être pratique de revoir ces infos dans quelques mois quand on aura tout oublié.<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/22.png" alt="" width="609" height="449" /></p>
<p>Voici le moment où on place la sonde sur l&#8217;écran. Je ne sais pas si c&#8217;est fait exprès mais l&#8217;image de la sonde est exactement à la taille réélle.<br />
Je mouille donc un peu la ventouse et applique la sonde sur l&#8217;écran. La sonde est très légère, je n&#8217;ai pas besoin d&#8217;appuyer fortement pour la faire tenir et je remarque au passage un petit ergot sur la ventouse qui me permettra de la décoller facilement.<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/23.png" alt="" width="609" height="422" /></p>
<p>A partir de ce moment le calibrage comment et le processus est complétement automatique.<br />
Je n&#8217;ai pas vraiment chronométré mais il dure entre 5 et 10 minutes.</p>
<p>La sonde sur l&#8217;écran:<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/spyder3_6_calib_dell.jpg" alt="" width="500" height="344" /></p>
<p>Une fois le calibrage termine, il propose d&#8217;enregistrer le profil ICC :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/24.png" alt="" width="609" height="449" /></p>
<p>Ensuite, cela devient encore plus sympa : le soft nous affiche une mire d&#8217;image sur laquelle on peut zoomer (par bloc de 4 images ou sur une image en particulier). En appuyant sur le bouton &#8216;permuter&#8217; on peut voir les images avant et après la calibration.<br />
Franchement je vois la différence. Mon écran avant calibration tournait visiblement un peu sur le vert.<br />
Ce qui me frappe le plus en différence ce sont les images N&amp;B dont l&#8217;affichage diffère fortement avant et après calibrage et j&#8217;ai une très nette préférence sur l&#8217;après calibrage (heureusement d&#8217;ailleurs, je ne sais pas ce que j&#8217;aurais fait dans le cas contraire&#8230;)<br />
L&#8217;image dans le coin en bas à gauche, avec le ciel démontre aussi assez bien les erreurs que l&#8217;on pourrait faire sur un écran mal qualibré : en mode &#8216;avant qualibrage&#8217;, sur mon écran, le ciel est terne, presque gris vert le haut et inciterait donc à pousser un peu les rouges au développement pour obtenir un ciel plus sympa. En mode après qualibrage, la dominante rouge/rose monte presque jusqu&#8217;en haut du ciel.<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/25.png" alt="" width="609" height="449" /></p>
<p>Ensuite c&#8217;est terminé.<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/26.png" alt="" width="609" height="449" /></p>
<p>Le profil ICC est automatiquement chargé dans Vista et s&#8217;applique à priori à toutes les applications (je pensais que seuls les softs capables de gérer les profils ICC en bénéficiaient, mais cela n&#8217;a pas l&#8217;air d&#8217;être le cas sous vista).<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/27.png" alt="" width="609" height="441" /></p>
<p>J&#8217;ai ensuite appliqué la même procédure pour calibrer mon second écran :<br />
<img src="/wp-content/uploads/Import/Blog/Spyder3/spyder3_7_calib_hyundai.jpg" alt="" width="500" height="422" /></p>
<p>Bonne nouvelle, après calibration des deux écrans, ils affichent des couleurs extrèmement proches. Mon vieux 17&nbsp;&raquo; est par contre un peu plus sombre, mais je n&#8217;arrive pas à le monter au niveau du Dell qui claque vraiment fort.</p>
<span id="Conclusion_et_autres_fonctionnalits."><h1>Conclusion et autres fonctionnalités.</h1></span>
<p>Un des arguments de vente de la sonde est le capteur de lumière ambiante qui permettrait de réajuster les profils en cas de détection de changement de lumière. Ici chez moi, le capteur indique &#8216;luminosité ambiante très faible&#8217;, ce qui ne m&#8217;étonne pas, il fait nuit et je n&#8217;ai pas de lampe proche.<br />
Pour mesurer continuellement ces changements de lumière il faut laisser brancher la sonde sur le port USB et je ne vais pas le faire chez moi pour deux raisons principales :</p>
<ul>
<li>c&#8217;est déjà le bordel sur mon bureau et il n&#8217;y a plus de place pour la sonde</li>
<li>je ne connais pas le degré de sensibilité à la poussière de ce genre de matos, mais je suppose que ce n&#8217;est jamais bon. Et un environnement PC, même si on nettoie souvent est poussiereux, l&#8217;electricité statique attire la poussière et je n&#8217;ai pas envie de salir le capteur.</li>
</ul>
<p>Je vais donc le laisser dans la boite et ne pas utiliser le capteur ambiant. Je ferais attention moi même aux changement et je referais une calibration à ce moment là.</p>
<p><span style="font-size: small;">En conclusion et pour résumer rapidement, je suis content de mon achat. L&#8217;appareil répond parfaitement à mes attentes, il a bien fonctionné et rapidement.<br />
Est-ce que cela vaut les 250€ ? Je ne sais pas et il faudra attendre des essais comparatifs et plus scientifiques pour le vérifier (notamment en comparaison avec d&#8217;autres produits meilleurs marché comme les spyder 2 express). Ce que je sais c&#8217;est que le spyder 2 express ne pouvait pas répondre à ma problématique de double écran et m&#8217;obligeait de toute façon à monter en gamme.<br />
Datacolor gagnerait par contre à soigner un peu plus la traduction de son soft.<br />
</span><br />
<a href="http://www.colorvision.ch/fr/products/prod_spyder3elite.php" target="_blank">site officiel datacolor</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/une-review-rapide-de-la-sonde-colorimtrique-spider-3-elite/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Django : une arborescence de catalogue</title>
		<link>http://www.chiroux.com/django-une-arborescence-de-catalogue/</link>
		<comments>http://www.chiroux.com/django-une-arborescence-de-catalogue/#comments</comments>
		<pubDate>Wed, 08 Aug 2007 22:26:49 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Obsolete]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[mootools]]></category>
		<category><![CDATA[mootree]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/article-1186612009.html</guid>
		<description><![CDATA[<p>C&#8217;est la semaine de la publication&#8230; Après le gadget vista pour la NB4, voici un article qui explique comment gérer une structure arborescente avec django (un excellent framework python).
J&#8217;espère que cet article sur django est le premier d&#8217;une longue série. J&#8217;ai comme beaucoup l&#8217;intention de basculer mon site sur cette techno, mais cela va prendre [...]]]></description>
			<content:encoded><![CDATA[<p>C&#8217;est la semaine de la publication&#8230; Après le gadget vista pour la NB4, voici un article qui explique comment gérer une structure arborescente avec django (un excellent framework python).<br />
J&#8217;espère que cet article sur django est le premier d&#8217;une longue série. J&#8217;ai comme beaucoup l&#8217;intention de basculer mon site sur cette techno, mais cela va prendre du temps. En attendant, j&#8217;essaierais de continuer à proposer des morceaux de codes ou des applications django comme celle-ci à l&#8217;avenir.</p>
<p>Depuis ma &#8216;conversion&#8217; à python, j&#8217;ai eu &#8211; entre autre &#8211; l&#8217;occasion de découvrir le framework de développement web django. Après avoir fait un peu de tour du marché des framework python open source, c&#8217;est vraiment celui qui m&#8217;a paru le plus prometteur avec une approche à la fois intelligente et très pragmatique du développement web. Depuis ce temps, je n&#8217;avais pas vraiment eu l&#8217;occasion de développer un site en particulier, l&#8217;occasion s&#8217;est présentée il y a environ un mois où j&#8217;ai eu le besoin de maquetter un petit site de gestion de catalogue de films (une sorte de proof of concept), pour lequel il a donc fallu constuire une arborescence du catalogue. Voici donc une petite application django qui permet de gérer un catalogue hierarchique.<span id="more-23"></span></p>
<p style="text-align: right;">[<a href="#Telechargement">cliquez ici pour accéder directement au téléchargement</a>]</p>
<span id="Fonctionnalits_et_technologies_utilises"><h1>Fonctionnalités et technologies utilisées</h1></span>
<p>A ce stade, le catalogue affiche une page web avec un catalogue hierarchique que l&#8217;on peut gérer dynamiquement.<br />
Toutes les modifications de ce catalogue sont réalisées en AJAX.<br />
Les fonctionnalités sont donc les suivantes :</p>
<ul>
<li>afficher un catalogue avec une vue hierarchique</li>
<li>déplacer des éléments du catalogue vers une autre position (avant, après ou à l&#8217;intérieur d&#8217;un autre élément)</li>
<li>Ajouter des éléments du catalogue (avant, après ou à l&#8217;intérieur d&#8217;un autre élement)</li>
<li>Supprimer un élément du catalogue</li>
<li>Modifier un élément du catalogue (le renommer)</li>
</ul>
<p>Les techno utilisées sont :</p>
<ul>
<li>Coté client
<ul>
<li>mootools (cela devient une habitude)</li>
</ul>
<ul>
<li>mootree (une extension à mootols qui permet de gérer justement des catalogue), dans une version modifiée par moro (http://forum.mootools.net/viewtopic.php?pid=23580) qui permet tout particulièrement la gestion des drag&amp;drop.</li>
</ul>
</li>
<li>Côté serveur
<ul>
<li>django et donc python (j&#8217;utilise la version de devt, à la revision 5830 au moment où j&#8217;écris ces lignes)</li>
</ul>
</li>
</ul>
<span id="Django"><h1>Django</h1></span>
<p>Comme à mon habitude, mon but n&#8217;est pas de réécrire ce qui a déjà été présenté et expliqué dans d&#8217;autres sites bien mieux que je ne pourrais le faire.<br />
Pour débuter sur django et pour aller plus loin ensuite, il existe de plus en plus de ressources sur le net.<br />
Je ne saurais trop vous conseiller de suivre les étapes du <a href="http://www.djangobook.com/" target="_blank">book django</a>.<br />
Le <a href="http://www.djangoproject.com/" target="_blank">site officiel de django</a> qui regroupe tout ce qui est nécesaire :<br />
Ainsi que le site français <a href="http://www.django-fr.org/" target="_blank">django-fr</a><br />
J&#8217;ai aussi trouvé beaucoup d&#8217;information très intéressantes et pertinentes sur le site de David Larlet : <a href="http://www.biologeek.com/" target="_blank">Biologeek</a></p>
<span id="Django_et_AJAX"><h2>Django et AJAX</h2></span>
<p>Le principal reproche que l&#8217;on fait généralement à django (notamment quand on le compare à rails <img src='http://www.chiroux.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  est qu&#8217;il ne gère pas les applications AJAX nativement.<br />
Nous allons voir plus loin que faire des applications AJAX avec django non seulement ne pose aucun problème mais en plus est extrèmement simple (comme souvent d&#8217;aileurs avec ce framework).</p>
<span id="Etape_1_la_modlisation_des_donnes"><h1>Etape 1, la modélisation des données</h1></span>
<p>Le principe général est que chaque élément est un noeud du catalogue et qu&#8217;il a donc un élémént &#8216;père&#8217;. Cela forme ainsi une structure hiérarchique potentiellement infinie. Les éléments qui n&#8217;ont pas de père sont les éléments en tête du catalogue.</p>
<p><img class="alignnone size-full wp-image-92" title="catalogue model" src="http://www.chiroux.com/wp-content/uploads/Import/Dev/catalogue model.JPG" alt="catalogue model" width="263" height="215" /></p>
<pre>
<pre class="brush: python;">class Catalogue(models.Model):
    node_name = models.CharField(maxlength=200)
    node_parent = models.ForeignKey('self', blank=True, null=True)
    path = models.CharField(maxlength=200, editable=False, null=True)
    indent_level = models.PositiveSmallIntegerField(editable=False, null=True)
    level_order = models.PositiveSmallIntegerField(editable=True, null=True)</pre>
</pre>
<p>l&#8217;élément de catalogue a également trois informations supplémentaires qui seront mises à jour automatiquement par l&#8217;application :</p>
<ul>
<li>le &#8216;path&#8217; qui représente de façon linéaire la position de l&#8217;élément dans le catalogue (ex pour l&#8217;émément d&#8217;id 4 : 1.2.3.4 signifie que son père est le 3 et que son grand père est le 2 et ainsi de suite)</li>
<li>l&#8217; &#8216;indent_level&#8217; représente le niveau de profondeur dans l&#8217;arbre où se site l&#8217;élément (0 est le niveau le plus haut, notre élément dans cet exemple a donc un indent_level de 3)</li>
<li>le &#8216;level_order&#8217; est un champ qui va servir à ordonner les éléments du même niveau hierarchique (pour ne pas obliger un tri en particulier)</li>
</ul>
<p>Ces 3 champs étant mis à jour dynamiquement, il est nécessaire, dans la définition du modèle, de réaliser des actions particulières lors de l&#8217;INSERT, l&#8217;UPDATE ou le DELETE d&#8217;un élémént.<br />
Dans les modèles django, c&#8217;est effectué en spécialisant les méthodes save() et delete() : a chaque action de save() [en cas d'INSERT ou UPDATE] ou de delete(), ces méthodes seront appelées et feront les calculs et modifications nécessaires à la mise à jour de ces données.</p>
<pre>
<pre class="brush: python;">    def save(self):
        &quot;&quot;&quot;Redéfinition du save afin de remplir les champs path, indent_level et level_order
        &quot;&quot;&quot;
        if self.id == None:
          # C'est un insert : On fait un premier save pour récupérer un id
          self.path = ''
          self.indent_level = 0
          #self.level_order=0
          super(Catalogue, self).save() # call the real save()

        # On modifie son path en ajoutant son id au path du parent
        parent = self.node_parent
        if self.node_parent != None:
          self.path=parent.path + '.%s' % self.id
          self.indent_level= parent.indent_level + 1
        else:
          self.path = '%s' % self.id
          self.indent_level = 0
        super(Catalogue, self).save() # call the real save()

        #Ensuite on resauvegarde les enfants pour remettre à jour path et indent_level
        for son in Catalogue.objects.filter(node_parent=self):
          son.save()</pre>
</pre>
<pre>
<pre class="brush: python;">	def delete(self):
	    &quot;&quot;&quot;Traitement du delete du catalogue afin notamment de raccrocher les fils au nouveau père
	    &quot;&quot;&quot;
	    #Identifier les fils
	    sons = Catalogue.objects.filter(node_parent=self)
	    for son in sons:
	      #pour chaque fils, le rattacher à mon propre pere
	      son.node_parent=self.node_parent
	      son.save()

	    super(Catalogue, self).delete() # call the real delete()</pre>
</pre>
<p>On peut remarquer ici que le delete ne supprime pas l&#8217;arborescence qui en sous l&#8217;élément effacé. C&#8217;est un choix de ma part, à la fois pour des questions de sécurité (eviter l&#8217;erreur fatale) et pour des raisons de gestions, cela me parait plus logique au jour le jour d&#8217;avoir des manipulations unitaires que des suppressions de catalogues complets.</p>
<p>Toutefois il serait imaginable de coder une sorte de &#8217;super delete&#8217; qui supprimerait l&#8217;élément et tous ses enfants.</p>
<span id="Etape_2_les_URLS"><h1>Etape 2, les URLS</h1></span>
<p>L&#8217;application et donc les URLS sont d&#8217;inspiration REST. Je ne peut pas encore parler de RESTFULL car je n&#8217;ai pas encore assez creusé le sujet. A ce sujet les articles de David sur son site, notamment <a href="http://www.biologeek.com/journal/index.php/une-solution-pour-faciliter-la-conception-d-applications-web-restful-avec-django" target="_blank">celui ci assez récent </a>sont des très bon points de départ.</p>
<p>La gestion des URLs est déléguée dans l&#8217;application, ainsi la config d&#8217;urls.py du projet est assez simple :</p>
<pre>
<pre class="brush: python;">urlpatterns = patterns('',
    (r'^catalogue/', include('myproject.catalogue.urls')),</pre>
</pre>
<p>le fichier urls.py du projet catalogue est plus intéressant :</p>
<pre>
<pre class="brush: python;">from django.conf.urls.defaults import *
from myproject.catalogue import views

urlpatterns = patterns('myproject.catalogue.views',
    (r'xml/?', 'catalogueXML'),
    (r'move/?', 'moveCatalogueItem'),
    (r'insert/?', 'insertCatalogueItem'),
    (r'delete/?', 'deleteCatalogueItem'),
    (r'update/?', 'updateCatalogueItem'),
    (r'/?$', 'catalogue'),</pre>
</pre>
<p>Les URLS globales seront donc de la forme : /catalogue/xml/, etc&#8230;</p>
<p>Voici leur rôle :</p>
<ul>
<li>/catalogue/
<ul>
<li>Affiche la page web du catalogue</li>
</ul>
</li>
<li>/catalogue/xml/
<ul>
<li>renvoie une strucute xml avec tout ou partie du catalogue (un filtrage peut être réalisé en envoyant des données en POST)</li>
</ul>
</li>
<li>/catalogue/move/
<ul>
<li>toujours en fonction des données envoyées en POST, permet de déplacer des éléments du catalogue</li>
</ul>
</li>
<li>/catalogue/insert/
<ul>
<li>insère un élément dans le catalogue (le PUT du REST)</li>
</ul>
</li>
<li>/catalogue/delete/
<ul>
<li>supprime un élément dans le catalogue</li>
</ul>
</li>
<li>/catalogue/update/
<ul>
<li>modifie un élément du catalogue</li>
</ul>
</li>
</ul>
<p>A chaque URL correspond une vue que nous allons décrire maintenant</p>
<span id="Etape_3_Les_vues"><h1>Etape 3, Les vues</h1></span>
<span id="la_vue_catalogue"><h2>la vue catalogue</h2></span>
<p>Cette vue est ultra simple et pourrait dans l&#8217;absolu être hébergée n&#8217;importe où : comme toutes les autres relations entre la page web et le site sont en AJAX, django n&#8217;a pas besoin de gérer cette vue. Elle est ajoutée ici pour des raisons de cohérence et de simplicité. Elle ne fait rien d&#8217;autre que d&#8217;afficher un template de la page catalogue.html. Nous reviendrons sur cette page plus bas car c&#8217;est là que se situe tout le code javascript.</p>
<span id="la_vue_catalogueXML"><h2>la vue catalogueXML</h2></span>
<p>cette vue renvoie une structure XML représentant l&#8217;arbre (ou une partie de l&#8217;arbre) du catalogue.<br />
Elle prend en argument en POST le &#8216;node_path&#8217; du node de l&#8217;arbre a partir duquel on souhaite récupérer l&#8217;arborescence. Si on n&#8217;envoie aucun paramètre c&#8217;est l&#8217;arbre entier qui est retourné.</p>
<p>A noter : toutes les vues gère à la fois la récupération des arguments en GET ou en POST, c&#8217;est principalement pour des raisons de debug.</p>
<p>Le parcours du catalogue en base est réalisé de façon récursive et cette méthode appelle donc une sous méthode récursive (recurseCatalogueXML) qui elle même continue à parcourir les sous éléments du noeud.<br />
Il y a d&#8217;autres façon de faire, certainement plus performantes (notamment en utilisant un parcours linéraire en ordonant les résultats du node_path), mais alors les fonctions pour générer la structure XML sont plus complexes à écrire. J&#8217;ai choisi ici la lisiblité au détriment de la performance.</p>
<pre>
<pre class="brush: python;">def catalogueXML(request):
    node=None
    try:
        if request.method == 'GET':
            debug('catalogueXML - method:GET')
            node_path = request.GET['node_path']
        elif request.method== 'POST':
            debug('catalogueXML - method:POST')
            node_path = request.POST['node_path']
        try:
            node = Catalogue.objects.get(path=node_path)
        except ObjectDoesNotExist:
            node = None
    except KeyError:
        # Pas de request, on part du root catalogue
        node = None

    xml = ''
    xml = recurseCatalogueXML(node)

    t = loader.get_template('catalogue_xml.xml')
    c = Context({
        'snippet': xml,
    })
    return HttpResponse(t.render(c), mimetype='application/xml')</pre>
</pre>
<pre>
<pre class="brush: python;">def recurseCatalogueXML(parent):
    if parent == None:
      items_list = Catalogue.objects.filter(node_parent__isnull=True).order_by('level_order')
    else:
      items_list = Catalogue.objects.filter(node_parent=parent).order_by('level_order')

    xml = ''

    for item in items_list:
        tmp_xml = recurseCatalogueXML(item)
        if tmp_xml == '':
            #pas d'enfants
            xml+= '\n'
        else:
            #des enfants
            xml+= '\n' + tmp_xml + ''

    return xml</pre>
</pre>
<span id="la_vue_updateCatalogueItem"><h2>la vue updateCatalogueItem</h2></span>
<p>C&#8217;est la vue &#8216;active&#8217; la plus simple. Elle prend en argument le path de l&#8217;item à modifier ainsi que le nouveau nom, récupère l&#8217;élément, le modifie et retourne le nouveau nom.</p>
<p>A noter : Pour cette vue et toutes les autres vues de modification, j&#8217;ai choisit aussi la simplicité concernant la gestion des valeurs de retour, en renvoyant du texte simple.<br />
Une bonne application bien faite et bien propre renverrait une structure JSON ou XML avec des valeurs normalisées. Je le modifierais peut-être à l&#8217;avenir dans ce sens (notamment en permettant de choisir le format de retout JSON ou XML). Il sera de toute façon obligatoire de le faire à partir du moment où il est nécessaire de retourner plusieurs éléments, ce qui est d&#8217;ailleurs en théorie le cas ici).</p>
<p>Dans cet exemple, si l&#8217;update n&#8217;a pas pu être réalisé, la vue renverra &#8216;NOK&#8217; et dans les autres cas le nouveau nom du noeud. Il y a donc un problème théorique si on renomme notre noeud en &#8216;NOK&#8217; et que tout se passe bien&#8230; j&#8217;ai un peu triché là dessus côté client, nous verrons tout à l&#8217;heure.</p>
<pre>
<pre class="brush: python;">def updateCatalogueItem(request):
    if request.method == 'GET':
        debug('updateCatalogueItem - method:GET')
        node_path = request.GET['node_path']
        item_new_name = request.GET['node_name']
    elif request.method== 'POST':
        debug('updateCatalogueItem - method:POST')
        node_path = request.POST['node_path']
        item_new_name = request.POST['node_name']

    debug('item:'+node_path)
    debug('new name:'+item_new_name)

    result=''
    try:
        node_to_be_updated = Catalogue.objects.get(path=node_path)
        node_to_be_updated.node_name = item_new_name
        node_to_be_updated.save()
        result=item_new_name
    except ObjectDoesNotExist:
        result='NOK'

    return HttpResponse(result)</pre>
</pre>
<span id="la_vue_deleteCatalogueItem"><h2>la vue deleteCatalogueItem</h2></span>
<p>Cette vue prends un seul paramètre : le node_path du noeud à supprimer et renvoye &#8216;OK&#8217; ou &#8216;NOK&#8217;.</p>
<p>En fait cette vue ne fait rien d&#8217;intelligent (elle récupère le noeud et le supprime) car toute l&#8217;intelligence de traitement (notamment rattacher les noeud fils au père du noeud supprimé) sont fait dans les modèles que nous avons vu tout à l&#8217;heure.</p>
<pre>
<pre class="brush: python;">def deleteCatalogueItem(request):
    if request.method == 'GET':
        debug('deleteCatalogueItem - method:GET')
        node_path = request.GET['node_path']
    elif request.method== 'POST':
        debug('deleteCatalogueItem - method:POST')
        node_path = request.POST['node_path']

    debug('node_path :'+node_path)

    result=''
    try:
        node_to_be_deleted = Catalogue.objects.get(path=node_path)
        node_to_be_deleted.delete()
        result='OK'
    except ObjectDoesNotExist:
        result='NOK'

    return HttpResponse(result)</pre>
</pre>
<span id="la_vue_insertCatalogueItem"><h2>la vue insertCatalogueItem</h2></span>
<p>Cette vue est un peu plus complexe car elle doit gérer 3 cas différents :</p>
<ul>
<li>L&#8217;insertion avant un item</li>
<li>L&#8217;insertion après un item</li>
<li>L&#8217;insertion à l&#8217;intérieur d&#8217;un item (et dans ce cas il faut gérer si c&#8217;est le premier élément sous l&#8217;item ou non)</li>
</ul>
<p>Elle prend donc 3 arguments :</p>
<ul>
<li>le nom de l&#8217;item à insérer</li>
<li>le node_path de l&#8217;item &#8216;près&#8217; duquel nous allons insérer ce nouveau noeud</li>
<li>le type d&#8217;insertion (before, after ou inside)</li>
</ul>
<pre>
<pre class="brush: python;">def insertCatalogueItem(request):
    if request.method == 'GET':
        debug('insertCatalogueItem - method:GET')
        new_node_name = request.GET['node_name']
        to_item = request.GET['to_item']
        insert_type = request.GET['insert_type'] # before, after or inside
    elif request.method== 'POST':
        debug('insertCatalogueItem - method:POST')
        new_node_name = request.POST['node_name']
        to_item = request.POST['to_item']
        insert_type = request.POST['insert_type'] # before, after or inside

    debug('node_name :'+new_node_name )
    debug('to:'+to_item)
    debug('type:'+insert_type)</pre>
</pre>
<p>Comme il faut gérer des notions comme avant ou après un noeud, il faut gérer l&#8217;ordre, c&#8217;est donc pour cela que le champ &#8216;level_order&#8217; existe.<br />
Donc pour insérer l&#8217;item au bon endroit, il est nécessaire de connaitre son père et son numéro d&#8217;ordre à côté de ses frères.<br />
La première chose à faire est donc de déterminer ces éléments en fonction des paramètres :</p>
<pre>
<pre class="brush: python;">#On récupère le père
if insert_type == 'before':
    new_node_parent=Catalogue.objects.get(path=to_item).node_parent
    new_level_order=Catalogue.objects.get(path=to_item).level_order
elif insert_type == 'after':
    new_node_parent=Catalogue.objects.get(path=to_item).node_parent
    new_level_order=Catalogue.objects.get(path=to_item).level_order+1
elif insert_type == 'inside':
    try:
        new_node_parent=Catalogue.objects.get(path=to_item)
    except ObjectDoesNotExist:
        # Il n'y a pas d'objet c'est vraissemblablement le premier
        new_node_parent = None

    try:
        items_list=Catalogue.objects.filter(node_parent=new_node_parent).order_by('-level_order')
        new_level_order = items_list[0].level_order+1
    except IndexError:
        new_level_order = 1</pre>
</pre>
<p>En cas d&#8217;insertion before ou after, le parent est le même : c&#8217;est le parent du node mis en référence.<br />
En cas d&#8217;insertion inside, le parent est directement le node mis en référence.<br />
J&#8217;ai choisi pour l&#8217;insersion inside d&#8217;insérer par défaut en dernière position, il faut donc récupérer quelle est la dernière position des fils du node en référence.</p>
<p>Ensuite, en cas d&#8217;insertion before ou after, on doit éventuellement décaler tous les autres items &#8216;frères&#8217; d&#8217;un cran afin de laisser la place à celui qui arrive :</p>
<pre>
<pre class="brush: python;"># Pour les insert before et after : On update le level order de tous les enregistrements suivant du même niveau
    if insert_type == 'before' or insert_type == 'after':
        items_list = Catalogue.objects.filter(node_parent=new_node_parent, level_order__gte=new_level_order).order_by('level_order')
        for item in items_list:
            item.level_order+=1
            item.save()</pre>
</pre>
<p>Enfin on crée l&#8217;élément avec ces valeurs et on retourne le path :</p>
<pre>
<pre class="brush: python;"># On crée l'enregistrement
p = Catalogue.objects.create(node_name=new_node_name, node_parent=new_node_parent, level_order=new_level_order)

return HttpResponse(p.path)</pre>
</pre>
<span id="la_vue_moveCatalogueItem"><h2>la vue moveCatalogueItem</h2></span>
<p>Comme pour l&#8217;insert, cette vue va gérer trois types de mouvements : before, after ou inside un élément.</p>
<pre>
<pre class="brush: python;">def moveCatalogueItem(request):
    if request.method == 'GET':
        debug('moveCatalogueItem - method:GET')
        item = request.GET['item']
        to_item = request.GET['to_item']
        move_type = request.GET['move_type'] # before, after or inside
    elif request.method== 'POST':
        debug('moveCatalogueItem - method:POST')
        item = request.POST['item']
        to_item = request.POST['to_item']
        move_type = request.POST['move_type'] # before, after or inside

    debug('item:'+item)
    debug('to:'+to_item)
    debug('type:'+move_type)</pre>
</pre>
<p>Ensuite, nous allons changer le parent de l&#8217;item en fonction des paramètres d&#8217;entrées pour le mettre au bon endroit :</p>
<pre>
<pre class="brush: python;">    # le père de l'item est maintenant le même père que le to
    # On récupère le père de to
    catalogue_item_to = Catalogue.objects.get(path=to_item)
    # On récupère l'item
    catalogue_item = Catalogue.objects.get(path=item)

    # On met le bon parent
    if move_type == 'inside':
        catalogue_item.node_parent = catalogue_item_to
    else:
        catalogue_item.node_parent = catalogue_item_to.node_parent
    catalogue_item.level_order = 0
    catalogue_item.save()</pre>
</pre>
<p>Enfin on gère l&#8217;ordre, en décalant tous les éléments qu&#8217;il est nécessaire de décaler.<br />
Cette partie est une des premières que j&#8217;avais réalisé, je m&#8217;aperçois aujourd&#8217;hui qu&#8217;elle est largement optimisable (éviter de parcourir tous les éléments)</p>
<pre>
<pre class="brush: python;">    items_list = Catalogue.objects.filter(node_parent=catalogue_item.node_parent).order_by('level_order')
    offset = 0
    for item in items_list:
        if item.path == to_item:
            if move_type == 'before':
                offset = 1
                catalogue_item.level_order = item.level_order # on donne le numéro d'ordre à l'item inséré puis on ajoute l'offset aux items suivants
                catalogue_item.save()
            elif move_type == 'after':
                #En insertion after, l'augmentation d'offset des items ne doit se produire qu'au tour suivant, on va donc 'forcer' un tour
                offset = 1
                catalogue_item.level_order = item.level_order+1
                catalogue_item.save()
                continue
            elif move_type == 'inside':
                pass
        if offset != 0:
            debug_string = 'On ajoute %s' % offset + ' a item %s' % item.node_name
            debug(debug_string)
            item.level_order+=offset
            item.save()
        last_level_order = item.level_order

    if offset == 0:
        # on a pas trouvé, donc on insére à la fin
        catalogue_item.level_order = last_level_order+1
        catalogue_item.save()

    result = catalogue_item.path
    return HttpResponse(result)</pre>
</pre>
<span id="Etape_4_le_ct_client_en_javascript"><h1>Etape 4, le côté client en javascript</h1></span>
<span id="Rsum_des_flux_AJAX"><h2>Résumé des flux AJAX</h2></span>
<p>Voici un petit dessin qui résume les différentes interfaces que l&#8217;on a définie dans l&#8217;application django et donc ce que le côté client doit transmettre et recevoir :</p>
<p><img title="Catalogue_flux_AJAX" src="../wp-content/uploads/Import/Dev/Catalogue_flux_AJAX.jpg" alt="Catalogue_flux_AJAX" width="491" height="744" /></p>
<span id="Le_code_HTML"><h2>Le code HTML</h2></span>
<p>Le code html est constitué de deux parties :<br />
le div qui va recevoir l&#8217;arbre :</p>
<pre>
<pre class="brush: xml;">&lt;div id=&quot;mytree_catalogue&quot;&gt;
&lt;/div&gt;</pre>
</pre>
<p>les formulaires qui servent à gérer les différentes actions possibles sur les éléments du catalogue.<br />
Ces formulaires sont très basiques et très moches. Le but est de montrer les fonctionnalités. Chacun intégrera ces fonctions à sa manière dans son site. Une implémentation sympa serait de gérer le bouton droit quand on est sur un élément de l&#8217;arbre et afficher un petit menu avec ces différentes possibilités. C&#8217;est éventuellement une évolution que j&#8217;apporterais à l&#8217;avenir.</p>
<p>Pour plus de lisibilité, j&#8217;ai séparé les différentes fonctions en différentes boites :</p>
<pre>
<pre class="brush: xml;">&lt;div id=&quot;boite1&quot; class=&quot;form_div&quot;&gt;
    &lt;input type=&quot;button&quot; value=&quot; expand all &quot; onclick=&quot;tree.expand()&quot; /&gt;
    &lt;input type=&quot;button&quot; value=&quot; collapse all &quot; onclick=&quot;tree.collapse()&quot; /&gt;
&lt;/div&gt;
&lt;div id=&quot;boite2&quot; class=&quot;form_div&quot;&gt;
    &lt;form id=&quot;insertForm&quot; action=&quot;/catalogue/insert/&quot; method=&quot;post&quot; name=&quot;insertForm&quot;&gt;
        &lt;input type=&quot;text&quot; value=&quot;&quot; name=&quot;node_name&quot; /&gt;&lt;br /&gt;
        &lt;input type=&quot;hidden&quot; value=&quot;&quot; name=&quot;to_item&quot; /&gt;
        &lt;input type=&quot;hidden&quot; value=&quot;before&quot; name=&quot;insert_type&quot; /&gt;
        &lt;input type=&quot;submit&quot; value=&quot; insert before&quot; name = &quot;button&quot; onclick=&quot;$('insertForm').insert_type.value='before';&quot; /&gt;
        &lt;input type=&quot;submit&quot; value=&quot; insert after&quot; name = &quot;button&quot; onclick=&quot;$('insertForm').insert_type.value='after'; &quot; /&gt;
        &lt;input type=&quot;submit&quot; value=&quot; insert inside&quot; name = &quot;button&quot; onclick=&quot;$('insertForm').insert_type.value='inside';&quot; /&gt;
    &lt;/form&gt;
&lt;/div&gt;
&lt;div id=&quot;boite3&quot; class=&quot;form_div&quot;&gt;
    &lt;form id=&quot;deleteForm&quot; action=&quot;/catalogue/delete/&quot; method=&quot;post&quot; name=&quot;deleteForm&quot;&gt;
        &lt;input type=&quot;hidden&quot; value=&quot;&quot; name=&quot;node_path&quot; /&gt;
        &lt;input type=&quot;submit&quot; value=&quot; delete selected &quot; name= &quot;button&quot; /&gt;
    &lt;/form&gt;
&lt;/div&gt;
&lt;div id=&quot;boite4&quot; class=&quot;form_div&quot;&gt;
    &lt;form id=&quot;updateForm&quot; action=&quot;/catalogue/update/&quot; method=&quot;post&quot; name=&quot;updateForm&quot;&gt;
        &lt;input type=&quot;text&quot; value=&quot;&quot; name=&quot;node_name&quot; /&gt;
        &lt;input type=&quot;hidden&quot; value=&quot;&quot; name=&quot;node_path&quot; /&gt;
        &lt;input type=&quot;submit&quot; value=&quot; update selected &quot; name= &quot;button&quot; /&gt;
    &lt;/form&gt;
&lt;/div&gt;</pre>
</pre>
<span id="Le_javascript"><h2>Le javascript</h2></span>
<p>Au chargement de la page, la fonction main() est appelée, tout est à l&#8217;intérieur.</p>
<span id="La_premire_chose__faire_est_d8217initaliser_le_mootree_:"><h3>La première chose à faire est d&#8217;initaliser le mootree :</h3></span>
<pre>
<pre class="brush: jscript;">    function main() {
    tree = new MooTreeControl({
            div: 'mytree_catalogue',
            mode: 'files',
            theme: '/site_media/js/mootree.gif',
            grid: true,
            onSelect: function() {
                //alert('coucou');
                $('updateForm').node_name.value = this.selected.text;
                $('updateForm').node_path.value = this.selected.id;

            },
            onReplace: function(from,to,where){
                var myXHR = new XHR({
                    method: 'post',
                    onSuccess: function(new_path) {
                        from.id = new_path;

                    },
                    headers: {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8'}
                    }).send('/catalogue/move/', 'item='+from.id+'&amp;amp;to_item='+to.id+'&amp;amp;move_type='+where);

                //alert([from.id,to.id,where])
            },
        nodeOptions: {
            text: 'Catalogue',
            open: true
        }
        });</pre>
</pre>
<p>L&#8217;évènement onReplace est un événement ajouté par moro sur le <a href="http://forum.mootools.net/viewtopic.php?pid=23580" target="_blank">forum mootools</a> il permet de gérer les déplacement d&#8217;items. Donc en cas d&#8217;événement de déplacement d&#8217;item, on lance cette fonction qui va réaliser un appel AJAX vers le serveur sur l&#8217;url /catalogue/move avec les bons paramètres.<br />
En cas de retour ok, on met à jour l&#8217;id de l&#8217;élément du tree. (dans mootree, j&#8217;ai mappé l&#8217;id sur le &#8216;path&#8217; du modèle de donnée python).<br />
Avec cette seule fonction, on a déjà géré le déplacement d&#8217;items au sein de l&#8217;arbre (merci à mootree et à moro..)</p>
<p>le onSelect permet de faire des action lorsqu&#8217;on sélectionne un élément de l&#8217;arbre, cela sert ici à renseigner le champ &#8216;nom de l&#8217;arbre&#8217; du formulaire de modification (c&#8217;est plus pratique et cela évite de le retaper).</p>
<p>Une fois le mootree initialisé, il faut le remplir, cela se fait avec la fonction &#8216;load&#8217; :</p>
<pre>
<pre class="brush: jscript;">tree.root.load('/catalogue/xml/')</pre>
</pre>
<p>N&#8217;ayant founi aucun paramètre à /catalogue/xml/, nous chargeons l&#8217;arbre en entier comme nous l&#8217;avons vu tout à l&#8217;heure.</p>
<p>Ensuite il faut gérer les différentes actions des formulaires.</p>
<span id="Commenons_par_le_formulaire_d8217insertion_:"><h3>Commençons par le formulaire d&#8217;insertion :</h3></span>
<pre>
<pre class="brush: jscript;">    $('insertForm').addEvent('submit', function(e) {
        // Prevent the submit event
        new Event(e).stop();

        // récupère l'item actuellement sélectionné
        if (tree.selected != null) {
            this.to_item.value = tree.selected.id;

            this.send({
                //update: log,
                onComplete: function(path_inserted) {
                    //log.removeClass('ajax-loading');
                    //alert(text);

                    if ($('insertForm').insert_type.value=='before') {
                        node = tree.selected.parent.insert({text:$('insertForm').node_name.value, id:path_inserted});
                        node.injectBefore(tree.selected);
                    }
                    if ($('insertForm').insert_type.value=='after') {
                        node = tree.selected.parent.insert({text:$('insertForm').node_name.value, id:path_inserted});
                        node.injectAfter(tree.selected);
                    }
                    if ($('insertForm').insert_type.value=='inside') {
                        node = tree.selected.insert({text:$('insertForm').node_name.value, id:path_inserted});
                    }
                }
            });
        }
    });</pre>
</pre>
<p>la fonction mootols <em>$(&#8216;insertForm&#8217;).addEvent </em>permet d&#8217;ajouter un gestionnaire d&#8217;évenement sur le formulaire (ici sur l&#8217;évènement &#8217;submit&#8217;). Donc a chaque fois que le formulaire sera validé, l&#8217;événement se déclenchera.<br />
La variable &#8216;this&#8217; au sein de cette fonction représente donc le formulaire.</p>
<p>Pour réaliser une insertion, il faut avoir sélectionné un élément de l&#8217;arbre.<br />
On le récupère en javascript via <em>tree.selected</em></p>
<p>ensuite le this.send est un raccouci mootools qui déclenche l&#8217;envoi en AJAX du formulaire vers l&#8217;url et en utilisant la méthode définie dans le formulaire (dans le code html du formulaire que l&#8217;on a vu plus haut).</p>
<p>Au retour, on déclenche l&#8217;événement &#8216;onComplete&#8217;.<br />
Dans cette fonction, on va ajouter l&#8217;élément à l&#8217;arbre côté client (car à ce moment il est déjà ajouté côté serveur). L&#8217;insertion est différente en fonction des types d&#8217;insertion (before, after ou inside), mais le principe est le même : on utilise la méthode &#8216;insert&#8217; de mootree.</p>
<p>Ensuite le formulaire de suppression :</p>
<p>Il est construit sur le même principe que l&#8217;insertion</p>
<pre>
<pre class="brush: jscript;">    $('deleteForm').addEvent('submit', function(e) {
        new Event(e).stop();

        if (tree.selected != null) {
            this.node_path.value = tree.selected.id;
            this.send({
                onComplete: function(result) {
                    // On récupère le père (pour le rechargemetn de la partie du tree)
                    node_parent = tree.selected.parent;
                    // Ici effacer le node du tree
                    tree.selected.remove();
                    // reload du tree
                    // TODO: faire en sorte de ne reloader que à partir du père (nécessite aussi de changer le code serveur)
                    node_parent.load('/catalogue/xml/?node_path='+node_parent.id);
                    //tree.root.load('/catalogue/xml/');
                }
            });
        }

    });</pre>
</pre>
<p>Au retour de l&#8217;appel Ajax, une fois l&#8217;élément supprimé côté serveur, on le supprime dans l&#8217;arbre mootree.<br />
Ensuite on déclenche un reload du sous arbre en partant du père de l&#8217;élément sélectionné : ceci est fait parceque la méthode remove() de mootree, contrairement à ce qu&#8217;on a choisit de faire côté serveur supprime tous les enfants avec. Donc il faut recharger cette partie de l&#8217;arbre.</p>
<span id="Ensuite_le_formulaire_de_mise__jour_:"><h3>Ensuite le formulaire de mise à jour :</h3></span>
<pre>
<pre class="brush: jscript;">    $('updateForm').addEvent('submit', function(e) {
        new Event(e).stop();

        if (tree.selected != null) {
            //this.node_path.value = tree.selected.id;
            this.send({
                onComplete: function(result) {
                    // On récupère le père (pour le rechargemetn de la partie du tree)
                    if (result != 'NOK') {
                        tree.selected.text=result;
                        tree.selected.update();
                    } else {
                        node_parent = tree.selected.parent;
                        node_parent.load('/catalogue/xml/?node_path='+node_parent.id);
                    }
                }
            });
        }
    });</pre>
</pre>
<p>Ici il y a un petit truc qui fait echo au côté serveur : nous avons vu précedemment que la fonction côté serveur renvoyait le nouveau nom en cas de réussite et &laquo;&nbsp;NOK&nbsp;&raquo; en cas d&#8217;échec. Donc ici, si la valeur retournée est différente de &laquo;&nbsp;NOK&nbsp;&raquo;, on met à jour le noeud mootree avec cette valeur, mais si le retour est &laquo;&nbsp;NOK&nbsp;&raquo;, nous ne sommes pas certain si c&#8217;était une erreur ou si l&#8217;utilisateur voulait vraiment renommer son noeud en &laquo;&nbsp;NOK&nbsp;&raquo;, donc on recharge cette partie de l&#8217;arbre depuis le serveur, car c&#8217;est toujours le serveur qui a raison.<br />
On voit ici les limites de ne renvoyer qu&#8217;une seule valeur à l&#8217;appel AJAX (qui d&#8217;ailleurs est un nom abusif dans ce cas <img src='http://www.chiroux.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> ). Dans une version plus évolué de ce programme, il faudra passer à un retour JSON ou XML et bien gérer tous les cas d&#8217;erreurs côté client.</p>
<span id="Enfin_le_formulaire_de_rechargement_:"><h3>Enfin le formulaire de rechargement :</h3></span>
<p>Le principe est simple : on recharge la partie de l&#8217;arbre qui est sous l&#8217;élément sélectionné :</p>
<pre>
<pre class="brush: jscript;">    $('reloadForm').addEvent('submit', function(e) {
        new Event(e).stop();

        if (tree.selected != null) {
            node = tree.selected;
            node.load('/catalogue/xml/?node_path='+node.id);
        }

    });</pre>
</pre>
<span id="Telechargement_et_Live_Dmo"><h1><a name="Telechargement"></a>Telechargement et Live Démo</h1></span>
<p>Le serveur de chiroux.com n&#8217;est pas -encore- compatible python / Django, donc pour héberger la démo j&#8217;ai choisi de le mettre sur alwaysdata suite aux conseils de <a href="http://jehaisleprintemps.net/detail.php?id=1595&amp;lang=fr" target="_blank">jehaisleprintemps</a>.</p>
<p><span style="font-size: medium;">Vous pouvez donc <a href="http://thomas_chiroux.alwaysdata.net/catalogue/" class="broken_link"  target="_blank">trouver la démo ici</a>. Amusez vous bien.</span></p>
<p>L&#8217;admin est également disponible <a href="http://thomas_chiroux.alwaysdata.net/admin/" class="broken_link"  target="_blank">en suivant ce lien</a>. Le login et pass sont : admin/admin</p>
<p>Vous pouvez télécharger l&#8217;archive d&#8217;un site django complet qui inclu cette seule application &#8216;catalogue&#8217; ci-dessous (archive extraite du site de démo) :</p>
Note: There is a file embedded within this post, please visit this post to download the file.
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/django-une-arborescence-de-catalogue/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Un gadget Vista pour la NeufBox 4</title>
		<link>http://www.chiroux.com/un-gadget-vista-pour-la-neufbox-4/</link>
		<comments>http://www.chiroux.com/un-gadget-vista-pour-la-neufbox-4/#comments</comments>
		<pubDate>Tue, 07 Aug 2007 15:17:54 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[mootools]]></category>
		<category><![CDATA[neufbox]]></category>
		<category><![CDATA[vista]]></category>
		<category><![CDATA[widget]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/article-1186499874.html</guid>
		<description><![CDATA[<p>C&#8217;est l&#8217;été, mais il fait moche, alors que faire a part un peu de code par ci par là&#8230;
Voici donc un petit gadget windows vista qui permet d&#8217;interroger la NB4 de neuf et d&#8217;afficher les états dans notre barre vista.
L&#8217;article explique comment ce gadget est codé et comment on interroge la NB4.
Bon ce n&#8217;est donc [...]]]></description>
			<content:encoded><![CDATA[<p>C&#8217;est l&#8217;été, mais il fait moche, alors que faire a part un peu de code par ci par là&#8230;<br />
Voici donc un petit gadget windows vista qui permet d&#8217;interroger la NB4 de neuf et d&#8217;afficher les états dans notre barre vista.<br />
L&#8217;article explique comment ce gadget est codé et comment on interroge la NB4.<br />
Bon ce n&#8217;est donc pas encore aujourd&#8217;hui que je pourrais écrire mon premier article sur une application django, mais j&#8217;y travaille&#8230;<br />
Peut être avant mes vacances, peut-être pas, nous verrons bien.</p>
<p>Neuf, avec la NB4, propose un routeur évolué et ouvert. Une petite API permet notamment d&#8217;interroger la NB4 et de récupérer les valeurs de différents paramètres en temps réél. Il n&#8217;y avait qu&#8217;un petit pas à franchir pour l&#8217;utiliser et afficher ces paramètres directement dans notre PC.</p>
<p><span id="more-24"></span></p>
<span id="Un_gadget_Vista"><h1>Un gadget Vista</h1></span>
<p>Etant sous vista sur mon PC &#8216;principal&#8217; (celui sur lequel je joue..), c&#8217;est tout naturellement que je me suis orienté sur cette plate-forme pour faire ce gadget. (Je vous rassure, tous mes autres PC sont sous linux <img src='http://www.chiroux.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Il doit être possible avec un minimum d&#8217;adaptation de le faire fonctionner sous mac ou sous d&#8217;autres outils style konfabulator (Yahoo widgets). Nous verrons cela peut-être dans une prochaine version.</p>
<p><a href="#Telechargement">[Je suis curieux mais paresseux et je voudrais tester tout de suite : envoyez moi directement au téléchargement]</a></p>
<span id="Sinon_un_gadget_vista_c8217est_quoi_"><h1>Sinon un gadget vista, c&#8217;est quoi ?</h1></span>
<p>Construire un gadget vista c&#8217;est assez simple : il suffit de placer les bons fichiers dans un répertoire.<br />
Les tutos officiels se trouvent ici :  <a href="http://gallery.live.com/devcenter.aspx#Dev_8" target="_blank">http://gallery.live.com/devcenter.aspx</a></p>
<p>il nous faut donc au minimum :</p>
<ul>
<li> un fichier gadget.xml, dans lequel nous allons décrire notre gadget</li>
<li> un fichier html, dont le nom est décrit dans le xml ci-dessus</li>
<li> un fichier d&#8217;icône (optionnel)</li>
<li>un css si on veut que ce soit joli</li>
</ul>
<span id="Le_Gadget_NB4"><h1>Le Gadget NB4</h1></span>
<span id="Les_fonctionnalits"><h2>Les fonctionnalités</h2></span>
<p>A ce jour, dans la version de dev actuelle, le gadget permet d&#8217;afficher deux type d&#8217;informations :</p>
<ul>
<li> des infos de type booléennes (on/off, oui/non, etc&#8230;) sous la forme (par défaut dans l&#8217;exemple) d&#8217;un petit feu rouge ou vert (mais vous pouvez mettre les graphismes que vous souhaitez)</li>
<li> des infos &#8216;historiques&#8217; (un graphique historique) basé sur des valeurs numériques retournées par la NB4</li>
</ul>
<p>Dans l&#8217;exemple, le gadget va chercher le status du lien DSL, le status du PPP, le status du wifi, ainsi que l&#8217;évolution du rapport signal/bruit de votre ligne :</p>
<p><img class="alignnone size-full wp-image-89" title="NB4_gadget_undocked" src="http://www.chiroux.com/wp-content/uploads/Import/Dev/NB4_gadget_undocked.JPG" alt="NB4_gadget_undocked" width="183" height="114" /></p>
<p>(Oui, il est moche, je sais, mais je suis assez nul en design et graphisme, je laisse ça aux spécialiste&#8230; le but est avant tout de démontrer la faisabilité de ce gadget. Si vous vous sentez l&#8217;âme d&#8217;un artiste, n&#8217;hésitez surtout pas <img src='http://www.chiroux.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<span id="Etat_du_dveloppement"><h2>Etat du développement</h2></span>
<p><strong><span style="color: #ff0000;">Le gadget est encore loin d&#8217;être terminé</span></strong>. Il manque tout particulièrement les écrans d&#8217;options qui permettrait à l&#8217;utilisateur lambda de choisir parmi une liste de paramètre ceux qu&#8217;il souhaite monitorer.<br />
Il faut pour cela construire les écrans de settins du gadget (ce qui n&#8217;est pas très compliqué en soit), mais également rendre plus générique un certain nombre d&#8217;éléments (comme les images on/off par exemple).<br />
J&#8217;ai souhaité quand même publier cet article avec ce qui avait déjà était fait, cela permettra aux plus bidouilleurs d&#8217;entre vous de continuer ou de partir sur d&#8217;autres orientations. Les grands principes sont là en tout cas.</p>
<span id="Comment_c8217est_fait_"><h2>Comment c&#8217;est fait ?</h2></span>
<p>Comme d&#8217;habitude, j&#8217;utilise  <a href="http://mootools.net/" target="_blank">mootools </a> comme librairie javascript, elle va notamment nous servir à réaliser les appels asynchrone vers la NB4 et récupérer les résultat à afficher.</p>
<p>Regardons maintenant un peu le contenu des fichiers :</p>
<span id="gadget.xml"><h3>gadget.xml</h3></span>
<p>Pas grand chose à dire sur ce fichier, on doit y updader le numéro de version, pointer sur le bon fichier d&#8217;icône et le bon fichier html. Les tutos microsofts sont assez clairs sur les contenus possible de ce fichier.</p>
<pre>
<pre class="brush: xml;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
&lt;gadget&gt;
    &lt;name&gt;Neuf Box 4 DEV&lt;/name&gt;
    &lt;namespace&gt;neuf.fr&lt;/namespace&gt;
    &lt;version&gt;0.9.2&lt;/version&gt;
    &lt;author name=&quot;Thomas Chiroux&quot;&gt;
        &lt;info url=&quot;www.chiroux.com&quot; /&gt;
    &lt;/author&gt;
    &lt;copyright&gt;2007&lt;/copyright&gt;
    &lt;description&gt;Etat de la NeufBox&lt;/description&gt;
    &lt;icons&gt;
     &lt;icon height=&quot;128&quot; width=&quot;69&quot; src=&quot;icon_dev.png&quot; /&gt;
   &lt;/icons&gt;
    &lt;hosts&gt;
        &lt;host name=&quot;sidebar&quot;&gt;
            &lt;base type=&quot;HTML&quot; apiVersion=&quot;1.0.0&quot; src=&quot;NB4.html&quot; /&gt;
            &lt;permissions&gt;full&lt;/permissions&gt;
            &lt;platform minPlatformVersion=&quot;0.1&quot; /&gt;
        &lt;/host&gt;
    &lt;/hosts&gt;
&lt;/gadget&gt;
</pre>
</pre>
<span id="NB4.html"><h3>NB4.html</h3></span>
<p>C&#8217;est le fichier html qui est appelé par le moteur de gadget de vista et qui est rendu à l&#8217;écran. Il est lui aussi assez simple, car tout se fera dans le javascript.<br />
Dans le fichier html, il faut placer les div des éléments qu&#8217;on souhaite afficher, dans l&#8217;ordre qu&#8217;on souhaite :</p>
<pre>
<pre class="brush: xml;">
		&lt;div id=&quot;ADSLStatus&quot; class=&quot;NB4OnOff&quot;&gt; &lt;/div&gt;
        &lt;div id=&quot;PPPStatus&quot; class=&quot;NB4OnOff&quot;&gt; &lt;/div&gt;
        &lt;div id=&quot;WlanStatus&quot; class=&quot;NB4OnOff&quot;&gt; &lt;/div&gt;
        &lt;div id=&quot;HistoGraphSNR&quot;&gt;&lt;div id=&quot;SNRValue&quot;&gt;  &lt;/div&gt;&lt;/div&gt;
</pre>
</pre>
<p>Nous avons donc ici 4 éléments : 3 éléments de type &#8216;NB4OnOff&#8217; et un élément de type &#8216;Histo&#8217;.</p>
<span id="NB4.js"><h3>NB4.js</h3></span>
<p>C&#8217;est le fichier le plus important et c&#8217;est là que tout est fait aujourd&#8217;hui. Il est importé par NB4.html, juste après <a href="http://mootools.net/" target="_blank">mootools</a>.</p>
<p>Il est en gros composé de 2 parties :</p>
<ul>
<li>la définition des 2 classes (NB4OnOffClass et NB4HistoGraphClass) actuellement disponibles. Ces classes sont indépendantes de la structure &#8216;gadget&#8217; et pourraient être réutilisée dans d&#8217;autres contextes</li>
<li>le code javascript qui permet de gérer le gadget, notamment son initialisation</li>
</ul>
<p>Passons d&#8217;abord sur le code &#8217;spécial gadget&#8217; et sur l&#8217;initialisation. Les plus courageux pourront ensuite creuser le code des classes :</p>
<p>NB4.html, au chargement de la page, commence par lancer initGadget() :</p>
<p>initGadget() fait 2 choses :<br />
Initialiser la gestion des évènements lié à la mise en dock (la barre de gadget) et à l&#8217;opération inverse (le dé-dockage ?) :</p>
<pre>
<pre class="brush: jscript;">
function initGadget() {
    // variables globales
    if (!DEBUG_BOUCHON)
      gDockFlag = System.Gadget.docked;
    gAjaxWorking = 0;

    // Initialise les évènements de Dock et UnDock
    if (!DEBUG_BOUCHON) {
      System.Gadget.onUndock = procUndockEvent;
      System.Gadget.onDock = procDockEvent;
    }
</pre>
</pre>
<p>Donc ici, si le gadget revient dans sa barre, la fonction &#8216;procDockEvent&#8217; sera exécutée et si il en sort, c&#8217;est la fonction &#8216;procUndockEvent&#8217; qui le sera. Le principe et le fonctionnement de ces fonctions est assez bien décrit dans les exemples de microsoft. En gros elles s&#8217;occupent de redimentionner la fenêtre d&#8217;affichage pour que tout soit parfaitement affiché).</p>
<p>(ne vous préoccupez pas pour l&#8217;instant du &#8216;DEBUG-BOUCHON&#8217;, on en parlera dans la partie plus détaillée sur le développement)</p>
<p>Ensuite et c&#8217;est le plus important, cette fonction initalise les objets d&#8217;interrogations de la NB4 :</p>
<pre>
<pre class="brush: jscript;">
    objADSLStatus = new NB4OnOffClass('adsl_status', 'ADSLStatus', 'NB4_ADSL_On.png', 'NB4_ADSL_Off.png',5000);
    objPPPStatus = new NB4OnOffClass('ppp_status', 'PPPStatus', 'NB4_PPP_On.png', 'NB4_PPP_Off.png',5000);
    objWlanStatus = new NB4OnOffClass('wlan_active', 'WlanStatus', 'NB4_WIFI_On.png', 'NB4_WIFI_Off.png',5000);
    objSNRValue = new NB4HistoGraphClass('adsl_noise_down', 'SNRValue', 'SNR', 32, 'barre.gif', 10000);
</pre>
</pre>
<p>Pour le &#8217;simple&#8217; utilisateur, c&#8217;est tout, il n&#8217;y a rien d&#8217;autre à faire que de changer ces valeurs. (plus tard, dans une version ultérieure, il y aura des écrans de paramétrages pour éviter d&#8217;avoir à toucher le code).</p>
<p>Prenons la première ligne et détaillons les paramètres :</p>
<ul>
<li>le premier (&#8216;adsl_status&#8217;) est le nom du paramètre de la NB4. Attention il doit être exact, sinon la NB4 ne renvoie rien (plus exactement, elle renvoie &#8216;=&#8217;)</li>
<li>le second (&#8216;ADSLStatus&#8217;) est l&#8217;ID du &lt;div&gt; dans le code html qui va afficher le contenu</li>
<li>les troisième et quatrièmes (&#8216;NB4_ADSL_On.png&#8217;, &#8216;NB4_ADSL_Off.png&#8217;) sont les images à afficher (qui s&#8217;afficheront dans le div ci-dessus) lorsque les valeurs récupérées sont respectivement on ou off (ou up ou down, etc..)</li>
<li>le cinquième et dernier (5000) est la fréquence de rafraichissement (en ms) : ici il est paramétré à 5s, donc cet objet ira interroger la NB4 toute les 5 secondes.</li>
</ul>
<p>Donc si vous voulez interroger un autre paramètre de type &#8216;On/off&#8217; dans la NB4, il faut changer une de ces valeurs par une autre et si vous voulez le faire clean, il faut adapter le nom de l&#8217;objet et celui du div dans le code html en fonction du paramètre choisi.</p>
<span id="Les_deux_classes_de_monitoring"><h3>Les deux classes de monitoring</h3></span>
<p>Ces deux classes sont construites sur des principes communs :</p>
<ol>
<li>initalisation des paramètres</li>
<li>gestion d&#8217;un timer pour les interrogations régulières</li>
<li>appel de la fonction de la neufBox en AJAX (en utilisant les fonctions de mootools)</li>
<li>récupération et traitement du résultat</li>
<li>Mise à jour de l&#8217;affichage du gadget en fonction du résultat obtenu</li>
</ol>
<span id="NB4OnOffClass"><h3>NB4OnOffClass</h3></span>
<p>Cette classe est la plus simple, son objectif est de récupérer une valeur de type &#8216;on&#8217; ou &#8216;up&#8217;  ou encore &#8216;off&#8217; ou &#8216;down&#8217; venant de la NB4 et d&#8217;afficher l&#8217;image correspondante.</p>
<span id="L8217initialisation_:"><h4>L&#8217;initialisation :</h4></span>
<p>D&#8217;abord on va initaliser les variables de la classe avec des valeurs par défaut :</p>
<pre>
<pre class="brush: jscript;">
var NB4OnOffClass = new Class({
  c_parameterName : null,
  c_elementName : null,
  c_onOff : 0, // OFF par défaut
  c_oldOnOff : -1, // la valeur précédent du test onOff (au début -1 pour initialiser)
  c_imageON : null,
  c_imageOFF : null,
  c_time : 0,
  c_intervalTime : 0,
  c_timer : null,
  c_stoptimer : 0,
  c_urlNB4 : null,
</pre>
</pre>
<p>Voici ensuite le constructeur : il est appelé lors de l&#8217;instanciation de la classe :<br />
il va prendre les paramètres que l&#8217;on a choisi :</p>
<pre>
<pre class="brush: jscript;">initialize: function(parameterName, elementName, imageON, imageOFF, delais){  // Contructeur</pre>
</pre>
<p>, les stocker au sein de la classe :</p>
<pre>
<pre class="brush: jscript;">
this.c_parameterName = parameterName;
    this.c_elementName = elementName;
    this.c_imageON = 'images/' + imageON;
    this.c_imageOFF = 'images/' + imageOFF;
    this.c_intervalTime = delais; // durée de rafraichissement
    var date = new Date();
    this.c_time = date.getTime();
    if (DEBUG_BOUCHON)
      this.c_urlNB4 = DEBUG_URL;
     else
      this.c_urlNB4 = 'http://192.168.1.1/kit/get'; // ici on met la valeur en dur car elle sera a terme stockée dans les params du gadget
</pre>
</pre>
<p>et lancer une première mise à jour :</p>
<pre>
<pre class="brush: jscript;">
    // Aller chercher la valeur dans la NB4
    this.Update();
    }
</pre>
</pre>
<span id="update_des_donnes"><h4>update des données</h4></span>
<p>La fonction update fait 3 choses :</p>
<ul>
<li>Affiche la petite icône typique des appels ajax dans le coin du gadget</li>
<li>Remet à zéro le timer</li>
<li>Lance l&#8217;appel AJAX (en méthode POST, avec en paramètre la valeur cherchée et en forçant l&#8217;encodage à vide (en utf-8 la NB4 ne renvoie rien)</li>
</ul>
<p>Dans l&#8217;appel AJAX, nous avons définit la fonction de call back qui sera appelée lors du retour de la valeur (l&#8217;appel est asynchrone) : onComplete: this.callbackAjaxok.bind(this)<br />
C&#8217;est donc la fonction callbackAjaxok de cette classe qui sera lancée au retour de la NB4</p>
<pre>
<pre class="brush: jscript;">
    Update: function() {
      gAjaxWorking += 1;
      $('AjaxIndicator').style.display = 'block';
        var date = new Date();
      this.c_time= date.getTime();
    new Ajax(this.c_urlNB4,{data:'name='+this.c_parameterName, onComplete: this.callbackAjaxok.bind(this), method: 'post', encoding: ''}).request();
  },
</pre>
</pre>
<span id="Retour_des_infos_de_l8217IAD_:_callbackAjaxok"><h4>Retour des infos de l&#8217;IAD : callbackAjaxok</h4></span>
<p>Cette fonction est donc appelé au retour OK, je laisse mootols gérer les cas d&#8217;erreur : dans notre gadget si il y a des erreurs, rien ne sera affiché.</p>
<p>Dans l&#8217;ordre cette fonction fait les choses suivantes :</p>
<ul>
<li>stoppe l&#8217;affichage de la petit icône &#8216;appel ajax en cours&#8217;</li>
<li>récupère la valeur de retour et positionne le status de la classe en fonction de ce retour</li>
<li>si la valeur a changé depuis la dernière fois, réaffiche l&#8217;image dans le gadget en fonction de la nouvelle valeur</li>
<li>vérifie le temps écoulé et prépare le prochain update : soit immédiatement si l&#8217;intervalle de temps est déjà écoulée, soit plus tard (via la fonction delay) si le temps n&#8217;est pas encore écoulé. L&#8217;appel à update refait une boucle et ainsi de suite.</li>
</ul>
<pre>
<pre class="brush: jscript;">
  callbackAjaxok: function(obj) {
    // ici récupère la valeur du paramètre on/off et le stocke dans c_onOff
    gAjaxWorking -= 1;
    if (gAjaxWorking==0) {
         $('AjaxIndicator').style.display = 'none';
      }

    if ($type(obj)=='string') {
      var lvalue = obj.split('=')[1].clean().toLowerCase();
      if (lvalue=='up' || lvalue=='on') {
        this.c_onOff=1;
      } else {
        this.c_onOff=0;
      }
      if (this.c_oldOnOff != this.c_onOff) {
          this.GetHTMLOutput();
      }
      this.c_oldOnOff = this.c_onOff;
    }
    if (!this.c_stoptimer) {
      var date = new Date();
      var currentIntervalTime = date.getTime() - this.c_time;
      if ( currentIntervalTime &lt; this.c_intervalTime) { // on a encore du temps : on attends
        this.c_timer = this.Update.delay(this.c_intervalTime-currentIntervalTime, this); // lance update au bout du délais restant
      } else { // on a dépassé l'heure : on repart directement pour un tour
        this.Update();
      }
    }
  }
</pre>
</pre>
<span id="l8217affichage_:_GetHTMLOutput"><h4>l&#8217;affichage : GetHTMLOutput</h4></span>
<p>Cette fonction prépare simplement le bon tag &lt;img&gt; en fonction de la valeur on ou off et l&#8217;affiche dans le div concerné :</p>
<pre>
<pre class="brush: jscript;">
  GetHTMLOutput: function() {
    // Affiche l'image dans le div de destination
    var lHTMLoutput = '&lt;img src=&quot;'; // &lt;p style=&quot;writing-mode: tb-rl; filter: flipH() flipV();&quot;&gt;TEST&lt;/p&gt;
    if (this.c_onOff==0) {
      lHTMLoutput += this.c_imageOFF;
    } else {
      lHTMLoutput += this.c_imageON;
    }
    lHTMLoutput += '&quot;/&gt;';

    $(this.c_elementName).innerHTML=lHTMLoutput;
  }
</pre>
</pre>
<span id="NB4HistoGraphClass"><h4>NB4HistoGraphClass</h4></span>
<p>Cette classe a pour but d&#8217;afficher une évolution dans le temps d&#8217;une valeur numérique. A chaque update, nous allons afficher une valeur supplémentaire (une barre de 1 pixel représentant la valeur plus précisément), à la manière du monitoring CPU et mémoire du perfmon de windows.</p>
<p>Le fonctionnement global de la classe est exactement le même que la NB4OnOff, je ne reviens donc pas sur les fonctions d&#8217;initialistion et d&#8217;update</p>
<p>Il y a un point important à noter dans callBackAjaxOk : au lieu d&#8217;écraser la valeur précédente comme c&#8217;est le cas dans la classe OnOff, ici on ajoute la valeur reçue à un tableau de valeur :</p>
<pre>
<pre class="brush: jscript;">this.c_histo.extend([lvalue]);</pre>
</pre>
<p>Toutefois, la grosse différence se situe dans le code de rendu html : GetHTMLOutput :</p>
<ul>
<li>D&#8217;abord on récupère la taille de la zone d&#8217;affichage (elle peut varier en fonction notamment du mode docké ou non docké)</li>
<li>Et on en déduit le nombre de barres verticales affichables (a noter que pour l&#8217;instant c_imgwidth est un paramètre fixé une fois pour toute)</li>
<li>Puis enfin on réduit le tableau des valeurs à ce nombre :</li>
</ul>
<pre>
<pre class="brush: jscript;">
	// Récupère la taille actuelle du div
   var divHeight = $(this.c_elementName).getStyle('height').toInt();
   var divWidth = $(this.c_elementName).getStyle('width').toInt();

   // nombre max de barres affichables (division entière)
   var maxBarres = (divWidth - (divWidth % this.c_imgwidth)) / this.c_imgwidth;
   // réduit le tableau avec les dernières valeurs affichables
   if (this.c_histo.length&gt;maxBarres) {
     this.c_histo = this.c_histo.slice(this.c_histo.length - maxBarres);
   }
</pre>
</pre>
<ul>
<li>Puis si on a définit une hauteur dynamique au graphe, on va chercher quelle est la plus grande valeur du tableau afin de positionner l&#8217;échelle. Sinon on positionne l&#8217;échelle à une valeur fixe</li>
</ul>
<pre>
<pre class="brush: jscript;">
    if (this.c_maxHeightSize==0) { // taille dynamique
      var valeurMax= 0;
      this.c_histo.each( function(val) {
        if (val &gt;= valeurMax) {
          valeurMax = val;
        }
      });
    } else {  // taille fixe
      valeurMax = this.c_maxHeightSize;
    }
</pre>
</pre>
<ul>
<li>Enfin on parcoure le tableau de valeur en créant à chaque fois une image verticale dont la hauteur correspond à la valeur chiffrée du tableau et on l&#8217;affiche ainsi que le texte du paramètre de la NB4 qui est monitoré et la dernière valeur chiffrée :</li>
</ul>
<pre>
<pre class="brush: jscript;">
    var derniereValeur = 0;
    // affiche le graph
    for(var i=0;i&lt;this.c_histo.length;i++) {
      lHTMLoutput += '&lt;img src=&quot;';
      lHTMLoutput += this.c_imageHisto; //this.c_imageHisto;
      lHTMLoutput += '&quot;';
      lHTMLoutput += ' style=&quot;position: absolute; bottom: 0px; left:'+ i + 'px; width:'+ this.c_imgwidth + '; height:' + this.c_histo[i]*(divHeight/valeurMax) + ';&quot;';
      lHTMLoutput += '/&gt;';
      derniereValeur = this.c_histo[i];
    }
    $(this.c_elementName).innerHTML=lHTMLoutput;
</pre>
</pre>
<span id="Telechargement_et_Installation"><h1><a name="Telechargement"></a>Telechargement et Installation</h1></span>
<p>Deux versions sont téléchargeables : une de devt et une &#8216;normale&#8217; (on ne peut pas l&#8217;appeler &#8216;de production&#8217; à partir du moment où ce gadget n&#8217;est pas encore terminé).<br />
Le but est de montrer comme serait le package &#8216;minimal&#8217; d&#8217;un gadget NB4. J&#8217;ai donc enlevé dans la version &#8216;normale&#8217; tous les fichiers non nécessaires.</p>
<p>Pour l&#8217;installer, il suffit de télécharger l&#8217;archive, de le dézipper et de double cliquer sur le gadget, il devrait s&#8217;installer tout seul. Ensuite, normalement il devrait se trouver dans le répertoire windows suivant : Bureau\&lt;votre nom de user&gt;\AppData\Local\Microsoft\Windows Sidebar\Gadgets</p>
<p>La seconde archive (de développement) intégre un mootools non compressé (pratique pour le debug) ainsi que deux fichier supplémentaires et une icône de développement :</p>
<ul>
<li>testFormPost.html qui est une simple page pour interroger la NB4 en POST et récupérer le resultat, pratique pour tester des paramètres et ce qu&#8217;ils retournent</li>
<li>bouchon.php : permet de simuler la NB4 :
<ul>
<li>paramétrez DEBUG_BOUCHON à true dans NB4.js</li>
<li>hébergez le repertoire du gadget sur un serveur php et appelez directement la page NB4.html depuis un firefox équipé de firebug</li>
<li>Cela permet de faire du débug pas à pas, ce qui est pratique dans certains cas quand on ne comprend pas ce qu&#8217;il se passe : un des gros problème du développement de gadgets est la difficulté de debug car le browser embarqué dans windows sidebar n&#8217;est pas du tout accessible ni modifiable.</li>
</ul>
</ul>
Note: There is a file embedded within this post, please visit this post to download the file.
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/un-gadget-vista-pour-la-neufbox-4/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Reproduire les jolis câbles de yahoo pipes</title>
		<link>http://www.chiroux.com/reproduire-les-jolis-cables-de-yahoo-pipes/</link>
		<comments>http://www.chiroux.com/reproduire-les-jolis-cables-de-yahoo-pipes/#comments</comments>
		<pubDate>Mon, 25 Jun 2007 13:38:22 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[mootools]]></category>
		<category><![CDATA[yahoo]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/article-1182778702.html</guid>
		<description><![CDATA[<p>Depuis que j&#8217;ai découvert Yahoo Pipes, je me demandais comment ils avaient pu faire une interface aussi chouette en html. Ce qui me posait le plus de pb c&#8217;était bien sur les jolis câbles de liaisons entre les boites, d&#8217;autant que je n&#8217;avait aucune idée de comment on pouvait faire sa en html. J&#8217;ai donc [...]]]></description>
			<content:encoded><![CDATA[<p>Depuis que j&#8217;ai découvert Yahoo Pipes, je me demandais comment ils avaient pu faire une interface aussi chouette en html. Ce qui me posait le plus de pb c&#8217;était bien sur les jolis câbles de liaisons entre les boites, d&#8217;autant que je n&#8217;avait aucune idée de comment on pouvait faire sa en html. J&#8217;ai donc décidé de m&#8217;y mettre, de comprendre et de le refaire.<span id="more-25"></span><br />
Pour commencer, j&#8217;ai pris mon firebug et j&#8217;ai exploré (inspecté !) les objets affichés pour trouver de quoi était fait le fameux lien.<br />
En fait tout est là : ce câble est un <strong>&lt;canvas&gt;</strong></p>
<span id="Mais_qu8217est-ce_donc_qu8217un_canvas_"><h2>Mais qu&#8217;est-ce donc qu&#8217;un canvas ?</h2></span>
<p>Un canvas est un nouveau élément HTML qui permet de manipuler des graphiques en bitmap. Le canvas est intégré au draft du HTML 5 et a été originellement proposé par apple et son safari. Il est supporté nativement par firefox depuis la 1.5 et n&#8217;est pas encore supporté par IE (mais il y a moyen de le faire marcher sous IE, on en parlera plus bas)</p>
<p>Je ne vais pas refaire le tuto de canvas, vous pouvez suivre ce tuto sur le site de mozilla, il est très bien fait.</p>
<span id="Les_8216wires8217_de_yahoo"><h2>Les &#8216;wires&#8217; de yahoo</h2></span>
<p>Mon but ici est de refaire le système de câbles entre deux boites comme dans yahoo pipes.</p>
<p>J&#8217;ai choisit d&#8217;utiliser mootools comme librairie javascript, parce que j&#8217;aime bien mootools. Cela permet de ne pas perdre de temps sur la gestion des box et des drag&amp;drop.</p>
<p>Donc j&#8217;ai simplement commencé par définir un bout d&#8217;html avec un container, deux boites et le fameux canvas :</p>
<pre>
<pre class="brush: xml;">&amp;lt;body  onload = &amp;quot;start();&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;containment&amp;quot;&amp;gt;
  &amp;lt;canvas id=&amp;quot;lien&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;
  &amp;lt;div id=&amp;quot;boite1&amp;quot;&amp;gt;
  1
  &amp;lt;/div&amp;gt;
  &amp;lt;div id=&amp;quot;boite2&amp;quot;&amp;gt;
  2
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;</pre>
</pre>
<p>le css qui va avec (rien de compliqué):</p>
<pre>
<pre class="brush: css;">&amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;
 #containment {
 height: 500px;
 width: 600px;
 background: #ddd;
 position: relative;
 }
#boite1 {
 height: 60px;
 width: 40px;
 border: 2px solid black;
 background: #39f;
 color: white;
 position: absolute;
 top: 50;
 left: 50;
 }

#boite2 {
 height: 55px;
 width: 40px;
 border: 2px solid black;
 background: blue;
 color: white;
 position: absolute;
 top: 250;
 left: 250;
 }

canvas {
 /*border: 1px solid red;*/
 position: absolute;
 top: 0;
 left: 0;
 }
 &amp;lt;/style&amp;gt;</pre>
</pre>
<p>Ensuite un peu de code mootools pour rendre les boites draggables :</p>
<pre>
<pre class="brush: jscript;">function start() {
 var container = $('containment');
new Drag.Move('boite1', {'container': container});
 new Drag.Move('boite2', {'container': container});

$('boite1').addEvent('mousemove', function() {
 replaceLink();
 });
 $('boite2').addEvent('mousemove', function() {
 replaceLink();
 });
 replaceLink();
 }</pre>
</pre>
<p>Donc tout sera fait dans la fonction &#8216;replaceLink&#8217;<br />
Cette fonction est en fait relativement simple :<br />
Elle fait (dans l&#8217;ordre) :<br />
- calculer les coordonnée du point de départ et du point d&#8217;arrivée du trait : dans cet exemple le point de départ est collé au bas de la boite 1 et le point d&#8217;arrivée en haut de la boite 2.</p>
<pre>
<pre class="brush: jscript;">
pointA_x = $('boite1').getStyle('left').toInt()+($('boite1').getStyle('width').toInt())/2;
pointA_y = $('boite1').getStyle('top').toInt()+($('boite1').getStyle('height').toInt());
pointB_x = $('boite2').getStyle('left').toInt()+($('boite2').getStyle('width').toInt())/2;
pointB_y = $('boite2').getStyle('top').toInt(); //+($('boite2').getStyle('height').toInt())/2;
</pre>
</pre>
<p>- Traiter correctement le point de départ et d&#8217;arrivée en fonction de la position relative des boites l&#8217;une par rapport à l&#8217;autre (notamment quand la boite 2 se retrouve au dessus de la 1, etc..)</p>
<pre>
<pre class="brush: jscript;"> pointA_x = $('boite1').getStyle('left').toInt()+($('boite1').getStyle('width').toInt())/2;
 pointA_y = $('boite1').getStyle('top').toInt()+($('boite1').getStyle('height').toInt());
 pointB_x = $('boite2').getStyle('left').toInt()+($('boite2').getStyle('width').toInt())/2;
 pointB_y = $('boite2').getStyle('top').toInt(); //+($('boite2').getStyle('height').toInt())/2;</pre>
</pre>
<p>- Préparer le contexte</p>
<pre>
<pre class="brush: jscript;"> lien_width = Math.abs(pointB_x - pointA_x);
 lien_x = Math.min(pointA_x, pointB_x);
 lien_height = Math.abs(pointB_y - pointA_y);
 lien_y = Math.min(pointA_y,pointB_y);</pre>
</pre>
<p>- Effacer l&#8217;image précédente</p>
<pre>
<pre class="brush: jscript;">ctx.clearRect(0,0,600,500);</pre>
</pre>
<p>- Dessiner un trait qui part bien du point de départ et qui arrive bien au point d&#8217;arrivée : ceci est fait en utilisant les fonctions &#8216;basiques&#8217; du canvas : MoveTo et soit LineTo soit bezierCurveTo si on veut un trait tout arrondi.</p>
<p>Donc en fait c&#8217;est très simple.<br />
Là où on passe un peu de temps c&#8217;est vraiment le rendre joli.</p>
<p>Au début, j&#8217;ai voulu adapter la taille du &lt;canvas&gt; au rectangle dont les coins sont formés par le point de départ du trait et le point d&#8217;arrivée (c&#8217;est ce qui me paraissait le plus logique). Cela fonctionne bien, me permet de travailler avec toujours le même système de coordonnées, mais le pb est que mon trait était alors rescallé automatiquement à la nouvelle taille du canvas (ce qui semble être un comportement standard). [<a href="http://www.chiroux.com/dev/testcanvas/testcanvas_bad.html" target="_blank">cliquez ici pour voir ce que cela donnait en live</a> : éloignez les boites pour le remarquer. (attention, cette version ne marche pas bien sous IE)]<br />
Yahoo semble pourtant utiliser cette technique, mais je n&#8217;ai pas réussit à la faire fonctionner, j&#8217;ai donc changé d&#8217;angle d&#8217;approche : j&#8217;ai arrêté de resizer mon canvas, je l&#8217;ai directement initalisé à la taille du conteneur et ensuite j&#8217;ai joué avec la translation :</p>
<pre>
<pre class="brush: jscript;"> ctx.save();
 ctx.translate(lien_x, lien_y);</pre>
</pre>
<p>Cela me permet d&#8217;avoir toujours le point (0,0) en haut à gauche du rectangle formé par le point de départ et le point d&#8217;arrivée.</p>
<p>Voici deux cas de figure en schéma :<br />
<img class="size-full wp-image-90 alignnone" title="canvas_expl1" src="http://www.chiroux.com/wp-content/uploads/Import/Dev/canvas_expl1.png" alt="canvas_expl1" width="570" height="434" /></p>
<p><img class="alignnone size-full wp-image-91" title="canvas_expl2" src="http://www.chiroux.com/wp-content/uploads/Import/Dev/canvas_expl2.png" alt="canvas_expl2" width="417" height="338" /><br />
- Ensuite il ne reste plus qu&#8217;à dessiner :<br />
la suite de la fonction est simplement là pour gérer les 4 cas de figure de la position relative de la boîte 1 par rapport à la boite 2. Cela ne sert finalement qu&#8217;à donner des bons paramètres à la courbe de bézier pour que cela soit joli.<br />
Il y a un autre élément où j&#8217;ai eu un peu de mal c&#8217;est pour faire en sorte que le trait ait un bord (plus foncé que l&#8217;intérieur dans l&#8217;exemple). J&#8217;ai essayé de jouer avec les propriétés border, mais c&#8217;est en fait beaucoup plus simple :<br />
il faut faire un trait foncé plus gros, puis un trait plus clair et plus fin qui va se superposer, tout simplement.<br />
Cette partie du code est optimisable (on peut la rendre plus compacte), mais je l&#8217;ai volontairement laissé tel quel pour gagner en lisibilité.</p>
<pre>
<pre class="brush: jscript;">ctx.beginPath(); m_a = 100; // offset pour les beziers
 if (pointA_x &amp;amp;lt; pointB_x) {
 if (pointA_y &amp;amp;lt; pointB_y) {
 // coin en haut à gauche
 ctx.lineWidth = 12;
 ctx.strokeStyle = '#07c';
 ctx.moveTo(0, 0);
 ctx.bezierCurveTo(0,m_a, lien_width, lien_height-m_a, lien_width, lien_height);
 ctx.stroke();
 ctx.lineWidth = 8;
 ctx.strokeStyle = '#09f';
 ctx.moveTo(0, 0);
 ctx.bezierCurveTo(0,m_a, lien_width, lien_height-m_a, lien_width, lien_height);
 ctx.stroke();
 } else {
 // coin en bas à gauche
 ctx.lineWidth = 12;
 ctx.strokeStyle = '#07c';
 ctx.moveTo(lien_width, 0);
 ctx.bezierCurveTo(lien_width, -m_a, 0, lien_height+m_a, 0, lien_height);
 ctx.stroke();
 ctx.lineWidth = 8;
 ctx.strokeStyle = '#09f';
 ctx.moveTo(lien_width, 0);
 ctx.bezierCurveTo(lien_width, -m_a, 0, lien_height+m_a, 0, lien_height);
 ctx.stroke();
 }
 } else {
 if (pointA_y &amp;amp;lt; pointB_y) {
 // coin en haut à droite
 ctx.lineWidth = 12;
 ctx.strokeStyle = '#07c';
 ctx.moveTo(lien_width, 0);
 ctx.bezierCurveTo(lien_width, m_a, 0, lien_height-m_a, 0, lien_height);
 ctx.stroke();
 ctx.lineWidth = 8;
 ctx.strokeStyle = '#09f';
 ctx.moveTo(lien_width, 0);
 ctx.bezierCurveTo(lien_width, m_a, 0, lien_height-m_a, 0, lien_height);
 ctx.stroke();
 } else {
 // coin en bas à droite
 ctx.lineWidth = 12;
 ctx.strokeStyle = '#07c';
 ctx.moveTo(0, 0);
 ctx.bezierCurveTo(0,-m_a, lien_width, lien_height+m_a, lien_width, lien_height);
 ctx.stroke();
 ctx.lineWidth = 8;
 ctx.strokeStyle = '#09f';
 ctx.moveTo(0, 0);
 ctx.bezierCurveTo(0,-m_a, lien_width, lien_height+m_a, lien_width, lien_height);
 ctx.stroke();
 }
 }
 ctx.restore();</pre>
</pre>
<span id="Fonctionnement_avec_IE"><h2>Fonctionnement avec IE</h2></span>
<p>IE ne supporte pas nativement les canvas (même pas IE7). Pour contourner ce pb, <a href="http://excanvas.sourceforge.net/" target="_blank">des gars de chez google ont eu l&#8217;excellente idée et le talent d&#8217;utiliser le support natif de VML dans IE pour émuler les canvas</a>. Il suffit d&#8217;inclure cette librairie pour faire fonctionner canvas sous IE, pourquoi s&#8217;en priver&#8230;</p>
<span id="Amliorations_et_Optimisations"><h2>Améliorations et Optimisations</h2></span>
<p>Je ne suis pas complètement satisfait des perfomances de cette démo, notamment quand on bouge la souris très vite. Yahoo n&#8217;a pas ce pb.<br />
A première vue, je supposerait que c&#8217;est du au clearRect que je fait pour effacer les dessins précédent, mais je n&#8217;ai pas trouvé d&#8217;autre solution pour l&#8217;instant. Si vous avez des idées, n&#8217;hésitez pas à les proposer en commentant cet article.</p>
<span id="Dmo_et_tlchargement"><h2>Démo et téléchargement</h2></span>
<ul>
<li><a href="http://www.chiroux.com/dev/testcanvas/testcanvas_2.html" target="_blank">Cliquez ici pour accéder à une démo live de ce code</a></li>
</ul>
<p>Ci-dessous pour télécharger le sources (incluant mootools 1.11 et Exanvas 0002) :</p>
Note: There is a file embedded within this post, please visit this post to download the file.
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/reproduire-les-jolis-cables-de-yahoo-pipes/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Remplacer mon tablet PC, oui mais par lequel ?</title>
		<link>http://www.chiroux.com/remplacer-mon-tablet-pc-oui-mais-par-lequel/</link>
		<comments>http://www.chiroux.com/remplacer-mon-tablet-pc-oui-mais-par-lequel/#comments</comments>
		<pubDate>Wed, 13 Jun 2007 23:03:00 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[Obsolete]]></category>
		<category><![CDATA[gadget]]></category>
		<category><![CDATA[tablet]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/article-1181775780.html</guid>
		<description><![CDATA[<p> Je commence sérieusement à envisager le remplacement de mon vieillissant Toshiba M200, mais aucun des produits actuellement disponible ne m&#8217;excite vraiment.
En attendant, je vais essayer de décrire mon futur tablet PC idéal (tout en essayant d&#8217;être un peu réaliste : je ne me contraint pas sur le prix, mais sur des technos existantes ou [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-medium wp-image-87" title="padd" src="http://www.chiroux.com/wp-content/uploads/Import/Blog/padd-300x168.gif" alt="padd" width="265" height="148" /> Je commence sérieusement à envisager le remplacement de mon vieillissant Toshiba M200, mais aucun des produits actuellement disponible ne m&#8217;excite vraiment.<br />
En attendant, je vais essayer de décrire mon futur tablet PC idéal (tout en essayant d&#8217;être un peu réaliste : je ne me contraint pas sur le prix, mais sur des technos existantes ou arrivant tout juste&#8230;)</p>
<p><span id="more-27"></span></p>
<p>Je vais essayer de classer dans l&#8217;ordre ce qui est le plus important pour moi et mon usage.</p>
<p>J&#8217;utilise mon tablet au bureau, constamment. Il remplace avantageusement mon bloc note papier depuis quelques années.<br />
<br/><br/></p>
<ul>
<li><strong>hybride ou sans clavier / form factor </strong><strong>? </strong>
<ul>
<li> Cest la première question que je me pose. Mon expérience du M200 me fait dire que le clavier cest quand même vraiment pratique&#8230; mais il reste inutile dans environ 90% de mon temps dutilisation. En mode bloc-note, finalement il ne me sert quà mettre mes mots de passe pour délocker windows et pour mounter mon volume crypté (voir partie sécurité pour ce point).</li>
<li> J&#8217;opterais donc pour un tablet SANS clavier afin de gagner en place et épaisseur, mais j&#8217;achèterais en plus un petit clavier bluetooth et/ou USB</li>
</ul>
</li>
<li><strong>l&#8217;écran</strong>
<ul>
<li> La résolution élevée du M200 (1400&#215;1050 sur un 12) me va parfaitement. J&#8217;ai eu l&#8217;occasion de tester et d&#8217;utiliser d&#8217;autres tablets en 1024&#215;768 et je trouve que c&#8217;est trop faible. Depuis par contre, on voit arriver des écrans pour portables en 16/9 ou 16/10 genre 1280&#215;800. J&#8217;ai peur que ce soit aussi un peu trop faible, mais mettons la limite à cette valeur.</li>
<li> Conclusion : entre 12&nbsp;&raquo; et 13&nbsp;&raquo;, mini 1280&#215;800, idéal 1680&#215;1050, ok en 1400&#215;1050.</li>
</ul>
</li>
<li><strong>le processeur</strong>
<ul>
<li> un core2 un peu puissant et low power comme le Santa Rosa serait parfait. Pas besoin a priori dune puissance absolue.</li>
</ul>
</li>
<li><strong>le disque dur</strong>
<ul>
<li> Bon là, je n&#8217;ai pas à réfléchir longtemps : SSD !</li>
<li> Je n&#8217;ai pas besoin d&#8217;une place folle, un SSD de 32Go serait suffisant, même si un 64Go ou un peu plus serait appréciable. Mais il est clair que pour des raisons d&#8217;autonomie, de perf et de solidité je préfère un SSD 32Go à un 2,5 ou 1,8 de 100Go ou plus.</li>
</ul>
</li>
<li><strong>RAM</strong>
<ul>
<li> plein de RAM, surtout avec Vista, au moins 2gig, 4gig si possible, mais alors en Vista 64bits</li>
</ul>
</li>
<li><strong>batterie/autonomie/alimentation</strong>
<ul>
<li> L&#8217;idéal serait d&#8217;avoir entre 8h et 10h de VRAIE autonomie en fonctionnement, en ayant activé les modes déconomies. Cela me permettrait de me promener en réunion uniquement avec le tablet et non avec tout l&#8217;attirail de câbles d&#8217;alimentations.</li>
</ul>
</li>
<li><strong>Sécurité </strong>
<ul>
<li> Comme je l&#8217;indiquait plus haut, les moments où j&#8217;ai besoin du clavier sont principalement pour inscrire mes mots de passe. Donc il me parrait vraiment utile d&#8217;avoir un lecteur d&#8217;empreintes, mais couplé à un vrai bon soft qui me permette d&#8217;attribuer des mots de passes à dautres applications, de manière vraiment sécurisée. J&#8217;utilise notamment Truecrypt (<a href="http://www.truecrypt.org/" target="_blank">http://www.truecrypt.org</a>) pour mes données avec de belles passphrases. Je sais bien qu&#8217;utiliser le lecteur d&#8217;empreinte baissera mon niveau de sécurité, mais je vais gagner beaucoup en usabilité.</li>
</ul>
</li>
<li><strong>carte Video</strong>
<ul>
<li> Le sujet prête à réflexion : je joue pas mal sur PC, mais en fait jamais avec mon tablet. J&#8217;aurais donc tendance à dire qu&#8217;une petite carte video, capable de décoder et d&#8217;afficher des films video en full screen/full résolution ainsi que faire tourner vista avec Aero est suffisante (pas besoin de fonction 3D avancée). Mais pour des raisons d&#8217;autonomie, il est peut-être souhaitable de ne même pas activer Aero en mode batterie.</li>
</ul>
</li>
<li><strong> autres fonctions et accessoires que je souhaiterais avoir dans ce Tablet :</strong>
<ul>
<li> Tablet en techno wacom (of course !)</li>
<li> wifi N</li>
<li> bluetooth</li>
<li> USB 2.0 : au moins 2, mais 4 serait mieux</li>
<li> un ou des boutons hardware pour activer/désactiver wifi et bluetooth</li>
</ul>
</li>
<li><strong> Ce que je ne veux pas particulièrement :</strong>
<ul>
<li> lecteur ou graveur DVD : je vis très bien sans avec mon M200 et comme cela permet de gagner place et poid, je préfère donc un tablet sans.</li>
</ul>
</li>
</ul>
<p>Bon c&#8217;est a peu près tout. Si vous voyez mon graal dans les magasins ou en annonce sur le net, n&#8217;hésitez pas à me prévenir ici !!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/remplacer-mon-tablet-pc-oui-mais-par-lequel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ultimate Tag Cloud : un nuage de tags pour MODx</title>
		<link>http://www.chiroux.com/utagcloud/</link>
		<comments>http://www.chiroux.com/utagcloud/#comments</comments>
		<pubDate>Sun, 07 Jan 2007 17:59:52 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Obsolete]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[modx]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/article-1168192792.html</guid>
		<description><![CDATA[<p>Depuis que j&#8217;ai basculé sur Modx, j&#8217;avais envie de m&#8217;amuser avec les tags. J&#8217;ai d&#8217;abord utilisé directement le snippet tagcloud, puis ce week-end je me suis dit qu&#8217;on pouvait faire mieux, beaucoup mieux, à l&#8217;image de zoomclouds par exemple.</p>
<p>J&#8217;utilise sur ce site depuis le début le snippet TagCloud, et j&#8217;ai décidé de l&#8217;améliorer fortement.
Voici quelques [...]]]></description>
			<content:encoded><![CDATA[<p>Depuis que j&#8217;ai basculé sur Modx, j&#8217;avais envie de m&#8217;amuser avec les tags. J&#8217;ai d&#8217;abord utilisé directement le snippet tagcloud, puis ce week-end je me suis dit qu&#8217;on pouvait faire mieux, beaucoup mieux, à l&#8217;image de zoomclouds par exemple.</p>
<p>J&#8217;utilise sur ce site depuis le début le snippet TagCloud, et j&#8217;ai décidé de l&#8217;améliorer fortement.<br />
Voici quelques unes de ces fonctionnalités :</p>
<ul>
<li>Construction automatique, sans besoin de préciser des tags pour chaque page (compte les mots et les range par quantité)</li>
<li>Recherche récursive</li>
<li>Exclusion de mots &#8216;courants&#8217; via des fichiers de langues (pour l&#8217;instant seul un fichier français a été réalisé)</li>
<li>Exclusion des caractères de contrôles via des fichiers de langues</li>
<li>Detection automatique des pluriels courants des mots et &#8216;fusion&#8217; des mots singuliers et pluriels dans un même tag (tout au moins fonctionne en français)</li>
<li>Le rendu est paramétrable par templates (chunks ou fichiers) et css</li>
<li>Plusieurs tagClouds différents sont possibles sur une même page</li>
<li>Colorisation des mots, possibilité de nombre de couleurs différentes potentiellement illimité (nécéssite autant de configuration des css)</li>
<li>Possibilité de paramétrer lors du lancement un certain nombre d&#8217;éléments consititutif du nuage :
<ul>
<li>nombre d&#8217;occurences minimales des mots à afficher</li>
<li>nombre de lettre minimales pour un mot</li>
<li>nombre total de mots maximum dans le nuage</li>
<li>taille minimale de la police</li>
<li>taille maximale de la police</li>
</ul>
</li>
</ul>
<p><span id="more-28"></span></p>
<span id="Installation"><h2>Installation</h2></span>
<p>L&#8217;installation est classique pour un snippet modx :</p>
<ul>
<li>créez un répertoire UtagCloud dans le répertoire assets/snippets/ de votre site</li>
<li>copiez le contenu de l&#8217;archive dans ce UtagCloud</li>
<li>dans modx, créez un snippet nommé &#8216;UtagCloud&#8217;</li>
<li>copiez le contenu du fichier UtagCloud_snippet.php.txt dans le contenu du snippet</li>
<li>Ensuite dans une page, invoquez le snippet comme vous avez l&#8217;habitude de le faire (voir les exemples plus bas)</li>
</ul>
<span id="Paramtres"><h2>Paramètres</h2></span>
<p>Voici l&#8217;explication des paramètres en français. Pour une explication en anglais, veuillez vous référer à l&#8217;entête du fichier UtagCloud_snippet.php.txt</p>
<ul>
<li><strong>&amp;parent</strong> =    liste des répertoires modx contenant des documents, séparés par une virgule. Si vous utilisez le paramètre récusif ci-après, vous pouvez mettre `0` dans parent pour parcourir tout le site.</li>
<li><strong>&amp;recursive</strong> =  [ 0 | 1 ] : Détermine si UtagCloud scanne les répertoires et documents modx récursivement ou non.</li>
<li><strong>&amp;min</strong> =      [ number ] : Nombre minimum d&#8217;occurence d&#8217;un mot afin qu&#8217;il soit affiché dans le nuage.</li>
<li><strong>&amp;landing</strong> =  [ number ] : l&#8217;ID de page de résultat de la recherche : chaque clic sur un mot du tag générera une recherche (FlexSearch) et aboutira sur cette page de résultat. Si vous n&#8217;avez pas de page de résultat, créez en une avec à l&#8217;intérieur :</li>
<li><strong>&amp;minsize</strong> =  [ number ] : Nombre de lettres minimale d&#8217;un mot pour qu&#8217;il soit affiché dans le nuage.</li>
<li><strong>&amp;wordsmax</strong> = [ number ] : Nombre maximum de mots du nuage. Indiquez zéro (`0`) pour un nombre illimité.</li>
<li><strong>&amp;fontmin</strong> =  [ number ] : Taille minimum de la police de caractère en % (pour les mots avec la plus petite occurence).</li>
<li><strong>&amp;fontmax</strong> =  [ number ] : Taille maximum de la police de caractère en % (pour les mots avec la plus grande occurence).</li>
<li><strong>&amp;lang </strong>=     [ text ] : Paramètre de langue pour lire les fichiers d&#8217;exclusion.</li>
<li><strong>&amp;template</strong> =   [ text ] : nom d&#8217;un Chunk ou d&#8217;un fichier dans templates/ qui sera utilisé pour affiche le contenu du nuage.</li>
<li><strong>&amp;css</strong> =        [ text ] : nom du fichier css qui sera inclus au document. Pratique si vous utilisez styleid, pour séparer les styles différents de vos nuages.</li>
<li><strong>&amp;styleid</strong> =    [ text | number ] : nouvelle instance de style (css) pour votre nuage. Permet d&#8217;avoir plusieurs nuages de style différents sur une même page.</li>
<li><strong>&amp;colors</strong> =     [ number ] : nombre de couleurs différentes pour les mots du nuage.</li>
<li><strong>&amp;plural_letter</strong> = [ text ] : définit la lettre qui sert à mettre au pluriel les mots de façon la plus courante (en français c&#8217;est le &#8217;s&#8217;). Permet de regrouper les mots singuliers et pluriels en un seul mot pour tout compter d&#8217;un coup. Si vous ne souhaitez pas utiliser cette fonctionnalité, définisez `#` comme valeur à ce paramètre.</li>
</ul>
<span id="Styles_d8217affichage_css"><h2>Styles d&#8217;affichage (css)</h2></span>
<p>UtagCloud utilise principalement deux classes de style, mais qui sont déclinable en fonction du styleid et du nombre de couleurs définies :</p>
<ul>
<li>.Utagcloud_XXX : où XXX est le styleid (par défaut c&#8217;est `0`)</li>
<li>.Utagcloudtext_XXX_CCC : où XXX est toujours le styleid et où CCC est la couleurID</li>
</ul>
<p>par exemple, si nous avons paramétré 5 couleurs, et que le styleid est celui par défaut, il faudra définir dans la css les styles suivants :</p>
<ul>
<li> .UtagCloud_0 {} : pour le div contenant le nuage.</li>
<li> .UtagCloudtext_0_1 {} : pour le texte de couleur numéro 1</li>
<li> .UtagCloudtext_0_2 {} : pour le texte de couleur numéro 2</li>
<li> .UtagCloudtext_0_3 {} : pour le texte de couleur numéro 3</li>
<li> .UtagCloudtext_0_4 {} : pour le texte de couleur numéro 4</li>
<li> .UtagCloudtext_0_5 {} : pour le texte de couleur numéro 5</li>
</ul>
<p>Ensuite, tout dépend du template et de ce que vous avez envie de faire. Vous pouvez par exemple avoir envie de paramétrer les :hover afin de changer le comportement quand on passe la souris.</p>
<span id="Tlcharger_l8217archive"><h2>Télécharger l&#8217;archive</h2></span>
Note: There is a file embedded within this post, please visit this post to download the file.
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/utagcloud/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Vérifier ses sauvegardes en python</title>
		<link>http://www.chiroux.com/dveloppement-vrifier-ses-sauvegardes-en-python/</link>
		<comments>http://www.chiroux.com/dveloppement-vrifier-ses-sauvegardes-en-python/#comments</comments>
		<pubDate>Thu, 28 Dec 2006 23:58:24 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Administration Système]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/article-1167350304.html</guid>
		<description><![CDATA[<p>Python est un langage que j&#8217;avais envie de regarder de plus près depuis quelques temps déjà  (ne serait-ce que parce que civ4 est personnalisable en python), mais je n&#8217;avais jamais eu vraiment le temps.
Comme je profite de mes vacances pour réorganiser mes fichiers et mes sauvegardes, j&#8217;ai du refaire un script de vérification sous [...]]]></description>
			<content:encoded><![CDATA[<p>Python est un langage que j&#8217;avais envie de regarder de plus près depuis quelques temps déjà  (ne serait-ce que parce que civ4 est personnalisable en python), mais je n&#8217;avais jamais eu vraiment le temps.<br />
Comme je profite de mes vacances pour réorganiser mes fichiers et mes sauvegardes, j&#8217;ai du refaire un script de vérification sous linux (auparavant en windows). Quelle bonne occasion pour s&#8217;y mettre plutôt que de bêtement faire ça en perl&#8230;</p>
<p>Le besoin est assez simple :<br />
j&#8217;ai deux PC sous linux qui stockent des fichiers et qui se sauvegardent en croisé (via d&#8217;autres scripts déjà écrits ; vu la simplicité et la puissance de python, je m&#8217;amuserais peut-être à les réécrire à l&#8217;occasion).<br />
Prenons par exemple le serveur 1, le script de sauvegarde qui tourne dessus copie son fichier sur le serveur 2 et s&#8217;arrange pour garder n générations.</p>
<p><span id="more-30"></span></p>
<p>Mon inquiétude (et donc ce que j&#8217;ai envie de vérifier) c&#8217;est que ce script de sauvegarde s&#8217;execute mal ou encore que la copie du serveur 1 vers le serveur 2 plante en plein milieu.</p>
<p>Mon but est donc de faire tourner localement sur le serveur 2 un script qui va vérifier que les sauvegardes sont bien présentes, de la bonne date et de la bonne taille.</p>
<p>Je suis désolé si vous autres experts de python trouvent ce script complètement horrible, ne respectant pas la phylosophie de python ou que sais-je. C&#8217;est mon premier script python et m&#8217;a permis de découvrir globalement ses possibilités, et je dois dire que je suis plutôt enthousiaste car c&#8217;est un langage a la fois simple et puissant.</p>
<span id="Fonctionnalits"><h2>Fonctionnalités</h2></span>
<span id="Vrification_de_la_prsence_du_fichier_de_sauvegarde_:"><h3>Vérification de la présence du fichier de sauvegarde :</h3></span>
<p>Pour chaque sauvegarde à vérifier, je voulais pouvoir en contrôler sa présence, sa fraicheur, ainsi que sa taille.<br />
Comme beaucoup de sauvegardes, mes fichiers backups ont une date/heure dans leur nom, j&#8217;ai donc choisit d&#8217;utiliser les expressions régulières pour essayer de trouver mes sauvegardes :<br />
Imaginons que dans le répertoire /home/backups, nous avons les fichiers suivants :</p>
<ul>
<li> sav_serveur1_MesSitesWebs_20061226200232.tar.gz</li>
<li> sav_serveur1_MesSitesWebs_20061227200227.tar.gz</li>
<li> sav_serveur1_MesSitesWebs_20061228200335.tar.gz</li>
</ul>
<p>Dans le programme, je vais donc lui dire de vérifier, dans le répertoire /home/backups (par exemple) les fichiers du type &#8216;.*MesSitesWebs.+\.tar\.gz&#8217;</p>
<p>Ensuite, je passe simplement 3 paramétres supplémentaires : un pour la vérification de la date et deux pour la taille :</p>
<span id="Vrification_de_la_date_:"><h3>Vérification de la date :</h3></span>
<p>Le principe est qu&#8217;il faut trouver au moins un fichier sauvegardé dont la date est dans l&#8217;intervalle &#8216;acceptable&#8217; de sauvegarde. J&#8217;ai défini des intervalles journalières, hebdo, mensuelles ou annuelles.<br />
Prenons par exemple journalière (&#8216;d&#8217;) : si le vérificateur ne trouve pas de fichier (dont le nom correspond à l&#8217;expression régulière bien sûr et) dont la date de modification est inférieure aux 24 dernières heures, alors le test échoue et une erreur est remontée.</p>
<span id="Vrification_de_la_taille_:"><h3>Vérification de la taille :</h3></span>
<p>le premier paramètre est la taille de comparaison<br />
le second est le type de comparaision :</p>
<ul>
<li> &#8216;eq&#8217; : la taille du fichier doit être exactement la même que la taille de comparaison</li>
<li> &#8217;sup&#8217; : la taille du fichier doit être plus grande que la taille entrée en paramètre (c&#8217;est souvent le cas pour une archive qui grossit un peu chaque jour)</li>
<li> &#8216;inf&#8217; : la taille du fichier doit être plus petite que la taille entrée en paramètre</li>
</ul>
<span id="Vrifications_multiples_:"><h3>Vérifications multiples :</h3></span>
<p>Dans la fonction main(), il est possible d&#8217;enchaîner plusieurs vérifications de suite (en copiant/collant un bloc de vérification et en modifiant les paramètres appropriés)</p>
<span id="Envoi_d8217un_email_en_cas_d8217chec_:"><h3>Envoi d&#8217;un email en cas d&#8217;échec :</h3></span>
<p>En cas d&#8217;échec d&#8217;au moins une des vérifications, un email sera envoyé au destinataire paramétré avec le détail des erreurs rencontrées.</p>
<span id="Personnalisation_du_script_:"><h2>Personnalisation du script :</h2></span>
<p>La quasi totalité de la personnalisation est dans la fonction main() :</p>
<p>ajouter / modifier les blocs de vérifications et en mettre autant que souhaité (mais au moins 1 bien sûr).<br />
exemple d&#8217;un bloc :</p>
<pre># Check d'un fichier backupé
 TmpIsOk, TmpTexte = VerifFilesInDirectory('/home/backups/', '.*_BACKUP_MYSQL.+\.tar\.gz', 'd', 1600000, 'sup')
 if TmpIsOk == -1:
 EmailTexte = EmailTexte + "\\n" + TmpTexte + "\\n"</pre>
<span id="Personnaliser_l8217envoi_d8217email_:"><h3>Personnaliser l&#8217;envoi d&#8217;email :</h3></span>
<p>Il faut simplement changer les paramètres de l&#8217;appel de la fonction SendEmail :</p>
<pre>SendEmail('admin@example.com', 'dest@domain.com', 'PB de SAUVEGARDE', EmailTexte, 'localhost')</pre>
<ul>
<li> La première adresse est l&#8217;adresse de l&#8217;expéditeur</li>
<li> La seconde celle du destinataire</li>
<li> Le troisième paramètre est l&#8217;objet du mail</li>
<li> Le quatrième doit être laissé tel quel, c&#8217;est le texte contruit par les vérifications de fichiers</li>
<li> Le dernier est l&#8217;adresse du serveur smtp, il peut être local comme dans l&#8217;exemple ou celui de votre isp (smtp.monisp.fr par exemple)</li>
</ul>
<span id="Crdits_et_liens"><h2>Crédits et liens</h2></span>
<p>Pour découvrir python, j&#8217;ai parcouru un certains nombre de sites très bien faits, dont voici un aperçu :</p>
<ul>
<li> <a href="http://diveintopython.adrahon.org/toc/index.html" target="_blank">Le livre &#8216;plongez au coeur de python&#8217;</a></li>
<li> <a href="http://docs.python.org/ref/" target="_blank">La référence officielle</a></li>
<li> <a href="http://docs.python.org/modindex.html" target="_blank">L&#8217;index officiel des modules</a></li>
</ul>
<span id="Tlcharger_l8217archive"><h2>Télécharger l&#8217;archive</h2></span>
Note: There is a file embedded within this post, please visit this post to download the file.
]]></content:encoded>
			<wfw:commentRss>http://www.chiroux.com/dveloppement-vrifier-ses-sauvegardes-en-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
