0. Introduction

Dans ce tutoriel je vais vous montrer comment exploiter la fonctionnalité d'extension de Codendi avec le nouveau système de tracker de la v 4.2 et son architecture en plugin.

Cet article va être un peu technique et va contenir du code PHP et des CSS. Si vous êtes d'accord avec ca ou si vous êtes curieux, alors on y va ;)


J'imagine que vous savez ce qu'est un rapport. Au cas où : dans un tracker, un rapport vous permet de rechercher des artefacts en fonction de critères. Jusqu'à maintenant, vous pouvez afficher les résultats de vos requêtes sous deux formes : les tableaux ou les graphiques.

http://blog.codendi.com/wp-content/uploads/2010/09/table.png

Exemple d'affichage de vos recherches dans les trackers sous forme de tableau


Le sujet de ce tuto est donc de créer une nouvelle façon d'afficher vos requêtes dans les trackers.

Peut-être que vous utilisez Codendi pour gérer vos « user stories » si vous êtes dans un environnement agile ? Jusqu'à maintenant vous ne pouvez pas voir vos artifacts sous forme de post-it pour ressembler à votre tableau de sprint. Trop triste pas vrai ? Je suis toujours déçu quand l'interface ne ressemble pas à la réalité.

Example of a kanban board

Exemple de tableau Kanban board, extrait d'un article sur Dr Bobbs


Il n'y a pas longtemps, j'ai découvert dans mon lecteur RSS un article de Christian Heilmann

sur « comment créer un effet post-it avec du CSS3 et de l'HTML5 ». On va voir si l'on peut l'implémenter dans Codendi. Prenez une version Codendi 4.2, relevez vos manches, c'est parti ! ;)

I. Créer un plugin

Tout d'abord, vous devez créer un plugin. Pour ça, vous pouvez copier un plugin existant, supprimer les éléments non utiles et le renommer. Ou encore mieux, utiliser le wizard pour la création de plugin.

 
Sélectionnez
$ cd /usr/share/codendi/plugins
$ ln -s ../codendi_tools/plugins/plugincreationwizard

Installer et activer ce plugin dans l'administration des plugins. Ensuite, utiliser le wizard (http://example.com/plugins/plugincreationwizard/) pour créer le squelette de votre nouveau plugin. Dans notre cas, on va l'appeler « tableau ». Activer l'espace web et les styles CSS mais pas besoins des db, mvc, cgi, etc Le code généré a été créé dans /usr/share/codendi/plugins/board/

Installez le, activez le>, même s'il ne fait rien pour l'instant.

II. Créer un nouvel "affichage"

Editer / usr / share / codendi /plugins/ board / include / boardPlugin.class.php pour ajouter des hooks. On ne va pas implémenter tous les hooks nécessaires puisqu'on ne veut pas importer de xml et qu'on n'a rien à stocker dans la base. On n'a que 10 minutes !

 
Sélectionnez
class boardPlugin extends Plugin {

    const RENDERER_TYPE = 'plugin_board';

    public function __construct($id) {
        parent::__construct($id);
        $this->_addHook('cssfile', 'cssFile', false);
        $this->_addHook('tracker_report_renderer_types' ,    'tracker_report_renderer_types',     false);
    }

    /**
     * This hook ask for types of report renderer
     *
     * @param array types Input/Output parameter. Expected format: $types['my_type'] => 'Label of the type'
     */
    public function tracker_report_renderer_types($params) {
        $params['types'][self::RENDERER_TYPE] = 'Board';
    }
    //...

Bien. Vous devriez pouvoir choisir "tableau" dans la liste déroulante des différents types d'affichages lors de la création d'un nouveau rapport comme ceci :

select


Maintenant, on ajoute des hooks supplémentaires pour créer un nouvel affichage :

 
Sélectionnez
//...

    public function __construct($id) {
        parent::__construct($id);
        $this->_addHook('cssfile', 'cssFile', false);
        $this->_addHook('tracker_report_renderer_types' ,    'tracker_report_renderer_types',     false);
        $this->_addHook('tracker_report_renderer_instance',  'tracker_report_renderer_instance',  false);
    }

    /**
     * This hook asks to create a new instance of a renderer
     *
     * @param array $params:
     *              mixed  'instance' Output parameter. must contain the new instance
     *              string 'type' the type of the new renderer
     *              array  'row' the base properties identifying the renderer (id, name, description, rank)
     *              Report 'report' the report
     *
     * @return void
     */
    public function tracker_report_renderer_instance($params) {
        if ($params['type'] == self::RENDERER_TYPE) {
            require_once('Board_Renderer.class.php');
            $params['instance'] = new Board_Renderer(
                $params['row']['id'],
                $params['report'],
                $params['row']['name'],
                $params['row']['description'],
                $params['row']['rank'],
                $this
            );
        }
    }

    //...

Et créer Board_Renderer.class.php (à côté de boardPlugin.class.php)

plugin = $plugin;
    }

    /**
     * Fetch content of the renderer
     * @param array $matching_ids
     * @param Request $request
     * @return string
     */
    public function fetch($matching_ids, $request) {
        //here will be the interesting part
        return '';
    }

    /*----- Implements below some abstract methods ----*/

    public function getIcon() {
                return '<img src="'. $this->plugin->getThemePath().'/images/renderer.png" />';
    }

    public function delete() {}

    public function getType() {
        return 'plugin_board';
    }

    public function processRequest(TrackerManager $tracker_manager, $request, $current_user) {
    }

    public function fetchWidget() {
        return '';
    }

    public function update() {
        return true;
    }   

    public function duplicate($from_renderer, $field_mapping) { }

    public function afterSaveObject($renderer) { }
}


Ouais  ! On peut créer notre nouvel affichage \o/

Maintenant, on va essayer d'afficher quelque chose d'intéressant. On va le faire avec la méthode php fetch. Un des paramètres est $matching_ids

C'est la liste des artefacts qui correspondent à votre recherche , séparés par des virgules. On va l'utiliser pour afficher vos artefacts. On remplace la méthode fetch par quelque chose de plus utile tel que :

 
Sélectionnez
public function fetch($matching_ids, $request) {
    $html = '';

    $total_rows = $matching_ids['id'] ? substr_count($matching_ids['id'], ',') + 1 : 0;
    if (!$total_rows) {
        return 'Nothing to display';
    }

    // Build a small sql query to fetch artifact titles (depends on tracker semantic)
    $sql = "SELECT A.id AS id, CVT.value AS title
            FROM tracker_artifact AS A
               LEFT JOIN (
                   tracker_changeset_value AS CV
                   INNER JOIN tracker_semantic_title as ST ON (CV.field_id = ST.field_id)
                   INNER JOIN tracker_changeset_value_text AS CVT ON (CV.id = CVT.changeset_value_id)
               ) ON (A.last_changeset_id = CV.changeset_id)
            WHERE A.id IN (". $matching_ids['id'] .")";
    $dao = new DataAccessObject();
    $html .= '<div class="tracker_renderer_board"><ul>';

    foreach ($dao->retrieve($sql) as $row) {

        $html .= '<li>';

        $html .= '<a href="/tracker/?aid='. $row['id'] .'">';

        $html .= '<p class="tracker_renderer_board_title">bug #'. $row['id'] .'</p>';

        $html .= '<p class="tracker_renderer_board_content"> '. $row['title'] .'</p>';

        $html .= '</a>';

        $html .= '</li>';

    }

    $html .= '</ul>';

    return $html;

}

III. Ajouter de la mise en forme

Vous pouvez aller voir votre résultat dans votre navigateur mais quelques règles CSS manquent. On fait un copier/coller et on adapte les CSS proposés par Christian Heilmann.

Editer plugins/ board /www/ themes /default/ css /style.css et ajouter les règles suivantes:

 
Sélectionnez
.tracker_renderer_board p {
  font-size:100%;
  font-weight:normal;
  margin: 0;
}
.tracker_renderer_board ul, .tracker_renderer_board li{
  list-style:none;
}
.tracker_renderer_board ul{
  overflow:hidden;
  padding:3em;
}
.tracker_renderer_board ul li a{
  text-decoration:none;
  color:#000;
  background:#ffc;
  display:block;
  height:10em;
  width:10em;
  padding:1em;
  -moz-box-shadow:5px 5px 7px rgba(33,33,33,1);
  -webkit-box-shadow: 5px 5px 7px rgba(33,33,33,.7);
  box-shadow: 5px 5px 7px rgba(33,33,33,.7);
  -moz-transition:-moz-transform .15s linear;
  -o-transition:-o-transform .15s linear;
  -webkit-transition:-webkit-transform .15s linear;
}
.tracker_renderer_board ul li{
  margin:1em;
  float:left;
}
.tracker_renderer_board ul li p.tracker_renderer_board_title {
  font-size:140%;
  font-weight:bold;
  padding-bottom:10px;
}
.tracker_renderer_board ul li p.tracker_renderer_board_content {
  font-family:"Reenie Beanie",arial,sans-serif;
  font-size:180%;
}
.tracker_renderer_board ul li a{
  -webkit-transform: rotate(-6deg);
  -o-transform: rotate(-6deg);
  -moz-transform:rotate(-6deg);
}
.tracker_renderer_board ul li:nth-child(even) a{
  -o-transform:rotate(4deg);
  -webkit-transform:rotate(4deg);
  -moz-transform:rotate(4deg);
  position:relative;
  top:5px;
/*  background:#cfc; */
}
.tracker_renderer_board ul li:nth-child(3n) a{
  -o-transform:rotate(-3deg);
  -webkit-transform:rotate(-3deg);
  -moz-transform:rotate(-3deg);
  position:relative;
  top:-5px;
/*  background:#ccf; */
}
.tracker_renderer_board ul li:nth-child(5n) a{
  -o-transform:rotate(5deg);
  -webkit-transform:rotate(5deg);
  -moz-transform:rotate(5deg);
  position:relative;
  top:-10px;
}
.tracker_renderer_board ul li a:hover,ul li a:focus{
  box-shadow:10px 10px 7px rgba(0,0,0,.7);
  -moz-box-shadow:10px 10px 7px rgba(0,0,0,.7);
  -webkit-box-shadow: 10px 10px 7px rgba(0,0,0,.7);
  -webkit-transform: scale(1.25);
  -moz-transform: scale(1.25);
  -o-transform: scale(1.25);
  position:relative;
  z-index:5;
}


Je vous laisse lire cet article pour plus de détails sur les règles CSS.

Une seule chose manque encore : la police ouverte Reenie Beanie . Ajouter simplement le tag associé dans le hook correspondant dans le plugin :

 
Sélectionnez

	  function cssFile($params) {

    // Only show the stylesheet if we're actually in the eve pages.

    // This stops styles inadvertently clashing with the main site.

    if (strpos($_SERVER['REQUEST_URI'], '/tracker/') === 0) {

        echo '<link rel="stylesheet" type="text/css" href="'.$this->getThemePath().'/css/style.css" />';

        echo '<link  href="http://fonts.googleapis.com/css?family=Reenie+Beanie:regular" rel="stylesheet" type="text/css" />';

    }

}
}


Et voilà! 8)

sticky

Vos artefacts affichés sous forme de post-it !

Merveilleux, pas vrai ?

Avec quelques lignes de code, on a créé un nouvel affichage pour les rapports. Bien entendu, il y a encore du travail pour parfaire le plugin : permettre l'import/export de la structure XML, le stocker dans la base de données, ajouter des colonnes et permettre le cliquer/glisser, mais l'objectif de ce tutoriel n'était pas de réinventer la roue 

Vous aussi développez vos propres plugins et partagez les. Ça m'intéresse de voir ce que vous avez fait ! Si vous avez des problèmes, postez vos questions sur la liste de distribution des développeurs Codendi devel sur le site communautaire www.codendi.org