def apply()

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,
    )
  }