Merge branch 'portable_infobox_mvp' into DAT-2745

Conflicts:
	extensions/wikia/PortableInfobox/services/Parser/Nodes/NodeImage.php
This commit is contained in:
Adam Robak 2015-05-07 14:54:23 +02:00
commit d14e824151
18 changed files with 380 additions and 72 deletions

View file

@ -3,9 +3,11 @@ $messages = [];
$messages[ 'en' ] = [
'portable-infobox-desc' => 'Create portable infoboxes which can be rendered using clean semantic HTML markup on
any skin / platform using using easy to understand powerful XML-like markup'
any skin / platform using using easy to understand powerful XML-like markup',
'unimplemented-infobox-tag' => 'Unimplemented infobox tag: <$1>'
];
$messages[ 'qqq' ] = [
'portable-infobox-desc' => 'Portable Infobox extension description'
'portable-infobox-desc' => 'Portable Infobox extension description',
'unimplemented-infobox-tag' => 'Error message for using unimplemented infobox tag; $1 is the tag name without pointy braces'
];

View file

@ -49,7 +49,6 @@ $wgAutoloadClasses[ 'PortableInfoboxHooks' ] = $dir . 'PortableInfoboxHooks.clas
// hooks
$wgHooks[ 'ParserFirstCallInit' ][] = 'PortableInfoboxParserTagController::parserTagInit';
$wgHooks['BeforePageDisplay'][] = 'PortableInfoboxHooks::onBeforePageDisplay';
$wgHooks[ 'SkinAfterBottomScripts' ][] = 'PortableInfoboxHooks::onSkinAfterBottomScripts';
// i18n mapping
$wgExtensionMessagesFiles[ 'PortableInfobox' ] = $dir . 'PortableInfobox.i18n.php';

View file

@ -5,22 +5,4 @@ class PortableInfoboxHooks {
Wikia::addAssetsToOutput('portable_infobox_scss');
return true;
}
/**
* Adds assets on the bottom of the body tag for special maps page
*
* @param {String} $skin
* @param {String} $text
*
* @return bool
*/
public static function onSkinAfterBottomScripts( $skin, &$text ) {
$scripts = AssetsManager::getInstance()->getURL( 'portable_infobox_js' );
foreach ( $scripts as $script ) {
$text .= Html::linkedScript( $script );
}
return true;
}
}

View file

@ -30,7 +30,14 @@ class PortableInfoboxParserTagController extends WikiaController {
$infoboxParser = new Wikia\PortableInfobox\Parser\XmlParser( $frame->getNamedArguments() );
$infoboxParser->setExternalParser( ( new Wikia\PortableInfobox\Parser\MediaWikiParserService( $parser, $frame ) ) );
try {
$data = $infoboxParser->getDataFromXmlString( $markup );
} catch ( \Wikia\PortableInfobox\Parser\Nodes\UnimplementedNodeException $e ) {
return [ $this->renderUnimplementedTagErrorMesssage( $e->getMessage() ), 'markerType' => 'nowiki' ];
}
//save for later api usage
$this->saveToParserOutput( $parser->getOutput(), $data );
@ -40,6 +47,13 @@ class PortableInfoboxParserTagController extends WikiaController {
return [ $renderedValue, 'markerType' => 'general' ];
}
private function renderUnimplementedTagErrorMesssage( $tagName ) {
$renderedValue = '<strong class="error"> '
. wfMessage( 'unimplemented-infobox-tag', [ $tagName ] )->escaped()
. '</strong>';
return $renderedValue;
}
protected function saveToParserOutput( \ParserOutput $parserOutput, $raw ) {
if ( !empty( $raw ) ) {
$infoboxes = $parserOutput->getProperty( self::INFOBOXES_PROPERTY_NAME );

4
crowdin.conf Normal file
View file

@ -0,0 +1,4 @@
[crowdin.project]
projectid = "ext-PortableInfobox"
format = "mediawiki"
source.file = "PortableInfobox.i18n.php"

View file

@ -1,23 +0,0 @@
'use strict';
(function($, vignette) {
var infoboxImages = $('.portable-infobox-image');
/**
* @desc loads infobox image image
* @param {number} index - array index
* @param {string} img - img html
*/
function loadImage(index, img) {
var $img = $(img),
url = $img.data('url'),
options = {
mode: vignette.mode.scaleToWidth,
width: $img.parent().width()
};
$img.attr('src', vignette.getThumbURL(url, options));
}
infoboxImages.each(loadImage);
})(jQuery, Vignette);

View file

@ -8,11 +8,16 @@ class NodeImage extends Node {
public function getData() {
return [
'value' => $this->resolveImageUrl( $this->getValueWithDefault( $this->xmlNode ) ),
'url' => $this->resolveImageUrl( $imageName ),
'name' => $this->getValueWithDefault( $this->xmlNode ),
'alt' => $this->getValueWithDefault( $this->xmlNode->{self::ALT_TAG_NAME} )
];
}
public function isEmpty( $data ) {
return !( isset( $data[ 'url' ] ) ) || empty( $data[ 'url' ] );
}
public function resolveImageUrl( $filename ) {
global $wgContLang;
$title = \Title::newFromText(

View file

@ -2,12 +2,9 @@
namespace Wikia\PortableInfobox\Parser\Nodes;
class NodeUnimplemented extends Node {
public function getType() {
return parent::getType() . '(unimplemented)';
}
public function getData() {
throw new UnimplementedNodeException('Unimplemented node type');
throw new UnimplementedNodeException( $this->getType() );
}
}

View file

@ -2,6 +2,7 @@
class PortableInfoboxRenderService extends WikiaService {
const LOGGER_LABEL = 'portable-infobox-render-not-supported-type';
const THUMBNAIL_WIDTH = 270;
private $templates = [
'wrapper' => 'PortableInfoboxWrapper.mustache',
@ -75,7 +76,8 @@ class PortableInfoboxRenderService extends WikiaService {
* @param array $comparisonData
* @return string - comparison HTML
*/
private function renderComparisonItem( $comparisonData ) {
private function renderComparisonItem( $comparisonData )
{
$comparisonHTMLContent = '';
foreach ($comparisonData as $set) {
@ -110,7 +112,13 @@ class PortableInfoboxRenderService extends WikiaService {
$comparisonHTMLContent .= $this->renderItem( 'comparison-set', [ 'content' => $setHTMLContent ] );
}
return $this->renderItem( 'comparison', [ 'content' => $comparisonHTMLContent ] );
if ( !empty( $comparisonHTMLContent ) ) {
$output = $this->renderItem('comparison', [ 'content' => $comparisonHTMLContent ] );
} else {
$output = '';
}
return $output;
}
/**
@ -145,6 +153,14 @@ class PortableInfoboxRenderService extends WikiaService {
* @return string - HTML
*/
private function renderItem( $type, array $data ) {
//TODO: with validated the performance of render Service and in the next phase we want to refactor it (make
// it modular) While doing this we also need to move this logic to appropriate image render class
if ( $type === 'image' ) {
$data[ 'thumbnail' ] = VignetteRequest::fromUrl( $data[ 'url' ] )
->scaleToWidth( self::THUMBNAIL_WIDTH )
->url();
}
return $this->templateEngine->clearData()
->setData( $data )
->render( $this->templates[ $type ] );

View file

@ -84,23 +84,23 @@ $infobox-section-header-background: transparentize($color-links, 0.9);
}
}
.portable-infobox-comparision {
.portable-infobox-comparison {
border-collapse: collapse;
width: 100%;
}
.portable-infobox-comparision-set {
.portable-infobox-comparison-set {
display: block;
}
.portable-infobox-comparision-set-header {
.portable-infobox-comparison-set-header {
box-sizing: border-box;
display: block;
text-align: left;
width: 100%;
}
.portable-infobox-comparision-item {
.portable-infobox-comparison-item {
box-sizing: border-box;
vertical-align: top;
width: $infobox-width / 2;

View file

@ -1,5 +1,5 @@
<div class="portable-infobox-item item-type-comparison">
<table class="portable-infobox-comparision">
<table class="portable-infobox-comparison">
<tbody>{{{content}}}</tbody>
</table>
</div>

View file

@ -1 +1 @@
<tr class="portable-infobox-comparision-set">{{{content}}}</tr>
<tr class="portable-infobox-comparison-set">{{{content}}}</tr>

View file

@ -1 +1 @@
<th class="portable-infobox-comparision-set-header">{{{content}}}</th>
<th class="portable-infobox-comparison-set-header">{{{content}}}</th>

View file

@ -1 +1 @@
<td class="portable-infobox-comparision-item">{{{content}}}</td>
<td class="portable-infobox-comparison-item">{{{content}}}</td>

View file

@ -1,5 +1,7 @@
<div class="portable-infobox-item item-type-image no-margins">
<figure>
<img class="portable-infobox-image" alt="{{alt}}" data-url="{{value}}" />
<a href="{{url}}" class="image image-thumbnail" title="{{alt}}">
<img src="{{thumbnail}}" class="portable-infobox-image" alt="{{alt}}" data-image-key="{{name}}" data-image-name="{{name}}"/>
</a>
</figure>
</div>

View file

@ -79,6 +79,18 @@ class ImageFilenameSanitizerTest extends WikiaBaseTest {
'en',
'',
'Empty file name'
],
[
'[[File:image.jpg|300px|lorem ipsum]]',
'es',
'image.jpg',
'Link to filename with canonical namespace, width and caption on a non-EN wiki '
],
[
'[[File:image.jpg|lorem ipsum]]',
'es',
'image.jpg',
'Link to filename with canonical namespace and caption on a non-EN wiki '
]
];
}

View file

@ -36,7 +36,8 @@ class PortableInfoboxParserNodesTest extends WikiaBaseTest {
$node = $this->getMockBuilder( 'Wikia\PortableInfobox\Parser\Nodes\NodeImage' )->setConstructorArgs( [ $xml, [ 'image2' => 'aaa.jpg', 'alt-source' => 'bbb' ] ] )->setMethods( [ 'resolveImageUrl' ] )->getMock();
$node->expects( $this->any() )->method( 'resolveImageUrl' )->will( $this->returnValue( 'aaa.jpg' ) );
$this->assertTrue( $node->getData()[ 'value' ] == 'aaa.jpg', 'value is not aaa.jpg' );
$this->assertTrue( $node->getData()[ 'url' ] == 'aaa.jpg', 'value is not aaa.jpg' );
$this->assertTrue( $node->getData()[ 'name' ] == 'aaa.jpg', 'value is not aaa.jpg' );
$this->assertTrue( $node->getData()[ 'alt' ] == 'bbb', 'alt is not bbb' );
$this->assertTrue( $nodeDefault->getData()[ 'alt' ] == 'default-alt', 'default alt' );
}

View file

@ -81,6 +81,20 @@ class PortableInfoboxRenderServiceTest extends PHPUnit_Framework_TestCase {
'output' => '<aside class="portable-infobox"><div class="portable-infobox-item item-type-image no-margins"><figure><img class="portable-infobox-image" alt="image alt" data-url="http://image.jpg"/></figure></div></aside>',
'description' => 'Only image'
],
[
'input' => [
[
'type' => 'pair',
'data' => [
'label' => 'test label',
'value' => 'test value'
],
'isEmpty' => false
]
],
'output' => '<aside class="portable-infobox"><div class="portable-infobox-item item-type-key-val portable-infobox-item-margins"><h3 class="portable-infobox-item-label portable-infobox-header-font">test label</h3><div class="portable-infobox-item-value">test value</div></div></aside>',
'description' => 'Only pair'
],
[
'input' => [
[
@ -135,6 +149,289 @@ class PortableInfoboxRenderServiceTest extends PHPUnit_Framework_TestCase {
],
'output' => '<aside class="portable-infobox"><div class="portable-infobox-item item-type-title portable-infobox-item-margins"><h2 class="portable-infobox-title">Test Title</h2></div><div class="portable-infobox-item item-type-key-val portable-infobox-item-margins"><h3 class="portable-infobox-item-label portable-infobox-header-font">test label</h3><div class="portable-infobox-item-value">test value</div></div></aside>',
'description' => 'Simple infobox with title, empty image and key-value pair'
],
[
'input' => [
[
'type' => 'title',
'data' => [
'value' => 'Test Title'
],
'isEmpty' => false
],
[
'type' => 'image',
'data' => [
'alt' => 'image alt',
'value' => 'http://image.jpg'
],
'isEmpty' => false
],
[
'type' => 'pair',
'data' => [],
'isEmpty' => true
]
],
'output' => '<aside class="portable-infobox">
<div class="portable-infobox-item item-type-title portable-infobox-item-margins">
<h2 class="portable-infobox-title">Test Title</h2>
</div>
<div class="portable-infobox-item item-type-image no-margins">
<figure>
<img class="portable-infobox-image" alt="image alt" data-url="http://image.jpg"/>
</figure>
</div>
</aside>',
'description' => 'Simple infobox with title, image and empty key-value pair'
],
[
'input' => [
[
'type' => 'title',
'data' => [
'value' => 'Test Title'
],
'isEmpty' => false
],
[
'type' => 'image',
'data' => [
'alt' => 'image alt',
'value' => 'http://image.jpg'
],
'isEmpty' => false
],
[
'type' => 'group',
'data' => [
'value' => [
[
'type' => 'header',
'data' => [
'value' => 'Test Header'
],
'isEmpty' => false
],
[
'type' => 'pair',
'data' => [
'label' => 'test label',
'value' => 'test value'
],
'isEmpty' => false,
],
[
'type' => 'pair',
'data' => [
'label' => 'test label',
'value' => 'test value'
],
'isEmpty' => false
]
]
],
'isEmpty' => false
]
],
'output' => '<aside class="portable-infobox">
<div class="portable-infobox-item item-type-title portable-infobox-item-margins">
<h2 class="portable-infobox-title">Test Title</h2>
</div>
<div class="portable-infobox-item item-type-image no-margins">
<figure>
<img class="portable-infobox-image" alt="image alt" data-url="http://image.jpg"/>
</figure>
</div>
<section class="portable-infobox-item item-type-group">
<div class="portable-infobox-item item-type-header portable-infobox-item-margins portable-infobox-header-background">
<h2 class="portable-infobox-header portable-infobox-header-font">Test Header</h2>
</div>
<div class="portable-infobox-item item-type-key-val portable-infobox-item-margins">
<h3 class="portable-infobox-item-label portable-infobox-header-font">test label</h3>
<div class="portable-infobox-item-value">test value</div>
</div>
<div class="portable-infobox-item item-type-key-val portable-infobox-item-margins">
<h3 class="portable-infobox-item-label portable-infobox-header-font">test label</h3>
<div class="portable-infobox-item-value">test value</div>
</div>
</section>
</aside>',
'description' => 'Infobox with title, image and group with header two key-value pairs'
],
[
'input' => [
[
'type' => 'title',
'data' => [
'value' => 'Test Title'
],
'isEmpty' => false
],
[
'type' => 'image',
'data' => [
'alt' => 'image alt',
'value' => 'http://image.jpg'
],
'isEmpty' => false
],
[
'type' => 'group',
'data' => [
'value' => [
[
'type' => 'header',
'data' => [
'value' => 'Test Header'
],
'isEmpty' => false
],
]
],
'isEmpty' => true
]
],
'output' => '<aside class="portable-infobox">
<div class="portable-infobox-item item-type-title portable-infobox-item-margins">
<h2 class="portable-infobox-title">Test Title</h2>
</div>
<div class="portable-infobox-item item-type-image no-margins">
<figure>
<img class="portable-infobox-image" alt="image alt" data-url="http://image.jpg"/>
</figure>
</div>
</aside>',
'description' => 'Infobox with title, image and empty group with header'
],
[
'input' => [
[
'type' => 'comparison',
'data' => [
'value' => [
[
'type' => 'set',
'data' => [],
'isEmpty' => true
],
[
'type' => 'set',
'data' => [],
'isEmpty' => true
],
],
],
'isEmpty' => true
]
],
'output' => '',
'description' => 'Infobox with empty comparison'
],
[
'input' => [
[
'type' => 'comparison',
'data' => [
'value' => [
[
'type' => 'set',
'data' => [],
'isEmpty' => true
],
[
'type' => 'set',
'data' => [
'value' => [
[
'type' => 'header',
'data' => [
'value' => 'Test Header'
],
'isEmpty' => false
],
[
'type' => 'pair',
'data' => [
'label' => 'test label',
'value' => 'test value'
],
'isEmpty' => false,
],
[
'type' => 'pair',
'data' => [
'label' => 'test label',
'value' => 'test value'
],
'isEmpty' => false
]
]
],
'isEmpty' => false
],
],
],
'isEmpty' => false
]
],
'output' => '<aside class="portable-infobox">
<div class="portable-infobox-item item-type-comparison">
<table class="portable-infobox-comparison">
<tbody>
<tr class="portable-infobox-comparison-set">
<th class="portable-infobox-comparison-set-header">
<div class="portable-infobox-item item-type-header portable-infobox-item-margins portable-infobox-header-background">
<h2 class="portable-infobox-header portable-infobox-header-font">Test Header</h2>
</div>
</th>
<td class="portable-infobox-comparison-item">
<div class="portable-infobox-item item-type-key-val portable-infobox-item-margins">
<h3 class="portable-infobox-item-label portable-infobox-header-font">test label</h3>
<div class="portable-infobox-item-value">test value</div>
</div>
</td>
<td class="portable-infobox-comparison-item">
<div class="portable-infobox-item item-type-key-val portable-infobox-item-margins">
<h3 class="portable-infobox-item-label portable-infobox-header-font">test label</h3>
<div class="portable-infobox-item-value">test value</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</aside>',
'description' => 'Infobox with comparison with two sets of which one is empty'
],
[
'input' => [
[
'type' => 'footer',
'data' => [],
'isEmpty' => true
]
],
'output' => '',
'description' => 'Infobox with empty footer'
],
[
'input' => [
[
'type' => 'footer',
'data' => [
'links' => '<p>Links</p>'
],
'isEmpty' => false
]
],
'output' => '<aside class="portable-infobox">
<footer class="portable-infobox-footer portable-infobox-item-margins portable-infobox-header-background
portable-infobox-header-font">
<p>Links</p>
</footer>
</aside>',
'description' => 'Infobox with footer'
]
];
}