wikimedia/mediawiki-extensions-Wikibase

View on GitHub
view/src/SiteLinksView.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace Wikibase\View;

use MediaWiki\Language\LanguageCode;
use MediaWiki\Parser\Sanitizer;
use MediaWiki\Site\Site;
use MediaWiki\Site\SiteList;
use ValueFormatters\NumberLocalizer;
use Wikibase\DataModel\Entity\ItemId;
use Wikibase\DataModel\Services\EntityId\EntityIdFormatter;
use Wikibase\DataModel\SiteLink;
use Wikibase\Lib\LanguageNameLookup;
use Wikibase\View\Template\TemplateFactory;

/**
 * Creates views for lists of site links.
 *
 * @license GPL-2.0-or-later
 * @author Adrian Heine <adrian.heine@wikimedia.de>
 * @author Bene* < benestar.wikimedia@gmail.com >
 */
class SiteLinksView {

    /**
     * @var EntityIdFormatter
     */
    private $entityIdFormatter;

    /**
     * @var TemplateFactory
     */
    private $templateFactory;

    /**
     * @var SiteList
     */
    private $sites;

    /**
     * @var EditSectionGenerator
     */
    private $sectionEditLinkGenerator;

    /**
     * @var LanguageNameLookup
     */
    private $languageNameLookup;

    /**
     * @var NumberLocalizer
     */
    private $numberLocalizer;

    /**
     * @var string[]
     */
    private $badgeItems;

    /**
     * @var string[]
     */
    private $specialSiteLinkGroups;

    /**
     * @var LocalizedTextProvider
     */
    private $textProvider;

    /**
     * @param TemplateFactory $templateFactory
     * @param SiteList $sites
     * @param EditSectionGenerator $sectionEditLinkGenerator
     * @param EntityIdFormatter $entityIdFormatter A plaintext producing EntityIdFormatter
     * @param LanguageNameLookup $languageNameLookup
     * @param NumberLocalizer $numberLocalizer
     * @param string[] $badgeItems
     * @param string[] $specialSiteLinkGroups
     * @param LocalizedTextProvider $textProvider
     */
    public function __construct(
        TemplateFactory $templateFactory,
        SiteList $sites,
        EditSectionGenerator $sectionEditLinkGenerator,
        EntityIdFormatter $entityIdFormatter,
        LanguageNameLookup $languageNameLookup,
        NumberLocalizer $numberLocalizer,
        array $badgeItems,
        array $specialSiteLinkGroups,
        LocalizedTextProvider $textProvider
    ) {
        $this->templateFactory = $templateFactory;
        $this->sites = $sites;
        $this->sectionEditLinkGenerator = $sectionEditLinkGenerator;
        $this->entityIdFormatter = $entityIdFormatter;
        $this->languageNameLookup = $languageNameLookup;
        $this->numberLocalizer = $numberLocalizer;
        $this->badgeItems = $badgeItems;
        $this->specialSiteLinkGroups = $specialSiteLinkGroups;
        $this->textProvider = $textProvider;
    }

    /**
     * Builds and returns the HTML representing a WikibaseEntity's site-links.
     *
     * @param SiteLink[] $siteLinks the site links to render
     * @param ItemId|null $itemId The id of the item or might be null, if a new item.
     * @param string[] $groups An array of site group IDs
     *
     * @return string HTML
     */
    public function getHtml( array $siteLinks, ?ItemId $itemId, array $groups ) {
        $html = '';

        if ( !$groups ) {
            return $html;
        }

        foreach ( $groups as $group ) {
            $html .= $this->getHtmlForSiteLinkGroup( $siteLinks, $itemId, $group );
        }

        $html = $this->templateFactory->render( 'wikibase-sitelinkgrouplistview',
            $this->templateFactory->render( 'wikibase-listview', $html )
        );

        $sectionHeading = $this->getHtmlForSectionHeading( 'wikibase-sitelinks' );

        return $sectionHeading . $html;
    }

    /**
     * Returns the HTML for the heading of the sitelinks section
     *
     * @param string $heading message key of the heading; also used as css class
     *
     * @return string HTML
     */
    private function getHtmlForSectionHeading( $heading ) {
        $html = $this->templateFactory->render(
            'wb-section-heading',
            $this->textProvider->getEscaped( $heading ),
            'sitelinks', // ID - TODO: should not be added if output page is not the entity's page
            $heading
        );

        return $html;
    }

    /**
     * Builds and returns the HTML representing a group of a WikibaseEntity's site-links.
     *
     * @param SiteLink[] $siteLinks the site links to render
     * @param ItemId|null $itemId The id of the item
     * @param string $group a site group ID
     *
     * @return string HTML
     */
    private function getHtmlForSiteLinkGroup( array $siteLinks, ?ItemId $itemId, $group ) {
        $siteLinksForTable = $this->getSiteLinksForTable(
            $this->getSitesForGroup( $group ),
            $siteLinks
        );

        $count = count( $siteLinksForTable );

        return $this->templateFactory->render(
            'wikibase-sitelinkgroupview',
            // TODO: support entity-id as prefix for element IDs.
            htmlspecialchars( 'sitelinks-' . $group, ENT_QUOTES ),
            $this->textProvider->getEscaped( 'wikibase-sitelinks-' . $group ),
            $this->textProvider->getEscaped( 'parentheses', [
                $this->textProvider->get(
                    'wikibase-sitelinks-counter',
                    [
                        $this->numberLocalizer->localizeNumber( $count ),
                    ]
                ),
            ] ),
            $this->templateFactory->render(
                'wikibase-sitelinklistview',
                $this->getHtmlForSiteLinks( $siteLinksForTable, $group === 'special' )
            ),
            htmlspecialchars( $group ),
            $this->sectionEditLinkGenerator->getSiteLinksEditSection( $itemId ),
            $count > 1 ? ' mw-collapsible' : ''
        );
    }

    /**
     * Get all sites for a given site group, with special handling for the
     * "special" site group.
     *
     * @param string $group
     *
     * @return SiteList
     */
    private function getSitesForGroup( $group ) {
        $siteList = new SiteList();

        if ( $group === 'special' ) {
            $groups = $this->specialSiteLinkGroups;
        } else {
            $groups = [ $group ];
        }

        foreach ( $groups as $group ) {
            $sites = $this->sites->getGroup( $group );
            foreach ( $sites as $site ) {
                $siteList->setSite( $site );
            }
        }

        return $siteList;
    }

    /**
     * @param SiteList $sites
     * @param SiteLink[] $itemSiteLinks
     *
     * @return array[]
     */
    private function getSiteLinksForTable( SiteList $sites, array $itemSiteLinks ) {
        $siteLinksForTable = []; // site links of the currently handled site group

        foreach ( $itemSiteLinks as $siteLink ) {
            if ( !$sites->hasSite( $siteLink->getSiteId() ) ) {
                // FIXME: Maybe show it instead
                continue;
            }

            $site = $sites->getSite( $siteLink->getSiteId() );

            $siteLinksForTable[] = [
                'siteLink' => $siteLink,
                'site' => $site,
            ];
        }

        // Sort the sitelinks according to their global id
        usort(
            $siteLinksForTable,
            function( array $a, array $b ) {
                /** @var SiteLink[] $a */
                /** @var SiteLink[] $b */
                return strcmp( $a['siteLink']->getSiteId(), $b['siteLink']->getSiteId() );
            }
        );

        return $siteLinksForTable;
    }

    /**
     * @param array[] $siteLinksForTable
     * @param bool $isSpecialGroup
     *
     * @return string HTML
     */
    private function getHtmlForSiteLinks( array $siteLinksForTable, $isSpecialGroup ) {
        $html = '';

        foreach ( $siteLinksForTable as $siteLinkForTable ) {
            $html .= $this->getHtmlForSiteLink( $siteLinkForTable, $isSpecialGroup );
        }

        return $html;
    }

    /**
     * @param array $siteLinkForTable
     * @param bool $isSpecialGroup
     *
     * @return string HTML
     */
    private function getHtmlForSiteLink( array $siteLinkForTable, $isSpecialGroup ) {
        /** @var Site $site */
        $site = $siteLinkForTable['site'];

        /** @var SiteLink $siteLink */
        $siteLink = $siteLinkForTable['siteLink'];

        if ( $site->getDomain() === '' ) {
            return $this->getHtmlForUnknownSiteLink( $siteLink );
        }

        $languageCode = $site->getLanguageCode();
        $siteId = $siteLink->getSiteId();

        // FIXME: this is a quickfix to allow a custom site-name for the site groups which are
        // special according to the specialSiteLinkGroups setting
        if ( $isSpecialGroup ) {
            $siteNameMsg = 'wikibase-sitelinks-sitename-' . $siteId;
            $siteName = $this->textProvider->has( $siteNameMsg ) ? $this->textProvider->get( $siteNameMsg ) : $siteId;
        } else {
            // TODO: get an actual site name rather then just the language
            $siteName = $this->languageNameLookup->getName( $languageCode );
        }

        return $this->templateFactory->render( 'wikibase-sitelinkview',
            htmlspecialchars( $siteId ), // ID used in classes
            htmlspecialchars( $siteId ), // displayed site ID
            htmlspecialchars( $siteName ),
            $this->getHtmlForPage( $siteLink, $site )
        );
    }

    /**
     * @param SiteLink $siteLink
     * @param Site $site
     *
     * @return string HTML
     */
    private function getHtmlForPage( SiteLink $siteLink, Site $site ) {
        $pageName = $siteLink->getPageName();

        return $this->templateFactory->render( 'wikibase-sitelinkview-pagename',
            htmlspecialchars( $site->getPageUrl( $pageName ) ),
            htmlspecialchars( $pageName ),
            $this->getHtmlForBadges( $siteLink->getBadges() ),
            htmlspecialchars( LanguageCode::bcp47( $site->getLanguageCode() ) ),
            'auto'
        );
    }

    /**
     * @param SiteLink $siteLink
     *
     * @return string HTML
     */
    private function getHtmlForUnknownSiteLink( SiteLink $siteLink ) {
        // FIXME: No need for separate template; Use 'wikibase-sitelinkview' template.
        return $this->templateFactory->render( 'wikibase-sitelinkview-unknown',
            htmlspecialchars( $siteLink->getSiteId() ),
            htmlspecialchars( $siteLink->getPageName() )
        );
    }

    /**
     * @param ItemId[] $badges
     *
     * @return string HTML
     */
    private function getHtmlForBadges( array $badges ) {
        $html = '';

        foreach ( $badges as $badge ) {
            $serialization = $badge->getSerialization();
            $classes = Sanitizer::escapeClass( $serialization );
            if ( !empty( $this->badgeItems[$serialization] ) ) {
                $classes .= ' ' . Sanitizer::escapeClass( $this->badgeItems[$serialization] );
            }

            $html .= $this->templateFactory->render( 'wb-badge',
                $classes,
                htmlspecialchars( $this->entityIdFormatter->formatEntityId( $badge ) ),
                $badge
            );
        }

        return $this->templateFactory->render( 'wikibase-badgeselector', $html );
    }

}