diff --git a/VERSIONS_HOTLINE/37078 b/VERSIONS_HOTLINE/37078
new file mode 100644
index 0000000000000000000000000000000000000000..8d58213172cbd1a515f4a5cfe51d19153220948a
--- /dev/null
+++ b/VERSIONS_HOTLINE/37078
@@ -0,0 +1 @@
+ - ticket #37078 : Cosmogramme : Le lien de génération de site Nanook ne créait pas les imports paniers
\ No newline at end of file
diff --git a/build.sh b/build.sh
index 0b5d611865ce06f89cf7a5ddba1836a66302c31e..754d033fbb705731352cef55e58fdf9366f65467 100755
--- a/build.sh
+++ b/build.sh
@@ -14,4 +14,4 @@ sed -i "s/integration_pwd=root/integration_pwd=opac/g" config.php
 sed -i "s/integration_base=opac3/integration_base=opac/g" config.php
 cd ..
 
-phpunit -c tests/phpunit.xml --exclude-group no-ci && cd cosmogramme/tests && phpunit --exclude-group no-ci
+phpunit -c tests/phpunit.xml --exclude-group no-ci && cd cosmogramme/tests && phpunit --exclude-group no-ci && cd ../cosmozend/tests && phpunit --exclude-group no-ci
diff --git a/cosmogramme/cosmozend/application/modules/cosmo/controllers/IntegrationController.php b/cosmogramme/cosmozend/application/modules/cosmo/controllers/IntegrationController.php
index 190f3613cfb53b4770d087decd0d442358a9daf2..2c2d8d92b0e744fe0f941d4512d34f60fa2cc0e3 100644
--- a/cosmogramme/cosmozend/application/modules/cosmo/controllers/IntegrationController.php
+++ b/cosmogramme/cosmozend/application/modules/cosmo/controllers/IntegrationController.php
@@ -21,6 +21,8 @@
 
 
 class Cosmo_IntegrationController extends Zend_Controller_Action{
+  use Trait_Translator;
+
 	public function preDispatch() {
 		$this->cosmoPath = $this->view->cosmoPath = new CosmoPaths();
 	}
@@ -54,5 +56,25 @@ class Cosmo_IntegrationController extends Zend_Controller_Action{
 		if (!$this->view->response)
 			$this->view->response = 'Impossible de contacter le service';
 	}
-}
-?>
\ No newline at end of file
+
+
+  public function generateAction() {
+    $this->view->titre = $this->_('Génération automatique des paramètres d\'intégration pour Pergame et Nanook');
+    $log = Class_Log::getInstance();
+    $dir = Class_Cosmogramme_LandingDirectory::getInstance();
+    if (!$dir->isValid()) {
+      $this->view->error = $log->getLastMessage();
+      return;
+    }
+
+    $this->view->directories = $dir->getSubdirectories();
+    if (!$this->_request->isPost())
+      return;
+
+    $var = (new Class_Cosmogramme_Generator())->generate($this->_request->getPost())
+      ? 'log' : 'error';
+    $this->view->__set($var, $log->getMessages());
+
+    $this->render('generate-log');
+  }
+}
\ No newline at end of file
diff --git a/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/generate-log.phtml b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/generate-log.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..929126a626b78ed8b923d31765ef2d93c7a956ff
--- /dev/null
+++ b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/generate-log.phtml
@@ -0,0 +1,13 @@
+<h1><?php echo $this->titre; ?></h1>
+
+<?php
+if ($this->error) {
+  echo $this->cosmoError($this->error[0]);
+  echo $this->cosmoButton($this->_('Retour'), $this->url(['module' => 'cosmo',
+                                                          'controller' => 'integration',
+                                                          'action' => 'generate'],
+                                                         null, true));
+  return;
+}
+
+echo implode($this->log);?>
diff --git a/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/generate.phtml b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/generate.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..c6e0bda52a8528ceea757dd2743863eb02fe537b
--- /dev/null
+++ b/cosmogramme/cosmozend/application/modules/cosmo/views/scripts/integration/generate.phtml
@@ -0,0 +1,65 @@
+<h1><?php echo $this->titre;?></h1>
+
+<h3><?php echo $this->_('Mode d\'emploi :'); ?></h3>
+<ol>
+	<li>Faire un export total depuis Pergame ou Nanook dans le dossier ftp de réplication des fichiers<br>
+	  voir <a href="config_variables.php" target="droite">la variable ftp_path (Chemins et variables système)</a>:
+    <?php echo Class_CosmoVar::getValueOf('ftp_path');?></li>
+	<li>Sélectionner le dossier dans le formulaire ci-dessous.</li>
+	<li>Sélectionner le SIGB dans le formulaire ci-dessous.</li>
+	<li>Valider.</li>
+</ol>
+
+<?php
+if ($this->error) {
+  echo $this->cosmoError($this->error);
+  return;
+}
+
+if (!$this->directories) {
+  echo $this->cosmoError($this->_('Aucun dossier n\'a été trouvé dans le dossier des transferts ftp'));
+  return;
+}
+?>
+
+<div class="liste">
+	<form method="post" action="">
+    <table class="form" width="100%" cellspacing="0" cellpadding="5">
+	    <tr><th class="form" colspan="2" align="left">Sélection du dossier ftp</th></tr>
+	    <tr>
+        <td class="form_first" align="right" width="43%">Nom du dossier ftp</td>
+        <td class="form_first"><?php echo $this->formSelect('path_ftp', '', [], $this->directories); ?></td>
+      </tr>
+	    <tr>
+        <td class="form_first" align="right">SIGB</td>
+        <td class="form_first"><?php echo $this->formSelect('type_sigb', '', [],
+                                                            [1 => 'Pergame', 13 => 'Nanook']); ?></td>
+      </tr>
+	    <tr>
+        <td class="form_first" align="right">url du web-service (Nanook uniquement)</td>
+        <td class="form_first"><?php echo $this->formText('service_nanook', '', ['size' => 55]); ?></td>
+      </tr>
+      <tr>
+        <td class="form_first" align="right">Format(0.8.7) :</td>
+        <td class="form_first">ip:port/chemin_tomcat/ilsdi/nom_base</td>
+      </tr>
+      <tr>
+        <td class="form_first" align="right">Exemple :</td>
+        <td class="form_first">62.193.55.152:8080/afi_NanookWs/ilsdi/NANOOK</td>
+      </tr>
+	    <tr>
+        <td class="form_first" align="right">Créer les annexes comme les bibliothèques</td>
+        <td class="form_first"><?php echo $this->formSelect('creer_annexes', '', [],
+                                                            [1 => 'oui', 2 => 'non']);?></td>
+      </tr>
+      <tr>
+        <td class="form_first" align="right">Synchroniser l'étalon à chaque import</td>
+        <td class="form_first"><?php echo $this->formSelect('synchro', '', [],
+                                                            [1 => 'oui', 2 => 'non']);?></td>
+      </tr>
+	    <tr>
+        <th class="form" colspan="2" align="center"><input type="submit" class="bouton" value="Valider"></th>
+      </tr>
+    </table>
+  </form>
+</div>
diff --git a/cosmogramme/cosmozend/index.php b/cosmogramme/cosmozend/index.php
index 5a13c4cc9b171a4ca7bdf6a15debc6e31829a7f3..1d9144579ad4686b7547b3c45d0225cf693c5f6a 100644
--- a/cosmogramme/cosmozend/index.php
+++ b/cosmogramme/cosmozend/index.php
@@ -30,6 +30,13 @@ set_include_path( $cosmozendPath
 . PATH_SEPARATOR . $cosmozendPath . '/application'
 . PATH_SEPARATOR . get_include_path());
 
+// pretending I am under Bokeh's root
+chdir('../..');
+
+Zend_Controller_Action_HelperBroker::resetHelpers();
+Zend_Controller_Action_HelperBroker::addHelper(new ZendAfi_Controller_Action_Helper_Cosmo_ViewRenderer());
+Zend_Controller_Action_HelperBroker::addPrefix('ZendAfi_Controller_Action_Helper_Cosmo');
+
 Zend_Session::start();
 Zend_Layout::startMvc();
 Zend_Layout::getMvcInstance()
diff --git a/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php b/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php
index 68f23d360dd185e5867ccf8f0d45cef54ecb1f72..311bf53dae7c17e3478b45140da0d20084eabc21 100644
--- a/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php
+++ b/cosmogramme/cosmozend/tests/CosmoControllerTestCase.php
@@ -16,7 +16,7 @@
  *
  * 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 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 
@@ -25,14 +25,20 @@ abstract class CosmoControllerTestCase extends Zend_Test_PHPUnit_ControllerTestC
 
 	public $bootstrap = 'bootstrap_frontcontroller.php';
 
+  protected $_storm_default_to_volatile = true;
+
 	public function setUp() {
 		parent::setUp();
 		Storm_Model_Abstract::unsetLoaders();
 		Storm_Cache::setDefaultZendCache(null);
+    if ($this->_storm_default_to_volatile)
+      Storm_Model_Loader::defaultToVolatile();
 	}
 
 
 	public function tearDown() {
+    if ($this->_storm_default_to_volatile)
+      Storm_Model_Loader::defaultToDb();
 		Storm_Model_Abstract::unsetLoaders();
 	}
 
diff --git a/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php b/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php
index bfa86a10728f6bd5dfb31eb129735faaa898fd22..99a242715fb3c6fb851471db725d5d9cd673cbf5 100644
--- a/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php
+++ b/cosmogramme/cosmozend/tests/application/modules/cosmo/controllers/IntegrationControllerTest.php
@@ -34,9 +34,13 @@ class Cosmo_IntegrationControllerTestActionTest extends CosmoControllerTestCase
 abstract class Cosmo_IntegrationControllerControlActionTestCase extends CosmoControllerTestCase {
 	public function setUp() {
 		parent::setUp();
-		Class_IntBib::beVolatile();
-		Class_Cosmogramme_Integration::beVolatile();
-		Class_IntProfilDonnees::beVolatile();
+    $this->fixture('Class_CosmoVar',
+                   ['id' => 'import_type_operation',
+                    'liste' => "0:Import incrémentiel\r\n1:Suppression d'exemplaires\r\n2:Import total\r\n3:Suppression fichier d'entête Pergame\r\n"]);
+
+    $this->fixture('Class_CosmoVar',
+                   ['id' => 'type_fichier',
+                    'liste' => "0:notices\r\n1:abonnés\r\n2:prêts\r\n3:reservations\r\n4:paniers\r\n"]);
 
 		$this->file_system = $this->mock();
 		Class_Cosmogramme_Integration::setFileSystem($this->file_system);
@@ -152,4 +156,401 @@ class Cosmo_IntegrationControllerControlActionWithBibTest extends Cosmo_Integrat
 	public function vendrediShouldBePresent() {
 		$this->assertXPathContentContains('//td', 'vendredi');
 	}
+}
+
+
+
+abstract class Cosmo_IntegrationControllerGenerateActionTestCase extends CosmoControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->fixture('Class_CosmoVar',
+                   ['id' => 'ftp_path',
+                    'valeur' => '../ftp/transfert/']);
+
+    $subdir_calls = 0;
+    $subdir_provider = function() use (&$subdir_calls) {
+      $subdir_calls++;
+      $mapping = [1 => '.', 2 => '..', 3 => 'library1', 4 => 'test'];
+      return array_key_exists($subdir_calls, $mapping) ? $mapping[$subdir_calls] : false;
+    };
+
+    $file_system = $this->mock()
+                        ->whenCalled('file_exists')->with('../ftp/transfert/')
+                        ->answers(true)
+
+                        ->whenCalled('opendir')->with('../ftp/transfert/')
+                        ->answers(true)
+
+                        ->whenCalled('readdir')->with(true)
+                        ->willDo($subdir_provider)
+
+                        ->whenCalled('is_dir')->with('../ftp/transfert/library1')
+                        ->answers(true)
+
+                        ->whenCalled('is_readable')
+                        ->with('../ftp/transfert/library1/etalon')
+                        ->answers(true)
+
+                        ->whenCalled('is_readable')
+                        ->with('../ftp/transfert/library1/etalon/annexes.txt')
+                        ->answers(true)
+
+                        ->whenCalled('is_readable')
+                        ->with('../ftp/transfert/library1/etalon/genres.txt')
+                        ->answers(true)
+
+                        ->whenCalled('is_readable')
+                        ->with('../ftp/transfert/library1/etalon/sections.txt')
+                        ->answers(true)
+
+                        ->whenCalled('is_readable')
+                        ->with('../ftp/transfert/library1/etalon/emplacements.txt')
+                        ->answers(true)
+
+                        ->whenCalled('file')
+                        ->with('../ftp/transfert/library1/etalon/annexes.txt')
+                        ->answers(['BIB_SPS_UTT|ID_SITE|LIBELLE',
+                                   '20|library1',
+                                   '12|library2'])
+
+                        ->whenCalled('file')
+                        ->with('../ftp/transfert/library1/etalon/sections.txt')
+                        ->answers(['BIB_C_SECTION|CODE|LIBELLE',
+                                   '1|Adulte',
+                                   '2|Jeunesse'])
+
+                        ->whenCalled('file')
+                        ->with('../ftp/transfert/library1/etalon/emplacements.txt')
+                        ->answers(['BIB_C_EMPLACEMENT|CODE|LIBELLE',
+                                   '4|Coin des tout-petits',
+                                   '5|Livres CD',
+                                   '6|Bacs imagiers',
+                                   '7|Albums'])
+
+                        ->whenCalled('file')
+                        ->with('../ftp/transfert/library1/etalon/genres.txt')
+                        ->answers(['BIB_GENRES|SUPPORT|CODE|LIBELLE|DOC',
+                                   '0|1|Album|f',
+                                   '0|2|Bande dessinée|f',
+                                   '0|3|Biographie|f'])
+
+                        ->beStrict();
+
+    Class_Cosmogramme_LandingDirectory::setFileSystem($file_system);
+  }
+
+
+  public function tearDown() {
+    Class_Log::getInstance()->reset();
+    parent::tearDown();
+  }
+}
+
+
+
+class Cosmo_IntegrationControllerGenerateActionTest extends Cosmo_IntegrationControllerGenerateActionTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('cosmo/integration/generate', true);
+  }
+
+
+  public function tearDown() {
+    Class_Cosmogramme_LandingDirectory::setFileSystem(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function titleShouldBeGenerationAutomatique() {
+    $this->assertXPathContentContains('//h1', 'Génération automatique');
+  }
+
+
+  /** @test */
+  public function library1ShouldBeSelectable() {
+    $this->assertXPath('//select[@name="path_ftp"]//option[@value="library1"]');
+  }
+
+
+  /** @test */
+  public function testShouldNotBeSelectable() {
+    $this->assertNotXPath('//select[@name="path_ftp"]//option[@value="test"]');
+  }
+
+
+  /** @test */
+  public function parentShouldNotBeSelectable() {
+    $this->assertNotXPath('//select[@name="path_ftp"]//option[@value=".."]');
+  }
+
+
+  /** @test */
+  public function sigbNanookShouldBeSelectable() {
+    $this->assertXPath('//select[@name="type_sigb"]//option[@value="13"]');
+  }
+
+
+  /** @test */
+  public function shouldBeAbleToChooseBranchCreationMode() {
+    $this->assertXPath('//select[@name="creer_annexes"]');
+  }
+
+
+  /** @test */
+  public function shouldBeAbleToEtalonSynchMode() {
+    $this->assertXPath('//select[@name="synchro"]');
+  }
+}
+
+
+
+class Cosmo_IntegrationControllerGenerateActionNanookPostTest
+  extends Cosmo_IntegrationControllerGenerateActionTestCase {
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->fixture('Class_IntProfilDonnees',
+                   ['id' => 104,
+                    'libelle' => 'Unimarc Nanook',
+                    'type_fichier' => 0]);
+
+    $this->fixture('Class_IntProfilDonnees',
+                   ['id' => 105,
+                    'libelle' => 'Abonné Nanook',
+                    'type_fichier' => 1]);
+
+    $time_source = $this->mock()->whenCalled('time')->answers(strtotime('2016-01-28'));
+    Class_Cosmogramme_Generator::setTimeSource($time_source);
+    Class_Cosmogramme_Generator_AbstractTask::setTimeSource($time_source);
+
+    $this->postDispatch('cosmo/integration/generate',
+                        ['path_ftp' => 'library1',
+                         'type_sigb' => 13,
+                         'service_nanook' => 'http://nanook-ws.net/ilsdi/',
+                         'creer_annexes' => 1,
+                         'synchro' => 1]);
+  }
+
+
+  public function tearDown() {
+    Class_Cosmogramme_Generator::setTimeSource(null);
+    Class_Cosmogramme_Generator_AbstractTask::setTimeSource(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldNotContainsForm() {
+    $this->assertNotXPath('//form');
+  }
+
+
+  /** @test */
+  public function shouldNotHaveError() {
+    $this->assertNotXPath('//font[@color="red"]', $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function shouldLogLibraryCreation() {
+    $this->assertXPathContentContains('//h3', '1 - Création des bibliothèques', $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function shouldHaveCreated2Libraries() {
+    $this->assertEquals(2, Class_Bib::count());
+  }
+
+
+  /** @test */
+  public function library1ShouldBeCreated() {
+    $this->assertNotNull(Class_Bib::findFirstBy(['id_site' => 20,
+                                                 'libelle' => 'library1',
+                                                 'ville' => 'library1',
+                                                 'id_zone' => 1,
+                                                 'visibilite' => Class_Bib::V_DATA]),
+                         $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function library1IntegrationShouldBeCreated() {
+    $this->assertNotNull($bib = Class_IntBib::findFirstBy(['id_bib' => 20,
+                                                           'nom' => 'library1',
+                                                           'nom_court' => 'library1',
+                                                           'qualite' => 5,
+                                                           'sigb' => Class_IntBib::SIGB_NANOOK,
+                                                           'planif_mode' => 'r',
+                                                           'planif_jours' => '1111111',
+                                                           'comm_sigb' => Class_IntBib::COM_NANOOK]),
+                         $this->_response->getBody());
+    return $bib;
+  }
+
+
+  /**
+   * @test
+   * @depends library1IntegrationShouldBeCreated
+   */
+  public function library1IntegrationCommParamsShouldContainsNanookWebservice($bib) {
+    $this->assertContains('http://nanook-ws.net/ilsdi/', $bib->getCommParams());
+  }
+
+
+  /** @test */
+  public function library1BranchShouldBeCreated() {
+    $this->assertNotNull(Class_CodifAnnexe::findFirstBy(['id_bib' => 20,
+                                                         'code' => 20,
+                                                         'libelle' => 'library1',
+                                                         'invisible' => 0]),
+                         $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function barCodeProfileShouldBeCreated() {
+    $this->assertNotNull($profile = Class_IntProfilDonnees::findFirstBy(['libelle' => 'Liste de codes-barres']));
+    return $profile;
+  }
+
+
+  /**
+   * @test
+   * @depends barCodeProfileShouldBeCreated
+   */
+  public function library1ShouldHeveItemDeletionPlanned($profile) {
+    $this->assertNotNull(Class_IntMajAuto::findFirstBy(['id_bib' => 20,
+                                                        'rang' => 2001,
+                                                        'profil' => $profile->getId(),
+                                                        'type_operation' => Class_IntMajAuto::OP_ITEMS_DELETION,
+                                                        'nom_fichier' => '../ftp/transfert/library1/site20/suppressions.txt']));
+  }
+
+
+  /** @test */
+  public function library1ShouldHaveRecordsTotalPlanned() {
+    $this->assertNotNull(Class_IntMajAuto::findFirstBy(['id_bib' => 20,
+                                                        'profil' => 104,
+                                                        'type_operation' => Class_IntMajAuto::OP_FULL_IMPORT,
+                                                        'nom_fichier' => '../ftp/transfert/library1/site20/notices_total.txt']));
+  }
+
+
+  /** @test */
+  public function library1ShouldHaveRecordsPartialPlanned() {
+    $this->assertNotNull(Class_IntMajAuto::findFirstBy(['id_bib' => 20,
+                                                        'profil' => 104,
+                                                        'type_operation' => Class_IntMajAuto::OP_PARTIAL_IMPORT,
+                                                        'nom_fichier' => '../ftp/transfert/library1/site20/notices.txt']));
+  }
+
+
+  /** @test */
+  public function library1ShouldHaveUsersPlanned() {
+    $this->assertNotNull(Class_IntMajAuto::findFirstBy(['id_bib' => 20,
+                                                        'profil' => 105,
+                                                        'type_operation' => Class_IntMajAuto::OP_FULL_IMPORT,
+                                                        'nom_fichier' => '../ftp/transfert/library1/site20/abonnes.txt']));
+  }
+
+
+  /** @test */
+  public function library1ShouldHaveLoansPlanned() {
+    $this->assertNotNull(Class_IntMajAuto::findFirstBy(['id_bib' => 20,
+                                                        'profil' => 102,
+                                                        'type_operation' => Class_IntMajAuto::OP_FULL_IMPORT,
+                                                        'nom_fichier' => '../ftp/transfert/library1/site20/prets.txt']));
+  }
+
+
+  /** @test */
+  public function library1ShouldNotHaveHoldsPlanned() {
+    $this->assertNull(Class_IntMajAuto::findFirstBy(['id_bib' => 20,
+                                                     'profil' => 103,
+                                                     'type_operation' => Class_IntMajAuto::OP_FULL_IMPORT,
+                                                     'nom_fichier' => '../ftp/transfert/library1/site20/reservations.txt']));
+  }
+
+
+  /** @test */
+  public function basketsProfileShouldBeCreated() {
+    $profile = Class_IntProfilDonnees::findFirstBy(['accents' => Class_IntProfilDonnees::ENCODING_UTF8,
+                                                    'type_fichier' => Class_IntProfilDonnees::FT_BASKETS,
+                                                    'format' => Class_IntProfilDonnees::FORMAT_PIPED_ASCII]);
+    $this->assertNotNull($profile);
+    return $profile;
+  }
+
+
+
+  /**
+   * @test
+   * @depends basketsProfileShouldBeCreated
+   */
+  public function library1ShouldHaveBasketsFullPlanned($profile) {
+    $this->assertNotNull(Class_IntMajAuto::findFirstBy(['id_bib' => 20,
+                                                        'profil' => $profile->getId(),
+                                                        'type_operation' => Class_IntMajAuto::OP_FULL_IMPORT,
+                                                        'nom_fichier' => '../ftp/transfert/library1/site20/paniers_total.txt']));
+  }
+
+
+  /**
+   * @test
+   * @depends basketsProfileShouldBeCreated
+   */
+  public function library1ShouldHaveBasketsPartialPlanned($profile) {
+    $this->assertNotNull(Class_IntMajAuto::findFirstBy(['id_bib' => 20,
+                                                        'profil' => $profile->getId(),
+                                                        'type_operation' => Class_IntMajAuto::OP_PARTIAL_IMPORT,
+                                                        'nom_fichier' => '../ftp/transfert/library1/site20/paniers.txt']));
+  }
+
+
+  /** @test */
+  public function adulteSectionShouldBeCreated() {
+    $this->assertNotNull(Class_CodifSection::findFirstBy(['libelle' => 'Adulte',
+                                                          'regles' => '995$9=1']));
+  }
+
+
+  /** @test */
+  public function coinLocationShouldBeCreated() {
+    $this->assertNotNull(Class_CodifEmplacement::findFirstBy(['libelle' => 'Coin des tout-petits',
+                                                              'regles' => '995$6=4']));
+  }
+
+
+  /** @test */
+  public function bdKindShouldBeCReated() {
+    $this->assertNotNull(Class_CodifGenre::findFirstBy(['libelle' => 'Biographie',
+                                                        'regles' => '995$7=3']));
+  }
+
+
+  /** @test */
+  public function shouldHaveCreated10Deweys() {
+    $this->assertEquals(10, Class_CodifDewey::count());
+  }
+
+
+  /** @test */
+  public function generaliteDeweyShouldHaveId0() {
+    $this->assertNotNull(Class_CodifDewey::findFirstBy(['id_dewey' => 0, 'libelle' => 'Généralités']));
+  }
+
+
+  /** @test */
+  public function histoireDeweyShouldHaveId9() {
+    $this->assertNotNull(Class_CodifDewey::findFirstBy(['id_dewey' => 9, 'libelle' => 'Histoire, géographie']));
+  }
+
+
+  /** @test */
+  public function shouldSayFinished() {
+    $this->assertXPathContentContains('//h2', 'Traitement terminé');
+  }
 }
\ No newline at end of file
diff --git a/cosmogramme/cosmozend/tests/bootstrap.php b/cosmogramme/cosmozend/tests/bootstrap.php
index 6151b95fc7fbb1adabe552877752ace5983a74f6..42ef07542e9f48174da7e7f14b4d5a6684b3e006 100644
--- a/cosmogramme/cosmozend/tests/bootstrap.php
+++ b/cosmogramme/cosmozend/tests/bootstrap.php
@@ -16,7 +16,7 @@
  *
  * 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 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 $cosmozendPath = realpath(dirname(__FILE__)) . '/../';
@@ -44,5 +44,4 @@ $_SERVER['SERVER_NAME'] = 'localhost';
 $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
 
 require_once 'CosmoControllerTestCase.php';
-
 ?>
\ No newline at end of file
diff --git a/cosmogramme/cosmozend/tests/bootstrap_frontcontroller.php b/cosmogramme/cosmozend/tests/bootstrap_frontcontroller.php
index 9117b75da9b8f0840e22107215dae58bcb78c7cf..4284852ad259fc36218f264c7c705f53eb0b9991 100644
--- a/cosmogramme/cosmozend/tests/bootstrap_frontcontroller.php
+++ b/cosmogramme/cosmozend/tests/bootstrap_frontcontroller.php
@@ -16,9 +16,13 @@
  *
  * 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 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
+Zend_Controller_Action_HelperBroker::resetHelpers();
+Zend_Controller_Action_HelperBroker::addHelper(new ZendAfi_Controller_Action_Helper_Cosmo_ViewRenderer());
+Zend_Controller_Action_HelperBroker::addPrefix('ZendAfi_Controller_Action_Helper_Cosmo');
+
 Zend_Layout::startMvc();
 Zend_Layout::getMvcInstance()->getView()
 ->addHelperPath('ZendAfi/View/Helper', 'ZendAfi_View_Helper');
diff --git a/cosmogramme/php/_menu.php b/cosmogramme/php/_menu.php
index 346fa582655c085848e3aefdf0d96c241221ee4d..0b27b1435522a3b4c563b5742ec8b847861d76a8 100644
--- a/cosmogramme/php/_menu.php
+++ b/cosmogramme/php/_menu.php
@@ -92,7 +92,7 @@ else
 	<div class="menu_section">Configurations</div>
 	<?php
 	ligneMenu("Variables","config_variables.php");
-	ligneMenu("Démarrer un nouvel OPAC","integre_generation_pergame.php");
+	ligneMenu("Démarrer un nouvel OPAC","../cosmozend/cosmo/integration/generate");
 	ligneMenu("Annexes","codif_annexe.php");
 	ligneMenu("Profils de données","config_profil_donnees.php");
 	ligneMenu("Intégrations programmées","config_integrations.php");
diff --git a/library/Class/Bib.php b/library/Class/Bib.php
index 95063416810e6bf843f52528178ca8c00fd5102d..e85b4753b1206046c604f4bcfebd931329501077 100644
--- a/library/Class/Bib.php
+++ b/library/Class/Bib.php
@@ -240,6 +240,7 @@ class Class_Bib extends Storm_Model_Abstract {
                                           'redmine_api_key' => ''];
 
   protected $_translate;
+  protected $_fixed_id = true;
 
   public function __construct() {
     parent::__construct();
diff --git a/library/Class/CodifDewey.php b/library/Class/CodifDewey.php
index 7c87cc69a31a7f47bac8bff19ff65b9ac9e7f922..c0e345541ff2d055ba8beba2442c398eda2fb14f 100644
--- a/library/Class/CodifDewey.php
+++ b/library/Class/CodifDewey.php
@@ -16,7 +16,7 @@
  *
  * 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 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 class Class_CodifDeweyLoader extends Storm_Model_Loader {
@@ -38,6 +38,7 @@ class Class_CodifDewey extends Storm_Model_Abstract {
   protected $_table_name = 'codif_dewey';
   protected $_table_primary = 'id_dewey';
   protected $_loader_class = 'Class_CodifDeweyLoader';
+  protected $_fixed_id = true;
 
 // ----------------------------------------------------------------
 // Rend une liste pour un champ suggestion
@@ -46,24 +47,24 @@ class Class_CodifDewey extends Storm_Model_Abstract {
     // Lancer la recherche
     $new = '';
     if($mode=="1") {
-      for($i=0; $i < strlen($recherche); $i++) 
+      for($i=0; $i < strlen($recherche); $i++)
         if($recherche[$i] >="0" and $recherche[$i] <= "9") $new.=$recherche[$i];
       $req="select id_dewey,libelle from codif_dewey where id_dewey like'".$new."%' order by id_dewey limit ".$limite_resultat;
     }
     if($mode=="2") $req="select id_dewey,libelle from codif_dewey where libelle like'".addslashes($recherche)."%' order by id_dewey limit ".$limite_resultat;
     if($mode=="3") $req="select id_dewey,libelle from codif_dewey where libelle like'%".addslashes($recherche)."%' order by id_dewey limit ".$limite_resultat;
-  
+
     $resultat=fetchAll($req);
-    
+
     // Mettre l'indice et le libelle
     if(!$resultat) return false;
     foreach($resultat as $enreg)
     {
       $liste[]=array($enreg["id_dewey"], $this->formatIndice($enreg["id_dewey"])." : ".$enreg["libelle"]);
     }
-    
+
     return $liste;
-  } 
+  }
 
 // ----------------------------------------------------------------
 // Rend une liste d'indices par niveau
@@ -71,7 +72,7 @@ class Class_CodifDewey extends Storm_Model_Abstract {
   static function getIndices($pere)
   {
     $sql = Zend_Registry::get('sql');
-    if($pere == "root") 
+    if($pere == "root")
       $liste=$sql->fetchAll("select * from codif_dewey where LENGTH(id_dewey)=1 order by id_dewey");
     else {
       $long=strlen($pere)+1;
@@ -80,7 +81,7 @@ class Class_CodifDewey extends Storm_Model_Abstract {
     }
     return $liste;
   }
-  
+
 // ----------------------------------------------------------------
 // Ponctue un indice dewey
 // ----------------------------------------------------------------
diff --git a/library/Class/Cosmogramme/Generator.php b/library/Class/Cosmogramme/Generator.php
new file mode 100644
index 0000000000000000000000000000000000000000..f96e6265ce8f01c576e08ae7ab4a1d3f3338139e
--- /dev/null
+++ b/library/Class/Cosmogramme/Generator.php
@@ -0,0 +1,506 @@
+<?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_Generator {
+  use Trait_Translator, Trait_Loggable, Trait_TimeSource;
+
+  protected $_params = [], $_landing_directory;
+
+
+  public function generate($params) {
+    $this->_params = $params;
+    $this->_landing_directory = Class_Cosmogramme_LandingDirectory::getInstance();
+
+    if (!$this->isValid())
+      return false;
+
+    $result = $this->_generateLibraries()
+      && $this->_generateSections()
+      && $this->_generateLocations()
+      && $this->_generateKinds()
+      && $this->_generateDewey();
+
+    $this->log('<br><h2>'. $this->_('Traitement terminé.') .'</h2>');
+
+    return $result;
+  }
+
+
+  protected function isValid() {
+    return $this->validateDirectory() && $this->validateProfiles();
+  }
+
+
+  protected function validateDirectory() {
+    return $this->_landing_directory->couldGenerateFrom($this->_params['path_ftp']);
+  }
+
+
+  protected function validateProfiles() {
+    return ($validator = $this->getProfilesValidator())
+      ? $validator->isValid() : true;
+  }
+
+
+  protected function getProfilesValidator() {
+    if (Class_IntBib::SIGB_NANOOK == $this->_params['type_sigb'])
+      return new Class_Cosmogramme_GeneratorProfilesNanookValidator();
+
+    if (Class_IntBib::SIGB_PERGAME == $this->_params['type_sigb'])
+      return new Class_Cosmogramme_GeneratorProfilesPergameValidator();
+
+    return null;
+  }
+
+
+  protected function _generateLibraries() {
+    if (!$libraries = $this->_landing_directory->getLibrariesOf($this->_params['path_ftp'])) {
+      $this->log($this->_('Étalon des bibliothèques vide'));
+      return false;
+    }
+
+    $this->_generateLibrariesEntities($libraries);
+    $this->_generateLibrariesBranches($libraries);
+    $this->_generatePlannedIntegrations($libraries);
+
+    return true;
+  }
+
+
+  protected function _generateLibrariesEntities($libraries) {
+    $this->logTitle($this->_('1 - Création des bibliothèques'));
+
+    $html = '';
+    foreach ($libraries as $data)
+      $html .= $this->_generateLibrary($data);
+
+    $this->log('<div class="liste"><table class="blank">' . $html . '</table></div>');
+  }
+
+
+  protected function _generateLibrary($data) {
+    if (!$elem = $this->_extractLibraryData($data))
+      return '';
+
+    if (!$bib = Class_Bib::findFirstBy(['id_site' => $elem[0]]))
+      $bib = new Class_Bib();
+
+    $bib->updateAttributes(['id_site' => $elem[0],
+                            'libelle' => trim($elem[1]),
+                            'ville' => $this->_params['path_ftp'],
+                            'id_zone' => 1,
+                            'visibilite' => Class_Bib::V_DATA])
+        ->save();
+
+    Class_IntBib::deleteBy(['id_bib' => $bib->getIdSite()]);
+    $int_bib = Class_IntBib::newInstance(['id_bib' => $bib->getIdSite(),
+                                          'nom' => $bib->getLibelle(),
+                                          'nom_court' => $bib->getLibelle(),
+                                          'qualite' => 5,
+                                          'sigb' => $this->_params['type_sigb'],
+                                          'planif_mode' => 'r',
+                                          'planif_jours' => '1111111',
+                                          'comm_sigb' => Class_IntBib::COM_PERGAME,
+                                          'comm_params' => serialize(['Autoriser_docs_disponibles' => '0',
+                                                                      'Max_par_carte' => '3',
+                                                                      'Max_par_document' => '3'])]);
+
+    if (Class_IntBib::SIGB_NANOOK == $this->_params['type_sigb'])
+      $int_bib
+        ->setCommSigb(Class_IntBib::COM_NANOOK)
+        ->setCommParams(serialize(['url_serveur' => $this->_params['service_nanook']]));
+
+		$int_bib->save();
+
+    return '<tr><td class="blank">Site n° '. $bib->getIdSite() . '</td><td class="blank">'. $bib->getLibelle() . '</td></tr>';
+  }
+
+
+  protected function _generateLibrariesBranches($libraries) {
+    $this->logTitle($this->_('2 - Création des annexes'));
+    if ('1' != $this->_params['creer_annexes']) {
+      $this->log($this->_('Non demandée'));
+      return;
+    }
+
+    $deleted = Class_CodifAnnexe::deleteBy([]);
+    $html = '<tr><td class="blank" colspan="2">' . $this->_('%d annexe(s) supprimée(s)', $deleted) . '</td></tr>';
+    foreach ($libraries as $data)
+      $html .= $this->_generateBranch($data);
+    $this->log('<div class="liste"><table class="blank">' . $html . '</table></div>');
+  }
+
+
+  protected function _generateBranch($data) {
+    if (!$elem = $this->_extractLibraryData($data))
+      return '';
+
+    $branch = Class_CodifAnnexe::newInstance(['id_bib' => $elem[0],
+                                              'code' => $elem[0],
+                                              'libelle' => trim($elem[1]),
+                                              'invisible' => 0]);
+    $branch->save();
+
+    return '<tr><td class="blank">Site n° '. $branch->getIdBib() .'</td><td class="blank">'. $branch->getLibelle() . '</td></tr>';
+  }
+
+
+  protected function _generatePlannedIntegrations($libraries) {
+    $this->logTitle($this->_('3 - Programmation des intégrations'));
+
+    $html = '';
+    foreach ($libraries as $data)
+      $html .= $this->_generatePlannedIntegration($data);
+
+    $this->log('<div class="liste"><table class="blank">' . $html . '</table></div>');
+  }
+
+
+  protected function _generatePlannedIntegration($data) {
+    if (!$elem = $this->_extractLibraryData($data))
+      return '';
+
+    $id_bib = $elem[0];
+    Class_IntMajAuto::deleteBy(['id_bib' => $id_bib]);
+    $base_path = $this->_landing_directory->getSubdirPath($this->_params['path_ftp']);
+    return $this->getPlannedGenerator()->plan($id_bib, $base_path);
+  }
+
+
+  protected function getPlannedGenerator() {
+    return (Class_IntBib::SIGB_NANOOK == $this->_params['type_sigb'])
+      ? new Class_Cosmogramme_GeneratorPlannedNanook()
+      : new Class_Cosmogramme_GeneratorPlannedPergame();
+  }
+
+
+  protected function _generateSections() {
+    $sections = $this->_landing_directory->getSectionsOf($this->_params['path_ftp']);
+    return (new Class_Cosmogramme_Generator_SectionsTask($this))->run($sections);
+  }
+
+
+  protected function _generateLocations() {
+    $locations = $this->_landing_directory->getLocationsOf($this->_params['path_ftp']);
+    return (new Class_Cosmogramme_Generator_LocationsTask($this))->run($locations);
+  }
+
+
+  protected function _generateKinds() {
+    $kinds = $this->_landing_directory->getKindsOf($this->_params['path_ftp']);
+    return (new Class_Cosmogramme_Generator_KindsTask($this))->run($kinds);
+  }
+
+
+  protected function _generateDewey() {
+    $this->logTitle($this->_('7 - Création des classes Dewey'));
+
+    Class_CodifDewey::deleteBy(['id_dewey' => range(0, 9)]);
+
+    $map = ['Généralités',
+            'Philosophie et disciplines connexes',
+            'Religion',
+            'Sciences sociales',
+            'Langues',
+            'Sciences de la nature et mathématiques',
+            'Technique (sciences appliquées)',
+            'Arts',
+            'Littérature (Belles-lettres)',
+            'Histoire, géographie'];
+
+    $html = '';
+    foreach($map as $k => $v) {
+      Class_CodifDewey::newInstance(['id_dewey' => $k, 'libelle' => $v])
+        ->save();
+      $html .= '<tr><td class="blank">'. $k .'</td><td class="blank">'. $v . '</td></tr>';
+    }
+
+    $this->log('<div class="liste"><table class="blank">' . $html . '</table></div>');
+
+    return true;
+  }
+
+
+  public function isPergame() {
+    return Class_IntBib::SIGB_PERGAME == $this->_params['type_sigb'];
+  }
+
+
+  public function isNanook() {
+    return Class_IntBib::SIGB_NANOOK == $this->_params['type_sigb'];
+  }
+
+
+  protected function logTitle($title) {
+    $this->log('<h3>' . $title . '</h3>');
+  }
+
+
+  protected function _extractLibraryData($data) {
+    $data = utf8_encode($data);
+		$elem = explode('|', $data);
+
+		return ($elem[0] == 'BIB_SPS_UTT') ? [] : $elem;
+  }
+}
+
+
+
+class Class_Cosmogramme_GeneratorProfilesAbstractValidator {
+  use Trait_Translator, Trait_Loggable;
+
+  public function isValid() {
+    if (!$definitions = $this->_getDefinitions())
+      return false;
+
+    foreach($this->_getDefinitions() as $parts)
+      if (!$this->_ensureProfile($parts[1], $parts[0], $parts[2]))
+        return false;
+
+    return true;
+  }
+
+
+  protected function _ensureProfile($label, $id, $type) {
+    if (!Class_IntProfilDonnees::findFirstBy(['id_profil' => $id, 'type_fichier' => $type])) {
+      $this->log($this->_('Profil de données %s (%d) non présent ou mal configuré', $label, $id));
+      return false;
+    }
+
+    return true;
+  }
+
+
+  protected function _getDefinitions() {
+    return [];
+  }
+}
+
+
+
+class Class_Cosmogramme_GeneratorProfilesNanookValidator
+  extends Class_Cosmogramme_GeneratorProfilesAbstractValidator {
+
+  protected function _getDefinitions() {
+    return[[104, 'Unimarc Nanook', Class_IntProfilDonnees::FT_RECORDS],
+           [105, 'Abonné Nanook', Class_IntProfilDonnees::FT_PATRONS]];
+  }
+}
+
+
+
+class Class_Cosmogramme_GeneratorProfilesPergameValidator
+  extends Class_Cosmogramme_GeneratorProfilesAbstractValidator {
+
+  protected function _getDefinitions() {
+    return[[100, 'Unimarc Pergame', Class_IntProfilDonnees::FT_RECORDS],
+           [101, 'Abonné Pergame', Class_IntProfilDonnees::FT_PATRONS],
+           [102, 'Prêts Pergame', Class_IntProfilDonnees::FT_LOANS],
+           [103, 'Réservations Pergame', Class_IntProfilDonnees::FT_HOLDS]];
+  }
+}
+
+
+
+class Class_Cosmogramme_GeneratorPlannedAbstract {
+  use Trait_Translator, Trait_Loggable;
+
+  protected
+    $_id_bib,
+    $_id_prog,
+    $_base_path,
+    $_html = '',
+    $_records_profile,
+    $_users_profile,
+    $_loans_profile = 102,
+    $_holds_profile = 103,
+    $_item_delete = false,
+    $_holds = false,
+    $_baskets = false;
+
+  public function plan($id_bib, $base_path) {
+    $this->_id_bib = $id_bib;
+    $this->_id_prog = $id_bib * 100;
+    $this->_base_path = $base_path . DIRECTORY_SEPARATOR . 'site' . $id_bib . DIRECTORY_SEPARATOR;
+
+    $this
+      ->itemDeletion()
+      ->recordsTotal()
+      ->recordsPartial()
+      ->users()
+      ->loans()
+      ->holds()
+      ->basketsTotal()
+      ->basketsPartial();
+
+    return $this->_html;
+  }
+
+
+  protected function itemDeletion() {
+    if (!$this->_item_delete)
+      return $this;
+
+    $profile = $this->_getBarCodeProfile();
+
+    return $this->planWith('Notices - suppression d\'exemplaires',
+                           $profile->getId(),
+                           Class_IntMajAuto::OP_ITEMS_DELETION,
+                           'suppressions.txt');
+  }
+
+
+  protected function recordsTotal() {
+    return $this->planWith('Notices - import total',
+                           $this->_records_profile,
+                           Class_IntMajAuto::OP_FULL_IMPORT,
+                           'notices_total.txt');
+  }
+
+
+  protected function recordsPartial() {
+    return $this->planWith('Notices - import incrémentiel',
+                           $this->_records_profile,
+                           Class_IntMajAuto::OP_PARTIAL_IMPORT,
+                           'notices.txt');
+  }
+
+
+  protected function users() {
+    return $this->planWith('Abonnés',
+                           $this->_users_profile,
+                           Class_IntMajAuto::OP_FULL_IMPORT,
+                           'abonnes.txt');
+  }
+
+
+  protected function loans() {
+    return $this->planWith('Prêts',
+                           $this->_loans_profile,
+                           Class_IntMajAuto::OP_FULL_IMPORT,
+                           'prets.txt');
+  }
+
+
+  protected function holds() {
+    return $this->_holds
+      ? $this->planWith('Réservations', $this->_holds_profile,
+                        Class_IntMajAuto::OP_FULL_IMPORT, 'reservations.txt')
+      : $this;
+  }
+
+
+  protected function basketsTotal() {
+    if (!$this->_baskets)
+      return $this;
+
+    $profile = $this->_getBasketProfile();
+
+    return $this->planWith('Paniers - import total',
+                           $profile->getId(),
+                           Class_IntMajAuto::OP_FULL_IMPORT,
+                           'paniers_total.txt');
+  }
+
+
+  protected function basketsPartial() {
+    if (!$this->_baskets)
+      return $this;
+
+    $profile = $this->_getBasketProfile();
+
+    return $this->planWith('Paniers - import incrémentiel',
+                           $profile->getId(),
+                           Class_IntMajAuto::OP_PARTIAL_IMPORT,
+                           'paniers.txt');
+  }
+
+
+  protected function planWith($name, $profile, $type, $file) {
+    $this->_html .= '<tr><td class="blank">Site n° '. $this->_id_bib . '</td><td class="blank">' . $name . '</td></tr>';
+    $this->_id_prog++;
+    Class_IntMajAuto::newInstance(['id_bib' => $this->_id_bib,
+                                   'libelle' => $name,
+                                   'profil' => $profile,
+                                   'type_operation' => $type,
+                                   'nom_fichier' => $this->_base_path . $file,
+                                   'rang' => $this->_id_prog])
+      ->save();
+
+    return $this;
+  }
+
+
+  protected function _getBarCodeProfile() {
+    $label = 'Liste de codes-barres';
+    if ($profile = Class_IntProfilDonnees::findFirstBy(['libelle' => $label]))
+      return $profile;
+
+
+    $attributs = 'a:6:{i:0;a:7:{s:8:"type_doc";a:11:{i:0;a:3:{s:4:"code";s:1:"0";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:1;a:3:{s:4:"code";s:1:"1";s:5:"label";s:5:"am;na";s:8:"zone_995";s:0:"";}i:2;a:3:{s:4:"code";s:1:"2";s:5:"label";s:2:"as";s:8:"zone_995";s:0:"";}i:3;a:3:{s:4:"code";s:1:"3";s:5:"label";s:3:"i;j";s:8:"zone_995";s:0:"";}i:4;a:3:{s:4:"code";s:1:"4";s:5:"label";s:1:"g";s:8:"zone_995";s:0:"";}i:5;a:3:{s:4:"code";s:1:"5";s:5:"label";s:3:"l;m";s:8:"zone_995";s:0:"";}i:6;a:3:{s:4:"code";s:1:"8";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:7;a:3:{s:4:"code";s:1:"9";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:8;a:3:{s:4:"code";s:2:"10";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:9;a:3:{s:4:"code";s:3:"100";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:10;a:3:{s:4:"code";s:2:"10";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}}s:17:"champ_code_barres";s:1:"f";s:10:"champ_cote";s:1:"k";s:11:"champ_genre";s:0:"";s:13:"champ_section";s:1:"q";s:17:"champ_emplacement";s:1:"u";s:12:"champ_annexe";s:1:"a";}i:1;a:1:{s:6:"champs";s:11:"code_barres";}i:2;a:1:{s:6:"champs";s:11:"code_barres";}i:3;a:1:{s:6:"champs";s:11:"code_barres";}i:5;a:3:{s:6:"champs";s:11:"code_barres";s:17:"xml_balise_abonne";s:0:"";s:17:"xml_champs_abonne";a:10:{s:6:"IDABON";s:0:"";s:9:"ORDREABON";s:0:"";s:3:"NOM";s:0:"";s:6:"PRENOM";s:0:"";s:9:"NAISSANCE";s:0:"";s:8:"PASSWORD";s:0:"";s:4:"MAIL";s:0:"";s:10:"DATE_DEBUT";s:0:"";s:8:"DATE_FIN";s:0:"";s:7:"ID_SIGB";s:0:"";}}i:4;a:5:{s:4:"zone";s:0:"";s:5:"champ";s:0:"";s:6:"format";s:0:"";s:5:"jours";s:0:"";s:7:"valeurs";s:0:"";}}';
+
+    $profile = Class_IntProfilDonnees::newInstance(['libelle' => $label,
+                                                    'accents' => Class_IntProfilDonnees::ENCODING_UTF8,
+                                                    'type_fichier' => Class_IntProfilDonnees::FT_RECORDS,
+                                                    'format' => Class_IntProfilDonnees::FORMAT_TABBED_ASCII,
+                                                    'attributs' => $attributs]);
+    $profile->save();
+    return $profile;
+  }
+
+
+  protected function _getBasketProfile() {
+    if ($profile = Class_IntProfilDonnees::findFirstBy(['accents' => Class_IntProfilDonnees::ENCODING_UTF8,
+                                                        'type_fichier' => Class_IntProfilDonnees::FT_BASKETS,
+                                                        'format' => Class_IntProfilDonnees::FORMAT_PIPED_ASCII]))
+      return $profile;
+
+    $profile = Class_IntProfilDonnees::newInstance(['libelle' => 'Paniers Nanook',
+                                                    'accents' => Class_IntProfilDonnees::ENCODING_UTF8,
+                                                    'rejet_periodiques' => 0,
+                                                    'id_article_periodique' => 4,
+                                                    'type_fichier' => Class_IntProfilDonnees::FT_BASKETS,
+                                                    'format' => Class_IntProfilDonnees::FORMAT_PIPED_ASCII,
+                                                    'attributs' => 'a:7:{i:0;a:9:{s:8:"type_doc";a:35:{i:0;a:3:{s:4:"code";s:1:"0";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:1;a:3:{s:4:"code";s:1:"1";s:5:"label";s:8:"am;na;mn";s:8:"zone_995";s:9:"LIV;MS;uu";}i:2;a:3:{s:4:"code";s:1:"2";s:5:"label";s:2:"as";s:8:"zone_995";s:3:"PER";}i:3;a:3:{s:4:"code";s:1:"3";s:5:"label";s:3:"i;j";s:8:"zone_995";s:17:"CD;LIVCD;LIVK7;K7";}i:4;a:3:{s:4:"code";s:1:"4";s:5:"label";s:1:"g";s:8:"zone_995";s:20:"DIAPO;DVD;VHS;VHD;VD";}i:5;a:3:{s:4:"code";s:1:"5";s:5:"label";s:6:"l;m;mm";s:8:"zone_995";s:3:"CDR";}i:6;a:3:{s:4:"code";s:1:"8";s:5:"label";s:0:"";s:8:"zone_995";s:1:"D";}i:7;a:3:{s:4:"code";s:1:"9";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:8;a:3:{s:4:"code";s:1:"8";s:5:"label";s:0:"";s:8:"zone_995";s:3:"DOS";}i:9;a:3:{s:4:"code";s:1:"9";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:10;a:3:{s:4:"code";s:2:"10";s:5:"label";s:0:"";s:8:"zone_995";s:6:"WEB;MF";}i:11;a:3:{s:4:"code";s:3:"102";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:12;a:3:{s:4:"code";s:3:"103";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:13;a:3:{s:4:"code";s:3:"104";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:14;a:3:{s:4:"code";s:3:"105";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:15;a:3:{s:4:"code";s:3:"106";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:16;a:3:{s:4:"code";s:3:"107";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:17;a:3:{s:4:"code";s:3:"108";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:18;a:3:{s:4:"code";s:3:"100";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:19;a:3:{s:4:"code";s:3:"101";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:20;a:3:{s:4:"code";s:3:"102";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:21;a:3:{s:4:"code";s:3:"103";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:22;a:3:{s:4:"code";s:3:"104";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:23;a:3:{s:4:"code";s:3:"105";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:24;a:3:{s:4:"code";s:3:"106";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:25;a:3:{s:4:"code";s:3:"107";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:26;a:3:{s:4:"code";s:3:"108";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:27;a:3:{s:4:"code";s:3:"109";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:28;a:3:{s:4:"code";s:3:"111";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:29;a:3:{s:4:"code";s:3:"110";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:30;a:3:{s:4:"code";s:3:"112";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:31;a:3:{s:4:"code";s:3:"114";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:32;a:3:{s:4:"code";s:3:"113";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:33;a:3:{s:4:"code";s:3:"115";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}i:34;a:3:{s:4:"code";s:3:"116";s:5:"label";s:0:"";s:8:"zone_995";s:0:"";}}s:17:"champ_code_barres";s:1:"a";s:10:"champ_cote";s:1:"f";s:14:"champ_type_doc";s:0:"";s:11:"champ_genre";s:1:"#";s:13:"champ_section";s:1:"w";s:17:"champ_emplacement";s:1:"x";s:12:"champ_annexe";s:1:"h";s:9:"champ_url";a:2:{s:4:"zone";s:0:"";s:5:"champ";s:0:"";}}i:1;a:1:{s:6:"champs";s:47:"ID_SIGB;LIBELLE;IDABON;MAIL;ROLE;ID_NOTICE_SIGB";}i:2;a:1:{s:6:"champs";s:47:"ID_SIGB;LIBELLE;IDABON;MAIL;ROLE;ID_NOTICE_SIGB";}i:3;a:1:{s:6:"champs";s:47:"ID_SIGB;LIBELLE;IDABON;MAIL;ROLE;ID_NOTICE_SIGB";}i:5;a:3:{s:6:"champs";s:47:"ID_SIGB;LIBELLE;IDABON;MAIL;ROLE;ID_NOTICE_SIGB";s:17:"xml_balise_abonne";s:0:"";s:17:"xml_champs_abonne";a:11:{s:6:"IDABON";s:0:"";s:9:"ORDREABON";s:0:"";s:3:"NOM";s:0:"";s:6:"PRENOM";s:0:"";s:9:"NAISSANCE";s:0:"";s:8:"PASSWORD";s:0:"";s:4:"MAIL";s:0:"";s:10:"DATE_DEBUT";s:0:"";s:8:"DATE_FIN";s:0:"";s:7:"ID_SIGB";s:0:"";s:9:"NUM_CARTE";s:0:"";}}i:4;a:5:{s:4:"zone";s:3:"995";s:5:"champ";s:1:"5";s:6:"format";s:1:"3";s:5:"jours";s:0:"";s:7:"valeurs";s:1:"1";}i:6;a:2:{s:4:"zone";s:0:"";s:5:"champ";s:0:"";}}']);
+    $profile->save();
+    return $profile;
+  }
+}
+
+
+
+class Class_Cosmogramme_GeneratorPlannedNanook extends Class_Cosmogramme_GeneratorPlannedAbstract{
+  protected
+    $_records_profile = 104,
+    $_users_profile = 105,
+    $_item_delete = true,
+    $_baskets = true;
+}
+
+
+
+class Class_Cosmogramme_GeneratorPlannedPergame extends Class_Cosmogramme_GeneratorPlannedAbstract{
+  protected
+    $_records_profile = 100,
+    $_users_profile = 101,
+    $_holds = true;
+}
\ No newline at end of file
diff --git a/library/Class/Cosmogramme/Generator/AbstractTask.php b/library/Class/Cosmogramme/Generator/AbstractTask.php
new file mode 100644
index 0000000000000000000000000000000000000000..f3b2730a43a0f586a927d88fbbf54c9057ed001d
--- /dev/null
+++ b/library/Class/Cosmogramme/Generator/AbstractTask.php
@@ -0,0 +1,126 @@
+<?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
+ */
+
+
+abstract class Class_Cosmogramme_Generator_AbstractTask {
+  use Trait_Translator, Trait_Loggable, Trait_TimeSource;
+
+  protected $_generator;
+
+  public function __construct($generator) {
+    $this->_generator = $generator;
+  }
+
+
+  public function run($datas) {
+    if (!$datas) {
+      $this->log($this->_('Étalon des %s vide', $this->_name));
+      return false;
+    }
+
+    $this->logTitle($this->_('%d - Création des %s', $this->_id, $this->_name));
+    $date = $this->getCurrentDate();
+
+    $datas = $this->_prepare($datas);
+    $html = '';
+    foreach($datas as $data)
+      $html .= $this->_runOne($data, $date);
+
+    $this->log('<div class="liste"><table class="blank">' . $html . '</table></div>');
+
+    $this
+      ->_directQuery('delete from ' . $this->_table . ' where date_maj != "' . $date. '"')
+      ->_directQuery('update ' . $this->_table . ' set date_maj=""');
+
+    return true;
+  }
+
+
+  protected function _runOne($data, $date) {
+    $data = utf8_encode($data);
+    $elems = explode('|', $data);
+
+    if ($this->_isExcluded($elems))
+      return '';
+
+    $label = trim($elems[1]);
+    $code = strtolower($elems[0]);
+
+    $model_class = $this->_model_class;
+    if (!$model = $model_class::findFirstBy(['libelle' => $label]))
+      $model = $model_class::newInstance(['libelle' => $label]);
+
+    $model
+      ->setRegles($this->_createOrConcatRules($code, $model, $date))
+      ->setDateMaj($date)
+      ->save();
+
+    return $this->renderOne($code, $label);
+  }
+
+
+  protected function _isExcluded($elems) {
+    return false;
+  }
+
+
+  protected function _directQuery($sql) {
+    Zend_Db_Table::getDefaultAdapter()->query($sql);
+    return $this;
+  }
+
+
+  protected function logTitle($title) {
+    $this->log('<h3>' . $title . '</h3>');
+  }
+
+
+  protected function renderOne($code, $label) {
+    return '<tr><td class="blank">' . $code . '</td><td class="blank">' . $label . '</td></tr>';
+  }
+
+
+  protected function _createOrConcatRules($code, $model, $date) {
+    return !$model->isNew() && $model->getDateMaj() == $date
+      ? $model->getRegles() . ';' . $code
+      : $this->_getRuleFor($code);
+  }
+
+
+  protected function _getRuleFor($code) {
+    return '';
+  }
+
+
+  protected function _prepare($datas) {
+    return $datas;
+  }
+
+
+  public function isPergame() {
+    return $this->_generator->isPergame();
+  }
+
+
+  public function isNanook() {
+    return $this->_generator->isNanook();
+  }
+}
\ No newline at end of file
diff --git a/library/Class/Cosmogramme/Generator/KindsTask.php b/library/Class/Cosmogramme/Generator/KindsTask.php
new file mode 100644
index 0000000000000000000000000000000000000000..355a96b11f0ec808c608d938957bebd5cc55c254
--- /dev/null
+++ b/library/Class/Cosmogramme/Generator/KindsTask.php
@@ -0,0 +1,83 @@
+<?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_Generator_KindsTask extends Class_Cosmogramme_Generator_AbstractTask {
+  protected
+    $_index = 6,
+    $_name = 'genres',
+    $_table = 'codif_genre';
+
+
+  protected function _prepare($datas) {
+    $this->_kinds = [];
+    foreach($datas as $data)
+      $this->_prepareOne($data);
+
+    return $this->_kinds;
+  }
+
+
+  protected function _prepareOne($data) {
+    $data = utf8_encode($data);
+    $elems = explode('|', $data);
+    if ('BIB_' == substr($elems[0], 0, 4) || !trim($elems[1]))
+      return '';
+
+    $key = Class_Indexation::getInstance()->alphaMaj($elems[2]);
+    $code = trim($elems[1]);
+    $label = trim($elems[2]);
+
+    if (array_key_exists($key, $this->_kinds)) {
+      $this->_kinds[$key]['codes'][] = $code;
+      return;
+    }
+
+    $this->_kinds[$key] = ['codes' => [$code], 'label' => $label];
+  }
+
+
+  protected function _runOne($data, $date) {
+    $label = $data['label'];
+    $codes = implode(';', $data['codes']);
+
+    if (!$model = Class_CodifGenre::findFirstBy(['libelle' => $label]))
+      $model = Class_CodifGenre::newInstance(['libelle' => $label]);
+
+    $model
+      ->setRegles($this->_getRuleFor($codes))
+      ->setDateMaj($date)
+      ->save();
+
+    return $this->renderOne($codes, $label);
+  }
+
+
+  protected function _getRuleFor($codes) {
+    $field = null;
+    if ($this->isNanook())
+      $field = '995$7';
+    if ($this->isPergame())
+      $field = '930$4';
+
+    if ($field)
+      return $field . '=' . $codes;
+  }
+}
diff --git a/library/Class/Cosmogramme/Generator/LocationsTask.php b/library/Class/Cosmogramme/Generator/LocationsTask.php
new file mode 100644
index 0000000000000000000000000000000000000000..90512c8173dfdbc1298331405bf07a7ec7b4c1af
--- /dev/null
+++ b/library/Class/Cosmogramme/Generator/LocationsTask.php
@@ -0,0 +1,38 @@
+<?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_Generator_LocationsTask extends Class_Cosmogramme_Generator_AbstractTask {
+  protected
+    $_index = 5,
+    $_name = 'emplacements',
+    $_table = 'codif_emplacement',
+    $_model_class = 'Class_CodifEmplacement';
+
+
+  protected function _isExcluded($elems) {
+    return 'BIB_' == substr($elems[0], 0, 4) || !trim($elems[0]);
+  }
+
+
+  protected function _getRuleFor($code) {
+    return '995$6=' . $code;
+  }
+}
diff --git a/library/Class/Cosmogramme/Generator/SectionsTask.php b/library/Class/Cosmogramme/Generator/SectionsTask.php
new file mode 100644
index 0000000000000000000000000000000000000000..97b53c0ef2e8fbc356abd7b2189fec96f5f8beeb
--- /dev/null
+++ b/library/Class/Cosmogramme/Generator/SectionsTask.php
@@ -0,0 +1,46 @@
+<?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_Generator_SectionsTask extends Class_Cosmogramme_Generator_AbstractTask {
+  protected
+    $_index = 4,
+    $_name = 'sections',
+    $_table = 'codif_section',
+    $_model_class = 'Class_CodifSection';
+
+
+  protected function _isExcluded($elems) {
+    return 'BIB_C_SECTION' == $elems[0] || !trim($elems[0]);
+  }
+
+
+  protected function _getRuleFor($code) {
+    $field = null;
+    if ($this->isNanook())
+      $field = '9';
+    if ($this->isPergame())
+      $field = 'q';
+
+    if ($field)
+      return '995$' . $field . '=' . strtolower($code);
+  }
+}
\ No newline at end of file
diff --git a/library/Class/Cosmogramme/LandingDirectory.php b/library/Class/Cosmogramme/LandingDirectory.php
new file mode 100644
index 0000000000000000000000000000000000000000..798c55efee2631ffe36012c8579f7a808ba8785d
--- /dev/null
+++ b/library/Class/Cosmogramme/LandingDirectory.php
@@ -0,0 +1,144 @@
+<?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_LandingDirectory {
+  use Trait_StaticFileSystem, Trait_Translator, Trait_Loggable, Trait_Singleton;
+
+  const VAR_NAME = 'ftp_path';
+  const STANDARD_NAME = 'etalon';
+
+  protected
+    $_path,
+    $_required_files = ['libraries' => 'annexes.txt',
+                        'kinds' => 'genres.txt',
+                        'sections' => 'sections.txt',
+                        'locations' => 'emplacements.txt'];
+
+
+  public function __construct() {
+    $this->_path = Class_CosmoVar::getValueOf(static::VAR_NAME);
+  }
+
+
+  public function isValid() {
+    if ($this->_path && $this->getFileSystem()->file_exists($this->_path))
+      return true;
+
+    $this->log($this->_('La variable ftp_path est absente, incorrecte ou le dossier n\'a pas été créé.'));
+    return false;
+  }
+
+
+  public function couldGenerateFrom($subdir) {
+    if (!$this->isValid())
+      return false;
+
+    if (!$subdir) {
+      $this->log($this->_('Aucun répertoire fourni'));
+      return false;
+    }
+
+    $file_system = $this->getFileSystem();
+    $standard_dir = $this->_getStandardDirPath($subdir);
+
+    if (!$file_system->is_readable($standard_dir)) {
+      $this->log($this->_('Répertoire %s non présent ou non lisible', $standard_dir));
+      return false;
+    }
+
+    foreach($this->_required_files as $file) {
+      $file_path = $this->_getStandardFilePath($subdir, $file);
+      if (!$file_system->is_readable($file_path)) {
+        $this->log($this->_('Fichier %s non présent ou non lisible', $file_path));
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+
+  public function getSubdirPath($subdir) {
+    return $this->_path
+      . (DIRECTORY_SEPARATOR != substr($this->_path, -1) ? DIRECTORY_SEPARATOR : '')
+      . $subdir;
+  }
+
+
+  protected function _getStandardDirPath($subdir) {
+    return $this->getSubdirPath($subdir) . DIRECTORY_SEPARATOR . static::STANDARD_NAME;
+  }
+
+
+  protected function _getStandardFilePath($subdir, $file) {
+    return $this->_getStandardDirPath($subdir) . DIRECTORY_SEPARATOR . $file;
+  }
+
+
+  public function getLibrariesOf($subdir) {
+    return $this->getFileNamed('libraries', $subdir);
+  }
+
+
+  public function getSectionsOf($subdir) {
+    return $this->getFileNamed('sections', $subdir);
+  }
+
+
+  public function getLocationsOf($subdir) {
+    return $this->getFileNamed('locations', $subdir);
+  }
+
+
+  public function getKindsOf($subdir) {
+    return $this->getFileNamed('kinds', $subdir);
+  }
+
+
+  protected function getFileNamed($name, $subdir) {
+    $file = $this->_getStandardFilePath($subdir, $this->_required_files[$name]);
+    return $this->getFileSystem()->file($file);
+  }
+
+
+  public function getSubdirectories() {
+    if (!$this->isValid())
+      return [];
+
+    $file_system = $this->getFileSystem();
+
+    if (!$dir = $file_system->opendir($this->_path))
+      return [];
+
+    $subdirs = [];
+    while (false !== $file = $file_system->readdir($dir)) {
+      if ('.' == substr($file, 0, 1)
+          || 'test' == $file
+          || !$file_system->is_dir($this->_path . $file))
+        continue;
+
+      $subdirs[$file] = $file;
+    }
+
+    return $subdirs;
+  }
+}
\ No newline at end of file
diff --git a/library/Class/IntBib.php b/library/Class/IntBib.php
index f5b8138b07d5cc1779e4424ba37b262afe7aaf04..1163fb5c4b424313f6436399b73a64f2a91460bb 100644
--- a/library/Class/IntBib.php
+++ b/library/Class/IntBib.php
@@ -43,6 +43,8 @@ class Class_IntBib extends Storm_Model_Abstract {
   const COM_CDSCRIPT = 12;
 
   const SIGB_NONE = 0;
+  const SIGB_PERGAME = 1;
+  const SIGB_NANOOK = 13;
 
   protected static $COM_CLASSES = [self::COM_PERGAME => 'Class_WebService_SIGB_Pergame',
                                    self::COM_OPSYS => 'Class_WebService_SIGB_Opsys',
diff --git a/library/Class/Log.php b/library/Class/Log.php
new file mode 100644
index 0000000000000000000000000000000000000000..b47bc1cad0b634f4a7edefcebdb7cc817fa4dfcd
--- /dev/null
+++ b/library/Class/Log.php
@@ -0,0 +1,48 @@
+<?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_Log {
+  use Trait_Singleton;
+
+  protected $_messages = [];
+
+  public function log($message) {
+    $this->_messages[] = $message;
+    return $this;
+  }
+
+
+  public function getLastMessage() {
+    return end($this->_messages);
+  }
+
+
+  public function getMessages() {
+    return $this->_messages;
+  }
+
+
+  public function reset() {
+    $this->_messages = [];
+    return $this;
+  }
+}
\ No newline at end of file
diff --git a/library/Class/Testing/FileSystem.php b/library/Class/Testing/FileSystem.php
index 09bba9a1ac942d30300f97617b184ff768c720f6..5f7a756a1eda8492047a59eb388a8974e87af5d1 100644
--- a/library/Class/Testing/FileSystem.php
+++ b/library/Class/Testing/FileSystem.php
@@ -25,8 +25,9 @@ class Class_Testing_FileSystem {
      'rmdir', 'unlink', 'fopen', 'fseek', 'fgets',
      'filesize', 'fclose', 'ftell', 'fread', 'feof',
      'getcwd', 'file_exists', 'scandir', 'is_dir',
-     'opendir', 'readdir', 'closedir', 'mkdir','glob','file', 'fwrite','rename',
-     'getimagesize', 'file_get_contents', 'sha1_file', 'is_file', 'pathinfo'
+     'opendir', 'readdir', 'closedir', 'mkdir', 'glob', 'file', 'fwrite','rename',
+     'getimagesize', 'file_get_contents', 'sha1_file', 'is_file', 'pathinfo',
+     'is_readable'
     ];
 
 
diff --git a/library/Trait/Loggable.php b/library/Trait/Loggable.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f29e8ce4099f93a9d57ff1fe3e4586cd3159d0a
--- /dev/null
+++ b/library/Trait/Loggable.php
@@ -0,0 +1,33 @@
+<?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
+ */
+
+
+trait Trait_Loggable {
+  public function getLog() {
+    return Class_Log::getInstance();
+  }
+
+
+  public function log($message) {
+    $this->getLog()->log($message);
+    return $this;
+  }
+}
\ No newline at end of file
diff --git a/library/ZendAfi/Controller/Action/Helper/Cosmo/ViewRenderer.php b/library/ZendAfi/Controller/Action/Helper/Cosmo/ViewRenderer.php
new file mode 100644
index 0000000000000000000000000000000000000000..fdf40231cfe4876cee520fd25ec90110775fc5a6
--- /dev/null
+++ b/library/ZendAfi/Controller/Action/Helper/Cosmo/ViewRenderer.php
@@ -0,0 +1,27 @@
+<?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 ZendAfi_Controller_Action_Helper_Cosmo_ViewRenderer extends Zend_Controller_Action_Helper_ViewRenderer{
+  public function __construct(Zend_View_Interface $view = null, array $options = array()) {
+    parent::__construct(new ZendAfi_CosmoView(), $options);
+  }
+}
\ No newline at end of file
diff --git a/library/ZendAfi/CosmoView.php b/library/ZendAfi/CosmoView.php
new file mode 100644
index 0000000000000000000000000000000000000000..26da1e600cbed5bb0c3eb4164bbd1d9b5139117c
--- /dev/null
+++ b/library/ZendAfi/CosmoView.php
@@ -0,0 +1,25 @@
+<?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 ZendAfi_CosmoView extends Zend_View {
+  use Trait_Translator;
+}
diff --git a/library/ZendAfi/View/Helper/CosmoError.php b/library/ZendAfi/View/Helper/CosmoError.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef94d80d5c430b3cbd5dfb1f0e6c544742d2aeea
--- /dev/null
+++ b/library/ZendAfi/View/Helper/CosmoError.php
@@ -0,0 +1,29 @@
+<?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 ZendAfi_View_Helper_CosmoError extends ZendAfi_View_Helper_BaseHelper {
+  public function cosmoError($message) {
+    return $this->_tag('br').$this->_tag('br')
+      . $this->_tag('center', $this->_tag('font', $message, ['size' => '5', 'color' => 'red']))
+      . $this->_tag('br').$this->_tag('br');
+  }
+}
\ No newline at end of file