in common/app/model/dotcomrendering/DotcomRenderingDataModel.scala [434:681]
def apply(
page: ContentPage,
request: RequestHeader,
pageType: PageType,
linkedData: List[LinkedData],
bodyBlocks: Seq[APIBlock],
mainBlock: Option[APIBlock] = None,
hasStoryPackage: Boolean = false,
storyPackage: Option[OnwardCollectionResponse] = None,
newsletter: Option[NewsletterData] = None,
keyEvents: Seq[APIBlock] = Seq.empty,
pagination: Option[Pagination] = None,
pinnedPost: Option[APIBlock] = None,
filterKeyEvents: Boolean = false,
mostRecentBlockId: Option[String] = None,
forceLive: Boolean = false,
crossword: Option[CrosswordData] = None,
): DotcomRenderingDataModel = {
val edition = Edition.edition(request)
val content = page.item
val isImmersive = content.metadata.format.exists(_.display == ImmersiveDisplay)
val isPaidContent = content.metadata.designType.contains(AdvertisementFeature)
/** @deprecated – Use byline instead */
val author: Author = Author(
byline = content.trail.byline,
twitterHandle = content.tags.contributors.headOption.flatMap(_.properties.twitterHandle),
)
def hasAffiliateLinks(
blocks: Seq[APIBlock],
): Boolean = {
blocks.exists(block => DotcomRenderingUtils.stringContainsAffiliateableLinks(block.bodyHtml))
}
val shouldAddAffiliateLinks = DotcomRenderingUtils.shouldAddAffiliateLinks(content)
val shouldAddDisclaimer = hasAffiliateLinks(bodyBlocks)
val contentDateTimes: ArticleDateTimes = ArticleDateTimes(
webPublicationDate = content.trail.webPublicationDate,
firstPublicationDate = content.fields.firstPublicationDate,
hasBeenModified = content.content.hasBeenModified,
lastModificationDate = content.fields.lastModified,
)
val switches: Map[String, Boolean] = conf.switches.Switches.all
.filter(_.exposeClientSide)
.foldLeft(Map.empty[String, Boolean])((acc, switch) => {
acc + (CamelCase.fromHyphenated(switch.name) -> switch.isSwitchedOn)
})
val config = Config(
switches = switches,
abTests = ActiveExperiments.getJsMap(request),
ampIframeUrl = assetURL("data/vendor/amp-iframe.html"),
googletagUrl = Configuration.googletag.jsLocation,
stage = common.Environment.stage,
frontendAssetsFullURL = Configuration.assets.fullURL(common.Environment.stage),
)
val combinedConfig: JsObject = {
val jsPageConfig: Map[String, JsValue] =
JavaScriptPage.getMap(page, Edition(request), pageType.isPreview, request)
Json.toJsObject(config).deepMerge(JsObject(jsPageConfig))
}
val calloutsUrl: Option[String] = combinedConfig.fields.toList
.find(_._1 == "calloutsUrl")
.flatMap(entry => entry._2.asOpt[String])
val dcrTags = content.tags.tags.map(Tag.apply)
val audioImageBlock: Option[ImageBlockElement] =
if (page.metadata.contentType.contains(DotcomContentType.Audio)) {
for {
thumbnail <- page.item.elements.thumbnail
} yield {
val imageData = thumbnail.images.allImages.headOption
.map { d =>
Map(
"copyright" -> "",
"alt" -> d.altText.getOrElse(""),
"caption" -> d.caption.getOrElse(""),
"credit" -> d.credit.getOrElse(""),
)
}
.getOrElse(Map.empty)
ImageBlockElement(
thumbnail.images,
imageData,
Some(true),
Role(Some("inline")),
Seq.empty,
)
}
} else {
None
}
def toDCRBlock(isMainBlock: Boolean = false) = { block: APIBlock =>
Block(
block = block,
page = page,
shouldAddAffiliateLinks = shouldAddAffiliateLinks,
request = request,
isMainBlock = isMainBlock,
calloutsUrl = calloutsUrl,
dateTimes = contentDateTimes,
tags = dcrTags,
)
}
val mainMediaElements = {
val pageElements = mainBlock
.map(toDCRBlock(isMainBlock = true))
.toList
.flatMap(_.elements)
page.metadata.contentType match {
case Some(DotcomContentType.Audio) =>
pageElements
.map {
case AudioBlockElement(assets, _) =>
AudioBlockElement(assets, content.elements.mainAudio.map(_.properties.id))
case pageElement => pageElement
}
case _ => pageElements
}
}
val bodyBlocksDCR =
bodyBlocks
.filter(_.published || pageType.isPreview) // TODO lift?
.map(toDCRBlock())
.toList
val keyEventsDCR = keyEvents.map(toDCRBlock())
val pinnedPostDCR = pinnedPost.map(toDCRBlock())
val commercial: Commercial = {
val editionCommercialProperties = content.metadata.commercial
.map { _.perEdition.mapKeys(_.id) }
.getOrElse(Map.empty[String, EditionCommercialProperties])
val prebidIndexSites = (for {
commercial <- content.metadata.commercial
sites <- commercial.prebidIndexSites
} yield sites.toList).getOrElse(List())
Commercial(
editionCommercialProperties,
prebidIndexSites,
content.metadata.commercial,
pageType,
)
}
val modifiedFormat = getModifiedContent(content, forceLive)
val isLegacyInteractive =
modifiedFormat.design == InteractiveDesign && content.trail.webPublicationDate
.isBefore(Chronos.javaTimeLocalDateTimeToJodaDateTime(InteractiveSwitchOver.date))
val matchData = makeMatchData(page)
def addAffiliateLinksDisclaimerDCR(shouldAddAffiliateLinks: Boolean, shouldAddDisclaimer: Boolean) = {
if (shouldAddAffiliateLinks && shouldAddDisclaimer) {
Some("true")
} else {
None
}
}
DotcomRenderingDataModel(
affiliateLinksDisclaimer = addAffiliateLinksDisclaimerDCR(shouldAddAffiliateLinks, shouldAddDisclaimer),
audioArticleImage = audioImageBlock,
author = author,
badge = Badges.badgeFor(content).map(badge => DCRBadge(badge.seriesTag, badge.imageUrl)),
beaconURL = Configuration.debug.beaconUrl,
blocks = bodyBlocksDCR,
byline = content.trail.byline,
commercialProperties = commercial.editionCommercialProperties,
config = combinedConfig,
contentType = content.metadata.contentType.map(_.name).getOrElse(""),
contributionsServiceUrl = Configuration.contributionsService.url,
designType = content.metadata.designType.map(_.toString).getOrElse("Article"),
editionId = edition.id,
editionLongForm = Edition(request).displayName,
format = modifiedFormat,
guardianBaseURL = Configuration.site.host,
hasRelated = content.content.showInRelated,
hasStoryPackage = hasStoryPackage,
storyPackage = storyPackage,
headline = content.trail.headline,
isAdFreeUser = views.support.Commercial.isAdFree(request),
isCommentable = content.trail.isCommentable,
isImmersive = isImmersive,
isLegacyInteractive = isLegacyInteractive,
isSpecialReport = isSpecialReport(page),
filterKeyEvents = filterKeyEvents,
pinnedPost = pinnedPostDCR,
keyEvents = keyEventsDCR.toList,
mostRecentBlockId = mostRecentBlockId,
linkedData = linkedData,
main = content.fields.main,
mainMediaElements = mainMediaElements,
matchUrl = matchData.map(_.matchUrl),
matchType = matchData.map(_.matchType),
nav = Nav(page, edition),
openGraphData = page.getOpenGraphProperties,
pageFooter = PageFooter(FooterLinks.getFooterByEdition(Edition(request))),
pageId = content.metadata.id,
canonicalUrl = CanonicalLink(request, content.metadata.webUrl),
pageType = pageType, // TODO this info duplicates what is already elsewhere in format?
pagination = pagination,
pillar = findPillar(content.metadata.pillar, content.metadata.designType),
publication = content.content.publication,
sectionLabel = Localisation(content.content.sectionLabelName.getOrElse(""))(request),
sectionName = content.metadata.section.map(_.value),
sectionUrl = content.content.sectionLabelLink.getOrElse(""),
shouldHideAds = content.content.shouldHideAdverts,
shouldHideReaderRevenue = content.fields.shouldHideReaderRevenue.getOrElse(isPaidContent),
showBottomSocialButtons = ContentLayout.showBottomSocialButtons(content),
slotMachineFlags = request.slotMachineFlags,
standfirst = TextCleaner.sanitiseLinks(edition)(content.fields.standfirst.getOrElse("")),
starRating = content.content.starRating,
subMetaKeywordLinks = content.content.submetaLinks.keywords.map(SubMetaLink.apply),
subMetaSectionLinks =
content.content.submetaLinks.sectionLabels.map(SubMetaLink.apply).filter(_.title.trim.nonEmpty),
tags = dcrTags,
trailText = TextCleaner.sanitiseLinks(edition)(content.trail.fields.trailText.getOrElse("")),
twitterData = page.getTwitterProperties,
version = 3,
webPublicationDate = content.trail.webPublicationDate.toString,
webPublicationDateDisplay =
GUDateTimeFormatNew.formatDateTimeForDisplay(content.trail.webPublicationDate, request),
webPublicationSecondaryDateDisplay = secondaryDateString(content, request),
webTitle = content.metadata.webTitle,
webURL = content.metadata.webUrl,
promotedNewsletter = newsletter,
showTableOfContents = content.fields.showTableOfContents.getOrElse(false),
lang = content.fields.lang,
isRightToLeftLang = content.fields.isRightToLeftLang,
crossword = crossword,
)
}