2015-04-27 14:05:31 +00:00
|
|
|
<?php
|
2021-06-02 16:52:59 +00:00
|
|
|
|
2018-08-09 09:49:10 +00:00
|
|
|
namespace PortableInfobox\Parser\Nodes;
|
2015-04-27 14:05:31 +00:00
|
|
|
|
2022-03-11 20:35:51 +00:00
|
|
|
use DOMDocument;
|
|
|
|
use DOMXpath;
|
|
|
|
use File;
|
2021-06-02 16:52:59 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2022-03-11 20:35:51 +00:00
|
|
|
use Parser;
|
2018-08-09 09:49:10 +00:00
|
|
|
use PortableInfobox\Helpers\FileNamespaceSanitizeHelper;
|
|
|
|
use PortableInfobox\Helpers\PortableInfoboxDataBag;
|
2018-08-22 14:20:49 +00:00
|
|
|
use PortableInfobox\Helpers\PortableInfoboxImagesHelper;
|
2022-03-11 20:35:51 +00:00
|
|
|
use PortableInfoboxRenderService;
|
|
|
|
use Title;
|
2015-05-07 12:37:13 +00:00
|
|
|
|
2018-08-12 09:45:29 +00:00
|
|
|
class NodeMedia extends Node {
|
2021-09-10 02:52:19 +00:00
|
|
|
private const GALLERY = 'GALLERY';
|
|
|
|
private const TABBER = 'TABBER';
|
2017-01-25 11:18:20 +00:00
|
|
|
|
2021-09-10 02:52:19 +00:00
|
|
|
private const ALLOWIMAGE_ATTR_NAME = 'image';
|
|
|
|
private const ALLOWVIDEO_ATTR_NAME = 'video';
|
|
|
|
private const ALLOWAUDIO_ATTR_NAME = 'audio';
|
2018-08-12 09:45:29 +00:00
|
|
|
|
2021-09-10 02:52:19 +00:00
|
|
|
private const ALT_TAG_NAME = 'alt';
|
|
|
|
private const CAPTION_TAG_NAME = 'caption';
|
2015-04-27 14:05:31 +00:00
|
|
|
|
2018-08-22 14:20:49 +00:00
|
|
|
private $helper;
|
|
|
|
|
2015-10-20 22:42:36 +00:00
|
|
|
public static function getMarkers( $value, $ext ) {
|
2022-03-11 20:35:51 +00:00
|
|
|
$regex = '/' . Parser::MARKER_PREFIX . "-$ext-[A-F0-9]{8}" . Parser::MARKER_SUFFIX . '/i';
|
2023-01-13 21:08:52 +00:00
|
|
|
if ( preg_match_all( $regex, $value ?? '', $out ) ) {
|
2015-10-20 22:42:36 +00:00
|
|
|
return $out[0];
|
|
|
|
} else {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-07 14:02:51 +00:00
|
|
|
public static function getGalleryData( $marker ) {
|
|
|
|
$gallery = PortableInfoboxDataBag::getInstance()->getGallery( $marker );
|
2021-09-10 02:52:19 +00:00
|
|
|
return isset( $gallery ) ? array_map( static function ( $image ) {
|
2018-08-07 14:02:51 +00:00
|
|
|
return [
|
2018-08-22 14:20:49 +00:00
|
|
|
'label' => $image[1],
|
2018-08-07 14:02:51 +00:00
|
|
|
'title' => $image[0]
|
|
|
|
];
|
|
|
|
}, $gallery->getimages() ) : [];
|
2015-10-20 22:42:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static function getTabberData( $html ) {
|
2018-08-04 17:12:35 +00:00
|
|
|
$data = [];
|
2018-10-07 14:44:55 +00:00
|
|
|
|
2022-03-11 20:35:51 +00:00
|
|
|
$doc = new DOMDocument();
|
2018-10-07 14:44:55 +00:00
|
|
|
$libXmlErrorSetting = libxml_use_internal_errors( true );
|
|
|
|
|
|
|
|
// encode for correct load
|
|
|
|
$doc->loadHTML( mb_convert_encoding( $html, 'HTML-ENTITIES', 'UTF-8' ) );
|
|
|
|
|
|
|
|
libxml_clear_errors();
|
|
|
|
libxml_use_internal_errors( $libXmlErrorSetting );
|
|
|
|
|
2022-03-11 20:35:51 +00:00
|
|
|
$xpath = new DOMXpath( $doc );
|
2018-10-07 14:44:55 +00:00
|
|
|
$divs = $xpath->query( '//div[@class=\'tabbertab\']' );
|
2015-10-20 22:42:36 +00:00
|
|
|
foreach ( $divs as $div ) {
|
2019-02-24 23:06:20 +00:00
|
|
|
if ( preg_match( '# src="(?:[^"]*/)?(?:\d+px-)?([^"]*?)"#', $doc->saveXml( $div ), $out ) ) {
|
2018-08-04 17:12:35 +00:00
|
|
|
$data[] = [
|
2018-10-07 14:44:55 +00:00
|
|
|
'label' => $div->getAttribute( 'title' ),
|
2018-08-04 17:12:35 +00:00
|
|
|
'title' => $out[1]
|
|
|
|
];
|
2015-10-20 22:42:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
2015-04-27 14:05:31 +00:00
|
|
|
public function getData() {
|
2015-06-11 09:34:28 +00:00
|
|
|
if ( !isset( $this->data ) ) {
|
2018-08-04 17:12:35 +00:00
|
|
|
$this->data = [];
|
2015-06-19 10:31:22 +00:00
|
|
|
|
2015-10-16 01:15:52 +00:00
|
|
|
// value passed to source parameter (or default)
|
|
|
|
$value = $this->getRawValueWithDefault( $this->xmlNode );
|
2018-08-22 14:20:49 +00:00
|
|
|
$helper = $this->getImageHelper();
|
2015-06-19 10:31:22 +00:00
|
|
|
|
2015-10-16 01:15:52 +00:00
|
|
|
if ( $this->containsTabberOrGallery( $value ) ) {
|
|
|
|
$this->data = $this->getImagesData( $value );
|
|
|
|
} else {
|
2018-08-04 17:12:35 +00:00
|
|
|
$this->data = [ $this->getImageData(
|
2015-10-16 01:15:52 +00:00
|
|
|
$value,
|
|
|
|
$this->getValueWithDefault( $this->xmlNode->{self::ALT_TAG_NAME} ),
|
|
|
|
$this->getValueWithDefault( $this->xmlNode->{self::CAPTION_TAG_NAME} )
|
2018-08-04 17:12:35 +00:00
|
|
|
) ];
|
2015-08-31 11:53:06 +00:00
|
|
|
}
|
2015-10-16 01:15:52 +00:00
|
|
|
}
|
|
|
|
return $this->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-10-02 07:41:19 +00:00
|
|
|
* Checks if parser preprocessed string containg Tabber or Gallery extension
|
2015-10-16 01:15:52 +00:00
|
|
|
* @param string $str String to check
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function containsTabberOrGallery( $str ) {
|
2018-10-02 07:41:19 +00:00
|
|
|
return !empty( self::getMarkers( $str, self::TABBER ) ) ||
|
|
|
|
!empty( self::getMarkers( $str, self::GALLERY ) );
|
2015-10-16 01:15:52 +00:00
|
|
|
}
|
|
|
|
|
2015-10-20 01:19:49 +00:00
|
|
|
private function getImagesData( $value ) {
|
2018-08-22 14:20:49 +00:00
|
|
|
$helper = $this->getImageHelper();
|
2018-08-11 07:39:21 +00:00
|
|
|
$data = [];
|
2015-10-20 01:19:49 +00:00
|
|
|
$items = array_merge( $this->getGalleryItems( $value ), $this->getTabberItems( $value ) );
|
2018-08-16 09:25:53 +00:00
|
|
|
foreach ( $items as $item ) {
|
2018-08-22 14:20:49 +00:00
|
|
|
$mediaItem = $this->getImageData( $item['title'], $item['label'], $item['label'] );
|
2021-09-10 02:52:19 +00:00
|
|
|
if ( (bool)$mediaItem ) {
|
2018-08-22 14:20:49 +00:00
|
|
|
$data[] = $mediaItem;
|
|
|
|
}
|
2015-10-16 01:15:52 +00:00
|
|
|
}
|
2018-08-22 14:20:49 +00:00
|
|
|
return count( $data ) > 1 ? $helper->extendImageCollectionData( $data ) : $data;
|
2015-10-19 23:56:20 +00:00
|
|
|
}
|
2015-10-16 01:15:52 +00:00
|
|
|
|
2015-10-20 01:19:49 +00:00
|
|
|
private function getGalleryItems( $value ) {
|
2017-01-25 11:18:20 +00:00
|
|
|
$galleryItems = [];
|
|
|
|
$galleryMarkers = self::getMarkers( $value, self::GALLERY );
|
|
|
|
foreach ( $galleryMarkers as $marker ) {
|
2018-08-07 14:02:51 +00:00
|
|
|
$galleryItems = array_merge( $galleryItems, self::getGalleryData( $marker ) );
|
2015-06-11 09:34:28 +00:00
|
|
|
}
|
2015-10-20 01:19:49 +00:00
|
|
|
return $galleryItems;
|
2015-10-16 01:15:52 +00:00
|
|
|
}
|
2015-06-10 09:28:33 +00:00
|
|
|
|
2015-10-20 23:09:23 +00:00
|
|
|
private function getTabberItems( $value ) {
|
2018-08-11 07:39:21 +00:00
|
|
|
$tabberItems = [];
|
2017-01-25 11:18:20 +00:00
|
|
|
$tabberMarkers = self::getMarkers( $value, self::TABBER );
|
|
|
|
foreach ( $tabberMarkers as $marker ) {
|
|
|
|
$tabberHtml = $this->getExternalParser()->parseRecursive( $marker );
|
2015-10-20 22:42:36 +00:00
|
|
|
$tabberItems = array_merge( $tabberItems, self::getTabberData( $tabberHtml ) );
|
2015-10-19 23:56:20 +00:00
|
|
|
}
|
2015-10-20 01:19:49 +00:00
|
|
|
return $tabberItems;
|
|
|
|
}
|
|
|
|
|
2015-12-23 10:20:39 +00:00
|
|
|
/**
|
2018-10-02 07:41:19 +00:00
|
|
|
* Prepare infobox image node data.
|
2015-12-23 12:20:55 +00:00
|
|
|
*
|
2015-12-23 10:20:39 +00:00
|
|
|
* @param $title
|
|
|
|
* @param $alt
|
|
|
|
* @param $caption
|
|
|
|
* @return array
|
|
|
|
*/
|
2015-10-16 01:15:52 +00:00
|
|
|
private function getImageData( $title, $alt, $caption ) {
|
2018-08-22 14:20:49 +00:00
|
|
|
$helper = $this->getImageHelper();
|
2022-03-11 20:35:51 +00:00
|
|
|
$titleObj = $title instanceof Title ? $title : $this->getImageAsTitleObject( $title );
|
2018-08-22 14:20:49 +00:00
|
|
|
$fileObj = $helper->getFile( $titleObj );
|
2015-10-16 01:15:52 +00:00
|
|
|
|
2018-08-16 09:25:53 +00:00
|
|
|
if ( !isset( $fileObj ) || !$this->isTypeAllowed( $fileObj->getMediaType() ) ) {
|
2018-08-12 09:45:29 +00:00
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2022-03-11 20:35:51 +00:00
|
|
|
if ( $titleObj instanceof Title ) {
|
2018-12-26 22:36:08 +00:00
|
|
|
$this->getExternalParser()->addImage( $titleObj );
|
2015-10-16 01:15:52 +00:00
|
|
|
}
|
|
|
|
|
2018-08-22 14:20:49 +00:00
|
|
|
$mediatype = $fileObj->getMediaType();
|
2015-10-16 01:15:52 +00:00
|
|
|
$image = [
|
|
|
|
'url' => $this->resolveImageUrl( $fileObj ),
|
|
|
|
'name' => $titleObj ? $titleObj->getText() : '',
|
2018-08-08 09:42:22 +00:00
|
|
|
'alt' => $alt ?? ( $titleObj ? $titleObj->getText() : null ),
|
2018-09-03 15:59:15 +00:00
|
|
|
'caption' => $caption ?: null,
|
2018-08-22 14:20:49 +00:00
|
|
|
'isImage' => in_array( $mediatype, [ MEDIATYPE_BITMAP, MEDIATYPE_DRAWING ] ),
|
|
|
|
'isVideo' => $mediatype === MEDIATYPE_VIDEO,
|
2018-12-27 01:01:19 +00:00
|
|
|
'isAudio' => $mediatype === MEDIATYPE_AUDIO,
|
2019-02-02 22:34:48 +00:00
|
|
|
'source' => $this->getPrimarySource(),
|
|
|
|
'item-name' => $this->getItemName()
|
2015-10-16 01:15:52 +00:00
|
|
|
];
|
|
|
|
|
2018-08-22 14:20:49 +00:00
|
|
|
if ( $image['isImage'] ) {
|
|
|
|
$image = array_merge( $image, $helper->extendImageData(
|
|
|
|
$fileObj,
|
2022-03-11 20:35:51 +00:00
|
|
|
PortableInfoboxRenderService::DEFAULT_DESKTOP_THUMBNAIL_WIDTH,
|
|
|
|
PortableInfoboxRenderService::DEFAULT_DESKTOP_INFOBOX_WIDTH
|
2018-08-22 14:20:49 +00:00
|
|
|
) );
|
|
|
|
}
|
|
|
|
|
2015-10-16 01:15:52 +00:00
|
|
|
return $image;
|
2015-04-27 14:05:31 +00:00
|
|
|
}
|
|
|
|
|
2015-06-10 09:28:33 +00:00
|
|
|
public function isEmpty() {
|
2015-06-11 09:34:28 +00:00
|
|
|
$data = $this->getData();
|
2015-10-16 01:15:52 +00:00
|
|
|
foreach ( $data as $dataItem ) {
|
2018-08-16 09:25:53 +00:00
|
|
|
if ( !empty( $dataItem['url'] ) ) {
|
2015-10-16 01:15:52 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2015-05-06 14:11:04 +00:00
|
|
|
}
|
|
|
|
|
2016-12-09 13:01:49 +00:00
|
|
|
public function getSources() {
|
|
|
|
$sources = $this->extractSourcesFromNode( $this->xmlNode );
|
2015-06-11 09:48:32 +00:00
|
|
|
if ( $this->xmlNode->{self::ALT_TAG_NAME} ) {
|
|
|
|
$sources = array_merge( $sources,
|
2016-12-09 13:01:49 +00:00
|
|
|
$this->extractSourcesFromNode( $this->xmlNode->{self::ALT_TAG_NAME} ) );
|
2015-06-11 09:48:32 +00:00
|
|
|
}
|
|
|
|
if ( $this->xmlNode->{self::CAPTION_TAG_NAME} ) {
|
|
|
|
$sources = array_merge( $sources,
|
2016-12-09 13:01:49 +00:00
|
|
|
$this->extractSourcesFromNode( $this->xmlNode->{self::CAPTION_TAG_NAME} ) );
|
2015-06-11 09:48:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return array_unique( $sources );
|
|
|
|
}
|
|
|
|
|
2015-05-11 12:36:23 +00:00
|
|
|
private function getImageAsTitleObject( $imageName ) {
|
2021-06-02 16:52:59 +00:00
|
|
|
$contLang = MediaWikiServices::getInstance()->getContentLanguage();
|
|
|
|
|
2022-03-11 20:35:51 +00:00
|
|
|
$title = Title::makeTitleSafe(
|
2015-08-31 11:53:06 +00:00
|
|
|
NS_FILE,
|
2021-06-02 16:52:59 +00:00
|
|
|
FileNamespaceSanitizeHelper::getInstance()->sanitizeImageFileName( $imageName, $contLang )
|
2015-05-07 12:37:13 +00:00
|
|
|
);
|
2015-06-03 14:53:31 +00:00
|
|
|
|
2015-05-11 12:36:23 +00:00
|
|
|
return $title;
|
|
|
|
}
|
|
|
|
|
2018-08-22 14:20:49 +00:00
|
|
|
protected function getImageHelper() {
|
|
|
|
if ( !isset( $this->helper ) ) {
|
|
|
|
$this->helper = new PortableInfoboxImagesHelper();
|
2018-08-09 09:49:10 +00:00
|
|
|
}
|
2018-08-22 14:20:49 +00:00
|
|
|
return $this->helper;
|
2015-07-21 14:29:14 +00:00
|
|
|
}
|
|
|
|
|
2015-05-22 15:27:20 +00:00
|
|
|
/**
|
2018-10-02 07:41:19 +00:00
|
|
|
* Returns image url for given image title
|
2022-03-11 20:35:51 +00:00
|
|
|
* @param File|null $file
|
2015-05-22 15:27:20 +00:00
|
|
|
* @return string url or '' if image doesn't exist
|
|
|
|
*/
|
2015-07-21 14:29:14 +00:00
|
|
|
public function resolveImageUrl( $file ) {
|
|
|
|
return $file ? $file->getUrl() : '';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-10-02 07:41:19 +00:00
|
|
|
* Checks if file media type is allowed
|
2018-08-12 09:45:29 +00:00
|
|
|
* @param string $type
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function isTypeAllowed( $type ) {
|
2018-08-16 09:25:53 +00:00
|
|
|
switch ( $type ) {
|
2018-08-12 09:45:29 +00:00
|
|
|
case MEDIATYPE_BITMAP:
|
|
|
|
case MEDIATYPE_DRAWING:
|
|
|
|
return $this->allowImage();
|
|
|
|
case MEDIATYPE_VIDEO:
|
|
|
|
return $this->allowVideo();
|
|
|
|
case MEDIATYPE_AUDIO:
|
|
|
|
return $this->allowAudio();
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-07-21 14:29:14 +00:00
|
|
|
* @return bool
|
|
|
|
*/
|
2018-08-12 09:45:29 +00:00
|
|
|
protected function allowImage() {
|
|
|
|
$attr = $this->getXmlAttribute( $this->xmlNode, self::ALLOWIMAGE_ATTR_NAME );
|
|
|
|
|
|
|
|
return !( isset( $attr ) && strtolower( $attr ) === 'false' );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function allowVideo() {
|
|
|
|
$attr = $this->getXmlAttribute( $this->xmlNode, self::ALLOWVIDEO_ATTR_NAME );
|
|
|
|
|
|
|
|
return !( isset( $attr ) && strtolower( $attr ) === 'false' );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function allowAudio() {
|
|
|
|
$attr = $this->getXmlAttribute( $this->xmlNode, self::ALLOWAUDIO_ATTR_NAME );
|
|
|
|
|
|
|
|
return !( isset( $attr ) && strtolower( $attr ) === 'false' );
|
2015-07-21 14:29:14 +00:00
|
|
|
}
|
2015-04-27 14:05:31 +00:00
|
|
|
}
|