Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/repo/includes/ParserOutput/EntityParserOutputGenerator.php
<?php

namespace Wikibase\Repo\ParserOutput;

use InvalidArgumentException;
use ParserOutput;
use SpecialPage;
use Wikibase\DataModel\Entity\EntityDocument;
use Wikibase\DataModel\Entity\EntityId;
use Wikibase\DataModel\Term\AliasesProvider;
use Wikibase\DataModel\Term\DescriptionsProvider;
use Wikibase\DataModel\Term\LabelsProvider;
use Wikibase\LanguageFallbackChain;
use Wikibase\Lib\LanguageNameLookup;
use Wikibase\Lib\Store\EntityInfo;
use Wikibase\Lib\Store\EntityInfoBuilderFactory;
use Wikibase\Lib\Store\EntityInfoTermLookup;
use Wikibase\Lib\Store\EntityTitleLookup;
use Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookup;
use Wikibase\Repo\LinkedData\EntityDataFormatProvider;
use Wikibase\Repo\MediaWikiLanguageDirectionalityLookup;
use Wikibase\Repo\MediaWikiLocalizedTextProvider;
use Wikibase\Repo\View\RepoSpecialPageLinker;
use Wikibase\View\EmptyEditSectionGenerator;
use Wikibase\View\LocalizedTextProvider;
use Wikibase\View\Template\TemplateFactory;
use Wikibase\View\TermsListView;
use Wikibase\View\ToolbarEditSectionGenerator;

/**
 * Creates the parser output for an entity.
 *
 * @note This class relies on Entity and behaves differently when you pass an item as paramater.
 *       We should split this into classes for items and other types of entities.
 *
 * @since 0.5
 *
 * @license GPL-2.0+
 * @author Bene* < benestar.wikimedia@gmail.com >
 * @author Katie Filbert < aude.wiki@gmail.com >
 */
class EntityParserOutputGenerator {

	/**
	 * @var DispatchingEntityViewFactory
	 */
	private $entityViewFactory;

	/**
	 * @var ParserOutputJsConfigBuilder
	 */
	private $configBuilder;

	/**
	 * @var EntityTitleLookup
	 */
	private $entityTitleLookup;

	/**
	 * @var EntityInfoBuilderFactory
	 */
	private $entityInfoBuilderFactory;

	/**
	 * @var LanguageFallbackChain
	 */
	private $languageFallbackChain;

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

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

	/**
	 * @var EntityDataFormatProvider
	 */
	private $entityDataFormatProvider;

	/**
	 * @var ParserOutputDataUpdater[]
	 */
	private $dataUpdaters;

	/**
	 * @var string
	 */
	private $languageCode;

	/**
	 * @var bool
	 */
	private $editable;

	/**
	 * @param DispatchingEntityViewFactory $entityViewFactory
	 * @param ParserOutputJsConfigBuilder $configBuilder
	 * @param EntityTitleLookup $entityTitleLookup
	 * @param EntityInfoBuilderFactory $entityInfoBuilderFactory
	 * @param LanguageFallbackChain $languageFallbackChain
	 * @param TemplateFactory $templateFactory
	 * @param LocalizedTextProvider $textProvider
	 * @param EntityDataFormatProvider $entityDataFormatProvider
	 * @param ParserOutputDataUpdater[] $dataUpdaters
	 * @param string $languageCode
	 * @param bool $editable
	 */
	public function __construct(
		DispatchingEntityViewFactory $entityViewFactory,
		ParserOutputJsConfigBuilder $configBuilder,
		EntityTitleLookup $entityTitleLookup,
		EntityInfoBuilderFactory $entityInfoBuilderFactory,
		LanguageFallbackChain $languageFallbackChain,
		TemplateFactory $templateFactory,
		LocalizedTextProvider $textProvider,
		EntityDataFormatProvider $entityDataFormatProvider,
		array $dataUpdaters,
		$languageCode,
		$editable
	) {
		$this->entityViewFactory = $entityViewFactory;
		$this->configBuilder = $configBuilder;
		$this->entityTitleLookup = $entityTitleLookup;
		$this->entityInfoBuilderFactory = $entityInfoBuilderFactory;
		$this->languageFallbackChain = $languageFallbackChain;
		$this->templateFactory = $templateFactory;
		$this->textProvider = $textProvider;
		$this->entityDataFormatProvider = $entityDataFormatProvider;
		$this->dataUpdaters = $dataUpdaters;
		$this->languageCode = $languageCode;
		$this->editable = $editable;
	}

	/**
	 * Creates the parser output for the given entity revision.
	 *
	 * @since 0.5
	 *
	 * @param EntityDocument $entity
	 * @param bool $generateHtml
	 *
	 * @throws InvalidArgumentException
	 * @return ParserOutput
	 */
	public function getParserOutput(
		EntityDocument $entity,
		$generateHtml = true
	) {
		$parserOutput = new ParserOutput();

		$updater = new EntityParserOutputDataUpdater( $parserOutput, $this->dataUpdaters );
		$updater->processEntity( $entity );
		$updater->finish();

		$configVars = $this->configBuilder->build( $entity );
		$parserOutput->addJsConfigVars( $configVars );
		$parserOutput->setExtensionData( 'wikibase-titletext', $this->getTitleText( $entity ) );

		if ( $generateHtml ) {
			$this->addHtmlToParserOutput(
				$parserOutput,
				$entity,
				$this->getEntityInfo( $parserOutput )
			);
		} else {
			// If we don't have HTML, the ParserOutput in question
			// shouldn't be cacheable.
			$parserOutput->updateCacheExpiry( 0 );
		}

		//@todo: record sitelinks as iwlinks

		$this->addModules( $parserOutput );

		//FIXME: some places, like Special:NewItem, don't want to override the page title.
		//	 But we still want to use OutputPage::addParserOutput to apply the modules etc from the ParserOutput.
		//	 So, for now, we leave it to the caller to override the display title, if desired.
		// set the display title
		//$parserOutput->setTitleText( $entity>getLabel( $langCode ) );

		// Sometimes extensions like SpamBlacklist might call getParserOutput
		// before the id is assigned, during the process of creating a new entity.
		// in that case, no alternate links are added, which probably is no problem.
		if ( $entity->getId() !== null ) {
			$this->addAlternateLinks( $parserOutput, $entity->getId() );
		}

		return $parserOutput;
	}

	/**
	 * Fetches some basic entity information from a set of entity IDs.
	 *
	 * @param ParserOutput $parserOutput
	 *
	 * @return EntityInfo
	 */
	private function getEntityInfo( ParserOutput $parserOutput ) {
		/**
		 * Set in ReferencedEntitiesDataUpdater.
		 *
		 * @see ReferencedEntitiesDataUpdater::updateParserOutput
		 * @fixme Use ReferencedEntitiesDataUpdater::getEntityIds instead.
		 */
		$entityIds = $parserOutput->getExtensionData( 'referenced-entities' );

		if ( !is_array( $entityIds ) ) {
			wfLogWarning( '$entityIds from ParserOutput "referenced-entities" extension data'
				. ' expected to be an array' );
			$entityIds = array();
		}

		$entityInfoBuilder = $this->entityInfoBuilderFactory->newEntityInfoBuilder( $entityIds );

		$entityInfoBuilder->resolveRedirects();
		$entityInfoBuilder->removeMissing();

		$entityInfoBuilder->collectTerms(
			array( 'label', 'description' ),
			$this->languageFallbackChain->getFetchLanguageCodes()
		);

		$entityInfoBuilder->collectDataTypes();
		$entityInfoBuilder->retainEntityInfo( $entityIds );

		return $entityInfoBuilder->getEntityInfo();
	}

	/**
	 * @param EntityDocument $entity
	 *
	 * @return string
	 */
	private function getTitleText( EntityDocument $entity ) {
		$titleText = null;

		if ( $entity instanceof LabelsProvider ) {
			$labels = $entity->getLabels()->toTextArray();
			$preferred = $this->languageFallbackChain->extractPreferredValue( $labels );

			if ( is_array( $preferred ) ) {
				$titleText = $preferred['value'];
			}
		}

		if ( !is_string( $titleText ) ) {
			$entityId = $entity->getId();

			if ( $entityId instanceof EntityId ) {
				$titleText = $entityId->getSerialization();
			}
		}

		return $titleText;
	}

	/**
	 * @param ParserOutput $parserOutput
	 * @param EntityDocument $entity
	 * @param EntityInfo $entityInfo
	 */
	private function addHtmlToParserOutput(
		ParserOutput $parserOutput,
		EntityDocument $entity,
		EntityInfo $entityInfo
	) {
		$labelDescriptionLookup = new LanguageFallbackLabelDescriptionLookup(
			new EntityInfoTermLookup( $entityInfo ),
			$this->languageFallbackChain
		);

		$editSectionGenerator = $this->editable ? new ToolbarEditSectionGenerator(
			new RepoSpecialPageLinker(),
			$this->templateFactory,
			$this->textProvider
		) : new EmptyEditSectionGenerator();

		$entityView = $this->entityViewFactory->newEntityView(
			$entity->getType(),
			$this->languageCode,
			$labelDescriptionLookup,
			$this->languageFallbackChain,
			$editSectionGenerator
		);

		// Set the display title to display the label together with the item's id
		$titleHtml = $entityView->getTitleHtml( $entity );
		$parserOutput->setTitleText( $titleHtml );

		$html = $entityView->getHtml( $entity );
		$parserOutput->setText( $html );
		$parserOutput->setExtensionData( 'wikibase-view-chunks', $entityView->getPlaceholders() );

		$parserOutput->setExtensionData( 'wikibase-terms-list-items', $this->getTermsListItems( $entity ) );
	}

	private function getTermsListItems( EntityDocument $entity ) {
		$termsListView = new TermsListView(
			TemplateFactory::getDefaultInstance(),
			new LanguageNameLookup( $this->languageCode ),
			new MediaWikiLocalizedTextProvider( $this->languageCode ),
			new MediaWikiLanguageDirectionalityLookup()
		);
		$allLanguages = [];
		if ( $entity instanceof AliasesProvider ) {
			$allLanguages += array_keys( $entity->getAliasGroups()->toTextArray() );
		}
		if ( $entity instanceof DescriptionsProvider ) {
			$allLanguages += array_keys( $entity->getDescriptions()->toTextArray() );
		}
		if ( $entity instanceof LabelsProvider ) {
			$allLanguages += array_keys( $entity->getLabels()->toTextArray() );
		}
		$allLanguages = array_unique( $allLanguages );

		return array_map(
			function( $languageCode ) use( $termsListView, $entity ) {
				return $termsListView->getListItemHtml(
					$entity,
					$entity,
					$entity instanceof AliasesProvider ? $entity : null,
					$languageCode
				);
			},
			$allLanguages
		);
	}

	/**
	 * @param ParserOutput $parserOutput
	 */
	private function addModules( ParserOutput $parserOutput ) {
		// make css available for JavaScript-less browsers
		$parserOutput->addModuleStyles( array(
			'wikibase.common',
			'jquery.ui.core.styles',
			'jquery.wikibase.statementview.RankSelector.styles',
			'jquery.wikibase.toolbar.styles',
			'jquery.wikibase.toolbarbutton.styles',
		) );

		// make sure required client-side resources will be loaded
		// FIXME: Separate the JavaScript that is also needed in read-only mode from
		// the JavaScript that is only necessary for editing.
		// Then load JavaScript accordingly depending on $editable.
		$parserOutput->addModules( 'wikibase.ui.entityViewInit' );

		// Load mobile styles, which have targets => mobile
		// and will only be loaded on mobile devices
		$parserOutput->addModules( 'wikibase.mobile' );
	}

	/**
	 * Add alternate links as extension data.
	 * OutputPageBeforeHTMLHookHandler will add these to the OutputPage.
	 *
	 * @param ParserOutput $parserOutput
	 * @param EntityId $entityId
	 */
	private function addAlternateLinks( ParserOutput $parserOutput, EntityId $entityId ) {
		$entityDataFormatProvider = $this->entityDataFormatProvider;
		$subPagePrefix = $entityId->getSerialization() . '.';

		$links = array();

		foreach ( $entityDataFormatProvider->getSupportedFormats() as $format ) {
			$ext = $entityDataFormatProvider->getExtension( $format );

			if ( $ext !== null ) {
				$entityDataTitle = SpecialPage::getTitleFor( 'EntityData', $subPagePrefix . $ext );

				$links[] = array(
					'rel' => 'alternate',
					'href' => $entityDataTitle->getCanonicalURL(),
					'type' => $entityDataFormatProvider->getMimeType( $format )
				);
			}
		}

		$parserOutput->setExtensionData( 'wikibase-alternate-links', $links );
	}

}