Re-add and use custom Twig extensions
This commit is contained in:
parent
26bffdb58d
commit
275103e149
14 changed files with 1841 additions and 26 deletions
|
@ -8,3 +8,11 @@ sculpin_content_types:
|
||||||
permalink: /blog/:basename/
|
permalink: /blog/:basename/
|
||||||
presentations:
|
presentations:
|
||||||
permalink: /presentations/:basename/
|
permalink: /presentations/:basename/
|
||||||
|
|
||||||
|
services:
|
||||||
|
App\Experience\TwigExtension\ExperienceTwigExtension:
|
||||||
|
tags:
|
||||||
|
- {name: twig.extension}
|
||||||
|
App\Presentation\TwigExtension\PresentationTwigExtension:
|
||||||
|
tags:
|
||||||
|
- {name: twig.extension}
|
||||||
|
|
|
@ -8,5 +8,18 @@
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
"sculpin/sculpin-theme-composer-plugin": true
|
"sculpin/sculpin-theme-composer-plugin": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^12.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1601
composer.lock
generated
1601
composer.lock
generated
File diff suppressed because it is too large
Load diff
8
phpunit.xml.dist
Normal file
8
phpunit.xml.dist
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="./vendor/autoload.php" colors="true" stopOnFailure="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.1/phpunit.xsd" cacheDirectory=".phpunit.cache">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="App">
|
||||||
|
<directory suffix="Test.php">tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
|
@ -10,7 +10,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="{{ site.prose_classes }}">
|
<div class="{{ site.prose_classes }}">
|
||||||
<p>I'm an certified Drupal Triple Expert with {{ macros.yearsOfExperience }} years of experience, a Drupal core contributor, <a href="/presentations">public speaker</a>, <a href="{{ site.youtube.channel.url }}/streams">live streamer</a>, and host of the <a href="/podcast">Beyond Blocks podcast</a>.</p>
|
<p>I'm an certified Drupal Triple Expert with {{ get_years_of_experience() }} years of experience, a Drupal core contributor, <a href="/presentations">public speaker</a>, <a href="{{ site.youtube.channel.url }}/streams">live streamer</a>, and host of the <a href="/podcast">Beyond Blocks podcast</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,8 +23,6 @@ faqs:
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% import "_macros" as macros %}
|
|
||||||
|
|
||||||
{# <h2>Who is this for?</h2> #}
|
{# <h2>Who is this for?</h2> #}
|
||||||
|
|
||||||
{# Pain #}
|
{# Pain #}
|
||||||
|
@ -33,7 +31,7 @@ faqs:
|
||||||
|
|
||||||
{# Fix #}
|
{# Fix #}
|
||||||
|
|
||||||
<p>As a professional Software Developer and Consultant with {{ macros.yearsOfExperience }} years of Drupal and PHP experience, I have a lot of knowledge that I use to help customers and their projects.</p>
|
<p>As a professional Software Developer and Consultant with {{ get_years_of_experience() }} years of Drupal and PHP experience, I have a lot of knowledge that I use to help customers and their projects.</p>
|
||||||
|
|
||||||
{# 1st call to action #}
|
{# 1st call to action #}
|
||||||
|
|
||||||
|
@ -93,7 +91,7 @@ faqs:
|
||||||
<h2>Who am I?</h2>
|
<h2>Who am I?</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>I'm an Acquia-certified Drupal expert with {{ macros.yearsOfExperience }} years of professional development experience.</li>
|
<li>I'm an Acquia-certified Drupal expert with {{ get_years_of_experience() }} years of professional development experience.</li>
|
||||||
<li>I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.</li>
|
<li>I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.</li>
|
||||||
<li>I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.</li>
|
<li>I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.</li>
|
||||||
<li>I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.</li>
|
<li>I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.</li>
|
||||||
|
|
|
@ -6,8 +6,6 @@ button:
|
||||||
url: https://buy.stripe.com/aEU4h0gBc4ro0p27sz
|
url: https://buy.stripe.com/aEU4h0gBc4ro0p27sz
|
||||||
---
|
---
|
||||||
|
|
||||||
{% import "_macros" as macros %}
|
|
||||||
|
|
||||||
{# Pain #}
|
{# Pain #}
|
||||||
|
|
||||||
Drupal 7 will be unsupported on the **5th of January 2025**.
|
Drupal 7 will be unsupported on the **5th of January 2025**.
|
||||||
|
@ -71,7 +69,7 @@ An upgrade roadmap is a personalised audit of your Drupal website and includes d
|
||||||
|
|
||||||
## Who am I?
|
## Who am I?
|
||||||
|
|
||||||
* I'm an Acquia-certified Drupal expert with {{ macros.yearsOfExperience }} years of professional development experience.
|
* I'm an Acquia-certified Drupal expert with {{ get_years_of_experience() }} years of professional development experience.
|
||||||
* I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.
|
* I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.
|
||||||
* I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.
|
* I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.
|
||||||
* I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.
|
* I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.
|
||||||
|
|
|
@ -7,8 +7,6 @@ link: https://savvycal.com/opdavies/pair
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% import "_macros" as macros %}
|
|
||||||
|
|
||||||
{# Pain #}
|
{# Pain #}
|
||||||
|
|
||||||
<p>Are you stuck adding a new feature or fixing a bug?</p>
|
<p>Are you stuck adding a new feature or fixing a bug?</p>
|
||||||
|
@ -51,7 +49,7 @@ link: https://savvycal.com/opdavies/pair
|
||||||
<h2>Who am I?</h2>
|
<h2>Who am I?</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>I'm an Acquia-certified Drupal expert with {{ macros.yearsOfExperience }} years of professional development experience.</li>
|
<li>I'm an Acquia-certified Drupal expert with {{ get_years_of_experience() }} years of professional development experience.</li>
|
||||||
<li>I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.</li>
|
<li>I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.</li>
|
||||||
<li>I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.</li>
|
<li>I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.</li>
|
||||||
<li>I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.</li>
|
<li>I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.</li>
|
||||||
|
|
|
@ -4,10 +4,7 @@ title: Presentations
|
||||||
use: [presentations]
|
use: [presentations]
|
||||||
---
|
---
|
||||||
|
|
||||||
{% set today = 'today'|date('U') %}
|
<p>Since September 2012, I have given {{ get_presentation_count(data.presentations) }} public talks and workshops at various conferences and meetups, in-person and remotely, on topics including PHP, Drupal, automated testing, Git, CSS, and systems administration.</p>
|
||||||
{% set presentation_count = data.presentations|reduce((count, presentation) => count + (presentation.events|filter(e => e.date|date('U') < today)|length), 0) %}
|
|
||||||
|
|
||||||
<p>Since September 2012, I have given {{ presentation_count }} public talks and workshops at various conferences and meetups, in-person and remotely, on topics including PHP, Drupal, automated testing, Git, CSS, and systems administration.</p>
|
|
||||||
|
|
||||||
{% for presentation in data.presentations|sort((a, b) => a.events|last.date|date('U') > b.events|last.date|date('U') ? -1 : 1) %}
|
{% for presentation in data.presentations|sort((a, b) => a.events|last.date|date('U') > b.events|last.date|date('U') ? -1 : 1) %}
|
||||||
<article>
|
<article>
|
||||||
|
|
|
@ -3,15 +3,13 @@ layout: page
|
||||||
title: Press Info
|
title: Press Info
|
||||||
---
|
---
|
||||||
|
|
||||||
{% import "_macros" as macros %}
|
|
||||||
|
|
||||||
The following information is provided as a cut-and-paste resource for conference organisers, media professionals, podcast hosts, and other interested parties.
|
The following information is provided as a cut-and-paste resource for conference organisers, media professionals, podcast hosts, and other interested parties.
|
||||||
|
|
||||||
Please feel free to use anything here as-is without checking with me first. If you have additional questions, you can <a href="mailto:{{ site.email }}">email me directly</a>.
|
Please feel free to use anything here as-is without checking with me first. If you have additional questions, you can <a href="mailto:{{ site.email }}">email me directly</a>.
|
||||||
|
|
||||||
## Short Bio
|
## Short Bio
|
||||||
|
|
||||||
Oliver is a Software Developer and Drupal expert with {{ macros.yearsOfExperience }} years experience. He specialises in code quality, automated testing and test-driven development.
|
Oliver is a Software Developer and Drupal expert with {{ get_years_of_experience() }} years experience. He specialises in code quality, automated testing and test-driven development.
|
||||||
|
|
||||||
## Sample Topics
|
## Sample Topics
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,9 @@ layout: page
|
||||||
title: Speaker Information
|
title: Speaker Information
|
||||||
---
|
---
|
||||||
|
|
||||||
{% import "_macros" as macros %}
|
|
||||||
|
|
||||||
## Bio
|
## Bio
|
||||||
|
|
||||||
Oliver is a Software Developer and Drupal Expert with {{ macros.yearsOfExperience }} years of experience. As well as consulting on large Drupal projects, Oliver helps Drupal Developers learn automated testing and test-driven development via a free email course and paid coaching and workshops. He regularly contributes to open-source software projects, including Drupal core.
|
Oliver is a Software Developer and Drupal Expert with {{ get_years_of_experience() }} years of experience. As well as consulting on large Drupal projects, Oliver helps Drupal Developers learn automated testing and test-driven development via a free email course and paid coaching and workshops. He regularly contributes to open-source software projects, including Drupal core.
|
||||||
|
|
||||||
## Photos
|
## Photos
|
||||||
|
|
||||||
|
|
29
src/Experience/TwigExtension/ExperienceTwigExtension.php
Normal file
29
src/Experience/TwigExtension/ExperienceTwigExtension.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Experience\TwigExtension;
|
||||||
|
|
||||||
|
use Sculpin\Contrib\ProxySourceCollection\ProxySourceItem;
|
||||||
|
use Twig\Extension\AbstractExtension;
|
||||||
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
|
class ExperienceTwigExtension extends AbstractExtension
|
||||||
|
{
|
||||||
|
private static $startYear = 2007;
|
||||||
|
|
||||||
|
public function getFunctions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFunction('get_years_of_experience', [$this, 'getYearsOfExperience']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'experience';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getYearsOfExperience(): int
|
||||||
|
{
|
||||||
|
return (new \DateTimeImmutable())->format('Y') - self::$startYear;
|
||||||
|
}
|
||||||
|
}
|
38
src/Presentation/TwigExtension/PresentationTwigExtension.php
Normal file
38
src/Presentation/TwigExtension/PresentationTwigExtension.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Presentation\TwigExtension;
|
||||||
|
|
||||||
|
use Sculpin\Contrib\ProxySourceCollection\ProxySourceItem;
|
||||||
|
use Twig\Extension\AbstractExtension;
|
||||||
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
|
class PresentationTwigExtension extends AbstractExtension
|
||||||
|
{
|
||||||
|
public function getFunctions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFunction('get_presentation_count', [$this, 'getPresentationCount']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'presentations';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPresentationCount(array $presentations): int
|
||||||
|
{
|
||||||
|
$today = (new \DateTime('today'))->getTimestamp();
|
||||||
|
|
||||||
|
return collect($presentations)
|
||||||
|
->flatMap(fn (ProxySourceItem $presentation) => $presentation->data()->get('events'))
|
||||||
|
->filter(
|
||||||
|
function (array $event) use ($today): bool {
|
||||||
|
assert(array_key_exists(array: $event, key: 'date'));
|
||||||
|
|
||||||
|
return $event['date'] < $today;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
->count();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Presentation\TwigExtension;
|
||||||
|
|
||||||
|
use App\Presentation\TwigExtension\PresentationTwigExtension;
|
||||||
|
use Dflydev\DotAccessConfiguration\Configuration;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Sculpin\Contrib\ProxySourceCollection\ProxySourceItem;
|
||||||
|
|
||||||
|
class PresentationTwigExtensionTest extends TestCase
|
||||||
|
{
|
||||||
|
private PresentationTwigExtension $extension;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
$this->extension = new PresentationTwigExtension();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoPastEvents(): void
|
||||||
|
{
|
||||||
|
$presentation = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('+1 days'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertPresentationCount(expectedCount: 0, presentations: [$presentation]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSinglePastEvent(): void
|
||||||
|
{
|
||||||
|
$presentationA = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('+1 days'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$presentationB = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('-3 days'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertPresentationCount(expectedCount: 1, presentations: [$presentationA, $presentationB]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSinglePresentationWithMultiplePastEvents(): void
|
||||||
|
{
|
||||||
|
$presentation = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('-1 days'))->getTimestamp()],
|
||||||
|
['date' => (new \DateTime('-1 week'))->getTimestamp()],
|
||||||
|
['date' => (new \DateTime('-1 year'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertPresentationCount(expectedCount: 3, presentations: [$presentation]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSinglePresentationWithMultiplePastAndFutureEvents(): void
|
||||||
|
{
|
||||||
|
$presentation = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('+1 day'))->getTimestamp()],
|
||||||
|
['date' => (new \DateTime('-1 day'))->getTimestamp()],
|
||||||
|
['date' => (new \DateTime('-1 week'))->getTimestamp()],
|
||||||
|
['date' => (new \DateTime('+1 year'))->getTimestamp()],
|
||||||
|
['date' => (new \DateTime('-1 year'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertPresentationCount(expectedCount: 3, presentations: [$presentation]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultiplePastEvents(): void
|
||||||
|
{
|
||||||
|
$presentationA = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('-1 days'))->getTimestamp()],
|
||||||
|
['date' => (new \DateTime('+1 days'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$presentationB = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('-3 days'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertPresentationCount(expectedCount: 2, presentations: [$presentationA, $presentationB]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTheCurrentDayIsNotCounted(): void
|
||||||
|
{
|
||||||
|
$presentationA = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('yesterday'))->getTimestamp()],
|
||||||
|
['date' => (new \DateTime('today'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$presentationB = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('today'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$presentationC = $this->createPresentation(
|
||||||
|
events: [
|
||||||
|
['date' => (new \DateTime('yesterday'))->getTimestamp()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertPresentationCount(expectedCount: 2, presentations: [$presentationA, $presentationB, $presentationC]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert the extension uses the correct number of presentations.
|
||||||
|
*/
|
||||||
|
private function assertPresentationCount(int $expectedCount, array $presentations): void
|
||||||
|
{
|
||||||
|
self::assertSame(
|
||||||
|
actual: $this->extension->getPresentationCount($presentations),
|
||||||
|
expected: $expectedCount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a mock presentation with a list of events.
|
||||||
|
*/
|
||||||
|
private function createPresentation(array $events): ProxySourceItem
|
||||||
|
{
|
||||||
|
$configuration = $this->createMock(Configuration::class);
|
||||||
|
$configuration->method('get')->with($this->identicalTo('events'))->willReturn($events);
|
||||||
|
|
||||||
|
$presentation = $this->createMock(ProxySourceItem::class);
|
||||||
|
$presentation->method('data')->willReturn($configuration);
|
||||||
|
|
||||||
|
return $presentation;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue