View source
<?php
namespace Drupal\fb_instant_articles\Normalizer;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\InfoParserInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\fb_instant_articles\AdTypes;
use Drupal\fb_instant_articles\Form\EntityViewDisplayEditForm;
use Drupal\serialization\Normalizer\NormalizerBase;
use Facebook\InstantArticles\Elements\Ad;
use Facebook\InstantArticles\Elements\Analytics;
use Facebook\InstantArticles\Elements\Author;
use Facebook\InstantArticles\Elements\Header;
use Facebook\InstantArticles\Elements\InstantArticle;
use Facebook\InstantArticles\Elements\Time;
class InstantArticleContentEntityNormalizer extends NormalizerBase {
use StringTranslationTrait;
use EntityHelperTrait;
protected $supportedInterfaceOrClass = 'Drupal\\Core\\Entity\\ContentEntityInterface';
protected $format = 'fbia';
protected $entityFieldManager;
protected $entityTypeManager;
protected $infoParser;
protected $moduleHandler;
protected $currentLanguage;
public function __construct(ConfigFactoryInterface $config, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) {
$this->config = $config
->get('fb_instant_articles.settings');
$this->entityFieldManager = $entity_field_manager;
$this->entityTypeManager = $entity_type_manager;
$this->infoParser = $info_parser;
$this->moduleHandler = $module_handler;
$this->currentLanguage = $language_manager
->getCurrentLanguage();
}
public function normalize($data, $format = NULL, array $context = []) {
if (isset($context['instant_article'])) {
$article = $context['instant_article'];
}
else {
$article = InstantArticle::create()
->addMetaProperty('op:generator:application', 'drupal/fb_instant_articles')
->addMetaProperty('op:generator:application:version', $this
->getApplicationVersion());
if ($this->currentLanguage
->getDirection() === LanguageInterface::DIRECTION_RTL) {
$article
->enableRTL();
}
if ($style = $this->config
->get('style')) {
$article
->withStyle($style);
}
$this
->normalizeCanonicalUrl($article, $data);
$this
->normalizeDefaultHeader($article, $data);
$this
->analyticsFromSettings($article);
$this
->adsFromSettings($article);
$context += [
'instant_article' => $article,
];
}
if ($display = $this
->entityViewDisplay($data, $context)) {
if (isset($context['views_style_plugin'])) {
$context['views_style_plugin']->displayHandler->display['cache_metadata']['tags'] = Cache::mergeTags($context['views_style_plugin']->displayHandler->display['cache_metadata']['tags'], $display
->getCacheTags());
}
$context['entity_view_display'] = $display;
$components = $this
->getApplicableComponents($display);
uasort($components, [
$this,
'sortComponents',
]);
foreach ($components as $name => $options) {
$this->serializer
->normalize($data
->get($name), $format, $context);
}
}
return $article;
}
protected function entityViewDisplay(ContentEntityInterface $entity, array $context) {
$fbia_display_id = $entity
->getEntityTypeId() . '.' . $entity
->bundle() . '.' . EntityViewDisplayEditForm::FBIA_VIEW_MODE;
$default_display_id = $entity
->getEntityTypeId() . '.' . $entity
->bundle() . '.default';
$storage = $this->entityTypeManager
->getStorage('entity_view_display');
if (isset($context['entity_view_display'])) {
return $context['entity_view_display'];
}
elseif (isset($context['view_mode']) && ($display = $storage
->load($entity
->getEntityTypeId() . '.' . $entity
->bundle() . '.' . $context['view_mode']))) {
return $display;
}
elseif (($display = $storage
->load($fbia_display_id)) && $display
->status()) {
return $display;
}
elseif ($display = $storage
->load($default_display_id)) {
return $display;
}
}
public function normalizeCanonicalUrl(InstantArticle $article, ContentEntityInterface $entity) {
$article
->withCanonicalURL($this
->entityCanonicalUrl($entity));
return $article;
}
public function normalizeDefaultHeader(InstantArticle $article, ContentEntityInterface $entity) {
$header = $article
->getHeader();
if (!$header) {
$header = Header::create();
$article
->withHeader($header);
}
if ($label = $entity
->label()) {
$header
->withTitle($label);
}
if ($created = $this
->entityCreatedTime($entity)) {
$header
->withPublishTime(Time::create(Time::PUBLISHED)
->withDatetime($created));
}
if ($changed = $this
->entityChangedTime($entity)) {
$header
->withModifyTime(Time::create(Time::MODIFIED)
->withDatetime($changed));
}
if ($author = $this
->entityAuthor($entity)) {
$header
->addAuthor(Author::create()
->withName($author));
}
return $article;
}
public function analyticsFromSettings(InstantArticle $article) {
if ($analytics_embed_code = $this->config
->get('analytics.embed_code')) {
$document = new \DOMDocument();
$fragment = $document
->createDocumentFragment();
$valid_html = @$fragment
->appendXML($analytics_embed_code);
if ($valid_html) {
$article
->addChild(Analytics::create()
->withHTML($fragment));
}
}
return $article;
}
protected function adsFromSettings(InstantArticle $article) {
$ads_type = $this->config
->get('ads.type');
if (!$ads_type || $ads_type === AdTypes::AD_TYPE_NONE) {
return $article;
}
$width = 300;
$height = 250;
$dimensions_match = [];
$dimensions_raw = $this->config
->get('ads.dimensions');
if (preg_match('/^(?:\\s)*(\\d+)x(\\d+)(?:\\s)*$/', $dimensions_raw, $dimensions_match)) {
$width = intval($dimensions_match[1]);
$height = intval($dimensions_match[2]);
}
$ad = Ad::create()
->enableDefaultForReuse()
->withWidth($width)
->withHeight($height);
$header = $article
->getHeader();
if (!$header) {
$header = Header::create();
$article
->withHeader($header);
}
switch ($ads_type) {
case AdTypes::AD_TYPE_FBAN:
$an_placement_id = $this->config
->get('ads.an_placement_id');
if ($an_placement_id) {
$ad
->withSource(Url::fromUri('https://www.facebook.com/adnw_request', [
'query' => [
'placement' => $an_placement_id,
'adtype' => 'banner' . $width . 'x' . $height,
],
])
->toString());
$header
->addAd($ad);
}
break;
case AdTypes::AD_TYPE_SOURCE_URL:
$iframe_url = $this->config
->get('ads.iframe_url');
if ($iframe_url) {
$ad
->withSource($iframe_url);
$header
->addAd($ad);
}
break;
case AdTypes::AD_TYPE_EMBED_CODE:
$embed_code = $this->config
->get('ads.embed_code');
if ($embed_code) {
$document = new \DOMDocument();
$fragment = $document
->createDocumentFragment();
$valid_html = @$fragment
->appendXML($embed_code);
if ($valid_html) {
$ad
->withHTML($fragment);
$header
->addAd($ad);
}
}
break;
}
$article
->enableAutomaticAdPlacement();
return $article;
}
protected function getApplicableComponents(EntityViewDisplay $display) {
$components = $display
->getComponents();
$field_definitions = $this->entityFieldManager
->getFieldDefinitions($display
->getTargetEntityTypeId(), $display
->getTargetBundle());
$fields_to_exclude = array_filter($field_definitions, function (FieldDefinitionInterface $field_definition) {
return !$field_definition
->isDisplayConfigurable('view');
});
$extra_fields = $this->entityFieldManager
->getExtraFields($display
->getTargetEntityTypeId(), $display
->getTargetBundle());
$extra_fields = isset($extra_fields['display']) ? $extra_fields['display'] : [];
return array_diff_key($components, $fields_to_exclude, $extra_fields);
}
public static function sortComponents(array $a, array $b) {
$regions = [
'header' => 0,
'content' => 1,
'footer' => 2,
];
$a_region = $a['region'];
$b_region = $b['region'];
$a_weight = isset($a['weight']) ? $a['weight'] : 0;
$b_weight = isset($b['weight']) ? $b['weight'] : 0;
if ($regions[$a_region] < $regions[$b_region]) {
return -1;
}
elseif ($regions[$a_region] > $regions[$b_region]) {
return 1;
}
else {
if ($a_weight == $b_weight) {
return 0;
}
return $a_weight < $b_weight ? -1 : 1;
}
}
protected function getApplicationVersion() {
$path = $this->moduleHandler
->getModule('fb_instant_articles')
->getPath();
$info = $this->infoParser
->parse($path . '/fb_instant_articles.info.yml');
if (isset($info['version'])) {
return $info['version'];
}
else {
return '8.x-2.x-dev';
}
}
}