Move all files to sculpin/

This commit is contained in:
Oliver Davies 2025-10-01 00:01:33 +01:00
parent c5d71803a5
commit 0f61b4e9ee
1514 changed files with 0 additions and 0 deletions

1
sculpin/.envrc Normal file
View file

@ -0,0 +1 @@
use flake

21
sculpin/.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
/.phpunit.cache
/.phpunit.result.cache
/output_*/
/vendor/
# Front-end assets.
node_modules
source/build
# ATDC pages
/source/atdc/*
# Direnv
/.direnv/
# Devenv
.devenv*
devenv.local.nix
# Pre-commit
.pre-commit-config.yaml

3
sculpin/.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "source/atdc"]
path = source/atdc
url = forgejo@ssh.oliverdavies.uk:opdavies/atdc-pages.git

1
sculpin/.ignore Normal file
View file

@ -0,0 +1 @@
*.mp3

8
sculpin/.mailmap Normal file
View file

@ -0,0 +1,8 @@
Oliver Davies <oliver@oliverdavies.dev> <339813+opdavies@users.noreply.github.com>
Oliver Davies <oliver@oliverdavies.dev> <oliver+github@oliverdavies.uk>
Oliver Davies <oliver@oliverdavies.dev> <oliver.davies@inviqa.com>
Oliver Davies <oliver@oliverdavies.dev> <oliver@microserve.io>
Oliver Davies <oliver@oliverdavies.dev> <oliver@oliver@oliverdavies.uk>
Oliver Davies <oliver@oliverdavies.dev> <oliver@oliverdavies.co.uk>
Oliver Davies <oliver@oliverdavies.dev> <oliver@oliverdavies.uk>
Oliver Davies <oliver@oliverdavies.dev> <opdavies@gmail.com>

View file

@ -0,0 +1 @@
MD013: false

12
sculpin/.tmux-sessionizer Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
PATH="${PATH}:./vendor/bin"
tmux new-window -dn scratch
tmux new-window -dn sculpin
tmux new-window -dn tailwind
tmux send-keys -t sculpin "sculpin generate --server --watch" Enter
tmux send-keys -t tailwind "cd assets && tailwindcss --input css/tailwind.css --output ../source/build/tailwind.css --watch" Enter
nvim .

5
sculpin/.yamlfmt.yaml Normal file
View file

@ -0,0 +1,5 @@
---
formatter:
include_document_start: true
retain_line_breaks_single: true
type: basic

27
sculpin/README.md Normal file
View file

@ -0,0 +1,27 @@
# oliverdavies.uk
[My personal website][website], built with [Sculpin][] - a PHP-based static site generator.
It contains pages about my products, services, public speaking and training, as well as my daily email archive, podcast episodes and old blog posts from earlier versions of the website.
## Styling
The styling is done using [Tailwind CSS].
## BrowserSync
I have [BrowserSync][] installed and use it to watch for changes to files and [automatically refresh my browser][0] instead of using Sculpin's built-in development server.
## Hosting
The website is hosted on a 1GB DigitalOcean droplet alongside other static websites.
I'm using Apache as my web server so I can easily manage redirects using the `.htaccess` file within the `source` directory..
As it's a static website, no PHP or database are needed.
[0]: https://www.oliverdavies.uk/blog/sculpin-browsersync
[browsersync]: https://browsersync.io
[sculpin]: https://sculpin.io
[tailwind css]: https://tailwindcss.com
[website]: https://www.oliverdavies.uk

View file

@ -0,0 +1,14 @@
<?php
use Opdavies\Sculpin\Bundle\TwigMarkdownBundle\SculpinTwigMarkdownBundle;
use Sculpin\Bundle\SculpinBundle\HttpKernel\AbstractKernel;
class SculpinKernel extends AbstractKernel
{
protected function getAdditionalSculpinBundles(): array
{
return [
SculpinTwigMarkdownBundle::class,
];
}
}

View file

@ -0,0 +1,18 @@
---
sculpin_content_types:
pages:
permalink: /:basename/
podcast_episodes:
permalink: /podcast/:basename/
posts:
permalink: /blog/:basename/
presentations:
permalink: /presentations/:basename/
services:
App\Experience\TwigExtension\ExperienceTwigExtension:
tags:
- {name: twig.extension}
App\Presentation\TwigExtension\PresentationTwigExtension:
tags:
- {name: twig.extension}

View file

@ -0,0 +1,355 @@
---
name: Oliver Davies
slogan: Certified Drupal expert, Developer and Consultant
email: oliver+website@oliverdavies.uk
url: http://localhost:8000
assets:
url: '%url%'
version: 6
banner_text: ~
code:
url: https://code.oliverdavies.uk
username: opdavies
ctas:
call: |
Are you still using Drupal 7 and dont know whats involved to upgrade to Drupal 10? <a href="%site.url%/call">Book a Drupal 7 upgrade consultation call</a> or <a href="%site.url%/drupal-upgrade">an upgrade roadmap</a>.
d7eol: |
Are you stuck on Drupal 7? <a href="%site.url%/drupal-upgrade">Plan your upgrade to Drupal 10 now!</a>
module: |
If you're creating a new Drupal module, try my <a href="https://github.com/opdavies/drupal-module-template">free Drupal module template</a>.
sponsor: |
Do you want to contribute more to open source but don't have the time? <a href="%site.url%/sponsor">Sponsor me to do it for you</a>!
subscription: |
Do you need immediate access to an expert Drupal Developer? With my <a href="%site.url%/subscription">Drupal development subscription</a>, make unlimited requests for a fixed monthly price in less time than posting to a job board!
testing_course: |
Do you want to learn about automated testing in Drupal? Take my <a href="%site.url%/atdc">free 10-day email course</a> and get daily lessons straight to your inbox.
pair: |
Need help or want another pair of eyes on your code? Book a <a href="%site.url%/call">1-on-1 consulting call</a> or an <a href="%site.url%/pair">online pair programming session</a> with a 100% money-back guarantee.,
drupalorg:
url: https://www.drupal.org/u/%drupalorg.username%
username: opdavies
features:
new_css: false
show_blog_post_count: true
github:
url: https://github.com/%github.username%
username: opdavies
mastodon:
username: opdavies
url: https://mastodon.social/@%mastodon.username%
menu_links:
- title: Home
url: /
- title: Presentations
url: /presentations
- title: Blog
url: /blog
- title: Podcast
url: /podcast
attributes:
title: The Beyond Blocks Podcast
- title: Books
url: /books
- title: Sponsor me
url: /sponsor
- title: Contact
url: 'mailto:%email%'
attributes:
title: Contact Oliver
meta:
description: |-
Oliver is an Acquia-certified Triple Drupal expert, core contributor, Developer, Consultant and multiple-time DrupalCon speaker.
testimonials:
- text: |
Oliver devised a creative and original approach to Drupal theme switching based on Tailwind and provided us with the building blocks and basic theming.
He gave several helpful mentoring sessions with our front end developer to support us to build this approach into our site.
name: Technical Director, European knowledge platform
image: ~
tags: [front]
- text: |
This course is really great and I've recommended it to my coworkers here at Palantir.net.
name: Daniel Davis-Boxleitner
title: Senior Engineer at Palantir.net
image:
url: /assets/images/recommendations/daniel-davis-boxleitner.jpg
tags: [testing, atdc]
- text: |
The course was very informative. One of the biggest pain points with Drupal testing was that there was no clear and definitive guide on setting up the php unit XML file to get functional and kernel tests working right away. Your guide was fantastic and I will definitely be using it going forward in my module development for work.
name: Frank Landry
title: ~
image:
url: /assets/images/recommendations/frank-landry.jpg
tags: [testing, atdc]
- text: |
Well done. You've created a really excellent resource here that has the potential to bring Drupal development forward a huge leap. Youve managed to simplify and share some often complex seeming issues.
name: Adam Nuttall
title: Drupal Engineer
image:
url: /assets/images/recommendations/adam-nuttall.jpg
tags: [testing, atdc]
- text: |
I'm liking your short emails. They're just the right length that isn't too distracting but I'm able to consume it in a single glance.
name: Kevin Coyle
title: Design System Engineering Consultant
url: https://www.coyledesign.co.uk
image:
url: /assets/images/recommendations/kevin-coyle.jpg
tags: [daily]
- text: |
I really love your daily posts. They are opinionated, and this gives room for thoughts, I appreciate this.
name: Boris Böhne
title: Drupal Developer
url: https://www.drupal.org/u/drubb
image:
url: /assets/images/recommendations/boris-bohne.jpg
tags: [daily]
- text: |
Following your "Automated tests" emails and they are great! Such a pleasant reading. I love how you start from the very beginning and keeping things simple, step by step.
Looking forward to more content!
name: Matthieu Scarset
title: Drupal Expert
url: https://matthieuscarset.com
image:
url: /assets/images/recommendations/matthieu-scarset.jpg
tags: [testing, atdc]
- text: |
Hi Oliver, we met briefly at the Tech Connect event in London last month. Been reading through a few of your latest posts and have found the messages valuable, especially as we spent the week learning about unit, integration and e2e testing. I have signed up to your mailing list to keep the good advice flowing!
name: Alexander Carr
title: Full Stack Software Engineer at School of Code
image:
url: /assets/images/recommendations/alexander-carr.jpg
tags: [daily]
- text: |
These emails are superb and make for very interesting reading. Thank you!
name: Adam Nuttall
title: Drupal Engineer
image:
url: /assets/images/recommendations/adam-nuttall.jpg
tags: [daily]
- text: Oliver's approach to testing is a continual reminder of his commitment to delivering high-quality, bug-free, software.
name: Mike Karthauser
title: Senior Software Engineer
image:
url: /assets/images/recommendations/mike-karthauser.jpg
tags: [daily, testing, coaching, atdc]
- text: |
I had the opportunity and good fortune to work with Oliver solving two problems that I was having on a Drupal Commerce site. I have done several Drupal sites using UberCart, but since it is deprecated, I chose to use Commerce. I had searched, posted to forums, and other normal means to find answers to my problems, to no response and to no avail.
I got a referral to Oliver and scheduled an appointment to discuss the problems on a Zoom call. After showing him via screen share where I was stumped, he offered different approaches to what I was doing, which I was fine with as long as it worked.
Once we solved the first problem, I was really elated and then focused on the second one, which was an easier fix. So in a short period of time, both problems were fixed and tested.
I found Oliver was affable and easy to work with. He has a strong work ethic and a desire to solve problems for his customers and can recommend working with him. I think one of his strengths is to find alternative solutions to problems.
name: Tom Evans
title: ~
image: ~
tags: [call]
- text: |
I am a big fan of your git approaches. I especially remember pairing with you and watching how many commands you run to solve many problems and how fast you were. It's a skill I believe not many have, particularly those who are used to working with a GUI like me, and personally I think it is quite valuable.
name: Marcos Duran
title: Senior Software Engineer
image:
url: /assets/images/recommendations/marcos-duran.jpg
tags: [git, daily, coaching]
- text: |
I like the "$ git log -S" and "$ git log --grep" commands, will definitely be using these, thanks!
name: Stephen Mulvihill
title: Solutions Architect
image:
url: /assets/images/recommendations/stephen-mulvihill.jpg
tags: [git, daily, coaching]
- text: |
Just wanted to say that your blog is amazing <3 I absolutely love it and usually share it with colleagues and some of the kids at my Code Club.
Thanks for contributing to the community with your amazing content!
name: Patty O'Callaghan
title: Tech Lead
url: https://pattyocallaghan.com
image:
url: /assets/images/recommendations/patty-ocallaghan.jpg
tags: [daily]
- text: |
I've wanted to explore testing for a while, but as a PHP developer with 10 years of Drupal experience who'd written next to no tests, I really needed guidance. Oliver's expertise in testing and TDD motivated me to seek his help.
Before our call, I'd started writing tests for my modules but needed direction, understanding the code to a degree but needing help with approach. Oliver clarified both unit and integration testing, providing solutions for my challenges, and shared his code for inspiration and help. He also gave me ideas on how to utilise contrib code to help me further.
Consulting with an expert, I gained the clarity and confidence I needed in tackling testing with structured, maintainable practices. Oliver's adaptability and tailored services make him highly recommendable.
Thanks, Oliver - I feel empowered and know exactly what approach to take now!
name: Tawny Bartlett
title: Senior Drupal Developer
url: https://www.playingwithpixels.co.uk
image:
url: /assets/images/recommendations/tawny.jpg
tags: [testing, coaching, call, atdc]
- text: |
I've worked with Oliver for a number of years on B2C and B2B web projects and he has always demonstrated himself to be an expert in his field.
As an insurance provider, some of our products and services don't naturally fit within a traditional ecommerce journey - but Oliver has always been able to come up with innovative ways to leverage core Drupal functionality and develop custom modules to meet our needs.
Friendly, flexible and diligent - I wouldn't hesitate to recommend Oliver to anyone looking for a Drupal developer to progress their next project.
name: Joe Howell
title: Director, Bastion Insurance
url: https://www.bastioninsurance.co.uk
image:
url: /assets/images/recommendations/joe-howell.jpg
tags: [front, subscription, coaching]
- text: |
We use Oliver for maintaining a couple of Drupal sites for which we no longer have the skills ourselves. We became aware of Oliver through his work in the Drupal community, and about a year ago we approached him to help us with the deep dive aspects of maintaining and developing Drupal sites. He's been really helpful and very responsive. Much appreciated!
name: Jon Hallett
title: Senior Systems Administrator at the University of Bristol
url: https://bristol.ac.uk
image:
url: /assets/images/recommendations/jon-hallett.jpeg
tags: [front, subscription]
- text: |
For over a decade we have worked with Oliver on a number of different projects. Initially our collaboration consisted of web maintenance and troubleshooting but we soon tapped Oliver to design, build and maintain a custom awards site which includes both submission and judging functionality. Oliver has deep and wide-ranging skills and I would certainly recommend his services!
name: Michael Itkoff
title: Cofounder
url: https://www.daylightbooks.org
image:
url: /assets/images/recommendations/michael-itkoff.jpg
tags: [front, subscription]
- text: |
Working with Oliver on the Seren website has been easy and beneficial.
As well as providing general maintenance support, he built a new Drupal module which integrated with our partners Glassboxx so that we could sell ebooks directly from our website. Oliver worked closely with the team at Glassboxx to create the integration which needed to communicate with the Glassboxx app so that users could download their purchases. He was able to resolve issues which came up along the way in order to create a functioning module which we now use on our site.
Oliver has extensive knowledge of Drupal and his familiarity with the Seren site meant he was able to fix problems quickly and efficiently as they arose.
He is reliable and has always been willing to discuss new ideas for how the site could function.
We would recommend working with him for his invaluable knowledge and ability to find solutions to problems at short notice. It has been a pleasure to work with him over the years.
name: Mick Felton
title: Publisher at Poetry Wales Press Ltd (Seren Books)
url: https://www.serenbooks.com
image:
url: /assets/images/recommendations/mick-felton.jpg
tags: [front, subscription, coaching]
- text: |
Oliver is a pleasure to work with, and I would engage him again without hesitation. He communicates regularly, ensures that he meets requirements, and suggests improvements to the potential solutions to the brief.
name: Duncan Davidson
title: Director at Rohallion
url: https://rohallion.agency
image:
url: /assets/images/recommendations/duncan.jpeg
tags: [front, subscription, coaching]
- text: |
We have only worked together for a short while but I can see Oliver is a Drupal expert.
His technical knowledge means we have been able to make improvements to the sites we manage quickly and efficiently.
If we have complex issues to contend with in the future I feel confident he will be able to deal with them.
name: Anonymous
title: Marketing Strategist
tags: [front, subscription]
- text: |
A fantastic and highly knowledgeable Drupal Developer. Oliver saved a struggling Drupal project with his wealth of Drupal experience.
name: Adam Cuddihy
title: Web Development Manager
url: ~
image:
url: /assets/images/recommendations/adam.jpeg
tags: [front, subscription]
- text: |
I had the pleasure of working with Oliver whilst building the first version of our drupal based intranet. His knowledge of Drupal and the wider infrastructure required to run a site was really invaluable.
At the time, we were very new to Drupal, so it gave us a great platform to learn from and expand our own knowledge.
He's the only external contractor that we've kept in touch with over the years, which goes to show how much we valued his input.
name: Huw Davies
title: Web Dev Manager / DevOps / Team Manager at Admiral Group Plc
url: https://admiral.com
image:
url: /assets/images/recommendations/huw.jpeg
tags: [front, subscription]
- text: |
Oliver really knows his stuff. Whether you are just starting out or looking to take your knowledge to the next level, his patient and clear way of explaining will help get you there.
name: Scott Euser
title: Head of Web Development
url: ~
image:
url: /assets/images/recommendations/scott-euser.jpg
tags: [testing, coaching]
- text: |
I have had the pleasure of working with Oliver on several projects at Microserve. He is a natural innovator and a great mentor who inspires others to explore new technologies and approaches. He is a highly knowledgeable professional with a passion for all things Drupal and the tenacity required to get the job done well.
name: Alan Hatch
title: Senior Drupal Developer at Microserve
url: ~
image:
url: /assets/images/recommendations/alan.jpeg
tags: [coaching]
- text: |
Oliver has been an outstanding contributor to the Drupal Association team. He is a talented developer who writes great code and applies his curiosity and love of learning to every project. He is also a fantastic team member, who gives to the team as much as he gets.
Oliver is the embodiment of everything good about the Drupal community.
name: Holly Ross
title: Executive Director at the Drupal Association
url: https://www.drupal.org/association
image:
url: /assets/images/recommendations/holly-ross.png
tags: [front, subscription]
- text: |
Oliver is a skilled Drupal developer with a passion for the Drupal community. As his direct supervisor, I was able to watch Oliver grow with the Drupal Association and contribute an amazing amount of effort and integrity to all of his work.
Everything we have thrown at Oliver, he has approached with an open and flexible mind that has allowed him to work on a wide range of projects and features for Drupal products.
name: Josh Mitchell
title: CTO at Drupal Association
url: https://joshuami.com
image:
url: /assets/images/recommendations/josh-mitchell.png
tags: [front, subscription]
- text: |
Oliver was great to work with. He has expert knowledge with Drupal and delivered exactly what we were looking for on time. He's understanding, friendly and easy to get along with. I would enjoy working with him again in the future.
name: Brian Hartwell
title: Interactive Creative Director
url: ~
image: ~
tags: [front, subscription]
- text: |
Oliver was fantastic to work with - pro-active and highly responsive, he worked well remotely and as part of a project team. His understanding of the project requirement(s) and ability to translate it into working code was essential and he delivered.
name: Brian Healy
title: Director of Business Development at Tincan
url: ~
image:
url: /assets/images/recommendations/brian-healy.png
tags: [front, subscription]
- text: |
Oliver is an amazing colleague, he's professional, full of knowledge and I could not recommend him more.
name: Chris Jarvis
title: Developer at Microserve
url: ~
image:
url: /assets/images/recommendations/chris-jarvis.jpg
- text: |
Oliver is seasoned Drupal and all round highly skilled and experienced web developer. I have worked with Oliver on an important project where he was reliable, prompt and ensured strict client deadline delivery and confidentiality at all times.
name: Daniel Easterbrook
title: Digital Strategy Consultant
tags: [front, subscription]
prose_classes: |
prose prose-p:text-black prose-a:font-light prose-a:text-blue-primary prose-p:text-lg prose-blockquote:border-blue-primary dark:marker:text-white prose-li:my-1 prose-li:text-lg prose-figcaption:text-white prose-li:text-black marker:text-black dark:prose-p:text-white dark:prose-invert dark:prose-a:text-blue-400 dark:prose-blockquote:border-blue-400 dark:prose-li:text-white hover:prose-a:no-underline prose-h2:text-xl prose-code:font-normal prose-h2:mb-4 prose-ul:my-3 dark:prose-hr:border-grey-400 prose-code:before:content-[''] prose-code:after:content-['']
transistor:
feed:
url: https://feeds.transistor.fm/beyond-blocks
share:
url: https://share.transistor.fm/e
youtube:
channel:
slug: opdavies
url: https://www.youtube.com/@%youtube.channel.slug%

View file

@ -0,0 +1,8 @@
---
imports:
- sculpin_site.yml
features:
new_css: false
url: https://www.oliverdavies.uk

View file

@ -0,0 +1,11 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
@font-face {
font-family: Roboto Condensed;
font-weight: 300 400 500 700;
src: url('../assets/fonts/RobotoCondensed-VariableFont_wght.ttf') format('truetype');
}
}

View file

@ -0,0 +1,38 @@
import colours from "tailwindcss/colors";
import type { Config } from "tailwindcss";
import formsPlugin from '@tailwindcss/forms';
import typographyPlugin from "@tailwindcss/typography";
export default {
content: [
"../app/config/**/*.yml",
"../source/**/*.{md,twig}",
],
theme: {
colors: {
black: "#000",
blue: {
primary: "#24608A",
400: "#60a5fa",
},
current: "currentColor",
grey: colours.stone,
inherit: "inherit",
transparent: "transparent",
white: "#fff",
},
extend: {
fontFamily: {
sans: [
"Roboto Condensed",
"Arial",
"Helvetica Neue",
"Helvetica",
"sans-serif",
],
},
},
},
plugins: [formsPlugin, typographyPlugin],
} satisfies Config;

5
sculpin/build Executable file
View file

@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -uexo pipefail
vendor/bin/sculpin generate --watch --server "$@"

28
sculpin/composer.json Normal file
View file

@ -0,0 +1,28 @@
{
"require": {
"opdavies/sculpin-twig-markdown-bundle": "^0.2.0",
"sculpin/sculpin": "^3.2"
},
"config": {
"allow-plugins": {
"sculpin/sculpin-theme-composer-plugin": true,
"phpstan/extension-installer": true
}
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"require-dev": {
"phpunit/phpunit": "^12.1",
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-strict-rules": "^2.0",
"phpstan/extension-installer": "^1.4"
}
}

5312
sculpin/composer.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
{ inputs, ... }:
{
imports = [ inputs.devshell.flakeModule ];
perSystem =
{ pkgs, self', ... }:
{
devshells.default = {
packages = with pkgs; [
nodePackages.browser-sync
(php83.buildEnv {
extraConfig = ''
error_reporting = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
'';
})
php83Packages.composer
self'.packages.drafts
self'.packages.new-draft
self'.packages.publish
tailwindcss
];
};
};
}

View file

@ -0,0 +1,16 @@
{
perSystem =
{ pkgs, ... }:
{
packages.drafts = pkgs.writeShellApplication {
name = "drafts";
runtimeInputs = with pkgs; [ gnugrep ];
text = ''
echo "oob"
grep -r 'draft: true' "''${1:-source}"
'';
};
};
}

View file

@ -0,0 +1,33 @@
{
perSystem =
{ pkgs, ... }:
{
packages.new-draft = pkgs.writeShellApplication {
name = "new-draft";
runtimeInputs = with pkgs; [
coreutils
git
];
text = ''
title="$1"
exclude_words="for|at|and"
filtered_title=$(echo "$title" | sed -E "s/\b($exclude_words)\b//g")
slug=$(echo "$filtered_title" | tr '[:upper:]' '[:lower:]' | sed -e 's/[^a-z0-9]/-/g' -e 's/-\+/-/g' -e 's/^-//g' -e 's/-$//g')
file="source/_posts/$slug.md"
echo -e "---\ntitle: $title\ndate: ~\ndraft: true\n---" > "$file"
echo "File created: $file"
git add "$file"
git commit -s -m "Add draft: $title"
'';
};
};
}

View file

@ -0,0 +1,21 @@
{
perSystem =
{ pkgs, ... }:
{
packages.publish = pkgs.writeShellApplication {
name = "publish";
runtimeInputs = with pkgs; [
rsync
php
];
text = ''
vendor/bin/sculpin generate --env prod
rsync -uvrP static/ output_prod "$@"
rsync -uvrP output_prod/ nixedo.oliverdavies.uk:/var/www/vhosts/website-sculpin "$@"
'';
};
};
}

View file

@ -0,0 +1,3 @@
{
systems = [ "x86_64-linux" ];
}

98
sculpin/flake.lock generated Normal file
View file

@ -0,0 +1,98 @@
{
"nodes": {
"devshell": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1741473158,
"narHash": "sha256-kWNaq6wQUbUMlPgw8Y+9/9wP0F8SHkjy24/mN3UAppg=",
"owner": "numtide",
"repo": "devshell",
"rev": "7c9e793ebe66bcba8292989a68c0419b737a22a0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1754487366,
"narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"import-tree": {
"locked": {
"lastModified": 1752730890,
"narHash": "sha256-GES8fapSLGz36MMPRVNkSUWXUTtqvGQNXHjRmRLfJUY=",
"owner": "vic",
"repo": "import-tree",
"rev": "6ebb8cb87987b20264c09296166543fd3761d274",
"type": "github"
},
"original": {
"owner": "vic",
"repo": "import-tree",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1755615617,
"narHash": "sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs+StOp19xNsbqdOg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "20075955deac2583bb12f07151c2df830ef346b4",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1753579242,
"narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"devshell": "devshell",
"flake-parts": "flake-parts",
"import-tree": "import-tree",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

20
sculpin/flake.nix Normal file
View file

@ -0,0 +1,20 @@
{
inputs = {
devshell.inputs.nixpkgs.follows = "nixpkgs";
devshell.url = "github:numtide/devshell";
flake-parts.url = "github:hercules-ci/flake-parts";
import-tree.url = "github:vic/import-tree";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs =
inputs:
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
(inputs.import-tree ./flake-modules)
];
};
}

View file

@ -0,0 +1,19 @@
parameters:
ignoreErrors:
-
message: '#^Dynamic call to static method PHPUnit\\Framework\\Assert\:\:identicalTo\(\)\.$#'
identifier: staticMethod.dynamicCall
count: 1
path: tests/Presentation/TwigExtension/PresentationTwigExtensionTest.php
-
message: '#^Static method PHPUnit\\Framework\\Assert\:\:assertSame\(\) invoked with named argument \$actual, but it''s not allowed because of @no\-named\-arguments\.$#'
identifier: argument.named
count: 1
path: tests/Presentation/TwigExtension/PresentationTwigExtensionTest.php
-
message: '#^Static method PHPUnit\\Framework\\Assert\:\:assertSame\(\) invoked with named argument \$expected, but it''s not allowed because of @no\-named\-arguments\.$#'
identifier: argument.named
count: 1
path: tests/Presentation/TwigExtension/PresentationTwigExtensionTest.php

View file

@ -0,0 +1,7 @@
parameters:
level: 9
paths:
- src
- tests
includes:
- phpstan-baseline.neon

8
sculpin/phpunit.xml.dist Normal file
View 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>

44
sculpin/run Executable file
View file

@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
set -o errexit
set -o nounset
set -o pipefail
PATH="${PATH}:./vendor/bin"
# Generate the site.
function generate {
local args=()
if [[ "${APP_ENV:-}" == "prod" ]]; then
args=(--env="prod")
else
args=(--server --watch)
fi
sculpin generate "${args[@]}" "${@}"
}
function help {
printf "%s <task> [args]\n\nTasks:\n" "${0}"
compgen -A function | grep -v "^_" | cat -n
printf "\nExtended help:\n Each task has comments for general usage\n"
}
# Start the project.
function start {
sculpin generate --server --watch "${@}"
}
# Include any local tasks.
# https://stackoverflow.com/a/6659698
[[ -e "${BASH_SOURCE%/*}/run.local" ]] && source "${BASH_SOURCE%/*}/run.local"
TIMEFORMAT="Task completed in %3lR"
time "${@:-help}"
# vim: ft=bash

114
sculpin/run.local Executable file
View file

@ -0,0 +1,114 @@
#!/usr/bin/env bash
function ci:build {
APP_ENV=prod nix develop --command ./run generate
}
function ci:install {
nix develop --command composer install
}
function ci:test {
nix develop --command ./run test --testdox --colors=always
}
function clean {
rm -fr output_*/ source/build/
}
# Create a new daily email.
function create-daily {
local date="${1}"
local title="${2}"
if [ "${date}" == "next" ]; then
next_date=$(ls -1 source/_daily_emails | tail -n 1 | tr -d '.md' | xargs -I {} date +%Y-%m-%d -d '{} +1 day')
else
next_date="${date}"
fi
filepath="source/_daily_emails/${next_date}.md"
shift 1
# Generate the title and slug.
title="${*}"
slug=$(echo "${title}" | \
tr '[:upper:]' '[:lower:]' | \
sed 's/[^a-z0-9]/-/g' | \
sed 's/\-\-+/-/g' | \
sed 's/^\-//;s/\-$//')
# Create the file.
cp -f --no-clobber resources/daily-email-stub.md "${filepath}"
date=$(date -d "${next_date}" +%Y-%m-%d)
day=$(date -d "${next_date}" +%d)
month=$(date -d "${next_date}" +%m)
year=$(date -d "${next_date}" +%Y)
# Replace the placeholders.
sed -i "s/{{ date }}/${date}/" "${filepath}"
sed -i "s/{{ title }}/${title}/" "${filepath}"
sed -i "s#{{ permalink }}#daily/${year}/${month}/${day}/${slug}#" "${filepath}"
# Create a commit with the appropriate date in the message
git add "${filepath}"
git commit --quiet -m "Add daily email for $(date -d "${next_date}" +%d/%m/%y)"
echo "${filepath}"
}
# Build CSS assets, this is meant to be run within the `assets` directory.
function npm:build:css {
local args=()
if [[ "${NODE_ENV:-}" == "production" ]]; then
args=(--minify)
else
args=(--watch)
fi
cd assets || exit
tailwindcss \
--config tailwind.config.ts \
--input css/tailwind.css \
--output ../source/build/tailwind.css "${args[@]}"
}
function setup {
git submodule update --recursive --init
git submodule update --recursive --remote || true
composer install
(cd assets && pnpm install)
(cd assets && NODE_ENV=production npm:build:css)
APP_ENV=prod generate
}
function publish {
git push
tag-release
git push --tags
git stash
clean
# export NODE_ENV=production
# (cd assets && npm:build:css)
APP_ENV=prod generate
rsync --archive --verbose --compress --update --delete \
output_prod/ ssh.oliverdavies.uk:/var/www/vhosts/website-sculpin
git stash pop
}
function test {
phpunit "${@}"
}
# vim: ft=bash

445
sculpin/source/.htaccess Normal file
View file

@ -0,0 +1,445 @@
Options +FollowSymLinks -MultiViews
RewriteEngine on
# Redirect all users to access the site WITH the 'www.' prefix.
RewriteCond %{HTTP_HOST} .
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Remove trailing slashes from directories.
DirectorySlash Off
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{REQUEST_FILENAME}/index.html -f
RewriteRule (.*) $1/index.html [L]
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Remove index.html from URLs.
RewriteCond %{THE_REQUEST} \s/+(.*/)?index\.html[\s?] [NC]
RewriteRule ^(.*)index\.html$ /$1 [L,R=301]
ErrorDocument 404 /404/index.html
RewriteRule ^daily/2024/08/15/docblocks-or-annotations$ /daily/2024/08/15/docblocks-or-attributes [R=301]
RewriteRule ^10-useful-drupal-6-modules-i-use-every-project$ /blog/10-useful-drupal-6-modules [R=301]
RewriteRule ^2010/04/05/styling-drupal-6s-taxonomy-lists-with-php-css-and-jquery$ /blog/style-drupal-6s-taxonomy-lists-php-css-jquery [R=301]
RewriteRule ^2010/04/28/using-imagecache-and-imagecrop-for-my-portfolio$ /blog/using-imagecache-imagecrop-my-portfolio [R=301]
RewriteRule ^2010/05/29/importing-images-using-the-imagefieldimport-module$ /blog/quickly-import-multiples-images-using-imagefieldimport-module [R=301]
RewriteRule ^2010/06/23/creating-a-block-of-social-media-icons-using-cck-views-and-nodequeue$ /blog/create-block-social-media-icons-using-cck-views-nodequeue [R=301]
RewriteRule ^2010/07/05/thanks$ /blog/thanks [R=301]
RewriteRule ^2010/08/17/create-a-better-photo-gallery-in-drupal-part-2$ /blog/create-better-photo-gallery-drupal-part-2 [R=301]
RewriteRule ^2014/05/21/git-format-patch$ /blog/git-format-patch-your-friend [R=301]
RewriteRule ^2PxmyqP$ /articles/examples-of-laravel-collections-in-drupal [R=301]
RewriteRule ^39CoG$ /articles/drupalcamp-london-testing-workshop [R=301]
RewriteRule ^3eGQr$ https://github.com/howToCodeWell/howToCodeWellFM/blob/c927e0b3589f1d7375002f7fd70f0bfc9fc90449/composer.json#L17 [R=301]
RewriteRule ^6UhLN$ https://github.com/opdavies/sculpin-twig-markdown-bundle/pull/1 [R=301]
RewriteRule ^6i3YZ$ https://www.youtube.com/watch?v=vUK5sEbd-dk [R=301]
RewriteRule ^9rv0Z$ https://www.drupal.org/project/override_node_options/issues/3109852 [R=301]
RewriteRule ^BhMZi$ https://git.drupalcode.org/search?utf8=%E2%9C%93&snippets=&scope=&repository_ref=8.x-1.x&search=baz&project_id=23203 [R=301]
RewriteRule ^NBi5h$ https://git.drupalcode.org/search?utf8=%E2%9C%93&search=bar&group_id=&project_id=23203&search_code=true&repository_ref=8.x-1.x&nav_source=navbar [R=301]
RewriteRule ^P5KQ5$ https://www.npmjs.com/package/tailwindcss-skip-link [R=301]
RewriteRule ^S8ZDA$ /articles/rebuilding-bartik-with-vuejs-tailwind-css-part-2 [R=301]
RewriteRule ^Wh48P$ https://github.com/opdavies/oliverdavies.uk/blob/master/source/_partials/talk/video.html.twig [R=301]
RewriteRule ^XbzS2$ https://github.com/opdavies/gmail-filter-builder [R=301]
RewriteRule ^YK1VH$ /articles/psr4-autoloading-test-cases-drupal-7 [R=301]
RewriteRule ^Yil$ https://drupalcamp.london/tickets/training [R=301]
RewriteRule ^about$ / [R=301]
RewriteRule ^about/cv$ /cv [R=301]
RewriteRule ^about/speaker$ /speaker [R=301]
RewriteRule ^about/speaker-information$ /speaker-information [R=301]
RewriteRule ^acquia-certifications$ https://certification.acquia.com/registry?fname=Oliver&lname=Davies&city=&state=&country=United+Kingdom&org=&exam=All [R=301]
RewriteRule ^acquia-certified$ https://certification.acquia.com/?fname=Oliver&lname=Davies [R=301]
RewriteRule ^ansible$ https://galaxy.ansible.com/opdavies [R=301]
RewriteRule ^ansible-molecule$ /articles/test-driven-ansible-role-development-molecule [R=301]
RewriteRule ^ansistrano-code$ https://github.com/opdavies/dransible [R=301]
RewriteRule ^ansistrano-demo$ https://www.youtube.com/watch?v=PLS4ET7FAcU [R=301]
RewriteRule ^ansistrano-slides$ /presentations/deploying-php-ansible-ansistrano [R=301]
RewriteRule ^archive/2022/10/20/run-vs-task-runner$ /archive/2022/10/19/run-vs-task-runners [R=301]
RewriteRule ^atNOQ$ https://youtu.be/r41dkD2EOo8 [R=301]
RewriteRule ^automatically-updating-talk-created-date$ https://gist.github.com/opdavies/4e75e1753d8603113f07f8264bb783d6 [R=301]
RewriteRule ^blog.xml$ /rss.xml [R=301]
RewriteRule ^blog/10-useful-drupal-6-modules$ /blog/useful-drupal-6-modules [R=301]
RewriteRule ^blog/10-years-working-full-time-drupal$ /blog/10-years-working-full-time-drupal-php [R=301]
RewriteRule ^blog/2010/04/05/style-drupal-6s-taxonomy-lists-php-css-and-jquery$ /blog/style-drupal-6s-taxonomy-lists-php-css-and-jquery [R=301]
RewriteRule ^blog/2010/04/05/styling-drupal-6s-taxonomy-lists-with-php-css-and-jquery$ /blog/style-drupal-6s-taxonomy-lists-php-css-jquery [R=301]
RewriteRule ^blog/2010/04/28/using-imagecache-and-imagecrop-my-portfolio$ /blog/using-imagecache-and-imagecrop-my-portfolio [R=301]
RewriteRule ^blog/2010/05/06/conditional-email-addresses-webform$ /blog/conditional-email-addresses-webform [R=301]
RewriteRule ^blog/2010/05/10/quickly-create-zen-subthemes-using-zenophile$ /blog/quickly-create-zen-subthemes-using-zenophile [R=301]
RewriteRule ^blog/2010/05/25/create-slideshow-multiple-images-using-fancy-slide$ /blog/create-slideshow-multiple-images-using-fancy-slide [R=301]
RewriteRule ^blog/2010/05/29/quickly-import-multiples-images-using-imagefieldimport-module$ /blog/quickly-import-multiples-images-using-imagefieldimport-module [R=301]
RewriteRule ^blog/2010/06/02/improve-jpg-quality-imagecache-and-imageapi$ /blog/improve-jpg-quality-imagecache-and-imageapi [R=301]
RewriteRule ^blog/2010/06/23/create-block-social-media-icons-using-cck-views-and-nodequeue$ /blog/create-block-social-media-icons-using-cck-views-and-nodequeue [R=301]
RewriteRule ^blog/2010/06/25/10-useful-drupal-6-modules$ /blog/10-useful-drupal-6-modules [R=301]
RewriteRule ^blog/2010/06/28/create-flickr-photo-gallery-using-feeds-cck-and-views$ /blog/create-flickr-photo-gallery-using-feeds-cck-and-views [R=301]
RewriteRule ^blog/2010/07/01/change-content-type-multiple-nodes-using-sql$ /blog/change-content-type-multiple-nodes-using-sql [R=301]
RewriteRule ^blog/2010/07/02/create-virtual-hosts-mac-os-x-using-virtualhostx$ /blog/create-virtual-hosts-mac-os-x-using-virtualhostx [R=301]
RewriteRule ^blog/2010/07/07/add-taxonomy-term-multiple-nodes-using-sql$ /blog/add-taxonomy-term-multiple-nodes-using-sql [R=301]
RewriteRule ^blog/2010/07/07/quickly-adding-taxonomy-term-multiple-nodes-using-sql$ /blog/add-taxonomy-term-multiple-nodes-using-sql [R=301]
RewriteRule ^blog/2010/07/12/overview-teleport-module$ /blog/review-teleport-module [R=301]
RewriteRule ^blog/2010/07/12/review-teleport-module$ /blog/review-teleport-module [R=301]
RewriteRule ^blog/2010/08/10/review-adminhover-module$ /blog/review-adminhover-module [R=301]
RewriteRule ^blog/2010/08/11/create-better-photo-gallery-drupal-part-1$ /blog/create-better-photo-gallery-drupal-part-1 [R=301]
RewriteRule ^blog/2010/08/11/how-create-better-photo-gallery-drupal-part-1$ /blog/create-better-photo-gallery-drupal-part-1 [R=301]
RewriteRule ^blog/2010/08/17/create-better-photo-gallery-drupal-part-2$ /blog/create-better-photo-gallery-drupal-part-2 [R=301]
RewriteRule ^blog/2010/08/20/review-image-caption-module$ /blog/review-image-caption-module [R=301]
RewriteRule ^blog/2010/09/26/south-wales-drupal-user-group$ /blog/south-wales-drupal-user-group [R=301]
RewriteRule ^blog/2010/10/10/create-and-apply-patches$ /blog/create-and-apply-patches [R=301]
RewriteRule ^blog/2010/10/13/create-better-photo-gallery-drupal-part-3$ /blog/create-better-photo-gallery-drupal-part-3 [R=301]
RewriteRule ^blog/2010/10/22/create-better-photo-gallery-drupal-part-21$ /blog/create-better-photo-gallery-drupal-part-21 [R=301]
RewriteRule ^blog/2010/11/04/use-regular-expressions-search-and-replace-coda-or-textmate$ /blog/use-regular-expressions-search-and-replace-coda-or-textmate [R=301]
RewriteRule ^blog/2011/02/14/easily-embed-typekit-fonts-your-drupal-website$ /blog/easily-embed-typekit-fonts-your-drupal-website [R=301]
RewriteRule ^blog/2011/03/15/display-number-facebook-fans-php$ /blog/display-number-facebook-fans-php [R=301]
RewriteRule ^blog/2011/03/31/proctor-stevenson$ /blog/proctor-stevenson [R=301]
RewriteRule ^blog/2011/05/20/proctors-hosting-next-drupal-meetup$ /blog/proctors-hosting-next-drupal-meetup [R=301]
RewriteRule ^blog/2011/05/23/imagefield-import-archive$ /blog/imagefield-import-archive [R=301]
RewriteRule ^blog/2011/08/28/create-multigroups-drupal-7-using-field-collections$ /blog/create-multigroups-drupal-7-using-field-collections [R=301]
RewriteRule ^blog/2011/10/19/install-and-configure-subversion-svn-server-ubuntu$ /blog/install-and-configure-subversion-svn-server-ubuntu [R=301]
RewriteRule ^blog/2011/10/install-and-configure-subversion-svn-server-ubuntu$ /blog/how-install-configure-subversion-svn-server-ubuntu [R=301]
RewriteRule ^blog/2012/01/04/site-upgraded-drupal-7$ /blog/site-upgraded-drupal-7 [R=301]
RewriteRule ^blog/2012/02/01/use-authorize-keys-create-passwordless-ssh-connection$ /blog/use-authorized-keys-create-passwordless-ssh-connection [R=301]
RewriteRule ^blog/2012/04/16/create-omega-subtheme-less-css-preprocessor-using-omega-tools-and-drush$ /blog/create-omega-subtheme-less-css-preprocessor-using-omega-tools-and-drush [R=301]
RewriteRule ^blog/2012/04/17/installing-nagios-centos$ /blog/installing-nagios-centos [R=301]
RewriteRule ^blog/2012/04/19/adding-custom-theme-templates-drupal-7$ /blog/adding-custom-theme-templates-drupal-7 [R=301]
RewriteRule ^blog/2012/04/adding-custom-theme-templates-drupal-7$ /blog/adding-custom-theme-templates-drupal-7 [R=301]
RewriteRule ^blog/2012/05/23/add-date-popup-calendar-custom-form$ /blog/add-date-popup-calendar-custom-form [R=301]
RewriteRule ^blog/2012/05/23/checkout-specific-revision-svn-command-line$ /blog/checkout-specific-revision-svn-command-line [R=301]
RewriteRule ^blog/2012/05/23/forward-one-domain-another-using-mod-rewrite-and-htaccess$ /blog/forward-one-domain-another-using-mod-rewrite-and-htaccess [R=301]
RewriteRule ^blog/2012/05/23/forward-one-domain-another-using-modrewrite-and-htaccess$ /blog/forward-one-domain-another-using-modrewrite-htaccess [R=301]
RewriteRule ^blog/2012/05/23/prevent-apache-displaying-text-files-within-web-browser$ /blog/prevent-apache-displaying-text-files-within-web-browser [R=301]
RewriteRule ^blog/2012/05/23/writing-info-file-drupal-7-theme$ /blog/writing-info-file-drupal-7-theme [R=301]
RewriteRule ^blog/2012/05/24/dividing-drupals-process-and-preprocess-functions-separate-files$ /blog/dividing-drupals-process-and-preprocess-functions-separate-files [R=301]
RewriteRule ^blog/2012/05/forward-one-domain-another-using-modrewrite-and-htaccess$ /blog/forward-one-domain-another-using-modrewrite-htaccess [R=301]
RewriteRule ^blog/2012/07/12/my-new-drupal-modules$ /blog/my-new-drupal-modules [R=301]
RewriteRule ^blog/2012/07/14/install-nomensa-media-player-drupal$ /blog/install-nomensa-media-player-drupal [R=301]
RewriteRule ^blog/2012/07/27/writing-article-linux-journal$ /blog/writing-article-linux-journal [R=301]
RewriteRule ^blog/2012/07/install-and-configure-nomensa-accessible-media-player-drupal$ /blog/install-configure-nomensa-accessible-media-player-drupal [R=301]
RewriteRule ^blog/2012/07/nomensa-accessible-media-player-drupal$ /blog/install-configure-nomensa-accessible-media-player-drupal [R=301]
RewriteRule ^blog/2012/08/18/display-custom-menu-drupal-7-theme-template-file$ /blog/display-custom-menu-drupal-7-theme-template-file [R=301]
RewriteRule ^blog/2012/09/06/reflections-speaking-unifieddiff$ /blog/reflections-speaking-unifieddiff [R=301]
RewriteRule ^blog/2012/10/25/my-sublime-text-2-settings$ /blog/my-sublime-text-2-settings [R=301]
RewriteRule ^blog/2012/11/15/accessible-bristol-site-launched$ /blog/accessible-bristol-site-launched [R=301]
RewriteRule ^blog/2012/11/17/open-sublime-text-2-mac-os-x-command-line$ /blog/open-sublime-text-2-mac-os-x-command-line [R=301]
RewriteRule ^blog/2012/12/06/use-sass-and-compass-drupal-7-using-sassy$ /blog/use-sass-and-compass-drupal-7-using-sassy [R=301]
RewriteRule ^blog/2012/12/use-sass-and-compass-drupal-7-using-sassy$ /blog/use-sass-and-compass-drupal-7-using-sassy [R=301]
RewriteRule ^blog/2013/01/09/checking-if-user-logged-drupal-right-way$ /blog/checking-if-user-logged-drupal-right-way [R=301]
RewriteRule ^blog/2013/02/16/creating-and-using-custom-tokens-drupal-7$ /blog/creating-and-using-custom-tokens-drupal-7 [R=301]
RewriteRule ^blog/2013/02/creating-and-using-custom-tokens-drupal-7$ /blog/creating-using-custom-tokens-drupal-7 [R=301]
RewriteRule ^blog/2013/03/02/quickest-way-install-sublime-text-2-ubuntu$ /blog/quickest-way-install-sublime-text-2-ubuntu [R=301]
RewriteRule ^blog/2013/04/20/leaving-nomensa-joining-precedent$ /blog/leaving-nomensa-joining-precedent [R=301]
RewriteRule ^blog/2013/04/27/display-git-branch-or-tag-names-your-bash-prompt$ /blog/display-git-branch-or-tag-names-your-bash-prompt [R=301]
RewriteRule ^blog/2013/04/display-git-branch-or-tag-names-your-bash-prompt$ /blog/display-git-branch-or-tag-names-your-bash-prompt [R=301]
RewriteRule ^blog/2013/06/13/some-useful-links-using-simpletest-drupal$ /blog/some-useful-links-using-simpletest-drupal [R=301]
RewriteRule ^blog/2013/07/17/creating-local-and-staging-sites-drupals-domain-module-enabled$ /blog/creating-local-and-staging-sites-drupals-domain-module-enabled [R=301]
RewriteRule ^blog/2013/07/26/going-drupalcon$ /blog/going-drupalcon [R=301]
RewriteRule ^blog/2013/09/06/create-a-zen-sub-theme-using-drush$ /blog/create-a-zen-sub-theme-using-drush [R=301]
RewriteRule ^blog/2013/09/create-zen-sub-theme-using-drush$ /blog/create-zen-sub-theme-using-drush [R=301]
RewriteRule ^blog/2013/11/19/dont-bootstrap-drupal-use-drush$ /blog/dont-bootstrap-drupal-use-drush [R=301]
RewriteRule ^blog/2013/11/27/useful-vagrant-commands$ /blog/useful-vagrant-commands [R=301]
RewriteRule ^blog/2013/11/dont-bootstrap-drupal-use-drush$ /blog/dont-bootstrap-drupal-use-drush [R=301]
RewriteRule ^blog/2013/12/24/quickly-apply-patches-using-git-and-curl-or-wget$ /blog/quickly-apply-patches-using-git-and-curl-or-wget [R=301]
RewriteRule ^blog/2013/12/31/download-different-versions-drupal-drush$ /blog/download-different-versions-drupal-drush [R=301]
RewriteRule ^blog/2013/12/quickly-apply-patches-using-git-and-curl-or-w$ /blog/quickly-apply-patches-using-git-curl-or-wget [R=301]
RewriteRule ^blog/2014/01/15/some-useful-git-aliases$ /blog/some-useful-git-aliases [R=301]
RewriteRule ^blog/2014/02/09/drupalcamp-london-2014$ /blog/drupalcamp-london-2014 [R=301]
RewriteRule ^blog/2014/03/03/what-git-flow$ /blog/what-git-flow [R=301]
RewriteRule ^blog/2014/05/03/drupal-association$ /blog/drupal-association [R=301]
RewriteRule ^blog/2014/05/06/thanks$ /blog/thanks [R=301]
RewriteRule ^blog/2014/05/21/git-format-patch$ /blog/git-format-patch [R=301]
RewriteRule ^blog/2014/07/02/drush-make-drupalbristol$ /blog/drush-make-drupalbristol [R=301]
RewriteRule ^blog/2014/10/06/fix-vagrant-loading-wrong-virtual-machine$ /blog/fix-vagrant-loading-wrong-virtual-machine [R=301]
RewriteRule ^blog/2014/10/21/updating-features-and-adding-components-using-drush$ /blog/updating-features-and-adding-components-using-drush [R=301]
RewriteRule ^blog/2014/11/18/include-css-fonts-using-sass-each-loop$ /blog/include-css-fonts-using-sass-each-loop [R=301]
RewriteRule ^blog/2014/11/20/using-remote-files-when-developing-locally-with-stage-file-proxy-module$ /blog/using-remote-files-when-developing-locally-with-stage-file-proxy-module [R=301]
RewriteRule ^blog/2014/11/27/pantheon-settings-files$ /blog/pantheon-settings-files [R=301]
RewriteRule ^blog/2014/12/20/include-local-drupal-settings-file-environment-configuration-and-overrides$ /blog/include-local-drupal-settings-file-environment-configuration-and-overrides [R=301]
RewriteRule ^blog/2015/04/03/how-to-define-a-minimum-drupal-core-version$ /blog/how-to-define-a-minimum-drupal-core-version [R=301]
RewriteRule ^blog/2015/06/18/updating-forked-repositories-on-github$ /blog/updating-forked-repositories-on-github [R=301]
RewriteRule ^blog/2015/07/19/sculpin-twig-resources$ /blog/sculpin-twig-resources [R=301]
RewriteRule ^blog/2015/07/21/automating-sculpin-jenkins$ /blog/automating-sculpin-jenkins [R=301]
RewriteRule ^blog/2015/12/22/programmatically-load-an-entityform-in-drupal-7$ /blog/programmatically-load-an-entityform-in-drupal-7 [R=301]
RewriteRule ^blog/2016/02/15/announcing-the-drupal-vm-generator$ /blog/announcing-the-drupal-vm-generator [R=301]
RewriteRule ^blog/2016/05/03/simplifying-drupal-migrations-with-xautoload$ /blog/simplifying-drupal-migrations-with-xautoload [R=301]
RewriteRule ^blog/2016/07/15/building-gmail-filters-with-php$ /blog/building-gmail-filters-with-php [R=301]
RewriteRule ^blog/2016/12/30/drupal-vm-generator-291-released$ /blog/drupal-vm-generator-291-released [R=301]
RewriteRule ^blog/2017/01/07/easier-sculpin-commands-with-composer-and-npm-scripts$ /blog/easier-sculpin-commands-with-composer-and-npm-scripts [R=301]
RewriteRule ^blog/2017/01/31/nginx-redirects-with-query-string-arguments$ /blog/nginx-redirects-with-query-string-arguments [R=301]
RewriteRule ^blog/2017/05/05/fixing-drupal-simpletest-docker$ /blog/2017/05/05/fixing-drupal-simpletest-issues-inside-docker-containers [R=301]
RewriteRule ^blog/2017/05/05/fixing-drupal-simpletest-issues-inside-docker-containers$ /blog/fixing-drupal-simpletest-issues-inside-docker-containers [R=301]
RewriteRule ^blog/2017/05/20/turning-drupal-module-into-feature$ /blog/turning-your-custom-drupal-module-feature [R=301]
RewriteRule ^blog/2017/06/09/introducing-the-drupal-meetups-twitterbot$ /blog/introducing-the-drupal-meetups-twitterbot [R=301]
RewriteRule ^blog/2017/07/13/publishing-sculpin-sites-with-github-pages$ /blog/publishing-sculpin-sites-github-pages [R=301]
RewriteRule ^blog/2017/11/07/tdd-test-driven-drupal$ /blog/tdd-test-driven-drupal [R=301]
RewriteRule ^blog/2017/11/07/writing-drupal-module-test-driven-development-tdd$ /blog/2017/11/07/tdd-test-driven-drupal [R=301]
RewriteRule ^blog/2018/01/30/drupalcamp-bristol-2018$ /blog/drupalcamp-bristol-2018 [R=301]
RewriteRule ^blog/2018/02/05/using-tailwind-css-in-your-drupal-theme$ /blog/using-tailwind-css-in-your-drupal-theme [R=301]
RewriteRule ^blog/2018/02/27/looking-forward-to-drupalcamp-london$ /blog/looking-forward-to-drupalcamp-london [R=301]
RewriteRule ^blog/2018/02/27/queuing-private-messages-in-drupal-8$ /blog/queuing-private-messages-in-drupal-8 [R=301]
RewriteRule ^blog/2018/02/28/building-the-new-phpsw-website$ /blog/building-the-new-phpsw-website [R=301]
RewriteRule ^blog/2018/03/02/yay-the-mediacurrent-contrib-half-hour-is-back$ /blog/yay-the-mediacurrent-contrib-half-hour-is-back [R=301]
RewriteRule ^blog/2018/03/04/tweets-from-drupalcamp-london$ /blog/tweets-from-drupalcamp-london [R=301]
RewriteRule ^blog/2018/04/23/back-to-the-future-git-diff-apply$ /blog/back-future-gits-diff-apply-commands [R=301]
RewriteRule ^blog/2018/05/06/creating-a-custom-phpunit-command-for-docksal$ /blog/creating-a-custom-phpunit-command-for-docksal [R=301]
RewriteRule ^blog/add-date-popup-calendar-custom-form$ /blog/how-add-date-popup-calendar-custom-form [R=301]
RewriteRule ^blog/adding-methods-decorating-entity-metadata-wrapper$ /blog/decorating-entity-metadata-wrapper-add-refactor-methods [R=301]
RewriteRule ^blog/announcing-drupal-vm-generator$ /blog/announcing-the-drupal-vm-generator [R=301]
RewriteRule ^blog/announcing-the-drupal-vm-config-generator$ /blog/announcing-the-drupal-vm-generator [R=301]
RewriteRule ^blog/back-to-the-future-git-diff-apply$ /blog/back-future-gits-diff-apply-commands [R=301]
RewriteRule ^blog/building-gmail-filters-in-php$ /blog/building-gmail-filters-php [R=301]
RewriteRule ^blog/building-new-phpsw-website$ /blog/building-the-new-phpsw-website [R=301]
RewriteRule ^blog/building-presentation-slides-reveal-js-tailwind-css$ /blog/building-presentation-slides-rst2pdf [R=301]
RewriteRule ^blog/building-speaker-leaderboard-php-south-wales-using-drupal-symfony$ /blog/building-speaker-leaderboard-php-south-wales-drupal-symfony [R=301]
RewriteRule ^blog/create-and-apply-patches$ /blog/how-create-apply-patches [R=301]
RewriteRule ^blog/create-flickr-photo-gallery-using-feeds-cck-and-views$ /blog/create-flickr-photo-gallery-using-feeds-cck-views [R=301]
RewriteRule ^blog/creating-and-using-custom-tokens-drupal-7$ /blog/creating-using-custom-tokens-drupal-7 [R=301]
RewriteRule ^blog/creating-custom-docksal-commands$ /blog/creating-custom-phpunit-command-docksal [R=301]
RewriteRule ^blog/debugging-drupal-commerce-promotions-illiminate-collections$ /blog/debugging-drupal-commerce-illuminate-collections [R=301]
RewriteRule ^blog/decorating-entity-metadata-wrapper-add-add-refactor-methods$ /blog/decorating-entity-metadata-wrapper-add-refactor-methods [R=301]
RewriteRule ^blog/dev-book-club-refactoring-chapter-1$ /blog/dev-book-club-notes-refactoring-chapter-1 [R=301]
RewriteRule ^blog/dividing-drupals-process-preprocess-functions-separate-files$ /blog/dividing-drupals-process-and-preprocess-functions-separate-files [R=301]
RewriteRule ^blog/drupal-8-commerce-fixing-no-such-customer-error-on-checkout$ /drupal-8-commerce-fixing-error-on-user-checkout [R=301]
RewriteRule ^blog/drupal-8-commerce-fixing-no-such-customer-error-user-checkout /drupal-8-commerce-fixing-error-on-user-checkout [R=301]
RewriteRule ^blog/drupal-vm-generator-291-released$ /blog/drupal-vm-generator-updates [R=301]
RewriteRule ^blog/drupalcamp-london-2019-tickets$ /blog/drupalcamp-london-2019-tickets-available-call-sessions [R=301]
RewriteRule ^blog/drush-make-drupalbristol$ /presentations/drush-make-drupalbristol [R=301]
RewriteRule ^blog/easier-git-repository-cloning-with-insteadof$ /blog/easier-git-repository-cloning-insteadof [R=301]
RewriteRule ^blog/easier-sculpin-commands-with-composer-and-npm-scripts$ /blog/easier-sculpin-commands-composer-npm-scripts [R=301]
RewriteRule ^blog/editing-meetup-videos-kdenlive$ /blog/editing-meetup-videos-linux-kdenlive [R=301]
RewriteRule ^blog/examples-of-laravel-collections-in-drupal$ /blog/using-laravel-collections-drupal [R=301]
RewriteRule ^blog/experimenting-with-events-in-drupal-8$ /blog/experimenting-events-drupal-8 [R=301]
RewriteRule ^blog/fix-vagrant-loading-wrong-virtual-machine$ /blog/how-fix-vagrant-loading-wrong-virtual-machine [R=301]
RewriteRule ^blog/fixing-drupal-simpletest-docker$ /blog/fixing-drupal-simpletest-issues-inside-docker-containers [R=301]
RewriteRule ^blog/forward-one-domain-another-using-modrewrite-and-htaccess$ /blog/forward-one-domain-another-using-modrewrite-htaccess [R=301]
RewriteRule ^blog/forward-one-domain-another-using-modrewrite-and-htaccess$ /blog/forward-one-domain-another-using-modrewrite-htaccess [R=301]
RewriteRule ^blog/git-format-patch$ /blog/git-format-patch-your-friend [R=301]
RewriteRule ^blog/how-easily-embed-typekit-fonts-your-drupal-website$ /blog/easily-embed-typekit-fonts-your-drupal-website [R=301]
RewriteRule ^blog/how-split-new-drupal-contrib-project-within-another-repository$ /blog/splitting-new-drupal-project-from-repo [R=301]
RewriteRule ^blog/how-style-drupal-6s-taxonomy-lists-php-css-and-jquery$ /blog/style-drupal-6s-taxonomy-lists-php-css-jquery [R=301]
RewriteRule ^blog/include-local-drupal-settings-file-environment-configuration-overrides$ /blog/include-local-drupal-settings-file-environment-configuration-and-overrides [R=301]
RewriteRule ^blog/install-and-configure-subversion-svn-server-ubuntu$ /blog/how-install-configure-subversion-svn-server-ubuntu [R=301]
RewriteRule ^blog/live-blogging-symfonylive-london$ /blog/live-blogging-symfonylive-london-2019 [R=301]
RewriteRule ^blog/minimum-core-version$ /blog/how-define-minimum-drupal-core-version [R=301]
RewriteRule ^blog/nginx-redirects-with-query-string-arguments$ /blog/nginx-redirects-query-string-arguments [R=301]
RewriteRule ^blog/null-users-and-system-users-in-drupal$ /blog/null-users-system-users-drupal [R=301]
RewriteRule ^blog/overridding-phpcs-configuration-drupal-ci$ /blog/overriding-phpcs-configuration-drupal-ci [R=301]
RewriteRule ^blog/pantheon-settings-files$ /blog/include-environment-specific-settings-files-pantheon [R=301]
RewriteRule ^blog/pdfpc-pdf-presenter-console-notes$ /blog/presenting-pdf-slides-using-pdfpc-pdf-presenter-console [R=301]
RewriteRule ^blog/php-apps-subdirectory-nginx$ /blog/how-put-your-php-application-subdirectory-another-site-nginx [R=301]
RewriteRule ^blog/presenting-tailwind-css-ansible-cms-philly$ /blog/presenting-on-tailwind-css-and-ansible-at-cms-philly [R=301]
RewriteRule ^blog/programmatically-load-(an-)?entityform-(in-)?drupal-7$ /blog/entityform [R=301]
RewriteRule ^blog/published-my-first-docker-images-docker-hub$ /blog/published-my-first-docker-images-docker-hub-adr-tools-sculpin-rst2pdf [R=301]
RewriteRule ^blog/publishing-sculpin-sites-(with-)?github-pages$ /blog/publishing-sculpin-sites-github-pages [R=301]
RewriteRule ^blog/queuing-private-messages-in-drupal-8$ /blog/queuing-private-messages-drupal-8 [R=301]
RewriteRule ^blog/quick-project-switching-in-phpstorm$ /blog/quick-project-switching-phpstorm [R=301]
RewriteRule ^blog/quickly-apply-patches-using-git-and-curl-or-wget$ /blog/quickly-apply-patches-using-git-curl-or-wget [R=301]
RewriteRule ^blog/rebuilding-bartik-with-vuejs-tailwind-css$ /blog/rebuilding-bartik-drupals-default-theme-vuejs-tailwind-css [R=301]
RewriteRule ^blog/rebuilding-bartik-with-vuejs-tailwind-css-part-2$ /blog/rebuilding-bartik-drupals-default-theme-vuejs-tailwind-css-part-2 [R=301]
RewriteRule ^blog/rebuilding-uis-tailwind-css$ /blog/uis-ive-rebuilt-tailwind-css [R=301]
RewriteRule ^blog/restructuring-my-tailwindjs-config-files$ /blog/restructuring-my-tailwindjs-configuration-files [R=301]
RewriteRule ^blog/retrieving-profile-data-user-using-entity-metadata-wrapper$ /blog/cleanly-retrieving-user-profile-data-using-entity-metadata-wrapper [R=301]
RewriteRule ^blog/running-drupal-with-symfony-local-server$ /blog/running-drupal-88-symfony-local-server [R=301]
RewriteRule ^blog/running-phpunit-tests-docksal-phpstorm$ /blog/how-run-drupal-8-phpunit-tests-within-docksal-phpstorm [R=301]
RewriteRule ^blog/simplifying-drupal-migrations-with-xautoload$ /blog/simplifying-drupal-migrations-xautoload [R=301]
RewriteRule ^blog/speaking-drupalcon-barcelona-2020$ /blog/speaking-drupalcon-europe-2020 [R=301]
RewriteRule ^blog/speaking-during-lockdown$ /blog/speaking-remotely-during-covid-19 [R=301]
RewriteRule ^blog/speaking-remotely-during-lockdown$ /blog/speaking-remotely-during-covid-19 [R=301]
RewriteRule ^blog/style-drupal-6s-taxonomy-lists-php-css-jquery$ /blog/style-drupal-6s-taxonomy-lists-php-css-and-jquery [R=301]
RewriteRule ^blog/survey-results-my-drupalcon-europe-session$ /blog/survey-results-my-drupalcon-europe-session-test-driven-drupal [R=301]
RewriteRule ^blog/system-users-null-users$ /blog/null-users-and-system-users-in-drupal [R=301]
RewriteRule ^blog/test-driven-drupal-gitstore-leanpub$ /blog/test-driven-drupal-on-gitstore-leanpub [R=301]
RewriteRule ^blog/test-driven-drupal-presentation-drupalcon-europe-0$ /blog/test-driven-drupal-presentation-drupalcon-europe [R=301]
RewriteRule ^blog/test-driven-drupal-session-drupalcon-europe$ /blog/test-driven-drupal-presentation-drupalcon-europe [R=301]
RewriteRule ^blog/test-driven-drupal-session-video-drupalcon-europe$ /blog/test-driven-drupal-presentation-drupalcon-europe [R=301]
RewriteRule ^blog/testing-tailwind-css-plugins-with-jest$ /blog/testing-tailwind-css-plugins-jest [R=301]
RewriteRule ^blog/testing-tailwindcss-plugins-with-jest$ /blog/testing-tailwind-css-plugins-jest [R=301]
RewriteRule ^blog/tweets-from-drupalcamp-london$ /blog/tweets-drupalcamp-london [R=301]
RewriteRule ^blog/updating-features-and-adding-components-using-drush$ /blog/updating-features-adding-components-using-drush [R=301]
RewriteRule ^blog/updating-forked-repositories-github$ /blog/updating-forked-github-repos [R=301]
RewriteRule ^blog/use-regular-expressions-search-and-replace-coda-or-textmate$ /blog/use-regular-expressions-search-replace-coda-or-textmate [R=301]
RewriteRule ^log/using-environment-variables-settings-docksal$ /blog/how-use-environment-variables-your-drupal-settings-docksal [R=301] [R=301]
RewriteRule ^blog/using-psr-4-autoloading-your-drupal-7-test-cases$ /blog/psr4-autoloading-test-cases-drupal-7 [R=301]
RewriteRule ^blog/using-tailwind-css-in-your-drupal-theme$ /blog/using-tailwind-css-your-drupal-theme [R=301]
RewriteRule ^blog/using-the-pcss-extension-postcss-webpack-encore$ /blog/using-pcss-extension-postcss-webpack-encore [R=301]
RewriteRule ^blog/weeknotes-june-5th$ /blog/weeknotes-2021-06-05 [R=301]
RewriteRule ^blog/writing-drupal-module-test-driven-development-tdd$ /blog/writing-new-drupal-8-module-using-test-driven-development-tdd [R=301]
RewriteRule ^book$ /test-driven-drupal [R=301]
RewriteRule ^build-configs$ /daily/2024/08/27/build-configs-is-open-source [R=301]
RewriteRule ^calendars?$ https://savvycal.com/opdavies [R=301]
RewriteRule ^cms-philly$ /articles/presenting-on-tailwind-css-and-ansible-at-cms-philly [R=301]
RewriteRule ^code-enigma-interview$ https://blog.codeenigma.com/interview-with-a-drupal-expert-9fcd8e0fad28 [R=301]
RewriteRule ^consulting$ / [R=301]
RewriteRule ^contrib-half-hour$ https://www.youtube.com/playlist?list=PLu-MxhbnjI9rHroPvZO5LEUhr58Yl0j_F [R=301]
RewriteRule ^cv$ /cv.txt [R=301]
RewriteRule ^d0P5z$ /presentations/drupal-8-php-libraries-drupalorg-api [R=301]
RewriteRule ^d7$ /drupal7 [R=301]
RewriteRule ^dcbristol-cfp$ https://www.papercall.io/drupalcamp-bristol-2019 [R=301]
RewriteRule ^dcbristol17-videos$ https://www.youtube.com/playlist?list=PLOwPvExSyLLngtd6R4PUD9MCXa6QL_obA [R=301]
RewriteRule ^dcbristol19-announced$ /articles/drupalcamp-bristol-2019-speakers-sessions-announced [R=301]
RewriteRule ^dclondon-sat$ https://drupalcamp.london/schedule/saturday [R=301]
RewriteRule ^dclondon-sun$ https://drupalcamp.london/schedule/sunday [R=301]
RewriteRule ^dclondon20$ /articles/drupalcamp-london-testing-workshop [R=301]
RewriteRule ^ddev-phpunit-command$ /blog/creating-custom-phpunit-command-ddev [R=301]
RewriteRule ^deploying-php-ansible$ /presentations/deploying-php-ansible-ansistrano [R=301]
RewriteRule ^dks7E$ https://www.youtube.com/watch?v=PLS4ET7FAcU [R=301]
RewriteRule ^do-library$ https://github.com/opdavies/drupalorg-api-php [R=301]
RewriteRule ^do-projects$ https://github.com/opdavies/drupal-module-drupalorg-projects [R=301]
RewriteRule ^docksal-phpunit-phpstorm$ /articles/running-phpunit-tests-docksal-phpstorm [R=301]
RewriteRule ^docksal-posts$ /articles/tags/docksal [R=301]
RewriteRule ^dransible$ https://github.com/opdavies/dransible [R=301]
RewriteRule ^dransible-drupal-9$ /blog/upgrading-dransible-project-drupal-9 [R=301]
RewriteRule ^drupal-bristol-march-19$ https://docs.google.com/presentation/d/1pk9LIN-hHX73kvDdo-lzgmKlAeH33_K_uvI0t7A-rvY/edit?usp=sharing [R=301]
RewriteRule ^drupal-consultant$ /drupal-consulting [R=301]
RewriteRule ^drupal-consulting$ / [R=301]
RewriteRule ^drupal-core-live-stream$ https://www.youtube.com/watch?v=OK4FWwh1gQU [R=301]
RewriteRule ^drupal-core-testing-gate$ https://www.drupal.org/core/gates#testing [R=301]
RewriteRule ^drupal-first-time-issues$ https://www.drupal.org/project/issues/search?text=&projects=&assigned=&submitted=&project_issue_followers=&status%5B%5D=Open&issue_tags_op=%3D&issue_tags=Novice [R=301]
RewriteRule ^drupal-forum-post$ http://www.webmaster-forums.net/webmasters-corner/developing-my-website-using-php-and-mysql#comment-1231537 [R=301]
RewriteRule ^drupal-marketplace-uk$ https://www.drupal.org/drupal-services?offices%5B%5D=24460 [R=301]
RewriteRule ^drupal-meetups-twitterbot$ /articles/introducing-the-drupal-meetups-twitterbot [R=301]
RewriteRule ^drupal-novice-issues$ https://www.drupal.org/project/issues/search?text=&projects=&assigned=&submitted=&project_issue_followers=&status%5B%5D=Open&issue_tags_op=%3D&issue_tags=Novice [R=301]
RewriteRule ^drupal-php-developer$ /drupal-consultant [R=301]
RewriteRule ^drupal-php-developer-consultant-uk$ /drupal-php-developer [R=301]
RewriteRule ^drupal-tailwind-demo$ https://www.youtube.com/watch?v=1eM-Gw6GI4g [R=301]
RewriteRule ^drupal-tailwindcss$ https://www.drupal.org/project/tailwindcss [R=301]
RewriteRule ^drupal-vuejs$ /presentations/decoupling-drupal-vuejs [R=301]
RewriteRule ^drupal7$ /drupal-upgrade [R=301]
RewriteRule ^drupalcamp-london-2019-tickets$ /articles/drupalcamp-london-2019-tickets [R=301]
RewriteRule ^drupalcamp-nyc-training$ https://www.youtube.com/watch?v=3M9c4UUzKm0 [R=301]
RewriteRule ^drupalorg$ https://www.drupal.org/u/opdavies [R=301]
RewriteRule ^drupalorg-project-issues$ https://www.drupal.org/project/issues/search?projects=Override+Node+Options%2C+Tailwind+CSS+Starter+Kit%2C+Block+ARIA+Landmark+Roles%2C+Copyright+Block+module%2C+System+User%2C+Null+User%2C+Collection+class%2C+Pathauto+Menu+Link%2C+Webform+ARIA&project_issue_followers=&status%5B%5D=1&status%5B%5D=13&status%5B%5D=8&status%5B%5D=14&status%5B%5D=15&issue_tags_op=%3D [R=301]
RewriteRule ^drupalversary$ https://github.com/opdavies/drupal-module-drupalversary [R=301]
RewriteRule ^elewant$ https://elewant.com/shepherd/admire/opdavies [R=301]
RewriteRule ^feed$ /rss.xml [R=301]
RewriteRule ^first-drupal-core-issue$ https://www.drupal.org/project/drupal/issues/753898 [R=301]
RewriteRule ^first-npm-package$ https://www.npmjs.com/package/tailwindcss-vuejs [R=301]
RewriteRule ^freeagent$ https://opdavies.freeagent.com [R=301]
RewriteRule ^git-flow$ /presentations/git-flow [R=301]
RewriteRule ^github-sculpin$ https://github.com/opdavies?tab=repositories&q=sculpin [R=301]
RewriteRule ^gitlab$ https://gitlab.com/opdavies [R=301]
RewriteRule ^gitstore$ https://enjoy.gitstore.app/maintainers/opdavies [R=301]
RewriteRule ^gmail-filters$ https://gitlab.com/opdavies/gmail-filters [R=301]
RewriteRule ^images/me-precedent.jpg$ /sites/default/files/images/social-avatar.jpg [R=301]
RewriteRule ^inviqa-tailwind-demo$ https://play.tailwindcss.com/Yfmw8O5UNN [R=301]
RewriteRule ^inviqa-tailwind-notes$ https://gist.github.com/opdavies/e6f0f4938506a6859acf1aca8b4e1a74 [R=301]
RewriteRule ^join-php-south-wales-slack$ https://join.slack.com/t/phpsouthwales/shared_invite/zt-4vuetc43-AvtEK1WqNzp5k1w4yWKOJA [R=301]
RewriteRule ^jy6rW$ https://www.meetup.com/PHP-South-Wales/events/264731393 [R=301]
RewriteRule ^kB6Jd$ /articles/running-drupal-with-symfony-local-server/ [R=301]
RewriteRule ^kmDRA$ https://www.bbc.co.uk/news/uk-46561779 [R=301]
RewriteRule ^leeds-php-drupal-9$ https://www.meetup.com/leedsphp/events/272504993 [R=301]
RewriteRule ^live$ https://www.youtube.com/channel/UCkeK0qF9HHUPQH_fvn4ghqQ [R=301]
RewriteRule ^npm$ https://www.npmjs.com/~opdavies [R=301]
RewriteRule ^oFlkS$ /articles/test-driven-drupal-on-gitstore-leanpub [R=301]
RewriteRule ^oliver-davies-uk-based-drupal-symfony-developer$ /oliver-davies-uk-based-drupal-php-developer [R=301]
RewriteRule ^pair-programming$ /pair [R=301]
RewriteRule ^pair-with-me$ /pair [R=301]
RewriteRule ^pairing$ /pair [R=301]
RewriteRule ^php-ansible$ /presentations/deploying-php-ansible-ansistrano [R=301]
RewriteRule ^presentations/what-is-this-drupal-thing$ /presentations/so-what-is-this-drupal-thing [R=301]
RewriteRule ^qSHAl$ /articles/published-my-first-npm-package/ [R=301]
RewriteRule ^qT1Rb$ https://github.com/opdavies/drupal-meetups-twitterbot [R=301]
RewriteRule ^rebuilding-acquia$ https://rebuilding-acquia.oliverdavies.uk [R=301]
RewriteRule ^rebuilding-bartik$ /articles/rebuilding-bartik-with-vuejs-tailwind-css [R=301]
RewriteRule ^rebuilding-bristol-js$ https://github.com/opdavies/rebuilding-bristol-js [R=301]
RewriteRule ^rebuilding-pantheon$ https://play.tailwindcss.com/LND98XihGI?layout=horizontal [R=301]
RewriteRule ^rebuilding-platformsh$ https://rebuilding-platformsh.oliverdavies.uk [R=301]
RewriteRule ^rebuilding-symfony$ https://github.com/opdavies/rebuilding-symfony [R=301]
RewriteRule ^rk29B$ https://www.meetup.com/PHP-South-Wales/events/268422525 [R=301]
RewriteRule ^roadmap$ /drupal-upgrade [R=301]
RewriteRule ^rss$ /rss.xml [R=301]
RewriteRule ^rst2pdf$ /presentations/building-presenting-slide-decks-rst2pdf [R=301]
RewriteRule ^s9MjJ$ https://symfonycasts.com/screencast/symfony [R=301]
RewriteRule ^sculpin$ /presentations/building-static-websites-sculpin [R=301]
RewriteRule ^sculpin-encore-versioning$ https://github.com/opdavies/oliverdavies.uk/commit/d192b04aefa6e7a21bfc1f2e0fe0a16111e0e8a2 [R=301]
RewriteRule ^sites/default/files/images/social-avatar.$ /images/social-avatar.jpg [R=301]
RewriteRule ^skills$ https://opdavies-skills-tailwindcss.netlify.com/ [R=301]
RewriteRule ^slides-drupal-9$ https://slides-upgrading-to-drupal-9.oliverdavies.uk [R=301]
RewriteRule ^slides-upgrading-to-drupal-9$ https://slides-upgrading-to-drupal-9.oliverdavies.uk [R=301]
RewriteRule ^slides-upgrading-to-drupal-9/index.html$ https://slides-upgrading-to-drupal-9.oliverdavies.uk [R=301]
RewriteRule ^slides-working-with-workspace$ https://slides-working-with-workspace.oliverdavies.uk [R=301]
RewriteRule ^speaker$ /press [R=301]
RewriteRule ^speaker-info$ /speaker [R=301]
RewriteRule ^speaker-information$ /speaker [R=301]
RewriteRule ^speaking-videos$ https://www.youtube.com/playlist?list=PLHn41Ay7w7kfAzczswrANch5oHAPZBlvu [R=301]
RewriteRule ^stream$ https://www.youtube.com/channel/UCkeK0qF9HHUPQH_fvn4ghqQ/live [R=301]
RewriteRule ^subscription$ / [R=301]
RewriteRule ^swap-markdown-parser$ https://github.com/opdavies/sculpin-twig-markdown-bundle-example/tree/swap-markdown-parser [R=301]
RewriteRule ^symfony$ https://connect.symfony.com/profile/opdavies [R=301]
RewriteRule ^symfony-server$ /articles/running-drupal-with-symfony-local-server [R=301]
RewriteRule ^symfonylive$ /articles/live-blogging-symfonylive-london [R=301]
RewriteRule ^symposium$ https://symposiumapp.com/u/opdavies [R=301]
RewriteRule ^tailwind$ /presentations/taking-flight-with-tailwind-css [R=301]
RewriteRule ^tailwind-css-talk$ /presentations/taking-flight-tailwind-css [R=301]
RewriteRule ^tailwind-repos$ https://github.com/opdavies?utf8=%E2%9C%93&tab=repositories&q=tailwindcss [R=301]
RewriteRule ^tailwind-talk$ /presentations/taking-flight-with-tailwind-css [R=301]
RewriteRule ^tailwindcss-demo$ http://tailwindcss-demo.oliverdavies.uk/ [R=301]
RewriteRule ^talks$ /presentations [R=301]
RewriteRule ^talks-offer-tweet$ https://twitter.com/opdavies/status/1250870367712935938 [R=301]
RewriteRule ^talks/2012/09/05/what-is-this-drupal-thing-unified-diff$ /presentations/so-what-is-this-drupal-thing [R=301]
RewriteRule ^talks/2013/07/10/drupal-ldap-swdug$ /presentations/drupal-ldap [R=301]
RewriteRule ^talks/2014/03/01/git-flow-drupalcamp-london-2014$ /presentations/git-flow [R=301]
RewriteRule ^talks/2014/07/02/drush-make-drupalbristol-drupal-bristol$ /presentations/drush-make-drupalbristol [R=301]
RewriteRule ^talks/2014/08/19/drupal-association-swdug$ /presentations/drupal-association [R=301]
RewriteRule ^talks/2015/01/18/drupalorg-2015-drupalcamp-brighton-2015$ /presentations/drupalorg-in-2015-whats-coming-next [R=301]
RewriteRule ^talks/2015/02/28/drupalorg-2015-drupalcamp-london-2015$ /presentations/drupalorg-in-2015-whats-coming-next [R=301]
RewriteRule ^talks/2015/04/08/drupal-8-phpsw$ /presentations/drupal-8 [R=301]
RewriteRule ^talks/2015/07/25/test-drive-twig-with-sculpin-drupalcamp-north-2015$ /presentations/test-drive-twig-with-sculpin [R=301]
RewriteRule ^talks/2015/08/25/dancing-for-drupal-umbristol$ /presentations/dancing-for-drupal [R=301]
RewriteRule ^talks/2015/10/14/sculpin-phpsw$ /presentations/sculpin [R=301]
RewriteRule ^talks/2016/03/05/drupal-8-module-development-drupalcamp-london-2016$ /presentations/getting-started-with-drupal-8-module-development [R=301]
RewriteRule ^talks/2016/03/09/drupal-vm-generator-nwdug$ /presentations/drupal-vm-generator [R=301]
RewriteRule ^talks/2016/04/02/drupal-vm-generator-drupal-bristol$ /presentations/drupal-vm-generator [R=301]
RewriteRule ^talks/2016/06/11/drupal-8-rejoining-the-herd-php-south-coast-2016$ /presentations/drupal-8-rejoining-the-herd [R=301]
RewriteRule ^talks/2016/07/23/drupal-vm-meet-symfony-console-drupalcamp-bristol-2016$ /presentations/drupal-vm-meet-symfony-console [R=301]
RewriteRule ^talks/2016/11/09/drupal-development-with-composer-phpsw$ /presentations/drupal-development-with-composer [R=301]
RewriteRule ^talks/2016/11/17/goodbye-drush-make-hello-composer-drupal-bristol$ /presentations/goodbye-drush-make-hello-composer [R=301]
RewriteRule ^talks/2017/01/18/getting-your-data-into-drupal-8-drupal-bristol$ /presentations/getting-your-data-into-drupal-8 [R=301]
RewriteRule ^talks/2017/03/04/getting-your-data-into-drupal-8-drupalcamp-london-2017$ /presentations/getting-your-data-into-drupal-8 [R=301]
RewriteRule ^talks/ansible-ansistrano$ https://www.oliverdavies.uk/presentations/deploying-php-ansible-ansistrano [R=301]
RewriteRule ^talks/archive$ /presentations [R=301]
RewriteRule ^talks/deploying-php-applications-fabric$ /presentations/deploying-php-fabric [R=301]
RewriteRule ^talks/deploying-php-applications-with-fabric$ /presentations/deploying-php-fabric [R=301]
RewriteRule ^talks/drupal-vm-generator-2$ /presentations/drupal-vm-generator [R=301]
RewriteRule ^talks/drupalorg-2015-2$ /presentations/drupalorg-2015 [R=301]
RewriteRule ^talks/drupalorg-in-2015-whats-coming-next$ /presentations/drupalorg-2015 [R=301]
RewriteRule ^talks/getting-started-with-drupal-8-module-development$ /drupal-8-module-development [R=301]
RewriteRule ^talks/having-fun-drupal-8-php-libraries-drupalorg-api$ /presentations/drupal-8-php-libraries-drupalorg-api [R=301]
RewriteRule ^talks/never-commit-master-introduction-git-flow$ /presentations/git-flow [R=301]
RewriteRule ^talks/sculpin$ /presentations/building-static-websites-sculpin [R=301]
RewriteRule ^talks/tailwind$ /presentations/taking-flight-with-tailwind-css/ [R=301]
RewriteRule ^talks/taking-flight-tailwind-css$ /presentations/taking-flight-with-tailwind-css [R=301]
RewriteRule ^talks/using-laravel-collections-outside-laravel$ /presentations/using-illuminate-collections-outside-laravel [R=301]
RewriteRule ^talks/what-is-this-drupal-thing$ /presentations/so-what-is-this-drupal-thing [R=301]
RewriteRule ^talks/what-is-this-drupal-thing-unified-diff$ /presentations/so-what-is-this-drupal-thing [R=301]
RewriteRule ^talks/working-workspace$ /presentations/working-with-workspace [R=301]
RewriteRule ^tdd-blog$ https://github.com/opdavies/drupal-module-tdd-blog [R=301]
RewriteRule ^tdd-test-driven-drupal$ /presentations/tdd-test-driven-drupal/ [R=301]
RewriteRule ^team-coaching$ / [R=301]
RewriteRule ^test-driven-drupal-book$ /test-driven-drupal [R=301]
RewriteRule ^testing-drupal$ /presentations/tdd-test-driven-drupal [R=301]
RewriteRule ^testing-drupal-intro$ https://inviqa.com/blog/drupal-automated-testing-introduction [R=301]
RewriteRule ^testing-tailwind-plugins$ /articles/testing-tailwindcss-plugins-with-jest [R=301]
RewriteRule ^testing-workshop$ https://github.com/opdavies/workshop-drupal-automated-testing [R=301]
RewriteRule ^testing-workshop-code$ https://github.com/opdavies/workshop-drupal-automated-testing-code [R=301]
RewriteRule ^todoist-filters$ https://gist.github.com/opdavies/6709fbdac5c3babbd94137bcc8b8e3c2 [R=301]
RewriteRule ^twitter-tweaks$ https://github.com/opdavies/chrome-extension-twitter-tweaks [R=301]
RewriteRule ^upgrading-to-drupal-9$ /presentations/upgrading-your-site-drupal-9 [R=301]
RewriteRule ^uxbjV$ https://www.drupal.org/project/copyright_block [R=301]
RewriteRule ^vyTEF$ https://www.npmjs.com/package/tailwindcss-vuejs [R=301]
RewriteRule ^webpack-encore-pcss-regex$ https://regexr.com/51iaf [R=301]
RewriteRule ^wordcamp-bristol-tailwindcss$ https://2019.bristol.wordcamp.org/session/taking-flight-with-tailwind-css [R=301]
RewriteRule ^wordpress-tailwind$ https://github.com/opdavies/wordcamp-bristol-2019 [R=301]
RewriteRule ^work$ /drupal-php-developer [R=301]
RewriteRule ^working-with-workspace$ /presentations/working-with-workspace [R=301]
RewriteRule ^workshop-drupal-testing$ https://github.com/opdavies/workshop-drupal-automated-testing [R=301]
RewriteRule ^workspace-demo$ https://github.com/opdavies/working-with-workspace-demo [R=301]
RewriteRule ^wp-tailwind$ https://wp-tailwind.oliverdavies.uk [R=301]
RewriteRule ^wp-tailwind-repo$ https://github.com/opdavies/wordcamp-bristol-2019 [R=301]
RewriteRule ^wp-tailwind-starter$ https://github.com/opdavies/wordpress-tailwindcss-startker-kit [R=301]
RewriteRule ^wp-tailwind-static$ https://wp-tailwind.oliverdavies.uk [R=301]
RewriteRule ^yXhoS$ /presentations/things-you-should-know-about-php [R=301]
# ^ewriteCond %{REQUEST_URI}$ !^/archive/?$
# RewriteRule ^archive/(.*)$$ /daily/$1 [L,R=301]
RewriteRule ^articles/(.*)$ /blog/$1 [L,R=301]
RewriteRule ^talks/(.*)$ /presentations/$1 [L,R=301]

View file

@ -0,0 +1,17 @@
{% import "_macros" as macros %}
<div>
<h2 class="text-xl font-bold">About me</h2>
<div class="mt-4">
<div class="flex space-x-4">
<div class="flex flex-col flex-shrink-0 justify-start">
<img src="/assets/images/social-avatar.jpg" alt="Picture of Oliver" class="my-0 w-16 h-16 rounded-full ring-[3px] ring-blue-primary dark:ring-white">
</div>
<div class="{{ site.prose_classes }}">
<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>

View file

@ -0,0 +1,9 @@
{% if site.banner_text %}
<div class="p-3 bg-[#ffc82c] text-black text-base mb-4 dark:text-white dark:bg-[#856200]">
<div class="min-w-full mx-auto px-6">
<div class="mx-auto max-w-xl prose prose-p:text-black prose:a:font-normal prose-p:text-sm prose-p:leading-normal dark:prose-p:text-white dark:prose-a:text-white text-center">
{{ site.banner_text|markdown }}
</div>
</div>
</div>
{% endif %}

View file

@ -0,0 +1,19 @@
{% set classes = [
'my-4 not-prose',
full_width ? 'flex' : 'inline-flex md:w-auto',
position == 'center' ? 'flex justify-center',
] -%}
<div class="{{ classes|join(' ') }}">
<a class="inline-flex gap-2 justify-center items-center py-3 px-6 w-full text-base font-medium text-white no-underline rounded-md duration-200 ease-in-out hover:bg-white focus:bg-white border-[2px] border-blue-primary bg-blue-primary transition-color dark:hover:border-white hover:text-blue-primary focus:text-blue-primary" href="{{ url }}">
<span>
{{ text|raw }}
</span>
<span>
<svg class="size-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3" />
</svg>
</span>
</a>
</div>

View file

@ -0,0 +1,7 @@
<aside
class="p-6 my-8 border border-grey-300 dark:bg-grey-800 dark:border-grey-700"
>
<h2 class="mt-0">{{ title }}</h2>
{% block callout_content %}{% endblock %}
</aside>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,13 @@
<section>
<h2>{{ title }}</h2>
<p>Here are the emails I sent this week:</p>
<ul>
{% for email in emails %}
<li><a href="{{ email.url }}">{{ email.title }}</a></li>
{% endfor %}
</ul>
</section>

View file

@ -0,0 +1,7 @@
<section>
<h2>Work with me and support the Drupal project</h2>
<div>
<p>5% from any Drupal-related product or service is donated to the <a href="https://www.drupal.org/association">Drupal Association</a>.</p>
</div>
</section>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
<figure class="block">
<img src="{{ image.src }}" alt="{{ image.alt }}" class="p-1 border">
{% if caption %}
<figcaption class="mt-1 mb-0 italic text-sm text-center text-gray-800">
{{ caption }}
</figcaption>
{% endif %}
</figure>

View file

@ -0,0 +1,10 @@
<div class="flex flex-col justify-between items-center md:flex-row">
<div>
<a href="/" aria-label="Go back to the Home page">
<svg aria-hidden="true" class="w-16 h-16 fill-current dark:text-blue-400 text-blue-primary md:w-18 md:h-18" viewBox="0 0 706 504" xmlns="http://www.w3.org/2000/svg">
<path d="M456.5 1.1c-12.3 1.5-31 5.5-44.1 9.4-12.7 3.9-63.6 24.6-64.1 26.2-.2.5 1.4 1.7 3.4 2.7 2.1 1 8.9 5.1 15.1 9.2l11.2 7.5 14.5-6c22.9-9.5 37.3-14 57.5-17.8 7.2-1.3 14.7-1.7 31-1.8 18.6 0 23.1.3 33 2.3 22 4.5 46.1 13.9 64.5 25.2 39.3 24.2 69.9 65.3 86.9 116.5 16.3 49.2 13 100.2-9.4 145.3-21.8 43.7-49.2 68.8-101.5 92.9-13.4 6.2-120.1 51.3-121.3 51.3-.5 0-15.7-35.1-33.7-78l-32.8-78 3.1-12.8c4.4-18 5.6-29.5 4.9-48.5-.6-16.8-2-25.7-6.3-38.7-12-35.8-40.8-69.2-74-85.6l-7.2-3.6-4.2-10c-2.4-5.6-3.9-10.3-3.4-10.7.5-.5 9.7-4.5 20.4-9s19.9-8.6 20.4-9.1c1.5-1.5-18.6-10.1-32.3-13.9l-6.8-1.9-20.6 8.7c-11.4 4.8-20.9 8.9-21.2 9.2-.2.3 2 6.3 5 13.3 3 7 5.5 12.8 5.5 12.9 0 .1-7.5.2-16.7.2-11.8.1-19.4.6-25.6 1.8-27.3 5.5-50.5 17.6-70.4 37-21.8 21.2-36.7 49-43 80.2-2.4 12.3-2.4 44 0 57.2 3.6 19.4 11.9 40.4 22.3 56 6.9 10.4 21.1 25.4 31.2 33 29.9 22.5 70.8 33.2 106.2 27.8 18.9-2.8 39.3-10.6 54.1-20.5 13.5-9.1 29.1-23.8 37.6-35.5 1.2-1.7 2.4-2.8 2.7-2.5.3.3 15.6 36.6 34.1 80.5 18.5 44 33.8 80.1 33.9 80.3.8.8 144.9-60.8 162.1-69.3 45.5-22.4 73.4-47.1 95.7-84.7 28-47.4 37.5-99.7 27.8-153.5-6.8-37.6-25-79-48.6-110.3-33.2-44.1-83-74.2-138.4-83.6-11.4-1.9-46.9-2.7-58.5-1.3zM259.2 141.4c42.4 10.9 77.8 50 84.8 93.8 1.6 9.9.8 34.5-1.4 44.8-5.2 24-15.5 43-32.6 60-20.7 20.6-42.8 31.3-67.7 32.7-26.9 1.5-53.2-6.2-74.3-21.7-29.4-21.7-46-56.2-46-95.7 0-45.4 27.2-89.6 66.1-107.2 8.2-3.7 21.7-7.9 29.4-9.1 10.4-1.6 30.8-.4 41.7 2.4z"></path>
<path d="M201 20.6c-83 11.2-157 71-186.5 150.8-22.3 60.3-18.3 134.9 10.2 192 21.5 43.1 59.6 81.6 102.1 103.4 21.1 10.9 46.3 19 71.2 22.9 16.2 2.5 53.1 2.5 68.5 0 25.9-4.2 45.2-10.5 69-22.2 14.4-7.1 39.7-23.2 41.8-26.7.8-1.2-.2-4.6-4.3-14.5-2.9-7-5.7-13.2-6.1-13.7-.5-.5-4.3 1.7-8.6 5.1C320 447.1 277.6 462 232 462c-59.8 0-115.2-26.3-154.8-73.5-32.2-38.3-48.8-88.7-46.9-142 2-53.7 22.1-99.6 60.7-138.5 28.1-28.3 63-47.2 102.9-55.7 11.9-2.6 14.1-2.8 38.6-2.8 28.3 0 39.4 1.3 59 7 27.9 8.1 58.5 26.1 80.9 47.6l10.9 10.5-14.3 6c-7.8 3.2-14.5 6.4-14.7 7-.2.7 16.5 41.3 37.1 90.4 20.7 49.1 37.6 90.1 37.6 91.1 0 3.4-7.1 24.2-11.6 33.8-2.4 5.1-6.6 13.1-9.4 17.7l-5 8.3 6.6 15.6c5.5 12.9 6.9 15.4 8 14.4 2.8-2.3 19.2-27.8 24.4-37.9l5.3-10.3 8.3 19.8c4.7 10.9 8.7 20.1 8.9 20.3.6.6 60.8-24.6 74.5-31.2 29.7-14.2 52.7-35.8 65.3-61.1 16.9-34 17.6-70.8 2.2-112-17-45.3-45.8-76.7-82.5-90-18.2-6.5-43.1-9.1-63.5-6.5-11.5 1.4-30 5.8-40.3 9.5l-7.3 2.6L402.3 91c-20.3-21.3-37.9-34.3-65.4-48.3-33.4-17.1-63.7-23.8-105.9-23.6-10.7.1-24.2.7-30 1.5zM504.5 122c9.2 2.5 22.1 8.3 29.2 13.1 6.9 4.7 18.7 16.3 24.3 23.9 15.1 20.6 26.3 49 29 74 2.3 20.7-3.1 43-14.5 60.5-8.7 13.3-27.6 29.5-44.5 38-6 3.1-47.3 20.6-47.5 20.2-1.1-1.6-87.5-208-87.3-208.3.2-.2 7.4-3.3 15.8-6.9 22.5-9.5 33.8-13 55-16.9 1.4-.2 9.3-.3 17.5-.1 11.5.2 16.9.8 23 2.5z"></path>
</svg>
</a>
</div>
</div>

View file

@ -0,0 +1,7 @@
<nav class="flex flex-wrap justify-center -mb-3">
{% for link in site.menu_links %}
<a class="mx-3 mb-3 text-base underline md:text-lg dark:text-white hover:no-underline link dark:hover:text-blue-400 hover:text-gray-900" href="{{ link.url }}" title="{{ link.attributes.title ?? link.title }}">
{{ link.title }}
</a>
{% endfor %}
</nav>

View file

@ -0,0 +1,9 @@
{# <div class="mb-10 border border-grey-800 dark:border-grey-600">
<div class="py-4 px-4 border-l-4 border-blue-primary">
<div class="{{ site.prose_classes }}">
<p>
<strong>Work with me and support the Drupal project.</strong> 5% from any Drupal-related product or service is donated to the <a href="https://www.drupal.org/association">Drupal Association</a>.
</p>
</div>
</div>
</div> #}

View file

@ -0,0 +1,22 @@
{% set other_episodes = [] %}
{% for episode in all_episodes %}
{% if guest in episode.guests and episode.topic is not same as topic %}
{% set other_episodes = other_episodes|merge([episode]) %}
{% endif %}
{% endfor %}
{% if other_episodes is not empty %}
<section>
<h2>Other episodes with {{ guest }}</h2>
<ul>
{% for episode in other_episodes %}
<li>
<a href="{{ episode.url|trim('/', 'right') }}">{{ episode.topic }}</a>
- {{ episode.date|date('jS F Y') }}
</li>
{% endfor %}
</ul>
</section>
{% endif %}

View file

@ -0,0 +1,46 @@
<section>
<h2>Events</h2>
<table>
<thead>
<th>Event name</th>
<th>Date</th>
<th>Location</th>
<th>Links</th>
</thead>
<tbody>
{% for event in events|sort((a, b) => b.date <=> a.date) %}
<tr>
<td>{{ event.name }}</td>
<td><time datetime="{{ event.date|date('Y-m-d') }}">{{ event.date|date('jS F Y') }}</time></td>
<td>
{% if event.online %}
Online
{% elseif event.location %}
{{ event.location }}
{% endif %}
</td>
<td>
{% if event.urls %}
<ul>
{% for key, link in event.urls if link %}
<li>
<a class="text-sm" href="{{ link }}" title="{% spaceless %}
{{ key == 'code' ? 'View the example code from %s.'|format(event.name) }}
{{ key == 'demo' ? 'View the demo from %s.'|format(event.name) }}
{{ key == 'slides' ? 'View the slides from %s.'|format(event.name) }}
{{ key == 'video' ? 'Watch the video recording from %s.'|format(event.name) }}
{{ key == 'website' ? 'View the website for %s.'|format(event.name) }}
{% endspaceless %}">
{{- key|capitalize -}}
</a>
</li>
{% endfor %}
</ul>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>

View file

@ -0,0 +1,25 @@
{% if events %}
<section>
<h2>Events</h2>
<ul>
{% for event in events|sort((a, b) => b.date <=> a.date) %}
<li>
{% if event.url %}
<a href="{{ event.url }}">{{ event.name }}</a>
{% else %}
{{ event.name }}
{% endif %}
{% if event.location and not event.online %}
in {{ event.location }}
{% endif %}
- {{ event.date|date('F jS, Y') }}
{{ event.online ? '(online)' }}
</li>
{% endfor %}
</ul>
</section>
{% endif %}

View file

@ -0,0 +1,15 @@
{% if speakerdeck.id %}
<section>
<h2>Slides</h2>
<div>
<noscript>**Please enable JavaScript to view slides.**</noscript>
<script
class="speakerdeck-embed"
data-id="{{ speakerdeck.id }}"
data-ratio="{{ speakerdeck.ratio ?? '1.29456384323641' }}"
src="//speakerdeck.com/assets/embed.js"
></script>
</div>
</section>
{% endif %}

View file

@ -0,0 +1,20 @@
{% if video.id %}
<section>
<h2>Video</h2>
<div>
{% if video.type == 'vimeo' %}
<div>
<iframe
allowfullscreen
class="w-full border border-gray-500 aspect-[16/9]"
frameborder="0"
src="https://player.vimeo.com/video/{{ video.id }}"
></iframe>
</div>
{% elseif video.type == 'youtube' %}
{% include 'youtube-video.html.twig' with { id: video.id } %}
{% endif %}
</div>
</section>
{% endif %}

View file

@ -0,0 +1,26 @@
{% for plan in plans %}
<div>
<h3>
<strong>
{{ plan.name }}:
£{{ plan.price|number_format }} per month
</strong>
</h3>
<p>{{ plan.tagline }}</p>
<ul>
{% for feature in plan.features|merge(page.features) %}
<li>{{ feature }}</li>
{% endfor %}
</ul>
<footer class="mt-6">
{% include 'button.html.twig' with {
text: 'Register now for the ' ~ plan.name|lower ~ ' plan',
url: plan.url,
withArrow: true,
} %}
</footer>
</div>
{% endfor %}

View file

@ -0,0 +1,50 @@
{% set limit = limit ?? 10 %}
{% set offset = offset ?? 0 %}
{% set testimonials = site.testimonials %}
{% if tag is not null %}
{% set testimonials = testimonials|filter(testimonial => tag in testimonial.tags) %}
{% endif %}
{% if limit > 0 %}
{% set testimonials = testimonials|slice(offset, limit) %}
{% endif %}
<section>
<h2 class="text-xl font-bold">{{ title|default('Testimonials') }}</h2>
<div class="mt-4 space-y-12">
{% for testimonial in testimonials %}
<div>
<div class="{{ site.prose_classes }}">
<blockquote>
{{ testimonial.text|markdown }}
</blockquote>
<div>
<footer class="mt-8 flex items-center space-x-4 space-x-reverse">
<span class="text-base">
{% if testimonial.url %}
<a href="{{ testimonial.url }}">{{ testimonial.name }}</a>
{% else %}
{{ testimonial.name }}
{% endif %}
{% if testimonial.title %}
- {{ testimonial.title }}
{% endif %}
</span>
{% if testimonial.image %}
<span class="not-prose order-first flex-shrink-0">
<img class="size-16 rounded-full ring-[3px] ring-blue-primary dark:ring-white" alt="Photo of {{ testimonial.name }}" src="{{ testimonial.image.url }}">
</span>
{% endif %}
</footer>
</div>
</div>
</div>
{% endfor %}
</div>
</section>

View file

@ -0,0 +1,10 @@
<div class="my-4 flex justify-center {{ class }}">
<blockquote
class="twitter-tweet"
lang="en"
{% if not data_cards %}data-cards="hidden"{% endif %}
{% if no_parent %}data-conversation="none"{% endif %}
>
{{ content|raw }}
</blockquote>
</div>

View file

@ -0,0 +1,8 @@
<div>
<iframe
allowfullscreen
class="w-full border border-gray-500 aspect-[16/9]"
frameborder="0"
src="https://www.youtube.com/embed/{{ id }}?rel=0&iv_load_policy=3"
></iframe>
</div>

View file

@ -0,0 +1,3 @@
{% macro yearsOfExperience() %}
{{ today|date('Y') - 2007 }}
{% endmacro %}

View file

@ -0,0 +1,102 @@
<!DOCTYPE html>
<html lang="en-GB">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
{% if site.features.new_css %}
{% else %}
<link href="/build/tailwind.css?v={{ site.assets.version }}" rel="stylesheet">
{% endif %}
<link rel="canonical" href="{{ site.url }}{{ page.url|trim('/', 'right') }}">
<meta name="author" content="Oliver Davies">
<meta name="generator" content="Sculpin (https://sculpin.io)">
<meta name="keywords" content="Drupal Developer, Drupal Consultant, Drupal Architect, Drupal Strategist, Drupal Trainer, Triple Certified Drupal Expert, Public Speaker, Workshop Trainer, Technical Lead, Technical Coach, Software Development Coach, Automated Testing, Test-Driven Development">
<meta name="twitter:card" content="summary">
<meta name="twitter:creator" content="@opdavies">
<meta name="twitter:image" content="/assets/images/od-logo.jpg">
<meta name="twitter:site" content="@opdavies">
<meta property="og:image" name="image" content="/assets/images/od-logo.jpg">
<meta property="og:site_name" content="Oliver Davies">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ site.url }}{{ page.url|trim('/', 'right') }}">
{% if page.blocks.meta_description %}
<meta name="description" content="{{ page.blocks.meta_description }}">
{% elseif page.meta.description %}
<meta name="description" content="{{ page.meta.description }}">
{% else %}
<meta name="description" content="{{ site.meta.description }}">
{% endif %}
{% block head_title %}
{% if page.meta.title %}
<title>{{ page.meta.title }}</title>
<meta property="og:title" content="{{ page.meta.title }}">
<meta property="twitter:title" content="{{ page.meta.title }}">
{% else %}
<title>{{ page.title }} | {{ site.name }}</title>
<meta property="og:title" content="{{ page.title }} | {{ site.name }}">
<meta property="twitter:title" content="{{ page.title }} | {{ site.name }}">
{% endif %}
{% endblock %}
{% block styles %}{% endblock %}
</head>
<body>
<a class="sr-only focus:not-sr-only" href="#footer">Jump to the navigation menu</a>
<div class="min-h-screen font-sans text-base font-light md:text-xl dark:text-white text-grey-900 selection:bg-blue-primary selection:text-white dark:bg-grey-900 dark:selection:bg-blue-primary dark:selection:text-white">
{% include 'banner.html.twig' %}
<div class="py-4 px-4 mx-auto max-w-xl">
<header class="flex flex-col justify-between items-center md:flex-row">
{% include 'logo.html.twig' %}
</header>
</div>
<div class="py-10 px-6 mx-auto max-w-xl md:py-10">
<div class="space-y-10">
{% block content_top %}{% endblock %}
<main>
{% if page.url matches "#^/atdc/#" %}
<div class="mb-10 border border-grey-800 dark:border-grey-600">
<div class="py-4 px-4">
<div class="{{ site.prose_classes }}">
<p>I'm currently updating this course for Drupal 11. If you find any bugs or have any issues, <a href="mailto:{{ site.email }}?subject=ATDC">please let me know</a>.
</div>
</div>
</div>
{% endif %}
<h1 class="mb-4 text-xl font-bold md:text-2xl">
{% block page_title %}{{ page.title }}{% endblock %}
</h1>
<div class="{{ site.prose_classes }}">
{% block content_wrapper %}
{% block content %}{% endblock %}
{% endblock %}
</div>
</main>
{% block content_bottom %}{% endblock %}
</div>
<footer class="mt-16 mb-6">
<a id="footer" tabindex="-1"></a>
{% include 'main-menu.html.twig' %}
</footer>
</div>
</div>
{# Add the Mastodon verification link so pages can be verified. #}
<a class="hidden" rel="me" href="{{ site.mastodon.url }}">Mastodon</a>
{% block scripts %}{% endblock %}
</body>
</html>

View file

@ -0,0 +1 @@
{% block content %}{% endblock %}

View file

@ -0,0 +1,5 @@
{% extends 'base' %}
{% block content_bottom %}
{% include 'about-me.html.twig' %}
{% endblock %}

View file

@ -0,0 +1,71 @@
{% extends 'page' %}
{% block meta_description -%}
Discussing {{ page.topic }} with {{ page.guests|join(' and ') }}
{%- endblock %}
{% block page_title -%}
{% set episodeNumber = page.url|trim('/')|split('/')|last|split('-')|first -%}
Episode {{ episodeNumber }}: {{ page.topic }} with {{ page.guests|join(' and ') }}
{%- endblock %}
{% block head_title %}
<title>{{ block('page_title') }} | Beyond Blocks podcast | {{ site.name }}</title>
<meta property="og:title" content="{{ block('page_title') }} | Beyond Blocks podcast | {{ site.name }}" />
<meta property="twitter:title" content="{{ block('page_title') }} | Beyond Blocks podcast | {{ site.name }}" />
{% endblock %}
{% block content_wrapper %}
{% if page.episode_filename %}
<audio controls>
<source src="/files/bb/episodes/{{ page.episode_filename }}" type="audio/mp3">
</audio>
{% endif %}
{{ parent() }}
{% if page.talking_points %}
<div>
<h2>Key points</h2>
<ul>
{% for point in page.talking_points %}
<li>{{ point }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if page.quotes %}
<div>
<h2>Quotes</h2>
<ul>
{% for quote in page.quotes %}
<li>{{ quote }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div>
<h2>Links</h2>
<ul>
{% for link in page.links %}
<li>
<a href="{{ link.1 }}">{{ link.0 }}</a>
</li>
{% endfor %}
<ul>
</div>
{% for guest in page.guests %}
{% include 'podcast/other-episodes.html.twig' with {
all_episodes: data.podcast_episodes,
guest,
topic: page.topic,
} only %}
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,11 @@
{% extends 'page' %}
{% block content_wrapper %}
<time datetime="{{ page.date|date('Y-m-d') }}">{{ page.date|date('F jS, Y') }}</time>
{{ parent() }}
{% endblock %}
{% block content_bottom %}
{% include 'about-me.html.twig' %}
{% endblock %}

View file

@ -0,0 +1,31 @@
{% extends 'page' %}
{% block meta_description %}{{ page.title }} presented by Oliver Davies{% endblock %}
{% block content_wrapper %}
{{ parent() }}
{% if block('abstract') %}
<h2>Abstract</h2>
{% block abstract %}{% endblock %}
{% endif %}
{% if page.new_events %}
{% include 'presentation/events-new' with {
events: page.events,
} %}
{% else %}
{% include 'presentation/slides' with {
speakerdeck: page.speakerdeck,
} %}
{% include 'presentation/video' with {
video: page.video,
} %}
{% include 'presentation/events' with {
events: page.events,
} %}
{% endif %}
{% endblock %}

View file

@ -0,0 +1,21 @@
---
layout: page
title: Page not found
---
{% block content %}
Perhaps you were looking for one of these pages:
- [Products and services](/pricing)
- [Public speaking and workshops](/presentations)
- [My daily email list](/daily)
- [The Beyond Blocks podcast](/podcast)
If you were looking for something else, you can [search for it](/search).
{% endblock %}
{% block content_bottom %}
{% include 'daily-email-form.html.twig' with {
intro: 'Sign up and get daily emails about Drupal, PHP and software development.'
} %}
{% endblock %}

View file

@ -0,0 +1,24 @@
---
layout: page
title: Welcome to the Test-Driven Drupal email course
meta:
title: Welcome to the Test-Driven Drupal email course | %site.name%
---
{% block head_meta %}
<meta name="robots" content="noindex">
{% endblock %}
{% block content %}
Hi. Thank you for registering for my Test-Driven Drupal email course.
Every email will be sent from my personal email address, so if you have a question about any email, press reply, send it to me, and I'll personally respond.
I'd also appreciate any feedback as you work through or complete the course.
Remember to check your inbox and confirm your email address to ensure you get the lessons.
In the meantime, why not [go to my daily email archive](/archive) and read some of my previous daily emails?
Found this page by accident? [Consider subscribing for the course](/atdc).
{% endblock %}

View file

@ -0,0 +1,26 @@
---
layout: page
title: Automated Testing in Drupal email course
---
{% block content %}
Do you want to learn how to write automated tests and do test-driven development in Drupal?
Register below for my free 10-day email course, where you'll learn to write a new Drupal module from scratch with automated tests.
Learn to test things like:
* Whether pages exist.
* Whether users can view specific pages based on their permissions.
* Whether certain content is visible on a page given particular circumstances.
* Refactoring code to test dedicated classes such as Services, Repositories, Builders and Actions.
{% include 'email-course-form.html.twig' %}
{% endblock %}
{% block content_bottom %}
<div class="space-y-12">
{% include 'testimonials' with { tag: 'atdc' } %}
</div>
{% endblock %}

View file

@ -0,0 +1,30 @@
---
layout: page
title: Hi, PHP UserGroup Munich!
---
{% block head_meta %}
<meta name="robots" content="noindex">
{% endblock %}
{% block content %}
Thanks for attending my talk on [Build Configs](/build-configs).
[These are the slides](/talks/building-build-configs) I presented and the example video:
{% include 'youtube-video' with { id: 'LkhsdmxReUc'} %}
From nothing to a working Drupal 10 website in less than a minute!
If you have any further questions, let me know.
## Can we work together?
[Get in touch](/contact) if you're interested in using Build Configs-based managed configuration files in your projects (I offer one-off implementations or ongoing updates via a subscription), you need an in-house tool built for your team, or if we can [work together another way](/pricing).
## While you're here
Why not join my [daily email list](/daily) or register for my [free email course](/atdc) on automated testing in Drupal?
Oliver
{% endblock %}

View file

@ -0,0 +1,32 @@
---
layout: page
title: Hi, PHP Oxford!
---
{% block head_meta %}
<meta name="robots" content="noindex">
{% endblock %}
{% block content %}
Thanks for attending my talk on [Build Configs](/build-configs).
[These are the slides](/presentations/building-build-configs) I presented and the example video:
{% include 'youtube-video' with { id: 'LkhsdmxReUc'} %}
From nothing to a working Drupal 10 website in less than a minute!
If you have any further questions, let me know.
## Can we work together?
[Get in touch](/contact) if you're interested in using Build Configs-based managed configuration files in your projects (I offer one-off implementations or ongoing updates via a subscription), you need an in-house tool built for your team, or if we can [work together another way](/pricing).
If you liked the talk, or [any of my others](/presentations), I can also present talks and run workshops for companies.
## While you're here
Why not join my [daily email list](/daily) or register for my [free email course](/atdc) on automated testing in Drupal?
Oliver
{% endblock %}

View file

@ -0,0 +1,27 @@
---
title: Blog
generator: pagination
pagination:
max_per_page: 30
provider: data.posts
use: [posts]
---
{% if site.features.show_blog_post_count %}
{{ data.posts|length|number_format }}
{% endif %}
<ul>
{% for post in page.pagination.items %}
<li>
<a href="{{ post.url|trim('/', 'right') }}">{{ post.title }}</a> - {{ post.date|date('jS F Y') }}
</li>
{% endfor %}
{% if page.pagination.previous_page or page.pagination.next_page %}
<nav class="flex justify-center pt-10 space-x-6">
{% if page.pagination.previous_page %}<a href="{{ site.url }}{{ page.pagination.previous_page.url }}">&larr; Newer Posts{% endif %}<br />
{% if page.pagination.next_page %}<a href="{{ site.url }}{{ page.pagination.next_page.url }}">Older Posts &rarr;</a>{% endif %}<br />
</nav>
{% endif %}
</ul>

View file

@ -0,0 +1,38 @@
---
layout: page
title: BrumPHP
---
{% block head_meta %}
<meta name="robots" content="noindex">
{% endblock %}
{% block content %}
Thank you for attending my Sculpin talk at BrumPHP.
[The slides and a previous recording](/talks/building-static-websites-sculpin) are on the talk page.
To see an example of a Sculpin project, you can see [the source code for this website](https://github.com/opdavies/oliverdavies.uk) or [my demo for PHP South West](https://github.com/opdavies/phpsw-sculpin-demo).
If you want to try Sculpin, you can use [the Sculpin Skeleton project I created](https://github.com/opdavies/sculpin-skeleton) as a starting point for new projects.
## Can we work together?
As well as building static websites with Sculpin, I do consulting and advisory work using other PHP projects - namely, Drupal and Symfony.
I offer [unlimited monthly Drupal consulting](/consulting), [1-on-1 consultation calls](/call) and [pair programming sessions](/pair) to get people unstuck, and fixed prices (not estimates) for development work.
## Are you stuck on Drupal 7, 8 or 9?
Drupal 7 will be end-of-life and no longer supported as of the 5th of January, 2025.
Drupal 8 and 9 are already unsupported.
If you have an outdated Drupal website and want to know more about upgrading to Drupal 10, [book your upgrade roadmap](/drupal-upgrade) and I'll help you get there.
## Stay in touch
The best way to stay in touch with me is my [email list](/daily) to which I send daily emails about Drupal and software development.
All emails are sent from my personal email address, so you can press reply and contact me directly.
{% endblock %}

View file

@ -0,0 +1,78 @@
---
layout: page
title: "Build Configs - configuration files as a service"
products:
-
price: 2500
link: https://buy.stripe.com/14kbJs98K1fc0p29AC
buttonText: Get started
-
price: 1000
link: https://buy.stripe.com/7sIdRAbgS8HEfjWfZ1
buttonText: Sign up
---
**Sprint zero in a box.**
All of my projects contain similar build configuration files, such as Docker and Docker Compose files, PHPUnit and PHPStan configuration, and CI/CD pipeline workflows. I used to maintain these manually which was time-intensive and there was no guarantee that the same features or options were available from project to project.
Build Configs is a tool I've written to maintain a canonical set of templates and generate them for each project based on a per-project configuration file.
This makes it quicker and easier for me to create or onboard new projects to work on and, as all of the base files are centralised, when I fix something or add a new feature, the changes can be pushed to every project that uses it.
I've been using this on personal projects such as my [Drupal](https://github.com/opdavies/docker-example-drupal), [LocalGov Drupal](https://github.com/opdavies/docker-example-drupal-localgov) and [Drupal Commerce Kickstart](https://github.com/opdavies/docker-example-drupal-commerce-kickstart) Docker examples and for various client projects.
## How does it work?
- I create a `build.yaml` file for the project that contains its configuration - what version of PHP it needs, which web server to use, which paths to run automated tests from or static analysis on, etc.
- I run the `build-configs` tool which generates the required files, such as a Dockerfile, Docker Compose file, PHPStan and PHPUnit configuration files, and a `run` script for automating project commands.
- The files are committed to version control and pushed to your code repository (GitHub, GitLab, Bitbucket, etc).
- As I add new features or make changes, I will re-run the process to re-generate the files and push any changes - ensuring youre always up to date.
## What are some of the recent new features?
* Adding additional databases for working with migrations or multi-site projects.
* Adding additional `run` tasks.
* Adding CI pipelines with GitHub Actions to run automated quality checks.
* Creating and running Git hooks to run automated checks before changes are pushed.
## Example
In this short video, I show how Build Configs works by creating a new Drupal 10 project, generating its configuration files, and opening it in a browser.
From nothing to a ready-to-work-on Drupal website in less than a minute.
{% include 'youtube-video.html.twig' with { id: 'LkhsdmxReUc'} %}
## Options
### Initial project setup - £{{ page.products.0.price|number_format() }}
To get your project started on the right foot, I will create a new Drupal project skeleton for you using `build-configs` that is ready to work on, including all of the generated files. You can even edit them if you like as they will get no ongoing updates.
{% include 'button.html.twig' with {
text: page.products.0.buttonText,
url: page.products.0.link,
} %}
### Ongoing updates and support (single site) - £{{ page.products.1.price|number_format }} per month
Once you have a project with `build-configs`, if you want to receive ongoing updates to the generated files, I can automatically refresh your files once changes are available and push them to your code repository.
This also includes unlimited support via a dedicated Slack channel and you can make feature requests for me to consider adding to the roadmap.
If you like, I can add you to a beta users list and youll get new experimental features before anyone else.
{% include 'button.html.twig' with {
text: page.products.1.buttonText,
url: page.products.1.link,
} %}
If you have multiple Drupal sites, [contact me](/contact) and we can work something out.
### Retro-fitting into an existing project
`build-configs` can also be added to an existing project. It can be tricky and will be different from project to project so [contact me](/contact) and we can discuss it further.
Once I know more about your project, Ill be happy to give you a fixed-price quote to do the work.

View file

@ -0,0 +1,124 @@
---
layout: page
title: Book a 1-on-1 consulting call
link: https://savvycal.com/opdavies/consulting-call
price: 350
faqs:
-
- What happens after I pay?
- Your call is scheduled at the time you pay. So after payment is received, we're all set!
-
- What if I can't find a suitable time?
- If you can't find a suitable time, due to your time zone, or weekly schedule, just <a href="mailto:oliver@oliverdavies.uk?subject=1-on-1 consulting call">send me an email</a>, and we can arrange something. I try to be as flexible as possible!
-
- Can my colleague or business partner join the call?
- No. This is a 1-on-1 call. You are welcome to record the conversation to share it after the fact.
-
- Do you offer something more hands-on?
- Sure! <a href="/pair">Book a pair programming session</a> instead and well work on your code together.
-
- I still have questions!
- No problem. Send me an email at <a href="mailto:oliver@oliverdavies.uk?subject=1-on-1 consulting call">oliver@oliverdavies.uk</a>.
---
{% block content %}
{# <h2>Who is this for?</h2> #}
{# Pain #}
{# Dream #}
{# Fix #}
<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 #}
{% include 'button' with {
position: 'centre',
text: 'Book your call now',
url: page.link,
withArrow: true,
} %}
<h2>Some things I can help you with</h2>
<ul>
<li>How to approach a new project or task.</li>
<li>PHP and Drupal fundamentals and best practices.</li>
<li>Adding eCommerce functionality with Drupal Commerce.</li>
<li>Upgrading Drupal websites from unsupported versions.</li>
<li>Writing your first automated tests with PHPUnit or starting with test-driven development.</li>
<li>Introducing static analysis or other code quality tools to your project.</li>
<li>Adopting a utility-first CSS approach, e.g. Tailwind CSS, within a new or existing theme.</li>
<li>Configuring continuous integration (CI) pipelines with GitHub Actions, GitLab CI or Bitbucket Pipelines.</li>
<li>Automating tasks with Docker or Ansible.</li>
<li>Help fixing a bug or some broken code.</li>
<li>Reviewing your code and providing advice and suggestions.</li>
</ul>
<p>In this 1-on-1 video call, I can help you answer questions in these areas or any others you may have.</p>
{% include 'button' with {
position: 'centre',
text: 'Book your call now',
url: page.link,
withArrow: true,
} %}
{# Social proof #}
{% include 'testimonials' with { tag: 'call' } %}
{# Overcome objections #}
<h2>100% money-back guarantee!</h2>
<p>If you don't find the call valuable, just let me know, and I'll refund 100% of the cost.</p>
<h2>Frequently asked questions</h2>
{% for faq in page.faqs %}
<li class="list-none">
<h3 class="mt-0 text-lg">{{ faq.0 }}</h3>
<p>{{ faq.1|raw }}</p>
</li>
{% endfor %}
{# Uniqueness #}
<h2>Who am I?</h2>
<ul>
<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 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>
</ul>
{# 2nd CTA #}
{% embed 'callout.html.twig' with {
title: 'Ready to book your call?',
} %}
{% block callout_content %}
{% include 'button.html.twig' with {
text: 'Book your call now for £' ~ page.price|number_format ~ '',
url: page.link,
withArrow: true,
} %}
<p class="mb-0">You can pay and reserve your time slot straight away. There are a limited number of available slots each month. If, after the call, you decide to do a longer engagement with me, the cost of this call will be deducted from that engagement.</p>
{% endblock %}
{% endembed %}
{# Urgency #}
<h2>Availability is limited</h2>
<p>Im only available for a few 1-on-1 calls per month.</p>
<p>Scheduling is first come, first served, so the sooner you book your time slot, the sooner you will have the answers you need to move your project forward.</p>
{% endblock %}

View file

@ -0,0 +1,10 @@
---
layout: page
title: Contact Oliver
---
The best way to get in touch with me is via <a href="mailto:{{ site.email }}">email</a>. I usually reply within one business day.
I'm also on [LinkedIn][linkedin].
[linkedin]: https://www.linkedin.com/in/opdavies

View file

@ -0,0 +1,36 @@
---
layout: page
title: DrupalCamp Ghent 2024
---
{% block head_meta %}
<meta name="robots" content="noindex">
{% endblock %}
{% block content %}
Thank you for attending my session at DrupalCamp Ghent.
You can view the slides and previous recordings of my [Test Driven Drupal][] and [Tailwind CSS][] talks on this website.
If you want to learn more, I released a video of the [Tailwind CSS workshop][workshop] I gave for DrupalCamp Florida, and I've created a [free 10-day email course][course] on automated testing in Drupal.
## Can we work together?
Automated testing, test-driven development and Tailwind CSS are some of my favourite subjects to teach.
If you want me to teach your team to do test-driven development or build UIs with Tailwind CSS, team training is included within my [unlimited monthly Drupal consulting][consulting].
## Stay in touch
The best way to stay in touch with me is my [email list] to which I send daily emails about Drupal and software development.
All emails are sent from my personal email address, so you can press reply and contact me directly.
[consulting]: /consulting
[course]: /atdc
[email list]: /daily
[tailwind css]: /presentations/taking-flight-with-tailwind-css
[test driven drupal]: /presentations/tdd-test-driven-drupal
[workshop]: /daily/2024/01/22/tailwind-css-workshop-recording
{% endblock %}

View file

@ -0,0 +1,40 @@
---
layout: page
title: Drupal London
---
{% block head_meta %}
<meta name="robots" content="noindex">
{% endblock %}
{% block content %}
Thank you for attending my session for the Drupal London meetup.
You can [view the slides and previous recording][slides] of the talk on this website and [the example repository on GitHub][repo].
If you want to learn more, I released a [free 10-day email course][course] on automated testing in Drupal and [live stream on YouTube][youtube]. Most recently, [I contributed tests to the Content Access by Path module][video].
## Can we work together?
Drupal, automated testing and test-driven development are some of my favourite subjects to teach.
If you want me to teach your team to do test-driven development, team training is included within my [unlimited monthly Drupal consulting][consulting].
## Stay in touch
The best way to stay in touch with me is my [email list] to which I send daily emails about Drupal and software development.
All emails are sent from my personal email address, so you can press reply and contact me directly.
I also host the [Beyond Blocks podcast][podcast] where I discuss Drupal, PHP and other software-related topics with people.
[consulting]: /consulting
[course]: /atdc
[email list]: /daily
[podcast]: /podcast
[repo]: http://github.com/opdavies/drupal-london-meetup
[slides]: /presentations/tdd-test-driven-drupal
[video]: https://www.youtube.com/watch?v=XTpliKd47Lg
[youtube]: https://www.youtube.com/@opdavies
{% endblock %}

View file

@ -0,0 +1,86 @@
---
layout: page
title: Are you stuck on Drupal 7, 8 or 9?
button:
text: Book your roadmap now
url: https://buy.stripe.com/aEU4h0gBc4ro0p27sz
---
{# Pain #}
Drupal 7 will be unsupported on the **5th of January 2025**.
Drupal 8 has been unsupported since **November 2021**, and Drupal 9 since **November 2023**.
Are you stuck on any of these versions?
* Do you need to upgrade your website before it reaches its end-of-life date and is no longer supported?
* Are you worried about rebuilding your website and migrating your data to Drupal 10?
* Is all of your business logic embedded within your Drupal code, making it hard to upgrade?
* Do you rely on modules or themes that are no longer updated or don't exist for Drupal 10?
{# Dream #}
## What if you had a clear path how to upgrade your website?
* What if you had a recommended approach to upgrade your website to Drupal 10?
* What if you knew about potential issues ahead of time?
* What if you had a list of modules to use in Drupal 10, alternatives for any missing modules, and what functionality would need custom code?
{# Fix #}
## Drupal upgrade roadmap
An upgrade roadmap is a personalised audit of your Drupal website and includes details and actionable steps to upgrade it, including identifying potential blockers you might encounter.
{# 1st call to action #}
{% include 'button.html.twig' with {
text: page.button.text ~ ' ',
url: page.button.url,
withArrow: true,
} %}
## How does it work?
* After receiving payment, we will arrange an initial call to discuss your project.
* You share your source code and provide access to your website.
* I'll need access to the source code for your website. Ideally, this is via GitHub, GitLab, Bitbucket, or whatever online version control you currently use, but I can also audit a zip file of your Git repository.
* I'll need a user account to access your website to review its configuration, such as content types, user roles, etc. You can delete this once the engagement is complete.
* Alternatively, I can use an export of the database to get a local version of your site up and running. Ideally, this would be sanitised to remove personal information such as usernames, email addresses and passwords.
* I'll send you the document once it's complete for you to review.
* We'll arrange a follow-up call to go through it in detail and for you to ask any questions.
{# 2nd CTA #}
{% include 'button.html.twig' with {
text: page.button.text,
url: page.button.url,
withArrow: true,
} %}
{# Social proof #}
{% include 'testimonials.html.twig' %}
{# Overcome objections #}
{# Uniqueness #}
## Who am I?
* 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 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.
{# Urgency #}
## Availability is limited
There will be no further extensions of Drupal 7 support, and I have limited availability due to the time required, so buy now to secure your place.
{% include 'button.html.twig' with {
text: page.button.text ~ ' &rarr;',
url: page.button.url,
} %}

View file

@ -0,0 +1,28 @@
---
layout: page
title: DrupalCamp Ghent 2024
---
{% block head_meta %}
<meta name="robots" content="noindex">
{% endblock %}
{% block content %}
Thank you for attending my session at DrupalCamp Ghent.
You can view the slides and previous recordings of my [Test Driven Drupal](/talks/tdd-test-driven-drupal) and [Tailwind CSS](/talks/taking-flight-with-tailwind-css) talks on this website.
If you want to learn more, I released a video of the [Tailwind CSS workshop](/daily/2024/01/22/tailwind-css-workshop-recording) I gave for DrupalCamp Florida, and I've created a [free 10-day email course](/atdc) on automated testing in Drupal.
## Can we work together?
Automated testing, test-driven development and Tailwind CSS are some of my favourite subjects to teach.
If you want me to teach your team to do test-driven development or build UIs with Tailwind CSS, team training is included in my [unlimited monthly Drupal consulting](/consulting).
## Stay in touch
The best way to stay in touch with me is my [email list](/daily) to which I send daily emails about Drupal and software development.
All emails are sent from my personal email address, so you can press reply and contact me directly.
{% endblock %}

View file

@ -0,0 +1,89 @@
---
layout: page
title: My Drupal Contributions
modules:
-
name: Block ARIA Landmark Roles
machine_name: block_aria_landmark_roles
type: module
usage: 1346
stars: 16
-
name: Copyright Block
machine_name: copyright_block
type: module
usage: 2514
stars: 10
-
name: Feature Toggle Twig
machine_name: feature_toggle_twig
type: module
-
name: Layout Builder Extra Templates
machine_name: override_node_options
type: module
usage: 325
stars: 11
-
name: Null User
machine_name: null_user
type: module
usage: 5
stars: 4
-
name: System User
machine_name: system_user
type: module
-
name: Tailwind CSS Starter Kit
machine_name: tailwindcss
type: theme
usage: 260
---
[This is my profile page on Drupal.org](https://www.drupal.org/u/opdavies) and these are some of the projects I maintain:
<ul>
{% for project in page.modules|sort((a, b) => b.usage <=> a.usage) %}
<li>
{{ project.type|capitalize }}:
<a href="https://www.drupal.org/project/{{ project.machine_name }}">{{ project.name }}</a>
{% if project.usage %}
- {{ project.usage|number_format }} websites
{%- if project.stars -%}
, {{ project.stars }} {{ project.stars == 1 ? 'star' : 'stars' }}
{% endif %}
{% endif %}
</li>
{% endfor %}
</ul>
## Example Projects on GitHub
- [Docker Example Drupal](https://github.com/opdavies/docker-example-drupal)
- [Docker Example Drupal Commerce Kickstart](https://github.com/opdavies/docker-example-drupal-commerce-kickstart)
- [Docker Example Drupal LocalGov](https://github.com/opdavies/docker-example-drupal-localgov)
## Events
- I was a speaker at DrupalCon Lille in October 2023.
- I was a speaker at DrupalCon Europe 2020 (online).
- I was a workshop trainer at DrupalCamp NYC 2020 (online).
- I was a workshop trainer at DrupalCamp London 2020 (online).
- I was a speaker at DrupalCon Amsterdam in October 2019.
- I was a contribution day mentor at DrupalCon Vienna 2017.
- I was a speaker at DrupalCamp Dublin 2017.
- I was a speaker at DrupalCamp Bristol 2016.
- I was an organiser of DrupalCamp Bristol (2016, 2017, 2019).
- I was a contribution day mentor at DrupalCon Los Angeles and Barcelona 2015.
- I was a speaker at DrupalCamp North 2015.
- I was a speaker at DrupalCamp Brighton 2015.
- I was a contribution day mentor at DrupalCon Amsterdam 2014.
- I was a speaker at DrupalCamp London (2014, 2015, 2016, 2017, 2019).
- I was a volunteer at DrupalCamp London 2014.
- I was a contribution day mentor at DrupalCon Prague 2013.
- I was an organiser of the South Wales Drupal user group (SWDUG) and Drupal Bristol user group.
## Other
- I'm a Board Member of the [Drupal England and Wales Association](https://drupal-england-wales.github.io) (DEW).

View file

@ -0,0 +1,46 @@
---
layout: page
title: Introduction to Automated Testing and Test-Driven Development with Drupal
drupal_version: 10
prices:
early: 299
full: 499
is_early_bird: true
next_date: 2024-02-23
---
Are you a Drupal Developer who wants to learn about automated testing and test-driven development, or do you manage a development team that you'd like to train?
I've delivered large Drupal projects using automated tests and test-driven development for custom functionality, and maintain Drupal modules with thousands of installations whilst using their tests to ensure working code and prevent regressions.
I offer an interactive full-day workshop (previously presented at DrupalCamp London, and remotely for DrupalCamp NYC) that provides an introduction to automated testing in Drupal and how to utilise test-driven development - which I've updated specifically for Drupal {{ page.drupal_version }}.
## Contents
- What is automated testing, and why write tests?
- What types of tests are available in Drupal?
- Outside-in vs. inside-out testing.
- Configuring Drupal and PHPUnit to run tests locally.
- Exercise: writing tests for existing Drupal core functionality.
- Exercise: adding tests to an existing custom module.
- What is test-driven development?
- Exercise: writing a new Drupal module from scratch with test-driven development.
- Q&A
<hr />
## Dates and prices
The workshop is currently only available remotely, and the next available date is <span class="font-bold">{{ page.next_date|date('F jS, Y') }}</span>.
Seats are available at <span class="font-bold">{{ page.is_early_bird ? 'an early bird price of £' ~ page.prices.early : 'a price of £' ~ page.prices.full }}</span>, with a 10% discount for bulk orders of 5 or more seats.
{% include 'button.html.twig' with {
text: 'Book your seat',
url: 'https://buy.stripe.com/aEU9Bk2KmaPM3Be8wJ',
withArrow: true,
} %}
<hr />
{% include 'testimonials' with { tag: 'testing' } %}

View file

@ -0,0 +1,35 @@
---
layout: page
title: Glossary
terms:
-
- Automated Testing
-
- CI pipeline
-
- Continuous integration
- |
The process of continuously integrating your code with other people's code, i.e. at least once a day.
This is done by regularly pulling and merging everyone else's code with yours and pushing yours so it's available for others to do the same. This can also refer to CI pipelines and tools like GitHub Actions and GitLab CI.
-
- Continuous Delivery and Deployment
-
- Git
-
- PHPStan
-
- Static analysis
-
- Test-Driven Development
-
- Trunk-based Development
-
- Version control
---
<dl>
{% for term in page.terms if term.1 %}
<dt>{{ term.0 }}</dt>
<dd>{{ term.1 }}</dd>
{% endfor %}
</dl>

View file

@ -0,0 +1,24 @@
---
title: Homelab
---
My homelab/home server is running on a TUXEDO InfinityBook Pro Gen7 laptop and uses [NixOS as the operating system][0].
It hosts this website and a number of other static websites.
I also use it to self-host these services:
- [Audio Bookshelf](https://www.audiobookshelf.org)
- [Forgejo](https://forgejo.org)
- [Home Assistant](https://www.home-assistant.io)
- [Homepage](https://gethomepage.dev)
- [Immich](https://immich.app)
- [Jellyfin](https://jellyfin.org)
- [Mastodon](https://joinmastodon.org) (work in progress)
- [Paperless-ngx](https://docs.paperless-ngx.com)
- [Peertube](https://peertube.tv)
- [Tube Archivist](https://www.tubearchivist.com)
- [Uptime Kuma](https://uptime.kuma.pet)
- [Vaultwarden](https://github.com/dani-garcia/vaultwarden)
[0]: {{site.code.url}}/{{site.code.username}}/nix-config

View file

@ -0,0 +1,101 @@
---
layout: page
permalink: /
title: Oliver Davies - Drupal Developer, Consultant and Speaker
meta:
title: Drupal Development and Consulting by Oliver Davies
urls:
exploratory_call: https://savvycal.com/opdavies/drupal-consulting-exploratory-call
---
{% block content %}
{# Pain #}
<ul>
<li>Are bugs and errors on your Drupal website losing you customers?</li>
<li>Are you stuck on an outdated or unsupported version of Drupal?</li>
<li>Are you unable to efficiently change your website and spend your time searching for workarounds?</li>
<li>Does it take too long to release new features and bug fixes?</li>
<li>Are you considering switching to Drupal from your current CMS and wondering if it's the right choice?</li>
</ul>
<!--
- Do you need something added or fixed on your website but don't know where to start?
- Do you want to enhance and upskill your in-house development team?
- Is your website full of legacy code that no one understands or you don't use and that you still need to maintain?
-->
<hr>
{# Solution #}
<h2>What can I do for you?</h2>
{# TODO: add more information about each of these. #}
<ul>
<li><strong>Analysis & Audit</strong>. I can help you identify expensive bottlenecks, hidden issues, and potential problems with your site. I'll put together a detailed report with actionable next-steps on how to fix any issues.</li>
<li><strong>Roadmap & Planning</strong>. We'll identify your goals and challenges, and I'll put together a custom roadmap to help you get there.</li>
<li><strong>Implementation and Maintenance</strong>. I can build your project for you, or work with your engineering team to accelerate your progress.</li>
<li><strong>Team Training</strong>. Give your team the skills they need to get more done. Grow, retain, and attract talented developers.</li>
<li><strong>Ongoing Advisory</strong>. Throughout the duration of your project, I'll be available to review progress, answer questions, recommend tools and processes, share emerging best practices, and keep your project on the right track.</li>
</ul>
<hr>
<h2>You're already running my code in production</h2>
<p>I've contributed code to Drupal core and written popular contributed Drupal modules and themes, PHP and JavaScript libraries, and Tailwind CSS plugins.</p>
<p>For example, the Override Node Options module is used on around 40,000 active Drupal websites.</p>
<hr>
<h2>Here's some of my recent work</h2>
<ul>
<li>I developed a Drupal module to integrate with a third-party translation provider, enabling automated translations of English-only content from XML feeds into different languages.</li>
<li>I created an example eCommerce application using Drupal Commerce for an asset finance company for their presentation to a multinational eCommerce company.</li>
<li>I migrated a membership portal for a UK health association from Drupal 7 to Drupal 10, migrating their content and users and rebuilding the required custom functionality.</li>
<li>I presented workshops on automated testing and test-driven development at Drupal conferences.</li>
<li>I assisted a UK consultancy in hiring their in-house Senior Drupal Developer by providing technical input for interviews and candidate feedback.</li>
<li>I developed a Tailwind CSS-based theme for a multi-tenant Drupal application, delivering the theme scaffolding, build system, and initial components. I also trained and supported the in-house development team.</li>
<li>I developed a Drupal Commerce application for an annual international photography competition that allows photographers to pay and submit their entries, including custom scoring functionality for jurors.</li>
</ul>
<hr>
<h2>Get in touch</h2>
<p>Unlike working with large agencies, you'll <strong>only work directly with me</strong>. You won't be handed off sub-contractors, offshore teams or Junior Developers who are learning on the job.</p>
<p>I only give fixed prices so <strong>you'll know upfront how much my work will cost</strong> and all of my work is covered by a bug-free guarantee.</p>
{% include 'button.html.twig' with {
full_width: true,
text: 'Click here to email Oliver',
url: 'mailto:' ~ site.email,
} %}
<hr>
{% include 'testimonials' with {
limit: 5,
tag: 'front',
title: 'Kind words from clients and customers',
} %}
<hr>
<h2>Get in touch</h2>
<p>Theres no reason to wait. Drop me a line and I'll get back to you ASAP.</p>
{% include 'button.html.twig' with {
full_width: true,
text: 'Click here to email Oliver',
url: 'mailto:' ~ site.email,
} %}
{% endblock %}

View file

@ -0,0 +1,16 @@
---
layout: page
title: An Introduction to Mob Programming
---
* <https://leanpub.com/mobprogramming>
* <https://tuple.app>
* <https://mob.sh>
* <https://www.jetbrains.com/code-with-me>
* <https://code.visualstudio.com/learn/collaboration/live-share>
<div class="mt-10 space-y-8">
{% include 'youtube-video' with { id: '28S4CVkYhWA' } %}
{% include 'youtube-video' with { id: 'ri-35oHWIU8' } %}
{% include 'youtube-video' with { id: 'jPLEbZLE3g8' } %}
</div>

View file

@ -0,0 +1,81 @@
---
layout: page
title: Pair program with me
price: 499
link: https://savvycal.com/opdavies/pair
---
{% block content %}
{# Pain #}
<p>Are you stuck adding a new feature or fixing a bug?</p>
<p>Do you need help starting a new Drupal module or theme?</p>
<p>Would you like another pair of eyes on your code, providing real-time suggestions and feedback rather than waiting for a code review?</p>
{# Dream #}
{# Fix #}
<h2>Book a pair programming session</h2>
<p>What if you could have a pair programming session with a Lead Developer and Drupal Expert with {numberOfYears} years of professional experience?</p>
<p>Book a 2-hour pair programming call, and we can work on your code together - adding new functionality, fixing bugs, writing tests for existing code, or something else.</p>
<p>Price: £{{ page.price|number_format }}</p>
{# 1st call to action #}
{% include 'button.html.twig' with {
text: 'Book your session now',
url: page.link,
} %}
{# Social proof #}
{% include 'testimonials.html.twig' %}
{# Overcome objections #}
<h2>100% money-back guarantee!</h2>
<p>If you don't find the session valuable, just let me know, and I'll refund 100% of the cost.</p>
{# Uniqueness #}
<h2>Who am I?</h2>
<ul>
<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 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>
</ul>
{# 2nd CTA #}
{% embed 'callout.html.twig' with {
title: 'Ready to book your session?',
} %}
{% block callout_content %}
{% include 'button.html.twig' with {
text: 'Book your session now for £' ~ page.price|number_format ~ ' ',
url: page.link,
withArrow: true,
} %}
<p class="mb-0">You can pay and reserve your time slot straight away. There are a limited number of available slots each month. If, after the call, you decide to do a longer engagement with me, the cost of this call will be deducted from that engagement.</p>
{% endblock %}
{% endembed %}
{# Urgency #}
<h2>Availability is limited</h2>
<p>Im only available for a few pair programming sessions per month.</p>
<p>Scheduling is first come, first served, so the sooner you book your time slot, the sooner you will have the answers you need to move your project forward.</p>
{% endblock %}

View file

@ -0,0 +1,38 @@
---
layout: page
title: PHP Berkshire
---
{% block head_meta %}
<meta name="robots" content="noindex">
{% endblock %}
{% block content %}
Thank you for attending my Sculpin talk at PHP Berkshire.
[The slides and a previous recording](/presentations/building-static-websites-sculpin) are on the talk page.
To see an example of a Sculpin project, you can see [the source code for this website](https://github.com/opdavies/oliverdavies.uk), [my Zettlekasten](https://github.com/opdavies/zet.oliverdavies.uk) or [my demo for PHP South West](https://github.com/opdavies/phpsw-sculpin-demo).
If you want to try Sculpin, you can use [the Sculpin Skeleton project I created](https://github.com/opdavies/sculpin-skeleton) as a starting point for new projects.
## Can we work together?
As well as building static websites with Sculpin, I do consulting and advisory work using other PHP projects - namely, Drupal and Symfony.
I offer [unlimited monthly Drupal consulting](/consulting), [1-on-1 consultation calls](/call) and [pair programming sessions](/pair) to get people unstuck, and fixed prices (not estimates) for development work.
## Are you stuck on Drupal 7, 8 or 9?
Drupal 7 will be end-of-life and no longer supported as of the 5th of January, 2025.
Drupal 8 and 9 are already unsupported.
If you have an outdated Drupal website and want to know more about upgrading to Drupal 10, [book your upgrade roadmap](/drupal-upgrade) and I'll help you get there.
## Stay in touch
The best way to stay in touch with me is my [email list](/daily) to which I send daily emails about Drupal and software development.
All emails are sent from my personal email address, so you can press reply and contact me directly.
{% endblock %}

View file

@ -0,0 +1,32 @@
---
layout: page
title: The Beyond Blocks podcast
meta:
description: A podcast about Drupal, PHP open-source and software development.
use:
- podcast_episodes
---
{% block content_bottom %}{% endblock %}
{% block content %}
A podcast about Drupal, PHP, open-source, and related software development topics.
Guests include people like [Matt Glaman](/podcast/1-retrofit), [Eirik Morland](/podcast/8-eirik-morland-violinist), [Tim Lehnen](/podcast/9-tim-lehnen), [Ryan Szrama](/podcast/13-ryan-szrama-centarro), [Sam Mortenson](/podcast/19-sam-mortenson) and [Jess Archer](/podcast/25-jess-archer-drush-laravel-prompts).
## Episodes {.sr-only}
{% for episode in data.podcast_episodes|reverse %}
{% set episodeNumber = episode.url|trim('/')|split('/')|last|split('-')|first %}
<h3>Episode {{ episodeNumber }}: {{ episode.topic }} with {{ episode.guests|join(' and ') }}</h3>
<time datetime="{{ episode.date|date('Y-m-d') }}">{{ episode.date|date('F jS, Y') }}</time>
{{ episode.blocks.content|markdown }}
<a href="{{ episode.url|trim('/', 'right') }}" aria-label="Listen to episode {{ episodeNumber }}">
Listen now
&rarr;
</a>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,23 @@
---
layout: page
title: Presentations
use: [presentations]
---
<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>
{% for presentation in data.presentations|sort((a, b) => a.events|last.date|date('U') > b.events|last.date|date('U') ? -1 : 1) %}
<article>
<div class="not-prose">
<h2 class="text-xl font-bold">
<a class="font-bold text-blue-primary dark:text-blue-400" href="{{ presentation.url|trim('/', 'right') }}">{{ presentation.title }}</a>
</h2>
</div>
{% if presentation.blocks.description %}
{{ presentation.blocks.description|raw }}
{% else %}
<p>{{ presentation.description }}</p>
{% endif %}
</article>
{% endfor %}

View file

@ -0,0 +1,43 @@
---
layout: page
title: Press Info
---
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>.
## Short Bio
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
* Getting started in software development, Drupal, or open-source software.
* Building your first Drupal website.
* Drupal module and theme development.
* Automated testing and test-driven development (TDD).
* Static analysis in PHP applications.
* Utility-first styling and Tailwind CSS.
* Git and different ways to use it (continuous integration, trunk-based development).
* Contributing to open-source software.
## Social Media and Elsewhere
* [Drupal.org][drupal]
* [GitHub][]
* [LinkedIn][]
* [Twitter][]
## Physical Location
[Caerleon, Wales](https://www.google.co.uk/maps/place/Caerleon,+Newport)
## Photo
![](/assets/images/social-avatar.jpg)
[drupal]: https://www.drupal.org/u/opdavies
[github]: https://github.com/opdavies
[linkedin]: https://www.linkedin.com/in/opdavies
[twitter]: https://twitter.com/opdavies

View file

@ -0,0 +1,114 @@
---
layout: page
title: Products and Services
products:
-
title: Drupal development subscription
description: |
In less time than it takes to post on a job board, and for a fraction of the cost, get unlimited access to a certified Drupal development expert, core contributor and multiple-time DrupalCon speaker for a fixed monthly fee. No surprises. Cancel anytime.
perMonth: true
price: 5000
isFrom: true
link:
text: Register now
url: /subscription
-
title: Drupal upgrade roadmap
description: |
Are you stuck on an already or soon-to-be unsupported version of Drupal? Get a personalised roadmap of your Drupal website, including details and actionable steps to upgrade it.
price: 5000
link:
text: Book your roadmap now
url: /drupal-upgrade
-
title: Diagnosis
description: |
An in-depth investigation into a single issue where I'll provide a report with my findings and advice on the next steps. Once you've purchased, you can book a Zoom call with me to discuss what you want me to investigate.
price: 2500
link:
text: Book now
url: https://buy.stripe.com/00gbJs84G2jg8Vy9AJ
-
title: Private talk or workshop
description: |
If you found one of my public speaking presentations or workshops useful, I'm available for private speaking engagements on a variety of topics to help your team succeed.
price: 2000
isFrom: true
link:
text: Schedule a talk
url: https://buy.stripe.com/eVa4h0bgSaPM6NqcMU
-
title: 1-on-1 consulting call
description: |
Book a 1-on-1 video call, and I can help you by answering questions about software development, architecture and automation, helping you write your first automated test, or reviewing some of your code and giving advice and suggestions.
price: 350
link:
text: Book your call now
url: /call
-
title: Pair program with me
description: |
Would you like another pair of eyes on your code, providing real-time suggestions and feedback rather than waiting for a code review? Book a 2-hour pair programming call and we can work on your code together.
price: 499
link:
text: Book your session now
url: /pair
-
title: Introduction to Automated Testing in Drupal - 10-day email course
description: |
Register for my free email course on automated testing in Drupal.
link:
text: Register now
url: /atdc
-
title: Drupal module template
description: |
If you're creating a new Drupal module, try starting with my free module template for Drupal 9 and 10.
link:
text: Download
url: https://github.com/opdavies/drupal-module-template
---
{% block content %}
{% for product in page.products %}
<article>
<h2>
{{ product.title }} -
{{ product.isFrom ? 'from' }}
{% if product.price %}
£{{ product.price|number_format() }}
{% else %}
free!
{% endif %}
{{ product.perMonth ? 'per month' }}
</h2>
<div>
<p>
{{ product.description }}
{% if product.remainingPlaces == 1 %}
<strong>1 place remaining!</strong>
{% elseif product.remainingPlaces > 1 %}
<strong>{{ product.remainingPlaces }} places remaining!</strong>
{% elseif product.isLimited %}
<strong>Limited places available!</strong>
{% endif %}
</p>
</div>
<footer>
<a href="{{ product.link.url }}">
{{ product.link.text }}
&rarr;
</a>
</footer>
</article>
{% endfor %}
{% endblock %}
{% block content_top %}
{% include 'message.html.twig' %}
{% endblock %}

View file

@ -0,0 +1,49 @@
---
layout: page
title: Search
common_searches:
- Drupal
- Test-Driven Development
- Automated testing
- Refactoring
- PHP
- Tailwind CSS
- Neovim
- Git
- Linux
---
<div>
<form
class="flex items-center space-x-6"
action="https://www.google.com/search"
method="get"
>
<div class="flex-1">
<input
type="hidden"
name="q"
value="site:https://www.oliverdavies.uk"
/>
<input class="w-full" type="text" name="q" alt="search" />
</div>
<div>
<input class="cursor-pointer button" type="submit" value="Search" />
</div>
</form>
</div>
<aside>
<h2>Common searches</h2>
<ul>
{% for term in page.common_searches %}
<li>
<a href="https://duckduckgo.com/?q={{ term|replace({' ': '+'}) }}+site%3A{{ site.url|url_encode }}">
{{ term }}
</a>
</li>
{% endfor %}
</ul>
</aside>

View file

@ -0,0 +1,10 @@
---
layout: page
title: Sculpin from Scratch
---
Coming soon: a daily email course on building static websites with Sculpin.
If you're interested, why not [watch my Sculpin talk](/presentations/building-static-websites-sculpin) in the meantime or check out [the source code for this website](https://github.com/opdavies/oliverdavies.uk)?
If you want me to let you know once the course is ready, <a href="mailto:{{ site.email }}">send me an email and I'll add you to the list</a>.

View file

@ -0,0 +1,22 @@
---
permalink: /sitemap.xml
use:
- pages
- podcast_episodes
- posts
- presentations
default_priorities:
posts: '0.2'
---
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% for content_type, content in data %}
{% for item in content %}
<url>
<loc>{{ site.url }}{{ item.url|trim('/', 'right') }}</loc>
<priority>{{ page.default_priorities[content_type]|default('0.5') }}</priority>
</url>
{% endfor %}
{% endfor %}
</urlset>

View file

@ -0,0 +1,32 @@
---
layout: page
title: Speaker Information
---
## Bio
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
- <https://www.dropbox.com/s/say1muiqedik0l4/0188395_thumb.jpg>
## Some events that I've spoken at
- BlueConf 2019 (Cardiff, UK)
- DrupalCamp Brighton 2015
- DrupalCamp Bristol 2016
- DrupalCamp Dublin 2017
- DrupalCamp London (2014, 2015, 2016, 2017, 2019, 2020)
- DrupalCamp North 2015 (Sunderland, UK)
- DrupalCon Amsterdam 2019
- DrupalCon Europe 2020 (Online)
- DrupalCon Lille 2023
- Nomad PHP
- Norfolk Developers' Conference (nor(DEV):con) 2023
- PHP North West 2017 (Manchester, UK - 10 year anniversary)
- PHP South Coast 2016 (Portsmouth, UK)
- PHP UK Conference 2018 (London, UK)
- WordCamp Bristol 2019
I also [gave a number of talks remotely](/blog/speaking-remotely-during-lockdown) for various user groups and conferences during COVID-19.

View file

@ -0,0 +1,31 @@
---
layout: page
title: Sponsor me to work on Open Source Software
---
I enjoy working with and on open source software.
I've been using PHP since 2007 and Drupal since 2008 and am an enthusiastic open source contributor.
Drupal core, Drupal.org, LocalGov Drupal and Drupal Commerce are some of the projects I've contributed to.
I maintain the popular Override Node Options module which is used on around 40,000 active Drupal websites and created a starterkit Drupal theme for Tailwind CSS.
There are lots of other examples on my [Drupal.org][0] and [GitHub][1] profiles.
I've found that companies want to contribute more, but are too busy.
Most projects have a custom module or library that was going to be contributed back, but it just hasn't happened yet.
## Why not sponsor a contributor?
If you don't have time to contribute, why not sponsor me to do it for you?
Whether it's contributing that module or library, adding a missing feature or fixing a bug in something you use, or you just want to sponsor me to work on open source, <a href="mailto:{{site.email}}?subject=Open source sponsorship">I'd love to hear from you</a>.
We can discuss how much time you want to sponsor and what you want me to focus on and I'll get to work.
You'll be mentioned on any relevant blog posts, live streams, issues and pull or merge requests, so everyone will know that you sponsored me and you'll get the kudos.
[0]: {{site.drupalorg.url}}
[1]: {{site.github.url}}

View file

@ -0,0 +1,9 @@
---
layout: page
title: Testimonials
---
{% include 'testimonials' with {
limit: 0,
title: 'All Testimonials',
} %}

View file

@ -0,0 +1,64 @@
---
layout: page
title: Things you should know about PHP
---
Thanks for attending my [Things you should know about PHP](/talks/things-you-should-know-about-php) talk.
I hope that you learned some things about PHP, its ecosystem, and its communities, and if you haven't tried using PHP yet, I'd encourage you to do so.
Here are links to the resources that I mention in the talk, plus a couple of extras.
## Resources
- [The PHP Foundation](https://thephp.foundation) - non-profit to support, advance, and develop the PHP language
- [PHP-FIG](https://www.php-fig.org) - PHP Framework Interop Group
- [Composer](https://getcomposer.org) - dependency manager
- [Drupal](https://www.drupal.org) - content management system
- [Jigsaw](https://jigsaw.tighten.co) - static site generator
- [Laravel](https://laravel.com) - framework
- [Nomad PHP](https://nomadphp.com) - online user group
- [PHP official images on Docker Hub](https://hub.docker.com/_/php)
- [PHPStan](https://phpstan.org) - static analysis tool
- [PHPUnit](https://phpunit.de) - testing framework
- [Pest](https://pestphp.com) - testing framework
- [Psalm](https://psalm.dev) - static analysis tool
- [Sculpin](khttps://sculpin.io) - static site generator
- [WordPress](https://wordpress.org) - content management system
- [php.net](https://www.php.net) - online documentation
- [php[architect]](https://www.phparch.com) - online magazine
## Books
- [Laravel: Up & Running](https://www.oreilly.com/library/view/laravel-up/9781492041207)
- [Symfony: The Fast Track](https://symfony.com/book)
## Videos
- [Codecourse](https://codecourse.com)
- [How to Code Well](https://www.howtocodewell.net)
- [Laracasts](https://laracasts.com)
- [SymfonyCasts](https://symfonycasts.com)
## Podcasts
- [How to Code Well podcast](https://howtocodewell.fm)
- [PHPUgly](https://www.phpugly.com)
- [Talking Drupal](https://talkingdrupal.com)
- [The Laravel Podcast](https://laravelpodcast.com)
- [The PHP Roundtable](https://phproundtable.com)
- [Voices of the elePHPant](https://voicesoftheelephpant.com)
## Can I help?
Do you want to introduce PHP to your company or team, or add one of these tools to your existing PHP application?
I offer consulting calls and services to reduce your onboarding time and get you up and running quicker and easier.
<div class="mt-6">
{% include 'button.html.twig' with {
text: 'Book your call ',
url: '/call',
withArrow: true,
} %}
</div>

View file

@ -0,0 +1,26 @@
---
layout: page
title: Welcome!
meta:
title: Welcome to my Daily Email list | %site.name%
---
{% block head_meta %}
<meta name="robots" content="noindex">
{% endblock %}
{% block content %}
Hi. Thank you for subscribing to my Daily Email list!
You'll soon start to get emails from me directly in your inbox. Every day.
The first might be later today!
Every email is sent from my personal email address, so if you have a question about any email, press reply, send it to me, and I'll personally respond.
Also, remember to check your inbox and confirm your email address to ensure you get my emails.
In the meantime, why not [go to the archive](/archive) and read some of my previous dailies?
Found this page by accident? [Consider joining the list](/daily).
{% endblock %}

View file

@ -0,0 +1,37 @@
---
date: 2023-11-10
topic: Retrofit
guests:
- Matt Glaman
links:
- - Retrofit
- https://retrofit-drupal.com
- - Retrofit on GitHub
- https://github.com/retrofit-drupal/retrofit
- - Running legacy Drupal 7 code on your Drupal 10 site
- https://mglaman.dev/blog/retrofit-running-legacy-drupal-7-code-your-drupal-10-site
- - PHPStan
- https://phpstan.org
- - phpstan-drupal
- https://github.com/mglaman/phpstan-drupal
- - Centarro
- https://www.centarro.io
- - Drupal Commerce
- https://drupalcommerce.org
- - Matt on GitHub
- https://github.com/mglaman
- - Matt on Drupal.org
- https://www.drupal.org/u/mglaman
- - Matt on YouTube
- https://www.youtube.com/@nmdmatt
- - Matt on Twitch
- https://www.twitch.tv/mglaman
transistor:
id: 7d728873
episode_filename: 1-retrofit.mp3
file_size: 20523767
duration: 0:43:52
use: [podcast_episodes]
---
In this episode, Oliver is joined by Matt Glaman to discuss Retrofit. A tool that makes it easier to upgrade Drupal websites by allowing legacy Drupal code to run on any version of Drupal.

View file

@ -0,0 +1,37 @@
---
date: 2024-02-10
topic: Twig, Symfony and SymfonyCasts
guests:
- Ryan Weaver
transistor:
id: f276e486
links:
- - Symfony
- https://symfony.com
- - SymfonyCasts
- https://symfonycasts.com
- - Symfony UX
- https://ux.symfony.com
- - Twig
- https://twig.symfony.com
- - Twig Components
- https://ux.symfony.com/twig-component
- - Composer
- https://getcomposer.org
- - 'Symfony: The Fast Track'
- https://symfony.com/book
- - SymfonyCasts Blog
- https://symfonycasts.com/blog
- - Ryan on Twitter
- https://twitter.com/weaverryan
- - Ryan on GitHub
- https://github.com/weaverryan
- - Ryan on YouTube
- https://www.youtube.com/@weaverryan
episode_filename: 10-symfonycasts.mp3
file_size: 26892969
duration: 0:53:59
use: [podcast_episodes]
---
This week, Oliver is joined by Ryan Weaver - Symfony Developer, Symfony core team member and Writer for SymfonyCasts to discuss the recent developments in Twig templates, SymfonyCasts, release cycles, and similarities between the Drupal and Symfony projects and communities.

View file

@ -0,0 +1,35 @@
---
date: 2024-02-16
topic: Build something useful in one day
guests:
- Mark Conroy
transistor:
id: c2c3f7b3
links:
- - mark.ie
- https://mark.ie
- - Annertech
- https://www.annertech.com
- - Running Plan Generator
- https://runningplangenerator.com
- - Web Component Design System
- https://web-components-design-system.mark.ie
talking_points:
- Building better websites faster, including Drupal distributions, such as LocalGov.
- What Drupal gives you out of the box and using the right tool for the job.
- Drupal migrations and migrating Drupal configuration from a spreadsheet.
- Saving time and improving efficiency by standardising base builds with Docksal and Composer.
- How to build projects, like the Running Plan Generator, in a day and training for 10Ks and marathons.
- Prioritising tasks.
- Annertech's development workflow and standardisation by developing on remote servers.
- Why you may not need preprocessors and complicated front-end build tools, and reducing complexity using vanilla CSS, JavaScript and web components.
- Lowering the barrier to entry to contribute to open-source projects.
- Building a design system/component library with web components.
- The Content Access by Path module that was developed for Essex County Council.
episode_filename: 11-something-useful.mp3
file_size: 30369780
duration: 1:02:28
use: [podcast_episodes]
---
This week, Oliver is joined by Mark Conroy - Director of Development at Annertech - to discuss all things front-end and how to build something useful in one day.

View file

@ -0,0 +1,22 @@
---
date: 2024-02-26
topic: Docker
guests:
- Nick Janetakis
transistor:
id: a0498296
links:
- - Nick's website and blog
- https://nickjanetakis.com
- - Nick on YouTube
- https://www.youtube.com/@NickJanetakis
- - Nick's Docker example repositories on GitHub
- https://github.com/nickjj?tab=repositories&q=-example
talking_points: []
episode_filename: 12-docker.mp3
file_size: 28021353
duration: 0:51:48
use: [podcast_episodes]
---
This week, Oliver is joined by Nick Janetakis - a Software Developer, Docker Captain and Teacher who focuses on building and deploying web apps - to discuss Docker, content creation and more.

View file

@ -0,0 +1,63 @@
---
date: 2024-03-04
topic: Centarro and Drupal Commerce
guests:
- Ryan Szrama
transistor:
id: ab539bef
links:
- - Centarro
- https://www.centarro.io
- - Ryan on Twitter
- https://twitter.com/ryanszrama
- - Ryan on Drupal Answers
- https://drupal.stackexchange.com/users/866/ryan-szrama
talking_points:
- eCommerce, Ubercart and Drupal Commerce.
- How Commerce Guys started.
- How Ryan started in the eCommerce space.
- The origins of Drupal Commerce and Commerce Guys.
- Commerce Kickstart.
quotes:
- The best code isn't the most novel or most compact, it's the most easily readable and easily extendable (RS).
- My only resource for learning PHP was php.net (RS)
- I'm a self-taught Developer or community-taught, I suppose (OD).
- Porting osCommerce into Drupal, which became Ubercart (RS).
- Do we do this in Ubercart and Drupal 6 that we know works, or this new thing that was Drupal Commerce in Drupal 7 (OD).
- It was built in Ubercart and was in production until quite recently, considering we're in 2024 (OD).
- Once you get thoroughly entrenched in a platform, the cost to re-platform outweighs the cost of maintaining it in-house (RS).
- If there's 10,000 of anybody, they're going to want to pay for support (RS).
- How do we convince them to invest in an upgrade that doesn't see them jettison Drupal entirely? (RS)
- One thing I like about Drupal Commerce is its flexibility. (OD)
- It doesn't need to be a t-shirt or book shop. You can do some outside the box things with it. (OD)
- Being a native extension of Drupal has a lot more advantages and pros than cons. (RS)
- Ubercart was more "batteries included" and "this is what it does", whereas Drupal Commerce is more flexible and although it requires a bit more setup to begin with, you can plug it together the way you want to. (OD)
- Our vision was to go the next step of getting off the Drupal island. (RS)
- The Commerce Addressing library is now over 17,000,000 downloads. (OD)
- Composer allowed us to separate our projects and separate our concerns. (RS)
- Do you know when you should not use Views and when you decouple that component and use JavaScript and the REST API? (RS)
- If we can reduce the number of times introduce those kinds of problems, that's how I would certify somebody. (RS)
chapters:
- 00:00 Introduction and DrupalCon Lille.
- 6:00 How did you get started in software development?
- 17:36 Getting into eCommerce.
- 19:14 The origins of Commerce Guys and Drupal Commerce.
- 20:26 How Oliver started with eCommerce projects.
- 22:68 Ubercart for Drupal 7?
- 30:04 Commerce Kickstart.
- 34:08 Commerce flexibility.
- 35:52 More commerce Kickstart.
- 38:62 Ubercart and Commerce differences.
- 40:34 The Commerce Kickstart project template.
- 42:50 Building Drupal Commerce 2.
- 46:38 Releasing new things.
- 51:58 Certified Centarro partners?
- 57:38 Centarro roadmap.
- 62:06 Wrapping up.
episode_filename: 13-drupal-commerce.mp3
file_size: 30353061
duration: 1:03:36
use: [podcast_episodes]
---
This week, Oliver is joined by Ryan Szrama, CEO of Centarro, to discuss Drupal Commerce, Commerce Kickstart, Mario Kart and the dreaded `cache_form` table.

View file

@ -0,0 +1,45 @@
---
date: 2024-03-11
topic: Diffy and Visual Regression Testing
guests:
- Yuri Gerasymov
transistor:
id: 5940d69f
links:
- - About Yuri
- https://ygerasimov.com/about-me
- - Diffy
- https://diffy.website
- - Yuri on Drupal.org
- https://www.drupal.org/u/ygerasimov
talking_points:
- What is visual regression testing?
- How do you deal with false positives?
- Different use cases for visual regression testing.
- Automatic updates.
- Scheduling content.
- Visual regression testing in CI.
- Diffy in WordPress.
- What's a baseline?
- Initial setup and onboarding feedback.
- Testing dark mode?
- Component testing with Storybook and Fractal?
- Testing local environments.
- Testing as authenticated users.
- The roadmap for Diffy.
quotes:
- We help development teams to have less visual bugs in their website. We take screenshots of the pages and compare them so you can see what changed and how. (YG)
- We built tools for you to mock the content. You provide selectors for the elements with the content of the article and we'll replace it with lorem ipsum text so it will be exactly the same across multiple environments. (YG)
- I can still write an assertion to check the text is on the page or not, but it won't confirm it's in the correct place. (OD)
- Having a tool checking for changes on a regular basis instead of only after a deployment would be very useful. (OD)
- So, you could have a tool like Violinst automatically creating pull requests and Diffy checking those PRs, so the two could work together? (OD)
- With visual testing, it's very easy to get started. (YG)
- Visual testing is great for showing your client your work. (YG)
chapters: []
episode_filename: 14-diffy.mp3
file_size: 22892297
duration: 0:46:25
use: [podcast_episodes]
---
This week, Oliver discusses visual regression testing and Diffy with Yuri Gerasymov.

View file

@ -0,0 +1,51 @@
---
date: 2024-03-20
topic: Domain-Driven Design
guests:
- Rob Allen
transistor:
id: 0de0b405
links:
- - Rob's blog
- https://akrabat.com
- - Nineteen Feet
- https://19ft.com
talking_points:
- Rebuild vs. rewrite.
- Writing good commit messages.
- Are code comments useful?
- Technical Design Documents and ADRs.
- Ubiquitous language and Domain-Driven Design.
- PHP UK, PHP South West, conferences and user groups.
- DDD in Drupal?
- DRY and YAGNI.
- When to refactor?
quotes:
- I quite like legacy projects because i think they've already proved their worth in the marketplace. (RA)
- I general, I think that rewriting the wrong approach nearly every single time. (RA)
- Things are so impermanent. The only things you can trust are in the source code and what's in the revision history of that source code.
- We have the "what" but we don't have the "why". (OD)
- As you do this for longer, I think you start picking up on what you wish you'd written in the past. (RA)
- I think nearly everything related to software development that really matters is invariably about communication. (RA)
- Nearly everything that results in good quality software is because good communication works. (RA)
- Ubiquitous language is using the same language the specialists are using. (RA)
- An awful lot about DDD is trying to get the communication right. (RA)
- If you pretend it's not happening, it doesn't mean it's not happening - just that you're ignoring the problem. (RA)
- It's cheaper to fix things earlier in the process. (RA)
- User groups are such a good community resource. We get to try thing (talks) out. (RA)
- A conference gives you a focused block of time to learn something. (RA)
- I think the tenets of DDD can are important regardless (of the size of the project). (RA)
- We can refactor our way out, but now the overall time is longer. (RA)
- Time spent upfront is tangible effect on the time spent later. (RA)
- The biggest one [benefit of DDD] is that you end up with a project that's fit for purpose.
- You're way more likely to deliver a project that does what the customer needs if you have listened and understood what they said. (RA)
- It's in the customer's best interest for you to get it right the first time. (OD)
- You have to be proactive. It doesn't happen by default. (RA)
chapters: []
episode_filename: 15-ddd.mp3
file_size: 24444165
duration: 0:56:08
use: [podcast_episodes]
---
This week, Oliver discusses Domain-Driven Design with PHP UK speaker, Rob Allen.

Some files were not shown because too many files have changed in this diff Show more