<?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; Programmation</title>
	<atom:link href="http://www.chiroux.com/category/informatique/programmation/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>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>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>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>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>
		<item>
		<title>Augmenter votre productivité avec outlook</title>
		<link>http://www.chiroux.com/augmenter-votre-productivite-avec-outlook/</link>
		<comments>http://www.chiroux.com/augmenter-votre-productivite-avec-outlook/#comments</comments>
		<pubDate>Tue, 13 Jun 2006 00:14:54 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[macro]]></category>
		<category><![CDATA[outlook]]></category>
		<category><![CDATA[productivité]]></category>
		<category><![CDATA[vbscript]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/article-1150157694.html</guid>
		<description><![CDATA[<p>Je viens de mettre en ligne un jeu de macros à  installer dans son outlook. Ces macros ajoutent un certain nombre de fonctions qui vous vous permettre de mieux gèrer le flux quotidien de vos emails.</p>
<p></p>
Introduction
<p>Cette macro est issue d&#8217;un constat que j&#8217;imagine pouvoir partager assez facilement : nous recevons trop de mails, ces [...]]]></description>
			<content:encoded><![CDATA[<p>Je viens de mettre en ligne un jeu de macros à  installer dans son outlook. Ces macros ajoutent un certain nombre de fonctions qui vous vous permettre de mieux gèrer le flux quotidien de vos emails.</p>
<p><span id="more-32"></span></p>
<span id="Introduction"><h2>Introduction</h2></span>
<p>Cette macro est issue d&#8217;un constat que j&#8217;imagine pouvoir partager assez facilement : nous recevons trop de mails, ces mails sont (trop) souvent envoyés à tort et à travers dans l&#8217;entreprise sans que vous soyez vraiment concernés, ils sont souvent associés de gros fichiers attachés qui ont la fâcheuse habitude de vous saturer votre boîte exchange, bref tout cela devient difficilement gérable.<br />
Il y a quelque temps déjà, j&#8217;ai donc décidé d&#8217;écrire une ou deux macros dans outlook pour m&#8217;aider à gérer tout cela. Au fil du temps, c&#8217;est devenu un ensemble de fonctionnalités et de petits outils qui pourront vous aider tous les jours, je l&#8217;espère.</p>
<span id="Crdits_et_Remerciements"><h2>Crédits et Remerciements</h2></span>
<p>Ces macros utilisent quelques outils glanés sur internet ou réalisés sur mesure, merci donc à :</p>
<ul>
<li>Patrick Bouffel et Olivier de Paulignac (<a href="http://www.smartextension.com/" class="broken_link"  target="_blank">http://www.smartextension.com/</a>) pour les librairies de zip (SmartJustZip.ocx)</li>
<li>Outlook Redemption : <a href="http://www.dimastr.com/redemption/" target="_blank">http://www.dimastr.com/redemption/</a> .Pour cette excellente extension à outlook dont je ne fait qu&#8217;effleurer les possibilités mais qui me permet d&#8217;éviter les difficultés d&#8217;accès aux objets outlook introduites par microsoft à partir d&#8217;outlook 2000.</li>
<li>Olivier Chami (Cham) pour ces récents ajouts et modifications aux différentes macros.</li>
</ul>
<p>Merci également aux &laquo;&nbsp;beta testeurs&nbsp;&raquo; qui ont bien voulu risquer leur config outlook  depuis le début : Patrick, Sylvain-Pierre, Cham, Benoît, Marc&#8230;</p>
<span id="Fonctionnalits_et_pr-requis"><h2>Fonctionnalités et pré-requis</h2></span>
<p>Les fonctionnalités actuellement gérées par la macro sont :</p>
<ul>
<li>Zip automatique des attachs des mails sortants</li>
<li>Zip automatique des attachs des mails entrants</li>
<li>Déplacement automatique des mails dont l&#8217;utilisateur n&#8217;est pas clairement destinataire</li>
<li>Filtrage automatique des mails dont les attachs sont considérées trop importantes</li>
<li>Export vers Excel de la taille de la boîte aux lettres, ainsi que de tous ces répertoires</li>
</ul>
<p>Ces macros fonctionnent tous les jours sur mon PC sous windows XP PRO+Outlook 2003+Exchange<br />
Elle a fonctionné (mais je n&#8217;ai pas testé depuis longtemps) sur Windows 2000 et outlook 2000 et 2002.<br />
Pour réaliser l&#8217;installation, il faut être administrateur local du poste.</p>
<p>Toutes ces macros manipulent des objets &#8216;mails&#8217;, mais ne touchent pas aux autres objets d&#8217;outlook (rendez-vous, notes..) et par conséquent ne zippent pas les attachs de ces éléments là.</p>
<span id="Comment_j8217ai_organis_mon_outlook"><h2>Comment j&#8217;ai organisé mon outlook</h2></span>
<p>L&#8217;utilisation de ces macros est adaptée à ma façon de m&#8217;organiser avec outlook. Ainsi si vous êtes organisé diféremment, il est possible que l&#8217;utilisation de ces macros ne soient pas optimale :</p>
<p>J&#8217;utilise donc outlook au travail, en connexion avec un serveur exchange (exchange, sa version, etc.. ne sont pas déterminants).<br />
J&#8217;utilise en priorité (et uniquement) ma boîte de réception exchange.<br />
Je ne stocke pas à long terme mes mails le répertoire principal d&#8217;arrivée des mails : seuls les mails non encore lus et à traiter (même s&#8217;ils sont lus) sont laissés dans la boîte principale =&gt; target 0 mails dans la boîte de réception.<br />
J&#8217;utilise les petits drapeux de rappels et la notion lu/non lu dans cette inbox pour gérer les priorités de traitement/lecture.<br />
Dès que j&#8217;ai lu et/ou traité un mail, je le déplace dans un des répertoires et sous répertoires de ma boîte de réception exchange : j&#8217;utilise des dizaines de répertoires (organisés par thèmes) pour ranger mes mails.<br />
Chacun de ces répertoire dispose d&#8217;une règle d&#8217;autoarchivage (souvent celle que j&#8217;ai définie par défaut) qui archive les vieux mails (traités donc : il n&#8217;y a pas de règle d&#8217;archivage dans ma inbox) dans des pst.<br />
Quand un .pst devient trop gros (quelques gigas) je le grave et j&#8217;en démarre un nouveau.</p>
<p>La macro s&#8217;applique donc principalement (en tout cas elle est testée dessus) sur un environnement outlook-exchange avec une utilisation de la boite stockée sur exhange et des .pst uniquement utilisés en archivage (lecture seule pour la consultation / écriture via des règles d&#8217;autoarchivage).</p>
<span id="Installation_et_Configuration"><h2>Installation et Configuration</h2></span>
<ul>
<li>Dézippez l&#8217;archive dans un endroit quelconque de votre disque (répertoire temporaire)</li>
<li>Vous avez normalement les fichiers suivants dans le répertoire bin_v1.97/
<ul>
<li>srcMacroOutlook_v1.97.txt</li>
<li>Redemption.dll</li>
<li>Install.bat</li>
<li>SmartJustZip.ocx</li>
<li>vbzip11.dll</li>
<li>unzip32.dll</li>
</ul>
</li>
<li>Lancer Install.bat : l&#8217;install va copier les dll et ocx dans votre système et va rendre active les dll
<ul>
<li>lorsque vous avez le popup d&#8217;install de rédemption, cliquez sur &#8216;I agree&#8217; puis Ok</li>
</ul>
</li>
<li>Ouvrez votre outlook</li>
<li>Allez dans Outils / Macros / Visual Basic Editor</li>
<li>Dans le menu projet, à gauche, ouvrez le premier projet, puis &#8216;microsoft Office Outlook Objets&#8217;, puis double cliquez sur &#8216;ThisOutlookSession&#8217;</li>
<li>Ouvrez le fichier srcMacroOutlook_v1.97.txt avec un éditeur de texte, sélectionnez tout, puis copier</li>
<li>collez le tout dans l&#8217;écran ouvert par &#8216;ThisOutlookSession&#8217;</li>
</ul>
<span id="Configuration"><h3>Configuration</h3></span>
<p>La configuration s&#8217;effectue en éditant un certain nombre de variables globales situées dans le début du code source de la macro. Ainsi si ce n&#8217;est pas encore présent, ouvrez outlook, ouvrez l&#8217;éditeur visual basic et double cliquez sur &#8216;ThisOutlookSession&#8217;</p>
<p>Descendez dans le code jusqu&#8217;à trouver la zone suivante :</p>
<pre>'=====================================================================================================================
' == Fonctionnalités (et activations / Désactivations des fonctionnalités)
'=====================================================================================================================
</pre>
<p>Le premier bloc concerne les fonctionnalités que vous souhaitez activer ou désactiver. Par défaut, toutes les fonctionnalités qui fonctionnent sont activées (la fonctionnalité 6 ne fonctionne pas et donc est inutile).<br />
ex : Pour autoriser le zip automatique des attachs des mails entrants, placez à 1 la variable mi_FUNC01, pour le désactiver, placez la variable à 0</p>
<p>Faites de même avec les autres fonctionnalités : la correspondance des fonctionnalité est dans le source, mais en voici un tableau récapitulatif :</p>
<table border="1" cellspacing="1" cellpadding="1" width="30%" align="center">
<tbody>
<tr>
<td align="center"><span style="font-size: xx-small;">N° Fonction</span></td>
<td align="center"><span style="font-size: xx-small;">Fonctionnalité</span></td>
</tr>
<tr>
<td align="center"><span style="font-size: xx-small;">mi_FUNC01</span></td>
<td align="center"><span style="font-size: xx-small;">Zip auto mails entrants</span></td>
</tr>
<tr>
<td align="center"><span style="font-size: xx-small;">mi_FUNC02</span></td>
<td align="center"><span style="font-size: xx-small;">Zip auto mails sortants</span></td>
</tr>
<tr>
<td align="center"><span style="font-size: xx-small;">mi_FUNC03</span></td>
<td align="center"><span style="font-size: xx-small;">Déplace mails non destinataire</span></td>
</tr>
<tr>
<td align="center"><span style="font-size: xx-small;">mi_FUNC04</span></td>
<td align="center"><span style="font-size: xx-small;">Gestion des Quotas</span></td>
</tr>
<tr>
<td align="center"><span style="font-size: xx-small;">mi_FUNC05</span></td>
<td align="center"><span style="font-size: xx-small;">Rattapage auto dans la inbox des mails non traités par la macro</span></td>
</tr>
<tr>
<td align="center"><span style="font-size: xx-small;">mi_FUNC06</span></td>
<td align="center"><span style="font-size: xx-small;">&lt;NON FONCTIONNEL&gt;</span></td>
</tr>
<tr>
<td align="center"><span style="font-size: xx-small;">mi_FUNC07</span></td>
<td align="center"><span style="font-size: xx-small;">refuse les mails entrants trop volumineux</span></td>
</tr>
</tbody>
</table>
<p>Les paramètres suivants sont situés juste en dessous dans la zone &#8216;Constantes / paramètres&#8217; :</p>
<ul>
<li><strong>ml_GROSMAIL</strong> : détermine la taille totale (en octets) d&#8217;un mail a partir de laquelle la macro va zipper les fichiers attachés. (utilisé pour les fonctions 01 et 02)</li>
<li><strong>ml_TROPGROSMAIL</strong> : détermine la taille totale (en octets) d&#8217;un mail au delà de laquelle un mail sera refusé par la fonction 07</li>
<li><strong>ms_MONNOM</strong> : inscrivez ici votre nom (en majuscules) tel qu&#8217;il apparait lorsque vous êtes destinataire d&#8217;un mail. La fonction 03 va utiliser cette valeur et va dérouter tous les mails entrants où ce nom n&#8217;apparaît pas dans le champ des destinataires (vous pouvez entrer seulement une partie du nom, mais le filtrage risque d&#8217;être moins précis)</li>
<li><strong>ms_REPERTOIRETEMPIN</strong> et <strong>ms_REPERTOIRETEMPIN</strong> : ce sont les répertoires temporaires qui sont utilisés lors du zip des attachs du mail : les fichiers en cours de zip transitent par ces répertoires. <span style="color: #ff0000;"><strong>Attention </strong>: une fois les répertoires déterminés, vous devrez les créer sur votre disque : la macro ne les crée pas automatiquement.</span></li>
<li><strong>ms_NONDEST_DIRECTORY_PARENT</strong> et<strong> ms_NONDEST_DIRECTORY_SON</strong> : correspond à l&#8217;arborescence qui permet d&#8217;arriver au répertoire dans lequel les mails &#8216;non destinataires&#8217; de la fonction 03 seront déplacés. Actuellement, dans la version 1.97, l&#8217;arborescence est obligatoirement à deux niveaux. Par défaut, les mails seront donc déplacés dans : Boite de Réception/Interne/CC. <span style="color: #ff0000;"><strong>Attention </strong>: vous devez également créer ces répertoires dans votre arborescence outlook pour que cela fonctionne.</span></li>
<li><strong>ms_NOMREPERTOIRE_QUOTACOUNT</strong> : nom du répertoire (situé directement sous la Boîte de réception) où seront stockées les informations de comptages des mails reçus (utilisé par la fonction 04 de gestion des quotas. <span style="color: #ff0000;"><strong>Attention</strong>, vous devrez également créer ce répertoire dans votre Boîte de réception outlook.</span></li>
<li><strong>mi_QUOTA</strong> : détermine le seul du quota (nombre de mails reçus dans la boite de réception, post filtrage et déplacements, notamment ceux de la fonction 03. Au delà du seuil, les mails seront automatiquement classés en importance faible.</li>
<li> <strong>mi_DONOTZIPEXT</strong> : liste des extensions de fichiers qu&#8217;il n&#8217;est pas nécessaire de zipper (attention à laisser le ; en début et fin de liste)</li>
</ul>
<span id="Fin_de_l8217installation"><h3>Fin de l&#8217;installation</h3></span>
<ul>
<li>Une fois la config terminée, fermez la fenêtre Visual Basic editor et fermez outlook</li>
<li>Répondez &#8216;oui&#8217; à la question sur l&#8217;enregistrement des modifications de ThisOutlookSession</li>
<li>Redémarrez outlook</li>
</ul>
<span id="Niveau_de_scurit_des_macros_et_signature_de_la_macro"><h3>Niveau de sécurité des macros et signature de la macro</h3></span>
<p>Depuis outlook 2002, microsoft est plus &#8216;difficile&#8217; dans sa gestion de la sécurité et particulièrement autours des macros. Si vous utilisez un outlook 2002 ou supérieur, vous allez certainement avoir un message d&#8217;avertissement qui vous demande d&#8217;exécuter ou non la macro. Ou pire, certains niveaux de sécurités interdisent complètement l&#8217;exécution des macros.<br />
Si vous le sentez bien, vous pouvez soit :<br />
- baisser le niveaux de sécurité d&#8217;outlook pour autoriser le lancement de cette macro<br />
- laisser le niveau de sécurité comme il est, mais signer le code : cela permet en gros de dire à outlook que ce code est un code de confiance et qu&#8217;il peut l&#8217;exécuter. Pour signer le code, il faut suivre une procédure qui est <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnout2k/html/oldigitalsignature.asp" target="_blank">décrite ici par microsoft</a>.</p>
<span id="Dtail_et_revue_des_fonctions"><h2>Détail et revue des fonctions</h2></span>
<span id="Zipper_les_fichiers_attachs"><h3>Zipper les fichiers attachés</h3></span>
<p>Le principe est le suivant :</p>
<ul>
<li>pour chaque mail (entrant ou sortant), la macro regarde si la taille totale du mail dépasse le seuil qui nous autorise à zipper.</li>
<li>Si c&#8217;est le cas, alors on regarde chaque fichier attaché : si l&#8217;extension n&#8217;est pas une extension d&#8217;un fichier déjà compressé, alors on le zippe (ceci quelque soit la taille du fichier concerné).</li>
<li>ex1 : un mail de 100ko avec deux fichiers attachés : un fichier texte de 40ko et un fichier Excel de 60ko : les deux fichiers seront zippés</li>
<li>ex2 : un mail de 560ko avec deux fichiers attachés : un fichier texte de 1ko et une image jpg de 559ko : le fichier txt sera zippé et le jpg ignoré (car déjà compressé)</li>
<li>Les attachs maintenant zippées, la taille totale de votre mail sera considérablement réduite.</li>
</ul>
<p>Vous pouvez empêcher qu&#8217;un email soit traité par cette fonction en ajoutant [NOZIP] au début de l&#8217;objet de votre message. Cette fonctionnalité marche aussi bien pour un émetteur qui vous envoie un mail (et met [NOZIP] dans l&#8217;objet) que pour vous qui envoyez un mail dont vous ne souhaitez pas zipper les attachs. Cette entête [NOZIP] sera supprimé de l&#8217;objet du mail une fois traité par la macro.</p>
<span id="Dplacement_des_mails_non_destinataire"><h3>Déplacement des mails non destinataire</h3></span>
<p>Il est nécessaire de détailler ce que j&#8217;appelle &laquo;&nbsp;non destinataire&nbsp;&raquo; et pourquoi je n&#8217;ai pas utilisé les règles standard de routage d&#8217;outlook qui permettent de déplacer les mails &#8216;où je suis en copie&#8217; :<br />
La grosse différence est précisément que la règle outlook route les mails où je suis <span style="text-decoration: underline;">explicitement en copie</span>, alors que la macro destinataire &#8216;rejette&#8217; tous les mails ou le nom recherché n&#8217;est pas <span style="text-decoration: underline;">explicitement dans les destinataires</span>.</p>
<p>Par exemple, si je m&#8217;appelle Dupont : un mail envoyé à une liste &#8216;Tous les interlocuteurs&#8217; ne sera pas traitée par la règle outlook mais sera déplacée par la fonction &#8216;non destinataire&#8217;, car le mot &#8216;Dupont&#8217; n&#8217;est pas présent dans le champ destinataire.</p>
<p>Attention toutefois, même si c&#8217;est une fonction que j&#8217;ai voulu et que j&#8217;ai choisi de mettre en place, elle peut vous faire rater un certain nombre de mails importants si vous ne vérifiez pas votre répertoire de copie et si vous êtes régulièrement dans des listes ou des alias.</p>
<span id="Gestion_des_Quotas"><h3>Gestion des Quotas</h3></span>
<p>Le principe est, une fois tous les filtrages et déplacement réalisés, de compter les mails que l&#8217;on reçoit chaque jour dans la boîte de réception et de réaliser une action une fois un &#8216;nombre raisonnable de mails à traiter par jour&#8217; est dépassé.</p>
<p>Au départ, je souhaitais rejeter tout mail au delà du quota (en le renvoyant à l&#8217;expéditeur par exemple, avec un message particulier expliquant le principe), mais je n&#8217;ai pas osé pousser le modèle : le monde de l&#8217;entreprise n&#8217;est pas encore prêt&#8230; (imaginez que le 1er mail au delà de votre quota est un mail de votre directeur général et qu&#8217;il est seulement 14h : votre mail automatique de rejet sera-t-il bien reçu et compris ? Je n&#8217;en suis pas certain ; il faut au moins une politique des mails un peu évoluée dans votre entreprise pour y arriver&#8230;et peut être une gestion de quota plus &#8216;fine&#8217; (white-lists, black-list, détermination auto des &#8216;collègues spammeurs&#8217;&#8230;)</p>
<p>En attendant, j&#8217;ai opté pour une solution &#8216;passive&#8217; : tout mail qui dépasse le quota devient un mail de faible importance, cela permet de roder la solution.</p>
<p>Regardez un coup de temps en temps votre répertoire de comptage, cela vous permettra d&#8217;avoir une idée de la quantité de mails que vous recevez, vous serez surpris je pense.</p>
<span id="Rattrapage_des_mails_non_traits_fonction_05"><h3>Rattrapage des mails non traités (fonction 05)</h3></span>
<p>Le principe de cette fonction n&#8217;est pas clair à comprendre en lisant le titre&#8230;j&#8217;en ai conscience, mais je n&#8217;ai pas trouvé de titre clair.<br />
Le sujet est assez simple finalement : toutes ces macros sont résolument des programmes qui s&#8217;exécutent du côté client (dans votre outlook), donc si vous fermez outlook, recevez des mails pendant qu&#8217;outlook est fermé et que vous réouvrez outlook, alors vous allez avoir un tas de mails dont les attachs ne sont pas zippées, dont les  envois en copie ne sont pas déplacés, etc&#8230;<br />
Cette fonction sert à rattraper le retard : au prochain mail que vous recevrez, il sera traité normalement par la macro, puis la fonction de rattrapage s&#8217;enclenchera et scannera toute la inbox à le recherche de mails qui n&#8217;ont pas été traités et va les traiter.</p>
<p>Vous pouvez également lancer manuellement ce traitement à tout moment en exécutant directement la macro &#8216;ScanneInbox&#8217; : dans outlook / Outils / Macro / Macros : choisissez &#8217;scanneInbox&#8217; puis exécuter.</p>
<span id="Refuser_les_mails_trop_volumineux"><h3>Refuser les mails trop volumineux</h3></span>
<p>Contrairement à la gestion des quotas où je ne suis pas allé au bout de la démarche, j&#8217;ai voulu forcer un changement de comportement déviant dans l&#8217;utilisation des mails qui consiste à mettre n&#8217;importe quoi en attach sans vérifier, et de balancer les mails à 150 personnes en destinataires et 300 en copie&#8230;<br />
Cette fonction va donc décider qu&#8217;un mail est définitivement trop gros et va purement et simplement le supprimer, l&#8217;annihiler définitivement. Un mail sera envoyé au destinataire pour lui suggérer de chercher une autre solution plus efficace qu&#8217;un mail pour partager ses données.</p>
<p>Bien sûr avant de rejeter on aura déjà zippé les attachs pour essayer de réduire la taille.</p>
<p>Le texte de réponse est pour l&#8217;instant directement dans le code de la fonction et dit ceci (en cas de mail avec des attachs) :<br />
<strong>Objet :</strong> <span style="font-family: Courier New;">Message dépassant la taille maximale</span><br />
<strong>Texte :</strong><br />
<span style="font-family: Courier New;">[Ceci est un message généré automatiquement]<br />
Votre message : [<em>objet du message</em>] n&#8217;a pas été délivré au destinataire.<br />
Même après avoir compressé les fichiers attachés, ce mail a encore une taille de : <em>xxx </em>ko</span></p>
<p><span style="font-family: Courier New;">Le destinataire n&#8217;a pas été informé de ce message.<br />
Le Message n&#8217;a pas été délivré ; il a été supprimé directement.</span></p>
<p><span style="font-family: Courier New;">Avant de réémettre ce message, merci de vérifier la taille et la pertinence des fichiers attachés.<br />
Si le ou les fichiers que vous voulez faire parvenir au destinataire sont toujours trop gros, envisagez l&#8217;utilisation des répertoires partagés !</span></p>
<p><span style="font-family: Courier New;">[Paramétrage actuel de la taille maximale autorisée : <em>yyyy </em>ko</span></p>
<span id="Export_de_la_taille_des_botes_au_lettres"><h3>Export de la taille des boîtes au lettres</h3></span>
<p>Encore une fois, j'ai préféré créer une petite macro plutôt qu'utiliser la fonction classique d'outlook pour connaitre la taille des répertoires de ma inbox. En effet, la fonction de base est certes très rapide, mais elle ne permet aucune interactivité : il n'y pas moyen par exemple de trier par ordre décroissant de taille des répertoires ou de taille de la somme des sous-répertoires.<br />
L'export Excell permet de faire toutes les analyses et les tris que l'on souhaite et c'est pratique de temps en temps pour déterminer ce qui réellement prends trop de place dans notre Boîte de réception.</p>
<p>Pour utiliser cette macro, vous devez la déclencher manuellement : menu outils / Macro / Macros. Choisissez 'MailBoxSize' et executez. Choississez ensuite le répertoire racine de ce que vous souhaitez calculer et attendez (la fonction n'est pas très rapide pour l'instant). Excel s'ouvrira automatiquement.</p>
<span id="Notes_pour_les_dveloppeurs"><h2>Notes pour les développeurs</h2></span>
<p>Je ne vais pas rentrer ici dans un détail poussé de chaque fonction, mais expliquer les quelques 'trucs' que j'ai utilisé pour faire marcher ces macros</p>
<span id="Le_champ_kilomtrage"><h3>Le champ 'kilométrage'</h3></span>
<p>J'ai piqué l'idée quelque part sur le net et je l'ai détournée pour servir mon utilisation.<br />
A la base, je ne sais pas trop a quoi sert le kilométrage dans un objet 'mail', en tout cas je ne l'ai jamais vu rempli a part via ces macros. C'est un champ texte complètement utilisable.<br />
Je m'en sert donc pour stocker dans chaque mail le passage à travers les différentes fonctions. Pour cela j'utilise une chaîne de caractère ou chaque caractère est un 'bit' (0 ou 1) indiquant si on est passée par la fonction xx ou non.<br />
xx est la position du bit dans la chaîne.<br />
Cela permet d'eviter de rezipper des attachs déjà zippée, de revérifier si on est destinataire ou non, etc..<br />
C'est donc avant tout un système pour améliorer les performances.<br />
Affichez le kilométrage dans les champs de votre inbox, c'est pratique pour savoir si vous avez des mails non traités ou non.</p>
<span id="Commentaires_bugs_et_amlioration"><h2>Commentaires, bugs et amélioration</h2></span>
<p>N'hésitez pas à me laisser un maximum d'input dans les commentaires ci-dessous sur les pb rencontrés, sur des suggestions d'amélioration ou sur des améliorations que vous avez réalisé.</p>
<span id="Tlcharger_larchive"><h2>Télécharger l'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/augmenter-votre-productivite-avec-outlook/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Tutoriel pour un formulaire Ajax</title>
		<link>http://www.chiroux.com/tutoriel-pour-un-formulaire-ajax/</link>
		<comments>http://www.chiroux.com/tutoriel-pour-un-formulaire-ajax/#comments</comments>
		<pubDate>Tue, 23 May 2006 23:06:50 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Obsolete]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[prototype]]></category>
		<category><![CDATA[script.aculo.us]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/article-1148425610.html</guid>
		<description><![CDATA[<p>Ayant eu le besoin de réaliser un formulaire, j&#8217;ai choisi de tester un peu la techno Ajax et d&#8217;en mesurer les avantages et inconvénients</p>
Introduction
<p>Vous trouverez ci-dessous un petit tutoriel pour installer et utiliser un système de formulaires web utilisant la technologie AJAX, très facile et rapide d&#8217;installation et de configuration une fois que vous en [...]]]></description>
			<content:encoded><![CDATA[<p>Ayant eu le besoin de réaliser un formulaire, j&#8217;ai choisi de tester un peu la techno Ajax et d&#8217;en mesurer les avantages et inconvénients<span id="more-34"></span></p>
<span id="Introduction"><h1>Introduction</h1></span>
<p>Vous trouverez ci-dessous un petit tutoriel pour installer et utiliser un système de formulaires web utilisant la technologie AJAX, très facile et rapide d&#8217;installation et de configuration une fois que vous en avez compris les principes.</p>
<p>Le cahier des charges que je me suis fixé était le suivant :</p>
<ul>
<li>faire un système de formulaire simple à implémenter</li>
<li>qu&#8217;il soit générique et ne nécessite quasiment pas de programmation pour implémenter un nouveau formulaire</li>
<li>que les validations soient réalisées au niveau serveur</li>
<li>que les informations de validation soient communiquées en temps réél au client</li>
<li>qu&#8217;il soit le plus sécurisé possible (notamment : impossibilité pour le client de passer outre les validations en modifiant les javascript par exemple)</li>
<li>en Php et Mysql</li>
<li>en séparant clairement fond et forme (xhtml 1.1)</li>
</ul>
<p>J&#8217;ai commencé par tout coder directement manuellement, puis à force d&#8217;améliorations, je me suis orienté vers des librairies plus génériques : <a href="http://www.prototypejs.org/" target="_blank">prototype </a>et <a href="http://script.aculo.us/" target="_blank">script.aculo.us</a>.</p>
<span id="Structure"><h1>Structure</h1></span>
<p>Le formulaire est très simple, il s&#8217;appuie sur :</p>
<ul>
<li>une table dans une base de donnée Mysql</li>
<li>une page php &#8216;backend&#8217; qui réalise les validations et les updates en base</li>
<li>une page php &#8216;frontend&#8217; qui affiche le formulaire</li>
</ul>
<p>ci-dessous le schéma général, y compris les includes.</p>
<p><img class="alignnone size-full wp-image-93" title="formulaire_schema" src="http://www.chiroux.com/wp-content/uploads/Import/Dev/formulaire_schema.jpg" alt="formulaire_schema" width="651" height="478" /></p>
<span id="Logique_gnrale"><h1>Logique générale</h1></span>
<p>Le principe est de surveiller un événement sur les champs du formulaire (par exemple, focus, blur, keyup) et sur cet évènement on déclenche un appel Ajax vers la page en back office. On transmet à cette page l&#8217;id de l&#8217;enregistrement, le nom du champ qui est en train d&#8217;être mis à jour et sa valeur. La page en backoffice effectue la validation et si cette validation est ok, on l&#8217;update en base. Elle renvoie une structure XML avec les informations des actions réalisées et les messages d&#8217;erreur si nécessaire.</p>
<p>L&#8217;ID de l&#8217;enregistrement à mettre à jour est généré au début de l&#8217;appel de la page &#8216;formulaire&#8217; et, afin de garder une persistence du formulaire non terminé, cet ID est stocké dans un cookie chez le client. Le cookie est effacé une fois le formulaire validé ou annulé (ainsi le client peut commencer à saisir un formulaire, fermer son navigateur, aller boire un café, partir en vacances.. et revenir le terminer quelques jours plus tard sans avoir perdu une seule information ; la seule contrainte de persistence est la durée de vie du cookie.</p>
<p>La page de backoffice effectue un traitement particulier lorsqu&#8217;elle reçoit l&#8217;ordre de validation complète du formulaire : elle repasse une validation de chaque champ, et vérifie également si tous les champs obligatoire sont présents. Si c&#8217;est ok, elle update en base, sinon renvoie une erreur.</p>
<span id="Un_peu_plus_de_technique"><h1>Un peu plus de technique</h1></span>
<p>Afin que le code html / php soit le plus propre possible, j&#8217;ai utilisé la fonction Event.observe de prototype afin de déclencher les observateurs d&#8217;évennements de mise à jour des champs du formulaire, ne vous étonnez donc pas de ne pas trouver de &#8216;onclick&#8217;, &#8216;onblur&#8217;, etc.. un peu partout dans le code, c&#8217;est normal, tout est géré dans le javascript init() dans formulaire.php. Ce script n&#8217;a pas besoin d&#8217;être paramétré lorsqu&#8217;on ajoute ou supprime des champs du formulaire : il scanne automatiquement tous les champs du formulaire et détermine les actions. Par défaut, les actions sont réalisées en keyup, c&#8217;est à dire que à chaque fois qu&#8217;on tape un caractère, on réalise l&#8217;appel AJAX. C&#8217;est peut-être un peu dangereux pour les performances de gros sites, vous pouvez alors par exemple remplacer les keyup par des blur.<br />
Selon les types de champs du formulaire, on appelle une fonction spécifique située dans formulaire_backoffice.js qui a pour objet de récupérer le nom du champ et sa valeur afin de déclencher l&#8217;appel AJAX.<br />
La fonction de call-back ajax interprête le XML de résultat et affiche l&#8217;erreur si nécessaire.</p>
<p>L&#8217;enregistrement en base est créé par le php la première fois qu&#8217;on appelle formulaire.php, ensuite, le cookie enregistré sur le poste client permet de garder le contexte.<br />
A chaque modification d&#8217;un champ, l&#8217;appel AJAX lance une mise à jour du champ qui est en train d&#8217;être modifié. Avant la mise à jour, le php appelé par l&#8217;appel XMLHttpRequest fait d&#8217;abord une validation. Les validations sont gérées à l&#8217;aide d&#8217;expressions régulières, c&#8217;est donc assez facile d&#8217;ajouter d&#8217;autres types de validations plus évoluées (la fonction est dans inc_validation.php). Si la validation est ok, alors on update ce champ en base.</p>
<p>Une conséquence importante de ce mode de fonctionnement est ce que j&#8217;appelle la fonctionnalité &#8216;big brother&#8217; : le formulaire enregistre tout : il suffit de venir au moins une fois sur le formulaire, commencer à le remplir. Même en ne le terminant jamais, il restera toujour un enregistrement en base reflétant ce formulaire à moitié rempli (toutefois, seuls les valeurs des champs qui passent la validation sont enregistrés en base).</p>
<p>L&#8217;appui sur &#8216;valider le formulaire&#8217; fait également un simple update en base sur ce même enregistrement et passe le champ &#8216;formulairevalide&#8217; à 1. Ainsi, on peut distinguer les formulaires terminés et validés (ils ont le champ &#8216;formulairevalide&#8217; à 1) et ceux jamais terminé (le champ est alors à zéro).</p>
<p>L&#8217;appui sur &#8216;annuler&#8217; efface simplement le cookie : l&#8217;utilisateur aura donc l&#8217;impression de repartir sur un formulaire vide, mais ce qui a été saisi dans le formulaire précédent est resté en base&#8230;</p>
<p>Pour les sites à fort taux de fréquentation, cette fonctionnalité peut éventuellement générer beaucoup d&#8217;enregistrements, il faudra peut-être prévoir une tache de &#8216;purge&#8217; des formulaires non validés dont la date de dernière modification est trop ancienne par exemple.</p>
<span id="Installation_et_Configuration"><h1>Installation et Configuration</h1></span>
<span id="Installation"><h2>Installation</h2></span>
<p>Téléchargez l&#8217;archive située en bas de cette page, dézippez cette archive dans un répertoire quelconque de votre site web (ex: http://www.monsite.com/formulaire/)</p>
<span id="Cration_de_la_base_de_donne"><h2>Création de la base de donnée</h2></span>
<p>Pour l&#8217;instant, vous allez juste créer la base et l&#8217;utilisateur qui a le droit d&#8217;y accéder.<br />
Si votre hébergeur ne vous laisse le droit qu&#8217;à une seule base, vous pouvez passer cette section, nous créeront la table plus tard.<br />
Sinon, créez une nouvelle base du nom de votre choix  (dans notre exemple : &#8216;formulaire&#8217;).<br />
Créez ensuite un utilisateur mysql avec, au minimum, les droits d&#8217;accès SELECT, INSERT, UPDATE sur la base (vous pouvez choisir si vous le souhaitez, plus tard, une fois la table créée, de limiter ces grants à uniquement la table du formulaire)</p>
<span id="Paramtrage_des_fichiers_de_configuration."><h2>Paramétrage des fichiers de configuration.</h2></span>
<p>Il y a deux fichiers de configuration : un pour le côté client et un pour le côté serveur.<br />
Commençont par le côté client :<br />
Editez le fichier &#8216;<strong><em>parameters.js</em></strong>&#8216; :</p>
<ul>
<li><strong>gs_Base_url</strong>: indiquez l&#8217;utl d&#8217;accès de votre formulaire (dans notre exemple : <em>http://www.monsite.com/formulaire/</em>) [<span style="color: #ff0000;"><strong>attention de ne pas oublier le slash (/) à la fin</strong></span>]</li>
<li><strong>gs_Redirect_url</strong>: indiquez l&#8217;url qui sera appelée à la fin de la validation de votre formulaire (dans notre exemple, on retourne à la home page : <em>http://www.monsite.com/</em></li>
<li><strong>gs_Cookie_name</strong>: indiquez le nom du cookie qui sera stocké sur le poste client (dans notre exemple : <em>formulaire</em>)</li>
<li><strong>gl_Cookie_time</strong>: on indique la durée de vie de ce cookie. Par défaut elle est de 30Jours après la date de création.</li>
</ul>
<p>Editez ensuite le fichier &#8216;<strong><em>inc_parameters.php</em></strong>&#8216; :</p>
<ul>
<li>dans <strong>$DBnombase</strong>, indiquez le nom de la base que l&#8217;on a créé tout à l&#8217;heure (ou le nom de la base donnée par votre hébergeur)</li>
<li>dans <strong>$DBnomtable</strong>, indiquez le nom de la table que l&#8217;on créera ensuite (dans notre exemple : t_formulaire)</li>
<li>dans les paramètres de mysql_connect, indiquez où se situe votre serveur (ici : localhost) et le login et password de connexion à la base (ici : login et password)</li>
<li>dans <strong>$gs_Cookie_name</strong> et <strong>$gl_Cookie_time</strong> indiquez <span style="text-decoration: underline;">exactement </span>les même valeurs que ce que vous avez paramétré dans parameters.js</li>
</ul>
<span id="Personnalisation_du_formulaire"><h2>Personnalisation du formulaire</h2></span>
<p>Il est temps maintenant de définir le contenu des champs du formulaire, pour cela, Editez le fichier &#8216;<strong><em>formulaire.php</em></strong>&#8216; :</p>
<span id="Paramtrage_gnraux_de_la_page"><h3>Paramétrage généraux de la page</h3></span>
<ul>
<li>Choisissez un titre à l&#8217;intérieur des tags &lt;title&gt;&lt;/title&gt; (dans le Head)</li>
<li>Dans le &lt;body&gt;
<ul>
<li>Laissez tel que le &lt;div id=&nbsp;&raquo;infobox&nbsp;&raquo;&gt;</li>
<li>Entez un titre dans les tags &lt;div&gt;&lt;/div&gt;</li>
<li>Mettez le texte que vous voulez devant le formulaire (en utilisant les classes de style &#8216;explication&#8217; et &#8216;question&#8217;)</li>
</ul>
</li>
</ul>
<span id="Le_formulaire"><h3>Le formulaire</h3></span>
<span id="Organisation_gnrale"><h4>Organisation générale</h4></span>
<p>Les éléments du formulaire sont organisés par groupe, un groupe est un ensemble de champs du formulaire à l&#8217;intérieur d&#8217;un &lt;fieldset&gt;<br />
Les fieldset sont des éléments de présentation, vous pouvez choisir de mettre tout le formulaire dans un seul fieldset ou à l&#8217;opposé de faire un fieldset par champ à remplir, c&#8217;est votre choix.<br />
Dans le formulaire par défaut, je montre quelques exemples différents.</p>
<p>Chaque fieldset doit avoir une légende qui est représentée par les tags &lt;legend&gt;&lt;/legend&gt;</p>
<p>la classe &laquo;&nbsp;noerreur&nbsp;&raquo; : chaque élément de formulaire (input, checkbox, radio, etc..) doit être encadré par un div de classe &laquo;&nbsp;noerreur&nbsp;&raquo;. Cette classe sera changée dynamiquement en classe &laquo;&nbsp;erreur&nbsp;&raquo; en cas de pb de validation de cet élément de formulaire.</p>
<span id="Principe_de_l8217organisation_des_champs_du_formulaire"><h4>Principe de l&#8217;organisation des champs du formulaire</h4></span>
<p><span style="color: #ff0000;"><strong>Ce principe est un des points les plus structurant de ce formulaire</strong></span>, revenons donc sur un des élements de mon cahier des charges : &laquo;&nbsp;impossibilité pour le client de passer outre les validations en modifiant les javascript&nbsp;&raquo;.</p>
<p>D&#8217;habitude, dans la plupart des formulaires classiques, on réalise une validation en javascript du côté du client (par exemple la vérification : est-ce qu&#8217;un email est bien un email). Bien sûr cette validation n&#8217;est pas suffisante, car il suffit d&#8217;interdire les javascripts pour passer outre la validation. Donc les formulaires un peu plus évolués (tous devraient le faire) doublent cette validation javascript par une validation côté serveur. Mais encore une fois, il faut bien passer les paramètres de validation au serveur. Pour un formulaire non AJAX, cela se fait par exemple dans les infos de POST. J&#8217;aurais pu également faire la même chose et simplement éviter le POST et  le rechargement de page en utilisant XMLHttpRequest. Mais rien n&#8217;empèche non plus une personne mal intentionnée de recopier le formulaire sur un poste local, d&#8217;en changer les paramètres et d&#8217;appeler le script serveur avec d&#8217;autres types de validation (changer une validation email en simple validation txt par exemple).</p>
<p>J&#8217;ai donc choisi de passer les paramètres de validation directement dans le nom des champs du formulaires. Et ce sont ces même noms qui structurent la table d&#8217;enregistrement de la base de donnée. Ainsi si une personne mal intentionnée cherche à changer une validation, elle doit changer le nom du champ et même si la validation passe, l&#8217;enregistrement en base de donnée ne passera pas.</p>
<p>Cette solution est plus sécurisante, mais elle a l&#8217;inconvénient de générer des noms de champs un peu compliqués et peut-être un peu compliqué à faire évoluer (dans le cas de nouveau types de validation par exemple qui nécessiteraient de nouveaux paramètres).</p>
<p>Les noms des champs ont donc une structure qu&#8217;il faut respecter, en voici les règles :</p>
<span id="Structure_du_nom_du_champ"><h4>Structure du nom du champ</h4></span>
<p>La Structure du champ est la suivante : <strong>ttt_x_y_z_mmm</strong></p>
<ol>
<li><em><strong>ttt </strong></em>: représente le type du champ, il peut prendre les valeurs suivantes :
<ul>
<li><strong>num </strong>: numérique entier</li>
<li><strong>real </strong>: numérique à virgules</li>
<li><strong>tel </strong>: téléphone (français)</li>
<li><strong>date </strong>: date de type JJ/MM/AAAA</li>
<li><strong>cp </strong>: code postal</li>
<li><strong>email </strong>: email</li>
<li><strong>txt </strong>: texte libre</li>
</ul>
</li>
<li><em><strong>x</strong></em> : prends deux valeurs possibles :
<ul>
<li><strong>m</strong> : mandatory (champ obligatoire)</li>
<li><strong>o</strong> : optionnal (champ optionnel)</li>
</ul>
</li>
<li><strong><em>y</em> </strong>: nombre de caractères minimum : valable uniquement pour les type de champs txt et email, mettre 0 dans les autres cas</li>
<li><em><strong>z</strong></em> : nombre de caractères maximum : valable uniquement pour les type de champs txt et email, mettre 0 dans les autres cas</li>
<li><em><strong>mmm </strong></em>: nom du champ</li>
</ol>
<p>exemples :</p>
<ul>
<li>txt_m_1_100_nom : nom est un champ texte obligatoire qui doit comporter entre 1 et 100 caractères</li>
<li>email_m_4_50_couriel : couriel est un champ de type email obligatoire qui doit comporter entre 4 et 50 caractères</li>
<li>num_o_0_0_quantite : quantite est un champ numérique entier optionnel</li>
<li>etc&#8230;</li>
</ul>
<span id="type_de_champ_et_type_de_formulaire"><h4>type de champ et type de formulaire</h4></span>
<p>Voici les associations que vous pouvez utiliser sans risque entre les types de champs du formulaire et les champs en html :</p>
<ul>
<li>input type = &laquo;&nbsp;text&nbsp;&raquo; : num | real | tel | date | cp | email | txt</li>
<li>textarea : num | real | tel | date | cp | email | txt</li>
<li>input type = &laquo;&nbsp;checkbox&nbsp;&raquo; :  num</li>
<li>input type = &laquo;&nbsp;radio&nbsp;&raquo; : num</li>
<li>select : num | txt (dans l&#8217;absolu, même si c&#8217;est moins logique : num | real | tel | date | cp | email | txt)</li>
</ul>
<p>Dans l&#8217;archive, vous trouverez un exemple de chaque cas d&#8217;input afin de vous permettre de copier/coller ce qui vous interesse.<br />
<span style="color: #ff0000;"><strong>Attention </strong></span>: il y a une petite subtilité avec les input radio : le champ &#8216;name&#8217; doit avoir la même valeur pour tout les inputs radio du même groupe (ainsi donc que l&#8217;appel de la fonction php &#8216;RecupereValeur&#8217; qui doit avoir ce même champ name comme paramètre). Par contre, les champs &#8216;id&#8217; doivent être uniques (même un numéro incrémental en plus du name par exemple), ceci afin de faire correspondre l&#8217;id de l&#8217;input et le label associé.</p>
<span id="Cration_de_la_table_dans_la_base_de_donne"><h2>Création de la table dans la base de donnée</h2></span>
<p>La table a quelques colonnes obligatoires :</p>
<ul>
<li>idformulaire : la clef principale de la table : un id incrémental</li>
<li>idsession : la valeur du cookie qui sert à garder la persistence du formulaire</li>
<li>datecreation : la date de la première insertion de l&#8217;enregistrement</li>
<li>datemodification : la date de dernière modification de l&#8217;enregistrement</li>
<li>adresseIP : l&#8217;adresseIP du poste client qui a inscrit le formulaire</li>
<li>formulairevalide : est-ce que le formulaire est validé par l&#8217;utilisateur (la valeur est a zéro tant que le formulaire n&#8217;est pas terminé)</li>
</ul>
<p>Les autres colonnes représentent exactement les noms des champs input de votre formulaire<br />
(ex : `txt_m_1_100_nom` varchar(100) NOT NULL default &nbsp;&raquo; qui correspond à l&#8217;imput &#8216;nom&#8217; de votre formulaire). <strong>Notez bien que le nom du champ doit être exactement le même que le paramètre &#8216;name&#8217; de l&#8217;inputField.</strong></p>
<p>Regardez le fichier table.sql dans l&#8217;archive d&#8217;exemple pour plus de détails.</p>
<span id="La_Forme"><h2>La Forme</h2></span>
<p>Toute la forme est gérée dans la feuille de style (style.css)<br />
L&#8217;objet de cet article n&#8217;est pas de faire un détail sur les feuilles de style : l&#8217;utilisation des styles pour ce formulaire est très simple. N&#8217;hésitez pas à aller faire un tour sur <a href="http://www.alsacreations.com/" target="_blank">Alsacréations </a>par exemple, un excellent site français qui vous apprendra un tas de choses très pratiques sur les css et sur bien d&#8217;autres choses.</p>
<span id="Conclusion__Reste__faire"><h1>Conclusion / Reste à faire</h1></span>
<p>J&#8217;espère que ce petit formulaire et ce tutoriel pourront vous aider dans vos projets. En tout cas, sentez vous libre de l&#8217;exploiter, le modifier, l&#8217;améliorer, le triturer. Si toutefois vous avez réalisé des améliorations, rencontré des bugs, souhaitez des améliorations, n&#8217;hésitez pas à m&#8217;en faire part en utilisant les commentaires ci-dessous.<br />
Ce formulaire est encore très incomplet et il reste pas mal de choses à faire si on voulait le rentre véritablement professionnel. Voici une petite todoList qui résume les points à améliorer :</p>
<ul>
<li>être tolérant aux browser qui n&#8217;ont pas XMLHttpRequest (revenir en mode post classique par exemple)</li>
<li>faire un système de validation multichamps (par exemple n checkbox doivent être coché parmi n)</li>
<li>faire évoluer les messages d&#8217;erreur :
<ul>
<li>afficher l&#8217;erreur de validation directement à côté du champ (paramètre optionnel par exemple)</li>
<li>afficher le faire que le champ est obligatoire ou non</li>
</ul>
</li>
<li>faire un système d&#8217;auto config (wizard ?) / d&#8217;install plus simplifié</li>
</ul>
<span id="Live_Demo"><h1>Live Demo</h1></span>
<p>J&#8217;ai mis en ligne le formulaire de démo situé dans l&#8217;archive, vous pourrez le retrouver <a href="http://www.chiroux.com/dev/formulaire/formulaire.php" target="_blank">en suivant ce lien</a>.</p>
<span id="Tlcharger_l8217archive"><h1>Télécharger l&#8217;archive</h1></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/tutoriel-pour-un-formulaire-ajax/feed/</wfw:commentRss>
		<slash:comments>34</slash:comments>
		</item>
		<item>
		<title>BDlisting : un newsFeed de ma liste de BD</title>
		<link>http://www.chiroux.com/bdlisting-un-newsfeed-de-ma-liste-de-bd/</link>
		<comments>http://www.chiroux.com/bdlisting-un-newsfeed-de-ma-liste-de-bd/#comments</comments>
		<pubDate>Wed, 17 May 2006 21:08:39 +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[php]]></category>
		<category><![CDATA[rss]]></category>

		<guid isPermaLink="false">http://www.chiroux.com/article-59.html</guid>
		<description><![CDATA[<p>J&#8217;ai remis à jour un php que j&#8217;avais fait basé sur l&#8217;export BDGweb de ma liste de BD réalisée sur BDGest&#8217;. Il peut exporter en html simple ou en RSS2.0 une liste complète, une liste des derniers achats ainsi que la liste des BD recherchées.</p>
<p>Maintenant BDListing peut aussi exporter du rss en plus de l&#8217;html.

L&#8217;utilisation [...]]]></description>
			<content:encoded><![CDATA[<p>J&#8217;ai remis à jour un php que j&#8217;avais fait basé sur l&#8217;export <a href="http://bdgweb.free.fr/" target="_blank">BDGweb</a> de ma liste de BD réalisée sur <a href="http://www.bdgest.com/" target="_blank">BDGest&#8217;</a>. Il peut exporter en html simple ou en RSS2.0 une liste complète, une liste des derniers achats ainsi que la liste des BD recherchées.</p>
<p>Maintenant BDListing peut aussi exporter du rss en plus de l&#8217;html.<br />
<span id="more-35"></span><br />
L&#8217;utilisation est simple :</p>
<ul>
<li>posez ce php dans votre répertoire racine de BDGweb</li>
<li>éditez le php pour mettre à jour l&#8217;URL de votre site</li>
</ul>
<p>Appelez-le avec les paramètres suivant :</p>
<ul>
<li>feed
<pre class="brush: xml;">[/html]</pre>
<p>(optionnel, defaut : html)</li>
<li>type [liste | rech | top] (optionnel, defaut : liste)</li>
<li>nbre (int) (optionnel, defaut : 50, utilisé uniquement si type=top)</li>
</ul>
<p>Les 3 types de liste sont :</p>
<ul>
<li>liste : un extract complet de la bade BDGWeb</li>
<li>rech : la liste des BD recherchées</li>
<li>top : le top n (par défaut : 50) des derniers achats (très pratique quand, comme moi, on a tellement peu de mémoire qu&#8217;on est capable d&#8217;acheter 2 fois la même BD deux semaines de suite <img src='http://www.chiroux.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </li>
</ul>
<p>Exemples :<br />
1) liste complète en html<br />
/BDlisting.php?feed=html&amp;type=liste</p>
<p>2) 75 derniers achats en rss 2.0<br />
/BDlisting.php?feed=rss&amp;type=top&amp;nbre=75</p>
<p>Retrouvez l&#8217;utilisation de ce programme avec ma propre liste :</p>
<ul>
<li><a href="http://bd.chiroux.com/BDlisting.php?feed=html&#038;type=liste" target="_blank">Liste complète html</a></li>
<li><a href="http://bd.chiroux.com/BDlisting.php?feed=rss&#038;type=top&#038;nbre=50" target="_blank">50 dernier achats en RSS</a></li>
</ul>
<span id="Tlcharger_le_source"><h2>Télécharger le source</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/bdlisting-un-newsfeed-de-ma-liste-de-bd/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
