diff --git a/FEATURES/80658 b/FEATURES/80658
deleted file mode 100644
index 58d2b67bedd6bc1b612eba1707d1443759eb034c..0000000000000000000000000000000000000000
--- a/FEATURES/80658
+++ /dev/null
@@ -1,10 +0,0 @@
-        '80658' =>
-            ['Label' => $this->_('Facettes dynamiques sur des sous-chaînes de champs unimarc'),
-             'Desc' => $this->_('Vous pouvez créer des facettes sur des portions d\'un champ unimarc. Par exemple, sur un champ 993$w formatté comme ceci: 2018-05-04, vous pouvez créer trois facettes sur l\'année, le mois et le jour.')',
-             'Image' => 'http://wiki.bokeh-library-portal.org/images/4/41/Dynamic_facet_public.png',
-             'Video' => 'https://youtu.be/KYYN22dqhnM',
-             'Category' => '',
-             'Right' => function($feature_description, $user) {return true;},
-             'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php?title=Facettes_dynamiques#A_partir_d.27une_partie_du_libell.C3.A9_d.27un_champ_unimarc',
-             'Test' => '',
-             'Date' => '2019-01-18'],
\ No newline at end of file
diff --git a/VERSIONS b/VERSIONS
index d9f960bf44523a5439e3e3f4b685c9b1b46fa5b7..c7f5df4a3db7a37f308cb78416b0b9ccf873398b 100644
--- a/VERSIONS
+++ b/VERSIONS
@@ -1,3 +1,54 @@
+07/02/2019 - v8.0.2
+
+ - ticket #86474 : Compte lecteur : Correction de la compatibilité du multi-carte avec l'authentification par le SIGB uniquement
+
+
+05/02/2019 - v8.0.1
+
+ - ticket #86381 : SIGB Nanook : Correction de l'authentification par SIGB uniquement
+
+
+04/02/2019 - v8.0.0
+
+ - ticket #86010 : Administration : activation des fonctionnalités de la version 8.0 .
+
+ - ticket #85632 : Administration : Variables : ajout de la colonne niveau d'accès et ouverture de certaines variables au rôle administrateur.
+
+ - ticket #86120 : Administration : Mise à jour du logo du logiciel Bokeh.
+
+ - ticket #69586 : Administration : Ajout de la possibilité d'inclure des articles dans les newsletters
+
+ - ticket #85637 : Administration : maintenance des entrées de menu gauche et ajout de liens vers la documentation.
+
+ - ticket #83983 : Adminsistraton : Amélioration de l'ergonomie de la navigation dans la gestion des bibliothèques
+ 
+ - ticket #85026 : Administration : ajout d'une valeur par défaut dans le formulaire de rattachement d'un panier à un  domaine.
+
+ - ticket #86172 : Enrichissements : import des identifiants Wikidata, BNF Ark, ISNI et YouTube dans la codification des auteurs. Enrichissement YouTube et INA des pages auteur.
+
+ - ticket #86160 : Enrichissements : maintenance des enrichissements Premiere
+
+ - ticket #86018 : Ressources numériques : Augmentation de la taille de la vignette générée pour les notices de ressources numériques
+
+ - ticket #80658 : Facettes dynamiques : possibilité de créer des facettes dynamiques sur des dates
+
+ - ticket #80876 : Abonnés : Ajout de la possibilité d'utiliser la fonction mot de passe oublié même si l'usager ne s'est encore jamais connecté sur Bokeh
+
+ - ticket #84280 : Cosmogramme : correction de la détection des fichiers en attente composés avec une date
+ 
+ - ticket #85822 : Maintenance : Retrait d'un lien obsolète vers les composants Flash non supportés
+
+ - ticket #81435 : Catalogue : Affichage basique d'une notice autorité matière 
+ 
+
+
+28/01/2019 - v7.12.56
+
+ - ticket #85532 : Administration : Les tickets d'assistance au statut "Réaliser à tester" sont comptabilisés dans le menu haut "Assistance"
+
+ - ticket #84896 : SIGB Nanook : activation de la réinitialisation des mots de passe par courriel
+
+
 21/01/2019 - v7.12.55
 
  - ticket #83545 : Administration, contrôle des URLS : corrections de la prise en charge des URLs contenant des caractères spéciaux
diff --git a/VERSIONS_WIP/80658 b/VERSIONS_WIP/80658
deleted file mode 100644
index dbe62c644a6b5db7889d79412b970fa3d8d9c81b..0000000000000000000000000000000000000000
--- a/VERSIONS_WIP/80658
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #80658 : Facettes dynamiques : possibilité de créer des facettes dynamiques sur des dates
\ No newline at end of file
diff --git a/VERSIONS_WIP/80876 b/VERSIONS_WIP/80876
deleted file mode 100644
index f85e7b4d3140e6933723a4f3fcc5b78accaf50c0..0000000000000000000000000000000000000000
--- a/VERSIONS_WIP/80876
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #80876 : Abonnés : Ajout de la possibilité d'utiliser la fonction mot de passe oublié même si l'usager ne s'est encore jamais connecté sur Bokeh
\ No newline at end of file
diff --git a/VERSIONS_WIP/81435 b/VERSIONS_WIP/81435
deleted file mode 100644
index 3483d7bd18773b2d40cb30f6a85cdb3e516b6aff..0000000000000000000000000000000000000000
--- a/VERSIONS_WIP/81435
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #81435 : Catalogue : Affichage basique d'une notice autorité
\ No newline at end of file
diff --git a/VERSIONS_WIP/83983 b/VERSIONS_WIP/83983
deleted file mode 100644
index 094937f9dbaad92618373de01bc1e851ceee5f30..0000000000000000000000000000000000000000
--- a/VERSIONS_WIP/83983
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #83983 : Amélioration de l'ergonomie de la navigation dans l'administration des bibliothèques
\ No newline at end of file
diff --git a/VERSIONS_WIP/84280 b/VERSIONS_WIP/84280
deleted file mode 100644
index ebf0032507a1389012b9c77a47423c633d174ff1..0000000000000000000000000000000000000000
--- a/VERSIONS_WIP/84280
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #84280 : Cosmogramme : correction de la détection des fichiers en attente composés avec une date
\ No newline at end of file
diff --git a/VERSIONS_WIP/85632 b/VERSIONS_WIP/85632
deleted file mode 100644
index ed0d8247f4f1e170c4b1739d6873dae3e59cd9d3..0000000000000000000000000000000000000000
--- a/VERSIONS_WIP/85632
+++ /dev/null
@@ -1 +0,0 @@
- - ticket #85632 : Administration : Variables : ajout de la colonne niveau d'accès et ouverture de certaines variables au rôle administrateur.
\ No newline at end of file
diff --git a/application/modules/admin/controllers/CatalogueController.php b/application/modules/admin/controllers/CatalogueController.php
index 59e586a3a2105f6dac1e59856eae05a101c1b91d..536fcb5aba3a2ee6db85ebc4f5806a7b2e87ae22 100644
--- a/application/modules/admin/controllers/CatalogueController.php
+++ b/application/modules/admin/controllers/CatalogueController.php
@@ -200,34 +200,37 @@ class Admin_CatalogueController extends ZendAfi_Controller_Action {
     if (!$catalogue = Class_Catalogue::find((int)$this->_getParam("id_catalogue")))
       return $this->_redirect("admin/catalogue/index");
 
-    if ($this->_request->isPost()) {
-      $panier = Class_PanierNotice::find($this->_request->getPost('id_panier'));
-      $catalogue->addPanierNotice($panier)->save();
-      $panier->addCatalogue($catalogue)->save();
-      $panier->index();
-      $this->_helper->notify($this->_('Panier "%s" ajouté', $panier->getLibelle()));
-      $this->_redirect('admin/catalogue/paniers/id_catalogue/' . $catalogue->getId());
-      return;
-    }
-
     if ($id_panier_to_remove = (int)$this->_getParam('remove')) {
       $panier = Class_PanierNotice::find($id_panier_to_remove);
       $catalogue->removePanierNotice($panier)->save();
       $panier->removeCatalogue($catalogue)->save();
 
       $this->_helper->notify($this->_('Panier "%s" retiré', $panier->getLibelle()));
-      $this->_redirect('admin/catalogue/paniers/id_catalogue/' . $catalogue->getId());
+      return $this->_redirect('admin/catalogue/paniers/id_catalogue/' . $catalogue->getId());
+    }
+
+    if (!$this->_request->isPost()) {
+      $this->view->form_paniers = $this->formAjoutPanier($catalogue);
+      $this->view->catalogue = $catalogue;
+      $this->view->titre = $this->_('Paniers du domaine: %s', $catalogue->getLibelle());
       return;
     }
 
-    $this->view->form_paniers = $this->formAjoutPanier($catalogue);
-    $this->view->catalogue = $catalogue;
-    $this->view->titre = $this->_('Paniers du domaine: %s', $catalogue->getLibelle());
+    if (!$panier = Class_PanierNotice::find((int)$this->_request->getPost('id_panier'))) {
+      $this->_helper->notify($this->_('Aucun panier sélectionné pour ajout'));
+      return $this->_redirect('admin/catalogue/paniers/id_catalogue/' . $catalogue->getId());
+    }
+
+    $catalogue->addPanierNotice($panier)->save();
+    $panier->addCatalogue($catalogue)->save();
+    $panier->index();
+    $this->_helper->notify($this->_('Panier "%s" ajouté', $panier->getLibelle()));
+    $this->_redirect('admin/catalogue/paniers/id_catalogue/' . $catalogue->getId());
   }
 
 
   protected function formAjoutPanier($catalogue) {
-    $options = [];
+    $options = ['' => $this->_('Veuiller sélectionner un panier')];
     $paniers = Class_PanierNotice::findAllBelongsToAdmin();
     foreach($paniers as $panier)
       $options[$panier->getId()] = $panier->getLibelleForAdmins();
diff --git a/application/modules/admin/controllers/RecordsController.php b/application/modules/admin/controllers/RecordsController.php
index 88e3f9e5ca2bdc9b5e971e2404e94864690d9b18..1b5e2ee32b486f235f54ee6773f9d8fc5d1af20c 100644
--- a/application/modules/admin/controllers/RecordsController.php
+++ b/application/modules/admin/controllers/RecordsController.php
@@ -49,6 +49,9 @@ class Admin_RecordsController extends ZendAfi_Controller_Action {
 
 
   public function thumbnailAction() {
+    if (!$record = Class_Notice::find((int)$this->_getParam('id')))
+      return $this->_redirectClose($this->_getReferer());
+
     $this->view->titre = $this->_('Modifier la vignette');
 
     $form = new ZendAfi_Form_VignetteNotice(['action' => $this->view->url(['module' => 'admin',
@@ -67,7 +70,7 @@ class Admin_RecordsController extends ZendAfi_Controller_Action {
     }
 
     $this->view->form = $form;
-    $this->view->url_vignette = Class_Notice::find((int)$this->_getParam('id'))->fetchUrlVignette();
+    $this->view->url_vignette = $record->fetchUrlLocalVignette();
   }
 
 
diff --git a/application/modules/opac/controllers/AbonneController.php b/application/modules/opac/controllers/AbonneController.php
index cfb930f01864d6b322febc2476c69a2bdf04b0af..e3ced754dd5164bb35d3c36aeec2578f8d636231 100644
--- a/application/modules/opac/controllers/AbonneController.php
+++ b/application/modules/opac/controllers/AbonneController.php
@@ -61,11 +61,12 @@ class AbonneController extends ZendAfi_Controller_Action {
 
 
   protected function clearEmprunteurCache() {
-    if (in_array($this->getRequest()->getActionName(), ['prets',
-                                                        'reservations',
-                                                        'fiche',
-                                                        'loans-history']))
-      Class_WebService_SIGB_EmprunteurCache::newInstance()->remove($this->_user);
+    if (!in_array($this->getRequest()->getActionName(),
+                  ['prets', 'reservations', 'fiche', 'loans-history']))
+      return;
+
+    foreach((new Class_User_Cards($this->_user)) as $user)
+      Class_WebService_SIGB_EmprunteurCache::newInstance()->remove($user);
   }
 
 
@@ -1224,14 +1225,13 @@ class AbonneController extends ZendAfi_Controller_Action {
 
     $this->view->form = $form;
 
-
     if (!$this->_request->isPost() || !$form->isValid($this->_request->getPost()))
       return $this;
 
-    if ((!$child = Class_Users::findFirstBy(['login' => $this->_getPost('login'),
-                                             'password' => $this->_getPost('password')]))
-        || !$child->isAbonne()) {
+    $child = Class_Auth::getInstance()
+      ->authenticateLoginPassword($this->_getPost('login'), $this->_getPost('password'));
 
+    if (!$child || !$child->isAbonne()) {
       $this->_helper->notify($this->_('Identifiant et/ou mot de passe incorrect'));
       return $this->_redirect('/abonne/add-card');
     }
diff --git a/application/modules/opac/controllers/AuthorController.php b/application/modules/opac/controllers/AuthorController.php
index 4326af5c7b7e0baf24e0444cd3c6cbde180c83c0..a234108d9922cd0abbb9307b852ed81067067890 100644
--- a/application/modules/opac/controllers/AuthorController.php
+++ b/application/modules/opac/controllers/AuthorController.php
@@ -24,11 +24,48 @@ class AuthorController extends ZendAfi_Controller_Action {
     if (!$author = $this->_findAuthor())
       throw new Zend_Controller_Action_Exception($this->view->_('Désolé, cette page n\'existe pas'), 404);
 
+    $this->_addInspectorGadget($author);
     $this->view->author_description = new Class_CodifAuteur_Description($author);
     $this->view->titre = $this->view->_('Auteur');
   }
 
 
+  public function renderYoutubeChannelAction() {
+    session_write_close();
+
+    if (!$author = $this->_findAuthor())
+      return $this->_helper->HTMLAjaxResponse('');
+
+    $items = [];
+    foreach((new Class_WebService_Youtube())
+            ->videosOfChannel($author->getYoutubeChannelId()) as $video) {
+      $items[] = $this->view->tag('div',
+                                  $this->view->tag('iframe', '', ['src' => $video->getEmbedUrl(),
+                                                                  'allow' => 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture',
+                                                                  'allowfullscreen' => 'true'])
+                                  .
+                                  $this->view->tag('h3', $video->getTitle()));
+    }
+
+    $html = $this->view->tag('div', implode(array_slice($items, 0, 3)));
+
+    $this->_helper->HTMLAjaxResponse($html);
+  }
+
+
+  protected function _addInspectorGadget($author) {
+    if (!$ig = Zend_Controller_Front::getInstance()
+         ->getPlugin('ZendAfi_Controller_Plugin_InspectorGadget'))
+      return;
+
+    if(!$ig->isEnabled())
+      return;
+
+    $ig->addButton(new Class_Entity(['Label' => $this->_('Auteur Bokeh'),
+                                     'Content' => $this->view->renderAuthorMetadata($author)]));
+  }
+
+
   protected function _findAuthor() {
     if ($id = (int)$this->_getParam('id'))
       return Class_CodifAuteur::find($id);
diff --git a/application/modules/opac/controllers/NoticeajaxController.php b/application/modules/opac/controllers/NoticeajaxController.php
index 68fcc879af570c8e7769c8592fd9686283af3181..e4e4c43c7d920e8be06cac4dd95fa4892e74d2cb 100644
--- a/application/modules/opac/controllers/NoticeajaxController.php
+++ b/application/modules/opac/controllers/NoticeajaxController.php
@@ -34,12 +34,6 @@ class NoticeAjaxController extends ZendAfi_Controller_Action {
 
   public function preDispatch() {
     parent::preDispatch();
-    // Desactiver le view renderer normal pour tous les modes sauf notice
-    if ($this->_request->getParam("action") != "notice") {
-      $viewRenderer = $this->getHelper('ViewRenderer');
-      $viewRenderer->setNoRender();
-    }
-
     $this->notice = $this->extractNoticeFromRequest();
     $this->id_notice = $this->notice->getId();
   }
@@ -101,12 +95,8 @@ class NoticeAjaxController extends ZendAfi_Controller_Action {
   public function renderExemplaires($notice_ids, $nb_notices_oeuvre, $display) {
     session_write_close();
 
-    $this->getResponse()->setHeader('Content-Type', 'text/html;charset=utf-8');
-
-    if (!$this->notice->hasExemplaires()) {
-      $this->getResponse()->setBody('');
-      return;
-    }
+    if (!$this->notice->hasExemplaires())
+      return $this->_sendResponse('');
 
     $exemplaires = $this->_loadExemplaire(["id_notice" => $notice_ids]);
     foreach($exemplaires as $exemplaire)
@@ -343,6 +333,7 @@ class NoticeAjaxController extends ZendAfi_Controller_Action {
   public function videosAction() {
     $width = $this->_getParam('width', 500) - 8; //8px du skin de l'INA sur lequel on n'a pas la main
     $height = (int)($width * 4/5);
+    $author = $this->_getParam('author', $this->notice->getAuteurPrincipal());
 
     if ($num_video = $this->_getParam("num_video", 0))  {
       $num_video = $num_video-1;
@@ -352,8 +343,8 @@ class NoticeAjaxController extends ZendAfi_Controller_Action {
     }
 
     unset($_SESSION["video_interview"]);
-    $notice=$this->notice->getNotice("JA");
-    if(!trim($notice["A"])) {
+
+    if(!$author) {
       $html=$this->notice_html->getNonTrouve($this->view->_("Cette notice n'a pas d'auteur."),true);
       $this->_sendResponseWithScripts($html);
       return;
@@ -361,10 +352,8 @@ class NoticeAjaxController extends ZendAfi_Controller_Action {
 
 
     if($this->service_afi > "")   {
-      $args = ['titre'  => $notice['J'],
-               'auteur' => $notice['A'],
-               'width'  => $width,
-               'height' => $height];
+      $args = ['auteur' => $author,
+               'width'  => $width];
       $data = Class_WebService_AllServices::runServiceAfiInterviews($args);
       $source = $data["source"];
       $videos = $data["videos"];
@@ -477,7 +466,6 @@ class NoticeAjaxController extends ZendAfi_Controller_Action {
 
 
   protected function _sendResponse($data) {
-    $this->getResponse()->setHeader('Content-Type', 'text/html;charset=utf-8');
-    $this->getResponse()->setBody($data);
+    $this->_helper->HTMLAjaxResponse($data);
   }
 }
\ No newline at end of file
diff --git a/application/modules/opac/controllers/RechercheController.php b/application/modules/opac/controllers/RechercheController.php
index 85547352ab3a59deb88a42283d1759083e601695..a3f3eebdf500f9f49891aa6ed749b2285a693716 100644
--- a/application/modules/opac/controllers/RechercheController.php
+++ b/application/modules/opac/controllers/RechercheController.php
@@ -517,6 +517,7 @@ class RechercheController extends ZendAfi_Controller_Action {
     $viewRenderer->setNoRender();
     session_write_close();
 
+    $this->_response->setHeader('Content-Type', 'image/png');
     $record = Class_Notice::find((int)$this->_getParam('id'));
     (new Class_WebService_Vignette())
       ->renderThumbnail($record->getId(),
diff --git a/application/modules/opac/controllers/RssController.php b/application/modules/opac/controllers/RssController.php
index 123c5ebb2400037cde2afb71e6a41191e48104ee..d3a0929cd3aef9d0be04850013b64e47b27481ab 100644
--- a/application/modules/opac/controllers/RssController.php
+++ b/application/modules/opac/controllers/RssController.php
@@ -290,6 +290,6 @@ class RssController extends Zend_Controller_Action
                    $notice->getAuteurPrincipal(),
                    $notice->getEditeur(),
                    $notice->getAnnee(),
-                   $this->view->absoluteUrl($notice->fetchUrlVignette()));
+                   $this->view->absoluteUrl($notice->fetchUrlLocalVignette()));
   }
 }
diff --git a/application/modules/opac/views/scripts/java/cube.phtml b/application/modules/opac/views/scripts/java/cube.phtml
index be5921e8fad87a9ff29eec460442514789ee8328..e74636ffdcfa519ff4cecdae237c3cb3e14d71ba 100644
--- a/application/modules/opac/views/scripts/java/cube.phtml
+++ b/application/modules/opac/views/scripts/java/cube.phtml
@@ -9,7 +9,7 @@ Class_ScriptLoader::getInstance()
 $html = '';
 foreach($this->notices as $notice)
   $html .= $this->tagAnchor($this->urlNotice($notice, $this->preferences, null, true),
-                            $this->tagImg($this->absoluteUrl($notice->fetchUrlVignette()),
+                            $this->tagImg($this->absoluteUrl($notice->fetchUrlLocalVignette()),
                                           ['title' => $notice->getTitrePrincipal()]),
                             ['target' => '_parent']);
 
diff --git a/application/modules/opac/views/scripts/java/diaporama.phtml b/application/modules/opac/views/scripts/java/diaporama.phtml
index 1d52fb62d6733186b1da91b7fb4418ecb97aa6eb..a2933ccb2ce98f1d3bc28e6e8ff6a44115643ad5 100644
--- a/application/modules/opac/views/scripts/java/diaporama.phtml
+++ b/application/modules/opac/views/scripts/java/diaporama.phtml
@@ -8,7 +8,7 @@ Class_ScriptLoader::getInstance()
 $records_html = '';
 foreach($this->notices as $notice)  {
   $onclick = "window.parent.location='". $this->urlNotice($notice, $this->preferences, null, true) . "';";
-  $records_html .= $this->tagImg($this->absoluteUrl($notice->fetchUrlVignette(true)),
+  $records_html .= $this->tagImg($this->absoluteUrl($notice->fetchUrlLocalVignette()),
                                  ['title' => $notice->getTitrePrincipal(),
                                   'onclick' => $onclick,
                                   'width' => $this->preferences["op_largeur_img"] . 'px',
diff --git a/application/modules/opac/views/scripts/java/jcarousel.phtml b/application/modules/opac/views/scripts/java/jcarousel.phtml
index 5b2db389b790f18f68d72d81cc84bcd78ff7b548..a1dba08814937278a2dec8a436af408433b2f093 100644
--- a/application/modules/opac/views/scripts/java/jcarousel.phtml
+++ b/application/modules/opac/views/scripts/java/jcarousel.phtml
@@ -49,7 +49,7 @@ function mycarousel_initCallback(carousel) {
 
 $lis = '';
 foreach($this->notices as $notice) {
-  $img = $this->tagImg($this->absoluteUrl($notice->fetchUrlVignette()),
+  $img = $this->tagImg($this->absoluteUrl($notice->fetchUrlLocalVignette()),
                        ['title' => $notice->getTitrePrincipal(),
                         'width' => $this->preferences["op_largeur_img"],
                         'height' => $this->preferences["op_hauteur_img"]]);
diff --git a/application/modules/opac/views/scripts/java/mycarousel_partial.phtml b/application/modules/opac/views/scripts/java/mycarousel_partial.phtml
index ce588d2660c0e0b0ddbe00674d33825f5aead62e..0a1d793f2b57f8f7c09e188add18262469ba19d8 100644
--- a/application/modules/opac/views/scripts/java/mycarousel_partial.phtml
+++ b/application/modules/opac/views/scripts/java/mycarousel_partial.phtml
@@ -1,7 +1,7 @@
 <?php
 $lis = '';
 foreach($this->records as $record) {
-  $img = $this->tagImg($this->absoluteUrl($record->fetchUrlVignette()),
+  $img = $this->tagImg($this->absoluteUrl($record->fetchUrlLocalVignette()),
                        ['title' => $record->getTitrePrincipal(),
                         'width' => $this->preferences["op_largeur_img"],
                         'height' => $this->preferences["op_hauteur_img"]]);
diff --git a/application/modules/opac/views/scripts/java/protoflow.phtml b/application/modules/opac/views/scripts/java/protoflow.phtml
index 3b0c263c82060827068fb41fdad49bc03a4a0dfb..0a3d26d63200b4ea9e3c63aaa6b2dde0a8f27032 100644
--- a/application/modules/opac/views/scripts/java/protoflow.phtml
+++ b/application/modules/opac/views/scripts/java/protoflow.phtml
@@ -15,7 +15,7 @@ Class_ScriptLoader::getInstance()
 
 $anchors = '';
 foreach($this->notices as $notice)  {
-  $img = $this->tagImg($this->absoluteUrl($notice->fetchUrlVignette()),
+  $img = $this->tagImg($this->absoluteUrl($notice->fetchUrlLocalVignette()),
                        ['border' => '0',
                         'width' => $this->preferences['op_largeur_img'],
                         'height' => $this->preferences['op_hauteur_img'],
diff --git a/application/modules/opac/views/scripts/java/slide_show.phtml b/application/modules/opac/views/scripts/java/slide_show.phtml
index 3ee457eabc2872368720041922cfd89ae4348624..63f9ca5e0d8d56dc1f50fcacc53b908235d9b4f9 100644
--- a/application/modules/opac/views/scripts/java/slide_show.phtml
+++ b/application/modules/opac/views/scripts/java/slide_show.phtml
@@ -14,7 +14,7 @@ if(!$hauteur) $hauteur=110;
 $images = '';
 foreach($this->notices as $notice){
   $images .= $this->tagAnchor($this->urlNotice($notice, $this->preferences, null, true),
-                              $this->tagImg($this->absoluteUrl($notice->fetchUrlVignette()),
+                              $this->tagImg($this->absoluteUrl($notice->fetchUrlLocalVignette()),
                                             ['alt' => $this->_('vignette notice') . ' ' . $notice->getTitrePrincipal(),
                                              'title' => $notice->getTitrePrincipal(),
                                              'width'  => $largeur,
diff --git a/application/modules/opac/views/scripts/noticeajax/notice.phtml b/application/modules/opac/views/scripts/noticeajax/notice.phtml
index 629f08f843145b2ad6660c718944f2569cd6cfee..0d5feea7c3b8821cbab36043ce80658b7124d0d2 100644
--- a/application/modules/opac/views/scripts/noticeajax/notice.phtml
+++ b/application/modules/opac/views/scripts/noticeajax/notice.phtml
@@ -1,7 +1,7 @@
 <table border="0" cellpadding="3" cellspacing="1">
     <tr>
       <td rowspan="2" valign="top">
-        <img src="<?php print($this->notice->fetchUrlVignette()); ?>" width="90px" onclick="<?php print($this->notice->fetchUrlImage());?> " style="cursor:pointer"/>
+        <img src="<?php print($this->notice->fetchUrlLocalVignette()); ?>" width="90px" onclick="<?php print($this->notice->fetchUrlImage());?> " style="cursor:pointer"/>
       </td>
       <td valign="top" width="550px">
         <table width="100%">
diff --git a/application/modules/telephone/views/scripts/recherche/viewnotice.phtml b/application/modules/telephone/views/scripts/recherche/viewnotice.phtml
index 6143477e2dae01923a2b8a01e89d54e5ae0f5b71..7bcdb5c83e6b75faf1cb25bf238e0cadacf0a93b 100644
--- a/application/modules/telephone/views/scripts/recherche/viewnotice.phtml
+++ b/application/modules/telephone/views/scripts/recherche/viewnotice.phtml
@@ -4,7 +4,7 @@ echo $this->toolbar($this->_("Recherche"),
 
 <ul data-role="listview" data-inset="true" class="doctype_<?php echo $this->notice->getTypeDoc();?>">
   <li data-theme="c">
-    <img src="<?php echo $this->notice->fetchUrlVignette();?>">
+    <img src="<?php echo $this->notice->fetchUrlLocalVignette();?>">
     <h3><?php echo $this->iconeSupport($this->notice->getTypeDoc()) . $this->notice->getTitrePrincipal() ;?></h3>
     <p class="serie">
       <?php
diff --git a/cosmogramme/sql/patch/patch_364.php b/cosmogramme/sql/patch/patch_364.php
new file mode 100644
index 0000000000000000000000000000000000000000..5726d7a178f346f9a10667b64dc047cad78908ef
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_364.php
@@ -0,0 +1,10 @@
+<?php
+$adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
+
+try {
+  $adapter->query('ALTER TABLE newsletters ADD articles_ids varchar(255) NULL default NULL');
+} catch(Exception $e) {}
+
+try {
+  $adapter->query('ALTER TABLE newsletters ADD articles_categories_ids varchar(255) NULL default NULL');
+} catch(Exception $e) {}
diff --git a/cosmogramme/sql/patch/patch_365.php b/cosmogramme/sql/patch/patch_365.php
new file mode 100644
index 0000000000000000000000000000000000000000..2fe0a194df255756149cd40ddcf4470b28432b1c
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_365.php
@@ -0,0 +1,2 @@
+<?php
+(new Class_Migration_BokehEight)->run();
\ No newline at end of file
diff --git a/cosmogramme/sql/patch/patch_366.php b/cosmogramme/sql/patch/patch_366.php
new file mode 100644
index 0000000000000000000000000000000000000000..a56677501337231dfff02e9060a83454ecd7b6f0
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_366.php
@@ -0,0 +1,10 @@
+<?php
+$adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
+
+try {
+  $adapter->query('alter table codif_auteur add column wikidata_id varchar(255)');
+  $adapter->query('alter table codif_auteur add column youtube_channel_id varchar(255)');
+  $adapter->query('alter table codif_auteur add column isni varchar(255)');
+  $adapter->query('alter table codif_auteur add column ark varchar(255)');
+  $adapter->query('alter table codif_auteur add index wikidata_id (wikidata_id)');
+} catch(Exception $e) {}
diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php
index e97e42be25625cb159d49690aeca99b5b3d87940..cf17b95e0f169a0d191db83c6b7baeeb1b9e1c9a 100644
--- a/library/Class/AdminVar.php
+++ b/library/Class/AdminVar.php
@@ -118,14 +118,14 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
     if (null !== $this->all_vars)
       return $this->all_vars;
 
-    return $this->all_vars=
+    return $this->all_vars =
       [
        'avis' => $this->_getCommentVars(),
        'modo' => $this->_getModerationVars(),
        'stat' => $this->_getStatVars(),
        'global' => $this->_getGlobalVars(),
        'cosmogramme' => $this->_getCosmogrammeVars(),
-       'catalogue' => $this->_getOAIVars(),
+       'catalogue' => $this->_getCatalogVars(),
        'newsletter' => $this->_getNewsletterVars(),
        'cms' => $this->_getCmsVars(),
        'album' => $this->_getAlbumVars(),
@@ -154,8 +154,11 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
 
             'ALBUM_MEDIA_NO_AUTOINDEX_TITLE' => Class_AdminVar_Meta::newOnOff($this->_('Bibliothèque numérique: ne pas créer de zone 464 pour les médias dont le titre n\'est pas saisi')),
 
-            'BIBNUM' => Class_AdminVar_Meta::newOnOff($this->_('Activer ou désactiver la bibliothèque numérique')),
-            'SITO_IN_ALBUMS' => Class_AdminVar_Meta::newOnOff($this->_('Gérer la sitothèque dans la bibliothèque numérique, nécessite l\'activation de la bibliothèque numérique'))->bePrivate(),
+            'BIBNUM' => Class_AdminVar_Meta::newOnOff($this->_('Activer ou désactiver la bibliothèque numérique'))->enable(),
+            'IMPORT_EAD' => Class_AdminVar_Meta::newOnOff($this->_('Activation de l\'import EAD')),
+            'OAI_REPOSITORY' => Class_AdminVar_Meta::newOnOff($this->_('Activation de l\'import de dépôts OAI')),
+            'OPDS' => Class_AdminVar_Meta::newOnOff($this->_('Activation de l\'import de catalogues OPDS'))->enable(),
+            'SITO_IN_ALBUMS' => Class_AdminVar_Meta::newOnOff($this->_('Gérer la sitothèque dans la bibliothèque numérique, nécessite l\'activation de la bibliothèque numérique')),
 
             'NUMILOG_URL' => Class_AdminVar_Meta::newDefault($this->_('Paramétrage <a href="http://forge.afi-sa.fr/projects/opac3/wiki/Ressources_externes_enrichissements#Numilog">Numilog</a>'))->bePrivate(),
             'NUMILOG_OAI_URL' => Class_AdminVar_Meta::newDefault($this->_('Paramétrage <a href="http://forge.afi-sa.fr/projects/opac3/wiki/Ressources_externes_enrichissements#Numilog">Numilog</a>'))->bePrivate(),
@@ -259,9 +262,9 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
                                                                                            ['name' => 'site_url', 'label' => $this->_('Url de recherche')] ]]]),
        'NOM_DOMAINE' => Class_AdminVar_Meta::newDefault($this->_('Nom de domaine principal de l\'OPAC, ex: monopac.macommune.fr')),
 
-       'CUSTOM_SEARCH_FORM' => Class_AdminVar_Meta::newOnOff($this->_('Activer les formulaires de recherche configurables'))->bePrivate(),
+       'CUSTOM_SEARCH_FORM' => Class_AdminVar_Meta::newOnOff($this->_('Activer les formulaires de recherche configurables'))->enable(),
        'ENABLE_SEARCH_MULTIPLE_RECORD_SELECTION' => Class_AdminVar_Meta::newOnOff($this->_('Activer la sélection multiple de notices dans le résultat de recherche')),
-       'AUTHOR_PAGE' => Class_AdminVar_Meta::newOnOff($this->_('Activer les fiches auteurs, tous les liens de recherche vers un auteur basculeront sur la fiche auteur')),
+       'AUTHOR_PAGE' => Class_AdminVar_Meta::newOnOff($this->_('Activer les fiches auteurs, tous les liens de recherche vers un auteur basculeront sur la fiche auteur'))->enable(),
       ];
   }
 
@@ -283,7 +286,7 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
             'WORKFLOW_TEXT_MAIL_ARTICLE_PENDING' => Class_AdminVar_Meta::newDefault($this->_('Contenu de l\'email de notification d\'article en attente de validation. Termes substitués: TITRE_ARTICLE, URL_ARTICLE, AUTHOR_ARTICLE, SAVED_BY_ARTICLE, STATUS_ARTICLE, NEXT_STATUS_ARTICLE')),
             'WORKFLOW_TEXT_MAIL_ARTICLE_REFUSED' => Class_AdminVar_Meta::newDefault($this->_('Contenu de l\'email de notification de refus d\'un article à valider. Termes substitués: TITRE_ARTICLE, URL_ARTICLE, AUTHOR_ARTICLE, SAVED_BY_ARTICLE')),
             'WORKFLOW_TEXT_MAIL_ARTICLE_VALIDATED' => Class_AdminVar_Meta::newDefault($this->_('Contenu de l\'email de notification de validation d\'un article. Termes substitués: TITRE_ARTICLE, URL_ARTICLE, AUTHOR_ARTICLE, SAVED_BY_ARTICLE')),
-
+            'CUSTOM_GENRE_ICON' => Class_AdminVar_Meta::newOnOff($this->_('Activation de l\'interface de personnalisation des icones des genres')),
     ];
   }
 
@@ -307,7 +310,7 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
             'FORCE_HTTPS' => Class_AdminVar_Meta::newOnOff($this->_('Forcer l\'accès au site par le protocole HTTPS. Nécessite l\'installation et la configuration appropriée du serveur Web')),
             'LOGIN_THROUGH_SIGB_ONLY' => Class_AdminVar_Meta::newOnOff($this->_('Les abonné.e.s peuvent se connecter uniquement via le webservice du SIGB.')
                                                                        . '<br/>'
-                                                                       . $this->_('De plus, à la connexion, l\'enregistrement des mots de passes des abonnés est désactivé.'))->bePrivate(),
+                                                                       . $this->_('De plus, à la connexion, l\'enregistrement des mots de passes des abonnés est désactivé.')),
             'OAUTH_ACCEPT_HTTP' => Class_AdminVar_Meta::newOnOff($this->_('Autoriser l\'accès aux API OAUTH via HTTP (non sécurisé - déconseillé)'), ['value' => 0]),
             'NB_AFFICH_AVIS_PAR_AUTEUR'  => Class_AdminVar_Meta::newDefault($this->_('Nombre d\'avis maximum à afficher par utilisateur.')),
             'REGISTER_OK' => Class_AdminVar_Meta::newEncodedData($this->_('Texte visible par l\'internaute après son inscription.')),
@@ -364,11 +367,15 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
             'STATUS_REPORT_PUSH_URL' => Class_AdminVar_Meta::newRawText($this->_('URL destinataire du rapport d\'état du système (0 pour désactiver)'), ['value' => 'http://pola.afi-sa.net/smile.php'])->bePrivate(),
             'STATUS_REPORT_TAGS' => Class_AdminVar_Meta::newMultiInput($this->_('Liste des tags à ajouter au rapport d\'état du système'))->bePrivate(),
             'FEATURES_TRACKING_ENABLE' => Class_AdminVar_Meta::newOnOff('Affiche les dernières modifications apportés au logiciel Bokeh', ['value' => 1]),
-            'INSPECTOR_GADGET_MARC_XML' => Class_AdminVar_Meta::newOnOff('Affiche le MARC XML de la notice dans Inspector Gadget'),
+            'INSPECTOR_GADGET_MARC_XML' => Class_AdminVar_Meta::newOnOff('Affiche le MARC XML de la notice dans Inspector Gadget')->enable(),
             'BUSINESS_EMAIL' => Class_AdminVar_Meta::newDefault($this->_('Email utilisé pour les demandes d\'accompagnement de mise en place ou d\'utilisation des fonctionnalités du logiciel'), ['value' => 'cial-bib@afi-sa.fr'])->bePrivate(),
-            'CODIFICATION_BROWSER' => Class_AdminVar_Meta::newOnOff($this->_('Activer l\'outil de parcours des codifications')),
+            'CODIFICATION_BROWSER' => Class_AdminVar_Meta::newOnOff($this->_('Activer l\'outil de parcours des codifications'))->enable(),
             'TEMPLATING' => Class_AdminVar_Meta::newOnOff('Active l\'accès au magasin de thèmes',
-                                                          ['value' => 0])->bePrivate()
+                                                          ['value' => 0])->bePrivate(),
+            'GENERATION_SITE' => Class_AdminVar_Meta::newOnOff($this->_('Activation de la génération de site.')),
+            'CUSTOM_FIELDS_REPORT' => Class_AdminVar_Meta::newOnOff($this->_('Activation des rapports statistiques sur les champs personnalisés')),
+            'WEBSERVICE_TEST' => Class_AdminVar_Meta::newOnOff($this->_('Activation des tests de webservices')),
+            'IMPORT_AVIS_OPAC2' => Class_AdminVar_Meta::newOnOff($this->_('Activation de l\'import des avis de l\'opac2')),
 ];
   }
 
@@ -407,8 +414,9 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
   }
 
 
-  protected function _getOAIVars() {
+  protected function _getCatalogVars() {
     return [
+            'IMPORT_THESAURUS' => Class_AdminVar_Meta::newOnOff($this->_('Activation de l\'import de thesaurus')),
             'OAI_SERVER' => Class_AdminVar_Meta::newOnOff($this->_('Activation du serveur OAI: permet le moissonnage des domaines par d\'autres logiciels via OAI')),
             'OAI_REPOSITORY_NAME' => Class_AdminVar_Meta::newDefault($this->_('Contenu de la balise "repositoryName" dans la réponse au verb Identify, si vide sera [NOM DU SERVEUR] Oai repository')),
             'OAI_ADMIN_EMAIL' => Class_AdminVar_Meta::newDefault($this->_('Contenu de la balise "adminEmail" dans la réponse au verb Identify, si vide sera tiré de la variable cosmogramme "mail_admin"')),];
@@ -877,6 +885,51 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
   }
 
 
+  public static function isImportThesaurusEnabled() {
+    return Class_AdminVar::isModuleEnabled('IMPORT_THESAURUS');
+  }
+
+
+  public static function isImportEadEnabled() {
+    return Class_AdminVar::isModuleEnabled('IMPORT_EAD');
+  }
+
+
+  public static function isOaiRepositoryEnabled() {
+    return Class_AdminVar::isModuleEnabled('OAI_REPOSITORY');
+  }
+
+
+  public static function isOpdsEnabled() {
+    return Class_AdminVar::isModuleEnabled('OPDS');
+  }
+
+
+  public static function isCustomGenreIconEnabled() {
+    return Class_AdminVar::isModuleEnabled('CUSTOM_GENRE_ICON');
+  }
+
+
+  public static function isGenerationSiteEnabled() {
+    return Class_AdminVar::isModuleEnabled('GENERATION_SITE');
+  }
+
+
+  public static function isCustomFieldsReportEnabled() {
+    return Class_AdminVar::isModuleEnabled('CUSTOM_FIELDS_REPORT');
+  }
+
+
+  public static function isWebserviceTestEnabled() {
+    return Class_AdminVar::isModuleEnabled('WEBSERVICE_TEST');
+  }
+
+
+  public static function isImportAvisOpac2Enabled() {
+    return Class_AdminVar::isModuleEnabled('IMPORT_AVIS_OPAC2');
+  }
+
+
   public function getBabelthequeId() {
     $mathes = [];
     if (preg_match('/bw_([^\.]+)\.js/', (string)Class_AdminVar::get('BABELTHEQUE_JS'), $matches))
@@ -939,6 +992,11 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
   }
 
 
+  public function isAuthorPageEnabled() {
+    return Class_AdminVar::isModuleEnabled('AUTHOR_PAGE');
+  }
+
+
   public function shouldKeepLastSigbRecord() {
     return Class_AdminVar::isModuleEnabled('KEEP_LAST_SIGB_RECORD');
   }
diff --git a/library/Class/AdminVar/Meta.php b/library/Class/AdminVar/Meta.php
index a60af071dbfbb74ebcfb4e16ac499cc6d0e53dc0..8cf86285c80532fcdb85dcbfbc1d8e4b251e514f 100644
--- a/library/Class/AdminVar/Meta.php
+++ b/library/Class/AdminVar/Meta.php
@@ -161,6 +161,12 @@ class Class_AdminVar_Meta {
   }
 
 
+  public function setValue($value) {
+    $this->_attributes['value'] = $value;
+    return $this;
+  }
+
+
   public function bePrivate() {
     $this->_attributes['role_level'] = ZendAfi_Acl_AdminControllerRoles::SUPER_ADMIN;
     return $this;
@@ -170,4 +176,12 @@ class Class_AdminVar_Meta {
   public function isPrivate() {
     return ZendAfi_Acl_AdminControllerRoles::SUPER_ADMIN == $this->getAttribute('role_level', null);
   }
+
+
+  public function enable() {
+    if (! $this->isOnOff())
+      return $this;
+
+    return $this->setValue(1);
+  }
 }
\ No newline at end of file
diff --git a/library/Class/Album.php b/library/Class/Album.php
index 0252f6b3066be0d103a49b6cb3c5eb1007f5a01f..1a0b3683d32639c7c0c17372d6ca94525b3e1a71 100644
--- a/library/Class/Album.php
+++ b/library/Class/Album.php
@@ -739,7 +739,7 @@ class Class_Album extends Storm_Model_Abstract {
 
     try {
       $image = new Imagick($this->getVignettePath());
-      $image->thumbnailImage(160, 0);
+      $image->thumbnailImage(300, 0);
 
       if (!$image->writeImage($this->getThumbnailPath())) {
         $this->addError('Erreur lors de l\'enregistrement de la vignette');
diff --git a/library/Class/Article/MailRenderer.php b/library/Class/Article/MailRenderer.php
new file mode 100644
index 0000000000000000000000000000000000000000..04239b891861096eb7c74f5f43176ff699ad541d
--- /dev/null
+++ b/library/Class/Article/MailRenderer.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Copyright (c) 2012-2019, 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_Article_MailRenderer {
+  use Trait_Translator;
+
+  public function renderTextInto($article, &$data) {
+    if (!$article)
+      return;
+
+    $data[] = '- ' . $article->getLabel();
+    $data[] = strip_tags($article->getFullContent());
+    $data[] = $this->_('Lien: ') . Class_Url::absolute(['controller' => 'cms',
+                                                        'action' => 'viewarticle',
+                                                        'id' => $article->getId()], null, true);
+    $data[] = "\n";
+  }
+
+
+  public function renderHtml($article) {
+    if (!$article)
+      return '';
+
+    return '<div style="padding:5px">' .
+      '<h1>' . $article->getLabel() . '</h1>' .
+      '<div>' . $article->getFullContent() . '</div>' .
+      '<div style="clear:both"></div>'.
+      '</div>';
+  }
+}
diff --git a/library/Class/Auth.php b/library/Class/Auth.php
new file mode 100644
index 0000000000000000000000000000000000000000..e9a2c8b20a4ed91d094a7486d68c857f9ee5d038
--- /dev/null
+++ b/library/Class/Auth.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright (c) 2012-2018, 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_Auth {
+  use Trait_Singleton;
+
+  public function authenticateLoginPassword($login, $password) {
+    $adapters = [new ZendAfi_Auth_Adapter_CommSigb(),
+                 new ZendAfi_Auth_Adapter_DbTable()];
+
+    foreach ($adapters as $adapter) {
+      if ((new ZendAfi_Auth_TryHarder($this, $adapter))->tryHarder($login, $password))
+        return $adapter->getAuthenticatedUser();;
+
+      if ($adapter->shouldBreakChain())
+        return;
+    }
+  }
+
+
+  public function authenticate($adapter) {
+    return $adapter->authenticate();
+  }
+}
diff --git a/library/Class/AvisNotice.php b/library/Class/AvisNotice.php
index 5e2c05e622ace988bfc3c2bc19d5f4930da02c4d..9e77876e86d7aee62eb750cc4001be5de91e5cda 100644
--- a/library/Class/AvisNotice.php
+++ b/library/Class/AvisNotice.php
@@ -435,7 +435,7 @@ class Class_AvisNotice  extends Storm_Model_Abstract {
   public function getUrlVignette() {
     if (!$notice = $this->getFirstNotice())
       return '';
-    return $notice->fetchUrlVignette();
+    return $notice->fetchUrlLocalVignette();
   }
 
 
diff --git a/library/Class/Bib.php b/library/Class/Bib.php
index 32a8e4217b26b18f090736e1b97f3304cf220cff..aa742310a53b563645884241bffe264dd0ef10e9 100644
--- a/library/Class/Bib.php
+++ b/library/Class/Bib.php
@@ -56,6 +56,13 @@ class BibLoader extends Storm_Model_Loader {
   }
 
 
+  public function findAllRedmineEnabled() {
+    return (new Storm_Model_Collection(Class_Bib::findAllBy(['order' => 'libelle'])))
+      ->select('isRedmineEnabled')
+      ->getArrayCopy();
+  }
+
+
   public function getUserFriendlyBibs() {
     $all_bibs = Class_Bib::getLoader()->findAllBy(['order' => 'libelle']);
     $bibs_as_array = [];
@@ -732,6 +739,11 @@ class Class_Bib extends Storm_Model_Abstract {
   }
 
 
+  public function isRedmineEnabled() {
+    return (bool)$this->getRedmineLoginOrKey();
+  }
+
+
   public function isReceivedFile() {
     if (!$this->_isFileInRequest('photo'))
       return true;
diff --git a/library/Class/CodifAuteur.php b/library/Class/CodifAuteur.php
index 2d7944ccae8c8c26fc7e381c1095d09e977749ca..cf45e5818da69827a02980a63d73b49ad2ee2d2f 100644
--- a/library/Class/CodifAuteur.php
+++ b/library/Class/CodifAuteur.php
@@ -118,7 +118,11 @@ class Class_CodifAuteur extends Storm_Model_Abstract {
     $_default_attribute_values = ['libelle' => '',
                                   'mots_renvois' => '',
                                   'id_bnf' => '',
-                                  'date_creation' => ''];
+                                  'date_creation' => '',
+                                  'wikidata_id' => '',
+                                  'youtube_channel_id' => '',
+                                  'isni' => '',
+                                  'ark' => ''];
 
   public function getCategorie() {
     return;
diff --git a/library/Class/CodifAuteur/Description.php b/library/Class/CodifAuteur/Description.php
index bf73f47d7ee1d9c2142cc21ce73bc0c37a1e0597..dc080df7f0248f141b227ac4fb800c353601410d 100644
--- a/library/Class/CodifAuteur/Description.php
+++ b/library/Class/CodifAuteur/Description.php
@@ -34,6 +34,11 @@ class Class_CodifAuteur_Description {
   }
 
 
+  public function getId() {
+    return $this->_author->getId();
+  }
+
+
   public function getLabel() {
     return $this->_author->getLibelle();
   }
@@ -61,6 +66,11 @@ class Class_CodifAuteur_Description {
   }
 
 
+  public function getYoutubeChannelId() {
+    return $this->_author->getYoutubeChannelId();
+  }
+
+
   public function getRecords() {
     if (isset($this->_all_records))
       return $this->_all_records;
@@ -93,6 +103,14 @@ class Class_CodifAuteur_Description {
                                    $this->getRecords(),
                                    $this->getAssociatedAuthors());
 
+    $author_attribs = array_intersect_key($data, ['wikidata_id' => '',
+                                                  'youtube_channel_id' => '',
+                                                  'ark' => '',
+                                                  'isni' => '']);
+    $this->_author
+      ->updateAttributes($author_attribs)
+      ->save();
+
     if ($biography = $data['biographie']) {
       $biography = $this->_enrichBiographyWithRecords($biography,
                                                       $this->getRecords(),
@@ -157,7 +175,9 @@ class Class_CodifAuteur_Description {
     $associated_authors = [];
     foreach($facets as $code => $count) {
       $id = substr($code, 1);
-      $associated_authors[$id] = ['author' => Class_CodifAuteur::find($id),
+      if (!$collaborator = Class_CodifAuteur::find($id))
+        continue;
+      $associated_authors[$id] = ['author' => $collaborator,
                                   'occurences' => $count];
     }
 
@@ -221,7 +241,7 @@ class Class_CodifAuteur_Description {
                                      'action' => 'view']) . '/id/';
     foreach($associated_authors as $association) {
       $author_label = $association['author']->getLibelle();
-      $search []= '%\b' . str_replace('%', '\%', preg_quote($author_label)) . '\b%i';
+      $search []= '%\b' . str_replace('%', '\%', preg_quote($author_label)) . '\b%u';
       $replace []=  $view->tag('a',
                                $author_label,
                                ['href' => $base_url . $association['author']->getId(),
diff --git a/library/Class/CosmoVar.php b/library/Class/CosmoVar.php
index 5dbbd818a50a91f29b56ef6fcf26064e50c51cb8..2d947484e044bead92e5a26303d698592989138e 100644
--- a/library/Class/CosmoVar.php
+++ b/library/Class/CosmoVar.php
@@ -115,7 +115,15 @@ class Class_CosmoVar extends Storm_Model_Abstract {
     $_table_name = 'variables',
     $_table_primary = 'clef',
     $_loader_class = 'Class_CosmoVarLoader',
-    $_fixed_id = true;
+    $_fixed_id = true,
+    $_default_attribute_values = ['commentaire' => '',
+                                  'type_champ' => 0,
+                                  'liste' => '',
+                                  'groupe' => 0,
+                                  'ordre' => 0,
+                                  'verrou' => '',
+                                  // 'hidden' => 0, warning : do not uncomment as script/opac2.sql does not define this column
+    ];
 
 
   public static function get($name) {
diff --git a/library/Class/Feature/List.php b/library/Class/Feature/List.php
index 1c517a02d62d22e942634dc148fd6be202183904..beb9305fa14c6f8e9411eed77b6cc0c2fca70b02 100644
--- a/library/Class/Feature/List.php
+++ b/library/Class/Feature/List.php
@@ -568,6 +568,27 @@ class Class_Feature_List {
              'Test' => '/author/view/named/molière',
              'Date' => '2018-11-19'],
 
+            '69586' =>
+            ['Label' => $this->_('Inclure des articles dans les lettres d\'information'),
+             'Desc' => $this->_('Bokeh permet d\'envoyer des articles dans les lettres d\'information.'),
+             'Image' => '',
+             'Video' => '',
+             'Category' => $this->_('Rédaction'),
+             'Right' => function($feature_description, $user) {return true;},
+             'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php?title=Lettre_d\'information',
+             'Test' => '',
+             'Date' => '2018-01-10'],
+        '80658' =>
+            ['Label' => $this->_('Facettes dynamiques sur des sous-chaînes de champs unimarc'),
+             'Desc' => $this->_('Vous pouvez créer des facettes sur des portions d\'un champ unimarc. Par exemple, sur un champ 993$w formatté comme ceci: 2018-05-04, vous pouvez créer trois facettes sur l\'année, le mois et le jour.'),
+             'Image' => 'http://wiki.bokeh-library-portal.org/images/4/41/Dynamic_facet_public.png',
+             'Video' => 'https://youtu.be/KYYN22dqhnM',
+             'Category' => '',
+             'Right' => function($feature_description, $user) {return true;},
+             'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php?title=Facettes_dynamiques#A_partir_d.27une_partie_du_libell.C3.A9_d.27un_champ_unimarc',
+             'Test' => '',
+             'Date' => '2019-01-18'],
+
     ];
   }
 }
\ No newline at end of file
diff --git a/library/Class/IntBib.php b/library/Class/IntBib.php
index 07fc38b00cf2a43b7d489a309c31ac0359554993..64d848c9b15f5d41cbfc9936145500581a55f536 100644
--- a/library/Class/IntBib.php
+++ b/library/Class/IntBib.php
@@ -48,7 +48,7 @@ class IntBibLoader extends Storm_Model_Loader {
   }
 
 
-  public function isSingleNanook($patrons_closure = null) {
+  public function isSingleNanook() {
     return (new Class_IntBib_SingleNanook())->isSingle(Class_IntBib::findAll());
   }
 
@@ -58,6 +58,11 @@ class IntBibLoader extends Storm_Model_Loader {
   }
 
 
+  public function isSingleKoha() {
+    return (new Class_IntBib_SingleKoha())->isSingle(Class_IntBib::findAll());
+  }
+
+
   public function findAllNanookWithPreRegistration() {
     $collection = new Storm_Model_Collection(Class_IntBib::findAll());
     $combo = $collection
@@ -109,6 +114,7 @@ class Class_IntBib extends Storm_Model_Abstract {
   const SIGB_PERGAME = 1;
   const SIGB_KOHA = 12;
   const SIGB_NANOOK = 13;
+  const SIGB_ORPHEE = 3;
 
   const TYPE = 'type';
   const URL_SERVER = 'url_server';
@@ -308,6 +314,11 @@ class Class_IntBib extends Storm_Model_Abstract {
   }
 
 
+  public function isKoha() {
+    return static::SIGB_KOHA == $this->getSigb();
+  }
+
+
   public function isCommKoha() {
     return Class_IntBib::COM_KOHA == $this->getCommSigb();
   }
diff --git a/library/Class/IntBib/SingleKoha.php b/library/Class/IntBib/SingleKoha.php
new file mode 100644
index 0000000000000000000000000000000000000000..6fcdecf2c3337e9228948f8c0c7ab3c881ff73b5
--- /dev/null
+++ b/library/Class/IntBib/SingleKoha.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Copyright (c) 2012-2019, 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_IntBib_SingleKoha extends Class_IntBib_SingleSigb {
+  protected function _isSigb($library) {
+    return $library->isKoha();
+  }
+}
\ No newline at end of file
diff --git a/library/Class/IntBib/SingleNanook.php b/library/Class/IntBib/SingleNanook.php
index 22ff4c11b49917bc0b5bb83834a8464145b5877d..f3b6acdf30cb8ed0777a6f05a30c22c239dc071a 100644
--- a/library/Class/IntBib/SingleNanook.php
+++ b/library/Class/IntBib/SingleNanook.php
@@ -20,42 +20,8 @@
  */
 
 
-class Class_Intbib_SingleNanook {
-  protected $_server = null;
-
-  public function isSingle($models) {
-    if(!$models)
-      return false;
-
-    $this->_server = null;
-    foreach($models as $library) {
-      if (!$this->_isLibraryNanook($library))
-        return false;
-    }
-    return true;
-  }
-
-
-  public function isSinglePatrons($models) {
-    $aCollection = (new Storm_Model_Collection($models))
-      ->select(function($model)
-                {
-                  return $model->hasPatronImport();
-                });
-
-    return $this->isSingle($aCollection->getArrayCopy());
-  }
-
-
-  protected function _isLibraryNanook($library) {
-    if (!$library->isNanook())
-      return false;
-
-    if (null == $this->_server) {
-      $this->_server = $library->getUrlServer();
-      return true;
-    }
-
-    return $this->_server == $library->getUrlServer();
+class Class_IntBib_SingleNanook extends Class_IntBib_SingleSigb {
+  protected function _isSigb($library) {
+    return $library->isNanook();
   }
 }
\ No newline at end of file
diff --git a/library/Class/IntBib/SingleSigb.php b/library/Class/IntBib/SingleSigb.php
new file mode 100644
index 0000000000000000000000000000000000000000..2bffee507c9c350c33d355decebbeb9066cf89cd
--- /dev/null
+++ b/library/Class/IntBib/SingleSigb.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Copyright (c) 2012-2019, 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_IntBib_SingleSigb {
+  protected $_server = null;
+
+  public function isSingle($models) {
+    if(!$models)
+      return false;
+
+    $this->_server = null;
+    foreach($models as $library) {
+      if (!$this->_isSigbAndSameServer($library))
+        return false;
+    }
+    return true;
+  }
+
+
+  public function isSinglePatrons($models) {
+    $aCollection = (new Storm_Model_Collection($models))
+      ->select(function($model)
+                {
+                  return $model->hasPatronImport();
+                });
+
+    return $this->isSingle($aCollection->getArrayCopy());
+  }
+
+
+
+  protected function _isSigbAndSameServer($library) {
+    if (!$this->_isSigb($library))
+      return false;
+
+    if (null == $this->_server) {
+      $this->_server = $library->getUrlServer();
+      return true;
+    }
+
+    return $this->_server == $library->getUrlServer();
+  }
+
+
+  abstract protected function _isSigb($library);
+}
\ No newline at end of file
diff --git a/library/Class/Migration/BokehEight.php b/library/Class/Migration/BokehEight.php
new file mode 100644
index 0000000000000000000000000000000000000000..c49a43665d4982b8eab091ef6a8545773685e597
--- /dev/null
+++ b/library/Class/Migration/BokehEight.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Copyright (c) 2012-2019, 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_Migration_BokehEight {
+
+  use Trait_Translator;
+
+
+  public function run() {
+    Class_AdminVar::set('CODIFICATION_BROWSER', 1);
+    Class_AdminVar::set('INSPECTOR_GADGET_MARC_XML', 1);
+    Class_AdminVar::set('CUSTOM_SEARCH_FORM', 1);
+    Class_AdminVar::set('AUTHOR_PAGE', 1);
+    Class_AdminVar::set('OPDS', 1);
+    Class_AdminVar::set('BIBNUM', 1);
+
+    $this
+      ->_manageLoginThroughSigbOnly()
+      ->_fixCosmoVarDateMajAlbums();
+  }
+
+
+  protected function _manageLoginThroughSigbOnly() {
+    if (Class_IntBib::isSingleNanook() || Class_IntBib::isSingleKoha())
+      Class_AdminVar::set('LOGIN_THROUGH_SIGB_ONLY', 1);
+
+    return $this;
+  }
+
+
+  protected function _fixCosmoVarDateMajAlbums() {
+    if (!$model = Class_CosmoVar::find('date_maj_albums'))
+      $model = Class_CosmoVar::newInstance()
+        ->setClef('date_maj_albums');
+
+    $model
+      ->setCommentaire($this->_('Dernière date de mise à jour des albums.'))
+      ->setGroupe(6)
+      ->save();
+  }
+}
\ No newline at end of file
diff --git a/library/Class/Newsletter.php b/library/Class/Newsletter.php
index ee3814bbe9be89fc4020c630886c21d53182c456..ca407e7229b8b3731c5876cb215e7ab9e4be616e 100644
--- a/library/Class/Newsletter.php
+++ b/library/Class/Newsletter.php
@@ -89,7 +89,9 @@ class Class_Newsletter extends Storm_Model_Abstract {
   protected $_notices_finder;
   protected $_recipent_size = 20;
   protected $_default_attribute_values = ['titre' => '',
-                                          'draft' => 0];
+                                          'draft' => 0,
+                                          'articles_ids' => '',
+                                          'articles_categories_ids' => ''];
 
 
   public function describeAssociationsOn($associations) {
@@ -136,11 +138,12 @@ class Class_Newsletter extends Storm_Model_Abstract {
     $template = new Class_Entity();
 
     $notices = $this->getNotices();
+    $articles = $this->getArticles();
 
     $template
       ->setTitre($this->getTitre())
-      ->setBodyText($this->_getBodyText($notices))
-      ->setBodyHTML($this->_getBodyHTML($notices))
+      ->setBodyText($this->_getBodyText($notices, $articles))
+      ->setBodyHTML($this->_getBodyHTML($notices, $articles))
       ->setExpediteur($this->getExpediteur());
 
     return $template;
@@ -214,10 +217,23 @@ class Class_Newsletter extends Storm_Model_Abstract {
                           'only_img' => false,
                           'aleatoire' => 0,
                           'tri' => 1);
+
     return Class_Notice::getNoticesFromPreferences($preferences);
   }
 
 
+  public function getArticles() {
+    $articles = [];
+
+    foreach (Class_ArticleCategorie::findAllBy(['id_cat' => $this->getArticlesCategoriesIds()]) as $categorie)
+      $articles = array_merge($articles,
+                              $categorie->getArticles());
+
+    return array_merge($articles,
+                       Class_Article::findAllBy(['id_article' => $this->getArticlesIds()]));
+  }
+
+
   protected function _htmlToText($html) {
     return strip_tags(preg_replace('/<br[^>]*>/i', "\n", $html));
   }
@@ -245,13 +261,17 @@ class Class_Newsletter extends Storm_Model_Abstract {
   }
 
 
-  protected function _getBodyText($records) {
+  protected function _getBodyText($records, $articles) {
     $lines = [$this->_htmlToText($this->getContenu())];
 
     $renderer = new Class_Notice_MailRenderer();
     foreach($records as $record)
       $renderer->renderTextInto($record, $lines);
 
+    $renderer = new Class_Article_MailRenderer();
+    foreach($articles as $article)
+      $renderer->renderTextInto($article, $lines);
+
     $lines[] = $this->_getUnsubscribeText();
 
     return implode("\n", $lines);
@@ -273,13 +293,17 @@ class Class_Newsletter extends Storm_Model_Abstract {
    * @param array $records
    * @return string
    */
-  protected function _getBodyHTML($records) {
+  protected function _getBodyHTML($records, $articles) {
     $html = $this->getContenu();
 
     $renderer = new Class_Notice_MailRenderer();
     foreach($records as $record)
       $html .= $renderer->renderHtml($record);
 
+    $renderer = new Class_Article_MailRenderer();
+    foreach($articles as $article)
+      $html .= $renderer->renderHtml($article);
+
     $html .= $this->_getUnsubscribeHTML();
 
     return $html;
diff --git a/library/Class/Notice.php b/library/Class/Notice.php
index d28aa97a2d5aea56e13029f3499d3c4ac2a27fb5..cca8a5e695123a8386488b45ef764ff6556b3b0b 100644
--- a/library/Class/Notice.php
+++ b/library/Class/Notice.php
@@ -1553,7 +1553,7 @@ class Class_Notice extends Storm_Model_Abstract {
     $visitor->visitTypeDoc($this->getTypeDoc());
     $visitor->visitNatureDoc($this->getNatureDocs());
     if ($this->hasVignette()) {
-      $visitor->visitVignette(Class_Url::absolute($this->fetchUrlVignette()));
+      $visitor->visitVignette(Class_Url::absolute($this->fetchUrlLocalVignette()));
       $visitor->visitImage(Class_Url::absolute($this->fetchUrlImage()));
     }
     $visitor->visitIsbn($this->getIsbn());
diff --git a/library/Class/Notice/Thumbnail.php b/library/Class/Notice/Thumbnail.php
index d65f4c5d07cacde2338b93ffa4c64c579b1ebebe..2b46ada2724b34829f3451339e26c4ac6676ea4c 100644
--- a/library/Class/Notice/Thumbnail.php
+++ b/library/Class/Notice/Thumbnail.php
@@ -32,11 +32,7 @@ class Class_Notice_Thumbnail {
     if ($this->_shouldRetry($record, $record->getUrlVignette()))
       return $this->_service->getAjaxUrl($record);
 
-    $url = $record->getUrlVignette();
-
-    return $this->_service->isNoData($url)
-      ? $this->_rawThumbnailUrl($record)
-      : $url;
+    return $record->getUrlVignette();
   }
 
 
diff --git a/library/Class/User/Cards.php b/library/Class/User/Cards.php
index 97c7d9b53ee5c83dcee067ec11c91d428cf584ef..8e28acf9599a198aa0a5316f1622002caedd7361 100644
--- a/library/Class/User/Cards.php
+++ b/library/Class/User/Cards.php
@@ -32,7 +32,10 @@ class Class_User_Cards extends Storm_Model_Collection {
 
 
   public function getLoansWithOutPNB($params = []) {
-    return $this->_decorateOperationFrom(function($card) use ($params) { return $card->getLoansWithOutPNB($params); });
+    return $this->_decorateOperationFrom(
+                                         function($card) use ($params) {
+                                           return $card->getLoansWithOutPNB($params);
+                                         });
   }
 
 
diff --git a/library/Class/Users.php b/library/Class/Users.php
index cd1d6d383e7f85f5c553d323b6243b012d5da983..d60433b86d97150983ae227dd2af6c518605bcdb 100644
--- a/library/Class/Users.php
+++ b/library/Class/Users.php
@@ -125,13 +125,16 @@ class UsersLoader extends Storm_Model_Loader {
 
 
   public function isLogged($user) {
-    if(!$user)
+    if (!$user)
       return false;
 
-    if(!$logged_user = Class_Users::getIdentity())
+    if (!$logged_user = Class_Users::getIdentity())
       return false;
 
-    return $user->getId() == $logged_user->getId();
+    if ($user->getId() == $logged_user->getId())
+      return true;
+
+    return $user->isChildOf($logged_user);
   }
 
 
@@ -1815,8 +1818,8 @@ class Class_Users extends Storm_Model_Abstract {
 
 
   public function getRedmineLibraries() {
-    if($this->getRoleLevel() >= ZendAfi_Acl_AdminControllerRoles::MODO_PORTAIL)
-      return Class_Bib::findAllBy(['order' => 'libelle']);
+    if ($this->getRoleLevel() >= ZendAfi_Acl_AdminControllerRoles::MODO_PORTAIL)
+      return Class_Bib::findAllRedmineEnabled();
 
     return [$this->getRedmineLibrary()];
   }
@@ -1902,4 +1905,13 @@ class Class_Users extends Storm_Model_Abstract {
   public function isSIGBProvidesChangePasswordService() {
     return ($sigb_com = $this->getSIGBComm()) && $sigb_com->providesChangePasswordService();
   }
+
+
+  public function isChildOf($other) {
+    foreach($this->getParentCards() as $parent)
+      if ($parent->getId() == $other->getId())
+        return true;
+
+    return false;
+  }
 }
diff --git a/library/Class/WebService/Fnac.php b/library/Class/WebService/Fnac.php
index 47fea507b1745d35b0aa5f6a2ad22e98166074ab..a793f23424d5d30cff739e0d850864083960bde8 100644
--- a/library/Class/WebService/Fnac.php
+++ b/library/Class/WebService/Fnac.php
@@ -56,6 +56,17 @@ class Class_WebService_Fnac extends Class_WebService_Abstract {
   }
 
 
+  public function httpGet($url,$options = []) {
+    return parent::httpGet($url, ['headers' => $this->httpHeaders()]);
+  }
+
+
+  public function httpHeaders() {
+    return ['User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0',
+            'Accept-Language' => 'fr-FR'];
+  }
+
+
   public function getUrlLireLaSuite($data) {
     $pos = ($pos = striPos($data,"summary")) ? $pos : striPos($data, 'resume');
 
diff --git a/library/Class/WebService/Premiere.php b/library/Class/WebService/Premiere.php
index 722cc0181afc2f3d1f657b67f98a0ddc396802ba..83f331afe65c78986af92df9f723eda477d949c6 100644
--- a/library/Class/WebService/Premiere.php
+++ b/library/Class/WebService/Premiere.php
@@ -18,15 +18,10 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
-//////////////////////////////////////////////////////////////////////////////////////////
-// OPAC3 - PREMIERE.fr
-//////////////////////////////////////////////////////////////////////////////////////////
 
-class Class_WebService_Premiere {
-  var $url_base;                    // Urls de base
 
-  function __construct()
-  {
+class Class_WebService_Premiere {
+  function __construct() {
     $url_base="http://www.premiere.fr";
     $this->url_base["root"]=$url_base;
     $this->url_base["resume"]=$url_base."/film/@TITRE@/";
@@ -44,14 +39,11 @@ class Class_WebService_Premiere {
     return array();
   }
 
-//------------------------------------------------------------------------------------------------------
-// Résumé
-//------------------------------------------------------------------------------------------------------
+
   function get_resume($titre) {
-    // Changer l'url pour recuperer la page
     $titre=$this->encoder_titre($titre);
     $url=str_replace("@TITRE@",$titre,$this->url_base["resume"]);
-    // Get http de la page
+
     try{
       $httpClient = Class_HttpClientFactory::getInstance()->newHttpClient();
       $httpClient->setUri($url);
@@ -63,11 +55,11 @@ class Class_WebService_Premiere {
     }
 
     // Recherche du bon bloc
-    $start = '<div class="field-item even" property="content:encoded">';
+    $start = 'Synopsis : ';
     if (!$pos = strPos($data, $start))
       return '';
     $pos = $pos + strlen($start);
-    $posfin=strPos($data,'">',$pos);
+    $posfin=strPos($data,'/>',$pos);
 
     return trim(strip_tags(substr($data, $pos, ($posfin-$pos))));
   }
diff --git a/library/Class/WebService/Redmine/Workflow/Afibre.php b/library/Class/WebService/Redmine/Workflow/Afibre.php
index b298a4442ee2e14e01dfdc7a2d3b774a85029e76..f739596668a0ca38257911b6ce2e44bf4ddc9753 100644
--- a/library/Class/WebService/Redmine/Workflow/Afibre.php
+++ b/library/Class/WebService/Redmine/Workflow/Afibre.php
@@ -19,7 +19,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
-class Class_WebService_Redmine_Workflow_Afibre extends  Class_WebService_Redmine_Workflow_Abstract {
+class Class_WebService_Redmine_Workflow_Afibre extends Class_WebService_Redmine_Workflow_Abstract {
   const
     A_QUALIFIER = 1,
     AFFECTE_HEBERGEMENT = 13,
@@ -34,24 +34,31 @@ class Class_WebService_Redmine_Workflow_Afibre extends  Class_WebService_Redmine
     A_INTEGRER = 17,
     RETOUR_DEV = 22;
 
-  protected $_transitions = [self::A_QUALIFIER => [self::FERME],
-                             14 => [self::A_QUALIFIER],
-                             15 => [],
-                             self::AFFECTE_HOTLINE => [self::AFFECTE_DEV, self::FERME],
-                             self::AFFECTE_SYSTEME => [],
-                             self::AFFECTE_DEV => [self::A_QUALIFIER, self::FERME],
-                             self::AFFECTE_HEBERGEMENT => [],
-                             self::RETOUR_DEV => [],
-                             self::EN_DEVELOPPEMENT => [self::FERME],
-                             18 => [],
-                             16 => [],
-                             self::A_TESTER => [self::FERME],
-                             self::A_INTEGRER  => [],
-                             self::QUESTION_CLIENT => [self::AFFECTE_HOTLINE, self::FERME],
-                             8 => [self::FERME],
-                             self::TRANSFERT_DEV => [],
-                             self::FERME => [self::A_QUALIFIER],
-                             6 => [self::A_QUALIFIER, self::FERME]];
+  /** @var array of [ from_status => [to_status, to_status...]] */
+  protected $_transitions = [];
+
+
+  public function __construct() {
+    $this->_transitions = [static::A_QUALIFIER => [static::FERME],
+                           14 => [static::A_QUALIFIER],
+                           15 => [],
+                           static::AFFECTE_HOTLINE => [static::AFFECTE_DEV, static::FERME],
+                           static::AFFECTE_SYSTEME => [],
+                           static::AFFECTE_DEV => [static::A_QUALIFIER, static::FERME],
+                           static::AFFECTE_HEBERGEMENT => [],
+                           static::RETOUR_DEV => [],
+                           static::EN_DEVELOPPEMENT => [static::FERME],
+                           18 => [],
+                           16 => [],
+                           static::A_TESTER => [static::FERME],
+                           static::A_INTEGRER  => [],
+                           static::QUESTION_CLIENT => [static::AFFECTE_HOTLINE, static::FERME],
+                           8 => [static::FERME],
+                           static::TRANSFERT_DEV => [],
+                           static::FERME => [static::A_QUALIFIER],
+                           6 => [static::A_QUALIFIER, static::FERME]];
+  }
+
 
   public function getTransitions() {
     return $this->_transitions;
@@ -59,7 +66,7 @@ class Class_WebService_Redmine_Workflow_Afibre extends  Class_WebService_Redmine
 
 
   public function getFormSelectedStatusIdForCurrent($status_id) {
-    return $status_id == self::QUESTION_CLIENT ? self::AFFECTE_HOTLINE : $status_id;
+    return $status_id == static::QUESTION_CLIENT ? static::AFFECTE_HOTLINE : $status_id;
   }
 
 
@@ -68,8 +75,9 @@ class Class_WebService_Redmine_Workflow_Afibre extends  Class_WebService_Redmine
 
     return count($issues
                  ->select(function($issue)
-                                 {
-                                   return $issue->getStatusId() == self::QUESTION_CLIENT;
-                                 }));
+                          {
+                            return in_array($issue->getStatusId(), [static::QUESTION_CLIENT,
+                                                                    static::A_TESTER]);
+                          }));
   }
 }
\ No newline at end of file
diff --git a/library/Class/WebService/SIGB/Dynix/Service.php b/library/Class/WebService/SIGB/Dynix/Service.php
index 5e4905b74a63db673c8655db13d63df710d6f9bc..53d2375184581710bba0927e05dbe10fe916a053 100644
--- a/library/Class/WebService/SIGB/Dynix/Service.php
+++ b/library/Class/WebService/SIGB/Dynix/Service.php
@@ -30,7 +30,7 @@ class Class_Webservice_SIGB_Dynix_Service extends Class_WebService_SIGB_Abstract
       'CRETDOY'=>'mediatheque.creteil.abbaye@agglo-plainecentrale94.fr',
       'ENMDAD'=>'',
       'CRETHAB'=>'medialudo.creteil@gpsea.fr',
-      'CRETMAC'=>'bibliotheque.creteil.mac@agglo-plainecentrale94.fr',
+      'CRETMAC'=>'mediabus@gpsea.fr',
       'CRETMUL'=>'',
       'CRETRES'=>'bibliotheque.creteil.servicesinternes@agglo-plainecentrale94.fr',
       'CRETVIL'=>'mediatheque.creteil.village@agglo-plainecentrale94.fr',
diff --git a/library/Class/WebService/SIGB/Nanook/Service.php b/library/Class/WebService/SIGB/Nanook/Service.php
index ac30505db7302a232a9b25eebd8838e3c37b82e2..616186c94f549ce454bb8e4f618efd37449f16e3 100644
--- a/library/Class/WebService/SIGB/Nanook/Service.php
+++ b/library/Class/WebService/SIGB/Nanook/Service.php
@@ -107,24 +107,44 @@ class Class_Webservice_SIGB_Nanook_Service extends Class_WebService_SIGB_Abstrac
    * @return Class_WebService_SIGB_Emprunteur
    */
   public function getEmprunteur($user) {
-    if (!$this->ilsdiAuthenticatePatron($user))
+    if (!$user
+        || (!$patron_id = $this->_patronIdFromUser($user)))
       return Class_WebService_SIGB_Emprunteur::nullInstance();
-    $emprunteur= $this->ilsdiGetPatronInfo(['patronId' => $user->getIdSigb()],
-                                     Class_WebService_SIGB_Nanook_PatronInfoReader::newInstance());
+
+    $emprunteur = $this->ilsdiGetPatronInfo(['patronId' => $user->getIdSigb()],
+                                            Class_WebService_SIGB_Nanook_PatronInfoReader::newInstance());
 
     if (!$emprunteur->getCodeBarres())
       $emprunteur->setCodeBarres($user->getLogin());
 
     $emprunteur->setPassword($user->getPassword());
+
     return $emprunteur;
   }
 
 
+  protected function _patronIdFromUser($user) {
+    $patron_id = Class_Users::isLogged($user)
+      ? $user->getIdSigb()
+      : '';
+
+    if (!$patron_id && $this->ilsdiAuthenticatePatron($user))
+      $patron_id = $user->getIdSigb();
+
+    return $patron_id;
+  }
+
+
   public function getUserAnnexe($user) {
     return $user->id_site;
   }
 
 
+  public function providesChangePasswordService() {
+    return true;
+  }
+
+
   public function saveEmprunteur($emprunteur) {
     $xml = $this->getWebClient()
       ->postData($this->buildQueryURL(['service'=>'UpdatePatronInfo',
diff --git a/library/Class/WebService/Vignette.php b/library/Class/WebService/Vignette.php
index 47957c76b4844d02d4cac37c41938ec7aa307088..b29eedd2185db8ee7e46edf1b2cc533fdda871fb 100644
--- a/library/Class/WebService/Vignette.php
+++ b/library/Class/WebService/Vignette.php
@@ -89,11 +89,7 @@ class Class_WebService_Vignette extends Class_WebService_Abstract {
 
 
   static function renderThumbnail($id_notice,$titre,$type_doc)  {
-    $image = static::createImage($titre,
-                                 $width=100,
-                                 $height=90,
-                                 $type_doc);
-
+    $image = static::createImage($titre, 300, 400, $type_doc);
     imagepng($image);
   }
 
@@ -114,7 +110,7 @@ class Class_WebService_Vignette extends Class_WebService_Abstract {
 
 
   public static function createImage($titre, $width=100, $height=110, $type_doc) {
-    $image = imagecreatetruecolor($width,$height);
+    $image = imagecreatetruecolor($width, $height);
 
     // Couleur de fond
     $fond = imagecolorallocate($image, 255, 255, 255);
@@ -122,7 +118,7 @@ class Class_WebService_Vignette extends Class_WebService_Abstract {
 
     // Titre
     if($titre > "")
-      $image = static::writeText($titre,$image);
+      $image = static::writeText($titre, $image);
     return $image;
   }
 
@@ -133,18 +129,17 @@ class Class_WebService_Vignette extends Class_WebService_Abstract {
     $texte=explode(";", $texte);
 
     // Parametres
-    $font = 3;
     $couleur = imagecolorallocate($image_obj, 102, 102, 102);
-    $pos_x=0;
-    $pos_y=2;
-    $hauteur=15;
-    $largeur=100;
+    $pos_x=20;
+    $pos_y=50;
+    $hauteur=50;
+    $largeur=200;
+
 
     // Afficher
     foreach($texte as $ligne)  {
-      $pos_x=($largeur/2)-(strlen($ligne) * 3.5);
-      imagestring($image_obj,$font, $pos_x, $pos_y, $ligne, $couleur);
-      $pos_y+=$hauteur;
+      imagettftext($image_obj, 30, 0, $pos_x, $pos_y, $couleur, PATH_FONTS . 'Vera.ttf', trim($ligne));
+      $pos_y += $hauteur;
     }
 
     return $image_obj;
diff --git a/library/Class/WebService/Youtube.php b/library/Class/WebService/Youtube.php
new file mode 100644
index 0000000000000000000000000000000000000000..b68498824d7f31c93a8b6313faabc36dc9d2379b
--- /dev/null
+++ b/library/Class/WebService/Youtube.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Copyright (c) 2012-2018, 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_WebService_Youtube extends Class_WebService_Abstract {
+  const
+    FEEDS_URL = 'https://www.youtube.com/feeds/videos.xml',
+    CHANNEL_URL = 'https://www.youtube.com/channel/',
+    EMBED_URL = 'https://www.youtube.com/embed/';
+
+  public function videosOfChannel($channel) {
+    if (!$channel)
+      return new Storm_Collection([]);
+
+    $rss = $this->httpGet(static::FEEDS_URL . '?' . http_build_query(['channel_id' => $channel]));
+
+    try {
+      $xml = new SimpleXMLElement($rss);
+    } catch(Exception $e) {
+      return new Storm_Collection([]);
+    }
+
+    $items = [];
+    foreach($xml->entry as $entry)
+      $items[] = (new Class_WebService_Youtube_Video())
+        ->setId(str_replace('yt:video:', '', $entry->id))
+        ->setTitle($entry->title);
+
+    return new Storm_Collection($items);
+  }
+}
+
+
+
+
+class Class_WebService_Youtube_Video extends Class_Entity {
+  public function getEmbedUrl() {
+    return Class_WebService_Youtube::EMBED_URL . $this->getId();
+  }
+}
diff --git a/library/ZendAfi/Acl/AdminControllerGroup.php b/library/ZendAfi/Acl/AdminControllerGroup.php
index 9693cfd026d3cd79eba28aa1ba6e2cf38f2f6eab..9624585c79d0b693c0c8353f3bf1436fe7fc06b7 100644
--- a/library/ZendAfi/Acl/AdminControllerGroup.php
+++ b/library/ZendAfi/Acl/AdminControllerGroup.php
@@ -50,8 +50,8 @@ class ZendAfi_Acl_AdminControllerGroup {
   public function __construct() {
     $this->_activated =  ['activity' => Class_AdminVar::isActivityEnabled(),
                           'album' => Class_AdminVar::isBibNumEnabled(),
-                          'oai' => Class_AdminVar::isBibNumEnabled(),
-                          'ead' => Class_AdminVar::isBibNumEnabled(),
+                          'oai' => Class_AdminVar::isBibNumEnabled() && Class_AdminVar::isOaiRepositoryEnabled(),
+                          'opds' => Class_AdminVar::isBibNumEnabled() && Class_AdminVar::isOpdsEnabled(),
                           'harvest/arte-vod-browse' => Class_AdminVar::isArteVODEnabled(),
                           'harvest/vodeclic-browse' => Class_AdminVar::isVodeclicEnabled(),
                           'harvest/orphea-browse' => Class_AdminVar::isOrpheaEnabled(),
@@ -61,11 +61,18 @@ class ZendAfi_Acl_AdminControllerGroup {
                           'harvest/soundcloud' => Class_AdminVar::isSoundCloudEnabled(),
                           'sito/create' => Class_AdminVar::isSitoInAlbums(),
                           'album/dilicom' => Class_AdminVar::isDilicomPNBEnabled(),
+                          'album/import_ead' => Class_AdminVar::isImportEadEnabled(),
                           'premier-chapitre' => Class_AdminVar::isPremierChapitreEnabled(),
                           'i18n' => Class_AdminVar::isTranslationEnabled(),
                           'stat/matomo' => (new Class_AdminVar_Matomo())->isEnabled(),
                           'multimedia' => Class_AdminVar::isMultimediaEnabled(),
                           'search-form' => Class_AdminVar::isCustomSearchFormEnabled(),
+                          'systeme/importthesaurus' => Class_AdminVar::isImportThesaurusEnabled(),
+                          'systeme/generationsite' => Class_AdminVar::isGenerationSiteEnabled(),
+                          'systeme/webservices' => Class_AdminVar::isWebserviceTestEnabled(),
+                          'systeme/importavisopac2' => Class_AdminVar::isImportAvisOpac2Enabled(),
+                          'profil/genres' => Class_AdminVar::isCustomGenreIconEnabled(),
+                          'custom-fields-report' => Class_AdminVar::isCustomFieldsReportEnabled(),
                           ];
 
     $this->_activated = array_merge($this->_activated,
diff --git a/library/ZendAfi/Acl/AdminControllerRoles.php b/library/ZendAfi/Acl/AdminControllerRoles.php
index 8e0cdfef71d309751b6a888aadf957e57ebe3d9c..8b54e8a589d93d7f09a852ced4cfb6ad72cad632 100644
--- a/library/ZendAfi/Acl/AdminControllerRoles.php
+++ b/library/ZendAfi/Acl/AdminControllerRoles.php
@@ -90,6 +90,9 @@ class ZendAfi_Acl_AdminControllerRoles extends Zend_Acl {
     $this->add(new Zend_Acl_Resource('print'));
     $this->add(new Zend_Acl_Resource('external-agendas'));
     $this->add(new Zend_Acl_Resource('systeme'));
+    $this->add(new Zend_Acl_Resource('systeme/webservices'));
+    $this->add(new Zend_Acl_Resource('systeme/phpinfo'));
+    $this->add(new Zend_Acl_Resource('custom-fields-report'));
     $this->add(new Zend_Acl_Resource('batch'));
     $this->add(new Zend_Acl_Resource('file-manager'));
     $this->add(new Zend_Acl_Resource('url-manager'));
@@ -191,6 +194,9 @@ class ZendAfi_Acl_AdminControllerRoles extends Zend_Acl {
     $this->deny('modo_portail','systeme');
     $this->deny('modo_portail','batch');
     $this->deny('modo_portail','search-form');
+    $this->deny('modo_portail','custom-fields-report');
+    $this->deny('modo_portail','systeme/webservices');
+    $this->deny('modo_portail','systeme/phpinfo');
     foreach($codifications as $controller)
       $this->deny('modo_portail', $controller);
 
diff --git a/library/ZendAfi/Auth/Adapter/Abstract.php b/library/ZendAfi/Auth/Adapter/Abstract.php
index 0f3b16eaf6cffc21ee6fe7210b2b257c0ca4d0c4..60702f343036d5c45a65082681a3a0d5ee7d6037 100644
--- a/library/ZendAfi/Auth/Adapter/Abstract.php
+++ b/library/ZendAfi/Auth/Adapter/Abstract.php
@@ -57,4 +57,10 @@ abstract class ZendAfi_Auth_Adapter_Abstract implements Zend_Auth_Adapter_Interf
   public function getResultObject() {
     return $this->_authenticated_user->toStdClass();
   }
+
+
+  /** @return Class_Users */
+  public function getAuthenticatedUser() {
+    return $this->_authenticated_user;
+  }
 }
diff --git a/library/ZendAfi/Controller/Action/Helper/HTMLAjaxResponse.php b/library/ZendAfi/Controller/Action/Helper/HTMLAjaxResponse.php
new file mode 100644
index 0000000000000000000000000000000000000000..1f7545a8fc0540c4649db9cf5af9d7dbc4edec27
--- /dev/null
+++ b/library/ZendAfi/Controller/Action/Helper/HTMLAjaxResponse.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Copyright (c) 2012-2017, 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_HTMLAjaxResponse extends Zend_Controller_Action_Helper_Abstract {
+  public function direct($content) {
+    $viewRenderer = $this->getActionController()->getHelper('ViewRenderer');
+    $viewRenderer->setNoRender();
+
+    $this->getResponse()->setHeader('Content-Type', 'text/html;charset=utf-8');
+    $this->getResponse()->setBody($content);
+  }
+}
\ No newline at end of file
diff --git a/library/ZendAfi/Controller/Plugin/InspectorGadget.php b/library/ZendAfi/Controller/Plugin/InspectorGadget.php
index 117cbcf823f903c5cac0ef25dd7b1e973a190a30..f4f1d6b558d6aa44913c72cd0478e758bb0b5ed8 100644
--- a/library/ZendAfi/Controller/Plugin/InspectorGadget.php
+++ b/library/ZendAfi/Controller/Plugin/InspectorGadget.php
@@ -191,7 +191,10 @@ create: function(event, ui) { if (ui.panel.hasClass(\'ig-accordion\')) ui.panel.
 
 
   public function log() {
-    $httpClient = Class_HttpClientFactory::getInstance()->getLastHttpClient();
+    if (!$httpClient = Class_HttpClientFactory::getInstance()->getLastHttpClient()) {
+      return $this;
+    }
+
     $response_code = $response_body = null;
     if ($response = $httpClient->getLastResponse()) {
       $response_code = $response->getStatus();
@@ -206,6 +209,7 @@ create: function(event, ui) { if (ui.panel.hasClass(\'ig-accordion\')) ui.panel.
        $response_code);
 
     $this->addCall($call);
+    return $this;
   }
 
 
diff --git a/library/ZendAfi/Form/Admin/Newsletter.php b/library/ZendAfi/Form/Admin/Newsletter.php
index 88cb4f85c7a686c954bc1ff125cee8f1024c25b1..63deb88f251960ecdae54d36cb72425c40dfa4f2 100644
--- a/library/ZendAfi/Form/Admin/Newsletter.php
+++ b/library/ZendAfi/Form/Admin/Newsletter.php
@@ -62,6 +62,7 @@ class ZendAfi_Form_Admin_Newsletter extends ZendAfi_Form {
                        'value' => 0,
                        'validators' => [new Zend_Validate_Int()]])
 
+
          ->addDisplayGroup(['titre', 'expediteur', 'draft'],
                            'letter',
                            ['legend' => $this->_('Lettre')])
@@ -74,4 +75,39 @@ class ZendAfi_Form_Admin_Newsletter extends ZendAfi_Form {
                            'notices',
                            ['legend' => $this->_('Notices')]);
   }
+
+
+  public function populate($datas) {
+    parent::populate($datas);
+
+    $this
+      ->addElement('treeSelect',
+                   'articles_selector',
+                   ['UrlDataSource' => Class_Url::assemble(['module' => 'admin',
+                                                            'controller' => 'bib',
+                                                            'action' => 'articles']),
+
+                    'InputIdItemsName' => 'articles_ids',
+                    'IdItems' => isset($datas['articles_ids']) ? $datas['articles_ids'] : '',
+                    'ItemRenderer' => function($id)
+                    {
+                      if ($model = Class_Article::find($id))
+                        return $model->getTitre();
+                    },
+
+                    'InputIdCategoriesName' => 'articles_categories_ids',
+                    'IdCategories' => isset($datas['articles_categories_ids']) ? $datas['articles_categories_ids'] : '',
+                    'CategoryRenderer' => function($id)
+                    {
+                      if ($model = Class_ArticleCategry::find($id))
+                        return $model->getLibelle();
+                    },
+
+                    ]);
+
+    $this->addDisplayGroup(['articles_selector'],
+                           'articles',
+                           ['legend' => $this->_('Articles')]);
+    return $this;
+  }
 }
diff --git a/library/ZendAfi/View/Helper/Abonne/Operation.php b/library/ZendAfi/View/Helper/Abonne/Operation.php
index 3055a83e4c6492d24f1dfdb944404007ebeef429..1a22ddc75699abd3de8316998c72e4468f2ac641 100644
--- a/library/ZendAfi/View/Helper/Abonne/Operation.php
+++ b/library/ZendAfi/View/Helper/Abonne/Operation.php
@@ -65,9 +65,7 @@ class ZendAfi_View_Helper_Abonne_Operation extends ZendAfi_View_Helper_BaseHelpe
 
     $record = $this->_getRecord($operation);
     return ($record->getId())
-      ? $this->view->Notice_Vignette($record,
-                                     $attribs,
-                                     ZendAfi_View_Helper_Notice_Vignette::MODE_VIEW)
+      ? $this->view->Notice_Vignette($record, $attribs)
       : '';
   }
 
diff --git a/library/ZendAfi/View/Helper/Admin/ContentNav.php b/library/ZendAfi/View/Helper/Admin/ContentNav.php
index 043d6a8a25a80d0f25a461b6c33832729d9a09e3..ea1d6c10e3c6788606ade9e57f42bb3ecc51552b 100644
--- a/library/ZendAfi/View/Helper/Admin/ContentNav.php
+++ b/library/ZendAfi/View/Helper/Admin/ContentNav.php
@@ -32,8 +32,9 @@ class ZendAfi_View_Helper_Admin_ContentNav extends ZendAfi_View_Helper_BaseHelpe
                 $this->menuMiseEnPage(),
                 $this->menuStats(),
                 $this->menuPortail(),
+                $this->menuCatalogue(),
                 $this->menuSysteme(),
-                $this->menuCatalogue()];
+    ];
 
     return $this->_tag('div',
                        implode($content),
@@ -67,6 +68,7 @@ class ZendAfi_View_Helper_Admin_ContentNav extends ZendAfi_View_Helper_BaseHelpe
                     ['newsletters',           $this->_("Lettres d'information"),  '/admin/newsletter'],
                     ['trainings',             $this->_('Activités'),             '/admin/activity'],
                     ['places',                $this->_('Lieux'),                  '/admin/lieu'],
+                    ['filebrowser',           $this->_('Explorateur de fichiers'), '/admin/file-manager'],
                    ]);
   }
 
@@ -140,24 +142,24 @@ class ZendAfi_View_Helper_Admin_ContentNav extends ZendAfi_View_Helper_BaseHelpe
                     ['links', $this->_('Contrôle des URL'), '/admin/url-manager'],
                     ['variables', $this->_('Variables'), '/admin/index/adminvar', [], $is_admin],
 
-                    ['webservice_tests', $this->_('Test des web-services'), '/admin/systeme/webservices',
-                     [], $is_super_admin],
+                    ['webservice_tests', $this->_('Test des web-services'), '/admin/systeme/webservices', [], $is_super_admin],
 
                     ['sendmail_tests', $this->_('Test envoi mails'), '/admin/systeme/mailtest', [], $is_super_admin],
                     ['php', $this->_('Informations système'), '/admin/systeme/phpinfo', [], $is_super_admin],
                     ['image_cache', $this->_('Cache des images'), '/admin/systeme/cacheimages', [], $is_admin],
 
                     ['migrate_comments', $this->_('Import avis opac2'), '/admin/systeme/importavisopac2',
-                     [], $is_super_admin],
+                     [],
+                     function ($user) use ($is_super_admin) {
+                        return $is_super_admin($user) && Class_AdminVar::isImportAvisOpac2Enabled();
+                      }],
 
-                    ['portal_init', $this->_('Génération du site'), '/admin/systeme/generationsite',
-                     [], function($user) { return defined('DEVELOPMENT') && $user->isAdmin();}],
+                    ['portal_init', $this->_('Génération du site'), '/admin/systeme/generationsite'],
 
-                    ['filebrowser', $this->_('Explorateur de fichiers'), '/admin/file-manager'],
                     ['search_form', $this->_('Formulaires de recherche'), '/admin/search-form'],
 
                     ['customfields', $this->_('Champs personnalisés'), '/admin/custom-fields/index', [], $is_admin],
-                    ['customreports', $this->_('Rapports statistiques'), '/admin/custom-fields-report', [], $is_admin]
+                    ['customreports', $this->_('Rapports statistiques'), '/admin/custom-fields-report']
                    ]);
   }
 
@@ -168,12 +170,8 @@ class ZendAfi_View_Helper_Admin_ContentNav extends ZendAfi_View_Helper_BaseHelpe
                    [["doctypes", $this->_("Types de documents"), "/admin/type-docs",
                      [], function($user) { return $user->isAdmin(); }],
 
-                    ["thesaurus_init", $this->_("Import Thesaurus"), "/admin/systeme/importthesaurus",
-                     [], function($user) { return defined('DEVELOPMENT') && $user->isAdmin();}],
-
-                    ["thesaurus_edit",
-                     $this->_("Parcourir les codifications"),    "/admin/codification-browser",
-                     []]
+                    ["thesaurus_init", $this->_("Import Thesaurus"), "/admin/systeme/importthesaurus"],
+                    ["thesaurus_edit", $this->_("Parcourir les codifications"), "/admin/codification-browser"]
                    ]);
   }
 
@@ -213,91 +211,3 @@ class ZendAfi_View_Helper_Admin_MenuGaucheAdminBloc {
             ['class' => 'menuGaucheAdmin']);
   }
 }
-
-
-
-class ZendAfi_View_Helper_Admin_MenuGaucheAdminItem {
-  protected static $_acl;
-
-  protected
-    $_icon,
-    $_title,
-    $_url,
-    $_attribs,
-    $_skin;
-
-
-  public static function newWith($params) {
-    return new static($params[0],
-                      $params[1],
-                      $params[2],
-                      isset($params[3]) ? $params[3] : [],
-                      isset($params[4]) ? $params[4] : null);
-  }
-
-
-  public function __construct($icon, $title, $url, $attribs=[], $filter=null) {
-    $this->_icon = $icon;
-    $this->_title = $title;
-    $this->_url = $url;
-    $this->_attribs = $attribs;
-    $this->_filter = $filter;
-    $this->_skin = Class_Admin_Skin::current();
-  }
-
-
-  public function renderFor($user, $view) {
-    if (!$this->_isAllowed($user))
-      return '';
-
-    $url = $this->_url;
-    if('http' != substr($url, 0, 4))
-      $url = BASE_URL . $url;
-
-    $extra_infos = isset($this->_attribs['extra_infos'])
-      ? $view->tag('span', $this->_attribs['extra_infos'], ['class' => 'menu_info'])
-      : '';
-
-    $attributes = (array_key_exists('REQUEST_URI', $_SERVER)
-                   && false !== strpos($_SERVER['REQUEST_URI'], $url))
-      ? ['class' => 'selected']
-      : [];
-
-    $anchor_params = ['href' => $url];
-    if (isset($this->_attribs['target']))
-      $anchor_params['target'] = $this->_attribs['target'];
-
-    $ico = $this->_skin->renderMenuIconOn($this->_icon, $view, ['alt' => $this->_title]);
-
-    return $view->tag('li',
-                      $view->tag('a', $ico . $this->_title, $anchor_params) . $extra_infos,
-                      $attributes);
-  }
-
-
-  protected function _isAllowed($user) {
-    if (($filter = $this->_filter) && !$filter($user))
-      return false;
-
-    if ('/admin/' != substr($this->_url, 0, 7))
-      return true;
-
-    $url = substr($this->_url, 7);
-    $parts = explode('/', $url);
-
-    return $this->getAcl()->isAllowed($user, $parts[0], isset($parts[1]) ? $parts[1] : '');
-  }
-
-
-  /** @category testing */
-  public static function setAcl($acl) {
-    static::$_acl = $acl;
-  }
-
-
-  protected function getAcl() {
-    return static::$_acl
-      ? static::$_acl
-      : static::$_acl = new ZendAfi_Acl_AdminControllerGroup();
-  }
-}
diff --git a/library/ZendAfi/View/Helper/Admin/HelpLink.php b/library/ZendAfi/View/Helper/Admin/HelpLink.php
index 149d4ea69da130030e1a56a65efc7fe48eba2b0c..8682363dadf3881c21d639ed5d2412a7e8b5a61f 100644
--- a/library/ZendAfi/View/Helper/Admin/HelpLink.php
+++ b/library/ZendAfi/View/Helper/Admin/HelpLink.php
@@ -76,7 +76,8 @@ class ZendAfi_View_Helper_Admin_HelpLinkBokehWiki {
                                   'library' => 'Afficher_les_horaires_d\'ouverture'],
      'ouvertures'             => ['index' => 'Ouvertures_des_bibliothèques'],
      'bibnum'                 => ['index' => 'Connecteurs'],
-     'album'                  => ['index' => 'Collections'],
+     'album'                  => ['index' => 'Collections',
+                                  'add-website' => 'Sitothèque_ressource_numérique'],
      'catalogue'              => ['index' => 'Domaines'],
      'cms'                    => ['index' => 'Articles_-_Créer,_rédiger_et_ordonner'],
      'custom-fields'          => ['index' => 'Gestion_des_champs_personnalisés'],
@@ -110,7 +111,15 @@ class ZendAfi_View_Helper_Admin_HelpLinkBokehWiki {
      'url-manager'            => ['index' => 'Gestion_des_URL'],
      'facets'                 => ['index' => 'Facettes_dynamiques'],
      'data-profile'           => ['index' => 'Catégorie:Profils_de_données'],
-     'integration'            => ['index' => 'Import_d\'un_étalon']
+     'integration'            => ['index' => 'Import_d\'un_étalon'],
+     'search-form'            => ['index' => 'Personnalisation_de_la_recherche_avancée'],
+     'sito'                   => ['index' => 'Indexation_des_sitothèques'],
+     'external-agendas'       => ['index' => 'Agendas_Externes'],
+     'lieu'                   => ['index' => 'Modifier_un_Lieu'],
+     'users'                  => ['index' => 'Gestion_des_utilisateurs'],
+     'codification-browser'   => ['index' => 'Explorateur_de_codifications'],
+     'batch'                  => ['index' => 'Batchs,_tâches_planifiées'],
+     'feature'                => ['index' => 'Nouvelles_fonctionnalités'],
     ];
 
 
diff --git a/library/ZendAfi/View/Helper/Admin/MenuGaucheAdminItem.php b/library/ZendAfi/View/Helper/Admin/MenuGaucheAdminItem.php
new file mode 100644
index 0000000000000000000000000000000000000000..76020eb755c4dd20bb6ec842c4db781ee600db8c
--- /dev/null
+++ b/library/ZendAfi/View/Helper/Admin/MenuGaucheAdminItem.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Copyright (c) 2012-2019, 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_Admin_MenuGaucheAdminItem {
+  protected static $_acl;
+
+  protected
+    $_icon,
+    $_title,
+    $_url,
+    $_attribs,
+    $_skin;
+
+
+  public static function newWith($params) {
+    return new static($params[0],
+                      $params[1],
+                      $params[2],
+                      isset($params[3]) ? $params[3] : [],
+                      isset($params[4]) ? $params[4] : null);
+  }
+
+
+  public function __construct($icon, $title, $url, $attribs=[], $filter=null) {
+    $this->_icon = $icon;
+    $this->_title = $title;
+    $this->_url = $url;
+    $this->_attribs = $attribs;
+    $this->_filter = $filter;
+    $this->_skin = Class_Admin_Skin::current();
+  }
+
+
+  public function renderFor($user, $view) {
+    if (!$this->_isAllowed($user))
+      return '';
+
+    $url = $this->_url;
+    if('http' != substr($url, 0, 4))
+      $url = BASE_URL . $url;
+
+    $extra_infos = isset($this->_attribs['extra_infos'])
+      ? $view->tag('span', $this->_attribs['extra_infos'], ['class' => 'menu_info'])
+      : '';
+
+    $attributes = (array_key_exists('REQUEST_URI', $_SERVER)
+                   && false !== strpos($_SERVER['REQUEST_URI'], $url))
+      ? ['class' => 'selected']
+      : [];
+
+    $anchor_params = ['href' => $url];
+    if (isset($this->_attribs['target']))
+      $anchor_params['target'] = $this->_attribs['target'];
+
+    $ico = $this->_skin->renderMenuIconOn($this->_icon, $view, ['alt' => $this->_title]);
+
+    return $view->tag('li',
+                      $view->tag('a', $ico . $this->_title, $anchor_params) . $extra_infos,
+                      $attributes);
+  }
+
+
+  protected function _isAllowed($user) {
+    if (($filter = $this->_filter) && !$filter($user))
+      return false;
+
+    if ('/admin/' != substr($this->_url, 0, 7))
+      return true;
+
+    $url = substr($this->_url, 7);
+    $parts = explode('/', $url);
+
+    return $this->getAcl()->isAllowed($user, $parts[0], isset($parts[1]) ? $parts[1] : '');
+  }
+
+
+  /** @category testing */
+  public static function setAcl($acl) {
+    static::$_acl = $acl;
+  }
+
+
+  protected function getAcl() {
+    return static::$_acl
+      ? static::$_acl
+      : static::$_acl = new ZendAfi_Acl_AdminControllerGroup();
+  }
+}
diff --git a/library/ZendAfi/View/Helper/AdvancedSearch.php b/library/ZendAfi/View/Helper/AdvancedSearch.php
index f75765a488448ab13fe7fcc952b53c4c48de1b03..56f501e541b449d0887a9b0b2bd895a58bb34679 100644
--- a/library/ZendAfi/View/Helper/AdvancedSearch.php
+++ b/library/ZendAfi/View/Helper/AdvancedSearch.php
@@ -59,8 +59,9 @@ class ZendAfi_View_Helper_AdvancedSearch extends ZendAfi_View_Helper_BaseHelper
 
 
   protected function _renderDefaultAdvancedSearch() {
-    return $this->view->renderForm($this->_defaultAdvancedSearch(),
-                                   ['append' => [$this->_clearFormButton()]]);
+    $form_id = 'advanced_form';
+    return $this->view->renderForm($this->_defaultAdvancedSearch($form_id),
+                                   ['append' => [$this->_clearFormButton($form_id)]]);
   }
 
 
@@ -77,8 +78,8 @@ class ZendAfi_View_Helper_AdvancedSearch extends ZendAfi_View_Helper_BaseHelper
   }
 
 
-  protected function _defaultAdvancedSearch() {
-    return $this->_populateForm((new ZendAfi_Form_AdvancedSearch)->loadDefaults());
+  protected function _defaultAdvancedSearch($form_id) {
+    return $this->_populateForm((new ZendAfi_Form_AdvancedSearch)->loadDefaults(), $form_id);
   }
 
 
diff --git a/library/ZendAfi/View/Helper/Avis.php b/library/ZendAfi/View/Helper/Avis.php
index 57d16d6e3939f371722e95dc7fd31539016796c8..1253a451cb3af9c0037810672df34a94728ad648 100644
--- a/library/ZendAfi/View/Helper/Avis.php
+++ b/library/ZendAfi/View/Helper/Avis.php
@@ -114,7 +114,7 @@ class ZendAfi_View_Helper_Avis extends ZendAfi_View_Helper_BaseHelper {
       if (strlen($auteur_principal = $notice->getAuteurPrincipal()) > 0)
         $title .= ' (' . $auteur_principal . ')';
 
-      $url_vignette = $notice->fetchUrlVignette();
+      $url_vignette = $notice->fetchUrlLocalVignette();
     }
     $content = $this->contenu_avis($avis);
 
diff --git a/library/ZendAfi/View/Helper/Notice/Vignette.php b/library/ZendAfi/View/Helper/Notice/Vignette.php
index def15dfe04d9fb91ffc0ed09062746341067d8fc..9b52cfde9bd8242d9215c4bfc2f9669864db162b 100644
--- a/library/ZendAfi/View/Helper/Notice/Vignette.php
+++ b/library/ZendAfi/View/Helper/Notice/Vignette.php
@@ -63,7 +63,7 @@ abstract class ZendAfi_View_Helper_Notice_VignetteStrategy {
 
     $this->_initUrl();
 
-    return $this->_service->isNoData($this->_getUrl())
+    return $this->_service->isNoData($this->_fetchUrl())
       ? $this->_renderNoThumbnail()
       : $this->_renderThumbnail();
   }
@@ -128,9 +128,7 @@ class ZendAfi_View_Helper_Notice_VignetteStrategyResult
 
 
   protected function _initUrl() {
-    $this->_record->isSigb()
-      ? $this->_record->fetchUrlLocalVignette()
-      : $this->_record->fetchUrlVignette();
+    $this->_record->fetchUrlVignette();
   }
 }
 
@@ -142,7 +140,7 @@ class ZendAfi_View_Helper_Notice_VignetteStrategyView
 
   protected function _renderThumbnail() {
     if ($this->_service->isNoData($this->_record->getUrlImage()))
-      return parent::_renderThumbnail();
+      return parent::_renderNoThumbnail();
 
     $id = 'vignette_' . $this->_record->getId();
 
diff --git a/library/ZendAfi/View/Helper/RenderAuthorDescription.php b/library/ZendAfi/View/Helper/RenderAuthorDescription.php
index db9ce4456fcb9c306c49794b87ff2f25759cce88..b9c0f88c782800eca2b248d3309bd27cc67b23ad 100644
--- a/library/ZendAfi/View/Helper/RenderAuthorDescription.php
+++ b/library/ZendAfi/View/Helper/RenderAuthorDescription.php
@@ -32,15 +32,58 @@ class ZendAfi_View_Helper_RenderAuthorDescription extends ZendAfi_View_Helper_Ba
                        $this->_renderBiography($author_description)
                        . $this->_renderAssociatedFacets($author_description)
                        . $this->_renderCollaborations($author_description)
+                       . $this->_renderYoutube($author_description)
                        . $this->_renderRecords($author_description),
                        ['class' => 'author',
                         'id' => $div_id]);
   }
 
 
+
+  protected function _renderYoutube($author_description) {
+    if (!$author_description->getYoutubeChannelId())
+      return '';
+
+    $id = 'youtube-channel-' . uniqid();
+
+    Class_ScriptLoader::getInstance()
+      ->addJQueryReady(sprintf('$("#%s").load("%s")',
+                               $id,
+                               $this->view->url(['controller' => 'author',
+                                                 'action' => 'render-youtube-channel',
+                                                 'id' => $author_description->getId()])));
+
+    return $this->_tag('div',
+                       $this->_tag('h2', $this->_('Chaîne Youtube'))
+                       . $this->_tag('div', '', ['id' => $id]),
+                       ['class' => 'youtube-channel']);
+  }
+
+
+  protected function _renderInterviews($author_description) {
+    if (!$author_description->getRecords())
+      return '';
+
+    $id = 'interviews-' . uniqid();
+    Class_ScriptLoader::getInstance()
+      ->addJQueryReady(sprintf('$("#%s").load("%s #interview_1")',
+                               $id,
+                               $this->view->url(['controller' => 'noticeajax',
+                                                 'action' => 'videos',
+                                                 'width' => 550,
+                                                 'author' => $author_description->getLabel(),
+                                                 'id' => $author_description->getRecords()[0]->getId()])));
+    return $this->_tag('div',
+                       '',
+                       ['id' => $id,
+                        'class' => 'interview']);
+  }
+
+
   protected function _renderBiography($author_description) {
     return $this->_tag('div',
-                       $author_description->renderBiographieOn($this->view)
+                       $this->_renderInterviews($author_description)
+                       . $author_description->renderBiographieOn($this->view)
                        .
                        $this->_renderSeeMore($this->_('Afficher la biographie complète')),
                        ['class' => 'biography']);
diff --git a/library/ZendAfi/View/Helper/RenderAuthorMetadata.php b/library/ZendAfi/View/Helper/RenderAuthorMetadata.php
new file mode 100644
index 0000000000000000000000000000000000000000..7dc65e4e8c9e9004656d0ff156f433c854c20986
--- /dev/null
+++ b/library/ZendAfi/View/Helper/RenderAuthorMetadata.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Copyright (c) 2012-2018, 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_renderAuthorMetadata extends ZendAfi_View_Helper_BaseHelper {
+
+  public function renderAuthorMetadata($author) {
+    $attributes = $author->toArray();
+
+    if ($id = $attributes['ark'])
+      $attributes['ark'] = $this->_tag('a',
+                                       $id,
+                                       ['href' => 'https://data.bnf.fr/fr/atelier/' . $id]);
+
+    if ($id = $attributes['wikidata_id'])
+      $attributes['wikidata_id'] = $this->_tag('a',
+                                               $id,
+                                               ['href' => 'https://www.wikidata.org/wiki/' . $id]);
+
+    if ($id = $attributes['youtube_channel_id'])
+      $attributes['youtube_channel_id'] = $this->_tag('a',
+                                                      $id,
+                                                      ['href' => Class_WebService_Youtube::CHANNEL_URL . $id]);
+
+    return
+      $this->_tag('h2', $this->_('Auteur : %s',
+                                 $author->getLibelle()))
+      . $this->_tag('dl',
+                    $this->_renderAttributes($attributes));
+  }
+
+
+  protected function _renderAttributes($attributes) {
+    $html = '';
+    foreach($attributes as $key => $value)
+      $html .= $this->_tag('dt', $key) . $this->_tag('dd', $value);
+    return $html;
+  }
+}
+?>
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/Telephone/Frbr.php b/library/ZendAfi/View/Helper/Telephone/Frbr.php
index 87c3acd775f856dc7c5ac79439c5459445fbfb71..67226b9ffa5b17c9c38cacaf41cbb7987f5b3668 100644
--- a/library/ZendAfi/View/Helper/Telephone/Frbr.php
+++ b/library/ZendAfi/View/Helper/Telephone/Frbr.php
@@ -45,7 +45,7 @@ class FrbrNoticesTelephoneRenderer extends Zend_View_Helper_HtmlElement {
   public function renderNotice($notice) {
     $url =  $this->view->urlNotice($notice);
 
-    $img = $this->view->tagImg($notice->fetchUrlVignette());
+    $img = $this->view->tagImg($notice->fetchUrlLocalVignette());
     $h3 = $this->view->tag('h3', $notice->getTitrePrincipal());
     $auteur = $this->view->tag('p', $notice->getAuteurPrincipal());
 
diff --git a/library/ZendAfi/View/Helper/Telephone/Kiosque.php b/library/ZendAfi/View/Helper/Telephone/Kiosque.php
index 6af5c696abf4e70d8ca6a980f230dd8ba3941a58..7a8a878f20375359a9b629694948a239862c9866 100644
--- a/library/ZendAfi/View/Helper/Telephone/Kiosque.php
+++ b/library/ZendAfi/View/Helper/Telephone/Kiosque.php
@@ -70,7 +70,7 @@ class ZendAfi_View_Helper_Telephone_Kiosque extends ZendAfi_View_Helper_Accueil_
                                                   'id' => $notice->getId(),
                                                   'type_doc' => $notice->getTypeDoc()],
                                                  null, true),
-                                $notice->fetchUrlVignette(true),
+                                $notice->fetchUrlLocalVignette(),
                                 $this->preferences["op_largeur_img"],
                                 htmlentities($notice->getTitrePrincipal()));
     }
diff --git a/library/ZendAfi/View/Helper/Telephone/ListeNotices.php b/library/ZendAfi/View/Helper/Telephone/ListeNotices.php
index ed15beee777085823e2e152ce09c9f49757886df..30e7ceafdd08f8c412ee4f5975b912f995209da7 100644
--- a/library/ZendAfi/View/Helper/Telephone/ListeNotices.php
+++ b/library/ZendAfi/View/Helper/Telephone/ListeNotices.php
@@ -90,7 +90,7 @@ class ZendAfi_View_Helper_Telephone_ListeNotices extends ZendAfi_View_Helper_Bas
 
     // Image
     $html .= sprintf('<tr><td width="70px" valign="top"><img src="%s" width="60px" style="cursor:pointer;"></td>',
-                     $notice->fetchUrlVignette());
+                     $notice->fetchUrlLocalVignette());
 
     // Titre / auteur principal
     $html .= '<td valign="top">' . $this->view->iconeSupport($type_doc) . '&nbsp;' . $notice->getTitrePrincipal() . BR . $notice->getAuteurPrincipal();
diff --git a/library/digital_resources/DiMusic/tests/DiMusicTest.php b/library/digital_resources/DiMusic/tests/DiMusicTest.php
index 20301543a77147f669fdd88b34874f4b73530b49..d570649f4fad546fa1e885ff9c3ca4a2fcfb25e9 100644
--- a/library/digital_resources/DiMusic/tests/DiMusicTest.php
+++ b/library/digital_resources/DiMusic/tests/DiMusicTest.php
@@ -525,7 +525,7 @@ abstract class DiMusicRechercheControllerTestCase extends AbstractControllerTest
 
   public function setUp() {
     parent::setUp();
-
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
     $this->_prepareFixtures();
 
     $bridges = $this->fixture('Class_Album',
diff --git a/library/startup.php b/library/startup.php
index 35110464758495911700a3b291974a491db76ce3..cf2decac515097b8d162dff0dbb1e394af31d8dd 100644
--- a/library/startup.php
+++ b/library/startup.php
@@ -80,8 +80,8 @@ class Bokeh_Engine {
 
 
   function setupConstants() {
-    defineConstant('BOKEH_MAJOR_VERSION','7.12');
-    defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.55');
+    defineConstant('BOKEH_MAJOR_VERSION','8.0');
+    defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.2');
 
     defineConstant('BOKEH_REMOTE_FILES', 'http://git.afi-sa.fr/afi/opacce/');
 
@@ -312,7 +312,6 @@ class Bokeh_Engine {
   function setupFrontController() {
     $this->_front_controller = $this
       ->newFrontController()
-      ->addControllerDirectory(ROOT_PATH.'afi/application/modules/opacpriv/controllers','opacpriv')
       ->setBaseURL(BASE_URL);
 
     $this->setupRoutes($this->_front_controller, $this->_config);
diff --git a/public/admin/skins/bokeh74/icons/bokeh-logo.png b/public/admin/skins/bokeh74/icons/bokeh-logo.png
index 7ea7b92e4386a68da35c120c00d7a5e395d3d7a1..ce9442ee00f9bf87ba5721a8d1f29ec958c378f6 100644
Binary files a/public/admin/skins/bokeh74/icons/bokeh-logo.png and b/public/admin/skins/bokeh74/icons/bokeh-logo.png differ
diff --git a/public/opac/css/global.css b/public/opac/css/global.css
index 6953382b6b6c7f1de6582a4d5c6daeca69e633dc..d5b580748f3eaec71ef07fe6a4c9ed002d9a58d4 100644
--- a/public/opac/css/global.css
+++ b/public/opac/css/global.css
@@ -3606,6 +3606,11 @@ a[href*="bookmarked-searches/notify"] img {
 }
 
 
+.author_view .interview {
+    margin-left: 10px;
+    float: right;
+}
+
 .author_view .author > div {
     clear: both;
 }
@@ -3620,6 +3625,7 @@ a[href*="bookmarked-searches/notify"] img {
 
 .author_view .author > .authors h2,
 .author_view .author > .facets h2,
+.author_view .author > .youtube-channel h2,
 .author_view .author .records h2 {
     border-bottom: 1px solid;
     padding-top: 10px;
@@ -3627,6 +3633,16 @@ a[href*="bookmarked-searches/notify"] img {
 }
 
 
+.author_view .author > .youtube-channel > div > div {
+    display: inline-flex;
+    column-gap: 20px;
+}
+
+.author_view .author > .youtube-channel h3 {
+    font-size: 0.9em;
+}
+
+
 .author_view .author > .facets {
     margin-bottom: 20px;
 }
diff --git a/scripts/emacs/yasnippet/snippets/text-mode/php-mode/class b/scripts/emacs/yasnippet/snippets/text-mode/php-mode/class
index 31100f1fc8fa502c88b5a636c4ba9514397ff4b2..121bc84ab8a099add59df20fc44f5ea189bcb539 100644
--- a/scripts/emacs/yasnippet/snippets/text-mode/php-mode/class
+++ b/scripts/emacs/yasnippet/snippets/text-mode/php-mode/class
@@ -3,7 +3,7 @@
 # --
 <?php
 /**
- * Copyright (c) 2012-2018, Agence Française Informatique (AFI). All rights reserved.
+ * Copyright (c) 2012-2019, 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
@@ -28,4 +28,3 @@ class ${class_name} {
       $0
    }
 }
-?>
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/AdminAbstractControllerTestCase.php b/tests/application/modules/admin/controllers/AdminAbstractControllerTestCase.php
index f30bfe8617bfc4b218b409ad68b3b7c803672296..64ef5ff067d3e0d9b1d54f1d9549c91d37b9c62c 100644
--- a/tests/application/modules/admin/controllers/AdminAbstractControllerTestCase.php
+++ b/tests/application/modules/admin/controllers/AdminAbstractControllerTestCase.php
@@ -31,5 +31,10 @@ abstract class Admin_AbstractControllerTestCase extends AbstractControllerTestCa
     $account->LOGIN = "sysadmin";
     $account->PSEUDO = "admin";
   }
+
+
+  public function tearDown() {
+    ZendAfi_View_Helper_Admin_MenuGaucheAdminItem::setAcl(null);
+    parent::tearDown();
+  }
 }
-?>
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php
index b39c7077e15b650b8350b3c504ac07d2db85fcb9..23a09001d62c1aee5155d067e86873f255bd1454 100644
--- a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php
+++ b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php
@@ -81,12 +81,6 @@ class AdminIndexControllerIndexActionTest extends AdminIndexControllerTestCase {
   public function menuGaucheShouldContainsCustomFields() {
     $this->assertXPathContentContains('//li//a','Champs personnalisés');
   }
-
-
-    /** @test */
-  public function menuGaucheShouldContainsCustomFieldReport() {
-    $this->assertXPathContentContains('//li//a','Rapports statistiques');
-  }
 }
 
 
diff --git a/tests/application/modules/admin/controllers/AlbumControllerTest.php b/tests/application/modules/admin/controllers/AlbumControllerTest.php
index 64821a20b22be6dcece4adb20178b04e35f8a1a1..c31ce32943db5f891790aa1e3f426f44b125e6bd 100644
--- a/tests/application/modules/admin/controllers/AlbumControllerTest.php
+++ b/tests/application/modules/admin/controllers/AlbumControllerTest.php
@@ -42,9 +42,9 @@ abstract class Admin_AlbumControllerTestCase extends Admin_AbstractControllerTes
     $fre = $langue_loader->newInstanceWithId('fre')->setLibelle('français');
     $dak = $langue_loader->newInstanceWithId('dak')->setLibelle('dakota');
 
-    Storm_Test_ObjectWrapper::onLoaderOfModel('Class_CodifLangue')
-      ->whenCalled('findAllBy')
-      ->answers(array($cus, $fre, $dak));
+    $this->onLoaderOfModel('Class_CodifLangue')
+         ->whenCalled('findAllBy')
+         ->answers([$cus, $fre, $dak]);
 
     $favoris = $this->fixture('Class_AlbumCategorie', ['id' => 2])
                     ->setParentId(0)
@@ -2646,6 +2646,9 @@ class Admin_AlbumControllerPreviewAlbumBibleSouvignyPostTest extends Admin_Album
 class Admin_AlbumControllerImportEADTest extends Admin_AlbumControllerTestCase {
   public function setUp() {
     parent::setUp();
+
+    Class_AdminVar::set('IMPORT_EAD', 1);
+
     $this->dispatch('/admin/album/import_ead');
   }
 
diff --git a/tests/application/modules/admin/controllers/CatalogueControllerTest.php b/tests/application/modules/admin/controllers/CatalogueControllerTest.php
index 0d36513e06fea794e300202cc04087031c7391d4..a8127f7474198b9c1ce2b5c27759cb24c9a70a8f 100644
--- a/tests/application/modules/admin/controllers/CatalogueControllerTest.php
+++ b/tests/application/modules/admin/controllers/CatalogueControllerTest.php
@@ -1357,12 +1357,20 @@ class CatalogueControllerPaniersHistoireTest extends AdminCatalogueControllerTes
   public function pageShouldContainsLinkToPreview() {
     $this->assertXPath('//a[contains(@href, "/recherche/simple/id_catalogue/100")]');
   }
+
+
+  /** @test */
+  public function defaultOptionShouldPresent() {
+    $this->assertXpathContentContains('//option', 'Veuiller sélectionner un panier');
+  }
 }
 
 
 
 
-class CatalogueControllerPaniersHistoirePostPanierSelectionSenorTest extends AdminCatalogueControllerTestCase {
+class CatalogueControllerPaniersHistoirePostPanierSelectionSenorTest
+  extends AdminCatalogueControllerTestCase {
+
   public function setUp() {
     parent::setUp();
 
@@ -1399,6 +1407,31 @@ class CatalogueControllerPaniersHistoirePostPanierSelectionSenorTest extends Adm
 
 
 
+class CatalogueControllerPaniersHistoirePostPanierSelectionEmptyTest
+  extends AdminCatalogueControllerTestCase {
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->postDispatch('/admin/catalogue/paniers/id_catalogue/100',
+                        ['id_panier' => '']);
+  }
+
+
+  /** @test */
+  public function answerShouldRedirectToCataloguePanier100() {
+    $this->assertRedirectTo('/admin/catalogue/paniers/id_catalogue/100');
+  }
+
+
+  /** @test */
+  public function messageShouldBeAucunPanierSelectionnePourAjout() {
+    $this->assertFlashMessengerContentContains('Aucun panier sélectionné pour ajout');
+  }
+}
+
+
+
 
 class CatalogueControllerPaniersHistoireRemovePanierJeunesseTest extends AdminCatalogueControllerTestCase {
   public function setUp() {
diff --git a/tests/application/modules/admin/controllers/CustomFieldsReportControllerTest.php b/tests/application/modules/admin/controllers/CustomFieldsReportControllerTest.php
index 5f0503bb5cdd721bc8ab80e8dd512f37c543a8ba..7f3e70280cfdb12904e909f26d9547672dc82837 100644
--- a/tests/application/modules/admin/controllers/CustomFieldsReportControllerTest.php
+++ b/tests/application/modules/admin/controllers/CustomFieldsReportControllerTest.php
@@ -25,6 +25,7 @@ class CustomFieldsReportControllerAddTest extends AbstractControllerTestCase {
 
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('CUSTOM_FIELDS_REPORT', 1);
     $this->dispatch('/admin/custom-fields-report/add', true);
   }
 
@@ -67,6 +68,8 @@ class CustomFieldsReportControllerEditTest extends AbstractControllerTestCase {
   public function setUp() {
     parent::setUp();
 
+    Class_AdminVar::set('CUSTOM_FIELDS_REPORT', 1);
+
     $this->fixture('Class_Report', [
       'id' => 2,
       'label' => 'Report',
@@ -90,6 +93,9 @@ class CustomFieldsReportControllerGenerateTest extends AbstractControllerTestCas
 
   public function setUp() {
     parent::setUp();
+
+    Class_AdminVar::set('CUSTOM_FIELDS_REPORT', 1);
+
     $this->fixture('Class_Report', [
       'id' => 42,
       'label' => '',
@@ -108,5 +114,4 @@ class CustomFieldsReportControllerGenerateTest extends AbstractControllerTestCas
   public function customFieldsJsShouldBePresent() {
     $this->assertXPath('//script[contains(@src, "custom-fields/custom_fields.js")]');
   }
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/NewsletterControllerTest.php b/tests/application/modules/admin/controllers/NewsletterControllerTest.php
index ee6e00933c3d30c2cf61b2efb6ec7ce63686bd5e..88a47098fedfc4236162ce123216eab52e5abaa4 100644
--- a/tests/application/modules/admin/controllers/NewsletterControllerTest.php
+++ b/tests/application/modules/admin/controllers/NewsletterControllerTest.php
@@ -316,6 +316,7 @@ class Admin_NewsletterControllerEditActionTest extends Admin_NewsletterControlle
                     'titre' => 'Nouveautés',
                     'contenu'=>'Notre sélection du mois',
                     'expediteur' => 'laurent@free.fr',
+                    'articles_ids' => '6-42',
                     'user_groups' => [Class_UserGroup::find(14),
                                       Class_UserGroup::find(15)]]);
 
@@ -359,6 +360,19 @@ class Admin_NewsletterControllerEditActionTest extends Admin_NewsletterControlle
   public function testSubmitButton() {
     $this->assertXPath("//div//button[contains(@onclick,\"submit()\")]");
   }
+
+
+  /** @test */
+  public function articlesTabShouldBePresent() {
+    $this->assertXPathContentContains('//form//fieldset//legend', 'Articles');
+  }
+
+
+  /** @test */
+  public function articleSelectionFieldsShouldBePresent() {
+    $this->assertXPath('//form//input[@name="articles_ids"][@value="6-42"]');
+    $this->assertXPath('//form//input[@name="articles_categories_ids"][@value=""]');
+  }
 }
 
 
@@ -374,7 +388,9 @@ class Admin_NewsletterControllerAddActionPostTest extends Admin_NewsletterContro
              'contenu' => 'Plein les yeux',
              'id_catalogue' => '',
              'id_panier' => '',
-             'nb_notices' => 2];
+             'nb_notices' => 2,
+             'articles_ids' => '6-42',
+             'articles_categories_ids' => '3-4'];
 
     $this->postDispatch('/admin/newsletter/add', $data);
     $this->_new = Class_Newsletter::findFirstBy(['titre' => 'Fêtes du lac']);
@@ -404,6 +420,17 @@ class Admin_NewsletterControllerAddActionPostTest extends Admin_NewsletterContro
     $this->assertEquals('Groupe manuel pour la lettre "Fêtes du lac"',
                         $this->_new->getDedicatedGroup()->getLibelle());
   }
+
+
+  /** @test */
+  public function articlesIdsShouldBe6_42() {
+    $this->assertEquals('6-42', $this->_new->getArticlesIds());
+  }
+
+  /** @test */
+  public function articlesCategoriesIdsShouldBe3And4() {
+    $this->assertEquals('3-4', $this->_new->getArticlesCategoriesIds());
+  }
 }
 
 
@@ -837,6 +864,27 @@ class Admin_NewsletterControllerPreviewActionTest extends Admin_NewsletterContro
                                         'libelle' => 'Mon catalogue',
                                         'type_doc' => 5]);
 
+    $septante_millions = $this->fixture('Class_Article',
+                                  ['id' => 35,
+                                   'titre' => '70 millions …',
+                                   'contenu' => 'Ils sont 70 millions prêts à prendre sa place.']);
+
+    $le_roi_des_cons = $this->fixture('Class_Article',
+                                      ['id' => 36,
+                                       'titre' => 'Le Roi des cons',
+                                       'contenu' => 'Il est beau, il est fier sur son throne.']);
+
+    $l_arnaque = $this->fixture('Class_Article',
+                                ['id' => 12,
+                                 'titre' => 'L\'arnaque',
+                                 'contenu' => 'La révolution a oublié de décapiter la misère et l\'exploitation.']);
+
+    $this->fixture('Class_ArticleCategorie',
+                   ['id' => 89,
+                    'libelle' => 'La France',
+                    'articles' => [$le_roi_des_cons,
+                                   $septante_millions]]);
+
     $nouveautes = $this->fixture('Class_Newsletter',
                                  ['id' => 3,
                                   'titre' => 'Nouveautés',
@@ -844,6 +892,8 @@ class Admin_NewsletterControllerPreviewActionTest extends Admin_NewsletterContro
                                   'id_catalogue' => 1,
                                   'nb_notices' => 0,
                                   'id_panier' => null,
+                                  'articles_categories_ids' => '89-99',
+                                  'articles_ids' => '12',
                                   'last_distribution_date' => '2012-03-02']);
 
 
@@ -854,18 +904,22 @@ class Admin_NewsletterControllerPreviewActionTest extends Admin_NewsletterContro
     $this->dispatch('/admin/newsletter/preview/id/3', true);
   }
 
+
   public function testFrom() {
     $this->assertQueryContentContains('p', 'laurent@afi-sa.net');
   }
 
+
   public function testSubject() {
     $this->assertQueryContentContains('p', 'Nouveautés');
   }
 
+
   public function testBodyText() {
     $this->assertQueryContentContains('p', 'Notre sélection du mois Hoho');
   }
 
+
   public function testBodyHtml() {
     $this->assertXPath('//div//img[@src="zork.jpg"]', $this->_response->getBody());
   }
@@ -876,16 +930,29 @@ class Admin_NewsletterControllerPreviewActionTest extends Admin_NewsletterContro
     $this->assertQueryContentContains('p', 'Martine à la plage', $this->_response->getBody());
   }
 
+
   /**  @test */
   public function noticeMartineALaPlageUrlShouldBeRechercheViewNotice42() {
     $this->assertXPath('//a[@href="' . Class_Url::absolute('/recherche/viewnotice/id/42') . '"]');
   }
 
+
   /** @test */
   public function templateShouldNotCreateNewsletterDispatch() {
     $this->assertEmpty(Class_Newsletter_Dispatch::findAll());
   }
 
+
+  /** @test */
+  public function leRoiDesConsShouldBeDisplay() {
+    $this->assertXpath('//div', 'Le Roi des cons', $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function lArnaqueShouldBeDisplay() {
+    $this->assertXpath('//div', 'L\'arnaque', $this->_response->getBody());
+  }
 }
 
 
diff --git a/tests/application/modules/admin/controllers/OaiControllerTest.php b/tests/application/modules/admin/controllers/OaiControllerTest.php
index bce98625543a380761d7f0b967338f8639bbfac0..817488322b86ef884991501c5aad07852ca4e115 100644
--- a/tests/application/modules/admin/controllers/OaiControllerTest.php
+++ b/tests/application/modules/admin/controllers/OaiControllerTest.php
@@ -48,6 +48,9 @@ abstract class Admin_OaiControllerTestCase extends Admin_AbstractControllerTestC
 class Admin_OaiControllerIndexActionTest extends Admin_OaiControllerTestCase  {
   public function setUp() {
     parent::setUp();
+
+    Class_AdminVar::set('OAI_REPOSITORY', 1);
+
     $this->dispatch('/admin/oai');
   }
 
@@ -91,6 +94,7 @@ class Admin_OaiControllerIndexActionTest extends Admin_OaiControllerTestCase  {
 class Admin_OaiControllerAddActionTest extends Admin_OaiControllerTestCase  {
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('OAI_REPOSITORY', 1);
     $this->dispatch('/admin/oai/add');
   }
 
@@ -107,6 +111,7 @@ class Admin_OaiControllerAddActionTest extends Admin_OaiControllerTestCase  {
 class Admin_OaiControllerEditActionTest extends Admin_OaiControllerTestCase  {
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('OAI_REPOSITORY', 1);
     $this->dispatch('/admin/oai/edit/id/4');
   }
 
@@ -123,6 +128,7 @@ class Admin_OaiControllerEditActionTest extends Admin_OaiControllerTestCase  {
 class Admin_OaiControllerBrowseGallicaActionTest extends Admin_OaiControllerTestCase  {
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('OAI_REPOSITORY', 1);
     $web_client = Storm_Test_ObjectWrapper::mock();
     Class_WebService_OAI::setDefaultHttpClient($web_client);
 
@@ -155,6 +161,9 @@ class Admin_OaiControllerImportIsaacAsimovFoundationTest extends Admin_OaiContro
 
   public function setUp() {
     parent::setUp();
+
+    Class_AdminVar::set('OAI_REPOSITORY', 1);
+
     $foundation = Class_NoticeOAI::getLoader()
       ->newInstanceWithId(23)
       ->setData(serialize(array('titre' => 'Prelude to foundation',
@@ -227,6 +236,7 @@ class Admin_OaiControllerImportIsaacAsimovFoundationTest extends Admin_OaiContro
 class Admin_OaiControllerSearchAsimovActionTest extends Admin_OaiControllerTestCase  {
   /** @test */
   public function pageShouldRedirectToOaiIndexWithExpressionRecherche() {
+    Class_AdminVar::set('OAI_REPOSITORY', 1);
     $this->dispatch('/admin/oai/search?expression=asimov', true);
     $this->assertRedirectTo('/admin/oai/index/expression/asimov');
   }
@@ -238,6 +248,7 @@ class Admin_OaiControllerSearchAsimovActionTest extends Admin_OaiControllerTestC
 abstract class Admin_OaiControllerSearchActionTestCase extends Admin_OaiControllerTestCase  {
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('OAI_REPOSITORY', 1);
     $this->mock_sql = Storm_Test_ObjectWrapper::on(Zend_Registry::get('sql'));
     Zend_Registry::set('sql', $this->mock_sql);
   }
@@ -307,8 +318,4 @@ class Admin_OaiControllerIndexSearchInsignifantWordActionTest extends Admin_OaiC
   public function pageShouldContainsPasAssezDeMotsSignifications() {
     $this->assertXPathContentContains('//div[@class="error"]', "Il n'y aucun mot assez significatif pour la recherche");
   }
-}
-
-
-
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/OpdsControllerTest.php b/tests/application/modules/admin/controllers/OpdsControllerTest.php
index 1beb9a10de8ed051b57b2883e9ebaf99f53cccd5..a9f8579094ffc4f9f19599e33079374f5ffd1f46 100644
--- a/tests/application/modules/admin/controllers/OpdsControllerTest.php
+++ b/tests/application/modules/admin/controllers/OpdsControllerTest.php
@@ -24,6 +24,9 @@ abstract class Admin_OpdsControllerTestCase extends Admin_AbstractControllerTest
 
   public function setUp() {
     parent::setUp();
+
+    Class_AdminVar::set('OPDS', 1);
+
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_OpdsCatalog')
       ->whenCalled('findAllBy')
       ->with(array('order' => 'libelle'))
@@ -59,6 +62,7 @@ class Admin_OpdsControllerWithReferentActionTest extends Admin_AbstractControlle
 
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('OPDS', 1);
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users')
       ->whenCalled('getIdentity')
       ->answers($user_referent=Class_Users::getLoader()->newInstanceWithId(2)
@@ -945,5 +949,4 @@ class Admin_OpdsControllerBrowseFeedbooksTestCase extends Admin_OpdsControllerTe
     $album = Class_Album::getLoader()->getFirstAttributeForLastCallOn('save');
     $this->assertEquals('In his extraordinary debut novel of psychological suspense, Thomas OCallaghan proves himself a worthy successor to Thomas Harris as he introduces one of the most compelling and terrifying serial killers since Hannibal Lecter in a book where every...', $album->getDescription());
   }
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/ProfilControllerTest.php b/tests/application/modules/admin/controllers/ProfilControllerTest.php
index d4c6642b22b56fbacc43a8fe9844531c90751e23..6b201a6be244c71fb5cf96010c5c08e59586167d 100644
--- a/tests/application/modules/admin/controllers/ProfilControllerTest.php
+++ b/tests/application/modules/admin/controllers/ProfilControllerTest.php
@@ -1357,17 +1357,19 @@ class Admin_ProfilControllerNonExistingProfileTest extends Admin_AbstractControl
 class Admin_ProfilControllerGenresActionTest extends Admin_AbstractControllerTestCase {
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('CUSTOM_GENRE_ICON', 1);
+
     $this->dispatch('/admin/profil/genres');
   }
 
   /** @test */
-  function actionShouldBeGenres() {
+  public function actionShouldBeGenres() {
     $this->assertAction('genres');
   }
-
 }
 
 
+
 class Admin_ProfilControllerEditMenuHorizontalTest extends Admin_AbstractControllerTestCase {
   public function setUp() {
     parent::setUp();
diff --git a/tests/application/modules/admin/controllers/RedmineControllerTest.php b/tests/application/modules/admin/controllers/RedmineControllerTest.php
index 8fc8aab752b9f33ce46d06d934da9cbcde9c5f9c..7ede71a553c9743788ddf45c1c206a1c0194e803 100644
--- a/tests/application/modules/admin/controllers/RedmineControllerTest.php
+++ b/tests/application/modules/admin/controllers/RedmineControllerTest.php
@@ -305,8 +305,8 @@ class Admin_RedmineControllerIndexTest extends Admin_RedmineControllerWithApiTes
 
 
   /** @test */
-  public function menuHorizontalShouldContainsOneIssueQuestionClient() {
-    $this->assertXPathContentContains('//div[contains(@class, "barre_nav")]//a[@title="Assistance"]//span[@class="menu_info"]', '1', $this->_response->getBody());
+  public function menuHorizontalShouldContainsTwoIssues() {
+    $this->assertXPathContentContains('//div[contains(@class, "barre_nav")]//a[@title="Assistance"]//span[@class="menu_info"]', '2', $this->_response->getBody());
   }
 }
 
@@ -331,7 +331,7 @@ class Admin_RedmineControllerCacheTest extends Admin_RedmineControllerWithApiTes
   public function onAdminIndexMenuHorizontalShouldGetResultFromCache() {
     $this->dispatch('/admin/index/index');
 
-    $this->assertXPathContentContains('//div[contains(@class, "barre_nav")]//a[@title="Assistance"]//span[@class="menu_info"]', '1');
+    $this->assertXPathContentContains('//div[contains(@class, "barre_nav")]//a[@title="Assistance"]//span[@class="menu_info"]', '2');
   }
 
 
diff --git a/tests/application/modules/admin/controllers/ReferentPortailControllerTest.php b/tests/application/modules/admin/controllers/ReferentPortailControllerTest.php
index 8df217220e728e80c0ca0f0aa9518aa79d270c76..4ff3a9189faaacf66e2ebf37cb26d4071d4d3558 100644
--- a/tests/application/modules/admin/controllers/ReferentPortailControllerTest.php
+++ b/tests/application/modules/admin/controllers/ReferentPortailControllerTest.php
@@ -16,12 +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
  */
 require_once 'AbstractControllerTestCase.php';
 
-abstract class ReferentPortailController_AbstractControllerTestCase extends AbstractControllerTestCase {
-  use  Trait_UserGroupFixtures;
+abstract class ReferentPortailControllerTestCase extends AbstractControllerTestCase {
+  use Trait_UserGroupFixtures;
+
   protected function _initProfilHook($profil) {
     $profil->setLibelle('AFI');
   }
@@ -36,10 +37,10 @@ abstract class ReferentPortailController_AbstractControllerTestCase extends Abst
   public function setUp() {
     parent::setUp();
     $user_referent=Class_Users::getLoader()->newInstanceWithId(777)
-                ->setLogin('referent')
-                ->setRoleLevel(ZendAfi_Acl_AdminControllerRoles::MODO_PORTAIL)
-                ->setPseudo('referent');
-    
+                                           ->setLogin('referent')
+                                           ->setRoleLevel(ZendAfi_Acl_AdminControllerRoles::MODO_PORTAIL)
+                                           ->setPseudo('referent');
+
     $this->addUserToRightsReferent($user_referent);
 
   }
@@ -47,39 +48,42 @@ abstract class ReferentPortailController_AbstractControllerTestCase extends Abst
 
 
 
-class ReferentPortailControllerIndexActionTest extends ReferentPortailController_AbstractControllerTestCase {
+class ReferentPortailControllerIndexActionTest extends ReferentPortailControllerTestCase {
   public function setUp() {
     parent::setUp();
-    Class_AdminVar::getLoader()->newInstanceWithId('BIBNUM')
-      ->setValeur(1);
+
+    Class_AdminVar::set('BIBNUM', 1);
+    Class_AdminVar::set('OPDS', 1);
+
     $this->dispatch('/admin/index', true);
   }
 
+
   /** @test */
   function menuGaucheShouldContainsSitotheque() {
     $this->assertXPathContentContains('//li//a','Sitothèque',$this->_response->getBody());
   }
 
 
- /** @test */
+  /** @test */
   function menuGaucheShouldContainsDomaines() {
     $this->assertXPathContentContains('//li//a','Domaines',$this->_response->getBody());
   }
 
 
- /** @test */
+  /** @test */
   function menuGaucheShouldContainsModeration() {
     $this->assertXPathContentContains('//li//a','Modération',$this->_response->getBody());
   }
 
 
- /** @test */
+  /** @test */
   function menuGaucheShouldContainsDemandeDinscription() {
     $this->assertXPathContentContains('//li//a','Demandes d\'inscription',$this->_response->getBody());
   }
 
 
- /** @test */
+  /** @test */
   function menuGaucheShouldContainsCollections() {
     $this->assertXPathContentContains('//li//a','Collections',$this->_response->getBody());
   }
@@ -99,8 +103,4 @@ class ReferentPortailControllerIndexActionTest extends ReferentPortailController
   function menuGaucheShouldContainsUtilisateurs() {
     $this->assertXPathContentContains('//li//a','Utilisateurs',$this->_response->getBody());
   }
-
-
-}
-
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/SystemeControllerOpac2ImportTest.php b/tests/application/modules/admin/controllers/SystemeControllerOpac2ImportTest.php
index ec4c1e924c4bb32f12886ad98192a5f7ef5fe6bf..43403ee7fe2104ef79558d8ed176654c0694b640 100644
--- a/tests/application/modules/admin/controllers/SystemeControllerOpac2ImportTest.php
+++ b/tests/application/modules/admin/controllers/SystemeControllerOpac2ImportTest.php
@@ -27,6 +27,8 @@ abstract class SystemeControllerOpac2ImportTestCase
     parent::setUp();
     Storm_Model_Loader::defaultToVolatile();
 
+    Class_AdminVar::set('IMPORT_AVIS_OPAC2', 1);
+
     $this->fixture('Class_AdminVar',
                    ['id' => 'AVIS_MAX_SAISIE',
                     'valeur' => '1000000']);
diff --git a/tests/application/modules/admin/controllers/SystemeControllerWebServicesTest.php b/tests/application/modules/admin/controllers/SystemeControllerWebServicesTest.php
index 1be3c1c221f959b5ac199a15d18bad4248a63c2f..e562f28fe138b60f94b116383acdd222741573c0 100644
--- a/tests/application/modules/admin/controllers/SystemeControllerWebServicesTest.php
+++ b/tests/application/modules/admin/controllers/SystemeControllerWebServicesTest.php
@@ -21,6 +21,9 @@
 class SystemeControllerWebServicesIndexActionTest extends Admin_AbstractControllerTestCase {
   public function setUp() {
     parent::setUp();
+
+    Class_AdminVar::set('WEBSERVICE_TEST', 1);
+
     $this->dispatch('admin/systeme/webservices');
   }
 
@@ -48,25 +51,23 @@ class SystemeControllerWebServicesActionTest extends Admin_AbstractControllerTes
    * @test
    */
   public function webServiceFnacGetResumeShouldWork() {
+    Class_AdminVar::set('WEBSERVICE_TEST', 1);
     $this->dispatch('/admin/systeme/webservices/id_service/Fnac/id_fonction/1');
     $this->assertXPathContentContains('//pre[@class="resultat"]',
-                                      'Un campus prestigieux figé sous la neige',
-                                      $this->_response->getBody());
+                                      'Un campus prestigieux figé sous la neige');
   }
 
 
   /**
-   * @group no-ci
    * @group longtest
    * @group integration
    * @test
    */
   public function webServicePremiereGetResumeShouldWork() {
+    Class_AdminVar::set('WEBSERVICE_TEST', 1);
     $this->dispatch('/admin/systeme/webservices/id_service/Premiere/id_fonction/1', true);
 
     $this->assertXPathContentContains('//pre[@class="resultat"]',
-                                      'Jake Sully est un ancien marine', $this->_response->getBody());
+                                      'Jake Sully est un ancien marine');
   }
-}
-
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/AbonneControllerLoansHistoryNanookTest.php b/tests/application/modules/opac/controllers/AbonneControllerLoansHistoryNanookTest.php
index c66ab627d62bcfbab33fa2b64e30b39ea4d48195..e445867f63aba0b44268ef3669a6cb981a794bdb 100644
--- a/tests/application/modules/opac/controllers/AbonneControllerLoansHistoryNanookTest.php
+++ b/tests/application/modules/opac/controllers/AbonneControllerLoansHistoryNanookTest.php
@@ -103,6 +103,7 @@ abstract class AbonneControllerLoansHistoryNanookTestCase extends AbstractContro
 class AbonneControllerLoansHistoryNanookIndexTest extends AbonneControllerLoansHistoryNanookTestCase {
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
     $this->dispatch('/abonne/loans-history',true);
   }
 
diff --git a/tests/application/modules/opac/controllers/AbonneControllerPretsTest.php b/tests/application/modules/opac/controllers/AbonneControllerPretsTest.php
index ee9f487f8db77f26467f8ad48e2ae6e6ff6edc16..dc8b18f4cfa89b8d3fe1d153da00b44c8f025da7 100644
--- a/tests/application/modules/opac/controllers/AbonneControllerPretsTest.php
+++ b/tests/application/modules/opac/controllers/AbonneControllerPretsTest.php
@@ -276,6 +276,9 @@ class AbonneControllerPretsExportThreePretsTest extends AbonneControllerPretsLis
 class AbonneControllerPretsListThreePretsTest extends AbonneControllerPretsListThreePretsTestCase {
   public function setUp() {
     parent::setUp();
+
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
+
     $this->fixture('Class_CodifAuteur',
                    ['id' => 565,
                     'libelle' => 'QUINROSE']);
@@ -468,12 +471,6 @@ class AbonneControllerPretsListThreePretsTest extends AbonneControllerPretsListT
     $this->assertXPathContentContains('//tbody//td[@class="date_retour"]',
                                       '01/01/2020');
   }
-
-
-  /** @test */
-  public function aButterflyAnchorIdShouldBeVignette889() {
-    $this->assertXPath('//a[@id="vignette_889"]');
-  }
 }
 
 
@@ -594,6 +591,7 @@ abstract class AbonneControllerPretsListReservationTestCase extends AbstractAbon
 class AbonneControllerPretsListReservationTest extends AbonneControllerPretsListReservationTestCase {
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
     $this->dispatch('/opac/abonne/reservations', true);
   }
 
@@ -641,7 +639,7 @@ class AbonneControllerPretsListReservationTest extends AbonneControllerPretsList
   }
 
 
-    /** @test */
+  /** @test */
   public function authorShouldBeRowlingAndLinkSearch() {
     $this->assertXPathContentContains('//tbody/tr[1]//td//a[@href="/recherche/simple/code_rebond/A456/retour_abonne/reservations"]',
                                       'JOANNE KATHLEEN ROWLING',
@@ -936,6 +934,7 @@ class AbonneControllerPretsPeriodicalTitleTest extends AbstractAbonneControllerP
                                                                   ['clef' => 'k', 'valeur' => 'J-REV 10-N°90']]),
                                           'notice' => $this->fixture('Class_Notice',
                                                                      ['id' => 889,
+                                                                      'url_vignette' => 'NO',
                                                                       'unimarc' => file_get_contents(__DIR__.'/petites_mains.mrc')])]));
 
     $loan = new Class_WebService_SIGB_Emprunt(12387, $item);
@@ -986,8 +985,8 @@ class AbonneControllerPretsPeriodicalTitleTest extends AbstractAbonneControllerP
 
 
   /** @test */
-  public function imageForPetitsMainsShouldBePresent() {
-    $this->assertXPath('//div[contains(@class, "nothumbnail")]');
+  public function petitesMainsShouldNotHaveThumbnail() {
+    $this->assertXPath('//div[contains(@class, "nothumbnail")]', $this->_response->getBody());
   }
 
 
diff --git a/tests/application/modules/opac/controllers/JavaControllerTest.php b/tests/application/modules/opac/controllers/JavaControllerTest.php
index fa41dac57e39bc7c1227d8db379d4c34e1283227..97f4ae5999dd08090e1d2828c9ea08ddc30eb3d7 100644
--- a/tests/application/modules/opac/controllers/JavaControllerTest.php
+++ b/tests/application/modules/opac/controllers/JavaControllerTest.php
@@ -251,7 +251,7 @@ class JavaControllerKiosqueSlideShowWithRedirectSettingsTest extends AbstractCon
       ->answers(Class_TypeDoc::LIVRE)
       ->whenCalled('getTitrePrincipal')
       ->answers('Programmers <br> Hell\'s "code"')
-      ->whenCalled('fetchUrlVignette')
+      ->whenCalled('fetchUrlLocalVignette')
       ->answers('/img/hell_code.png')
       ->whenCalled('getClefAlpha')
       ->answers('PROGRAMMER_HELL');
@@ -273,7 +273,7 @@ class JavaControllerKiosqueSlideShowWithRedirectSettingsTest extends AbstractCon
       ->whenCalled('getClefAlpha')
       ->answers('HARLOCK')
 
-      ->whenCalled('fetchUrlVignette')
+      ->whenCalled('fetchUrlLocalVignette')
       ->answers('/img/harlock.png');
 
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Catalogue')
diff --git a/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php b/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
index c32c86d73d88ccc5e7cb28fca347dcb2f8f8b5c1..41a5ea88bb769afce3443e0fe57e5dec91c2c0b3 100644
--- a/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
+++ b/tests/application/modules/opac/controllers/NoticeAjaxControllerTest.php
@@ -172,7 +172,7 @@ class NoticeAjaxControllerNoticeSimilairesSouleymaneTest extends NoticeAjaxContr
 
   public function setUp() {
     parent::setUp();
-
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
     Class_Notice::newInstanceWithId(18787,
                                     ['titres' => 'IRM SOULEY',
                                      'auteurs' => 'MOI',
@@ -555,28 +555,30 @@ class NoticeAjaxControllerBibliographiesTest extends NoticeAjaxControllerLastFmT
 }
 
 
-class NoticeAjaxControllerInterviewsTest extends AbstractControllerTestCase {
 
 
+class NoticeAjaxControllerInterviewsTest extends AbstractControllerTestCase {
   /** @test */
-  public function shouldContainsTitle() {
+  public function responseH3ShouldContainsFrodoInterview() {
     $this->fixture('Class_Notice', ['id' => 777])
          ->setAuteurPrincipal('Frodo')
          ->save();
 
     Class_WebService_AllServices::setHttpClient(
                                                 Storm_Test_ObjectWrapper::mock()
-                                                ->whenCalled('open_url')->answers(json_encode(['source' => 'Testing',
-                                                                                               'videos' => [['titre' => '',
-                                                                                                            'contenu' => '']]])));
+                                                ->whenCalled('open_url')
+                                                ->answers(json_encode(['source' => 'Testing',
+                                                                       'videos' => [['titre' => 'Frodo interview by M.Drucker',
+                                                                                     'contenu' => '']]])));
 
     $this->dispatch('noticeajax/videos?id_notice=777', true);
-    $this->assertXPath('//h3');
+    $this->assertXPathContentContains('//h3', 'Frodo interview by M.Drucker');
   }
 }
 
 
 
+
 /** @see http://forge.afi-sa.fr/issues/16741 */
 class NoticeAjaxControllerResumeGamWidgetTest extends AbstractControllerTestCase {
 
diff --git a/tests/application/modules/opac/controllers/RechercheControllerTest.php b/tests/application/modules/opac/controllers/RechercheControllerTest.php
index 52b31dd80522fe94b3118eee58c116789ecf96c5..339237723974e2015cd57f3444f8e0bd012d37c2 100644
--- a/tests/application/modules/opac/controllers/RechercheControllerTest.php
+++ b/tests/application/modules/opac/controllers/RechercheControllerTest.php
@@ -304,6 +304,8 @@ class RechercheControllerViewNoticeWithPreferencesTest extends RechercheControll
   public function setUp() {
     parent::setUp();
 
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
+
     $time_source = new TimeSourceForTest('2013-12-27 09:00:00');
     Class_Notice::setTimeSource($time_source);
 
@@ -492,7 +494,8 @@ class RechercheControllerViewNoticeWithPreferencesTest extends RechercheControll
 
   /** @test */
   public function vignetteShouldBeGenerated() {
-    $this->assertXPath('//div[@class="nothumbnail type_doc_1"]');
+    $this->assertXPath('//div[@class="nothumbnail type_doc_1"]',
+                       $this->_response->getBody());
   }
 
 
@@ -561,6 +564,7 @@ class RechercheControllerViewNoticeWithPreferencesTest extends RechercheControll
 class RechercheControllerViewNoticeTest extends RechercheControllerNoticeTestCase {
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
     $this->dispatch('recherche/viewnotice/id/345', true);
   }
 
@@ -572,16 +576,19 @@ class RechercheControllerViewNoticeTest extends RechercheControllerNoticeTestCas
                                       $this->_response->getBody());
   }
 
+
   /** @test */
   public function pageShouldContainsOnlyTwoH1() {
     $this->assertXPathCount('//h1', 2, $this->_response->getBody());
   }
 
+
   /** @test */
   public function pageShouldContainsH1WithTitleAndAuthor() {
     $this->assertXPath('//h1//span[contains(text(), "Cinéma d\'animation")][following-sibling::span[text()="Bernard Génin"]]');
   }
 
+
   /** @test */
   public function tagReseauSociauxShouldBePresent() {
     $this->assertXPath('//div[@id="reseaux-sociaux"]');
diff --git a/tests/application/modules/opac/controllers/RssControllerTest.php b/tests/application/modules/opac/controllers/RssControllerTest.php
index 7463863cc61fdd54d69448cf153dc5076ceb0d75..7ae5b88f23826320166e4819fd1c9d6ed9b81607 100644
--- a/tests/application/modules/opac/controllers/RssControllerTest.php
+++ b/tests/application/modules/opac/controllers/RssControllerTest.php
@@ -437,8 +437,8 @@ class RssControllerCritiquesTest extends AbstractControllerTestCase {
 
 
   /** @test */
-  public function rssItemFirstImageShouldBeThumbnail() {
-    $this->assertContains('recherche/vignette/clef/4d2b6c2f478673298fefbb7321ce940c/id_notice/1"/><media:content', $this->_response->getBody());
+  public function rssItemFirstImageShouldBeRawThumbnail() {
+    $this->assertContains('recherche/raw-thumbnail/id/1"/><media:content', $this->_response->getBody());
   }
 }
 
diff --git a/tests/application/modules/telephone/controllers/RechercheControllerTest.php b/tests/application/modules/telephone/controllers/RechercheControllerTest.php
index b49f1022d8cc81ae954f653096a434767e782db9..d2bdced0ced16caa444babdb854c4c4d3feae4e2 100644
--- a/tests/application/modules/telephone/controllers/RechercheControllerTest.php
+++ b/tests/application/modules/telephone/controllers/RechercheControllerTest.php
@@ -264,7 +264,7 @@ class Telephone_RechercheControllerFrbrWithLinksTest extends TelephoneAbstractCo
 
   /** @test */
   public function tintinPicarosShouldHaveImageFromVignetteAction() {
-    $this->assertXPath('//li/a/img[contains(@src, "/recherche/vignette/clef")][contains(@src, "/id_notice/555")]');
+    $this->assertXPath('//li/a/img[contains(@src, "recherche/raw-thumbnail/id/555")]');
   }
 }
 
diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php
index 8766b990ff6beb2487790267f1784437ee960782..4eb5ae89b45542799e95045101302cadc1579ed2 100644
--- a/tests/db/UpgradeDBTest.php
+++ b/tests/db/UpgradeDBTest.php
@@ -1999,7 +1999,6 @@ class UpgradeDB_341_Test extends UpgradeDBTestCase {
 
 
 
-
 class UpgradeDB_342_Test extends UpgradeDBTestCase {
   public function prepare() {}
 
@@ -2409,7 +2408,6 @@ class UpgradeDB_361_Test extends UpgradeDBTestCase {
 
 
 
-
 class UpgradeDB_362_Test extends UpgradeDBTestCase {
   public function prepare() {
     $this->silentQuery('ALTER TABLE album drop KEY date_maj');
@@ -2438,3 +2436,72 @@ class UpgradeDB_363_Test extends UpgradeDBTestCase {
     $this->assertEquals($expected, $thesaurus['rules']);
   }
 }
+
+
+
+
+class UpgradeDB_364_Test extends UpgradeDBTestCase {
+  public function prepare() {
+    $this->silentQuery('ALTER TABLE newsletters DROP column articles_ids');
+    $this->silentQuery('ALTER TABLE newsletters DROP column articles_categories_ids');
+  }
+
+  /** @test **/
+  public function articlesIdsShouldBePresent() {
+    $this->assertColumn('newsletters', 'articles_ids');
+  }
+
+  /** @test **/
+  public function articlesCategoriesShouldBePresent() {
+    $this->assertColumn('newsletters', 'articles_categories_ids');
+  }
+}
+
+
+
+class UpgradeDB_365_Test extends UpgradeDBTestCase {
+  public function prepare() {}
+
+  /** @test */
+  public function placeholderForBokehEightMigration() {}
+}
+
+
+
+
+class UpgradeDB_366_Test extends UpgradeDBTestCase {
+  public function prepare() {
+    $this
+      ->silentQuery('ALTER TABLE codif_auteur DROP KEY wikidata_id')
+      ->silentQuery('ALTER TABLE codif_auteur DROP COLUMN wikidata_id')
+      ->silentQuery('ALTER TABLE codif_auteur DROP COLUMN isni')
+      ->silentQuery('ALTER TABLE codif_auteur DROP COLUMN ark')
+      ->silentQuery('ALTER TABLE codif_auteur DROP COLUMN youtube_channel_id');
+  }
+
+
+  public function datas() {
+    return
+      [
+       ['wikidata_id',  'varchar(255)'],
+       ['isni', 'varchar(255)'],
+       ['ark', 'varchar(255)'],
+       ['youtube_channel_id', 'varchar(255)']
+      ];
+  }
+
+
+  /**
+   * @test
+   * @dataProvider datas
+   */
+  public function fieldsShouldExists($field, $type) {
+    $this->assertFieldType('codif_auteur', $field, $type);
+  }
+
+
+  /** @test */
+  public function itemTypeShouldBeIndexed() {
+    $this->assertIndex('codif_auteur', 'wikidata_id');
+  }
+}
diff --git a/tests/fixtures/RedmineFixtures.php b/tests/fixtures/RedmineFixtures.php
index 5bab9f300c192019c13ba59da5569c81b8682bd0..4aac063424b4e3c9167b4b75ad4a0f4e9db3b9cf 100644
--- a/tests/fixtures/RedmineFixtures.php
+++ b/tests/fixtures/RedmineFixtures.php
@@ -87,7 +87,8 @@ class RedmineFixtures {
                           'story_points' => ''],
 
                          static::issue34247(),
-                         static::issue34248()],
+                         static::issue34248(),
+                         static::issue34249()],
             'total_count' => 2,
             'offset' => 0 ,
             'limit' => 25];
@@ -158,6 +159,38 @@ class RedmineFixtures {
   }
 
 
+  public static function issue34249() {
+    return ['id' => 34249,
+            'project' => ['id' => 56,
+                          'name' => 'Développement Bokeh AFI-OPAC 2.0'],
+            'tracker' => ['id' => 2,
+                          'name' => 'Développement'],
+            'status' => ['id' => 3,
+                         'name' => 'Réaliser à tester'],
+            'priority' => ['id' => 4,
+                           'name' => 'Normal'],
+            'author' => ['id' => 207,
+                         'name' => 'gloas'],
+            'assigned_to' => ['id' => 207,
+                              'name' => 'gloas'],
+            'subject' => 'Un problème à tester',
+            'description' => 'important',
+            'done_ratio' => 80,
+            'custom_fields' => [ ['id' => 37,
+                                  'name' => 'Module Portail',
+                                  'value' => ''],
+                                ['id' => 5 ,
+                                 'name' => 'Priorité client',
+                                 'value' => 'Normale'],
+                                ['id' => 11,
+                                 'name' => 'Phase',
+                                 'value' => '']],
+            'created_on' => '2015-12-04T09:19:11Z',
+            'updated_on' => '2015-12-04T09:25:42Z',
+            'story_points' => ''];
+  }
+
+
   public static function projectHotline() {
     return ['project' => ['name' => 'Hotline']];
   }
diff --git a/tests/library/Class/DecodageUnimarcTest.php b/tests/library/Class/DecodageUnimarcTest.php
index d306b3aa18bb6ce9635e7ab0e5df2d3140ee1434..5a474e0d5d716b2ec1d1b5a85de4627b8f3e3deb 100644
--- a/tests/library/Class/DecodageUnimarcTest.php
+++ b/tests/library/Class/DecodageUnimarcTest.php
@@ -246,6 +246,7 @@ class DecodageUnimarcEcosseTest extends PHPUnit_Framework_TestCase {
 
 class DecodageUnimarcConcertoAuteursTest extends PHPUnit_Framework_TestCase {
   public function setUp() {
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
     $this->concerto = new Class_Notice();
     $this->concerto->setUnimarc("00963njm0 2200265   450 001000600000010002000006101001300026200002400039200002600063200002700089200002800116200004400144210004000188215003300228608003800261608003800299610005900337610005900396686000600455700006500461700005300526702002900579702004200608702004700650265555  aHMU907286d15.7  afreafre1 aPiano concerto N  3� iIallegro ma non tanto� iII Intermezzo : adagio� iIII Finale : alla breve� iRhapsody on a theme of Paganini, op. 43  aArlesd 2001cHarmonia mundi France  a1 disque compacte1 brochure  amusique instrumentale orchestrale  amusique instrumentale orchestrale  aConcertos (piano) - Disques compactsxDisques compacts  aConcertos (piano) - Disques compactsxDisques compacts  a3 1aRACHMANINOVbSergueï Vassilievitch4Fonction indéterminée 1aRACHMANINOVbSergueï Vassilievitch4Compositeur 1aNAKAMATSUbJon4Musicien 1aSEAMANbChristopher4Chef d'orchestre 1aROCHESTER PHILHARMONIC ORCHESTRA4Musicien");
 
@@ -317,7 +318,7 @@ class DecodageUnimarcDVDUnHerosTresDiscretTest extends ModelTestCase {
 
   public function setUp() {
     parent::setUp();
-
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
     $this->fixture('Class_CodifLangue',
                    ['id' => 23,
                     'libelle' => '']);
diff --git a/tests/library/Class/Migration/BokehEightTest.php b/tests/library/Class/Migration/BokehEightTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8871cc1f0522c6220f25a347371dce44dd590bc9
--- /dev/null
+++ b/tests/library/Class/Migration/BokehEightTest.php
@@ -0,0 +1,168 @@
+<?php
+/**
+ * Copyright (c) 2012-2019, 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_Migration_BokehEightTest extends ModelTestCase {
+
+  /** @test */
+  public function bibnumShouldBecomeEnabled() {
+    Class_AdminVar::set('BIBNUM', 0);
+    (new Class_Migration_BokehEight)->run();
+    $this->assertTrue(Class_AdminVar::isBibNumEnabled());
+  }
+
+
+  /** @test */
+  public function bibnumDefaultShouldBeEnabled() {
+    $this->assertTrue(Class_AdminVar::isBibNumEnabled());
+  }
+
+
+  /** @test */
+  public function opdsShouldBecomeEnabled() {
+    Class_AdminVar::set('OPDS', 0);
+    (new Class_Migration_BokehEight)->run();
+    $this->assertTrue(Class_AdminVar::isOpdsEnabled());
+  }
+
+
+  /** @test */
+  public function opdsDefaultShouldBeEnabled() {
+    $this->assertTrue(Class_AdminVar::isOpdsEnabled());
+  }
+
+
+  /** @test */
+  public function codificationBrowserShouldBecomeEnabled() {
+    Class_AdminVar::set('CODIFICATION_BROWSER', 0);
+    (new Class_Migration_BokehEight)->run();
+    $this->assertTrue(Class_AdminVar::isCodificationBrowserEnabled());
+  }
+
+
+  /** @test */
+  public function codificationBrowserDefaultShouldBeEnabled() {
+    $this->assertTrue(Class_AdminVar::isCodificationBrowserEnabled());
+  }
+
+
+  /** @test */
+  public function inspectorGadgetMarcXMLShouldBecomeEnabled() {
+    Class_AdminVar::set('INSPECTOR_GADGET_MARC_XML', 0);
+    (new Class_Migration_BokehEight)->run();
+    $this->assertTrue(Class_AdminVar::isInspectorGadgetMarcXmlEnabled());
+  }
+
+
+  /** @test */
+  public function inspectorGadgetMarcXMLShouldBeEnabled() {
+    $this->assertTrue(Class_AdminVar::isInspectorGadgetMarcXmlEnabled());
+  }
+
+
+  /** @test */
+  public function customSearchFormShouldBecomeEnabled() {
+    Class_AdminVar::set('CUSTOM_SEARCH_FORM', 0);
+    (new Class_Migration_BokehEight)->run();
+    $this->assertTrue(Class_AdminVar::isCustomSearchFormEnabled());
+  }
+
+
+  /** @test */
+  public function customSearchFormShouldBeEnabled() {
+    $this->assertTrue(Class_AdminVar::isCustomSearchFormEnabled());
+  }
+
+
+  /** @test */
+  public function authorPageShouldBecomeEnabled() {
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
+    (new Class_Migration_BokehEight)->run();
+    $this->assertTrue(Class_AdminVar::isAuthorPageEnabled());
+  }
+
+
+  /** @test */
+  public function authorPageShouldBeEnabled() {
+    $this->assertTrue(Class_AdminVar::isAuthorPageEnabled());
+  }
+
+
+  /** @test */
+  public function withSingleKohaLoginThroughSigbOnlyShouldBecomeEnabled() {
+    Class_AdminVar::set('LOGIN_THROUGH_SIGB_ONLY', 0);
+    $this->fixture('Class_Intbib',
+                   ['id' => 4,
+                    'sigb' => Class_IntBib::SIGB_KOHA,
+                    'comm_params' => ['url_serveur' => 'https://service.local']]);
+    (new Class_Migration_BokehEight)->run();
+    $this->assertTrue(Class_AdminVar::isLoginThroughSigbOnlyEnabled());
+  }
+
+
+  /** @test */
+  public function withSingleNanookLoginThroughSigbOnlyShouldBecomeEnabled() {
+    Class_AdminVar::set('LOGIN_THROUGH_SIGB_ONLY', 0);
+    $this->fixture('Class_Intbib',
+                   ['id' => 4,
+                    'sigb' => Class_IntBib::SIGB_NANOOK,
+                    'comm_params' => ['url_serveur' => 'https://service.local']]);
+    (new Class_Migration_BokehEight)->run();
+    $this->assertTrue(Class_AdminVar::isLoginThroughSigbOnlyEnabled());
+  }
+
+
+  /** @test */
+  public function withOrpheeLoginThroughSigbOnlyShouldRemainDisabled() {
+    Class_AdminVar::set('LOGIN_THROUGH_SIGB_ONLY', 0);
+    $this->fixture('Class_Intbib',
+                   ['id' => 4,
+                    'sigb' => Class_IntBib::SIGB_ORPHEE,
+                    'comm_params' => ['url_serveur' => 'https://service.local']]);
+    (new Class_Migration_BokehEight)->run();
+    $this->assertFalse(Class_AdminVar::isLoginThroughSigbOnlyEnabled());
+  }
+
+
+  /** @test */
+  public function withBothNanookAndKohaLoginThroughSigbOnlyShouldRemainDisabled() {
+    Class_AdminVar::set('LOGIN_THROUGH_SIGB_ONLY', 0);
+    $this->fixture('Class_Intbib',
+                   ['id' => 4,
+                    'sigb' => Class_IntBib::SIGB_NANOOK,
+                    'comm_params' => ['url_serveur' => 'https://service-nanook.local']]);
+    $this->fixture('Class_Intbib',
+                   ['id' => 5,
+                    'sigb' => Class_IntBib::SIGB_KOHA,
+                    'comm_params' => ['url_serveur' => 'https://service-koha.local']]);
+
+    (new Class_Migration_BokehEight)->run();
+    $this->assertFalse(Class_AdminVar::isLoginThroughSigbOnlyEnabled());
+  }
+
+
+  /** @test */
+  public function variableDateMajAlbumsShouldHaveGroup6() {
+    Class_CosmoVar::setValueOf('date_maj_albums', '');
+    (new Class_Migration_BokehEight)->run();
+    $this->assertEquals(6, Class_CosmoVar::find('date_maj_albums')->getGroupe());
+  }
+}
diff --git a/tests/library/Class/NoticeTest.php b/tests/library/Class/NoticeTest.php
index b667eb73bb17cce90743dad4058fc68461291dd8..d103fa05efa9d05b68ea81efbb13565f6f39dab4 100644
--- a/tests/library/Class/NoticeTest.php
+++ b/tests/library/Class/NoticeTest.php
@@ -151,9 +151,11 @@ class NoticeVignetteTest extends ModelTestCase {
 
 
   /** @test */
-  public function serialWithThumbnailFetchUrlLocalVignetteShouldPassMainTitleAndCollectionKey() {
+  public function serialWithThumbnailFetchUrlLocalVignetteShouldCallServiceAndSetUrlVignette() {
+    $thumb_url = 'https//server.ext/img/thumb/diplo.jpg';
     $this->_http_client
       ->whenCalled('open_url')
+
       ->with('http://cache.org?'
              .'titre='.urlencode('Monde Diplo: mars 2017')
              .'&auteur=diplo'
@@ -163,26 +165,23 @@ class NoticeVignetteTest extends ModelTestCase {
              .'&src='.Class_WebService_AllServices::createSecurityKey()
              .'&api=2.0'
              .'&action=10')
-      ->answers(json_encode(['statut_recherche' => '0',
+
+      ->answers(json_encode(['statut_recherche' => '2',
                              'source' => 'Amazon',
-                             'vignette' => 'diplo.jpg',
-                             'image' => 'diplo.jpg']))
+                             'vignette' => $thumb_url,
+                             'image' => 'https//server.ext/img/big/diplo.jpg']))
       ->beStrict();
 
-    $this->assertEquals(Class_Url::assemble(['controller' => 'recherche',
-                                             'action' => 'raw-thumbnail',
-                                             'id' => 9]),
-                        $this->_serial_without_thumbnails->fetchUrlLocalVignette());
+    $this->assertEquals($thumb_url, $this->_serial_without_thumbnails->fetchUrlLocalVignette());
+    $this->assertEquals($thumb_url, $this->_serial_without_thumbnails->getUrlVignette());
   }
 
 
   /** @test */
-  public function withInexistingThumbnailFetchUrlVignetteShouldAnswerRawUrlThumbnail() {
+  public function withInexistingThumbnailFetchUrlVignetteShouldAnswerNo() {
     $this->assertFalse($this->_http_client->methodHasBeenCalled('open_url'));
 
-    $this->assertEquals(Class_Url::assemble(['controller' => 'recherche',
-                                             'action' => 'raw-thumbnail',
-                                             'id' => 2]),
+    $this->assertEquals('NO',
                         $this->_notice_inexisting_thumbnails->fetchUrlVignette());
   }
 
diff --git a/tests/library/Class/User/LostPassTest.php b/tests/library/Class/User/LostPassTest.php
index 7844721e2458757f810826a709797e50f2f568fb..3d000ea4d701c14e4b53cbc7c69377e77412ca3c 100644
--- a/tests/library/Class/User/LostPassTest.php
+++ b/tests/library/Class/User/LostPassTest.php
@@ -92,6 +92,15 @@ class Class_User_LostPassSendTest extends ModelTestCase {
   }
 
 
+  /** @test */
+  public function withNanookMailShouldContainsResetLink() {
+    $this->_sigb->setCommSigb(Class_IntBib::COM_NANOOK)->save();
+    (new Class_User_LostPass($this->_user))->send();
+    $mail = $this->_mail_transport->getSentMails()[0]->getBodyText()->getContent();
+    $this->assertContains('/auth/reset-password', $mail);
+  }
+
+
   /** @test */
   public function withPergameShouldNotsendMail() {
     $this->_sigb->setCommSigb(Class_IntBib::COM_PERGAME)->save();
diff --git a/tests/library/Class/WebService/FnacTest.php b/tests/library/Class/WebService/FnacTest.php
index 37b8ffbed8c7b9ca589566dfd735e3bd64b52320..60fd00648a41de9ca977f35c8277d374ce4b7f30 100644
--- a/tests/library/Class/WebService/FnacTest.php
+++ b/tests/library/Class/WebService/FnacTest.php
@@ -20,18 +20,32 @@
  */
 
 
-abstract class FnacTestCase extends PHPUnit_Framework_TestCase {
+abstract class FnacTestCase extends ModelTestCase {
   protected $_fnac;
   protected $_http_client;
 
   public function setUp() {
     $this->_fnac = new Class_WebService_Fnac();
 
-    $this->_http_client = Storm_Test_ObjectWrapper::mock();
+    $this->_http_client = $this->mock()->beStrict();
     Class_WebService_Fnac::setDefaultHttpClient($this->_http_client);
   }
 
 
+  protected function _openUrlWillAnswer($url, $answer) {
+    $this->_http_client
+      ->whenCalled('open_url')
+      ->with($url, ['headers' =>  $this->_fnac->httpHeaders()])
+      ->answers($answer);
+
+    return $this;
+  }
+
+  protected function _fixtureFileContent($file) {
+    return file_get_contents(__DIR__ . '/../../../fixtures/' . $file);
+  }
+
+
   public function tearDown() {
     Class_WebService_Fnac::setDefaultHttpClient(null);
     parent::tearDown();
@@ -42,23 +56,14 @@ abstract class FnacTestCase extends PHPUnit_Framework_TestCase {
 
 
 class FnacHarryPotterTest extends FnacTestCase {
-  public function setup() {
-    parent::setUp();
-
-    $this->_http_client
-      ->whenCalled('open_url')
-      ->with('http://recherche.fnac.com/r/2070572676')
-      ->answers(file_get_contents(realpath(dirname(__FILE__)). '/../../../fixtures/fnac_harry_potter_front.html'))
-
-      ->whenCalled('open_url')
-      ->with('http://livre.fnac.com/a1715839/Harry-Potter-T6-Harry-Potter-et-le-Prince-de-Sang-Mele-J-K-Rowling')
-      ->answers(file_get_contents(realpath(dirname(__FILE__)). '/../../../fixtures/fnac_harry_potter_suite.html'))
-      ->beStrict();
-  }
-
-
   /** @test */
   public function getResumeShouldFetchItFromPotterSuite() {
+    $this->_openUrlWillAnswer('http://recherche.fnac.com/r/2070572676',
+                              $this->_fixtureFileContent('fnac_harry_potter_front.html'))
+         ->_openUrlWillAnswer('http://livre.fnac.com/a1715839/Harry-Potter-T6-Harry-Potter-et-le-Prince-de-Sang-Mele-J-K-Rowling',
+                              $this->_fixtureFileContent('fnac_harry_potter_suite.html'))
+      ;
+
     $resume = $this->_fnac->getResume('2-07-057267-6');
     $this->assertContains('Harry, Ron et Hermione entrent', $resume);
     $this->assertContains('Le sens des responsabilités et du sacrifice, revêtent',  $resume);
@@ -69,28 +74,18 @@ class FnacHarryPotterTest extends FnacTestCase {
 
 
 class FnacMilleniumTest extends FnacTestCase {
-  public function setup() {
-    parent::setUp();
-
-    $this->_http_client
-      ->whenCalled('open_url')
-      ->with('http://recherche.fnac.com/r/9782742765010')
-      ->answers(file_get_contents(realpath(dirname(__FILE__)). '/../../../fixtures/fnac_millenium_front.html'))
-
-      ->whenCalled('open_url')
-      ->with('http://livre.fnac.com/a1891354/Millenium-Tome-2-La-fille-qui-revait-d-un-bidon-d-essence-et-d-une-allumette-Stieg-Larsson?NUMERICAL=Y#FORMAT=ePub')
-      ->answers(file_get_contents(realpath(dirname(__FILE__)). '/../../../fixtures/fnac_millenium_suite.html'))
-
-      ->whenCalled('open_url')
-      ->with('http://livre.fnac.com/a1891354/Millenium-Tome-2-La-fille-qui-revait-d-un-bidon-d-essence-et-d-une-allumette-Stieg-Larsson#ficheResume')
-      ->answers(file_get_contents(realpath(dirname(__FILE__)). '/../../../fixtures/fnac_millenium_suite.html'))
+  /** @test */
+  public function getResumeShouldFetchItFromMilleniumSuite() {
+    $this->_openUrlWillAnswer('http://recherche.fnac.com/r/9782742765010',
+                              $this->_fixtureFileContent('fnac_millenium_front.html'))
 
-      ->beStrict();
-  }
+         ->_openUrlWillAnswer('http://livre.fnac.com/a1891354/Millenium-Tome-2-La-fille-qui-revait-d-un-bidon-d-essence-et-d-une-allumette-Stieg-Larsson?NUMERICAL=Y#FORMAT=ePub',
+                              $this->_fixtureFileContent('fnac_millenium_suite.html'))
 
+         ->_openUrlWillAnswer('http://livre.fnac.com/a1891354/Millenium-Tome-2-La-fille-qui-revait-d-un-bidon-d-essence-et-d-une-allumette-Stieg-Larsson#ficheResume',
+                              $this->_fixtureFileContent('fnac_millenium_suite.html'))
+      ;
 
-  /** @test */
-  public function getResumeShouldFetchItFromMilleniumSuite() {
     $resume = $this->_fnac->getResume('978-2-7427-6501-0');
     $this->assertEquals('Tandis que Lisbeth Salander coule des journées supposées tranquilles aux Caraïbes',
                         $resume);
@@ -101,22 +96,10 @@ class FnacMilleniumTest extends FnacTestCase {
 
 
 class FnactNoLinkFoundTest extends FnacTestCase {
-  public function setup() {
-    parent::setUp();
-
-    $this->_http_client
-      ->whenCalled('open_url')
-      ->with('http://recherche.fnac.com/r/2070572676')
-      ->answers('bla bla bla')
-      ->beStrict();
-  }
-
-
   /** @test */
   public function getResumeShourdReturnEmptyString() {
+    $this->_openUrlWillAnswer('http://recherche.fnac.com/r/2070572676', 'bla bla bla');
     $resume = $this->_fnac->getResume('2-07-057267-6');
     $this->assertEmpty($resume);
   }
 }
-
-?>
\ No newline at end of file
diff --git a/tests/library/Class/WebService/SIGB/NanookTest.php b/tests/library/Class/WebService/SIGB/NanookTest.php
index be53739a3d0b0833d0495ef77a7145bb47594f8c..53716110fefc694feff85f2dbe62a20d482a9ca9 100644
--- a/tests/library/Class/WebService/SIGB/NanookTest.php
+++ b/tests/library/Class/WebService/SIGB/NanookTest.php
@@ -888,6 +888,35 @@ class NanookGetEmprunteurAuthenticateTest extends NanookTestCase {
     $this->assertEmpty($emprunteur->getPassword());
   }
 
+
+
+  /** @test */
+  public function withLoginThroughSigbOnlyAndAlreadyLoggedShouldNotAuthenticatePatron() {
+    $this->_mock_web_client
+      ->whenCalled('open_url')
+      ->with('http://localhost:8080/afi_Nanook/ilsdi/service/GetPatronInfo/patronId/999')
+      ->answers(NanookFixtures::xmlGetPatronError());
+
+    Class_AdminVar::set('LOGIN_THROUGH_SIGB_ONLY', '1');
+
+    $user = $this->fixture('Class_Users',
+                   ['id' => 98734,
+                    'id_site' => 3,
+                    'idabon' => 'A-00001',
+                    'id_sigb' => '999',
+                    'login' => '90175000410218',
+                    'password' => '',
+                    'role_level' => ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB,
+                   ]);
+
+    ZendAfi_Auth::getInstance()->logUser($user);
+
+    $emprunteur = $this->_service->getEmprunteur($user);
+
+    $this->assertTrue($this->_mock_web_client
+                      ->methodHasBeenCalledWithParams('open_url',
+                                                      ['http://localhost:8080/afi_Nanook/ilsdi/service/GetPatronInfo/patronId/999']));
+  }
 }
 
 
diff --git a/tests/library/ZendAfi/View/Helper/Admin/MenuGaucheAdminTest.php b/tests/library/ZendAfi/View/Helper/Admin/MenuGaucheAdminTest.php
index b81b0e4aff3b49b100e7589b2dd060824957a7c4..64c32e9924bbfe346f4bc2191d0c820a1425a602 100644
--- a/tests/library/ZendAfi/View/Helper/Admin/MenuGaucheAdminTest.php
+++ b/tests/library/ZendAfi/View/Helper/Admin/MenuGaucheAdminTest.php
@@ -35,6 +35,8 @@ abstract class ZendAfi_View_Helper_Admin_MenuGaucheAdminTestCase extends ViewHel
     $this->helper->setView($this->view);
 
     ZendAfi_View_Helper_Admin_MenuGaucheAdminItem::setAcl(null);
+    Class_AdminVar::set('OPDS', 1);
+    Class_AdminVar::set('BIBNUM', 1);
 
     $this->html = $this->helper->Admin_ContentNav();
   }
@@ -60,7 +62,6 @@ class ZendAfi_View_Helper_Admin_MenuGaucheAdminVariableAsAdminTest
 
   public function _prepareFixtures() {
     parent::_prepareFixtures();
-
     $this->login(ZendAfi_Acl_AdminControllerRoles::ADMIN_PORTAIL);
     RessourcesNumeriquesFixtures::activateOrphea();
     RessourcesNumeriquesFixtures::deactivateDilicom();
@@ -354,10 +355,20 @@ class ZendAfi_View_Helper_Admin_MenuGaucheAdminRightsTest extends ViewHelperTest
     $this->helper = new ZendAfi_View_Helper_Admin_ContentNav();
     $this->helper->setView($this->view);
     ZendAfi_View_Helper_Admin_MenuGaucheAdminItem::setAcl(null);
+
     Class_AdminVar::set('ACTIVITY', 1);
     Class_AdminVar::set('BIBNUM', 1);
     Class_AdminVar::set('SITO_IN_ALBUMS', 1);
     Class_AdminVar::set('CODIFICATION_BROWSER', 1);
+    Class_AdminVar::set('OAI_REPOSITORY', 1);
+    Class_AdminVar::set('CUSTOM_GENRE_ICON', 1);
+    Class_AdminVar::set('WEBSERVICE_TEST', 1);
+    Class_AdminVar::set('IMPORT_AVIS_OPAC2', 1);
+    Class_AdminVar::set('GENERATION_SITE', 1);
+    Class_AdminVar::set('CUSTOM_FIELDS_REPORT', 1);
+    Class_AdminVar::set('IMPORT_THESAURUS', 1);
+    Class_AdminVar::set('IMPORT_EAD', 1);
+
   }
 
 
diff --git a/tests/library/ZendAfi/View/Helper/AuthorAnchorTest.php b/tests/library/ZendAfi/View/Helper/AuthorAnchorTest.php
index 96a803057163cf4a11a7c82c1040e83fb304f416..e2efa037f6f55e09a195ef27561e8c4dfd723046 100644
--- a/tests/library/ZendAfi/View/Helper/AuthorAnchorTest.php
+++ b/tests/library/ZendAfi/View/Helper/AuthorAnchorTest.php
@@ -27,6 +27,8 @@ class ZendAfi_View_Helper_AnchorAuthorSimpleTest extends ViewHelperTestCase {
   public function setup() {
     parent::setUp();
 
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
+
     $brisson = $this->fixture('Class_CodifAuteur',
                               ['id' => 10,
                                'libelle' => 'Stéphanie BRISSON',
diff --git a/tests/library/ZendAfi/View/Helper/ListeNotices/MurTest.php b/tests/library/ZendAfi/View/Helper/ListeNotices/MurTest.php
index f8fb8e50367d5d041d45ad138b6e10433521552a..abed31518e03f501b5fa6bfa6a2bef8deb372010 100644
--- a/tests/library/ZendAfi/View/Helper/ListeNotices/MurTest.php
+++ b/tests/library/ZendAfi/View/Helper/ListeNotices/MurTest.php
@@ -22,6 +22,7 @@
 
 abstract class ZendAfi_View_Helper_ListeNotices_MurTestCase extends ViewHelperTestCase {
   protected $_html;
+  protected $_storm_default_to_volatile = true;
 
   public function setUp() {
     parent::setUp();
diff --git a/tests/library/ZendAfi/View/Helper/Notice/VignetteTest.php b/tests/library/ZendAfi/View/Helper/Notice/VignetteTest.php
index 2b09e102d5e6a99087e15dc0cdf67f806ea15228..add9ab700f150d6eed65206e706b606fa42b01c7 100644
--- a/tests/library/ZendAfi/View/Helper/Notice/VignetteTest.php
+++ b/tests/library/ZendAfi/View/Helper/Notice/VignetteTest.php
@@ -70,7 +70,7 @@ class ZendAfi_View_Helper_Notice_VignetteNoThumbnailTest
 
 
   /** @test */
-  public function withThumbnailZoneAndUrlInUnimarcResultShouldContainsThumbnail() {
+  public function withThumbnailZoneAndUrlInUnimarcResultAndNoDataShouldTryToRegenerateThumbnailInAjax() {
     $record = $this->fixture('Class_Notice',
                              ['id' => 42,
                               'type_doc' => 1,
@@ -84,6 +84,6 @@ class ZendAfi_View_Helper_Notice_VignetteNoThumbnailTest
       ->setModulePreference('recherche', 'viewnotice1', 'thumbnail_fields', '859-u');
 
     $html = $this->_helper->notice_Vignette($record);
-    $this->assertXPath($html, '//img[@src="https://epicpics.org/7bxDlg.png"]', $html);
+    $this->assertXPath($html, '//img[contains(@src, "/recherche/vignette/clef/")]', $html);
   }
 }
diff --git a/tests/library/ZendAfi/View/Helper/SuggestsTest.php b/tests/library/ZendAfi/View/Helper/SuggestsTest.php
index a886184925ac2850c3483dde8beba3ebfecc3a8d..8a953d75bda8642eb59dea18406ed82a3cc56edf 100644
--- a/tests/library/ZendAfi/View/Helper/SuggestsTest.php
+++ b/tests/library/ZendAfi/View/Helper/SuggestsTest.php
@@ -29,6 +29,8 @@ class ZendAfi_View_Helper_SuggestsTest extends ViewHelperTestCase {
   public function setUp() {
     parent::setUp();
 
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
+
     $this->fixture('Class_CodifAuteur',
                    ['id' => 43,
                     'libelle' => 'Pomme d\'API']);
@@ -112,5 +114,3 @@ class ZendAfi_View_Helper_SuggestsTest extends ViewHelperTestCase {
 
   }
 }
-
-?>
\ No newline at end of file
diff --git a/tests/library/ZendAfi/View/Helper/Telephone/KiosqueTest.php b/tests/library/ZendAfi/View/Helper/Telephone/KiosqueTest.php
index 2e24aaf3fc2468690fdb790307f7d0335a2a9c07..380853187c5919302870658400e73a7ba8bc87a1 100644
--- a/tests/library/ZendAfi/View/Helper/Telephone/KiosqueTest.php
+++ b/tests/library/ZendAfi/View/Helper/Telephone/KiosqueTest.php
@@ -33,8 +33,7 @@ class Telephone_KiosqueTest extends ViewHelperTestCase {
       ->answers(10)
       ->whenCalled('getTitrePrincipal')
       ->answers('Programmers <br> Hell\'s "code"')
-      ->whenCalled('fetchUrlVignette')
-      ->with(true)
+      ->whenCalled('fetchUrlLocalVignette')
       ->answers('/img/hell_code.png');
 
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Catalogue')
diff --git a/tests/scenarios/AdvancedSearch/AdminConfigurationTest.php b/tests/scenarios/AdvancedSearch/AdminConfigurationTest.php
index ae828b91d92eca5e8f82443b34d8dc4512028071..252cc063c5de409862d6ecbdfc52a8b6db5179fc 100644
--- a/tests/scenarios/AdvancedSearch/AdminConfigurationTest.php
+++ b/tests/scenarios/AdvancedSearch/AdminConfigurationTest.php
@@ -20,7 +20,7 @@
  */
 
 
-class AdvancedSearch_AdminConfigurationUnactiveTest extends   Admin_AbstractControllerTestCase {
+class AdvancedSearch_AdminConfigurationUnactiveTest extends Admin_AbstractControllerTestCase {
   protected $_storm_default_to_volatile = true;
 
   public function setUp() {
@@ -29,12 +29,6 @@ class AdvancedSearch_AdminConfigurationUnactiveTest extends   Admin_AbstractCont
   }
 
 
-  public function tearDown() {
-    ZendAfi_View_Helper_Admin_MenuGaucheAdminItem::setAcl(null);
-    parent::tearDown();
-  }
-
-
   /** @test */
   public function menuAdvancedFormShouldBeDisplayed() {
     $this->dispatch('/admin');
@@ -64,12 +58,6 @@ abstract class AdvancedSearch_ActiveTestCase extends Admin_AbstractControllerTes
                     'visible' => true
                    ]);
   }
-
-
-  public function tearDown() {
-    ZendAfi_View_Helper_Admin_MenuGaucheAdminItem::setAcl(null);
-    parent::tearDown();
-  }
 }
 
 
diff --git a/tests/scenarios/AlbumAudioRecord/AlbumAudioRecordTest.php b/tests/scenarios/AlbumAudioRecord/AlbumAudioRecordTest.php
index 62346993881234d840702db0c697f62f27aecc22..ba38b9c6ebc4387685f3bfe91f7a1624ebe08197 100644
--- a/tests/scenarios/AlbumAudioRecord/AlbumAudioRecordTest.php
+++ b/tests/scenarios/AlbumAudioRecord/AlbumAudioRecordTest.php
@@ -117,6 +117,8 @@ class AlbumAudioRecordViewNoticeTest extends AlbumAudioRecordTestCase {
   public function setUp() {
     parent::setUp();
 
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
+
     $this->mock_sql = $this->mock()
                            ->whenCalled('execute')->answers('')
                            ->whenCalled('fetchOne')->answers(null)
@@ -295,6 +297,7 @@ class AlbumAudioRecordViewMorceauxTest extends AlbumAudioRecordTestCase {
 class AlbumAudioRecordViewDetailsTest extends AlbumAudioRecordTestCase {
   public function setUp() {
     parent::setUp();
+    Class_AdminVar::set('AUTHOR_PAGE', 0);
     $this->dispatch('/opac/noticeajax/detail/id_notice/'.$this->_notice->getId(), true);
   }
 
diff --git a/tests/scenarios/AuthorPage/AuthorPageTest.php b/tests/scenarios/AuthorPage/AuthorPageTest.php
index 6d8582fc45f60e8a54f7fda653294144be8f714a..55e3a0b10df46f7a916700b124dff9ab66c738e7 100644
--- a/tests/scenarios/AuthorPage/AuthorPageTest.php
+++ b/tests/scenarios/AuthorPage/AuthorPageTest.php
@@ -35,7 +35,7 @@ abstract class AuthorPageTestCase extends AbstractControllerTestCase {
                     'type_doc' => Class_TypeDoc::LIVRE,
                     'clef_alpha'=>'GAVROCHE_HUGO',
                     'titre_principal' => 'Gavroche',
-                    'facettes' => 'A2408 A3 G4 M6',
+                    'facettes' => 'A2408 A3 A666 G4 M6',
                     'url_vignette' => 'gavroche.jpg']);
 
     $this->fixture('Class_Notice',
@@ -79,7 +79,7 @@ abstract class AuthorPageTestCase extends AbstractControllerTestCase {
     $this->mock_sql = $this->mock()
                            ->whenCalled('fetchAll')
                            ->with("select id_notice, facettes from notices Where (MATCH(facettes) AGAINST('+A2408' IN BOOLEAN MODE)) and type=1", true, false)
-                           ->answers([[8, 'A2408 A3 G4 M6'],
+                           ->answers([[8, 'A2408 A666 A3 G4 M6'],
                                       [9, 'A2408 M6 A4'],
                                       [10, 'A2408 A3']]);
     Zend_Registry::set('sql', $this->mock_sql);
@@ -98,7 +98,11 @@ abstract class AuthorPageTestCase extends AbstractControllerTestCase {
                              'source' => 'Wikipedia',
                              'biographie' => '1980 : <i>Les Misérables</i> (adaptation d\'Alain Boublil et Claude-Michel Schönberg) pour Robert Hossein, est devenue ....avec Marcus %s \Mill bizarre',
                              'vignette' => 'http://images.fr/hugo_thumb.jpg',
-                             'image' => 'http://images.fr/hugo_big.jpg']))
+                             'image' => 'http://images.fr/hugo_big.jpg',
+                             'wikidata_id' => 'Q8298',
+                             'youtube_channel_id' => 'UC1l7wYrva1qCH-wgqcHaaRg',
+                             'isni' => '0000 0000 7839 1751',
+                             'ark' => 'ark:/12148/cb13971020f']))
       ->beStrict();
   }
 }
@@ -140,11 +144,58 @@ class AuthorPageViewIncompleteAuthor extends AuthorPageTestCase {
 
 
 
+class AuthorPageViewAuthorWithoutRecordTest extends AuthorPageTestCase {
+  /** @test */
+  public function pageShouldHaveH1WithSuperCanard() {
+    $this->fixture('Class_CodifAuteur',
+                   ['id' => 666,
+                    'libelle' => 'Super canard']);
+
+    $this->mock_sql->whenCalled('fetchAll')
+                   ->with('select id_notice, facettes from notices Where (MATCH(facettes) AGAINST(\'+A666\' IN BOOLEAN MODE)) and type=1', true, false)
+                   ->answers([]);
+
+    $this->dispatch('/author/view/id/666');
+    $this->assertXPathContentContains('//h1', 'Super canard');
+  }
+}
+
+
+
+
 class AuthorPageViewByIdTest extends AuthorPageTestCase {
   public function setUp() {
     parent::setUp();
 
-    $this->dispatch('/author/view/id/2408');
+    $this->dispatch('/author/view/id/2408/inspector_gadget/1');
+  }
+
+
+  /** @test */
+  public function pageShouldContainsInspectorGadgetButton() {
+    $this->assertXPathContentContains('//button[contains(@class,"admin-button")]', 'Auteur Bokeh');
+  }
+
+
+  /** @test */
+  public function authorWikdataIdShouldBeQ8298() {
+    $this->assertEquals('Q8298', Class_CodifAuteur::find(2408)->getWikidataId());
+  }
+
+
+  /** @test */
+  public function authorYoutubeChannelIdShouldBeUCxxx() {
+    $this->assertEquals('UC1l7wYrva1qCH-wgqcHaaRg', Class_CodifAuteur::find(2408)->getYoutubeChannelId());
+  }
+
+    /** @test */
+  public function authorISNIShouldBe0000000078391751() {
+    $this->assertEquals('0000 0000 7839 1751', Class_CodifAuteur::find(2408)->getIsni());
+  }
+
+    /** @test */
+  public function authorARKShouldBecb13971020f() {
+    $this->assertEquals('ark:/12148/cb13971020f', Class_CodifAuteur::find(2408)->getArk());
   }
 
 
@@ -273,13 +324,14 @@ class AuthorPageViewByIdTest extends AuthorPageTestCase {
 
   /** @test */
   public function pageShouldLinkGavrocheWithCodeRebondAndFacet() {
-    $this->assertXPath('//div[@class="records"]//div[@class="simple_wall"]/a[contains(@href, "/recherche/viewnotice/id/8/clef/GAVROCHE_HUGO/code_rebond/A2408/facette/T1")]');
+    $this->assertXPath('//div[@class="records"]//div[@class="simple_wall"]/a[contains(@href, "/recherche/viewnotice/id/8/inspector_gadget/1/clef/GAVROCHE_HUGO/code_rebond/A2408/facette/T1")]',
+                       $this->_response->getBody());
   }
 
 
   /** @test */
   public function pageShouldLinkLesMiserablesWithCodeRebondAndFacet() {
-    $this->assertXPath('//div[@class="records"]//div[@class="simple_wall"]/a[contains(@href, "/recherche/viewnotice/id/9/clef/MISERABLES_HUGO/code_rebond/A2408/facette/T1")]');
+    $this->assertXPath('//div[@class="records"]//div[@class="simple_wall"]/a[contains(@href, "/recherche/viewnotice/id/9/inspector_gadget/1/clef/MISERABLES_HUGO/code_rebond/A2408/facette/T1")]');
   }
 }
 
@@ -323,10 +375,10 @@ class AuthorPagePhoneTest extends AuthorPageTestCase {
 class AuthorPageViewByNameTest extends AuthorPageTestCase {
   /** @test */
   public function searchVictorHugoShouldShowVictorHugo() {
-    Storm_Test_ObjectWrapper::onLoaderOfModel('Class_CodifAuteur')
-      ->whenCalled('findWithFullName')
-      ->with('Victor Hugo')
-      ->answers(Class_CodifAuteur::find(2408));
+    $this->onLoaderOfModel('Class_CodifAuteur')
+         ->whenCalled('findWithFullName')
+         ->with('Victor Hugo')
+         ->answers(Class_CodifAuteur::find(2408));
 
     $this->dispatch('/author/view/named/Victor+Hugo');
 
@@ -336,7 +388,7 @@ class AuthorPageViewByNameTest extends AuthorPageTestCase {
 
   /** @test */
   public function searchUnknownShouldDisplay404() {
-    Storm_Test_ObjectWrapper::onLoaderOfModel('Class_CodifAuteur')
+    $this->onLoaderOfModel('Class_CodifAuteur')
         ->whenCalled('findWithFullName')
         ->answers(null);
 
@@ -428,4 +480,52 @@ class AuthorPageViewRecordWithoutAuthorTest extends AuthorPageTestCase {
   public function shouldNotBeError() {
     $this->assertXPathContentContains('//h1', 'Les contacteurs');
   }
+}
+
+
+
+
+class AuteurPageNoticeAjaxRenderYoutubeChannelTest extends AuthorPageTestCase {
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+
+    Class_CodifAuteur::find(2408)
+      ->setYoutubeChannelId('UC1l7wYrva1qCH-wgqcHaaRg')
+      ->assertSave();
+
+    Class_WebService_Youtube::setHttpClient($this->mock()
+                                            ->whenCalled('open_url')
+                                            ->with('https://www.youtube.com/feeds/videos.xml?channel_id=UC1l7wYrva1qCH-wgqcHaaRg')
+                                            ->answers(file_get_contents(__DIR__ . '/guetta_channel.xml')));
+
+    $this->dispatch('/author/render-youtube-channel/id/2408');
+  }
+
+
+  public function tearDown() {
+    Class_WebService_Youtube::setHttpClient(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function playlist441EmbedShouldBePresent() {
+    $this->assertXPath('//iframe[@src="https://www.youtube.com/embed/KrczknSz1rk"]');
+  }
+
+
+  /** @test */
+  public function numberOfIframesShouldBeThree() {
+    $this->assertXPathCount('//iframe', 3);
+
+  }
+
+
+  /** @test */
+  public function playlist441TitleShouldBePresent() {
+    $this->assertXPathContentContains('//h3', 'David Guetta Playlist 441');
+  }
 }
\ No newline at end of file
diff --git a/tests/scenarios/AuthorPage/guetta_channel.xml b/tests/scenarios/AuthorPage/guetta_channel.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c6bfa977612035a28fb30ee80a22ccc0caa309ef
--- /dev/null
+++ b/tests/scenarios/AuthorPage/guetta_channel.xml
@@ -0,0 +1,722 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom">
+  <link rel="self" href="http://www.youtube.com/feeds/videos.xml?channel_id=UC1l7wYrva1qCH-wgqcHaaRg"/>
+  <id>yt:channel:UC1l7wYrva1qCH-wgqcHaaRg</id>
+  <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+  <title>David Guetta</title>
+  <link rel="alternate" href="https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg"/>
+  <author>
+    <name>David Guetta</name>
+    <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+  </author>
+  <published>2009-12-11T07:02:52+00:00</published>
+  <entry>
+    <id>yt:video:KrczknSz1rk</id>
+    <yt:videoId>KrczknSz1rk</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta Playlist 441</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=KrczknSz1rk"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2019-01-25T17:58:11+00:00</published>
+    <updated>2019-01-30T14:04:24+00:00</updated>
+    <media:group>
+      <media:title>David Guetta Playlist 441</media:title>
+      <media:content url="https://www.youtube.com/v/KrczknSz1rk?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i4.ytimg.com/vi/KrczknSz1rk/hqdefault.jpg" width="480" height="360"/>
+      <media:description>#DavidGuettaPlaylist 441
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      BUY THE NEW #DAVIDGUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      David Guetta Playlist #441 Tracklisting:
+      01. Jack Back - Grenade
+      02. Sevenn - Lollipop (Extended Mix)
+      03. Exit 11 feat. Danny Howard - Masakali (Extended mix)
+      04. Elderbrook - Old Friend
+      05. FreeFall - Dance
+      06. Movement Machina - Nakatomi (Original Mix)
+      07. CamelPhat X Cristoph - Breathe
+      08. ID - Tell Me You Need Me (Extended Mix)
+      09. Leandro Da Silva - Gopher Mambo (Extended Mix)
+      10. Damien N-Drix - Hush Money
+      11. Raiden &amp; Tom Tyger - Electric (Extended Mix)
+      12. Matt Nash - Frequency
+      13. Toby Green - Party People (Extended Mix)
+      14. Castion &amp; Dylerz feat. NEAD - Take Me (Extended Mix)
+      15. deadmau5 - Drama Free ft. Lights (Chris Lorenzo Remix)
+      16. ESH - The Jungle
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="3082" average="4.85" min="1" max="5"/>
+	<media:statistics views="64784"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:-K0JE7YW7kc</id>
+    <yt:videoId>-K0JE7YW7kc</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta Playlist 440</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=-K0JE7YW7kc"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2019-01-18T17:58:46+00:00</published>
+    <updated>2019-01-30T13:11:53+00:00</updated>
+    <media:group>
+      <media:title>David Guetta Playlist 440</media:title>
+      <media:content url="https://www.youtube.com/v/-K0JE7YW7kc?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i2.ytimg.com/vi/-K0JE7YW7kc/hqdefault.jpg" width="480" height="360"/>
+      <media:description>#DavidGuettaPlaylist 440
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      BUY THE NEW #DAVIDGUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      David Guetta Playlist #440 Tracklisting:
+      01. Gemellini - Smash The Disco (Extended Mix)
+      02. Axwell - Nobody Else (Extended Mix)
+      03. Jordan Jay - Never Know
+      04. John Dahlbäck - Voyager (Extended Mix)
+      05. Federico Scavo - Blow It (Federico Scavo Remix 2019)
+      06. Cedric Gervais - Do It Tonight (Tom Staar Extended Remix)
+      07. DJ S.K.T - Lipstick &amp; Perfume (Extended)
+      08. Jack Back - Grenade
+      09. Wheats - U N I (Original Mix)
+      10. Sander Kleinenberg - London Girl (Extended Mix)
+      11. PBH &amp; Jack Shizzle - Zion
+      12. Tobi Kramer - Once Upon A Time In NYC
+      13. Mark Knight &amp; Danny Howard - You Can Do It Baby (Original Mix)
+      14. Weiss &amp; Eli Brown - Push It Up
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="3127" average="4.83" min="1" max="5"/>
+	<media:statistics views="96495"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:bn8conNCqPc</id>
+    <yt:videoId>bn8conNCqPc</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta Playlist 439</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=bn8conNCqPc"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2019-01-11T17:57:46+00:00</published>
+    <updated>2019-01-30T13:04:01+00:00</updated>
+    <media:group>
+      <media:title>David Guetta Playlist 439</media:title>
+      <media:content url="https://www.youtube.com/v/bn8conNCqPc?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i3.ytimg.com/vi/bn8conNCqPc/hqdefault.jpg" width="480" height="360"/>
+      <media:description>#DavidGuettaPlaylist 439
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      BUY THE NEW #DAVIDGUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      David Guetta Playlist #439 Tracklisting:
+      01. Black Coffee &amp; David Guetta ft Delilah Montagu - Drive (Solardo Club Mix)
+      02. Huxley - 5 Dollars (Original Mix)
+      03. Kyle Kinch - Manchester (Original Mix)
+      04. Merk &amp; Kremont - Sushi
+      05. Matroda - Mr.Donk
+      06. Jack Beats &amp; DJ Zinc ft. MC GQ - Raise it Up
+      07. GODAMN - Trouble
+      08. Jack Back - Think Think Think
+      09. Daddy's Groove - Bvulgari (Club Mix)
+      10. Robin Aristo - Pump The Bomb (Extended Mix)
+      11. Pessto - Digiboom (Extended Mix)
+      12. Marc Benjamin - Blaster
+      13. Kryder &amp; HIIO - La Luna (Styline Extended Remix)
+      14. Firebeatz &amp; Yozo - Rock To The Rhythm (Extended Mix)
+      15. NOTD - Been There Done That (Toby Green Extended Remix)
+      16. The Chainsmokers - This Feeling (Tom Staar Remix)
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="4101" average="4.88" min="1" max="5"/>
+	<media:statistics views="113260"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:tBu02hHfmwE</id>
+    <yt:videoId>tBu02hHfmwE</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta Playlist 438</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=tBu02hHfmwE"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2019-01-04T17:58:17+00:00</published>
+    <updated>2019-01-30T10:11:11+00:00</updated>
+    <media:group>
+      <media:title>David Guetta Playlist 438</media:title>
+      <media:content url="https://www.youtube.com/v/tBu02hHfmwE?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i1.ytimg.com/vi/tBu02hHfmwE/hqdefault.jpg" width="480" height="360"/>
+      <media:description>#DavidGuettaPlaylist 438
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      BUY THE NEW #DAVIDGUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      David Guetta Playlist #438 Tracklisting:
+      Part 1
+      01. David Guetta - She Knows How To Love Me (feat. Jess Glynne &amp; Stefflon Don)
+      02. David Guetta, Bebe Rexha &amp; J Balvin - Say My Name
+      03. Flash 89 - Tattle Tail
+      04. Just Kiddin - Hysteria (VIP Mix)
+      05. Reblok - Your Face
+      06. Fraanklyn - Bubbles (Extended Mix)
+      07. Shiba San - Crush That (Original Mix)
+      Part 2
+      08. Tim Baresko - Obsession (Original Mix)
+      09. M-22 - How Does It Feel (Weiss Dub Mix)
+      10. Siwell - On The Fly (Extended Mix)
+      11. Sunnery James &amp; Ryan Marciano vs Marc Volt - In My Mind (Extended Mix)
+      12. MOTi &amp; Vigiland - Mad Love [One Kiss Mashup]
+      13. A-Trak &amp; Baauer - Dumbo Drop (Gammer Remix)
+      14. Queen - Bohemian rhapsody (Digitalz Remix)
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="4488" average="4.88" min="1" max="5"/>
+	<media:statistics views="106333"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:UFUXdov7WkE</id>
+    <yt:videoId>UFUXdov7WkE</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta - Megamashup by Djs From Mars</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=UFUXdov7WkE"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-31T16:58:45+00:00</published>
+    <updated>2019-01-30T14:17:15+00:00</updated>
+    <media:group>
+      <media:title>David Guetta - Megamashup by Djs From Mars</media:title>
+      <media:content url="https://www.youtube.com/v/UFUXdov7WkE?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i2.ytimg.com/vi/UFUXdov7WkE/hqdefault.jpg" width="480" height="360"/>
+      <media:description>BUY THE NEW DAVID GUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      #DavidGuetta  #DjsFromMars #mashup
+
+      Mashup by Djs From Mars
+      Video editing: Damien Saintobert
+      (P) &amp; (C) 2018 What A Music Ltd, Under Exclusive Licence to Parlophone/Warner Music France, a Warner Music Group Company
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="45981" average="4.95" min="1" max="5"/>
+	<media:statistics views="975765"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:hIAvrG2P4FY</id>
+    <yt:videoId>hIAvrG2P4FY</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta Playlist 437</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=hIAvrG2P4FY"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-28T17:58:43+00:00</published>
+    <updated>2019-01-30T11:47:38+00:00</updated>
+    <media:group>
+      <media:title>David Guetta Playlist 437</media:title>
+      <media:content url="https://www.youtube.com/v/hIAvrG2P4FY?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i1.ytimg.com/vi/hIAvrG2P4FY/hqdefault.jpg" width="480" height="360"/>
+      <media:description>#DavidGuettaPlaylist 437
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      BUY THE NEW #DAVIDGUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      David Guetta Playlist #437 Tracklisting:
+      Part 1
+      01. David Guetta - Im That Bitch (feat. Saweetie)
+      02. Calvin Harris &amp; Sam Smith - Promises (David Guetta Extended Remix)
+      03. Tungevaag &amp; Raaban - Hey Baby (Extended Mix)
+      04. ATICA - Country Boy
+      05. Dennis Cartier - Underground Melody (Extended Mix)
+      06. Skrillex, JOYRYDE - AGEN WIDA (Original Mix)
+      07. R-Wan - Secret (Original Mix)
+      08. Maddix feat. Kris Kiss - Shuttin It Down (Extended Mix)
+      Part 2
+      09. Jack Back - What 2 Say
+      10. Silk City &amp; Dua Lipa - Electricity (Ten Ven Remix)
+      11. Maur &amp; Roobinz - Gospel Man
+      12. Mark Knight &amp; Danny Howard - Playing With My Heart (Original Mix)
+      13. Ten Ven - Just About (Kideko Remix)
+      14. Florian Picasso - Glitch (Extended Mix)
+      15.Tim van Werd - Message From The Whales (Extended Mix)
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="3952" average="4.86" min="1" max="5"/>
+	<media:statistics views="100927"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:KSiX5-_5OaM</id>
+    <yt:videoId>KSiX5-_5OaM</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta, Bebe Rexha &amp; J Balvin - Say My Name (Lucas &amp; Steve remix)</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=KSiX5-_5OaM"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-28T07:00:00+00:00</published>
+    <updated>2019-01-30T13:24:09+00:00</updated>
+    <media:group>
+      <media:title>David Guetta, Bebe Rexha &amp; J Balvin - Say My Name (Lucas &amp; Steve remix)</media:title>
+      <media:content url="https://www.youtube.com/v/KSiX5-_5OaM?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i4.ytimg.com/vi/KSiX5-_5OaM/hqdefault.jpg" width="480" height="360"/>
+      <media:description>BUY/LISTEN: https://guetta.co/saymynamelsremixAY
+
+      BUY THE NEW DAVID GUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      #DavidGuetta #BebeRexha #JBalvin #SayMyName #LucasAndSteve
+
+      David Guetta, Bebe Rexha &amp; J Balvin – Say My Name
+      (David Guetta, Giorgio Tuinfort, Boaz van de Beatz, Jose Balvin, Alejandro Ramirez, Thomas Troelsen, Emily Warren, Britt Burton, Phil Leigh, Matt Holmes)
+      Produced by David Guetta, Giorgio Tuinfort &amp; Boaz van de Beatz
+      All Instruments and Programming by David Guetta, Giorgio Tuinfort &amp; Boaz van de Beatz
+      Piano by Giorgio Tuinfort
+      Additional Keyboards by Matthew Holmes &amp; Philip Leigh
+      Bass by Quincell Adolphin
+      Recorded by Boaz van de Beatz at Poundcake Studios Rotterdam, Netherlands
+      Vocals produced by Mitch Allan
+      Mixed and mastered by Daddy’s Groove at TRIBE Studios Naples, Italy
+      Publishers: JackBack Publishing Ltd, admin by Write Here Music (SACEM) and Shapiro Bernstein (ASCAP); NEW CLASSIC (BUMA); From The Beatz Publishing (BMGTalpa) / AT Publishing; Universal Music Publishing (BMI); Warner music publishing; EMI Music Publishing Denmark; Havenwood House / Prescription Songs (ASCAP); WB Music Corp/BMB Top Songs (ASCAP); Hotel Cabana / Sony ATV (PRS)
+      J Balvin appears courtesy of Universal Music Latino
+      Bebe Rexha appears courtesy of Warner Bros
+      (P) &amp; (C) 2018 What A Music Ltd, Under Exclusive Licence to Parlophone/Warner Music France, a Warner Music Group Company
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="9920" average="4.85" min="1" max="5"/>
+	<media:statistics views="236918"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:627UW2cUrzg</id>
+    <yt:videoId>627UW2cUrzg</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta Playlist 436</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=627UW2cUrzg"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-21T17:58:17+00:00</published>
+    <updated>2019-01-29T13:43:53+00:00</updated>
+    <media:group>
+      <media:title>David Guetta Playlist 436</media:title>
+      <media:content url="https://www.youtube.com/v/627UW2cUrzg?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i3.ytimg.com/vi/627UW2cUrzg/hqdefault.jpg" width="480" height="360"/>
+      <media:description>#DavidGuettaPlaylist 436
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      BUY THE NEW #DAVIDGUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      David Guetta Playlist #436 Tracklisting:
+      Part 1
+      01. David Guetta Bebe Rexha &amp; J. Balvin - Say My Name
+      02. DJ Snake - Taki Taki
+      03. Biray - Baby Boy (Extended Mix)
+      04. Bingo Players &amp; Bali Bandits - Body Rock
+      05. Exodus &amp; Kom feat Jonny Rose - Not Alone
+      06. Nicky Romero &amp; Deniz Koyu ft. Walk off the Earth - Paradise (Deniz Koyu Festival Extended Mix)
+      07. Oliver Heldens - Fire In My Soul ft Shungudzo (Extended Mix)
+      08. ID - Who Got The Keys
+      Part 2
+      09. Jack Back - Orion
+      10. Malaa - We Get Crunk (Original Mix)
+      11. Simon Kidzoo - Yalama (Extended Mix)
+      12. Franky Wah - Freak
+      13. Kaskade feat. Madge - Tight (Original Mix)
+      14. Space Jump Salute - To The Funk (Extended Mix)
+      15. Tiësto - Grapevine (Extended Mix)
+      16. KURA - Lambo (Laidback Luke Extended Remix)
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="3653" average="4.88" min="1" max="5"/>
+	<media:statistics views="97741"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:MlZNrGPzLf8</id>
+    <yt:videoId>MlZNrGPzLf8</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta, Bebe Rexha &amp; J Balvin - Say My Name (JP Candela &amp; ATK1 remix)</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=MlZNrGPzLf8"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-21T07:00:00+00:00</published>
+    <updated>2019-01-30T13:11:18+00:00</updated>
+    <media:group>
+      <media:title>David Guetta, Bebe Rexha &amp; J Balvin - Say My Name (JP Candela &amp; ATK1 remix)</media:title>
+      <media:content url="https://www.youtube.com/v/MlZNrGPzLf8?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i2.ytimg.com/vi/MlZNrGPzLf8/hqdefault.jpg" width="480" height="360"/>
+      <media:description>BUY/LISTEN: https://guetta.co/saymynamejpcaremixAY
+
+      BUY THE NEW DAVID GUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      #DavidGuetta #BebeRexha #JBalvin #SayMyName #JPCandela #ATK1
+
+      David Guetta, Bebe Rexha &amp; J Balvin – Say My Name
+      (David Guetta, Giorgio Tuinfort, Boaz van de Beatz, Jose Balvin, Alejandro Ramirez, Thomas Troelsen, Emily Warren, Britt Burton, Phil Leigh, Matt Holmes)
+      Produced by David Guetta, Giorgio Tuinfort &amp; Boaz van de Beatz
+      All Instruments and Programming by David Guetta, Giorgio Tuinfort &amp; Boaz van de Beatz
+      Piano by Giorgio Tuinfort
+      Additional Keyboards by Matthew Holmes &amp; Philip Leigh
+      Bass by Quincell Adolphin
+      Recorded by Boaz van de Beatz at Poundcake Studios Rotterdam, Netherlands
+      Vocals produced by Mitch Allan
+      Mixed and mastered by Daddy’s Groove at TRIBE Studios Naples, Italy
+      Publishers: JackBack Publishing Ltd, admin by Write Here Music (SACEM) and Shapiro Bernstein (ASCAP); NEW CLASSIC (BUMA); From The Beatz Publishing (BMGTalpa) / AT Publishing; Universal Music Publishing (BMI); Warner music publishing; EMI Music Publishing Denmark; Havenwood House / Prescription Songs (ASCAP); WB Music Corp/BMB Top Songs (ASCAP); Hotel Cabana / Sony ATV (PRS)
+      J Balvin appears courtesy of Universal Music Latino
+      Bebe Rexha appears courtesy of Warner Bros
+      (P) &amp; (C) 2018 What A Music Ltd, Under Exclusive Licence to Parlophone/Warner Music France, a Warner Music Group Company
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="4693" average="4.63" min="1" max="5"/>
+	<media:statistics views="136539"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:8hdVSpCTxUM</id>
+    <yt:videoId>8hdVSpCTxUM</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta Playlist 435</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=8hdVSpCTxUM"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-14T17:58:45+00:00</published>
+    <updated>2019-01-29T18:06:03+00:00</updated>
+    <media:group>
+      <media:title>David Guetta Playlist 435</media:title>
+      <media:content url="https://www.youtube.com/v/8hdVSpCTxUM?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i1.ytimg.com/vi/8hdVSpCTxUM/hqdefault.jpg" width="480" height="360"/>
+      <media:description>#DavidGuettaPlaylist 435
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      BUY THE NEW #DAVIDGUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      David Guetta Playlist #435 Tracklisting:
+      Part 1
+      01. David Guetta &amp; CeCe Rogers - Freedom
+      02. ARTBAT - Prometheus (Original Mix)
+      03. ATFC - Strong 2 Survive (Original Mix)
+      04. Dateless - Grandma Funk (Rave Mix)
+      05. Lee Foss &amp; Eli Brown - Freak You Right (Club Mix)
+      06. Route 94 - House &amp; Pressure
+      Part 2
+      07. Jack Back - (It Happens) Sometimes
+      08. GotSome - GURLS &amp; BOYZ (Extended)
+      09. Piem - Pleasure (Original Mix)
+      10. Jack Back - Back and Forth
+      11. Hannah Wants - You Keep Comin'
+      12. Damien N-Drix - Kobeba
+      13. Just Kiddin - Hysteria (Extended Mix)
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="3090" average="4.86" min="1" max="5"/>
+	<media:statistics views="78410"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:0EsJhYFZyGI</id>
+    <yt:videoId>0EsJhYFZyGI</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta - Love Is Gone (Behind The Scenes)</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=0EsJhYFZyGI"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-14T17:01:11+00:00</published>
+    <updated>2019-01-30T08:34:55+00:00</updated>
+    <media:group>
+      <media:title>David Guetta - Love Is Gone (Behind The Scenes)</media:title>
+      <media:content url="https://www.youtube.com/v/0EsJhYFZyGI?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i1.ytimg.com/vi/0EsJhYFZyGI/hqdefault.jpg" width="480" height="360"/>
+      <media:description>David Guetta feat. Chris Willis - Love Is Gone&#13;
+      from the album POP LIFE - Movie edited by Damien Saintobert &#13;
+      © GUM PROD 2007&#13;
+      www.davidguetta.com</media:description>
+      <media:community>
+	<media:starRating count="3940" average="4.89" min="1" max="5"/>
+	<media:statistics views="99555"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:vIjdyLRtaKg</id>
+    <yt:videoId>vIjdyLRtaKg</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta, Bebe Rexha &amp; J Balvin - Say My Name (Corey James remix)</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=vIjdyLRtaKg"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-14T09:18:32+00:00</published>
+    <updated>2019-01-30T01:37:35+00:00</updated>
+    <media:group>
+      <media:title>David Guetta, Bebe Rexha &amp; J Balvin - Say My Name (Corey James remix)</media:title>
+      <media:content url="https://www.youtube.com/v/vIjdyLRtaKg?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i3.ytimg.com/vi/vIjdyLRtaKg/hqdefault.jpg" width="480" height="360"/>
+      <media:description>BUY/LISTEN: https://guetta.co/SMNCoreyJamesID
+
+      BUY THE NEW DAVID GUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      #DavidGuetta #BebeRexha #JBalvin #SayMyName #CoreyJames
+
+      David Guetta, Bebe Rexha &amp; J Balvin – Say My Name
+      (David Guetta, Giorgio Tuinfort, Boaz van de Beatz, Jose Balvin, Alejandro Ramirez, Thomas Troelsen, Emily Warren, Britt Burton, Phil Leigh, Matt Holmes)
+      Produced by David Guetta, Giorgio Tuinfort &amp; Boaz van de Beatz
+      All Instruments and Programming by David Guetta, Giorgio Tuinfort &amp; Boaz van de Beatz
+      Piano by Giorgio Tuinfort
+      Additional Keyboards by Matthew Holmes &amp; Philip Leigh
+      Bass by Quincell Adolphin
+      Recorded by Boaz van de Beatz at Poundcake Studios Rotterdam, Netherlands
+      Vocals produced by Mitch Allan
+      Mixed and mastered by Daddy’s Groove at TRIBE Studios Naples, Italy
+      Publishers: JackBack Publishing Ltd, admin by Write Here Music (SACEM) and Shapiro Bernstein (ASCAP); NEW CLASSIC (BUMA); From The Beatz Publishing (BMGTalpa) / AT Publishing; Universal Music Publishing (BMI); Warner music publishing; EMI Music Publishing Denmark; Havenwood House / Prescription Songs (ASCAP); WB Music Corp/BMB Top Songs (ASCAP); Hotel Cabana / Sony ATV (PRS)
+      J Balvin appears courtesy of Universal Music Latino
+      Bebe Rexha appears courtesy of Warner Bros
+      (P) &amp; (C) 2018 What A Music Ltd, Under Exclusive Licence to Parlophone/Warner Music France, a Warner Music Group Company
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="6576" average="4.81" min="1" max="5"/>
+	<media:statistics views="148804"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:F6ONqRZF5XU</id>
+    <yt:videoId>F6ONqRZF5XU</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta Playlist 434</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=F6ONqRZF5XU"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-07T17:58:27+00:00</published>
+    <updated>2019-01-30T10:32:18+00:00</updated>
+    <media:group>
+      <media:title>David Guetta Playlist 434</media:title>
+      <media:content url="https://www.youtube.com/v/F6ONqRZF5XU?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i3.ytimg.com/vi/F6ONqRZF5XU/hqdefault.jpg" width="480" height="360"/>
+      <media:description>#DavidGuettaPlaylist 434
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      BUY THE NEW DAVID GUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      David Guetta Playlist #434 Tracklisting:
+      Part 1
+      01. Jack Back - Inferno
+      02. Max Chapman &amp; Pirate Copy - Call The Police
+      03. Technique feat. Emmalyn &amp; Jeida Woods - Paces (Torren Foot Remix)
+      04. Scott Forshaw &amp; Greg Stainer vs Jeck - Got Somethin'
+      05. Chuck Roberts feat. Monique Bingham - In The Beginning (There Was Jack) (Illyus &amp; Barrientos Remix)
+      06. Dave Sol &amp; Roland Clark - Beat That Drum (Extended Mix)
+      Part 2
+      07. David Guetta - Just a Little More Love (Jack Back 2018 remix)
+      08. Ummet Ozcan - The Cell
+      09. Quintino - How It's Done
+      10. Steven Vegas - Bukka
+      11. Deorro x MAKJ x Max Styler - Bring It Back
+      12. Lifelike &amp; Kris Menace - Discopolis 2.0 (Sander van Doorn Extended Remix)
+      13. Tom Staar vs Sunnery James &amp; Ryan Marciano - Bombs Away
+      14. Antoine Delvig &amp; AYOR - Low Rider (Extended Mix)
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="3869" average="4.87" min="1" max="5"/>
+	<media:statistics views="91488"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:HpgpqZfH_wE</id>
+    <yt:videoId>HpgpqZfH_wE</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta Playlist 433</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=HpgpqZfH_wE"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-03T18:00:01+00:00</published>
+    <updated>2019-01-30T12:05:57+00:00</updated>
+    <media:group>
+      <media:title>David Guetta Playlist 433</media:title>
+      <media:content url="https://www.youtube.com/v/HpgpqZfH_wE?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i1.ytimg.com/vi/HpgpqZfH_wE/hqdefault.jpg" width="480" height="360"/>
+      <media:description>#DavidGuettaPlaylist 433
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      BUY THE NEW DAVID GUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+
+      David Guetta Playlist #433 Tracklisting:
+      Part 1
+      01. David Guetta - Im That Bitch (feat. Saweetie)
+      02. Jack Wins ft ILY - Alive
+      03. Aronauer &amp; Red Woods - Like Rollo (Club Mix)
+      04. Twoell - Drift
+      05. Siks - Energy (Extended Mix)
+      06. LMNTRX &amp; CAMARDA - I Wanna See
+      07. Mark Bale - Wasted
+      08. Antoine Delvig - Blonde Breda (Extended Mix)
+      09. Notion - Real (Extended Mix)
+      Part 2
+      10. Jack Back - Grenade
+      11. Flashmob - Bo Bo Boom (Original Mix)
+      12. MDE &amp; Juany Bravo - Soi Sauce
+      13. Pirate Copy &amp; Iglesias - Copicat
+      14. Dateless - Grandma Funk (Rave Mix)
+      15. Dombresky &amp; Noizu - Rave Alarm (Original Mix)
+      16. Mosimann - The Groove (Extended Mix)
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="4322" average="4.86" min="1" max="5"/>
+	<media:statistics views="95099"/>
+      </media:community>
+    </media:group>
+  </entry>
+  <entry>
+    <id>yt:video:7bQutzmqgfY</id>
+    <yt:videoId>7bQutzmqgfY</yt:videoId>
+    <yt:channelId>UC1l7wYrva1qCH-wgqcHaaRg</yt:channelId>
+    <title>David Guetta, Bebe Rexha &amp; J Balvin - Say My Name (Sidney Samson remix)</title>
+    <link rel="alternate" href="https://www.youtube.com/watch?v=7bQutzmqgfY"/>
+    <author>
+      <name>David Guetta</name>
+      <uri>https://www.youtube.com/channel/UC1l7wYrva1qCH-wgqcHaaRg</uri>
+    </author>
+    <published>2018-12-03T14:13:22+00:00</published>
+    <updated>2019-01-30T11:55:27+00:00</updated>
+    <media:group>
+      <media:title>David Guetta, Bebe Rexha &amp; J Balvin - Say My Name (Sidney Samson remix)</media:title>
+      <media:content url="https://www.youtube.com/v/7bQutzmqgfY?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+      <media:thumbnail url="https://i4.ytimg.com/vi/7bQutzmqgfY/hqdefault.jpg" width="480" height="360"/>
+      <media:description>BUY THE NEW DAVID GUETTA ALBUM NOW : https://davidguetta.lnk.to/Album7AY
+      🔔 Subscribe to be notified for new videos ➡️ http://bit.ly/GuettaYouTube 🔔
+
+      #DavidGuetta #BebeRexha #JBalvin #SayMyName #SidneySamson
+
+      David Guetta, Bebe Rexha &amp; J Balvin – Say My Name
+      (David Guetta, Giorgio Tuinfort, Boaz van de Beatz, Jose Balvin, Alejandro Ramirez, Thomas Troelsen, Emily Warren, Britt Burton, Phil Leigh, Matt Holmes)
+      Produced by David Guetta, Giorgio Tuinfort &amp; Boaz van de Beatz
+      All Instruments and Programming by David Guetta, Giorgio Tuinfort &amp; Boaz van de Beatz
+      Piano by Giorgio Tuinfort
+      Additional Keyboards by Matthew Holmes &amp; Philip Leigh
+      Bass by Quincell Adolphin
+      Recorded by Boaz van de Beatz at Poundcake Studios Rotterdam, Netherlands
+      Vocals produced by Mitch Allan
+      Mixed and mastered by Daddy’s Groove at TRIBE Studios Naples, Italy
+      Publishers: JackBack Publishing Ltd, admin by Write Here Music (SACEM) and Shapiro Bernstein (ASCAP); NEW CLASSIC (BUMA); From The Beatz Publishing (BMGTalpa) / AT Publishing; Universal Music Publishing (BMI); Warner music publishing; EMI Music Publishing Denmark; Havenwood House / Prescription Songs (ASCAP); WB Music Corp/BMB Top Songs (ASCAP); Hotel Cabana / Sony ATV (PRS)
+      J Balvin appears courtesy of Universal Music Latino
+      Bebe Rexha appears courtesy of Warner Bros
+      (P) &amp; (C) 2018 What A Music Ltd, Under Exclusive Licence to Parlophone/Warner Music France, a Warner Music Group Company
+
+      Follow David Guetta:
+      http://www.davidguetta.com
+      http://facebook.com/DavidGuetta
+      http://www.twitter.com/DavidGuetta
+      http://www.instagram.com/davidguetta</media:description>
+      <media:community>
+	<media:starRating count="7369" average="4.63" min="1" max="5"/>
+	<media:statistics views="191834"/>
+      </media:community>
+    </media:group>
+  </entry>
+</feed>