Freelance Expert Technique Magento - PHP + JS : Créer un calendrier interactif avec jQuery

CONTACT
RSS

PHP + JS : Créer un calendrier interactif avec jQuery

Nous allons ici voir comment r√©aliser un calendrier interactif dans le type de celui propos√© par Google Calendar. Je vous pr√©viens quand m√™me, √ßa va √™tre relativement long et complexe. J’ai r√©alis√© ceci dans le cadre d’un projet et je vous propose ici un turoriel sur une version “Lite”. Le but est d’obtenir un calendrier interactif r√©pondant aux actions suivantes : d√©placer des √©v√©nements, redimensionner des √©v√©nements (par le bas), Cliquer sur un √©v√©nement (Popup d’info) et double cliquer dans un espace vide (Cr√©er un √©v√©nement). Chacune de ces actions entrainera √©galement un enregistrement des modifications dans la base de donn√©es.

Vous pouvez ci-dessous voir une d√©monstration de ce que vous allons obtenir. La d√©monstration est uniquement compos√©e de Javascript, css et Html, l’enregistrement des actions effectu√©es sur les √©v√©nements n’est pas effective. Les sources du Contr√īleur, de la vue associ√©e et du javascript sont disponibles tout en bas du tutoriel (Les sources ci-dessous sont celles de la d√©mo).

Ce tutoriel se décompose en 5 étapes :

  1. Principes de base
  2. Base de données
  3. Contr√īleur
  4. Vue
  5. Ajax

√Čtape 1. Les principes de base

Voici comment fonctionne la bête. Nous allons créer un tableau avec une seule ligne et 7 colonnes (sans compter la 1ere ligne de titre)

<table>
  <th>
    <!-- Les en-têtes -->
  </th>
  <tr>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</table>

Dans chacune des colonnes, nous allons afficher les événement de la journée y correspondant. Le positionnement des événements se fera en respectant les principes suivants:

  1. 10px = 15 minutes
  2. Taill√© d’un √©l√©ment = 10px * sa dur√©e en quart d’heure
  3. Positionnement initial d’un √©l√©ment = 10px * ( heure de d√©part en minute √† partir de 0h00 * 15)

Dans un premier temps, nous allons attaquer la construction du tableau de données qui nous servira à construire le rendu visuel de nos événements au sein de notre Calendrier.

√Čtape¬†2. La partie BDD

Pour stocker nos événements, nous allons utiliser la table suivante :

CREATE TABLE IF NOT EXISTS `evenement` (
  `evenement_id` int(30) NOT NULL AUTO_INCREMENT,
  `evenement_titre` varchar(255) COLLATE utf8_bin NOT NULL,
  `evenement_contenu` text COLLATE utf8_bin NOT NULL,
  `evenement_debut` int(30) NOT NULL,
  `evenement_fin` int(30) NOT NULL,
  `evenement_lieu` varchar(255) COLLATE utf8_bin NOT NULL,
  `evenement_idUser` int(30) NOT NULL,
  PRIMARY KEY (`evenement_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=16 ;

Je ne pense pas qu’il soit n√©cessaire de pr√©ciser l’utilit√© de chacun des champs, leurs noms sont assez explicites. Attaquons maintenant la partie PHP !

√Čtape¬†3. La partie Contr√īleur

J’ai d√©velopp√© ce module sous Zend Framework, je vais donc ici √©galement respecter le mod√®le MVC pour construire mon tableau de donn√©es. Le but est d’obtenir un tableau de la forme suivante :

Array
(
    [0] => Array
        (
            [class] => other_day
            [day_start] => 1261954800
            [nb] => 0
            [tsu] => 0
        )

    [1] => Array
        (
            [class] => other_day
            [day_start] => 1262041200
            [events] => Array
                (
                    [0] => Array
                        (
                            [id] => 6
                            [titre] => (Sans titre)
                            [lieu] => (Inconnu)
                            [debut] => 1262055600
                            [fin] => 1262062800
                            [qhoccured] => 8
                            [size] => 80
                            [qhstart] => 16
                        )

                )

            [nb] => 1
            [tsu] => 80
        )
)

[Index du tableau] (de 0 à 6) : Les jours de la semaine

  • [class] : current_day ou other_day (Jour actuel ou non)
  • [day_start] : Timestamp de d√©part du jour
  • [events]: Array() regroupant les √©v√©nements de la journ√©e
    • [id] : Identifiant de l’√©v√©nement
    • [titre] : Titre de l’√©v√©nement
    • [lieu] : Lieu
    • [debut] : Timestamp de d√©part
    • [fin] : Timestamp de fin
    • [qhoccurend] : Dur√©e de l’√©v√©nement en Quart d’heure
    • [size] : Taille de l’√©v√©nement en Pixels
    • [qhstart] : Quart d’heure de d√©part
  • [nb] : Nombre d’√©v√©nements dans la journ√©e
  • [tsu] (Total size used) : Taille (en Pixel) totale utilis√©e par les √©v√©nements

Voil√† pour le tableau √† obtenir, je d√©taillerai l’utilit√© de chacun de ces √©l√©ments quand nous traiterons la construction visuelle du Calendrier. Maintenant,¬†attelons¬†nous √† la construction de ce fameux tableau. Voici comment nous allons proc√©der :

  1. L’Action (la fonction) re√ßoit en¬†param√®tre¬†la date d’une journ√©e (Sous forme de 3¬†param√®tres¬†d,m et y). si aucune date n’est envoy√©e, on prend la date actuelle.
  2. On construit le timestamp de cette date qu’on envoie √† une fonction.
  3. La fonction nous retourne le timestamp de départ (0h00) du premier jour de la semaine contenant la journée.
  4. Nous calculons le timestamp de fin de la semaine.
  5. Nous récupérons les événements dont la date de début est supérieure ou égale à celle du début de la semaine cible et dont la date de fin est inférieure ou égale à celle de fin de la semaine.
  6. Nous construisons un premier tableau, contenant les en-têtes qui serviront aux th du table
    de notre Calendrier.
  7. On construit notre tableau de données (structure vue précédemment).
  8. On envoie les deux tableaux à notre vue.

Allez hop, on met les mains dans le cambouis, on attaque le code en lui-même !

- Réception des paramètres et envoie à la fonction :

function indexAction() {
       /*calcul du timestamp équivalent à 7 jours (moins 1 sec, pour ne pas déborder sur la semaine suivate)
        $sept_jours=(7*24*60*60)-1;

        if(!$this->_request->getParam('d')) {
            $jour=date("d",time());
            $mois=date("m",time());
            $an=date("Y",time());
        }else {
            $jour=$this->_request->getParam('d');
            $mois=$this->_request->getParam('m');
            $an=$this->_request->getParam('y');
        }

        $timestamp_concerne=mktime(0, 0, 0, $mois, $jour, $an);
        $semaine_debut = $this->find_first_day_of_week($timestamp_concerne);

Comme vous le voyez, si je n’ai pas re√ßu de param√®tres sp√©cifiant la volont√© d’afficher une autre semaine que celle actuelle, je prend la date actuelle. Puis j’envoie le tout √† ma fonction.

- Récupérer le début de la semaine

Pour r√©cup√©rer le d√©but de la semaine, j’ai construit une petite fonction.

</span>
<pre>function find_first_day_of_week($timestamp, $start_of_week='monday') {
    $day=date('D',$target_week);
    switch(strtoupper($day)) {
        case "MON":$day_left=0;
            break;
        case "TUE":$day_left=1;
            break;
        case "WED":$day_left=2;
            break;
        case "THU":$day_left=3;
            break;
        case "FRI":$day_left=4;
            break;
        case "SAT":$day_left=5;
            break;
        case "SUN":$day_left=6;
            break;
        default:$day_left=0;
            break;
    }
    $one_day=24*60*60;
    $first_day = $target_week - ($one_day * $day_left);
    return $first_day;
}

La fonction reçoit en paramètre le timestamp,  elle prend le chiffre correspondant au jour, puis y retire de 0 à 6 jour pour obtenir le premier jour de la semaine.

- Calcul fin de la semaine et récupération des événements

Maintenant que nous avons le jour de début de la semaine, nous allons pouvoir trouver celui de fin et ainsi récupérer tous les événement compris dans cet espace temps. Mais voyons ceci avant :

$semaine_fin = $semaine_debut + $sept_jours;
$this->view->next_week=$semaine_debut+(7*24*60*60);
$this->view->next_week_start=date("d/m/Y",$semaine_debut+(7*24*60*60));
$this->view->next_week_end=date("d/m/Y",$semaine_debut+(7*24*60*60)+(7*24*60*60)-1);

$this->view->bef_week=$semaine_debut-(7*24*60*60);
$this->view->bef_week_start=date("d/m/Y",$semaine_debut-(7*24*60*60));
$this->view->bef_week_end=date("d/m/Y",$semaine_debut-1);
$this->view->semaine_debut=$semaine_debut;
$this->view->semaine_fin=$semaine_fin;

Comme vous le voyez, je r√©cup√®re la fin de ma semaine et j’envoie √† ma vue une s√©rie de variables. Ces derni√®res serviront en fait √† construire des liens renvoyant vers cette m√™me page pour acc√©der √† la semaine suivante et √† la semaine pr√©c√©dente.
Maintenant, la requête pour obtenir les événements :

/*ma table evenement */
$table_evenement = new Admin_Model_Db_Evenement();
$select = $table_evenement->select()
    ->where('evenement_debut BETWEEN '.$semaine_debut.' AND '.$semaine_fin)
    ->order('evenement_debut');

$row = $table_evenement->fetchAll($select);

Je récupère tous mes événements dans la variable $row.

- Construction des en-têtes du tableau

Pour les en-têtes, je prend juste le libellé du jour ainsi que la date en version numérique (jour/mois).

$thead=array(
    "1" => array("1" => "Lu. ".date("j/m",$semaine_debut),
    "2" => $semaine_debut
    ),
    "2" => array("1" => "Ma. ".date("j/m",$semaine_debut+(1*24*60*60)),
    "2" => $semaine_debut+(1*24*60*60)
    ),
    "3" => array("1" => "Me. ".date("j/m",$semaine_debut+(2*24*60*60)),
    "2" => $semaine_debut+(2*24*60*60)
    ),
    "4" => array("1" => "Je. ".date("j/m",$semaine_debut+(3*24*60*60)),
    "2" => $semaine_debut+(3*24*60*60)
    ),
    "5" => array("1" => "Ve. ".date("j/m",$semaine_debut+(4*24*60*60)),
    "2" => $semaine_debut+(4*24*60*60)
    ),
    "6" => array("1" => "Sa. ".date("j/m",$semaine_debut+(5*24*60*60)),
    "2" => $semaine_debut+(5*24*60*60)
    ),
    "7" => array("1" => "Di. ".date("j/m",$semaine_fin),
    "2" => $semaine_fin
    )
);
/*j'envoie le tout à ma vue*/
$this->view->thead=$thead;

- Construction du tableau de contenu

Nous y voil√† ! Maintenant nous allons construire notre tableau de donn√©es pour l’affichage de notre calendrier. Mais avant √ßa, nous allons construire, √©galement pour l’affichage, un autre tableau comprenant les horaires de la journ√©e (j’aurais tr√®s bien pu faire √ßa dans la vue, mais je le fais ici parce que je suis un ouf).

$jour_actuel_debut=mktime(0,0,0,date("m",time()),date("d",time()),date("Y",time()));
$horaires=array();
for($i=0;$i<24;$i++) {
    if($i<10)
        $horaire="0".$i."h00";
    else
        $horaire=$i."h00";

    $horaires[$i]=$horaire;
}
/*j'envoie à ma vue*/
$this->view->horaires=$horaires;

Allez, voyons de plus près la construction du tableau du contenu :

$table_content=array();
for($i=0;$i<7;$i++) {
    $debut_jour=$semaine_debut+($i*24*60*60);
    $fin_jour=$debut_jour+((1*24*60*60)-1);
    $info="";
    if($debut_jour==$jour_actuel_debut)
        $class="current_day";
    else
        $class="other_day";
    $titre="";
    $debut="";
    $fin="";
    $table_content[$i]['class']=$class;
    $table_content[$i]['day_start']=$debut_jour;
    $count=0;
    $total_size_used=0;
    foreach($row as $event) {
        if(($event->evenement_debut >= $debut_jour)
            &&($event->evenement_fin <= $fin_jour)) {
            $table_content[$i]['events'][$count]['id']=$event->evenement_id;
            $table_content[$i]['events'][$count]['titre']=$event->evenement_titre;
            $table_content[$i]['events'][$count]['lieu']=$event->evenement_lieu;
            $table_content[$i]['events'][$count]['debut']=$event->evenement_debut;
            $table_content[$i]['events'][$count]['fin']=$event->evenement_fin;
            $duree_secondes=$event->evenement_fin - $event->evenement_debut;
            $duree_heure=$duree_secondes/60/60;
            $duree_quart_heure=$duree_heure*4;
            $table_content[$i]['events'][$count]['qhoccured']=ceil($duree_quart_heure);
            $size=ceil($duree_quart_heure)*10;//10px par quart d'heure pour l'affichage
            $table_content[$i]['events'][$count]['size']=$size;
$quart_heure_journee=(date('H',$event->evenement_debut)*4)+(date('i',$event->evenement_debut)/15);
            $table_content[$i]['events'][$count]['qhstart']=ceil($quart_heure_journee);
            $count+=1;
            $total_size_used+=$size;
        }
    }
    $table_content[$i]["nb"]=$count;
    $table_content[$i]["tsu"]=$total_size_used;
}
$this->view->table_content=$table_content;

Dl√©nond√©diou ! Qu’est ce que c’est que ce truc ? Allez, d√©cortiquons un peu le tout.

$table_content=array();
for($i=0;$i<7;$i++) {
    $debut_jour=$semaine_debut+($i*24*60*60);
    $fin_jour=$debut_jour+((1*24*60*60)-1);
    $info="";

    if($debut_jour==$jour_actuel_debut)
        $class="current_day";
    else
        $class="other_day";

    $titre="";
    $debut="";
    $fin="";

    $table_content[$i]['class']=$class;
    $table_content[$i]['day_start']=$debut_jour;

    $count=0;
    $total_size_used=0;

Au d√©part, j’instancie mon tableau, puis je boucle sur les jours de la semaine, de sorte que chaque entr√©e principale du tableau corresponde √† un jour de semaine (je n’aurais donc plus qu’√† cr√©er une colonne pour chaque entr√©e du tableau dans ma vue. Donc, pour chacun des jours, je r√©cup√®re le timestamp de d√©but et de fin de ce dernier. Si ce jour correspond au jour actuel, je le sp√©cifie en le stockant dans une sous-entr√©e class de l’entr√©e du jour. De m√™me, je stocke le timestamp du jour dans une autre sous-entr√©e day_start. Enfin, j’initialise mon compteur de nombre d’√©v√©nement et celui de la taille totale utilis√©e.

foreach($row as $event) {
    if(($event->evenement_debut >= $debut_jour)
        &&($event->evenement_fin <= $fin_jour)) {

        /*je remplis les informations */
        $table_content[$i]['events'][$count]['id']=$event->evenement_id;
        $table_content[$i]['events'][$count]['titre']=$event->evenement_titre;
        $table_content[$i]['events'][$count]['lieu']=$event->evenement_lieu;
        $table_content[$i]['events'][$count]['debut']=$event->evenement_debut;
        $table_content[$i]['events'][$count]['fin']=$event->evenement_fin;

        /* je stocke le nombre de quart d'heure */
        $duree_secondes=$event->evenement_fin - $event->evenement_debut;
        $duree_heure=$duree_secondes/60/60;
        $duree_quart_heure=$duree_heure*4;
        $table_content[$i]['events'][$count]['qhoccured']=ceil($duree_quart_heure);

        /* ceil() -> arrondir entier supérieur*/
        $size=ceil($duree_quart_heure)*10;//10px par quart d'heure pour l'affichage
        /*je stocke la taille en pixel de l'événement */
        $table_content[$i]['events'][$count]['size']=$size;

        /*je recupere le quart d'heure de la journée correspondant
         * au départ de l'evenement */
        $quart_heure_journee=(date('H',$event->evenement_debut)*4)+(date('i',$event->evenement_debut)/15);
        $table_content[$i]['events'][$count]['qhstart']=ceil($quart_heure_journee);

        /* j'incrémente mes compteurs*/
        $count+=1;
        $total_size_used+=$size;
    }
}

Ensuite, je boucle sur mes √©v√©nements. Pour chacun d’entre eux, je regarde si il se trouve dans l’espace temps cible. Si il s’y trouve, je stocke un certain nombre d’informations qui serviront plus tard, ainsi que le nombre de quart d’heure de la dur√©e de l’√©v√©nement, sa taille en pixel, le quart d’heure de d√©part, puis j’incr√©mente mes compteurs.

$table_content[$i]["nb"]=$count;
$table_content[$i]["tsu"]=$total_size_used;

Finalement, je stocke dans l’entr√©e principale du tableau en cours (la journ√©e), le nombre d’√©v√©nements s’y trouvant ainsi que la taille en pixel qu’ils occupent √† eux tous.

$this->view->table_content=$table_content;

Puis j’envoie le tout √† ma vue.
Tadaaaam ! Voil√†, c’en est fini de la partie Php ! Je vous invite fortement √† aller vous servir un caf√©, manger un truc, fumer une clope, peu importe, car c’est loin d’√™tre termin√© ! (Je dois avouer que je prend un certain plaisir √† vous torturer avec ce tutoriel). Allez, on envoie la partie traitant de la vue !

√Čtape¬†4. La partie VUE

Maintenant que nous avons g√©n√©r√© toutes les informations¬†n√©cessaires, nous allons mettre en place l’affichage de notre calendrier.

<div id="gen_new_content" title="Nouvel événement">
    <form action="">
        <label class="label_evenement" for="new_event_title">Objet : </label><input type="text" class="lab" name="new_event_title" id="new_event_title" /><br />
        <label class="label_evenement" for="new_event_lieu">Lieu : </label><input type="text" class="lab" name="new_event_lieu" id="new_event_lieu" />
    </form>
</div>
<div id="create_event"></div>
<div id="ajax_load"></div>
<div id="dialog" title="Suppression">Veuillez confirmer la suppression</div>

Dans un premier temps, nous cr√©ons des emplacements qui vont servir √† g√©n√©rer du contenu via le Javascrit (ce que nous verrons dans l’√©tape 5, je ne d√©taille donc pas ceci pour l’instant).

<div id="switcher_agenda_options">
    <div id="choix_plage_horaire">
        <?php
        $next_week_start=$this->next_week;
        $bef_week_start=$this->bef_week;

        $day_next=date('d',$next_week_start);
        $month_next=date('m',$next_week_start);
        $year_next=date('Y',$next_week_start);

        $day_bef=date('d',$bef_week_start);
        $month_bef=date('m',$bef_week_start);
        $year_bef=date('Y',$bef_week_start);
        ?>
        <a class="info" href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","d"=>"$day_bef","m"=>"$month_bef","y"=>"$year_bef"),'default','true'); ?>"><img src="<?php echo $this->baseUrl()."/public/themes/admin/img/icons/bef_week.png";?>" alt="before" /><span><?php echo $this->bef_week_start." au ".$this->bef_week_end; ?></span></a>
        <a class="info" href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","d"=>"$day_next","m"=>"$month_next","y"=>"$year_next"),'default','true'); ?>"><img src="<?php echo $this->baseUrl()."/public/themes/admin/img/icons/next_week.png";?>" alt="next" /><span><?php echo $this->next_week_start." au ".$this->next_week_end; ?></span></a>
        <span class="semaine_en_cours">Semaine du <?php echo date('d/m/Y',$this->semaine_debut)." au ".date('d/m/Y',$this->semaine_fin);?></span>

    </div>
    <div id="switcher_agenda">
        <span class="switcher_agenda_inside selected_switch_agenda"><a href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"index"),'default','true'); ?>">Semaine</a></span>
        <span class="switcher_agenda_inside"><a href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"journee"),'default','true'); ?>">Journée</a></span>
        <span class="switcher_agenda_inside"><a href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"planning"),'default','true'); ?>">Planning</a></span>
    </div>
</div>

Nous mettons ensuite en place ce que j’appelle le switcher agenda. Il s’agit en fait de liens regroup√©s dans deux parties. La premi√®re partie comprend un lien vers la semaine suivante ainsi que la pr√©c√©dente. L’autre partie comprend des liens vers la visualisation type Journ√©e et la visualisation type Planning. Ces deux visualisations ne sont pas trait√©es dans ce tutoriel, je ne vais pas non plus tout vous donner :)


<div id="calendrier">
<table id="calendar_table">
<thead>
<tr>
<th></th>
<?php foreach($this->thead as $thead):
$day_link=date('d',$thead[2]);
$month_link=date('m',$thead[2]);
$year_link=date('Y',$thead[2]);
$class_th="";
if(mktime(0,0,0,$month_link,$day_link,$year_link) == mktime(0,0,0,date('m',time()),date('d',time()),date('Y',time())))
$class_th=" style='color: #D96666;font-weight:bolder;' ";
?>
<th><a <?php echo $class_th;?> href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"journee","d"=>"$day_link","m"=>"$month_link","y"=>"$year_link"),'default','true'); ?>"><?php echo $thead[1];?></a></th>
<?php endforeach; ?>
</tr>
</thead>
<div id="calendrier">    <table id="calendar_table">        <thead>            <tr>                <th></th>                <?php foreach($this->thead as $thead):                    $day_link=date('d',$thead[2]);                    $month_link=date('m',$thead[2]);                    $year_link=date('Y',$thead[2]);
$class_th="";
if(mktime(0,0,0,$month_link,$day_link,$year_link) == mktime(0,0,0,date('m',time()),date('d',time()),date('Y',time())))                        $class_th=" style='color: #D96666;font-weight:bolder;' ";                    ?>                <th><a <?php echo $class_th;?> href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"journee","d"=>"$day_link","m"=>"$month_link","y"=>"$year_link"),'default','true'); ?>"><?php echo $thead[1];?></a></th>                <?php endforeach; ?>            </tr>        </thead>

Nous attaquons maintenant la mise en place du calendrier en lui-m√™me. Nous cr√©ons d’abord son emplacement, puis le tableau. Nous mettons en place la head du tableau via notre array() de th pass√© pr√©alablement √† notre vue (voir Partie Contr√īleur). Au passage, vous remarquerez que j’applique un style diff√©rent au th si il correspond √† date actuelle.

<tbody>
    <tr>
        <td class="info_horaires">
            <?php foreach($this->horaires as $horaire):?>

                <?php echo "<div class='info_horaires_content'>".$horaire."</div>"; ?>
            <?php endforeach; ?>
        </td>

J’ouvre le corps de mon tableau ainsi qu’une premi√®re et unique ligne. Dans la premi√®re colonne, j’utilise le array() pass√© √† ma vue pour afficher les diff√©rentes plages horaires. Nous pouvons maintenant passer √† l’affichage des √©v√©nements !

for($i=0;$i<$content['nb'];$i++) {
            $content_current_debut=$content['events'][$i]['debut'];
            $content_current_fin=$content['events'][$i]['fin'];
            $content_current_size=$content['events'][$i]['size'];
            $content_current_id=$content['events'][$i]['id'];
            $padding_top=$content['events'][$i]['qhstart']*10;

            echo'
<div class="calendar_event" id="'.$content_current_id.'" style="height:'.$content_current_size.'px; margin-top:'.$padding_top.'px;">
<div class="calendar_event_date" id="'.$content_current_id.'_date" >
<span id="'.$content_current_id.'_date_debut_heure">'.date('H',$content_current_debut).'</span>:
<span id="'.$content_current_id.'_date_debut_minute">'.date('i',$content_current_debut).'</span> -
<span id="'.$content_current_id.'_date_fin_heure">'.date('H',$content_current_fin).'</span>:
<span id="'.$content_current_id.'_date_fin_minute">'.date('i',$content_current_fin).'</span>
</div>
<div class="calendar_event_title" id="'.$content_current_id.'_title">'.$content['events'][$i]['titre'].'</div>
<div class="calendar_event_lieu" id="'.$content_current_id.'_lieu" style="display:none;">'.$content['events'][$i]['lieu'].'</div>
</div>';
            //$qh=intval( $content['events'][$i]['qhstart'] + $content['events'][$i]['qhoccured'] -1);
        }

Je boucle donc sur chacune des entr√©es de mon array() regroupant les journ√©es¬†auxquelles¬†sont associ√©s les √©v√©nements (voir partie Contr√īleur). Pour chaque journ√©e, je cr√©e donc une nouvelle colonne. Je lui affecte sa classe css (d√©finie dans le contr√īleur, si il s’agit d’un jour normal ou du jour actuel). Je boucle ensuite sur les √©v√©nements contenus dans la journ√©e. ¬†Pour chacun d’entre eux, je leur applique une height et un margin-top en css. Je vous invite √† aller jeter un coup d’oeil au css de la demo, vous verrez que les √©v√©nements sont en absolute. A titre indicatif, voici le css des √©v√©nements :

div.calendar_event{
    background-color:rgb(217, 102, 102);
    color:#fff;
    font-weight:bolder;
    font-size:0.8em;
    position:absolute;
    text-align:center;
}

Il ne me reste plus qu’√† fermer ma ligne et mon tableau.

            </tr>
        </tbody>
    </table>
</div>

Et voil√† pour l’√©tape traitant de la vue. Le rendu est maintenant actif, on voit nos magnifique petits √©v√©nements mais on ne peut pas encore les bouger. C’est justement ce que nous allons voir dans l’√©tape suivante.

Etape 5. La partie AJAX

Alors, avant d’attaquer le javascript en lui-m√™me, nous allons devoir nous retaper un peu de php. En effet, pour enregistrer les modifications effectu√©es sur notre calendrier, nous allons devoir charger des fonctions php dans des balises Html (De l’ajax quoi). Nous allons donc tout d’abord cr√©er un contr√īleur qui comprendra toutes les fonctions que nous appellerons par la suite.

class AjaxController extends Zend_Controller_Action {

	function init() {
		$layout = Zend_Layout::getMvcInstance();
		$layout->disableLayout();
	}

Je met donc en place mon contr√īleur. Dans le init(), je desactive le layout pour √©viter les doublons dans le #ajax_load du design du site. Je vais en effet, apr√®s ex√©cution d’une fonction, afficher un petit message pour indiquer que tout s’est bien pass√© (comme vous l’avez surement vu dans la d√©mo, le cadre au fond jaune). Voyons maintenant les diff√©rentes Actions de mon contr√īleur.

function evenementAction() {
        $heure_debut = $this->_request->getParam('d');
        $heure_fin = $this->_request->getParam('f');
        $id = $this->_request->getParam('id');

        $lesEvents = new Admin_Model_Db_Evenement();
        $row = $lesEvents->fetchRow('evenement_id='.$id);

        $debut = mktime(date('H', $heure_debut), date('i', $heure_debut), 0, date('m', $row->evenement_debut), date('d', $row->evenement_debut), date('Y', $row->evenement_debut));

        $fin = mktime(date('H', $heure_fin), date('i', $heure_fin), 0, date('m', $row->evenement_fin), date('d', $row->evenement_fin), date('Y', $row->evenement_fin));

        $row->evenement_debut = $debut;
        $row->evenement_fin = $fin;
        $row->save();

        $this->view->info = "Modifications enregistrées.";
}

L’Action evenement sera appel√©e quand un √©v√©nement sera d√©plac√©. Elle re√ßoit en param√®tre la nouvelle heure de d√©but et la nouvelle heure de fin, converti le tout en timestamp et enregistre le tout.

function evenementresizeAction() {
        $duree = $this->_request->getParam('dur');
        $id = $this->_request->getParam('id');

        $lesEvents = new Admin_Model_Db_Evenement();
        $row = $lesEvents->fetchRow('evenement_id='.$id);
        $row->evenement_fin = $row->evenement_debut + $duree;
        $row->save();

        $this->view->info = "Horaire de fin modifiée.";
}

evenementresize g√®re, comme son nom l’indique, le redimensionnement d’un √©v√©nement (qui ne pourra √™tre redimensionn√© que par le bas). Elle re√ßoit donc la nouvelle dur√©e, calcule la nouvelle heure de fin et enregistre le tout.

function evenementcreationAction() {
        $evenement = new Admin_Model_Db_Evenement();
        $row = $evenement->createRow();
        $start = $this->_request->getParam('ds');
        $end = $this->_request->getParam('de');

        $row->evenement_titre = "(Sans titre)";
        $row->evenement_contenu = "";
        $row->evenement_lieu = "(Inconnu)";
        $row->evenement_debut = $start;
        $row->evenement_fin = $end;
        $idEve = $row->save();

        $this->view->info = $idEve;
}

evenementcreation crée un événement et nous renvoie son id (que nous récupérerons pour générer le rendu visuel).

function specifyeventAction() {
        $titre = $this->_request->getParam('titre');
        $lieu = $this->_request->getParam('lieu');
        $id = $this->_request->getParam('id');

        $lesEvents = new Admin_Model_Db_Evenement();
        $row = $lesEvents->fetchRow('evenement_id='.$id);
        if (! empty($titre))
                $row->evenement_titre = $titre;
        if (! empty($lieu))
                $row->evenement_lieu = $lieu;
        $row->save();
        $this->view->info = "événement enregistré.";
}

Quand nous allons cr√©er double-cliquer dans un espace vide, cela cr√©era automatiquement un √©v√©nement (gr√Ęce √† l’action vu juste avant). Ensuite une alert javascript personnalis√©e appara√ģtra nous permettant de sp√©cifier un nouveau nom et lieu pour notre √©v√©nement.

function supprimereventAction() {
        $id = $this->_request->getParam('id');
        $evenement = new Admin_Model_Db_Evenement();
        $evenement->delete('evenement_id='.$id);
        $this->view->info = "événement supprimé.";
}

Et enfin, la suppression de notre événement.
N’oubliez pas que pour chacune de ces actions, il vous faudra cr√©er la vue associ√©e. Voici comment j’ai code chacune des vues associ√©es √† l’AjaxController (De toutes fa√ßons, vous les avez dans les sources √† la fin du tutoriel).

<?php if(isset($this->error)) echo $this->error;  ?>
<?php if(isset($this->info)) echo "<div id='info_modification_ini'>".$this->info."</div>";  ?>

Voil√†, maintenant nous pouvons enfin attaquer le javascript en lui-m√™me ! Nous allons commencer par cr√©er une petite fonction qui retournera l’url de base. J’ai fait un petit article l√† dessus ya pas longtemps d’ailleurs. Vous pouvez tr√®s bien sp√©cifier l’adresse de votre site manuellement √† la place de cette fonction.

function getBaseURL() {
    var url = location.href;
    var baseURL = url.substring(0, url.indexOf('/', 14));

    if (baseURL.indexOf('http://localhost') != -1) {
        var pathname = location.pathname;
        var index1 = url.indexOf(pathname);
        var index2 = url.indexOf("/", index1 + 1);
        var baseLocalUrl = url.substr(0, index2);

        return baseLocalUrl;
    }
    else {
        return baseURL;
    }

}

En local √ßa marche niquel, √† v√©rifier en ligne car je n’ai pas pu tester (j’ai du la modifier par rapport √† la mienne). Ensuite, nous allons √©crire quelques lignes qui sont nous permettre d’adapter la taille des √©v√©nements proportionnellemnt √† la taille des td :

/* Taille des events */
var td_width=$(".calendar_td").width();
$(".calendar_event").css({
    "width" : td_width*0.85,
    "margin-left" : (td_width-(td_width*0.85))/2
});

Encore quelques touches de design avant d’attaquer la programmation des √©v√©nements, histoire d’avoir des beaux arrondis :) (J’utilise jQuery Corner)

/*------------------   ARRONDIS  --------------------*/
$('.lab_form').corner();
$('.identifiant').corner("round 9px");
$('.input_form').corner();
$('.form_list').corner();
$('.switcher_content_content').corner("left");
$('.calendar_event').corner("cc:#fff");
$('.calendar_event_date').corner("top cc:#fff");
$(".switcher_agenda_inside").corner("top");
$("div#select_agenda").corner("top");
$(".select_agenda_selector").corner("round 4px");
});

Et voilà, nous sommes maintenant prêts à attaquer le jQuery en lui-même. On va commencer par le Draggable(), permettant le déplacement des éléments.

/*  Déplacement event */
$(".calendar_event").draggable({
    containment: "parent",
    grid: [10, 10],
    delay: 100,
    drag: function(event, ui) {
        /*placement de l'événement*/
        /*changement affichage des horaires*/
    },
    stop: function(event, ui) {
        /*placement de l'evenement*/
        /*changement affichage horaire*/
        /*Enregistrement des modifications */
    }
});

D’abord un petit aper√ßu sans le code associ√© au drag et au stop. On voit plusieurs choses, la premi√®re est que l’√©v√©nement ne peut √™tre d√©plac√© que dans son parent (le TD). Ensuite il ne peut se d√©placer que de 10px en 10px (de Quart d’heure en quart d’heure donc) et qu’il y a un d√©lai de 0.1 sec avant de commencer le drag. Le d√©lai est l√† car si je n’en met pas, parfois le navigateur croit que je viens d’effectuer un Click au lieu du commencement d’un Drag. Nous allons maintenant voir le code associ√© au drag et au stop.

drag: function(event, ui) {
    var object_drop = $(this);
    var object_position=object_drop.position();
    var this_position=$(this).parent().position();
    current_position=object_position.top - this_position.top - 1;

    /*placement de l'evenement*/
    var marg_css=object_drop.css("margin-top");
    var marg_css_value=parseInt(marg_css.replace(".px",""));
    var margin_top=marg_css_value+current_position;
    if(margin_top<0)margin_top=0;
    margin_top=parseInt(margin_top);

    /*changement affichage horaire*/
    var id_event=object_drop.attr("id");

    var depart_en_sec=(((margin_top/10)/4-1)*60)*60;
    var depart_en_millisec=depart_en_sec*1000;
    var height_css=object_drop.css("height");
    var height_css_value=parseInt(height_css.replace(".px",""));
    var duree_en_sec=(((height_css_value/10)/4)*60)*60;
    var duree_en_millisec=duree_en_sec*1000;
    var fin_en_sec=depart_en_sec + duree_en_sec;
    var fin_en_millisec=depart_en_millisec + duree_en_millisec;
    nouvelle_heure_depart = new Date();
    nouvelle_heure_depart.setTime(depart_en_millisec);
    nouvelle_heure_fin = new Date();
    nouvelle_heure_fin.setTime(fin_en_millisec);
    $("#"+id_event+"_date_debut_heure").html(nouvelle_heure_depart.getHours());
    $("#"+id_event+"_date_debut_minute").html(nouvelle_heure_depart.getMinutes());
    $("#"+id_event+"_date_fin_heure").html(nouvelle_heure_fin.getHours());
    $("#"+id_event+"_date_fin_minute").html(nouvelle_heure_fin.getMinutes());
},

Voici comment √ßa se passe : Je r√©cup√®re la position de mon objet ainsi que celle de son parent (le TD). Je calcule sa position avec ces informations, suite √† quoi je calcule sa nouvelle plage horaire en r√©cup√©rant l’ancienne et j’applique la nouvelle horaire. Je n’oublie bien sur pas de modifier le margin-top.

stop: function(event, ui) {
    var object_drop = $(this);
    var object_position=object_drop.position();
    var this_position=$(this).parent().position();
    current_position=object_position.top - this_position.top - 1;

    /*placement de l'evenement*/
    var marg_css=object_drop.css("margin-top");
    var marg_css_value=parseInt(marg_css.replace(".px",""));
    var margin_top=marg_css_value+current_position;
    if(margin_top<0)margin_top=0;
    margin_top=parseInt(margin_top);

    /*changement affichage horaire*/
    var id_event=object_drop.attr("id");

    var depart_en_sec=(((margin_top/10)/4-1)*60)*60;
    var depart_en_millisec=depart_en_sec*1000;
    var height_css=object_drop.css("height");
    var height_css_value=parseInt(height_css.replace(".px",""));
    var duree_en_sec=(((height_css_value/10)/4)*60)*60;
    var duree_en_millisec=duree_en_sec*1000;
    var fin_en_sec=depart_en_sec + duree_en_sec;
    var fin_en_millisec=depart_en_millisec + duree_en_millisec;
    nouvelle_heure_depart = new Date();
    nouvelle_heure_depart.setTime(depart_en_millisec);
    nouvelle_heure_fin = new Date();
    nouvelle_heure_fin.setTime(fin_en_millisec);
    $("#"+id_event+"_date_debut_heure").html(nouvelle_heure_depart.getHours());
    $("#"+id_event+"_date_debut_minute").html(nouvelle_heure_depart.getMinutes());
    $("#"+id_event+"_date_fin_heure").html(nouvelle_heure_fin.getHours());
    $("#"+id_event+"_date_fin_minute").html(nouvelle_heure_fin.getMinutes());
    $("#ajax_load").load(getBaseURL()+"/admin/ajax/evenement/id/"+id_event+"/d/"+depart_en_sec+"/f/"+fin_en_sec);
    $("#ajax_load").html("Modifications enregistr&eacute;es.");
}

Pour le stop, le principe est exactement le m√™me, sauf qu’en plus je vais charger mon action evenement de mon AjaxController en lui passant les param√®tres qui vont bien. Notre √©v√©nement peut donc maintenant √™tre d√©plac√© et chaque d√©placement engendre une modification de sa place horaire qui est enregistr√©e dans la base de donn√©es.
Nous allons maintenant nous attaquer au redimensionnement de l’√©v√©nement :

$(".calendar_event").resizable({
    handles: 's',
    grid: [0, 10],
    stop: function(event, ui) {
        var object_drop = $(this);
        var id_event=object_drop.attr("id");
        var height_css=object_drop.css("height");
        var height_css_value=parseInt(height_css.replace(".px",""));
        var duree_en_sec=(((height_css_value/10)/4)*60)*60;
        $("#ajax_load").load(getBaseURL()+"/admin/ajax/evenementresize/id/"+id_event+"/dur/"+duree_en_sec);
    },
    resize: function(event,ui) {
        var object_drop = $(this);
        var id_event=object_drop.attr("id");
        var heure_depart=parseInt($("#"+id_event+"_date_debut_heure").html());
        var min_depart=parseInt($("#"+id_event+"_date_debut_minute").html());
        var heure_ref = new Date();
        heure_ref.setHours(heure_depart, min_depart, 0, 0);
        var timestamp=heure_ref.getTime();
        var height_css=object_drop.css("height");
        var height_css_value=parseInt(height_css.replace(".px",""));
        var duree_en_milli=((((height_css_value/10)/4)*60)*60)*1000;

        var new_heure = new Date();
        new_heure.setTime(timestamp+duree_en_milli);

        $("#"+id_event+"_date_fin_heure").html(new_heure.getHours());
        $("#"+id_event+"_date_fin_minute").html(new_heure.getMinutes());
    }
});

Il n’y a rien de bien compliqu√© dans le redimensionnement. On voit juste que dans les param√®tres de resizable je sp√©cifie que ce dernier ne peut √™tre redimensionn√© que par le bas (handles : 's'), et que par “pic” de 10 px (soit 15 minutes). Ensuite le principe est exactement le m√™me que pour le d√©placement sauf que cette fois-ci, je r√©cup√®re la propri√©t√© height et non sa position.
Maintenant, le dernier point avant la cr√©ation : l’affichage de l’alert permettant de supprimer, modifier, ou voir les d√©tails de l’√©v√©nement.

/*info event*/
$(".calendar_event").click(function(e){
    var object_clicked = $(this);
    var id_event=object_clicked.attr("id");

    $("#dialog").dialog({
        bgiframe: true,
        resizable: true,
        height:140,
        width:500,
        modal: true,
        overlay: {
            backgroundColor: '#000',
            opacity: 0.5
        },
        beforeclose: function(event, ui) {
            $(this).dialog('destroy');
        },
        open: function(event, ui) {
            var heure_depart=$("#"+id_event+"_date_debut_heure").html();
            var min_depart=$("#"+id_event+"_date_debut_minute").html();
            var heure_fin=$("#"+id_event+"_date_fin_heure").html();
            var min_fin=$("#"+id_event+"_date_fin_minute").html();
            var titre_eve=$("#"+id_event+"_title").html();
            var lieu_eve=$("#"+id_event+"_lieu").html();

            var contenu = "<p>";
            contenu += "<b>Durée : </b>"+heure_depart+":"+min_depart+" à ";
            contenu += heure_fin+":"+min_fin+"<br />";
            contenu += "<b>Lieu : </b>"+lieu_eve+"<br />";
            contenu += "</p>";
            $("#ui-dialog-title-dialog").html(titre_eve);
            $("#dialog").html(contenu);
        },
        buttons: {
            'Voir détails': function() {
                $(this).dialog('destroy');
                var url_details=getBaseURL()+"/admin/evenements/voir/id/"+id_event;
                $(location).attr('href',url_details);
            },
            'Modifier': function() {
                $(this).dialog('destroy');
            },
            'Supprimer': function() {
                $(this).html("Veuillez Confirmer la suppression");
                $("#dialog").dialog('destroy');
                $("#dialog").dialog({
                    bgiframe: true,
                    resizable: true,
                    height:140,
                    modal: true,
                    beforeclose: function(event, ui) {
                        $(this).dialog('destroy');
                    },
                    buttons: {
                        'Supprimer': function() {
                            $(this).dialog('destroy');
                            $("#ajax_load").load(getBaseURL()+"/admin/ajax/supprimerevent/id/"+id_event);
                            $("#"+id_event).hide("highlight",{
                                direction: "vertical",
                                color: "#A60000"
                            },2000);
                        },
                        'Annuler': function() {
                            $(this).dialog('destroy');
                        }
                    }
                });

            }
        }

    });
});

Alors, j’utilise en fait ici un Widget de jQuery : le Dialog. C’est pour cela qu’au d√©but de l’√©tape 4 (la Vue), nous avons cr√©√© un div #Dialog. En fait, le contenu va √™tre appel√© par jQuery pour avoir un rendu super chouette (qui change des alert on ne peut plus moche). Ici je personnalise donc cette fonction permettant d’afficher le Dialog. Quand je clique sur l’objet, la premi√®re chose que je fais est de r√©cup√©rer l’id de l’√©v√©nement ainsi qu’un pointeur vers mon objet, car si je fais $(this) dans les fonctions du Dialog, √ßa pointera vers le dialog et non vers mon Objet (j’ai cherch√© toute une aprem pourquoi j’arrivais pas √† le r√©cup√©rer…).
Je cr√©e ensuite mon Dialog, qui m’affiche le contenu dessin√© en html dans la vue. Modifier et Voir sont ici vides, il faut juste y mettre une redirection vers la page d’affichage et vers la page de modification d’un √©v√©nement. Attention, vous voyez ici que j’ai mis des $(this).dialog('destroy'); partout. C’est parce que si je ne le met pas, quand je quitte un dialog, je ne peux pas en r√©ouvrir un autre, ce qui est assez embettant.
Pour la suppression, je transforme mon dialog en un autre dont le bouton Supprimer me charge en Ajax l’action supprimerevent.
Passons maintenant au plus gros du tutoriel, la cr√©ation d’un nouvel √©v√©nement via double-clique :

$(".calendar_td").dblclick(function(e){
    /*
    a- gif de chargement
    b- recuperation de la journée cible et de la position de depart
    c- creation de l'event et recuperation de son id
    d- ouverture d'un Dialog jQuery pour spécifier nom et lieu de l'event (si on clique sur annuler ça supprime l'event créé)
    e- creation d'un evenement (en html, pour la visualisation)
    f- application des effets de Drag, Resize et Click à l'event
    */
});

Le fait qu’on doive rappliquer les fonctions Draggable,Click et Resizable au nouvel √©v√©nement se justifie par le fait qu’il n’√©tait pas pr√©sent au chargement de la page. Le javascript se chargeant au chargement, il ne peut donc pas y √™tre associ√© et nous devons le refaire pour chaque √©v√©nement cr√©√©. Allez, voyons le tout plus en d√©tail :

a) Gif de chargement

$("#ajax_load").html('<p align="center"><img src="loading.gif" /></p>');

Je remplis mon div de chargement avec un Gif (non fourni dans les sources). Par la suite, je vais cr√©er un nouvel √©v√©nement dont je vais r√©cup√©rer l’id (retourn√© par la fonction) pour construire mon √©v√©nement visuellement. Cette fonction sera charg√©e de mani√®re synchrone et peut donc, selon l’√©tat de surcharge de la base de donn√©es, prendre jusqu’√† deux secondes. Le gif est l√† pour rassurer l’utilisateur que la page n’a pas plant√© et qu’il ne cherche donc pas √† spammer le tout de double-clic.
b) Récupération des informations

var position_choisie=e.pageY-$(this).position().top;

var to_round=Math.round(position_choisie);
var to_string=String(to_round);
var str_length=to_string.length-1;
var to_substr=to_string.substr(0,str_length);
var to_int=parseInt(to_substr);
if(!to_int){
    to_int=0;
}
var margin_top=to_int*10;

var depart_en_sec=(((margin_top/10)/4-1)*60)*60;
var depart_en_millisec=depart_en_sec*1000;
var height_css_value=parseInt(80);
var duree_en_sec=(((height_css_value/10)/4)*60)*60;
var duree_en_millisec=duree_en_sec*1000;
var fin_en_sec=depart_en_sec + duree_en_sec;
var fin_en_millisec=depart_en_millisec + duree_en_millisec;
nouvelle_heure_depart = new Date();
nouvelle_heure_depart.setTime(depart_en_millisec);
nouvelle_heure_fin = new Date();
nouvelle_heure_fin.setTime(fin_en_millisec);

var day_choose=(parseInt($(this).attr("id")));
var day_start=day_choose + ( nouvelle_heure_depart.getHours() * 60 * 60 ) + ( nouvelle_heure_depart.getMinutes() * 60 );
var day_end=day_choose + ( nouvelle_heure_fin.getHours() * 60 * 60 ) + ( nouvelle_heure_fin.getMinutes() * 60 );

Je r√©cup√®re donc la position, √† partir de laquelle je calcule l’heure de d√©part (j’y ajoute 2h, j’ai choisi qu’un √©v√©nement par d√©faut ferait 2h de dur√©e lors de sa cr√©ation). Ensuite je r√©cup√®re la journ√©e (donc le timestamp a √©t√© stock√© dans l’id de chaque TD, puis je charge ma fonction de mani√®re synchrone.

c) creation de l’event et recuperation de son id

/*creation de l'event dans la bdd*/
var url_create=getBaseURL()+"/admin/ajax/evenementcreation/ds/"+day_start+"/de/"+day_end;
var event_id = $.ajax({
    url: url_create,
    async: false
}).responseText;

d) Ouverture du Dialog

/*dialog de remplissage*/
$("#gen_new_content").dialog({
    bgiframe: true,
    resizable: true,
    height:200,
    width:500,
    modal: true,
    beforeclose: function(event, ui) {
        $(this).dialog('destroy');
        $("#new_event_title").val("");
        $("#new_event_lieu").val("");
    },
    buttons: {
        'Enregistrer': function() {
            $(this).dialog('destroy');
            var new_titre=$("#new_event_title").val();
            var new_lieu=$("#new_event_lieu").val();
            agenda_id=$("#agenda_id").val();
            if(new_titre!=""){
                $("#"+event_id+'_title').html(new_titre);
            }
            if(new_lieu!=""){
                $("#"+event_id+'_lieu').html(new_lieu);
            }
            new_ti=new_titre.replace(/ /gi,"&nbsp;");
            new_li=new_lieu.replace(/ /gi,"&nbsp;");
            $("#"+event_id).removeClass("select_agenda_red");
            class_color_agenda=$("#"+agenda_id+"_agenda_id").html();
            $("#"+event_id).addClass(class_color_agenda);
            $("#ajax_load").load(getBaseURL()+"/admin/ajax/specifyevent/id/"+event_id+"/titre/"+new_ti+"/lieu/"+new_li+"/ag/"+agenda_id);
            $("#ajax_load").html("Evenement cr&eacute&eacute..");
            $("#new_event_title").val("");
            $("#new_event_lieu").val("");
        },
        'Annuler': function() {
            $(this).dialog('destroy');
            $("#ajax_load").load(getBaseURL()+"/admin/ajax/supprimerevent/id/"+event_id);
            $("#ajax_load").html("Cr&eacute;ation de l'&eacute;v&eacute;nement annul&eacute;e.");
            $("#"+event_id).hide("highlight",{
                direction: "vertical",
                color: "#A60000"
            },1000);
            $("#new_event_title").val("");
            $("#new_event_lieu").val("");
        }

    }
});

Un dialog comme tous les autres. Il appelle l’action specifyevent en lui passant les param√®tres qui vont bien si on choisi de valider l’√©v√©nement, sinon il appelle supprimerevent si on choisir d’annuler.

e) Cr√©ation visuelle de l’√©v√©nement

$("#ajax_load").html("");
var event = $('<div></div>')
.appendTo($(this))
.attr('class','calendar_event select_agenda_red')
.attr('id',event_id)
.css({
    height:"80px",
    marginTop:margin_top+"px"
});

var event_date = $('<div></div>')
.appendTo(event)
.attr('class','calendar_event_date')
.attr('div',event_id+'_calendar_event_date');

var event_date_heure_debut = $('<span>'+nouvelle_heure_depart.getHours()+'</span>')
.appendTo(event_date)
.attr('id',event_id+'_date_debut_heure');

$('<span>:</span>')
.appendTo(event_date);

var event_date_minute_debut = $('<span>'+nouvelle_heure_depart.getMinutes()+'</span>')
.appendTo(event_date)
.attr('id',event_id+'_date_debut_minute');

$('<span> - </span>')
.appendTo(event_date);

var event_date_heure_fin = $('<span>'+nouvelle_heure_fin.getHours()+'</span>')
.appendTo(event_date)
.attr('id',event_id+'_date_fin_heure');

$('<span>:</span>')
.appendTo(event_date);

var event_date_heure_fin = $('<span>'+nouvelle_heure_fin.getMinutes()+'</span>')
.appendTo(event_date)
.attr('id',event_id+'_date_fin_minute');

var event_date = $('<div>(Sans titre)</div>')
.appendTo(event)
.attr('class','calendar_event_title')
.attr('id',event_id+'_title');

var event_date = $('<div>(Inconnu)</div>')
.appendTo(event)
.attr('class','calendar_event_lieu')
.attr('id',event_id+'_lieu');

event.corner();
$(".calendar_event_date").corner("top cc:#fff");
var td_width=$(".calendar_td").width();
event.css({
    "width" : td_width*0.85,
    "margin-left" : (td_width-(td_width*0.85))/2
});

Comme vous le voyez, je vide d’abord ma div #ajax_load pour retirer l’effet de chargement (car pour arriver √† cette √©tape, c’est forc√©ment que l’id a √©t√© retourn√© et que le Dialog est apparu). Ensuite il s’agit d’une construction toute b√™te de html, pas la peine de s’y attarder.
f) Application des effets sur l’objet.

event.draggable();
event.resizable();
event.click();

Je ne met pas le d√©tail des fonctions, car il s’agit d’un copi√©-coll√© des fonctions pr√©c√©dentes.

THE END

Et voil√†, ce tutoriel est enfin termin√© !¬†J’esp√®re¬†que personne n’est mort au cours de sa lecture, m√™me si je pense que la plupart pr√©f√©reront juste prendre les sources et se d√©merder pour comprendre.¬†Je vous met ci-dessous les sources finales, elles comprennent les contr√īleurs, les vues, le javascript et les backgrounds, √† vous d’arranger le tout si vous voulez l’int√©grer quelque part. Au fait, si j’ai vous voyez des choses qui, √†-priori, ne servent √† rien, c’est que j’ai oubli√© des les enlever. J’ai all√©g√© au maximum mon Calendrier pour vous le proposer sous forme de tutoriel.

CATÉGORIES :

Zend

, jQuery

    • Schmidt.J
    • March 27th, 2010

    Tr√®s bon tutoriel, m√™me si parfois c’est pas bien expliquer.

    • SFEZ
    • March 29th, 2010

    Certes, ce n’est pas l’endroit pour le dire mais je trouve ce site magnifique et tr√®s bien fait. Merci pour le TUTO du calendrier ca fait un moment que j’en cherche un dans le genre.

    • Lesny
    • March 30th, 2010

    merci :D

    • dimitri
    • April 8th, 2010

    tres bon tutoriel, mais j’aimerais avoir quelques infos plus p√©cifique : j’essaie d’integrer votre code avec Symfony, sans succes, auriez-vous d√©ja essayer cette manoeuvre ?@Lesny

    • Lesny
    • April 9th, 2010

    Bonjour Dimitri,
    D√©sol√© mais je n’ai jamais essay√© d’int√©grer mon code sous Symfony. De plus, je n’ai jamais travaill√© sous ce framework, je ne pourrais donc t’√™tre une grande aide sur ton probl√®me.

    En revanche, Symfony est il également basé sur un design pattern MVC ? je suppose que oui. Ne faisant ici appel à aucune fonction spécifique de Zend, je ne vois pas pourquoi ce dernier ne fonctionnerait pas sous Symfony.

    Quel est ton probl√®me exactement? Quelle est l’erreur g√©n√©r√©e ? Je t’aiderai dans la mesure du possible.

    • Ferdinand AMOI
    • April 15th, 2010

    Salut!
    Bon tuto sur le calendrier.
    Mais étant donné que vous utilisez la bibliothèque jquery, voici un plugin jquery pour le calendrier : http://arshaw.com/fullcalendar/
    Maintenant reste √† cr√©er une base de donn√©es et d’enregistrer les √©v√©nements dans le calendrier avec le json ou autre.
    facilement utilisable avec le zend framework et symfony.

    • Lesny
    • April 15th, 2010

    Ha, je ne connaissais pas ce plug in.
    Et dire que j’ai pass√© 2 jours √† coder comme un autiste pour d√©velopper le Calendrier ^^

    Mais pour maintenant, je continuerai d’utiliser le mien. C’est quand m√™me nettement plus la classe d’utiliser un outil qu’on a soi-m√™me d√©velopp√© :p

    • DrRevolte
    • May 4th, 2010

    Salut lesny,

    Pour t’avouer, je ne comprends pas ton tuto !

    pourquoi ne pas avoir donner le fichier php dans le .rar “source” ?

    et tout tes petits bouts de code en haut ce met tous dans le même fichier, qui est un fichier php ?

    Petit blocage pour ce bout de code :

    $table_evenement = new Admin_Model_Db_Evenement();

    la classe : Admin_Model_Db_Evenement (ou est-elle situé ?)

    Merci

    • Lesny
    • May 4th, 2010

    Bonjour DrRevolte,
    Je pense que ton probl√®me principal r√©side dans le fait que tu ne connaisse pas le design pattern “MVC” ou m√™me ce qu’est un design pattern.

    Ce calendrier a pour vocation d’√™tre directement impl√©ment√© dans une application bas√©e sous le ZendFramework, utilisant le design pattern MVC. (Controlleur -> Vue).

    Je ne vais pas ici t’expliquer comment tout ceci fonctionne, tu trouveras toutes ces informations sur le Web et m√™me sur notre site ;) Glenn nous a fait de super Tutos pour apprendre √† ma√ģtriser le Zend Framework. (A la conqu√™te de Zend Framework).

    Pour faire simple, les Controlleurs balancent le code aux vues.
    La classe Admin_Model_Db_Evenement n’est autre qu’une classe h√©ritant d’une classe native de ZF permettant la liaison directe avec la table Evenement de la base de donn√©es.

    Maintenant, libre √† toi de bidouiller ce code pour l’int√©grer dans un script proc√©dural, tu en fais ce que tu veux ^^
    Mais tu ne pourras pas le claquer tel qu’il est actuellement agenc√©, ce qui √©tait je pense, ton objectif.

    J’esp√®re avoir pu t’√©clairer.
    Charles.

    • DrRevolte
    • May 4th, 2010

    Bonjour,

    Merci de se renseignement rapide !

    vue que je dois cr√©er un agenda en ASP/Ajax ! Cela me sera impossible √©tant donn√© que Zend est un framework PHP, donc oui j’avais pas comprit au d√©but qu’on aurait obligatoirement besoin de ZF !

    Merci beaucoup de votre rapidité, je vais me tourner vers FullCalendar alors !

    Bonne Journée

    • ornythorink
    • May 17th, 2010

    Bonjour Nanane

    Du beau boulot ! ;)
    Ok pour les critiques sur le MVC ..bon. Mais j’ai a peu pr√®s la m√™me chose MVC (Symfony + ZF + un plugin de Rob Monie pour int√©grer le JSON de google agenda dans le calendrier). Le “plus” de ce que je viens de lire rapidement, c’est ton int√©gration rapide et l√©g√®re de la partie JSON/liaison √©v√®nement calendrier. C’est surement plus souple comme √ßa. En fait c’est dommage que je n’ai pas fait de recherche avant sur l’aspect coin arrondi sinon j’aurai s√Ľrement adopt√© directement ta solution.

    Félicitation et bon courage.

    • Lesny
    • May 17th, 2010

    Merci :)

  1. C’est Bon, J’aime votre site web!

    • Lesny
    • July 6th, 2010

    :p
    Heureux que ça te plaise ^^
    Dommage qu’en ce moment nous n’ayons pas le temps de le remettre au gout du jour.
    J’ai encore une tripot√©e de tutos √† pondre avec ce que j’ai fait ces 4 derniers mois, je prendrai du temps sous peu pour les r√©diger.

    • flashnet
    • July 6th, 2010

    Salut,

    Tu tuto est super, il va me permettre de reprendre des idées pour réaliser la même chose avec le framework scriptacoulous & prototype.

    Je vois qu’on ne peut pas d√©placer les rendez-vous √† droite ou √† gauche et je bloque sur le m√™me probl√®me, aurait-tu une solution ? Merci

    • Lesny
    • July 7th, 2010

    Salut Flashnet.
    Pour permettre le déplacement entre colonnes des Div, tu peux définir utiliser la fonction sortable() de jQuery.

    En revanche, j’ai une question :
    A moins d’y √™tre contraint du fait d’une structure l’utilisant, pourquoi utilises tu Proto/Scriptacu ? Les classes ne sont plus vraiment mises √† jour et les perspectives d’√©volution quasiment nulles. Autant utiliser jQuery qui regroupe actuellement une communaut√© de d√©veloppeurs bien plus importante que celle restant autour de prototype/scriptacu

    • Marcelinos
    • November 29th, 2010

    merci pour le tuto !

    c’est exactement ce que je cherche √† faire depuis longtemps
    mais je ne sais pas comment faire?

    Mais tout mon probl√®me est que je ne connais franchement rien sur le framework Jquery ! et donc je veux l’apprendre! !

    Alors ce serait tr√®s sympas de m’indiquer ou de me donner un bon cours sur √ßa !

    merci infiniment d’avance…

    Bravo pour ton tuto

  2. Merci pour ces infos, je vais √©plucher tout √ßa et m’en inspirer pour d√©velopper un calendrier avec Yii framework. A bient√īt!

    • devdevydev
    • July 19th, 2011

    Je d√©conseille ce planning,qui n’est pas du tout √©volutif et adaptable.
    Je recommande le JQuery weekcalendar, qui est tout a fait modifiable et adaptable

    • Guillaume
    • February 10th, 2012

    C’est vraiment super bien fait ton truc mais je ne peux pas l’utiliser car il n’y a pas les noms de fichiers dans lesquels tu places tes bouts de code. Soi-disant parce que c’est fait avec ton framework. Moi qui ne l’utilise pas, je suis bloqu√©. C’est bien dommage…

    • Netounet
    • April 16th, 2012

    Sympas ce tuto il m’a bien servi pour g√©rer mes plannings. J’ai tout de m√™me rencontr√© quelques soucis avec le draggable quand je voulais remonter un rdv, le margin-top me bloquait, pas trouv√© pk mais comme j’√©tais dans un layout js je me suis arrang√© en transf√©rant le margin-top sur du top √† l’initialisation de la grille et √ßa a r√©solu mon probl√®me.

    Autre suggestion : rajouter une petite m√©thode dans la r√©cup√©ration des infos horaires par rapport √† la position de l’√©l√©ment en ajustant la valeur r√©cup√©r√©e par rapport √† la grille pour √©viter d’avoir des horaires 10h03 …

    /**
    * fix top value to grid
    */
    get_fixed_top: function(subject, timeIntervalHeight) {
    var top = jQuery(subject).position().top – jQuery(subject).parent().position().top;
    top = top = timeIntervalHeight/2 ? timeIntervalHeight : 0);
    }

    Sachant que je ne travaille qu’avec du top dans mon cas je ne prend plus en compte le margin-top mais l’id√©e est l√†

    Autrement en rajoutant le droppable on peut facilement gérer le déplacement entre les jours

    Implémentation réalisée sur symfony2

    Merci

    PS : +1 pour le webdesgin :)

    • Netounet
    • April 16th, 2012

    Oula il m’a mang√© pas mal de code de mon comment voici une version escape

    /**
    * fix top value to grid
    */
    get_fixed_top: function(subject, timeIntervalHeight) {
    var top = jQuery(subject).position().top – jQuery(subject).parent().position().top;
    top = top <= 0 ? 0 : top;
    return parseInt(top / timeIntervalHeight) * timeIntervalHeight + parseInt(top % timeIntervalHeight >= timeIntervalHeight/2 ? timeIntervalHeight : 0);
    }

  3. Very interesting info !Perfect just what I was looking for!

    • major200322
    • February 27th, 2013

    j’est pas l‚Äôint√©grer dans les fichier de zend framework
    plz qui peux m’aider stp!!!!

    • motii
    • May 4th, 2013

    salut lesny
    j’ai trop bien aimer ton tutoriel, m√™me si malheureusement j’ai rien compris, je suis un √©tudiant en g√©nie indus et je veux bien concevoir un calendrier le m√™me que vous avez fait, c’est pour √ßa que j’aimerai tr√®s bien avoir votre mail afin de vous expliqu√© mon probl√®me, merci trop :)

  1. No trackbacks yet.