Commit cf13da60 authored by Patrick Barroca's avatar Patrick Barroca 🐧

Merge branch 'sandbox_author_page_ina' into 'master'

Sandbox author page ina

See merge request !2995
parents 4350e924 60997dd5
Pipeline #6239 failed with stage
in 28 minutes and 43 seconds
- ticket #86172 : Enrichissements : import des identifiants Wikidata, BNF Ark, ISNI et YouTube dans la codification des auteurs. Enrichissement YouTube et INA des pages auteur.
\ No newline at end of file
......@@ -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();
}
......
......@@ -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);
......
......@@ -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
......@@ -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(),
......
......@@ -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()));
}
}
......@@ -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']);
......
......@@ -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',
......
......@@ -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"]]);
......
<?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"]]);
......
......@@ -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'],
......
......@@ -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,
......
<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%">
......
......@@ -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
......
<?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) {}
......@@ -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();
}
......
......@@ -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;
......
......@@ -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(),
......
......@@ -1552,7 +1552,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());
......
......@@ -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();
}
......
......@@ -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;
......
<?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();
}
}
<?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
......@@ -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;
}
......
......@@ -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)
: '';
}
......
......@@ -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);
......
......@@ -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();
......
......@@ -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']);
......
<?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',