diff --git a/cosmogramme/php/integration/pseudo_notices.php b/cosmogramme/php/integration/pseudo_notices.php
index e8d5a286c07db607c392dde8693d82204579ef84..93fce93607d2c60d42fbc5ced0906c10528fb950 100644
--- a/cosmogramme/php/integration/pseudo_notices.php
+++ b/cosmogramme/php/integration/pseudo_notices.php
@@ -18,281 +18,10 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
-// ----------------------------------------------------------------
-// PSEUDO-NOTICES - cms rss sitotheque et albums (phase 0.1 a 0.6)
-// ----------------------------------------------------------------
 
-// ----------------------------------------------------------------
-// Batch
-// ----------------------------------------------------------------
 
-if ($phase >= 0 and $phase < 0.2) {
-	startIntegrationPhase('Batchs');
-	$phase = 0.2;
-}
-
-function deletePseudoNotice($id_notice) {
-	Class_Notice::find($id_notice)->delete();
-}
-// ----------------------------------------------------------------
-// CMS
-// ----------------------------------------------------------------
-if ($phase > 0.1 and $phase < 0.202) {
-		unset($phase_data);
-		$phase_data["nombre"] = 0;
-		$phase_data["timeStart"] = time();
-		$phase_data["pointeur_reprise"] = 0;
-		setVariable("traitement_phase", "Pseudo-notices : CMS");
-		$log->ecrire("<h4>Traitement des pseudo-notices</h4>");
-		$log->ecrire(BR . BR . '<span class="violet">Notices CMS :</span>' . BR);
-
-    $phase = 0.2002;
-}
-
-
-
-// ----------------------------------------------------------------
-// Articles CMS
-// ----------------------------------------------------------------
-if ($phase == 0.2002) {
-	if ($phase_data["nombre"] and !$mode_cron)
-    print('<span class="violet">Notices CMS :</span>' . BR);
-
-	if (!$mode_cron and $chrono->tempsPasse() > 10)
-    sauveContexte();
-
-	$chrono->start();
-
-  while($articles = Class_Article::findAllBy(['where' => 'id_article >' . $phase_data["pointeur_reprise"],
-                                              'order' => 'id_article',
-                                              'limit' => 100
-                                              ])) {
-		foreach($articles as $article) {
-			if (!$mode_cron and $chrono->tempsPasse() > 10)
-        sauveContexte();
-
-			$phase_data["nombre"]++;
-
-      $article->index();
-      $record = $article->getNotice();
-      $statut = $record
-        ? ['statut' => 1,
-           'id_notice' => $record->getId(),
-           'unimarc' => $record->getUnimarc(),
-           'code_barres' => '',
-           'facettes' => $record->getFacettes()]
-        : ['statut' => 0, 'id_notice' => 0];
-
-      tracePseudoNotice($ret, $statut);
-      $phase_data["pointeur_reprise"] = $article->getId();
-		}
-	}
-	traceRecapPseudoNotices($phase_data);
-	$phase = 0.201;
-}
-
-
-
-// ----------------------------------------------------------------
-// FILS RSS
-// ----------------------------------------------------------------
-if ($phase < 0.3)
-{
-	// init variables
-	if ($phase == 0.201)
-	{
-		unset($phase_data);
-		$phase_data["nombre"] = 0;
-		$phase_data["timeStart"] = time();
-		$phase_data["pointeur_reprise"] = 0;
-		setVariable("traitement_phase", "Pseudo-notices : FILS RSS :");
-		$log->ecrire('<span class="violet">Notices FILS RSS :</span>' . BR);
-	}
-	else print('<span class="violet">Notices FILS RSS :</span>' . BR);
-
-	// suppression des notices et des exemplaires
-	$phase = 0.21;
-	$items = fetchAll("select id_notice from notices where type_doc=9");
-	if ($items)
-	{
-		foreach ($items as $item)
-		{
-			if (!$mode_cron and $chrono->tempsPasse() > 10) sauveContexte();
-			$id_notice = $item["id_notice"];
-			deletePseudoNotice($id_notice);
-		}
-	}
-	$phase = 0.3;
-}
-
-if ($phase == 0.3)
-	{
-	if ($phase_data["nombre"] and !$mode_cron) print('<span class="violet">Notices FILS RSS :</span>' . BR);
-	if (!$mode_cron and $chrono->tempsPasse() > 10) sauveContexte();
-	$chrono->start();
-	$result = $sql->prepareListe("select * from rss_flux where ID_RSS >" . $phase_data["pointeur_reprise"] . " order by ID_RSS");
-	if ($result)
-	{
-		while ($enreg = $sql->fetchNext($result))
-		{
-			if (!$mode_cron and $chrono->tempsPasse() > 10) sauveContexte();
-			$enreg["id_bib"] = $sql->fetchOne("select ID_SITE from rss_categorie where ID_CAT=" . $enreg["ID_CAT"]);
-			$phase_data["nombre"]++;
-			$ret = $notice->traitePseudoNotice(9, $enreg);
-			tracePseudoNotice($ret, $enreg);
-			$phase_data["pointeur_reprise"] = $enreg["ID_RSS"];
-		}
-	}
-	traceRecapPseudoNotices($phase_data);
-	$phase = 0.4;
-}
-
-// ----------------------------------------------------------------
-// SITHOTHEQUE
-// ----------------------------------------------------------------
-if ($phase < 0.6)
-{
-	// init variables
-	if ($phase == 0.4)
-	{
-		unset($phase_data);
-		$phase_data["nombre"] = 0;
-		$phase_data["timeStart"] = time();
-		$phase_data["pointeur_reprise"] = 0;
-		setVariable("traitement_phase", "Pseudo-notices : SITOTHEQUE :");
-		$log->ecrire('<span class="violet">Notices SITOTHEQUE :</span>' . BR);
-	}
-	else print('<span class="violet">Notices SITOTHEQUE :</span>' . BR);
-
-	// suppression des notices et des exemplaires
-	$phase = 0.41;
-	$items = fetchAll("select id_notice from notices where type_doc=10");
-	if ($items)
-	{
-		foreach ($items as $item)
-		{
-			if (!$mode_cron and $chrono->tempsPasse() > 10) sauveContexte();
-			$id_notice = $item["id_notice"];
-			deletePseudoNotice($id_notice);
-		}
-	}
-	$phase = 0.5;
-}
-
-if ($phase == 0.5)
-{
-	if ($phase_data["nombre"] and !$mode_cron) print('<span class="violet">Notices SITOTHEQUE :</span>' . BR);
-	if (!$mode_cron and $chrono->tempsPasse() > 10) sauveContexte();
-	$chrono->start();
-	$result = $sql->prepareListe("select * from sito_url where ID_SITO >" . $phase_data["pointeur_reprise"] . " order by ID_SITO");
-	if ($result)
-	{
-		while ($enreg = $sql->fetchNext($result))
-		{
-			if (!$mode_cron and $chrono->tempsPasse() > 10) sauveContexte();
-			$enreg["id_bib"] = $sql->fetchOne("select ID_SITE from sito_categorie where ID_CAT=" . $enreg["ID_CAT"]);
-			$phase_data["nombre"]++;
-			$ret = $notice->traitePseudoNotice(10, $enreg);
-			tracePseudoNotice($ret, $enreg);
-			$phase_data["pointeur_reprise"] = $enreg["ID_SITO"];
-		}
-	}
-	traceRecapPseudoNotices($phase_data);
-	$phase = 0.6;
-}
-
-// ----------------------------------------------------------------
-// ALBUMS
-// ----------------------------------------------------------------
-
-
-if ($phase < 0.7) {
-  unset($phase_data);
-  $phase_data["nombre"] = 0;
-  $phase_data["timeStart"] = time();
-  $phase_data["pointeur_reprise"] = 0;
-  setVariable("traitement_phase", "Pseudo-notices : RESSOURCES NUMERIQUES :");
-  $log->ecrire('<span class="violet">Notices RESSOURCES NUMERIQUES :</span>' . BR);
-
-  $phase = 0.7;
-}
-
-
-if ($phase == 0.7) {
-	if ($phase_data["nombre"] and !$mode_cron)
-		print('<span class="violet">Notices RESSOURCES NUMERIQUES : mise à jour</span>' . BR);
-
-	if (!$mode_cron and $chrono->tempsPasse() > 10)
-		sauveContexte();
-
-	$chrono->start();
-
-  while ( $albums = Class_Album::findAllBy(['where' => 'id > ' . $phase_data["pointeur_reprise"],
-                                            'order' => 'id',
-                                            'limit' => 100]) ) {
-    foreach($albums as $album) {
-      if (!$mode_cron and $chrono->tempsPasse() > 10) sauveContexte();
-
-      $phase_data["nombre"]++;
-
-      $album->index();
-      $record = $album->getNotice();
-      $statut = $record
-        ? ['statut' => 1,
-           'id_notice' => $record->getId(),
-           'unimarc' => $record->getUnimarc(),
-           'code_barres' => '',
-           'facettes' => $record->getFacettes()]
-        : ['statut' => 0, 'id_notice' => 0];
-
-      tracePseudoNotice($ret, $statut);
-      $phase_data["pointeur_reprise"] = $album->getId();
-    }
-  }
-
-	traceRecapPseudoNotices($phase_data);
-	$phase = 1;
-}
-
-// ----------------------------------------------------------------
-// Affichage detail pour les pseudo-notices
-// ----------------------------------------------------------------
-function tracePseudoNotice($ret, $enreg)
-{
-	global $debug_level, $phase_data, $log, $compteur;
-
-	// compteurs
-	if ($ret["statut"] > 0) $compteur[$ret["statut"]]++;
-	$phase_data["compteur"][$ret["statut"]]++;
-	if ($debug_level == 0) return;
-
-	// Traces
-	if ($ret["id_notice"]) $log->ecrire('<a class="notice" href="' . URL_BASE . "php/analyse_afficher_notice_full.php?id_notice=" . $ret["id_notice"] . '">Notice n° ' . $ret["id_notice"] . '</a>' . BR);
-	$log->ecrire('<table class="blank" cellspacing="0" cellpadding="5px" style="margin-left:15px;margin-bottom:10px">');
-	$log->ecrire('<tr><td class="blank">Titre</td><td class="blank">' . $enreg["TITRE"] . '</td></tr>');
-	$log->ecrire('<tr><td class="blank">Tags</td><td class="blank">' . $enreg["TAGS"] . '</td></tr>');
-	$log->ecrire('<tr><td class="blank">Code-barres</td><td class="blank">' . $ret["code_barres"] . '</td></tr>');
-	$log->ecrire('<tr><td class="blank">Unimarc</td><td class="blank">' . $ret["unimarc"] . '</td></tr>');
-	$log->ecrire('</table>');
-}
-
-// ----------------------------------------------------------------
-// Recap pour les pseudo-notices
-// ----------------------------------------------------------------
-function traceRecapPseudoNotices($phase_data)
-{
-	global $log, $chrono;
-
-	if ($phase_data["nombre"] == 0)
-	{
-		$log->ecrire('<span class="vert">Aucune notice à traiter</span><br>');
-		return;
-	} else
-	{
-		$log->ecrire('<span class="vert">' . $phase_data["nombre"] . ' notices(s) traitée(s)</span>' . BR);
-		$chrono->timeStart = $phase_data["timeStart"];
-		$log->ecrire('<span class="vert">Temps de traitement : ' . $chrono->end() . " (" . $chrono->moyenne($phase_data["nombre"], "notices") . ")</span>" . BR.BR);
-	}
-}
-
-?>
+startIntegrationPhase('Batchs');
+startIntegrationPhase('PseudoRecordCms');
+startIntegrationPhase('PseudoRecordRss');
+startIntegrationPhase('PseudoRecordSitotheque');
+startIntegrationPhase('PseudoRecordAlbum');
diff --git a/library/Class/Album.php b/library/Class/Album.php
index 80145a2ae483661603b06c87015680dcd7429083..de3a91553ed54612f41694709d8eb5c4b2840ebf 100644
--- a/library/Class/Album.php
+++ b/library/Class/Album.php
@@ -858,7 +858,7 @@ class Class_Album extends Storm_Model_Abstract {
 
 
   protected function hasToBeIndexed() {
-		return $this->isVisible();
+		return $this->isVisible() && $this->isValidated();
 	}
 
 
diff --git a/library/Class/Article.php b/library/Class/Article.php
index 28c86fa2b70ae99ed96dccb150aec4d2f244e02b..0b95f11d77a0e86d280a44ebd1f52a8ea755a698 100644
--- a/library/Class/Article.php
+++ b/library/Class/Article.php
@@ -791,8 +791,7 @@ class Class_Article extends Storm_Model_Abstract {
 
 
   protected function hasToBeIndexed() {
-    return ($this->isVisible()
-      && $this->_get('indexation') == 1);
+    return $this->isVisible() && $this->_get('indexation') == 1;
   }
 
 
@@ -992,10 +991,10 @@ class Class_Article extends Storm_Model_Abstract {
    * @return bool
    */
   public function isVisible() {
-    return ($this->isVisibleByDates() && $this->isVisibleByWorkflow());
-
+    return $this->isVisibleByDates() && $this->isVisibleByWorkflow();
   }
 
+
   /**
    * @return bool
    */
@@ -1003,13 +1002,15 @@ class Class_Article extends Storm_Model_Abstract {
     return !$this->isVisible();
   }
 
+
   /**
    * @return bool
    */
   public function isVisibleByWorkflow() {
-    return (self::STATUS_VALIDATED == $this->getStatus());
+    return self::STATUS_VALIDATED == $this->getStatus();
   }
 
+
   /**
    * @return bool
    */
@@ -1029,6 +1030,7 @@ class Class_Article extends Storm_Model_Abstract {
     return (($now >= $start) && ($now <= $end));
   }
 
+
   public function beVisible() {
     $date = new DateTime();
 
@@ -1037,6 +1039,7 @@ class Class_Article extends Storm_Model_Abstract {
       ->save();
   }
 
+
   public function beInvisible() {
     $date = new DateTime();
     $date->modify('-1 day');
@@ -1046,6 +1049,7 @@ class Class_Article extends Storm_Model_Abstract {
       ->save();
   }
 
+
   /**
    * @return Class_Article
    */
@@ -1054,6 +1058,7 @@ class Class_Article extends Storm_Model_Abstract {
     return $this;
   }
 
+
   /**
    * @return Class_Article
    */
@@ -1138,6 +1143,7 @@ class Class_Article extends Storm_Model_Abstract {
     return $this->_set('titre', strip_tags($data));
   }
 
+
   public function getNomCompletAuteur() {
     if ($auteur=$this->getAuteur())
       return $auteur->getNomComplet();
@@ -1159,12 +1165,12 @@ class Class_Article extends Storm_Model_Abstract {
     return Class_Profil::getRoot()->findByPath($categorie->getPath());
   }
 
+
   public function renderOn($canvas) {
     return $canvas->renderArticle($this);
   }
 
 
-
   public function getTitleInfo() {
     return '';
   }
@@ -1188,6 +1194,7 @@ class Class_Article extends Storm_Model_Abstract {
     return $this->getTitre();
   }
 
+
   public function copy() {
     $copy = new Class_Article();
     $attributes = $this->_attributes;
@@ -1276,6 +1283,5 @@ class Class_Article extends Storm_Model_Abstract {
       return '';
     return $author->getLoginOrFullName();
   }
-
 }
 ?>
\ No newline at end of file
diff --git a/library/Class/Cosmogramme/Integration/PhaseAbstract.php b/library/Class/Cosmogramme/Integration/PhaseAbstract.php
index 24de62fd1c341bb66a3dbb21adf53c0974432025..559cae022514d619a35491e2013198372ecbb4eb 100644
--- a/library/Class/Cosmogramme/Integration/PhaseAbstract.php
+++ b/library/Class/Cosmogramme/Integration/PhaseAbstract.php
@@ -21,8 +21,7 @@
 
 
 abstract class Class_Cosmogramme_Integration_PhaseAbstract {
-  use Trait_TimeSource;
-  use Trait_StaticFileSystem;
+  use Trait_TimeSource, Trait_StaticFileSystem, Trait_Translator;
 
   protected $_label = '';
   protected $_phase, $_log, $_printer, $_chrono, $_is_time_out;
diff --git a/library/Class/Cosmogramme/Integration/PhasePseudoRecord.php b/library/Class/Cosmogramme/Integration/PhasePseudoRecord.php
index 99139102b968e00cc044c9988b9f38fb247228b0..c305c39f48d7cc4d63d717849749e85f339e2a3f 100644
--- a/library/Class/Cosmogramme/Integration/PhasePseudoRecord.php
+++ b/library/Class/Cosmogramme/Integration/PhasePseudoRecord.php
@@ -49,6 +49,8 @@ abstract class Class_Cosmogramme_Integration_PhasePseudoRecord
 
       $this->_runPage($models);
     }
+
+    $this->_summarize();
   }
 
 
@@ -71,7 +73,7 @@ abstract class Class_Cosmogramme_Integration_PhasePseudoRecord
     $this->_setData('pointeur_reprise', $model->getId());
 
     if (!$old_record && !$new_record)
-      continue;
+      return;
 
     if (!$old_record) {
       $this->_incrementCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT);
@@ -87,6 +89,26 @@ abstract class Class_Cosmogramme_Integration_PhasePseudoRecord
   }
 
 
+  protected function _summarize() {
+    if (!$processed = $this->_getData('nombre')) {
+      $this->_log->ecrire($this->_tagGreen($this->_('Aucune notice à traiter')) . '<br>');
+      return;
+    }
+
+    $trace = [$this->_tagGreen($this->_('%s notice(s) traitée(s)', $processed)),
+              $this->_tagGreen($this->_('Temps de traitement ')
+                               . $this->_chrono->endFile()
+                               . ' (' . $this->_chrono->meanOnFile($processed, 'notices') . ')')];
+
+    $this->_log->ecrire(implode('<br>', $trace));
+  }
+
+
+  protected function _tagGreen($content) {
+    return '<span class="vert">' . $content . '</span>';
+  }
+
+
   protected function _wasRunning() {
     return $this->_getData('nombre') > 0;
   }
diff --git a/library/Class/Cosmogramme/Integration/PhasePseudoRecordAlbum.php b/library/Class/Cosmogramme/Integration/PhasePseudoRecordAlbum.php
new file mode 100644
index 0000000000000000000000000000000000000000..4a798cd03e7c0d376b00a98fbb48d2a68a9566a0
--- /dev/null
+++ b/library/Class/Cosmogramme/Integration/PhasePseudoRecordAlbum.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_Cosmogramme_Integration_PhasePseudoRecordAlbum extends Class_Cosmogramme_Integration_PhasePseudoRecord {
+  const MY_ID = 0.5;
+
+  protected $_label = 'Pseudo-notices : RESSOURCES NUMERIQUES';
+  protected $_model_name = 'Class_Album';
+
+
+  /** @return array **/
+  protected function _previousPhaseIds() {
+    return [0.4];
+  }
+}
diff --git a/library/Class/Cosmogramme/Integration/PhasePseudoRecordCms.php b/library/Class/Cosmogramme/Integration/PhasePseudoRecordCms.php
index 377dff16d2906faabdf8ae2af960e82eab6ae59d..7daa79bdac5b3c0f457ad910783a127b4a4bc5e2 100644
--- a/library/Class/Cosmogramme/Integration/PhasePseudoRecordCms.php
+++ b/library/Class/Cosmogramme/Integration/PhasePseudoRecordCms.php
@@ -21,10 +21,10 @@
 
 
 class Class_Cosmogramme_Integration_PhasePseudoRecordCms extends Class_Cosmogramme_Integration_PhasePseudoRecord {
-	const MY_ID = 0.2;
+  const MY_ID = 0.2;
 
-	protected $_label = 'Pseudo-notices : CMS';
-	protected $_model_name = 'Class_Article';
+  protected $_label = 'Pseudo-notices : CMS';
+  protected $_model_name = 'Class_Article';
 
 
   /** @return array **/
diff --git a/library/Class/Cosmogramme/Integration/PhasePseudoRecordRss.php b/library/Class/Cosmogramme/Integration/PhasePseudoRecordRss.php
new file mode 100644
index 0000000000000000000000000000000000000000..773f9fcb5446515cb91d7be8bbd33b9f9f036d2e
--- /dev/null
+++ b/library/Class/Cosmogramme/Integration/PhasePseudoRecordRss.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_Cosmogramme_Integration_PhasePseudoRecordRss extends Class_Cosmogramme_Integration_PhasePseudoRecord {
+  const MY_ID = 0.3;
+
+  protected $_label = 'Pseudo-notices : FILS RSS';
+  protected $_model_name = 'Class_Rss';
+
+
+  /** @return array **/
+  protected function _previousPhaseIds() {
+    return [0.2];
+  }
+}
diff --git a/library/Class/Cosmogramme/Integration/PhasePseudoRecordSitotheque.php b/library/Class/Cosmogramme/Integration/PhasePseudoRecordSitotheque.php
new file mode 100644
index 0000000000000000000000000000000000000000..7df83130d2e97310186c14cacd2e421698e84955
--- /dev/null
+++ b/library/Class/Cosmogramme/Integration/PhasePseudoRecordSitotheque.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_Cosmogramme_Integration_PhasePseudoRecordSitotheque
+  extends Class_Cosmogramme_Integration_PhasePseudoRecord {
+	const MY_ID = 0.4;
+
+  protected $_label = 'Pseudo-notices : SITOTHEQUE';
+  protected $_model_name = 'Class_Sitotheque';
+
+
+  /** @return array **/
+  protected function _previousPhaseIds() {
+    return [0.3];
+  }
+}
diff --git a/tests/library/Class/Cosmogramme/Integration/PhasePseudoRecordTest.php b/tests/library/Class/Cosmogramme/Integration/PhasePseudoRecordTest.php
index 5c08bd422ae693fab068f3d7aca36aa01d5a93dc..e70ba38d96b5acd0bf1eb97656b1628a7a6f2d90 100644
--- a/tests/library/Class/Cosmogramme/Integration/PhasePseudoRecordTest.php
+++ b/tests/library/Class/Cosmogramme/Integration/PhasePseudoRecordTest.php
@@ -20,45 +20,67 @@
  */
 
 
-abstract class PhasePseudoRecordCmsTestCase extends Class_Cosmogramme_Integration_PhaseTestCase {
+abstract class PhasePseudoRecordTestCase extends Class_Cosmogramme_Integration_PhaseTestCase {
+  protected
+    $_phase_name,
+    $_phase_label,
+    $_model_name,
+    $_model_id;
+
   public function setUp() {
     parent::setUp();
-    $this->_phase = $this->_buildPhase('PseudoRecordCms')->run();
+    $this->_phase = $this->_buildPhase($this->_phase_name)->run();
   }
 
 
   protected function _prepareFixtures() {
-    $art_15 = $this->fixture('Class_Article',
-                             ['id' => 15,
-                              'titre' => 'Article 15',
-                              'contenu' => 'Content 15',
-                              'indexation' => 1]);
-
-    $art_16 = $this->fixture('Class_Article',
-                             ['id' => 16,
-                              'titre' => 'Article 16',
-                              'contenu' => 'Content 16']);
-
-    $this->onLoaderOfModel('Class_Article')
+    $loader = $this->onLoaderOfModel($this->_model_name)
+
          ->whenCalled('findAllBy')
-         ->with(['where' => 'id_article > 0',
-                 'order' => 'id_article',
+         ->with(['where' => $this->_model_id . ' > 0',
+                 'order' => $this->_model_id,
                  'limit' => 100])
-         ->answers([$art_15])
+         ->answers([$this->_buildModel(15)])
 
          ->whenCalled('findAllBy')
-         ->with(['where' => 'id_article > 15',
-                 'order' => 'id_article',
+         ->with(['where' => $this->_model_id . ' > 15',
+                 'order' => $this->_model_id,
                  'limit' => 100])
-         ->answers([$art_16])
+         ->answers([$this->_buildModel(16)])
 
          ->whenCalled('findAllBy')->answers([]);
   }
+
+
+  protected function _buildModel($id) {
+    return $this->fixture($this->_model_name,
+                          $this->_getModelAttribs($id));
+  }
+
+
+  protected function _getModelAttribs($id) {
+    return ['id' => $id];
+  }
+
+
+  protected function _getModelRecord($id) {
+    return call_user_func([$this->_model_name, 'find'], $id)->getNotice();
+  }
+
+
+  public function assertNullRecordOf($id) {
+    $this->assertNull($this->_getModelRecord($id));
+  }
+
+
+  public function assertNotNullRecordOf($id) {
+    $this->assertNotNull($this->_getModelRecord($id));
+  }
 }
 
 
 
-class PhasePseudoRecordCmsInvalidPreviousPhaseTest extends PhasePseudoRecordCmsTestCase {
+trait PhasePseudoRecordInvalidPreviousPhase {
   protected function _getPreviousPhase() {
     return (new Class_Cosmogramme_Integration_Phase(2))
       ->beCron();
@@ -67,49 +89,40 @@ class PhasePseudoRecordCmsInvalidPreviousPhaseTest extends PhasePseudoRecordCmsT
 
   /** @test */
   public function recordShouldNotBeCreated() {
-    $this->assertNull(Class_Article::find(15)->getNotice());
+    $this->assertNullRecordOf(15);
   }
 
 
   /** @test */
   public function logShouldNotContainsPhaseLabel() {
-    $this->assertNotLogContains('Pseudo-notices : CMS');
+    $this->assertNotLogContains($this->_phase_label);
   }
 }
 
 
-
-class PhasePseudoRecordCmsValidCronTest extends PhasePseudoRecordCmsTestCase {
-  protected function _getPreviousPhase() {
-    return (new Class_Cosmogramme_Integration_Phase(0.1))
-      ->setData('pointeur_reprise', 45)
-      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 5)
-      ->beCron();
-  }
-
-
+trait PhasePseudoRecordValidCron {
   /** @test */
-  public function shouldHaveCreatedArticle15PseudoRecord() {
-    $this->assertNotNull(Class_Article::find(15)->getNotice());
+  public function shouldHaveCreatedFirstPseudoRecord() {
+    $this->assertNotNullRecordOf(15);
   }
 
 
   /** @test */
-  public function shouldHaveCreatedArticle16PseudoRecord() {
-    $this->assertNotNull(Class_Article::find(16)->getNotice());
+  public function shouldHaveCreatedSecondPseudoRecord() {
+    $this->assertNotNullRecordOf(16);
   }
 
 
   /** @test */
   public function traitemenPhaseShouldBePseudoRecord() {
-    $this->assertEquals('Pseudo-notices : CMS',
+    $this->assertEquals($this->_phase_label,
                         Class_CosmoVar::get('traitement_phase'));
   }
 
 
   /** @test */
   public function logShouldContainsPhaseLabel() {
-    $this->assertLogContains('Pseudo-notices : CMS');
+    $this->assertLogContains($this->_phase_label);
   }
 
 
@@ -125,17 +138,18 @@ class PhasePseudoRecordCmsValidCronTest extends PhasePseudoRecordCmsTestCase {
                         $this->_phase
                         ->getCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT));
   }
-}
-
 
 
-
-class PhasePseudoRecordCmsValidInteractiveTimeoutTest extends PhasePseudoRecordCmsTestCase {
-  protected function _getPreviousPhase() {
-    return (new Class_Cosmogramme_Integration_Phase(0.1));
+  /** @test */
+  public function shouldDisplayRunSummary() {
+    $this->assertLogContains('2 notice(s) traitée(s)');
+    $this->assertLogContains('Temps de traitement');
   }
+}
 
 
+
+trait PhasePseudoRecordValidInteractiveTimeout {
   protected function _prepareFixtures() {
     parent::_prepareFixtures();
     $this->_time_source = $this
@@ -144,8 +158,6 @@ class PhasePseudoRecordCmsValidInteractiveTimeoutTest extends PhasePseudoRecordC
       ->whenCalled('dateYmd')->answers('2015-09-01');
 
     Class_Cosmogramme_Chronometre::setTimeSource($this->_time_source);
-    Class_Cosmogramme_Integration_PhasePseudoRecord::setTimeSource($this->_time_source);
-
     $this->_chrono->start(mktime(0, 0, 0, 9, 1, 2015));
   }
 
@@ -158,16 +170,7 @@ class PhasePseudoRecordCmsValidInteractiveTimeoutTest extends PhasePseudoRecordC
 
 
 
-class PhasePseudoRecordCmsValidInteractiveCallbackTest extends PhasePseudoRecordCmsTestCase {
-  protected function _getPreviousPhase() {
-    return (new Class_Cosmogramme_Integration_Phase(0.2))
-      ->beCallBack()
-      ->setData('nombre', 1)
-      ->setData('pointeur_reprise', 15)
-      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 6);
-  }
-
-
+trait PhasePseudoRecordValidInteractiveCallback {
   protected function _prepareFixtures() {
     parent::_prepareFixtures();
     $this->_time_source = $this
@@ -176,27 +179,25 @@ class PhasePseudoRecordCmsValidInteractiveCallbackTest extends PhasePseudoRecord
       ->whenCalled('dateYmd')->answers('2015-09-01');
 
     Class_Cosmogramme_Chronometre::setTimeSource($this->_time_source);
-    Class_Cosmogramme_Integration_PhasePseudoRecord::setTimeSource($this->_time_source);
-
     $this->_chrono->start(mktime(0, 0, 0, 9, 1, 2015));
   }
 
 
   /** @test */
   public function shouldNotHaveCreatedArticle15PseudoRecord() {
-    $this->assertNull(Class_Article::find(15)->getNotice());
+    $this->assertNullRecordOf(15);
   }
 
 
   /** @test */
   public function shouldHaveCreatedArticle16PseudoRecord() {
-    $this->assertNotNull(Class_Article::find(16)->getNotice());
+    $this->assertNotNullRecordOf(16);
   }
 
 
   /** @test */
   public function printerShouldContainsPhaseLabel() {
-    $this->assertPrinterContains('Pseudo-notices : CMS');
+    $this->assertPrinterContains($this->_phase_label);
   }
 
 
@@ -212,4 +213,263 @@ class PhasePseudoRecordCmsValidInteractiveCallbackTest extends PhasePseudoRecord
                         $this->_phase
                         ->getCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT));
   }
+}
+
+
+/**
+ * CMS
+ */
+
+abstract class PhasePseudoRecordCmsTestCase extends PhasePseudoRecordTestCase {
+  protected
+    $_phase_name = 'PseudoRecordCms',
+    $_phase_label = 'Pseudo-notices : CMS',
+    $_model_name = 'Class_Article',
+    $_model_id = 'id_article';
+
+
+  protected function _getModelAttribs($id) {
+    return array_merge(parent::_getModelAttribs($id),
+                       ['titre' => 'Article ' . $id,
+                        'contenu' => 'Content ' . $id]);
+  }
+}
+
+
+
+class PhasePseudoRecordCmsInvalidPreviousPhaseTest extends PhasePseudoRecordCmsTestCase {
+  use PhasePseudoRecordInvalidPreviousPhase;
+}
+
+
+
+class PhasePseudoRecordCmsValidCronTest extends PhasePseudoRecordCmsTestCase {
+  use PhasePseudoRecordValidCron;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.1))
+      ->setData('pointeur_reprise', 45)
+      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 5)
+      ->beCron();
+  }
+}
+
+
+
+class PhasePseudoRecordCmsValidInteractiveTimeoutTest
+  extends PhasePseudoRecordCmsTestCase {
+  use PhasePseudoRecordValidInteractiveTimeout;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.1));
+  }
+}
+
+
+
+class PhasePseudoRecordCmsValidInteractiveCallbackTest
+  extends PhasePseudoRecordCmsTestCase {
+  use PhasePseudoRecordValidInteractiveCallback;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.2))
+      ->beCallBack()
+      ->setData('nombre', 1)
+      ->setData('pointeur_reprise', 15)
+      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 6);
+  }
+}
+
+
+/**
+ * RSS
+ */
+
+abstract class PhasePseudoRecordRssTestCase extends PhasePseudoRecordTestCase {
+  protected
+    $_phase_name = 'PseudoRecordRss',
+    $_phase_label = 'Pseudo-notices : FILS RSS',
+    $_model_name = 'Class_Rss',
+    $_model_id = 'id_rss';
+
+  protected function _getModelAttribs($id) {
+    return array_merge(parent::_getModelAttribs($id),
+                       ['titre' => 'RSS ' . $id]);
+  }
+}
+
+
+
+class PhasePseudoRecordRssInvalidPreviousPhaseTest extends PhasePseudoRecordRssTestCase {
+  use PhasePseudoRecordInvalidPreviousPhase;
+}
+
+
+
+class PhasePseudoRecordRssValidCronTest extends PhasePseudoRecordRssTestCase {
+  use PhasePseudoRecordValidCron;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.2))
+      ->setData('pointeur_reprise', 45)
+      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 5)
+      ->beCron();
+  }
+}
+
+
+
+class PhasePseudoRecordRssValidInteractiveTimeoutTest
+  extends PhasePseudoRecordRssTestCase {
+  use PhasePseudoRecordValidInteractiveTimeout;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.2));
+  }
+}
+
+
+
+class PhasePseudoRecordRssValidInteractiveCallbackTest
+  extends PhasePseudoRecordRssTestCase {
+  use PhasePseudoRecordValidInteractiveCallback;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.3))
+      ->beCallBack()
+      ->setData('nombre', 1)
+      ->setData('pointeur_reprise', 15)
+      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 6);
+  }
+}
+
+
+
+/**
+ * SITO
+ */
+
+abstract class PhasePseudoRecordSitoTestCase extends PhasePseudoRecordTestCase {
+  protected
+    $_phase_name = 'PseudoRecordSitotheque',
+    $_phase_label = 'Pseudo-notices : SITOTHEQUE',
+    $_model_name = 'Class_Sitotheque',
+    $_model_id = 'id_sito';
+
+  protected function _getModelAttribs($id) {
+    return array_merge(parent::_getModelAttribs($id),
+                       ['titre' => 'Sito ' . $id,
+                        'url' => 'http://server.com/' . $id]);
+  }
+}
+
+
+
+class PhasePseudoRecordSitoInvalidPreviousPhaseTest extends PhasePseudoRecordSitoTestCase {
+  use PhasePseudoRecordInvalidPreviousPhase;
+}
+
+
+
+class PhasePseudoRecordSitoValidCronTest extends PhasePseudoRecordSitoTestCase {
+  use PhasePseudoRecordValidCron;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.3))
+      ->setData('pointeur_reprise', 45)
+      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 5)
+      ->beCron();
+  }
+}
+
+
+
+class PhasePseudoRecordSitoValidInteractiveTimeoutTest
+  extends PhasePseudoRecordSitoTestCase {
+  use PhasePseudoRecordValidInteractiveTimeout;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.3));
+  }
+}
+
+
+
+class PhasePseudoRecordSitoValidInteractiveCallbackTest
+  extends PhasePseudoRecordSitoTestCase {
+  use PhasePseudoRecordValidInteractiveCallback;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.4))
+      ->beCallBack()
+      ->setData('nombre', 1)
+      ->setData('pointeur_reprise', 15)
+      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 6);
+  }
+}
+
+
+
+/**
+ * Albums
+ */
+
+abstract class PhasePseudoRecordAlbumTestCase extends PhasePseudoRecordTestCase {
+  protected
+    $_phase_name = 'PseudoRecordAlbum',
+    $_phase_label = 'Pseudo-notices : RESSOURCES NUMERIQUES',
+    $_model_name = 'Class_Album',
+    $_model_id = 'id';
+
+  protected function _getModelAttribs($id) {
+    return array_merge(parent::_getModelAttribs($id),
+                       ['titre' => 'Album ' . $id,
+                        'visible' => 1,
+                        'status' => Class_Album::STATUS_VALIDATED]);
+  }
+}
+
+
+
+class PhasePseudoRecordAlbumInvalidPreviousPhaseTest extends PhasePseudoRecordAlbumTestCase {
+  use PhasePseudoRecordInvalidPreviousPhase;
+}
+
+
+
+class PhasePseudoRecordAlbumValidCronTest extends PhasePseudoRecordAlbumTestCase {
+  use PhasePseudoRecordValidCron;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.4))
+      ->setData('pointeur_reprise', 45)
+      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 5)
+      ->beCron();
+  }
+}
+
+
+
+class PhasePseudoRecordAlbumValidInteractiveTimeoutTest
+  extends PhasePseudoRecordAlbumTestCase {
+  use PhasePseudoRecordValidInteractiveTimeout;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.4));
+  }
+}
+
+
+
+class PhasePseudoRecordAlbumValidInteractiveCallbackTest
+  extends PhasePseudoRecordAlbumTestCase {
+  use PhasePseudoRecordValidInteractiveCallback;
+
+  protected function _getPreviousPhase() {
+    return (new Class_Cosmogramme_Integration_Phase(0.5))
+      ->beCallBack()
+      ->setData('nombre', 1)
+      ->setData('pointeur_reprise', 15)
+      ->setCount(Class_Cosmogramme_Integration_Phase::RECORD_INSERT, 6);
+  }
 }
\ No newline at end of file