Skip to content

Commit

Permalink
Merge pull request #1228 from wmde/page_display_handler
Browse files Browse the repository at this point in the history
Load custom templates for content pages
  • Loading branch information
gbirke committed May 9, 2018
2 parents 648b636 + 93a378f commit 2a7c93c
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 126 deletions.
70 changes: 70 additions & 0 deletions app/RouteHandlers/PageDisplayHandler.php
@@ -0,0 +1,70 @@
<?php

declare( strict_types = 1 );

namespace WMDE\Fundraising\Frontend\App\RouteHandlers;

use Silex\Application;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use WMDE\Fundraising\Frontend\Factories\FunFunFactory;
use WMDE\Fundraising\Frontend\Presentation\ContentPage\ContentNotFoundException;
use WMDE\Fundraising\Frontend\Presentation\ContentPage\PageNotFoundException;
use WMDE\Fundraising\Frontend\Presentation\TwigTemplate;

/**
* @license GNU GPL v2+
* @author Tim Eulitz < tim.eulitz@wikimedia.de >
* @author Gabriel Birke < gabriel.birke@wikimedia.de >
*/
class PageDisplayHandler {

private $ffFactory;
private $app;

public function __construct( FunFunFactory $ffFactory, Application $app ) {
$this->ffFactory = $ffFactory;
$this->app = $app;
}

function handle( $pageName ) {
$pageSelector = $this->ffFactory->getContentPagePageSelector();

try {
$pageId = $pageSelector->getPageId( $pageName );
}
catch ( PageNotFoundException $exception ) {
throw new NotFoundHttpException( "Page page name '$pageName' not found." );
}

try {
return $this->getPageTemplate( $pageId )->render( [ 'page_id' => $pageId ] );
}
catch ( \Twig_Error_Runtime $exception ) {
if ( $exception->getPrevious() instanceof ContentNotFoundException ) {
throw new NotFoundHttpException( "Content for page id '$pageId' not found." );
}

throw $exception;
}
}

/**
* Checks if file matching page ID exists in page_layouts directory and loads template if it exists
* Otherwise, falls back to base.twig template
*
* @param string $pageId
* @param array $context Additional variables for template
*
* @return TwigTemplate
*/
public function getPageTemplate( string $pageId, array $context = [] ): TwigTemplate {
$template = 'page_layouts' . DIRECTORY_SEPARATOR . 'base.html.twig';
$pageTemplate = 'page_layouts' . DIRECTORY_SEPARATOR . $pageId . '.html.twig';

if ( file_exists( $this->ffFactory->getAbsoluteSkinDirectory() . DIRECTORY_SEPARATOR . $pageTemplate ) ) {
$template = $pageTemplate;
}

return $this->ffFactory->getLayoutTemplate($template, $context);
}
}
26 changes: 3 additions & 23 deletions app/routes.php
Expand Up @@ -12,7 +12,6 @@
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Validation;
Expand Down Expand Up @@ -41,8 +40,6 @@
use WMDE\Fundraising\MembershipContext\UseCases\ShowApplicationConfirmation\ShowAppConfirmationRequest;
use WMDE\Fundraising\PaymentContext\Domain\Model\Iban;
use WMDE\Fundraising\PaymentContext\UseCases\GenerateIban\GenerateIbanRequest;
use WMDE\Fundraising\Frontend\Presentation\ContentPage\ContentNotFoundException;
use WMDE\Fundraising\Frontend\Presentation\ContentPage\PageNotFoundException;
use WMDE\Fundraising\Frontend\Presentation\DonationMembershipApplicationAdapter;
use WMDE\Fundraising\Frontend\UseCases\GetInTouch\GetInTouchRequest;
use WMDE\Fundraising\Frontend\Validation\ConstraintViolationListMapper;
Expand Down Expand Up @@ -184,26 +181,9 @@ function( Request $request ) use ( $app, $ffFactory ) {

$app->get(
'page/{pageName}',
function( $pageName ) use ( $ffFactory ) {
$pageSelector = $ffFactory->getContentPagePageSelector();

try {
$pageId = $pageSelector->getPageId( $pageName );
} catch ( PageNotFoundException $exception ) {
throw new NotFoundHttpException( "Page page name '$pageName' not found." );
}

try {
return $ffFactory->getLayoutTemplate( 'Display_Page_Layout.twig' )->render( [
'page_id' => $pageId
] );
} catch ( Twig_Error_Runtime $exception ) {
if ($exception->getPrevious() instanceof ContentNotFoundException) {
throw new NotFoundHttpException( "Content for page id '$pageId' not found." );
}

throw $exception;
}
function( Application $app, $pageName ) use ( $ffFactory ) {
return ( new \WMDE\Fundraising\Frontend\App\RouteHandlers\PageDisplayHandler( $ffFactory, $app ) )
->handle( $pageName );
}
)
->bind( 'page' );
Expand Down
56 changes: 0 additions & 56 deletions skins/10h16/templates/Display_Page_Layout.twig

This file was deleted.

56 changes: 56 additions & 0 deletions skins/10h16/templates/page_layouts/base.html.twig
@@ -0,0 +1,56 @@
{% extends 'Base_Layout.html.twig' %}

{% block page_identifier %}page-{$ page_id $}{% endblock %}

{% block main %}
<section>
<div class="container clearfix">
<div class="ltcol">
&nbsp;
</div>

<div class="ctcol">
<div class="box partial rounded no-margin">
<div class="box-header container clearfix">
<span class="icon-hand-right f-left">{$ page_id|trans({}, 'pageTitles') $}</span>
</div>

<div class="box-section box-footer">
<div class="container clearfix sandboxedcontent {$ page_id $}">
{$ web_content("pages/#{page_id}") $}
</div>
</div>
</div>
</div>

<div class="rtcol">
<div class="itz-logo">
<a href="{$ 'content_pages_itz_link' | trans | e ( 'html_attr' ) $}" target="_blank" title="{$ 'content_pages_itz_title' | trans | e ( 'html_attr' ) $}">
<img src="{$ 'content_pages_itz_logo' | trans | e ( 'html_attr' ) $}" alt="{$ 'content_pages_itz_title' | trans | e ( 'html_attr' ) $}">
</a>
</div>

{% if page_id == 'supporters' %}
<div class="icon-ok-sign _icon-list-item last">
<p>
<strong>Spenden auch Sie!</strong>
</p>

<p>
Unterstützen Sie die Wikipedia und Freies Wissen mit Ihrer <a target="_blank" href="{$ path( '/', { piwik_campaign: 'spenden_wikimedia_benefactors', piwik_kwd: 'benefactors' } ) $}">Spende</a>.
</p>
</div>
{% else %}
<div class="icon-hand-right _icon-list-item last">
<p>
<strong>Spenden</strong>
</p>
<p>
<a href="{$ path( '/' ) $}" target="_blank">Zurück</a> zur Spendenseite
</p>
</div>
{% endif %}
</div>
</div>
</section>
{% endblock %}
Empty file.
6 changes: 6 additions & 0 deletions skins/cat17/templates/page_layouts/supporters.html.twig
@@ -0,0 +1,6 @@
{% extends 'page_layouts/base.html.twig' %}
{% set page_works_without_js = false %}

{% block scripts %}
<script src="{$ basepath $}/skins/cat17/scripts/supporters.js"></script>
{% endblock %}
5 changes: 5 additions & 0 deletions skins/test/templates/page_layouts/test.html.twig
@@ -0,0 +1,5 @@
{% extends 'page_layouts/base.html.twig' %}

{% block main %}
<div class="test-block">Test</div>
{% endblock %}
10 changes: 9 additions & 1 deletion src/Factories/FunFunFactory.php
Expand Up @@ -330,7 +330,7 @@ public function register( Container $container ): void {

$container['twig'] = function() {
$config = $this->config['twig'];
$config['loaders']['filesystem']['template-dir'] = 'skins/' . $this->getSkinSettings()->getSkin() . '/templates';
$config['loaders']['filesystem']['template-dir'] = $this->getSkinDirectory();

$twigFactory = $this->newTwigFactory( $config );
$configurator = $twigFactory->newTwigEnvironmentConfigurator();
Expand Down Expand Up @@ -1602,4 +1602,12 @@ public function newDonationAmountConstraint(): ValidatorConstraint {
public function newIsCustomDonationAmountValidator(): IsCustomAmountValidator {
return new IsCustomAmountValidator( $this->getPresetAmountsSettings( 'donations' ) );
}

public function getSkinDirectory(): string {
return 'skins/' . $this->getSkinSettings()->getSkin() . '/templates';
}

public function getAbsoluteSkinDirectory(): string {
return $this->getAbsolutePath( $this->getSkinDirectory() );
}
}
103 changes: 57 additions & 46 deletions tests/EdgeToEdge/Routes/DisplayPageRouteTest.php
Expand Up @@ -18,36 +18,32 @@
*/
class DisplayPageRouteTest extends WebRouteTestCase {

public function testWhenPageDoesNotExist_missingResponseIsReturnedAndHasHeaderAndFooter(): void {
$client = $this->createClient(
public function testWhenPageHasCustomTemplate_customTemplateIsRendered(): void {
$this->createEnvironment(
[],
function ( FunFunFactory $factory ): void {
$pageSelector = $this->createMock( PageSelector::class );
$pageSelector
->method( 'getPageId' )
->with( 'kittens' )
->willThrowException( new PageNotFoundException() );
$factory->setContentPagePageSelector( $pageSelector );
}
);
$client->request( 'GET', '/page/kittens' );
function ( Client $client, FunFunFactory $factory ): void {
$factory->setContentPagePageSelector( $this->getMockPageSelector( 'test' ) );
$factory->setContentProvider( $this->getVfsContentProvider( [ 'test.twig' => '' ] ) );

$content = $client->getResponse()->getContent();
$crawler = $client->request( 'GET', '/page/test' );

$this->assertContains(
'page_not_found',
$content
$this->assertCount( 1, $crawler->filter( '.test-block' ) );
}
);
}

$this->assertContains(
'page header',
$content
public function testWhenPageDoesNotExist_missingResponseIsReturnedAndHasHeaderAndFooter(): void {
$client = $this->createClient( [],
function ( FunFunFactory $factory ): void {
$factory->setContentPagePageSelector( $this->getNotFoundPageSelector( 'kittens' ) );
}
);
$client->request( 'GET', '/page/kittens' );
$content = $client->getResponse()->getContent();

$this->assertContains(
'page footer',
$content
);
$this->assertContains( 'page_not_found', $content );
$this->assertContains( 'page header', $content );
$this->assertContains( 'page footer', $content );
}

public function testWhenPageDoesNotExist_noUnescapedPageNameIsShown(): void {
Expand All @@ -74,30 +70,14 @@ public function testWhenRequestedContentPageExists_itGetsEmbeddedAndHasHeaderAnd
$this->createEnvironment(
[],
function ( Client $client, FunFunFactory $factory ): void {
$factory->setContentPagePageSelector( $this->getMockPageSelector( 'unicorns' ) );
$factory->setContentProvider(
$this->getVfsContentProvider(
[ 'unicorns.twig' => '<p>Rosa plüsch einhorns tanzen auf Regenbogen</p>' ]
)
);

$pageSelector = $this->createMock( PageSelector::class );
$pageSelector
->method( 'getPageId' )
->willReturnArgument( 0 )
->with( 'einhorns' )
->willReturn( 'unicorns' );
$factory->setContentPagePageSelector( $pageSelector );

$content = vfsStream::setup( 'content', null, [
'web' => [
'pages' => [
'unicorns.twig' => '<p>Rosa plüsch einhorns tanzen auf Regenbogen</p>',
]
],
'mail' => [],
'shared' => [],
] );
$provider = new ContentProvider( [
'content_path' => $content->url()
] );
$factory->setContentProvider( $provider );

$crawler = $client->request( 'GET', '/page/einhorns' );
$crawler = $client->request( 'GET', '/page/unicorns' );

$this->assertCount( 1, $crawler->filter( 'body.page-unicorns' ) );
$this->assertCount( 1, $crawler->filter( 'header:contains("page header")' ) );
Expand All @@ -115,4 +95,35 @@ public function testWhenPageNameContainsSlash_404isReturned(): void {

$this->assert404( $client->getResponse() );
}

private function getMockPageSelector( string $pageId ): PageSelector {
$pageSelector = $this->createMock( PageSelector::class );
$pageSelector
->method( 'getPageId' )
->willReturnArgument( 0 )
->with( $pageId )
->willReturn( $pageId );
return $pageSelector;
}

private function getNotFoundPageSelector( string $pageId ): PageSelector {
$pageSelector = $this->createMock( PageSelector::class );
$pageSelector
->method( 'getPageId' )
->with( $pageId )
->willThrowException( new PageNotFoundException() );
return $pageSelector;
}

private function getVfsContentProvider( array $pages ): ContentProvider {
$content = vfsStream::setup( 'content', null,
[
'web' => [ 'pages' => $pages ],
'mail' => [],
'shared' => [],
]
);
$provider = new ContentProvider( [ 'content_path' => $content->url() ] );
return $provider;
}
}

0 comments on commit 2a7c93c

Please sign in to comment.