From 02daf2295f0d61f5098a278dc740577fd8b48474 Mon Sep 17 00:00:00 2001
From: gloas <gloas@afi-sa.fr>
Date: Mon, 7 Sep 2020 09:24:06 +0200
Subject: [PATCH] dev #116469 improve EAD Export

---
 VERSIONS_WIP/116469                           |   1 +
 .../AlbumCategorie/EadGallicaVisitor.php      | 226 +++++++++++++++++-
 library/Class/AlbumCategorie/EadVisitor.php   |   5 +-
 library/Class/FRBR/Link.php                   |   4 +-
 library/Class/Notice.php                      |  39 ++-
 library/Class/NoticeUnimarc/Fluent.php        |   9 +
 .../Library/View/Wrapper/Record.php           |   6 +-
 .../AlbumControllerExportEadGallicaTest.php   | 222 ++++++++++++-----
 ..._Watkins_entraineur_public_a_Chantilly.uni |   1 +
 9 files changed, 421 insertions(+), 92 deletions(-)
 create mode 100644 VERSIONS_WIP/116469
 create mode 100644 tests/application/modules/admin/controllers/Arthur_Watkins_entraineur_public_a_Chantilly.uni

diff --git a/VERSIONS_WIP/116469 b/VERSIONS_WIP/116469
new file mode 100644
index 00000000000..d68645ee1a4
--- /dev/null
+++ b/VERSIONS_WIP/116469
@@ -0,0 +1 @@
+ - ticket #116469 : Bibliothèque numérique : amélioration de la compatibilité de l'export EAD pour Gallica
\ No newline at end of file
diff --git a/library/Class/AlbumCategorie/EadGallicaVisitor.php b/library/Class/AlbumCategorie/EadGallicaVisitor.php
index 28b44978ea4..d1599602586 100644
--- a/library/Class/AlbumCategorie/EadGallicaVisitor.php
+++ b/library/Class/AlbumCategorie/EadGallicaVisitor.php
@@ -73,16 +73,16 @@ class Class_AlbumCategorie_EadGallicaVisitor extends Class_AlbumCategorie_EadVis
 
 
   public function visitUnitdate($album) {
-    return ($date = $album->getNote('305$a'))
+    return ($date = $album->getAnnee())
       ? $this->_builder->unitdate(['normal' => str_replace('-', '', $date)], $date)
       : '';
   }
 
 
   public function visitPhysdesc($album) {
-    $tab_physdesc = array_filter([$this->_propertyInTag([$album, 'getDuration'], 'extent'),
-                                  $this->_propertyInTag([$album, 'getMisc'], 'genreform'),
-                                  $this->_propertyInTag([$album, 'getFormat'], 'dimensions')]);
+    $tab_physdesc = array_filter([$album->getDuration(),
+                                  $album->getMisc(),
+                                  $album->getFormat()]);
 
     return $tab_physdesc
       ? $this->_builder->physdesc(implode(' ; ', $tab_physdesc))
@@ -106,6 +106,8 @@ class Class_AlbumCategorie_EadGallicaVisitor extends Class_AlbumCategorie_EadVis
 
 
   public function visitAlbum($album) {
+    $album = Class_AlbumCategorie_EadGallicaVisitor_AlbumWrapper::newFor($album);
+
     $this->_xml_content .= '<c id="'
       . $album->getIdEad()
       . '" level="item">'
@@ -153,12 +155,12 @@ class Class_AlbumCategorie_EadGallicaVisitor extends Class_AlbumCategorie_EadVis
                                return $b->controlaccess(
                                                         $b->subject($b->cdata($matiere->getLibelle())));
                              },
-                             Class_Matiere::findAllBy(['id_matiere' => explode(';', $album->getMatiere())])
+                             Class_CodifMatiere::findAllBy(['id_matiere' => explode(';', $album->getMatiere())])
     );
   }
 
 
-  public function visitRessource() {
+  public function visitRessource($ressource, $index) {
   }
 
 
@@ -184,3 +186,215 @@ class Class_AlbumCategorie_EadGallicaVisitor extends Class_AlbumCategorie_EadVis
                                            ));
   }
 }
+
+
+
+class Class_AlbumCategorie_EadGallicaVisitor_AlbumWrapper {
+
+  protected
+    $_album,
+    $_record,
+    $_duration,
+    $_misc,
+    $_format,
+    $_description,
+    $_provenance,
+    $_authors,
+    $_matiere,
+    $_genre,
+    $_title,
+    $_subtitle,
+    $_cote,
+    $_langue,
+    $_annee;
+
+
+  public static function newFor($album) {
+    if ( ! $link = Class_FRBR_Link::findFirstRecordLinkForAlbum($album))
+      return $album;
+
+    if ( ! $record = $link->getEntityOfType(Class_FRBR_Link::TYPE_NOTICE))
+      return $album;
+
+    return new static($album, $record);
+  }
+
+
+  public function __construct($album, $record) {
+    $this->_album = $album;
+    $this->_record = $record;
+    $this->_item = new Class_Exemplaire;
+    $this->_initFromRecordOrAlbum();
+  }
+
+
+  protected function _initFromRecordOrAlbum() {
+    if ($item = $this->_record->getFirstExemplaire())
+      $this->_item = $item;
+
+    $this->_duration = $this->_getRecordSubfieldOrFallback('215', 'a', 'getDuration');
+    $this->_misc = $this->_getRecordSubfieldOrFallback('215', 'c', 'getMisc');
+    $this->_format = $this->_getRecordSubfieldOrFallback('215', 'd', 'getFormat');
+    $this->_provenance = $this->_getRecordSubfieldOrFallback('317', 'a', 'getProvenance');
+
+    $this->_description = ($description = $this->_record->getResume())
+      ? $description
+      : $this->_album->getDescription();
+
+    $this->_title = ($title = $this->_record->getTitrePrincipal(' '))
+      ? $title
+      : $this->_album->getTitre();
+
+    $this->_subtitle = ($subtitle = $this->_record->getSubtitle())
+      ? $subtitle
+      : $this->_album->getSousTitre();
+
+    $this->_cote = ($cote = $this->_item->getCote())
+      ? $cote
+      : $this->_album->getCote();
+
+    $this->_matiere = $this->_getRecordCodifOrFallback(Class_CodifMatiere::CODE_FACETTE, 'getMatiere');
+    $this->_genre = $this->_getRecordCodifOrFallback(Class_CodifGenre::CODE_FACETTE, 'getGenre');
+
+    $this->_annee = $this->_initAnnee();
+    $this->_authors = $this->_initAuthors();
+    $this->_langue = $this->_initLangue();
+  }
+
+
+  protected function _getRecordSubfieldOrFallback($zone, $field, $fallback) {
+    $values = $this->_record->get_subfield($zone, $field);
+    return ($value = reset($values))
+      ? $value
+      : call_user_func([$this->_album, $fallback]);
+  }
+
+
+  protected function _getRecordCodifOrFallback($facet_code, $fallback) {
+    return ($values = $this->_initCodif($facet_code))
+      ? $values
+      : call_user_func([$this->_album, $fallback]);
+  }
+
+
+  protected function _initAuthors() {
+   if (!$authors = $this->_record->getAuteursUnimarc(false, true))
+      return $this->_album->getAuthors();
+
+    return array_map(function($author)
+                     {
+                       return new Class_Notice_Author($author->getAuthorName(), $author->getFonction());
+                     },
+                     $authors);
+  }
+
+
+  protected function _initCodif($facet_code) {
+    $facets = $this->_record->getChampNotice($facet_code, $this->_record->getFacettes());
+    $facets = array_map(function($facet)
+                        {
+                          return $facet->getId();
+                        },
+                        $facets);
+
+    return implode(';', $facets);
+  }
+
+
+  protected function _initAnnee() {
+    if (!$annee = $this->_record->get_subfield('210', 'd'))
+      return $this->_album->getAnnee();
+
+    $annee = reset($annee);
+
+    return preg_match('/\d{4}/' , $annee, $matches)
+      ? $matches[0]
+      : $this->_album->getAnnee();
+  }
+
+
+  protected function _initLangue() {
+    return ($langues = array_filter(array_map(function($lang)
+                                              {
+                                                return Class_CodifLangue::find($lang);
+                                              },
+                                              $this->_record->getLangueCodes())))
+      ? reset($langues)
+      : $this->_album->getLangue();
+  }
+
+
+  public function getIdEad() {
+    return $this->_album->getIdEad();
+  }
+
+
+  public function getLangue() {
+    return $this->_langue;
+  }
+
+
+  public function getCote() {
+    return $this->_cote;
+  }
+
+
+  public function getTitre() {
+    return $this->_title;
+  }
+
+
+  public function getSousTitre() {
+    return $this->_subtitle;
+  }
+
+
+  public function getDuration() {
+    return $this->_duration;
+  }
+
+
+  public function getMisc() {
+    return $this->_misc;
+  }
+
+
+  public function getFormat() {
+    return $this->_format;
+  }
+
+
+  public function getDescription() {
+    return $this->_description;
+  }
+
+
+  public function getProvenance() {
+    return $this->_provenance;
+  }
+
+
+  public function getAuthors() {
+    return $this->_authors;
+  }
+
+
+  public function getMatiere() {
+    return $this->_matiere;
+  }
+
+
+  public function getGenre() {
+    return $this->_genre;
+  }
+
+
+  public function getAnnee() {
+    return $this->_annee;
+  }
+
+
+  public function acceptEadVisitor($visitor) {
+    return $this->_album->acceptEadVisitor($visitor);
+  }
+}
\ No newline at end of file
diff --git a/library/Class/AlbumCategorie/EadVisitor.php b/library/Class/AlbumCategorie/EadVisitor.php
index f4af661bb39..0e340c0e7fa 100644
--- a/library/Class/AlbumCategorie/EadVisitor.php
+++ b/library/Class/AlbumCategorie/EadVisitor.php
@@ -123,8 +123,9 @@ class Class_AlbumCategorie_EadVisitor {
   }
 
 
-  public function _constructDid($album) {
+  protected function _constructDid($album) {
     $b = $this->_builder;
+
     $langue = $album->getLangue();
 
     return $b
@@ -133,7 +134,7 @@ class Class_AlbumCategorie_EadVisitor {
             . $this->visitUnitdate($album)
             . ($langue
                ? $b->langmaterial($b->language(['langcode' => $langue->getId()],
-                                            $langue->getLibelle()))
+                                               $langue->getLibelle()))
                : '')
             . $this->visitPhysdesc($album))
       . $this->visitDescription($album->getDescription())
diff --git a/library/Class/FRBR/Link.php b/library/Class/FRBR/Link.php
index 851eb713e8a..5e47ad136b4 100644
--- a/library/Class/FRBR/Link.php
+++ b/library/Class/FRBR/Link.php
@@ -106,7 +106,9 @@ class FRBR_LinkLoader extends Storm_Model_Loader {
    * @return Class_FRBR_Link or null
    */
   public function findFirstRecordLinkForAlbum($album) {
-    return Class_FRBR_Link::findFirstBy($this->_recordLinkFilterFromAlbum($album));
+    return ($link = Class_FRBR_Link::findFirstBy($this->_recordLinkFilterFromAlbum($album)))
+      ? $link
+      : Class_FRBR_Link::findFirstBy($this->_albumLinkFilterFromRecord($album));
   }
 
 
diff --git a/library/Class/Notice.php b/library/Class/Notice.php
index c084c79e7e6..7b9748c5579 100644
--- a/library/Class/Notice.php
+++ b/library/Class/Notice.php
@@ -920,13 +920,11 @@ class Class_Notice extends Storm_Model_Abstract {
   }
 
 
-// ----------------------------------------------------------------
-// Titre + sous-titre paramétré pour affichage des listes
-// ----------------------------------------------------------------
-  public function getTitreEtSousTitre()
-  {
+  public function getTitreEtSousTitre($separator=BR) {
     unset ($this->_titre_principal);
-    return $this->getTitrePrincipal() . $this->getSubtitle();
+    return implode(' : ',
+                   array_filter([$this->getTitrePrincipal($separator),
+                                 $this->getSubtitle()]));
   }
 
 
@@ -935,20 +933,17 @@ class Class_Notice extends Storm_Model_Abstract {
     if (!count($zones))
       return '';
 
-    $subtitle = '';
+    $subtitles = [];
     foreach($zones as $zone) {
-      $data = $this->get_subfield(substr($zone,0,3), substr($zone,4,1));
-      if (!count($data)) continue;
-      $subtitle .= ' : '.$this->filtreTitre($data[0]);
+      $data = $this->get_subfield(substr($zone, 0, 3), substr($zone, 4, 1));
+      $subtitles[] = $this->filtreTitre(reset($data));
     }
-    return $subtitle;
+
+    return implode(' : ', array_filter($subtitles));
   }
 
-// ----------------------------------------------------------------
-// Label docs sonores
-// ----------------------------------------------------------------
-  public function getLabelSonore()
-  {
+
+  public function getLabelSonore() {
     $bloc=$this->get_subfield('071');
     if(!count($bloc)) return;
     $data=$this->decoupe_bloc_champ($bloc[0]);
@@ -1284,7 +1279,8 @@ class Class_Notice extends Storm_Model_Abstract {
     $label = trim($prenom . ' ' . $nom) . (($fonction) ? ' (' . $fonction . ')': '');
     return (new Class_Notice_FieldAuthor($codif_auteur))
       ->setLabel($label)
-      ->setFonction($fonction);
+      ->setFonction($fonction)
+      ->setAuthorName($codif_auteur->getLibelle());
   }
 
 
@@ -1426,9 +1422,7 @@ class Class_Notice extends Storm_Model_Abstract {
     return $this;
   }
 
-// ----------------------------------------------------------------
-// Notes bibliographiques
-// ----------------------------------------------------------------
+
   public function getNotes()
   {
     $zones = array('300a', '303a', '304a', '305a', '306a', '307a', '308a', '310a', '312a', '313a', '314a', '316a', '317a', '320a', '321a', '323a', '334abcd', '337a', '345a');
@@ -1507,8 +1501,9 @@ class Class_Notice extends Storm_Model_Abstract {
 
     $data = $this->get_subfield('330', 'a');
 
-    return $this->_resume = (($resume = $this->_getHtmlSummary($data)) ?
-                             $resume : $this->_getTextSummary($data));
+    return $this->_resume = ($resume = $this->_getHtmlSummary($data))
+                             ? $resume
+                             : $this->_getTextSummary($data);
   }
 
 
diff --git a/library/Class/NoticeUnimarc/Fluent.php b/library/Class/NoticeUnimarc/Fluent.php
index 4fbfdd00bc0..c009bf706d5 100644
--- a/library/Class/NoticeUnimarc/Fluent.php
+++ b/library/Class/NoticeUnimarc/Fluent.php
@@ -65,6 +65,15 @@ class Class_NoticeUnimarc_Fluent {
   }
 
 
+  public function clearZone($label) {
+    $this->_zones = $this->_zones->reject(function($zone) use ($label)
+                                          {
+                                            return $zone->isLabel($label);
+                                          });
+    return $this;
+  }
+
+
   public function newZone() {
     $zone = new Class_NoticeUnimarc_Zone();
     $this->_zones->append($zone);
diff --git a/library/templates/Intonation/Library/View/Wrapper/Record.php b/library/templates/Intonation/Library/View/Wrapper/Record.php
index a124e9424a5..b873dcdc0b1 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Record.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Record.php
@@ -31,9 +31,9 @@ class Intonation_Library_View_Wrapper_Record extends Intonation_Library_View_Wra
 
 
   public function getMainTitle() {
-    return $this->_main_title ?
-      $this->_main_title :
-      ($this->_main_title = $this->_model->getTitrePrincipal(' ') . $this->_model->getSubtitle());
+    return $this->_main_title = $this->_main_title
+      ? $this->_main_title
+      : $this->_model->getTitreEtSousTitre(' ');
   }
 
 
diff --git a/tests/application/modules/admin/controllers/AlbumControllerExportEadGallicaTest.php b/tests/application/modules/admin/controllers/AlbumControllerExportEadGallicaTest.php
index 6a493f0b977..438502be38a 100644
--- a/tests/application/modules/admin/controllers/AlbumControllerExportEadGallicaTest.php
+++ b/tests/application/modules/admin/controllers/AlbumControllerExportEadGallicaTest.php
@@ -56,7 +56,7 @@ abstract class Admin_AbstractAlbumControllerExportEadTestCase
 
     $this->_xpath = new Storm_Test_XPathXML();
 
-    $this->fixture('Class_Matiere',
+    $this->fixture('Class_CodifMatiere',
                    ['libelle' => 'R\'N\'B : France',
                     'id' => 1,
                     'id_matiere' => 1,
@@ -66,7 +66,7 @@ abstract class Admin_AbstractAlbumControllerExportEadTestCase
                     'id_bnf' => '',
                     'nb_notices' => 0]);
 
-    $this->fixture('Class_Matiere',
+    $this->fixture('Class_CodifMatiere',
                    ['libelle' => 'Race chevaline',
                     'id' => 2,
                     'id_matiere' => 2,
@@ -76,7 +76,7 @@ abstract class Admin_AbstractAlbumControllerExportEadTestCase
                     'id_bnf' => '',
                     'nb_notices' => 0]);
 
-    $this->fixture('Class_Matiere',
+    $this->fixture('Class_CodifMatiere',
                    ['libelle' => 'Rabhi : Daniel',
                     'id' => 3,
                     'id_matiere' => 3,
@@ -86,7 +86,7 @@ abstract class Admin_AbstractAlbumControllerExportEadTestCase
                     'id_bnf' => '',
                     'nb_notices' => 0]);
 
-    $this->fixture('Class_Matiere',
+    $this->fixture('Class_CodifMatiere',
                    ['libelle' => 'Rabier : Christophe',
                     'id' => 4,
                     'id_matiere' => 4,
@@ -97,8 +97,7 @@ abstract class Admin_AbstractAlbumControllerExportEadTestCase
                     'nb_notices' => 0]);
 
     $album = $this->fixture('Class_Album',
-                            ['notice_id' => 561605,
-                             'titre' => 'Le Tout-turf illustre l\'album n°1 de Caza 1942-1943',
+                            ['titre' => 'Le Tout-turf illustre l\'album n°1 de Caza 1942-1943',
                              'sous_titre' => '',
                              'fichier' => '13140_Romdea07 009 01 Le Tout-turf illustre l\'album n°1 de Caza 1942-1943.jpg',
                              'pdf' => '',
@@ -115,7 +114,7 @@ abstract class Admin_AbstractAlbumControllerExportEadTestCase
                              'cfg_thumbnails' => 'a:2:{s:15:"thumbnail_width";s:3:"400";s:16:"op_hauteur_boite";s:3:"800";}',
                              'provenance' => 'Romanet',
                              'cote' => 'Romdea07 009 01 ',
-                             'notes' => 'a:5:{s:5:"215$a";s:4:"vol4";s:5:"215$c";s:4:"ill.";s:5:"215$d";s:2:"A4";s:5:"305$a";s:10:"2020-08-11";s:5:"306$a";s:24:"Mediatheque de Deauville";}',
+                             'notes' => 'a:4:{s:5:"215$a";s:4:"vol4";s:5:"215$c";s:4:"ill.";s:5:"215$d";s:2:"A4";s:5:"306$a";s:24:"Mediatheque de Deauville";}',
                              'visible' => 1,
                              'droits' => '',
                              'nature_doc' => '',
@@ -144,6 +143,7 @@ abstract class Admin_AbstractAlbumControllerExportEadTestCase
                    ]);
 
     $this->fixture('Class_CodifLangue', ['id'=>'fre', 'libelle'=>'Français']);
+    $this->fixture('Class_CodifLangue', ['id'=>'eng', 'libelle'=>'English']);
 
 
     $this->_xpath->registerNameSpace('ead', 'urn:isbn:1-931666-22-9');
@@ -248,10 +248,10 @@ class Admin_AlbumControllerExportEadGallicaBasicTest
 
 
   /** @test */
-  public function shouldContainsUnitdate() {
+  public function shouldContainsUnitdate1942() {
     $this->_xpath->assertXpathContentContains($this->_xml,
-                                              '//ead:archdesc/ead:dsc//ead:unitdate[@normal="20200811"]',
-                                              '2020-08-11');
+                                              '//ead:archdesc/ead:dsc//ead:unitdate[@normal="1942"]',
+                                              '1942');
   }
 
 
@@ -447,26 +447,10 @@ class Admin_AlbumControllerExportEadGallicaBasicTest
 
 
   /** @test */
-  public function xmlPhysdescShouldcontainsExtentVol4() {
+  public function xmlShouldContainsPhysdescVol4IllA4() {
     $this->_xpath->assertXPathContentContains($this->_xml,
-                                              '//ead:c[@level="item"]/ead:did/ead:physdesc/ead:extent',
-                                              'vol4');
-  }
-
-
-  /** @test */
-  public function xmlPhysdescShouldcontainsGenreformIll() {
-    $this->_xpath->assertXPathContentContains($this->_xml,
-                                              '//ead:c[@level="item"]/ead:did/ead:physdesc/ead:genreform',
-                                              'ill.');
-  }
-
-
-  /** @test */
-  public function xmlPhysdescShouldcontainsDimensionsA4() {
-    $this->_xpath->assertXPathContentContains($this->_xml,
-                                              '//ead:c[@level="item"]/ead:did/ead:physdesc/ead:dimensions',
-                                              'A4');
+                                              '//ead:c[@level="item"]/ead:did/ead:physdesc',
+                                              'vol4 ; ill. ; A4');
   }
 }
 
@@ -497,7 +481,6 @@ class Admin_AlbumControllerExportEadGallicaDescriptionHTMLTest
                                               '//ead:scopecontent',
                                               'Imaginé par un être supérieur');
   }
-
 }
 
 
@@ -511,8 +494,8 @@ class Admin_AlbumControllerExportEadGallicaAlbumWithoutNotesTest
   public function setUp() {
     parent::setUp();
 
-    Class_Album::find(13140)
-      ->setNotes([]);
+    Class_Album::find(13140)->setNotes([])->setAnnee('')->assertSave();
+
     $this->postDispatch('admin/album/export-ead', ['cat_id' => 1,
                                                    'is_gallica' => 1]);
     $this->_xml = $this->_response->getBody();
@@ -538,7 +521,7 @@ class Admin_AlbumControllerExportEadGallicaAlbumWithoutNotesTest
 
 
 
-class Admin_AlbumControllerExportEadGallicaAlbumWithPartialNoteTest
+class Admin_AlbumControllerExportEadGallicaAlbumWithoutProvenanceTest
   extends Admin_AbstractAlbumControllerExportEadTestCase {
   protected
     $_xml;
@@ -547,7 +530,7 @@ class Admin_AlbumControllerExportEadGallicaAlbumWithPartialNoteTest
     parent::setUp();
 
     Class_Album::find(13140)
-      ->setNotes('a:4:{s:5:"215$a";s:0:"";s:5:"215$c";s:4:"ill.";s:5:"215$d";s:2:"A4";s:5:"306$a";s:24:"Mediatheque de Deauville";}')
+      ->setProvenance('')
       ->assertSave();
     $this->postDispatch('admin/album/export-ead', ['cat_id' => 1,
                                                    'is_gallica' => 1]);
@@ -556,53 +539,176 @@ class Admin_AlbumControllerExportEadGallicaAlbumWithPartialNoteTest
 
 
   /** @test */
-  public function xmlShouldNotContainsPhysdescExtent() {
+  public function xmlShouldNotContainsCustodhist() {
     $this->_xpath
       ->assertNotXpath($this->_xml,
-                       '//ead:archdesc/ead:dsc//ead:physdesc/ead:extent');
+                       '//ead:archdesc/ead:dsc//ead:custodhist');
+  }
+}
+
+
+
+
+class Admin_AlbumControllerExportEadGallicaAlbumWithRecordFRBRTest
+  extends Admin_AbstractAlbumControllerExportEadTestCase {
+  protected
+    $_xml;
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->fixture('Class_CodifGenre',
+                   ['id' => 1,
+                    'libelle' => 'Carte postale']);
+
+    $legacy = (new Class_NoticeUnimarc_Writer)
+      ->setNotice(file_get_contents(__DIR__ . '/Arthur_Watkins_entraineur_public_a_Chantilly.uni'));
+
+    $fluent = Class_NoticeUnimarc_Fluent::fromLegacy($legacy)
+      ->clearZone('210')
+      ->zoneWithChildren('101', ['a' => 'eng'])
+      ->zoneWithChildren('210', ['d' => '08/09/2020'])
+      ->zoneWithChildren('700', ['a' => 'Inconnu', '4' => '600', '6' => 'Photographe'])
+      ->zoneWithChildren('317', ['a' => 'Dom Camillo, sept. 88'])
+      ;
+
+    $this->fixture('Class_CodifAuteur',
+                   ['id' => 28829,
+                    'libelle' => 'Inconnu']);
+
+    $record = $this->fixture('Class_Notice',
+                             ['id' => 12387,
+                              'facettes' => 'M1 M2 G1',
+                              'unimarc' => $fluent->render(),
+                              'exemplaires' => [$this->fixture('Class_Exemplaire',
+                                                               ['id' => 23,
+                                                                'cote' => 'TAB HH 222'])]]);
+
+    $album = Class_Album::find(13140)
+      ->setNotes('');
+
+    $album->assertSave();
+
+    $album_absolute_url = Class_Url::absolute(array_merge(['id' => $album->getId()],
+                                                          $album->getPermalink()),
+                                              null, true);
+
+    $frbr_type = $this->fixture('Class_FRBR_LinkType',
+                                ['id' => 1898,
+                                 'libelle' => 'Version numérique de',
+                                 'from_source' => 'Version originale',
+                                 'from_target' => 'Version numérique']);
+
+    $this->fixture('Class_FRBR_Link',
+                   ['id' => 234234,
+                    'source' => $record->getAbsoluteUrl(),
+                    'target' => $album_absolute_url,
+                    'type' => $frbr_type]);
+
+    $this->postDispatch('admin/album/export-ead', ['cat_id' => 1,
+                                                   'is_gallica' => 1]);
+
+    $this->_xml = $this->_response->getBody();
   }
 
 
   /** @test */
-  public function xmlShouldContainsPhysdescGenreform() {
+  public function xmlShouldContainsUnitdate2020() {
     $this->_xpath
-      ->assertXPath($this->_xml,
-                    '//ead:archdesc/ead:dsc//ead:physdesc/ead:genreform');
+      ->assertXpathContentContains($this->_xml,
+                                   '//ead:archdesc/ead:dsc//ead:unitdate[@normal="2020"]',
+                                   '2020');
   }
 
 
   /** @test */
-  public function xmlShouldContainsPhysdescdimensions() {
+  public function xmlShouldContainsLanguageEngEnglish() {
+    $this->_xpath->assertXpathContentContains($this->_xml,
+                                              '//ead:langmaterial/ead:language[@langcode="eng"]',
+                                              'English');
+  }
+
+
+  /** @test */
+  public function xmlShouldContainsUnittitleArthurWatkinsLesSports() {
     $this->_xpath
-      ->assertXPath($this->_xml,
-                    '//ead:archdesc/ead:dsc//ead:physdesc/ead:dimensions');
+      ->assertXPathContentContains($this->_xml,
+                                   '//ead:archdesc//ead:did/ead:unittitle',
+                                   'Arthur Watkins, entraîneur public à Chantilly. : Les sports. Nos entraîneurs.');
   }
-}
 
 
+  /** @test */
+  public function xmlShouldContainsUnitidTypeCote() {
+    $this->_xpath
+      ->assertXPathContentContains($this->_xml,
+                                   '//ead:archdesc//ead:did/ead:unitid[@type="cote"]',
+                                   'TAB HH 222');
+  }
 
 
-class Admin_AlbumControllerExportEadGallicaAlbumWithoutProvenanceTest
-  extends Admin_AbstractAlbumControllerExportEadTestCase {
-  protected
-    $_xml;
+  /** @test */
+  public function xmlShouldContainsPhysdescImpPhotomecaNEtB9x14cm() {
+    $this->_xpath
+      ->assertXPathContentContains($this->_xml,
+                                   '//ead:archdesc/ead:dsc//ead:physdesc',
+                                   '1 imp. photoméca. ; n. et b. ; 9 x 14 cm');
+  }
 
-  public function setUp() {
-    parent::setUp();
 
-    Class_Album::find(13140)
-      ->setProvenance('')
-      ->assertSave();
-    $this->postDispatch('admin/album/export-ead', ['cat_id' => 1,
-                                                   'is_gallica' => 1]);
-    $this->_xml = $this->_response->getBody();
+  /** @test */
+  public function xmlShouldNotContainsPhysdescExtent() {
+    $this->_xpath
+      ->assertNotXPath($this->_xml, '//ead:archdesc/ead:dsc//ead:physdesc/ead:extent');
   }
 
 
   /** @test */
-  public function xmlShouldNotContainsCustodhist() {
+  public function xmlShouldNotContainsPhysdescDimensions() {
     $this->_xpath
-      ->assertNotXpath($this->_xml,
-                       '//ead:archdesc/ead:dsc//ead:custodhist');
+      ->assertNotXPath($this->_xml, '//ead:archdesc/ead:dsc//ead:physdesc/ead:dimensions');
+  }
+
+
+  /** @test */
+  public function xmlShouldNotContainsPhysdescGenreform() {
+    $this->_xpath
+      ->assertNotXPath($this->_xml, '//ead:archdesc/ead:dsc//ead:physdesc/ead:genreform');
+  }
+
+
+  /** @test */
+  public function xmlShouldContainsGenreformCartePostale() {
+    $this->_xpath
+      ->assertXPathContentContains($this->_xml,
+                                   '//ead:archdesc/ead:dsc//ead:controlaccess/ead:genreform',
+                                   'Carte postale');
+  }
+
+
+  /** @test */
+  public function xmlShouldContainsSubjectRaceChevaline() {
+    $this->_xpath
+      ->assertXpathContentContains($this->_xml,
+                                   '//ead:archdesc/ead:dsc//ead:controlaccess[child::ead:subject]',
+                                   'Race chevaline');
+  }
+
+
+  /** @test */
+  public function xmlShouldContainsAuthorInconnu() {
+    $this->_xpath
+      ->assertXpathContentContains($this->_xml,
+                                   '//ead:archdesc/ead:dsc//ead:controlaccess/ead:persname[@normal="Inconnu"][@role="Photographe"]',
+                                   'Inconnu');
+  }
+
+
+  /** @test */
+  public function xmlShouldContainsCustodhistDomCamillo() {
+    $this->_xpath
+      ->assertXpathContentContains($this->_xml,
+                                   '//ead:archdesc/ead:dsc//ead:custodhist/ead:p',
+                                   'Dom Camillo, sept. 88');
   }
 }
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/Arthur_Watkins_entraineur_public_a_Chantilly.uni b/tests/application/modules/admin/controllers/Arthur_Watkins_entraineur_public_a_Chantilly.uni
new file mode 100644
index 00000000000..9f91811818f
--- /dev/null
+++ b/tests/application/modules/admin/controllers/Arthur_Watkins_entraineur_public_a_Chantilly.uni
@@ -0,0 +1 @@
+00382nkm0 2200133   450 001000700000100001300007200008400020210002200104215004400126305000800170316002500178606002800203801001700231794320  a201901291 aArthur Watkins, entraîneur public à Chantilly.eLes sports. Nos entraîneurs.  cND Phot.d[s. d.]  a1 imp. photoméca.cn. et b.d9 x 14 cm  a365  atimbrée, a voyagé.  aEntraîneurs de chevaux 2aFrc20190718
\ No newline at end of file
-- 
GitLab