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.
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é.
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.
$
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 !
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 :
Maintenant,
on
ajoute
des
hooks
supplémentaires pour créer un nouvel affichage :
//...
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 :
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:
.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:
3
em;
}
.tracker_renderer_board
ul li a{
text-decoration:
none
;
color:
#000
;
background:
#ffc
;
display:
block
;
height:
10
em;
width:
10
em;
padding:
1
em;
-moz-box-shadow:
5
px 5
px 7
px rgba(
33
,
33
,
33
,
1
);
-webkit-box-shadow:
5
px 5
px 7
px rgba(
33
,
33
,
33
,
.7);
box-shadow:
5
px 5
px 7
px 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:
1
em;
float:
left
;
}
.tracker_renderer_board
ul li p.tracker_renderer_board_title
{
font-size:
140
%;
font-weight:
bold
;
padding-bottom:
10
px;
}
.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
(
4
deg);
-webkit-transform:
rotate
(
4
deg);
-moz-transform:
rotate
(
4
deg);
position:
relative
;
top
:
5
px;
/* background:#cfc; */
}
.tracker_renderer_board
ul li:
nth-child
(
3
n)
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
(
5
n)
a{
-o-transform:
rotate
(
5
deg);
-webkit-transform:
rotate
(
5
deg);
-moz-transform:
rotate
(
5
deg);
position:
relative
;
top
:
-10px;
}
.tracker_renderer_board
ul li a:
hover
,
ul li a:
focus
{
box-shadow:
10
px 10
px 7
px rgba(
0
,
0
,
0
,
.7);
-moz-box-shadow:
10
px 10
px 7
px rgba(
0
,
0
,
0
,
.7);
-webkit-box-shadow:
10
px 10
px 7
px 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 :
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à!
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