![The basic pdfpc speaker view.](/sites/default/files/2021-04/pdfpc%20-%20presenter%20%28Upgrading%20to%20Drupal%209%29_004.png)
Welcome to {{ title }}
+No front page content has been created yet.
+Follow the User Guide to start building your site.
+diff --git a/app/config/sculpin_kernel.yml b/app/config/sculpin_kernel.yml
index 54c021a8..7b459e5c 100644
--- a/app/config/sculpin_kernel.yml
+++ b/app/config/sculpin_kernel.yml
@@ -6,6 +6,6 @@ sculpin_content_types:
podcast_episodes:
permalink: /podcast/:basename/
posts:
- enabled: false
+ permalink: /blog/:basename/
talks:
permalink: /talks/:basename/
diff --git a/source/_includes/figure.html.twig b/source/_includes/figure.html.twig
new file mode 100644
index 00000000..c8de55c1
--- /dev/null
+++ b/source/_includes/figure.html.twig
@@ -0,0 +1,8 @@
+
+ {% if caption %}
+
+ {{ content|raw }}
+
++ +10 years ago today, I started working for [Horse & Country TV](https://horseandcountry.tv) in what was my full-time Drupal development role. + +I'd been learning and working with Drupal for a couple of years prior to this, working on some personal and freelance projects, but when I was looking to move back to this area of Wales, this job on my doorstep was ideal. + +Initially starting as the sole Developer before another started a few months later, I remember being very excited to see and learn how this site has been built. Some of the main things that I remember working on was re-developing the Events section and adding paid events with [Ubercart](https://www.drupal.org/project/ubercart), and expanding my module development knowledge by adding a custom block that programmatically showed the current and next programme on the channel. + +As well as working with Drupal itself, it was a great opportunity to get more hands-on experience with Linux servers and to learn new tools such as [Git](https://git-scm.com) for version control. + +I also remember being asked to contribute to a public issue on Drupal.org as part of the interview process to demonstrate my debugging abilities. I decided to look at [this Drupal 6 issue](https://www.drupal.org/node/753898), and posted a comment with some updated code that I then forwarded on, and then uploaded a patch to the issue queue. This is still one of my favourite approaches for interviews, and one that I've used myself since when interviewing people for roles that use open source technologies. I much prefer this to working on internal, company specific coding tests, as it gives the interviewee some real world experience and exposure to the project itself and its community, rather than just how to _use_ it. + +Posting on a Drupal core issue and submitting patches was a bit scary at the time, but I think paved the way for me later contributing to core and other Drupal and open source projects. In fact, I was a Contribution Day mentor at DrupalCon Los Angeles in 2015 and helped someone get _their_ first commit to core when [a fix was committed to Drupal 8](https://git.drupalcode.org/project/drupal/commit/9cdd22c). + +After this role, I've worked for various agencies working primarily with Drupal and PHP, as well as for the [Drupal Association](https://www.drupal.org/assocation) itself. Whilst in recent years I've also started working with other frameworks like Symfony and Vue.js, Drupal and PHP has always been my core specialism. + +I've been very excited by the developments in both PHP and Drupal in recent versions, and I'm looking forward to the next 10 years working with them. + +Thank you Horse & Country for giving me the chance to start on my full-time Drupal journey! diff --git a/source/_posts/2014.md b/source/_posts/2014.md new file mode 100644 index 00000000..0ecae67b --- /dev/null +++ b/source/_posts/2014.md @@ -0,0 +1,84 @@ +--- +title: "2014" +date: 2015-03-20 +excerpt: A look back at 2014. +tags: + - drupal-association + - drupalcamp-london + - personal +tweets: true +--- + +A lot happened in 2014. Here are some of the main things that I'd like to +highlight. + +## Joined the Drupal Association + +This was the main thing for me this year, in May I left +[Precedent](http://precedent.com) and joined the +[Drupal Association](https://assoc.drupal.org). I work on the Engineering team, +focused mainly on [Drupal.org](https://www.drupal.org) but I've also done some +theming work on the DrupalCon [Amsterdam](http://amsterdam2014.drupal.org) and +[Latin America](http://latinamerica2015.drupal.org) websites, and some +pre-launch work on [Drupal Jobs](https://jobs.drupal.org). + +Some of the tasks that I've worked on so far are: + +- Fixing remaining issues from the Drupal.org Drupal 7 upgrade. +- Improving pages for + [Supporting Partners](https://www.drupal.org/supporters/partners), + [Technology Supporters](https://www.drupal.org/supporters/technology) and + [Hosting Partners](https://www.drupal.org/supporters/hosting). These + previously were manually updated pages using HTML tables, which are now + dynamic pages built with [Views](https://www.drupal.org/project/views) using + organisation nodes. +- Configuring human-readable paths for user profiles using + [Pathauto](https://www.drupal.org/project/pathauto). Only a small change, but + made a big difference to end-users. +- Migration of user data from profile values to fields, and various user profile + improvements. This was great because now we can do things like reference + mentors by their username and display their picture on your profile, as well + as show lists of peope listing a user as their mentor. This, I think, adds a + more personal element to Drupal.org because we can see the actual people and + not just a list of names on a page. + +I've started keeping a list of tasks that I've been involved with on my +[Work](/work/) page, and will be adding more things as I work on them. + +### Portland + +I was able to travel to Portland, Oregon twice last year to meet with the rest +of the Association staff. Both times I met new people and it was great to spend +some work and social time with everyone, and it was great to have everyone +together as a team. + +## My First DrupalCamp + +In February, I attended [DrupalCamp London](http://2014.drupalcamplondon.co.uk). +This was my first time attending a Camp, and I managed to attend some great +sessions as well as meet people who I'd never previously met in person. I was +also a volunteer and speaker, where I talked about +[Git Flow](/blog/what-git-flow/) - a workflow for managing your Git projects. + +{% include 'tweet' with { + content: '10 years ago today, I started my first full-time Web Developer job, working for @HorseAndCountry on their (at the time) #Drupal 6 website.
— Oliver Davies (@opdavies) July 19, 2020
Great presentation by @opdavies on git flow at #dclondon very well prepared and presented. pic.twitter.com/tDINp2Nsbn
— Greg Franklin (@gfranklin) March 2, 2014' +} %} + +I was also able to do a little bit of sprinting whilst I was there, reviewing +other people's modules and patches. + +Attending this and [DrupalCon Prague](https://prague2013.drupal.org) in 2013 +have really opened my eyes to the face-to-face side of the Drupal community, and +I plan on attending a lot more Camps and Cons in the future. + +## DrupalCon Amsterdam + +I was also able to travel to Holland and attend +[DrupalCon Amsterdam](https://amsterdam2014.drupal.org) along with other members +of Association staff. + +## DrupalCamp Bristol + +In October, we started planning for +[DrupalCamp Bristol](http://www.drupalcampbristol.co.uk). I'm one of the +founding Committee members, diff --git a/source/_posts/accessible-bristol-site.md b/source/_posts/accessible-bristol-site.md new file mode 100644 index 00000000..4c0d7891 --- /dev/null +++ b/source/_posts/accessible-bristol-site.md @@ -0,0 +1,30 @@ +--- +title: Accessible Bristol site launched +date: 2012-11-15 +excerpt: + I'm happy to report that the Accessible Bristol was launched this week, on + Drupal 7. +tags: + - accessibility + - accessible-bristol + - nomensa +--- + +I'm happy to announce that the +[Accessible Bristol](http://www.accessiblebristol.org.uk) website was launched +this week, on Drupal 7. The site has been developed over the past few months, +and uses the [User Relationships](http://drupal.org/project/user_relationships) +and [Privatemsg](http://drupal.org/project/privatemsg) modules to provide a +community-based platform where people with an interest in accessibility can +register and network with each other. + +The site has been developed over the past few months, and uses the +[User Relationships](http://drupal.org/project/user_relationships) and +[Privatemsg](http://drupal.org/project/privatemsg) modules to provide a +community-based platform where people with an interest in accessibility can +register and network with each other. + +The group is hosting a launch event on the 28th November at the Council House, +College Green, Bristol. Interested? More information is available at ++Hacking away on the new @phpsw website with @DaveLiddament and @kasiazien. pic.twitter.com/kmfjdQSOUq
— Oliver Davies (@opdavies) February 26, 2018
Pretty cool being back in the centre of Cardiff. pic.twitter.com/kh7Oi2tPDD
— Oliver Davies (@opdavies) July 31, 2018', +} %} + +## Speakers + +[Rob Allen][2] was the main speaker, who gave an interesting talk and a brave +live demo on serverless PHP and OpenWhisk. I always enjoy watching Rob speak, +which I’ve done a number of times at different events, and it was great to be +able to chat for a while after the meetup too. + +{% include 'tweet' with { + class: 'my-6', + data_cards: true, + content: 'Great to see @akrabat speaking about serverless PHP at the first @phpSouthWales meetup. #php #phpc #cardiff pic.twitter.com/Q9YaQ6O1fB
— Oliver Davies (@opdavies) July 31, 2018', +} %} + +We also had a couple of lightning talks, starting with [Ismael Velasco][3] +giving an introduction to progressive web applications (PWAs). I can see some +potential uses for this on my current work project, and I look forward to seeing +the full talk soon). + +I gave an updated version of my [Tailwind CSS lightning talk][4], and enjoyed +being able to show some examples of new sites using Tailwind such as [Laravel +Nova][5], [Spatie][6]’s new website and PHP South Wales itself! + +{% include 'tweet' with { + class: 'my-6', + data_cards: true, + content: 'Lightning talk time, first @IsmaelVelasco talking about #PWA 😎🎉 pic.twitter.com/KrJGZlIp7V
— PHP South Wales (@phpSouthWales) July 31, 2018', +} %} + +## Conclusion + +It’s great to have a meetup in Cardiff again, and having thought about organsing +something myself previously, I’m glad to see someone step forward to do so. This +shows that there's still a strong PHP community in Cardiff and South Wales, and +hopefully this will be the first meetup of many. I’ll look forward to seeing the +local community grow! + +Thanks again to Steve and Amy for organising, Eagle Labs for hosting, the +sponsors, and Rob and Ismael for speaking. + +It would be great to see even more people at the next one. If you’re interested, +take a look at the [group’s website][0], [meetup.com group][7] and [Twitter +profile][8]. Alternatively, get in touch with myself or one of the organisers +for more information. + +**Croeso ac iechyd da PHP South Wales!** + +[0]: https://www.phpsouthwales.uk +[1]: https://labs.uk.barclays/locations/cardiff-en +[2]: https://twitter.com/akrabat +[3]: https://twitter.com/IsmaelVelasco +[4]: /talks/taking-flight-with-tailwind-css +[5]: https://nova.laravel.com +[6]: https://spatie.be +[7]: https://www.meetup.com/PHP-South-Wales +[8]: https://twitter.com/phpsouthwales +[9]: https://phpsw.uk diff --git a/source/_posts/debugging-drupal-commerce-illuminate-collections.md b/source/_posts/debugging-drupal-commerce-illuminate-collections.md new file mode 100644 index 00000000..6d94c045 --- /dev/null +++ b/source/_posts/debugging-drupal-commerce-illuminate-collections.md @@ -0,0 +1,140 @@ +--- +title: Debugging Drupal Commerce Promotions and Adjustments using Illuminate Collections (Drupal 8) +date: 2018-10-24 +excerpt: Using Laravel’s Illuminate Collections to debug an issue with a Drupal Commerce promotion. +tags: + - drupal + - drupal-8 + - drupal-commerce + - drupal-planet + - illuminate-collections + - laravel-collections + - php +promoted: true +--- + +Today I found another instance where I decided to use [Illuminate +Collections][0] within my Drupal 8 code; whilst I was debugging an issue where a +[Drupal Commerce][1] promotion was incorrectly being applied to an order. + +No adjustments were showing in the Drupal UI for that order, so after some +initial investigation and finding that `$order->getAdjustments()` was empty, I +determined that I would need to get the adjustments from each order item within +the order. + +If the order were an array, this is how it would be structured in this +situation: + +```php +$order = [ + 'id' => 1, + 'items' => [ + [ + 'id' => 1, + 'adjustments' => [ + ['name' => 'Adjustment 1'], + ['name' => 'Adjustment 2'], + ['name' => 'Adjustment 3'], + ] + ], + [ + 'id' => 2, + 'adjustments' => [ + ['name' => 'Adjustment 4'], + ] + ], + [ + 'id' => 3, + 'adjustments' => [ + ['name' => 'Adjustment 5'], + ['name' => 'Adjustment 6'], + ] + ], + ], +]; +``` + +## Getting the order items + +I started by using `$order->getItems()` to load the order’s items, converted +them into a Collection, and used the Collection’s `pipe()` method and the +`dump()` function provided by the [Devel module][2] to output the order items. + +```php +collect($order->getItems()) + ->pipe(function (Collection $collection) { + dump($collection); + }); +``` + +## Get the order item adjustments + +Now we have a Collection of order items, for each item we need to get it’s +adjustments. We can do this with `map()`, then call `getAdjustments()` on the +order item. + +This would return a Collection of arrays, with each array containing it’s own +adjustments, so we can use `flatten()` to collapse all the adjustments into one +single-dimensional array. + +```php +collect($order->getItems()) + ->map(function (OrderItem $order_item) { + return $order_item->getAdjustments(); + }) + ->flatten(1); +``` + +There are a couple of refactors that we can do here though: + +- Use `flatMap()` to combine the `flatten()` and `map()` methods. +- Use [higher order messages][3] to delegate straight to the `getAdjustments()` + method on the order, rather than having to create a closure and call the + method within it. + +```php +collect($order->getItems()) + ->flatMap->getAdjustments(); +``` + +## Filtering + +In this scenario, each order item had three adjustments - the correct promotion, +the incorrect one and the standard VAT addition. I wasn’t concerned about the +VAT adjustment for debugging, so I used `filter()` to remove it based on the +result of the adjustment’s `getSourceId()` method. + +```php +collect($order->getItems()) + ->flatMap->getAdjustments() + ->filter(function (Adjustment $adjustment) { + return $adjustment->getSourceId() != 'vat'; + }); +``` + +## Conclusion + +Now I have just the relevant adjustments, I want to be able to load each one to +load it and check it’s conditions. To do this, I need just the source IDs. + +Again, I can use a higher order message to directly call `getSourceId()` on the +adjustment and return it’s value to `map()`. + +```php +collect($order->getItems()) + ->flatMap->getAdjustments() + ->filter(function (Adjustment $adjustment) { + return $adjustment->getSourceId() != 'vat'; + }) + ->map->getSourceId(); +``` + +This returns a Collection containing just the relevant promotion IDs being +applied to the order that I can use for debugging. + +Now just to find out why the incorrect promotion was applying! + +[0]: https://laravel.com/docs/collections +[1]: https://drupalcommerce.org +[2]: https://www.drupal.org/project/devel +[3]: https://laravel-news.com/higher-order-messaging diff --git a/source/_posts/debugging-php-docker-xdebug-neovim-dap.md b/source/_posts/debugging-php-docker-xdebug-neovim-dap.md new file mode 100755 index 00000000..a6a0fe3b --- /dev/null +++ b/source/_posts/debugging-php-docker-xdebug-neovim-dap.md @@ -0,0 +1,102 @@ +--- +title: Debugging PHP in Docker with Xdebug, Neovim and DAP +date: ~ +tags: + - docker + - neovim + - dap + - xdebug + - php + - drupal +draft: true +--- + +I've been a full-time Neovim user for a year at the time of writing this post and whilst I was a semi-regular Xdebug user, it's something that I've managed to work around and have mostly resorted to `var_dump()`, `dump()`, or `dd()` instead for debugging. + +This week though, whilst working on some particularly tricky PHP code, I decided to spend some time and get Xdebug working and be able to use a step debugger within Neovim. + +https://gist.githubusercontent.com/opdavies/688a3c8917893bf34a3da32ff69c1837/raw/112e16634930d312cd04c525de42a198c8a32bb9/dap.lua + +## Installing Xdebug + +Installing Xdebug itself within Docker was straight forward. I was able to add two lines to my existing `RUN` command - `pecl install xdebug` to install the extension and `docker-php-ext-enable xdebug` to enable it. + +Now when I run `php -v` inside my container, I can see that it mentions Xdebug. + +## Configuring Xdebug + +https://www.youtube.com/watch?v=ZIGdBSD6zvU + +``` +xdebug.mode=develop,debug +xdebug.client_host=host.docker.internal +xdebug.discover_client_host=0 +xdebug.output_dir=/tmp/xdebug +xdebug.log=/tmp/xdebug/xdebug-example.log +xdebug.start_with_request=yes +``` +## Installing DAP plugins + +I use [Packer](https://github.com/wbthomason/packer.nvim) for managing my Neovim plugins so I needed to install some additional ones to add the DAP (debug adapter protocol) functionality. + +```lua +use "mfussenegger/nvim-dap" +use "rcarriga/nvim-dap-ui" +use "theHamsta/nvim-dap-virtual-text" +use "nvim-telescope/telescope-dap.nvim" +``` + +## Installing DAP dependencies + +[https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#PHP](https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#PHP) + +There's also a prerequisite for install the `vscode-php-debug` adapter. + +I configure my laptop with Ansible, so I added a new `debugger` role that is responsible for cloning this repository and installing its contents: + +[https://github.com/opdavies/dotfiles/blob/7681c535269049556736f1f857c8c9fd800857a3/roles/debugger/tasks/php.yaml](https://github.com/opdavies/dotfiles/blob/7681c535269049556736f1f857c8c9fd800857a3/roles/debugger/tasks/php.yaml) + +## Configuring DAP for Xdebug + +```lua +dap.adapters.php = { + type = "executable", + command = "node", + args = { os.getenv("HOME") .. "/build/vscode-php-debug/out/phpDebug.js" } +} + +dap.configurations.php = { + { + type = "php", + request = "launch", + name = "Listen for Xdebug", + port = 9003, + pathMappings = { + ["/var/www/html"] = "${workspaceFolder}" + } + } +} +``` + +I first needed to configure the adapter to use `vscode-php-debug` and then add a DAP configuration. + +The default port for the step debugger is now 9003 rather than 9000 so I changed this from the default, and as I'm working with PHP inside a container, I also added a path mapping so that my code could be found. + +## Testing the connection + +> [Step Debug] Creating socket for 'host.docker.internal:9003', getaddrinfo: Invalid argument. + +```yaml +services: + php: + volumes: + - "/tmp/xdebug:/tmp/xdebug" + extra_hosts: + - "host.docker.internal:host-gateway" +``` + +--- + +keymaps: + +https://github.com/opdavies/docker-drupal-example diff --git a/source/_posts/decorating-entity-metadata-wrapper-add-refactor-methods.md b/source/_posts/decorating-entity-metadata-wrapper-add-refactor-methods.md new file mode 100644 index 00000000..2ef5cd52 --- /dev/null +++ b/source/_posts/decorating-entity-metadata-wrapper-add-refactor-methods.md @@ -0,0 +1,141 @@ +--- +title: Decorating an Entity Metadata Wrapper to add and refactor methods +excerpt: How to use the Decorator design pattern with Drupal 7's EntityMetadataWrapper to extend it, and add and refactor custom methods. +tags: + - drupal + - drupal-7 + - drupal-planet + - php +date: 2021-02-24 +--- + +Following [yesterday's Entity Metadata Wrapper blog post](/blog/cleanly-retrieving-user-profile-data-using-entity-metadata-wrapper) and as I continued to work on this task, I noticed some duplication and found that I was repeating several of the same chaining steps in different methods in the same file. For example: + +```php +public function getFirstName(): string { + return $this + ->get('profile_user_basic') // Get the pupil's profile. + ->get('field_first_name') + ->value(); +} + +private function getTeacherFirstName(): string { + $this + ->get('profile_student') // Get the pupil's profile. + ->get('field_class') // Get the pupil's class. + ->get('field_teacher') // Get the class' teacher. + ->get('profile_user_basic') // Get the teacher's profile. + ->get('field_first_name') + ->value(); +} +``` + +In both cases, the last three lines are the same, where the same profile type is loaded, and the value is loaded from a field. + +I wanted to find a way to remove this duplication whilst also making the code more readable. Ideally, this would mean adding a method like `getFirstNameFromBasicProfile()` that would group the last three steps. + +## Extending the EntityDrupalWrapper + +I've done this before, where I've created a custom wrapper class with its own methods and extends `EntityDrupalWrapper`. This is how that might look: + +```php +final class PupilWrapper extends \EntityDrupalWrapper { + + public function __construct(\stdClass $data, $info = []) { + parent::__construct('user', $data, $info); + } + + public function getFirstName(): string { + return $this->getFirstNameFromBasicProfile(); + } + + public function getTeacherFirstName(): string { + return $this + ->get('profile_student') + ->get('field_class') + ->get('field_teacher') + ->getFirstNameFromBasicProfile(); + } + + private function getFirstNameFromBasicProfile(): string { + return $this + ->get('profile_user_basic') + ->get('field_first_name') + ->value(); + } + +} +``` + +Whilst this has worked in previous situations, this time I had this error: + +> Error: Call to undefined method EntityDrupalWrapper::getFirstNameFromBasicProfile() in Drupal\my_module\EntityWrapper\PupilWrapper->getTeacherFirstName + +This is because the `get()` method is returning an instance of `EntityStructureWrapper` (another class that extends `EntityDrupalWrapper`) which means that `getFirstNameFromBasicProfile()` is not accessible though it's in the same file. + +I tried overridding the `get()` method but wasn't able to get this to work. + +## Decorating the EntityDrupalWrapper + +Another option that I tried was to follow the Decorator design pattern, and add a new class that takes an `EntityDrupalWrapper` as an argument as uses it internally but doesn't extend it. Here's an example: + +```php +final class PupilWrapper { + + private $accountWrapper; + + public function __construct(\EntityMetadataWrapper $accountWrapper) { + $this->accountWrapper = $accountWrapper; + } + + public function getFirstName(): string { + return $this->getFirstNameFromBasicProfile(); + } + + public function getTeacherFirstName(): string { + return $this + ->get('profile_student') + ->get('field_class') + ->get('field_teacher') + ->getFirstNameFromBasicProfile(); + } + + private function getFirstNameFromBasicProfile(): string { + return $this + ->get('profile_user_basic') + ->get('field_first_name') + ->value(); + } + +} +``` + +In this case, the constructor argument is an instance of `EntityMetadataWrapper` so that it could be either an `EntityDrupalWrapper` or `EntityStructureWrapper`. + +### Re-adding required wrapper methods + +As the `get()` method is missing, this would cause an error: + +> Error: Call to undefined method Drupal\my_module\EntityWrapper\PupilWrapper::get() in Drupal\my_module\EntityWrapper\PupilWrapper->getFirstName() + +However, we can re-add it, have it get the value from `accountWrapper` and return another instance of `PupilWrapper` so that `getFirstNameFromBasicProfile()` will be available. + +```php +public function get(string $property): self { + return new self($this->accountWrapper->get($property)); +} +``` + +The `value()` method is also required, but this can delegate to the decorated wrapper: + +> Error: Call to undefined method Drupal\my_module\EntityWrapper\PupilWrapper::value() in Drupal\my_module\EntityWrapper\PupilWrapper->getFirstName() + +```php +public function value(): string { + return $this->accountWrapper->value(); +} +``` + +## Conclusion + +This was the first time that I tried extending Drupal 7's entity metadata wrappers in this way, but it worked well, removes the duplication and cleans up the code further. diff --git a/source/_posts/display-custom-menu-drupal-7-theme-template-file.md b/source/_posts/display-custom-menu-drupal-7-theme-template-file.md new file mode 100644 index 00000000..092916fd --- /dev/null +++ b/source/_posts/display-custom-menu-drupal-7-theme-template-file.md @@ -0,0 +1,27 @@ +--- +title: Display a Custom Menu in a Drupal 7 Theme Template File +date: 2012-08-18 +excerpt: The code needed to display a menu in a Drupal 7 template file. +tags: + - aria + - drupal + - drupal-7 + - drupal-planet + - php +--- + +For reference, this is the code needed to display a menu in a Drupal 7 template +file, including the navigation ARIA role. + +```php +$menu_name = 'menu-footer-menu'; +$menu_id = 'footer-menu'; +print theme('links', array( + 'links' => menu_navigation_links($menu_name), + 'attributes' => array( + 'id' => $menu_id, + 'role' => 'navigation', + 'class'=> array('links', 'inline') + ) +)); +``` diff --git a/source/_posts/display-git-branch-or-tag-names-your-bash-prompt.md b/source/_posts/display-git-branch-or-tag-names-your-bash-prompt.md new file mode 100644 index 00000000..0403bdfa --- /dev/null +++ b/source/_posts/display-git-branch-or-tag-names-your-bash-prompt.md @@ -0,0 +1,67 @@ +--- +title: Display Git Branch or Tag Names in your Bash Prompt +date: 2013-04-27 +excerpt: Whilst watching Drupalize.me's recent Introduction to Git series, I thought it was useful the way that the current Git branch or tag name was displayed in the bash prompt. Here's how to do it. +tags: + - drupal + - drupal-planet + - git + - terminal +--- + +Whilst watching [Drupalize.me](http://drupalize.me 'Drupalize.me')'s recent +[Introduction to Git series](http://drupalize.me/series/introduction-git-series 'Introduction to Git on Drupalize.me'), +I thought it was useful the way that the current Git branch or tag name was +displayed in the bash prompt. + +Here's how to do it. + +For example (with some slight modifications): + +```bash +oliver@oliver-mbp:~/Development/drupal(master) $ +oliver@oliver-mbp:~/Development/a11y_checklist(7.x-1.0) $ +``` + +Here's how to do it. + +To begin with, create a new file to contain the functions, + +```bash +vim ~/.bash/git-prompt +``` + +Paste the following code into the file, and save it. + +```bash +parse_git_branch () { + git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/' +} + +parse_git_tag () { + git describe --tags 2> /dev/null +} + +parse_git_branch_or_tag() { + local OUT="$(parse_git_branch)" + if [ "$OUT" == " ((no branch))" ]; then + OUT="($(parse_git_tag))"; + fi + echo $OUT +} +``` + +Edit your `.bashrc` or `.bash_profile` file to override the PS1 value. + +```bash +vim ~/.bashrc +``` + +Add the following code at the bottom of the file, and save it. + +```bash +source ~/.bash/git-prompt +PS1="\u@\h:\w\$(parse_git_branch_or_tag) $ " +``` + +Restart your Terminal or type `source ~/.bashrc` to see your changes. diff --git a/source/_posts/display-number-facebook-fans-php.md b/source/_posts/display-number-facebook-fans-php.md new file mode 100644 index 00000000..fcf28c28 --- /dev/null +++ b/source/_posts/display-number-facebook-fans-php.md @@ -0,0 +1,30 @@ +--- +title: Display the Number of Facebook fans in PHP +date: 2011-03-15 +excerpt: How to use PHP to display the number of fans of a Facebook page. +tags: + - php +--- + +Replace the \$page_id value with your Page ID number (unless you want to show +the number of fans for this site).You can find your Page ID by logging into your +Facebook account, going to 'Adverts and Pages', clicking 'Edit page', and +looking at the URL. + +For example, mine is +DrupalCamp Bristol is returning next month for a one-day, single-track conference, and we have just finished announcing the accepted sessions and speakers. It includes a mixture of new and returning speakers, presenting sessions including **Drupal in a microservice architecture**, **Automate to manage repetitive tasks with Ansible** and **Doing good with Drupal**.
+ +Find out more about all of our sessions and speakers on [the DrupalCamp Bristol +website][website], as well as view the schedule for the day. + +Also, at the time of writing, [early bird tickets are still available][tickets] +for a few more days! + +In the meantime, the videos from the 2017 Camp are on [our YouTube +channel][youtube], including the opening keynote from [Emma Karayiannis][emma]: + +{% include 'youtube-video' with { id: honnav4YlAA } %} + +[emma]: https://twitter.com/embobmaria +[tickets]: https://2019.drupalcampbristol.co.uk/tickets +[website]: https://2019.drupalcampbristol.co.uk +[youtube]: https://opdavi.es/dcbristol17-videos diff --git a/source/_posts/drupalcamp-bristol-early-bird-tickets-sessions-sponsors.md b/source/_posts/drupalcamp-bristol-early-bird-tickets-sessions-sponsors.md new file mode 100644 index 00000000..a3710daf --- /dev/null +++ b/source/_posts/drupalcamp-bristol-early-bird-tickets-sessions-sponsors.md @@ -0,0 +1,68 @@ +--- +title: DrupalCamp Bristol 2017 - Early Bird Tickets, Call for Sessions, Sponsors +date: 2017-05-15 +excerpt: In less than two months time, DrupalCamp Bristol will be back for our third year. +tags: + - drupal + - drupal-planet + - drupalcamp + - drupalcamp-bristol +meta: + image: + url: /assets/image/blog/drupalcamp-bristol-17-logo.jpg + height: 228 + width: 448 + type: image/jpg +--- + +![DrupalCamp Bristol 2017 logo](/images/blog/drupalcamp-bristol-17-logo.jpg)
+ +In less than two months time, [DrupalCamp Bristol][0] will be back for our third +year! (July seems to come around quicker each year). This is this year’s +schedule and venues: + +- 30th June - CXO (Business) day - [Watershed][1] +- 1st July - Developer conference - [University of Bristol, School of + Chemistry][2] +- 2nd July - Contribution sprints - Venue TBC + +Today we announced [Emma Karayiannis][3] as our Saturday keynote speaker, and +we’ll be announcing some of the other speakers later this week. + +Not submitted your session yet? The [session submissions][12] are open until May +31st. We’re looking for talks not only on Drupal, but other related topics such +as PHP, Symfony, server administration/DevOps, project management, case studies, +being human etc. If you want to submit but want to ask something beforehand, +please [send us an email][4] or ping us on [Twitter][5]. + +Not spoken at a DrupalCamp before? No problem. We’re looking for both new and +experienced speakers, and have both long (45 minutes) and short (20 minutes) +talk slots available. + +Not bought your tickets yet? [Early bird tickets][10] for the CXO and conference +days are still available! The sprint day tickets are free but limited, so do +register for a ticket to claim your place. + +We still have [sponsorships opportunities][6] available (big thanks to +[Microserve][7], [Deeson][8] and [Proctors][9]) who have already signed up), but +be quick if you want to be included in our brochure so that we can get you added +before our print deadline! Without our sponsors, putting on this event each year +would not be possible. + +Any other questions? Take a look at [our website][0] or get in touch via +[Twitter][5] or [email][11]. + +[0]: https://2017.drupalcampbristol.co.uk +[1]: http://www.watershed.co.uk +[2]: http://www.bris.ac.uk/chemistry +[3]: http://emmakarayiannis.com +[4]: mailto:speakers@drupalcampbristol.co.uk +[5]: https://twitter.com/DrupalCampBris +[6]: https://2017.drupalcampbristol.co.uk/sponsorship +[7]: https://microserve.io +[8]: https://www.deeson.co.uk +[9]: http://www.proctors.co.uk +[10]: + https://www.eventbrite.co.uk/e/drupalcamp-bristol-2017-tickets-33574193316#ticket +[11]: mailto:info@drupalcampbristol.co.uk +[12]: https://2017.drupalcampbristol.co.uk/#block-dcb2017-page-title diff --git a/source/_posts/drupalcamp-london-2014.md b/source/_posts/drupalcamp-london-2014.md new file mode 100644 index 00000000..a40cd49e --- /dev/null +++ b/source/_posts/drupalcamp-london-2014.md @@ -0,0 +1,24 @@ +--- +title: DrupalCamp London 2014 +date: 2014-02-09 +excerpt: It's all booked, I'm going to be attending DrupalCamp London. +tags: + - drupal + - drupalcamp-london + - git + - git-flow +--- + +It's all booked, I'm going to be attending +[DrupalCamp London](http://2014.drupalcamplondon.co.uk) this year, my first +DrupalCamp! + +I'm going as a volunteer, so I'm going to be helping with the registrations on +the Saturday morning and for another couple hours elsewhere over the weekend. +I've also offered to help organise and oversee some code sprints, although I'm +definitely wanting to do some sprinting of my own and attend a few sessions. + +I'm looking forward to meeting some new people as well as catching up with some +people that I met at [DrupalCon Prague](http://prague2013.drupal.org). + +If you're also coming, see you there! diff --git a/source/_posts/drupalcamp-london-2019-tickets.md b/source/_posts/drupalcamp-london-2019-tickets.md new file mode 100644 index 00000000..dcd538ab --- /dev/null +++ b/source/_posts/drupalcamp-london-2019-tickets.md @@ -0,0 +1,39 @@ +--- +title: DrupalCamp London 2019 - Tickets Available and Call for Sessions +date: 2018-11-20 +excerpt: DrupalCamp London early-bird tickets are now available, and their call for sessions is open. +tags: + - conferences + - drupal + - drupalcamp + - drupalcamp-london +has_tweets: true +--- + +It was announced this week that [early-bird tickets are now available][0] for +[DrupalCamp London 2019][1], as well as their [call for sessions being open][2]. + +{% include 'tweet' with { + content: 'The time is finally here. You can now purchase your tickets. Early Bird finishes on 2nd January 2019 - https://t.co/aG6jstmWzv #Drupal
— DrupalCamp London (@DrupalCampLDN) November 19, 2018 +', +} %} + +I’ve attended, given talks and volunteered previously, would definitely +recommend others doing so, and I plan on attending and submitting again myself +for 2019. If there’s something in particular that you’d like to see me give a +talk on, let me know - I’d be happy to hear any suggestions. Alternatively, if +you’d like to submit and would like some help writing an abstract or want some +feedback on a talk idea, please get in touch. + +_Note: I am not an organiser of DrupalCamp London, nor am I involved with the +session selection process._ + +Hopefully there will be no [#uksnow][3] this year! + +DrupalCamp London is the 1-3 March 2019. Early bird tickets are available until +2 January 2019, and the call for sessions is open until 21 January. + +[0]: https://twitter.com/DrupalCampLDN/status/1064584179113971712 +[1]: https://drupalcamp.london +[2]: https://drupalcamp.london/get-involved/submit-a-session +[3]: /articles/tweets-drupalcamp-london diff --git a/source/_posts/drupalcamp-london-testing-workshop.mdx b/source/_posts/drupalcamp-london-testing-workshop.mdx new file mode 100644 index 00000000..d43ea9ef --- /dev/null +++ b/source/_posts/drupalcamp-london-testing-workshop.mdx @@ -0,0 +1,29 @@ +--- +title: Testing Workshop at DrupalCamp London 2020 +excerpt: This year, I’m teaching a workshop at DrupalCamp London. +tags: + - drupal + - drupalcamp + - testing +date: 2020-02-05 +lead_image: + url: /images/blog/testing-workshop-drupalcamp-london/lead.jpg +--- + +diff --git a/source/_posts/going-drupalcon.md b/source/_posts/going-drupalcon.md new file mode 100644 index 00000000..18ce7b23 --- /dev/null +++ b/source/_posts/going-drupalcon.md @@ -0,0 +1,16 @@ +--- +title: Going to DrupalCon +date: 2013-07-26 +excerpt: Precedent are sending myself and two of our other Drupal Developers to Drupalcon Prague. +tags: + - drupalcon + - precedent +--- + +[Precedent](http://www.precedent.co.uk) are sending myself and two of our other +Drupal Developers to [Drupalcon Prague](http://prague2013.drupal.org). + +Having wanted to attend the last few Drupalcons (London, especially) but not +being able to, I'm definitely looking forward to this one. + +See you there! diff --git a/source/_posts/going-full-vim.md b/source/_posts/going-full-vim.md new file mode 100644 index 00000000..453e7051 --- /dev/null +++ b/source/_posts/going-full-vim.md @@ -0,0 +1,50 @@ +--- +title: Going "Full Vim" for my development work +excerpt: I've recently been using Neovim for all of my coding, as well as for my blog posts and slide decks. +tags: + - vim +date: 2021-07-08 +--- + +For the past few months, I've gone "full Vim" ([Neovim][], to be exact) when writing any code - including anything for work or freelance projects, this blog post, and any presentation slide decks. + +I've been a long-time casual Vim user, enabling Vi or Vim mode within other editors, including Sublime Text, PhpStorm, and VS Code, and using Vim to write Git commit messages or to edit single files before closing it again. I remember searching how to add Vim features like relative line numbers in other editors, and trying things that would work within Vim but not when using in a plugin or extension. + +I've seen people and companies like [Lorna Jane Mitchell][], Suz Hinton ([noopkat][]), and [Thoughtbot][] using Vim in their presentations and videos for a long time but I haven't tried to switch before myself. + +Inspired by them and others including [Robin Malfait][], [TheAltF4Stream][], [Codico][], [Michael Dyrynda][], [ThePrimeagen][] with their recent live streams, videos, podcasts, and courses, I decided to give it a try. + +## Plugins + +You can see the [full list of plugins on GitHub](https://github.com/opdavies/dotfiles/blob/main/.config/nvim/plugins.vim), but here are some of the main ones that I've been using so far: + +- [fzf](https://github.com/junegunn/fzf.vim) - a fuzzy-finder to easily locate and open files. +- [CoC](https://github.com/neoclide/coc.nvim) and [Intelephense](https://intelephense.com) - adds auto-completion and code snippet support, including refactorings such as renaming symbols. +- [NERDTree](https://github.com/preservim/nerdtree) - a tree explorer, though I usually use the fuzzy finder so this isn't used that often. +- [Git gutter](https://github.com/airblade/vim-gitgutter) - displays Git diff information in the gutter of the current file. +- [Blamer](https://github.com/APZelos/blamer.nvim) - inspired by the GitLens plugin for VS Code, displays `git blame` information for the current line. +- [Nord](https://github.com/arcticicestudio/nord-vim), [jellybeans](https://github.com/nanotech/jellybeans.vim), and [ayu](https://github.com/ayu-theme/ayu-vim) - different themes that I'm trying and switching between. + +## Configuration + +If you'd like to see my full Neovim configuration, see the `.config/nvim` directory and the `init.vim` file in my [Dotfiles repository on GitHub](https://github.com/opdavies/dotfiles/tree/main/.config/nvim). + +## Conclusion + +I'm enjoying my first few months of using Vim full-time, and so far, I haven't looked back. I''ve had no issues using it in a Windows/WSL 2 environment either, which was great. + +I have a [cheat sheet on GitHub Gists](https://gist.github.com/opdavies/f944261b54f70b43f2297cab6779cf59) where I note the current things that I'm trying to learn and commit to memory. + +As I use it and learn more, I'm sure that I'll be posting more Vim-related content here too. + +Have any Vim/Neovim suggestions, tips, or tricks? Let me know on Twitter. + +[codico]: https://www.twitch.tv/codico +[lorna jane mitchell]: https://lornajane.net +[michael dyrynda]: https://dyrynda.com.au +[neovim]: https://neovim.io +[noopkat]: https://www.twitch.tv/noopkat +[robin malfait]: https://twitter.com/malfaitrobin +[thealtf4stream]: https://www.twitch.tv/thealtf4stream +[theprimeagen]: https://twitter.com/theprimeagen +[thoughtbot]: https://thoughtbot.com diff --git a/source/_posts/how-add-date-popup-calendar-custom-form.md b/source/_posts/how-add-date-popup-calendar-custom-form.md new file mode 100644 index 00000000..807b6b54 --- /dev/null +++ b/source/_posts/how-add-date-popup-calendar-custom-form.md @@ -0,0 +1,44 @@ +--- +title: How to add a date popup calendar onto a custom form +date: 2012-05-23 +excerpt: How to use a date popup calendar within your custom module. +tags: + - calendar + - date + - drupal + - drupal-7 + - drupal-planet + - form-api + - forms +--- + +How to use a date popup calendar within your custom module. + +First, I need to download the +[Date](http://drupal.org/project/date 'Date module on Drupal.org') module, and +make my module dependent on date_popup by adding the following line into my +module's .info file. + +```ini +dependencies[] = date_popup +``` + +Within my form builder function: + +```php +$form['date'] = array( + '#title' => t('Arrival date'), + + // Provided by the date_popup module + '#type' => 'date_popup', + + // Uses the PHP date() format - http://php.net/manual/en/function.date.php + '#date_format' => 'j F Y', + + // Limits the year range to the next two upcoming years + '#date_year_range' => '0:+2', + + // Default value must be in 'Y-m-d' format. + '#default_value' => date('Y-m-d', time()), +); +``` diff --git a/source/_posts/how-create-apply-patches.md b/source/_posts/how-create-apply-patches.md new file mode 100644 index 00000000..ab1e67a8 --- /dev/null +++ b/source/_posts/how-create-apply-patches.md @@ -0,0 +1,50 @@ +--- +title: How to Create and Apply Patches +date: 2010-10-10 +excerpt: How to create and apply patches, ready for the Drupal.org issue queues. +tags: + - drupal-6 + - drupal-planet + - modules + - patches +--- + +Earlier this year, I posted a solution to +[an issue](http://drupal.org/node/753898) on the Drupal.org issue queue. +Originally, I just posted the code back onto the issue, but have now created a +patch that can easily be applied to any Drupal 6 installation. Here is a +run-through of the process of creating and applying a patch. In this case, I +made changes to the `user_pass_validate()` function that's found within +`modules/user/user.pages.inc`. + +To begin with, a download a fresh copy of Drupal 6.19 and created a copy of the +original user.pages.inc file. Within the duplicate file, I made the same changes +to the function that I did in earlier code, and saved the changes. Now, within +my Terminal, I can navigate to Drupal's root directory and create the patch. + +```bash +diff -rup modules/user/user.pages.inc modules/user/user.pages2.inc > /Users/oliver/Desktop/different_messages_for_blocked_users.patch +``` + +This command compares the differences between the two files, and creates the +specified patch file. + +To apply the patch to my Drupal installation, I go back to Terminal and run the +following code: + +```bash +patch -p0 < /Users/oliver/Desktop/different_messages_for_blocked_users.patch +``` + +If, for some reason, I need to reverse the patch, I can run this code: + +```bash +patch -p0 -R < /Users/oliver/Desktop/different_messages_for_blocked_users.patch +``` + +And that's it! + +There is also a Git patch creation workflow, which is described at +If you're using GitHub Actions to run tests for your PHP projects and want colours in the output, append `--colors=always` to your phpunit command. pic.twitter.com/0AVwxCP4Bv
— Oliver Davies (@opdavies) May 13, 2020
![](/images/blog/drupal-meetups-twitterbot.png)
+ +The [Drupal Meetups Twitterbot][0] is a small project that I worked on a few +months ago, but hadn't got around to promoting yet. It’s intention is to provide +[one Twitter account][1] where people can get the up to date news from various +Drupal meetups. + +It works by having a whitelist of [Twitter accounts and hashtags][2] to search +for, uses [Codebird][3] to query the Twitter API and retweets any matching +tweets on a scheduled basis. + +If you would like your meetup group to be added to the list of searched +accounts, please [open an issue][4] on the GitHub repo. + +[0]: https://github.com/opdavies/drupal-meetups-twitterbot +[1]: https://twitter.com/drupal_meetups +[2]: + https://github.com/opdavies/drupal-meetups-twitterbot/blob/master/bootstrap/config.php +[3]: https://www.jublo.net/projects/codebird/php +[4]: https://github.com/opdavies/drupal-meetups-twitterbot/issues/new diff --git a/source/_posts/leaving-nomensa-joining-precedent.md b/source/_posts/leaving-nomensa-joining-precedent.md new file mode 100644 index 00000000..a5b3c45b --- /dev/null +++ b/source/_posts/leaving-nomensa-joining-precedent.md @@ -0,0 +1,32 @@ +--- +title: Leaving Nomensa, Joining Precedent +date: 2013-04-20 +excerpt: Yesterday was my last day working at Nomensa. Next week, I'll be starting as a Senior Developer at Precedent. +tags: + - nomensa + - personal + - precedent +--- + +Yesterday was my last day working at +[Nomensa](http://www.nomensa.com 'Nomensa'). Next week, I'll be starting as a +Senior Developer at [Precedent](http://www.precedent.co.uk 'Precedent'). + +The last 14 months that I've been working at Nomensa have been absolutely +fantastic, and had allowed me to work on some great projects for great clients - +mainly [unionlearn](http://www.unionlearn.org 'unionlearn') and +[Digital Theatre Plus](http://www.digitaltheatreplus.com 'Digital Theatre Plus'). +I've learned so much about accessibility and web standards, and have pretty much +changed my whole approach to front-end development to accommodate best +practices. I've also been involved with the Drupal Accessibility group since +starting at Nomensa, and have written several accessibility-focused Drupal +modules, including the +[Nomensa Accessible Media Player](http://drupal.org/project/nomensa_amp 'The Nomensa Accessible Media Player Drupal module') +module and the +[Accessibility Checklist](http://drupal.org/project/a11y_checklist 'The accessibility checklist for Drupal'). +I'll definitely be continuing my interest in accessibility, championing best +practices, and incorporating it into my future work wherever possible. + +With that all said, I'm really looking forward to starting my new role at +Precedent, tackling some new challenges, and I'm sure that it'll be as great a +place to work as Nomensa was. diff --git a/source/_posts/live-blogging-symfonylive-london-2019.md b/source/_posts/live-blogging-symfonylive-london-2019.md new file mode 100644 index 00000000..1cdc3a9a --- /dev/null +++ b/source/_posts/live-blogging-symfonylive-london-2019.md @@ -0,0 +1,713 @@ +--- +title: Live Blogging From SymfonyLive London 2019 +date: 2019-09-13 +tags: + - conference + - php + - symfony + - symfonylive +--- + +Inspired by [Matt Stauffer](https://twitter.com/stauffermatt)'s +[live blogging of the keynote](https://mattstauffer.com/blog/introducing-laravel-vapor) +at Laracon US, I’m going to do the same for the sessions that I’m attending at +[SymfonyLive London 2019](https://london2019.live.symfony.com)... + +## Keynote (Back to the basics) + +**Embrace the Linux philosophy** + +- How we grow the Symfony ecosystem. Built abstracts. +- HttpFoundation, HttpKernel +- Moved to infrastructure +- A few abstractions on top of PHP. Improved versions of PHP functions (`dump` + and `var_dump`) +- Started a add higher level abstractions (e.g. Mailer), built on the lower + ones. +- Recently worked on PHPUnit assertions. Mailer in Symony 4.4. Can test if an + email is sent or queued + +**Building flexible high-level abstractions on top of low-level ones** + +### What's next? + +- Mailer announced in London last year. New component. +- System emails? e.g. new customer, new invoice. +- Symfony Mailer = Built-in responsive, flexible, and generic system emails + - Twig with TwigExtraBundle + - Twig `inky-extra` package (Twig 1.12+) + - Zurb Foundation for Emails CSS stylesheet + - Twig `cssinliner-extra` package (Twig 1.12+) + - Optimised Twig layouts +- `SystemEmail` class extends templated email +- Can set importance, +- Customisable +- Always trying to keep flexible, so things can be overidden and customised + +### Sending SMS messages + +- new `Texter` and `SmsMessage` class for sending SMS messages +- Same abstraction as emails, but for SMS messages +- Based on HttpClient + Symfony Messenger and third-party providers (Twilio and + Nexmo) `twilio://` and `nemxo://` +- Can set via transport `$sms->setTransport('nexmo')` +- Extend the `SystemEmail` and do what you want +- Failover + +### Sending Messages + +- Create `ChatMessage` +- Telegram and Slack +- `$message->setTransport('telegram')`, `$bus->dispatch($message)` +- Send to Slack **and** Telegram +- `SlackOptions` and `TelegramOptions` for adding emojis etc +- Common transport layer `TransportInterface`, `MessageInterface` +- Failover - e.g. if Twilio is down, send to Telegram + +### New component - SymfonyNotifier + +- Channels - email, SMS, chat +- Transport, slack, telegram, twilio +- Create a notification, arguments are message and transports (array) +- Receiver +- Customise notifications, `InvoiceNotification` extends `Notification`. + `getChannels` + - Override default rendering + - `ChatNotificationInterface` - `asChatMessage()` +- Semantic configuration + - `composer req twilio-notifier telegram-notifier` +- Channels + - Mailer + - Chatter + - Texter + - Browser + - Pusher (iOS, Android, Desktop native notifications) + - Database (web notification centre) + - **A unified way to notify Users via a unified Transport layer** +- Each integration is only 40 lines of code + +### What about a SystemNotification? + +- Autoconfigured channels +- `new SystemNotification`, `Notifier::getSystemReceivers` +- Importance, automatically configures channels +- Different channels based on importance +- `ExceptionNotification` - get email with stack trace attached + +Notifier + +- send messages via a unified api +- send to one or many receivers +- Default configu or custom one + +### How can we leverage this new infrastructure? + +- `Monolog NotifierHandler` - triggered on `Error` level logs +- Uses notified channel configuration +- Converts Error level logs to importance levels +- Configurablelike other Notifications +- 40 lines of code +- Failed Messages Listener - 10 lines of glue code + +- **Experimental component in 5.0** +- Can't in in 4.4 as it's a LTS version +- First time an experimental component is added +- Stable in 5.1 + +## Queues, busses and the Messenger component (Tobias Nyholm) + +- Stack is top and buttom - Last-in, first-out +- Queue is back and front - last in, first out + +### 2013 + +- Using Symfony, used 40 or 50 bundles in a project - too much information! +- Used to copy and paste, duplicate a lot of code +- Testing your controllers - controllers as services? +- Controllers are 'comfortable' +- Tried adding `CurrentUserProvider` service to core, should be passed as an + argument. Cannot test. +- 'Having Symfony all over the place wasn't the best thing' - when to framework + (Matthias Noback) + - Hexagonal architecture + - Keep your kernel away from infrastructure. Let the framework handle the + infrastructure. +- Controller -> Command -> Command Bus -> `CommandHandler` + +#### What did we win? + +- Can leverage Middleware with a command bus +- Queues as a service (RabbitMQ) +- Work queue - one producer, multiple consumers +- Queues should be durable - messages are also stored on disk, consumers should + acknowledge a message once a message is handled +- Publish/subscribe + - Producer -> Fanout/direct with routing (multiple queues) -> multiple + consumers +- Topics - wildcards + +### 2016 + +- New intern. Understand everything, 'just PHP'. Plain PHP application, not + 'scary Symfony' + +### Symfony Messenger + +- `composer req symfony/messager` - best MessageBus implementation +- Message -> Message bus -> Message handler +- Message is a plain PHP class +- Handler is a normal PHP class which is invokable +- `messenger:message_hander` tag in config +- Autowire with `MessageHandlerInterface` +- What if it takes 20 seconds to send a message? Use asynchronous. +- Transports as middleware (needs sender, receiver, configurable with DSN, + encode/decode). `MESSENGER_DSN` added to `.env` +- Start consumer with `bin/console messager:consume-messages`. Time limit with + `--time-limit 300` +- PHP Enqueue - production ready, battle-tested messaging solution for PHP + +### Issues + +- Transformers, takes an object and transforms into an array - + `FooTransformer implements TransformerInterface`. +- Don't break other apps by changing the payload. + +#### Multiple buses + +- Command bus, query bus, event bus +- Separate actions from reactions + +#### Envelope + +- Stamps for metadata - has the item been on the queue already? + +#### Failures + +- Requeue, different queue or same queue after a period of time +- Failed queue 1 every minute, failed queue 2 every hour - temporary glitches or + a bug? + +#### Creating entities + +- What if two users registered at the same tiem? Use uuids rather than IDs. +- Symfony validation - can be used on messages, not just forms. + +- Cache everything + + - Option 1: HTTP request -> Thin app (gets responses from Redis) -> POST to + queue. Every GET request would warm cache + - Option 2: HTTP request -> Thin app -> return 200 response -> pass to workers + +- Tip: put Command and CommandHandlers in the same directory + +## HttpClient (Nicolas Grekas) + +- new symfony component, released in may +- Httpclient contracts, separate package that contains interfaces + - Symfony + - PHP-FIG + - Httplug +- `HttpClient::create()`. `$client->get()` +- JSON decoded with error handling +- Used on symfony.com website (#1391). Replaces Guzzle `Client` for + `HttpClientInterface` +- Object is stateless, Guzzle is not. Doesn't handle cookies, cookies are state +- Remove boilerplate - use `toArray()` +- Options as third argument - array of headers, similar to Guzzle + +### What can we do with the Response? + +- `getStatusCode(): int` +- `getHeaders(): array` +- `getContent(): string` +- `toArray(): array` +- `cancel(): void` +- `getInfo(): array` - metadata +- Everything is lazy! +- 80% of use-cases covered + +### What about PSR-18? + +- Decorator/adapter to change to PSR compatible +- Same for Httplug + +### What about the remaining 20%? + +- Options are part of the abstraction, not the implementation + +#### Some of the options + +- `timeout` - control inactivity periods +- `proxy` - get through a http proxy +- `on_progress` - display a progress bar / build a scoped client +- `base_url` - resolve relative URLS / build a scoped client +- `resolve` - protect webhooks against calls to internal endpoints +- `max_redirects` - disable or limit redirects + +- Robust and failsafe by default + +- Streamable uploads - `$mimeParts->toIterable()`. +- donwload a file + + ```php + foreach ($client->stream($response) as $chunk) { + // ... + } + ``` + +* Responses are lazy, requests are concurrent +* Asychronus requests. Reading in network order + +``` +foreach ($client->stream($responses) as $response => $chunk) { + if ($chunk->isLast()) { + // a $response completed + } else { + // a $response's got network activity or timeout + } +} +``` + +- 379 request completed in 0.4s! +- `Stream` has second argument, max number of seconds to wait before yielding a + timeout chunk +- `ResponseInterface::getInfo()` - get response headers, redirect count and URL, + start time, HTTP method and code, user data and URL + - `getInfo('debug')` - displays debug information + +### The components + +- `NativeHttpClient` and `CurlHttpClient` + - both provide + - 100% contracts + - secure directs + - extended (time) info + - transparent HTTP compression and (de)chunking + - automatic HTTP proxy configuration via env vars + +#### `NativeHttpClient` + +- is most portable, works for everyone +- based on HTTP stream wrapper with fixed redirect logic +- blocking until response headers arrive + +#### `CurlHttpClient` + +- Requires ext-curl with fixed redirection logic +- Multiplexing response headers and bodies +- Leverages HTTP/2 and PUSH when available +- Keeps connections open also between synchronous requests, no DNS resolution so + things are faster + +#### Decorators + +- ScopingHttpClient - auto-configure options based on request URL +- MockHttpClient - for testing, doesn't make actual HTTP requests +- CachingHttpClient - adds caching on a HTTP request +- Psr18Client +- HttplugClient +- TraceableHttpClient + +### Combining + +#### FrameworkBundle/Autowiring + +```yaml +framework: + http_client: + max_host_connections: 4 + deault_options: + # .... + scoped_client: + # ... +``` + +#### HttpBrowser + +- HttpClient + DomCrawler + CssSelector + HttpKernel + BrowserKit +- RIP Goutte! + +### Coming in 4.4... + +- `max_duration` +- `buffer` based on a callable +- `$chunk->isInformational()` +- `$response->toStream()` +- Async-compatible extensibility, when decoration is not enough + +`composer req symfony/http-client` + +## Symfony Checker is coming (Valentine Boineau) + +- Static analysis tool for Symfony + - Does a method exist? + - Is it deprecated? +- insight.symfony.com +- @symfonyinsight +- Released soon + +### Differences + +- Specialise in Symfony - can see more relevant things +- Different interface to other services + +## Feeling unfulfilled by SPA promises? Go back to Twig (Dan Blows) + +A way on the front-end JS, CSS, images at the beginning of the request, sends a +HTTP request (XHR/AJAX) to the back-end + +### Why SPAs? + +- A way on the front-end JS, CSS, images at the beginning of the request, sends + a HTTP request (XHR/AJAX) to the back-end +- no full page refresh +- Supposed to be much quicker +- 'Right tool for the job' - JS on the front-end, PHP on the back-end +- Division of responsibility == faster development +- Reusable API - Api -> Mobile App and SPA - easy to add another consumer +- Easier to debug? + +### Why not SPAs? + +- Lots of HTTP requests (400 to load the initial page on one project) == slow + front end +- Blurred responsibilities == tightly coupled teams +- harder to debug, bugs fall between systems and teams. Huge gap between + front-end and back-end, passing responsibilites. +- You can fix these problems in SPAs, but is it worth it? + - Examples of good SPAs - Trello, Flickr + +### Using Twig as an alternative to an SPA? + +#### Faster UI - Try and figure out where the problem is. + +If you're trying to speed things up, find out where the problem is. + +- Browser tools +- Web Debug Toolbar +- Blackfire +- Optimise and monitor + +#### Speed up Twig + +- Speeding up Symfony +- ext/twig (PHP5 only, not PHP 7) +- Store compiled templates in Opcache, make sure it's enabled +- Render assets though the webserver (assetic not running all the time) + +#### Edge side includes + +- Component cached differently to the rest of the page +- Varnish/Nginx +- `render_esi` +- News block that caches frequently, rest of the page + +#### HTTP/2 with Weblink + +- slow finding CSS files to load - 'push' over CSS files, doesn't need to wait +- `preload()` - https://symfony.com/doc/current/web_link.html + +#### Live updating pages + +- Instantly update when sports results are updated, news articles are added +- Mercure - https://github.com/symfony/mercure +- LiveTwig - whole block or whole section, and live update `render_live` +- Turbolinks - replace whole body, keeps CSS and JS in memory. Merges new stuff + in. `helthe/turbolinks` +- ReactPHP - shares kernel between requests + +### Writing better code with Twig + +- Keep templates simple. Avoid spaghetti code, only about UI. HTML or small + amounts of Twig. +- Avoid delimeter chains + - Bad:`blog_post.authors.first.user_account.email_address` + - Good `{{ blog_post.authors_email_address }}` + - Less brittle, slow + +* Filters + - Use filters to be precise + - Custom filters + - Avoid chains. Can cause odd results. Create a new filter in PHP +* Functions + - Write your own functions + - Simpler templates + - Get data, can use boolean statements +* Components + - Break a page into components rather than one large page + - `include()` + - Use `only` to only pass that data. less tightenly coupled. + * `render` calls the whole of Symfony, boots Kernel, can be expensive and slow + * Loosely couple templates and controllers + - Keep responses simple + - What makes sense + - if you need extra data in the template, get it in the template + * View models + - Mixed results + - `BlogPostViewModel` + - Can result in boilerplate code + - Can be useful if the view model is different to the Entity + * DRY + - "Don't repeat yourself" + +- Faster development + - Separate UI tests from back-end tests. Different layers for different teams. + People don't need to run everything if they are only changing certain + things. + +* Help your front end + - Webpack - Encore + - Type hinting in functions and filters, easier to debug + - Logging + - Friendly exceptions - help front-end devs by returning meaningful, readbale + errors + - Web Debug Toolbar and Profiler, provide training for toolbar and profilers + - Twig-friendly development environment - Twig support in IDEs and text + editors + +SPAs are sometimes teh right solution. Why do they want to use it, can the same +benefits be added with Twig? + +3 most important points: + +- Profile, identidy, optimise, monitor +- Loosely couple templates to your app code +- Help your front ends - put your front end developers first +- You don't need to use a SPA for single pages, use JavaScript for that one + page. It doesn't need to be all or nothing. + +## BDD Your Symfony Application (Kamil Kokot) + +- Applying BDD to Sylius +- 2 years since release of Sylius (Symfony 2 alpha) +- The business part is more important than the code part + +### What is BDD? + +- Behaviour driven development. Combines TDD and DDD, into an agile methodology +- Encourages communication and creates shared understanding +- Living, executable documentation that non-programmers understand. Always + correct. +- Feature file + - Feature + - Scenario - example of the behaviour for this feature. Simple, atomic. (e.g. + I need a product in order to add it to a cart) + - In order to... + - Who gets the benefit? + +### BDD in practice + +- Feature: booking flight tickets +- Scenario: booking flight ticket for one person + - Given there are the following flights... + - When I visit '/flight/LTN-WAW' + - Then I should be on '/flight/LTN-WAW' + - Add I should see "Your flight has been booked." in "#result" +- In the BDD way - what is the business logic? What is the value for this + scenario? What is the reason 'why', and who benefits from this? + - We just need to know that there are 5 seats left on a flight + - Talk and communicate about how the feature is going to work - not just + developers + - BDD aids communication +- Questions we can ask + - Can we get a different outcome when the context changes? + - When there was only one seat available + - When there were no available seats + - Can we get the same outcome when the event changes? Can we change 'When' and + 'Then stays the same' + - When it is booked for an adult and a child + - When it is booked for an adult + - Does anything else happen that is not mentioned? + - Generate an invoice if a seat is booked + - a pilot would like to get a notification that a seat was booked. + * Figuring out the rules + - Adults are 15+ years old + - Children are 2-14 years old + - Infants and children can only travel with an adult + - We don't allow for overbooking + - Translating rules into examples + - Add a new scenario for each rule - e.g. don't allow over booking + - "And the flight should be no longer available..." + +### Behat + +- Used to automate and execute BDD tests, also SpecDDD +- maps steps to PHP code +- Given a context, when an event, then an outcome +- Domain Context, API context +- class implements `Context`, annotations for `@Given`, `@When`, `@Then`. allows + for arguments and regular expressions +- Suites: change what code is executed, and what scenarios are executed. context + and tags +- FriendsOfBehat SymfonyExtension - integrates Behat with Symfony + - Contexts registered as Symfony services - inject dependencies, service as a + context in Behat. Need to be 'public' for it to work + - Reduces boilerplate code. Supports autowiring. + - Zero configuration + +### Domain context + +- `Given` verb matches `@Given` annotation. Same for `When` and `Then`. +- Transformers, type hint name string, return Client instance + +### API context + +- inject `FlightBookingService` and `KernelBrowser` +- Use `$this->kernelBrowser->request()` +- Use `assert()` function wuthin `@Then` + +### Back to reality - how it's done with Sylius + +- Business part applies to all context. Start talking about what needs to be + done, start communicating +- Implement contexts for UI and API +- 12716 steps, 1175 scenarios, 8 min 8 sec, 2.4 scenarios /sec +- 12x faster than JS (17 min 48 sec, 0.19 scenario / sec) +- Treat test CI environment like production + + - Turn off debug settings, add caching + - Enable OPcache + +- Write features in a natural way +- Too many setup steps - merge steps. less visual debt. e.g. Create currency, + zone and locale when creating a store +- Avoid scenarios that are too detailed. You should specify only what's + important to this scenario. + +## Migrating to Symfony one route at a time (Steve Winter) + +- New client with an old application, built in an old version of another + framework with unusual dependency management, no tests, no version control and + deploying via FTP. Done over a ~3 month period. + +- Subscription based index of suppliers +- New requirements to implement by the client +- Our requirements: Needed a deployment process, make it testable, fix the build + chain +- Solution attempt 1: Migrate to a new version of the current framework + - Minor template and design changes were fine + - Modifiy features, add new dependencies. +- Solution attempt 2: Upgrade to the latest version - same outcome due to + multiple BC breaks (no semver), lots of manual steps +- Solution attempt 3: Symfony! + - Semver! Backwards compatibility promise + - Symfony app to run in parallel, Apache proxy rules and minor changes to the + legacy app, added data transfer mechanisms + - Anything new done in Symfony + - Installed on the same server with it's own vhost but not publicly accessible + - Deployed independently of legacy app + +### Apache proxy rules + +Proxy `/public` to symfony app + +### Legacy app + +- Shared cookie for single login between apps - user account details (name etc), + session details (login time) + +### Added functionality + +- Built in Symfony +- new proxy rules for new routes +- Add menu links to legacy app menu +- How do we show how many reminders are active? + - Symfony based API called from the front-end + +### Migrating routes + +- Rebuilt or extend in Symfony app +- Test and deploy, then update the apache config to add new proxy rules + +### A gotcha + +- Legacy app uses CSRF +- Needed to track the token, added to shared cookie and pass through to the + Symfony side + +### Storing data + +- Both apps using the same data with different credentials +- Some shared tables, some tables are specific to each app + +### Remaining challenges + +- User session management, still handled by legacy app +- Templating/CSS - two versions of everything + - Next step: move all CSS to Symfony + +### Summary + +- Add Symfony app, Apache proxy rules for routes +- User transfer mechanisms +- New functionality added in Symfony + +### Is this right for you? + +It depends. Fine for a 'modest' size. Use a real proxy for larger scale apps, +use different servers with database replication. + +## Closing Keynote: The fabulous World of Emojis and other Unicode symbols (Nicolas Grekas) + +- ASCII. Still used today. Map between the first 128 numbers to characters. OK + for UK and US. +- 256 numbers in Windows-1252 (character sets). Each country had their own set. +- It's legacy. 0.2% for Windows-1252. 88.8% for UTF-8 (Feb 2017) +- Unicode: 130k characters, 135 scripts (alphabets) +- Validation errors using native alphabet - e.g. invalid last name when + submitting a form +- 17 plans, each square is 255 code points +- Emojis are characters, not images +- Gliph is a visual representation of a character +- From code points to bytes + - UTF-8: 1,2,3 or 4 bytes + - UTF16: 2 or 4 bytes + - UTF-32: 4 bytes +- UTF-8 is compatible with ASCII +- Case sensitivity - 1k characters are concerned. One uppercase letter, two + lower case variants. Turkish exception (similar looking letters that are + different letters with different meanings). Full case folding. +- Collations - ordering is depends on the language. 'ch' in Spanish is a single + character. +- Single number in unicode to represent accents. Combining characters. +- Composed (NFC) and decomposed (NFD) forms - normalisation for comparison +- Grapheme clusters - multiple characters, but one letter as you write it + (separate characters for letters and accent) +- Emjois - combining characters. e.g. Combine face with colour. Different codes + and character names. Also applies to ligatures. A way to combine several + images together into one single visual representation. + +### unicode fundamentals + +- uppercase, lowercase, folding +- compositions, ligatures +- comparistions - normalisations and collations +- segmentation: characters, words, sentences and hyphens +- locales: cultural conventions, translitterations +- identifiers & security, confusables +- display: direction, width + +### unicode in practice + +- MySQL - `utf*_*`. `SET NAMES utf8mb4` for security and storing emojis. Cannot + store emojis with `utf8` + +### in php + +- `mb_*()` +- `iconv_*()` +- `preg_*()` +- `grapheme_*()` `normalizer_*()` +- `symfony/polyfill-*` - pure PHP implementation +- Made a component - **symfony/string** - + https://github.com/symfony/symfony/pull/33553 +- Object orientated api for strings. Immutable value objects +- `AbstractString` + - `GraphemeString` + - `Utf8String` + - `BinaryString` + +* AbstractString - Methods to serialize, get length, to binary or grapheme or + utf8 + - Methods for starts with, ends with, is empty, join, prepend, split, trim, + title etc diff --git a/source/_posts/looking-forward-to-drupalcamp-london.md b/source/_posts/looking-forward-to-drupalcamp-london.md new file mode 100644 index 00000000..ca95135b --- /dev/null +++ b/source/_posts/looking-forward-to-drupalcamp-london.md @@ -0,0 +1,66 @@ +--- +title: Looking forward to DrupalCamp London +date: 2018-02-27 +excerpt: This weekend is DrupalCamp London 2018. I’ll be there along with a number of my Microserve colleagues. +tags: + - drupal + - drupalcamp + - drupalcamp-london + - speaking +--- + +This weekend is [DrupalCamp London 2018][1]. I’ll be there along with a number +of my [Microserve][2] colleagues. + +I look forward to DrupalCamp London every year, partly because it was the first +DrupalCamp that I attended back in 2014. It was also the first DrupalCamp that I +[gave a talk][3] at, when I presented a session about Git Flow having given only +one user group talk before. + +I’ve presented sessions at every DrupalCamp London since (including two last +year), and I’m lucky enough to be [speaking again this year][4] due to one of +the originally announced speakers no longer being able to make it to the event. + +Here are some other sessions that I’m hoping to see (in no particular order): + +- Keynote by [Ryan Szrama][5] from [Commerce Guys][6] +- [Drupal 8 Services And Dependency Injection](https://drupalcamp.london/session/drupal-8-services-and-dependency-injection) + by Phil Norton +- [Growing developers with Drupal](https://drupalcamp.london/session/growing-developers-drupal) + by Fran Garcia-Linares (fjgarlin) +- [How to make it easier for newcomers to get involved in Drupal](https://drupalcamp.london/session/how-make-it-easier-newcomers-get-involved-drupal) + by heather +- [Let’s take the best route - Exploring Drupal 8 Routing System](https://drupalcamp.london/session/lets-take-best-route-exploring-drupal-8-routing-system) + by surbhi +- [New recipe of Decoupling: Drupal 8, Symfony and Slim Framework](https://drupalcamp.london/session/new-recipe-decoupling-drupal-8-symfony-and-slim-framework) + by Jyoti Singh +- [Plugin API by examples](https://drupalcamp.london/session/plugin-api-examples) + by Gabriele (gambry) +- [Value of mentorship in the community](https://drupalcamp.london/session/value-mentorship-community) + by Hussain Abbas (hussainweb) +- [Warden - Helping Drupal Agencies Sleep at Night](https://drupalcamp.london/session/warden-helping-drupal-agencies-sleep-night) + by Mike Davis + +Unfortunately there are some time slots where I’d like to see more than one of +the talks (including when I’m going to be speaking). This regularly happens at +conferences, but I’ll look forward to watching those on [YouTube][7] after the +event. + +I’m also looking forward to catching up with former colleagues, spending some +time in the "hallway track" and hopefully doing some sprinting too! + +## Finally + +For nostalgia, [here’s the blog post][0] that I wrote before I attended my first +DrupalCamp London. + +See everyone this weekend! + +[0]: {{site.url}}/blog/2014/02/09/drupalcamp-london-2014 +[1]: https://drupalcamp.london +[2]: {{site.companies.microserve.url}} +[3]: {{site.url}}/talks/git-flow +[4]: {{site.url}}/talks/deploying-drupal-fabric +[5]: http://ryanszrama.com +[6]: https://commerceguys.com +[7]: https://www.youtube.com/channel/UCsaB96zszIP4Y3czs-ndiIA diff --git a/source/_posts/mediacurrent-contrib-half-hour-is-back.md b/source/_posts/mediacurrent-contrib-half-hour-is-back.md new file mode 100644 index 00000000..0402a70f --- /dev/null +++ b/source/_posts/mediacurrent-contrib-half-hour-is-back.md @@ -0,0 +1,67 @@ +--- +title: Yay, the Mediacurrent Contrib Half Hour is Back! +date: 2018-03-02 +excerpt: Mediacurrent’s "contrib half hour sessions" are back. +tags: + - contribution + - drupal + - open-source +has_tweets: true +--- + +Back in November, [Mediacurrent introduced][1] the contrib half hour - a weekly +online meeting to provide guidance and assistance on contributing to Drupal and +Drupal projects. A range of topics were covered in the first few sessions, +including finding and testing bug fixes, Composer, Drush, and how to re-roll +patches. + +From Damien's [introductory blog post][2]: + +> Not sure what this whole "patch" thing is? Have a core change that you can't +> quite finish? Running into a problem with a contrib module, or a theme, or a +> 3rd party library, and not sure how to fix it? New to maintaining a module and +> unsure of what to do next? Wondering how to get your module through the +> security opt-in process? Is your project's issue queue getting you down? Join +> us every Thursday at noon EST for the Mediacurrent Contrib Half Hour where +> we'll be available to help solve contrib challenges. +> +> Each week we'll host a live meeting to give step-by-step guidance on some best +> practices for contributing to Drupal, and provide Q and A assistance for our +> favorite open source (OSS) content management system (CMS). The meetings will +> be lead by yours truly, Damien McKenna, a prolific contributor to the Drupal +> community, and my coworkers here at Mediacurrent. + +There is also an [updates blog post][3] that continues to show the latest +information, and the video recordings are [uploaded to YouTube][0] after the +session. Here is the first one from November: + + + +I enjoyed watching the first few videos, as I’m always interested in +contribution to Drupal and open-source and how to encourage it, but then no new +videos were uploaded for a while and I hoped that it hadn’t faded away. + +I’m glad to see today that it’s back and that all of the previous videos have +been uploaded and added to the [YouTube playlist][0], and that [on the update +post][3] there are scheduled topics for the rest of this month including +documentation and automated testing. + ++All of the @mediacurrent #ContribHalfHour videos have been uploaded to our Youtube channel: https://t.co/1sWZT5sRSN
— Damien McKenna (@DamienMcKenna) March 2, 2018
Note: I accidentally forgot to save the Feb 22nd video, sorry :-\
{{ page.excerpt }}
+ +## The Traditional Way + +The typical way of including test cases in Drupal 7 is to add one or more +classes within a `.test` file - e.g. `opdavies.test`. This would typically +include all of the different test cases for that module, and would be placed in +the root of the module’s directory alongside the `.info` and `.module` files. + +In order to load the files, each file would need to be declared within the +`.info` file for the module. + +There is a convention that if you have multiple tests for your project, these +can be split into different files and grouped within a `tests` directory. + +```ini +; Load a test file at the root of the module +files[] = opdavies.test + +; Load a test file from within a subdirectory +files[] = tests/foo.test +files[] = tests/bar.test +``` + +## Using the xautoload Module + +Whilst splitting tests into separate files makes things more organised, each +file needs to be loaded separately. This can be made simpler by using the +[Xautoload module][], which supports wildcards when declaring files. + +[xautoload module]: https://www.drupal.org/project/xautoload + +```ini +files[] = tests/**/*.test +``` + +This would load all of the `.test` files within the tests directory. + +## Using PSR-4 Autoloading + +Another option is to use PSR-4 (or PSR-0) autoloading. + +This should be a lot more familiar to those who have worked with Drupal 8, +Symfony etc, and means that each test case is in its own file which is cleaner, +files have the `.php` extension which is more standard, and the name of the file +matches the name of the test class for consistency. + +To do this, create a `src/Tests` (PSR-4) or `lib/Drupal/{module_name}/Tests` +(PSR-0) directory within your module, and then add or move your test cases +there. Add the appropriate namespace for your module, and ensure that +`DrupalWebTestCase` or `DrupalUnitTestCase` is also namespaced. + +```php +// src/Tests/Functional/OliverDaviesTest.php + +namespace Drupal\opdavies\Tests\Functional; + +class OliverDaviesTest extends \DrupalWebTestCase { + // ... +} +``` + +This also supports subdirectories, so you can group classes within `Functional` +and `Unit` directories if you like. + +If you want to see an real-world example, see the Drupal 7 branch of the +[Override Node Options module][override_node_options]. + +[override_node_options]: + https://git.drupalcode.org/project/override_node_options/tree/7.x-1.x + +### Digging into the simpletest_test_get_all function + +This is the code within `simpletest.module` that makes this work: + +```php +// simpletest_test_get_all() + +// ... + +$module_dir = DRUPAL_ROOT . '/' . dirname($filename); + +// Search both the 'lib/Drupal/mymodule' directory (for PSR-0 classes) +// and the 'src' directory (for PSR-4 classes). +foreach (array( + 'lib/Drupal/' . $name, + 'src', +) as $subdir) { + + // Build directory in which the test files would reside. + $tests_dir = $module_dir . '/' . $subdir . '/Tests'; + + // Scan it for test files if it exists. + if (is_dir($tests_dir)) { + $files = file_scan_directory($tests_dir, '/.*\\.php/'); + if (!empty($files)) { + foreach ($files as $file) { + + // Convert the file name into the namespaced class name. + $replacements = array( + '/' => '\\', + $module_dir . '/' => '', + 'lib/' => '', + 'src/' => 'Drupal\\' . $name . '\\', + '.php' => '', + ); + $classes[] = strtr($file->uri, $replacements); + } + } + } +} +``` + +It looks for a the tests directory (`src/Tests` or +`lib/Drupal/{module_name}/Tests`) within the module, and then finds any `.php` +files within it. It then converts the file name into the fully qualified +(namespaced) class name and loads it automatically. + +### Running the Tests + +You can still run the tests from within the Simpletest UI, or from the command +line using `run-tests.sh`. + +If you want to run a specific test case using the `--class` option, you will now +need to include the fully qualified name. + +``` +php scripts/run-tests.sh --class Drupal\\opdavies\\Tests\\Functional\\OliverDaviesTest +``` diff --git a/source/_posts/published-my-first-docker-images-docker-hub-adr-tools-sculpin-rst2pdf.md b/source/_posts/published-my-first-docker-images-docker-hub-adr-tools-sculpin-rst2pdf.md new file mode 100644 index 00000000..bf2bba18 --- /dev/null +++ b/source/_posts/published-my-first-docker-images-docker-hub-adr-tools-sculpin-rst2pdf.md @@ -0,0 +1,97 @@ +--- +title: Published my first Docker images on Docker Hub (ADR Tools, Sculpin, rst2pdf) +excerpt: I recently released my first images to the Docker Hub, for ADR Tools, the Sculpin site generator, and rst2pdf. +date: 2021-04-20 +tags: + - docker + - rst2pdf + - sculpin +--- + +I've used Docker for some time for local development, making use of container images from Docker Hub and creating my own project-specific images, but I hadn't pushed any to [my Docker Hub profile](https://hub.docker.com/u/opdavies) for anyone else to use - until now. + +I've pushed several images to Docker Hub recently: + +- One for using [ADR Tools](https://github.com/npryce/adr-tools) to work with architectural decision records. +- Two for generating and serving sites built with the [Sculpin static site generator](https://sculpin.io). +- One for working with [rst2pdf](https://rst2pdf.org) that I use for presentation slides, with another image coming for watching and automatically re-compiling the PDF. + +![](/images/blog/jackson-octocat.png)
+ +Earlier this week I moved this site from my personal Linode server to [GitHub +Pages][0]. + +This made sense as I already kept the source code in [on GitHub][1], the issue +was that GitHub Pages doesn’t know how to dynamically parse and generate a +Sculpin site like it does with some other static site generators. It can though +parse and serve HTML files, which is what Sculpin generates. It’s just a case of +how those files are added to GitHub. + +I’ve seen different implementations of this, mostly where the Sculpin code is on +one branch, and the generated HTML code is on a separate `gh-pages` or `master` +branch (depending on your repository name). I’m not fond of this approach as it +means automatically checking out and merging branches which can get messy, and +also it’s weird to look at a repo’s branches page and see one branch maybe tens +or hundreds of commits both ahead and behind the default branch. + +This has been made simpler and tidier now that we can use a `docs` directory +within the repository to serve content. + +I think you can start typing and it will filter?
— Oliver Davies (@opdavies) August 28, 2018', +} %} + +On the PhpStorm welcome screen that displays when you first open it, your recent +projects are displayed on the left-hand side of the screen, and are filterable. +That means that I can start typing a project name, e.g. `oli`, and I will only +see projects that start with that input. + +![The PhpStorm welcome screen with filters applied to the project list](/images/blog/quick-project-switching-phpstorm/welcome-screen.png){.with-border +.with-padding} + +That’s great when opening a project from scratch, but what about when we’re +already within a project and just want to be able to switch to another? + +{% include 'tweet' with { + content: 'You can also use 'Open recent' within the actions list, and then filter the list of projects. pic.twitter.com/k8G9iIQNP0
— Oliver Davies (@opdavies) August 28, 2018', +} %} + +There’s also a way to access this list once PhpStorm is open, by clicking 'Open +Recent' within the File menu. The issue here though is that this list is not +filterable. + +You can also access this list using the keyboard, though the 'Search everywhere' +or 'Find action' panes, and these are filterable. + +![Using the 'find action' pane to find 'Open Recent'](/images/blog/quick-project-switching-phpstorm/find-action.png){.with-border +.with-padding} + +Once the 'Open Recent' option is selected, you see the same project list as on +the welcome screen, which is filtered in the same way by starting to type +potential project names. + +![The filtered project list](/images/blog/quick-project-switching-phpstorm/open-recent.png){.with-border +.with-padding} + +## Adding a Keyboard Shortcut + +We can make this easier by adding a new keyboard shortcut. Within the Keymap +preferences, you can search for 'Open Recent' and right-click it to add a new +keyboard shortcut and define the key combination. + +![Finding the 'Open Recent' shortcut in the Keymap preferences](/images/blog/quick-project-switching-phpstorm/adding-keyboard-shortcut-1.png){.with-border +.with-padding} + +![Assigning a keyboard shortcut](/images/blog/quick-project-switching-phpstorm/adding-keyboard-shortcut-2.png){.with-border +.with-padding} + +This this shortcut added, you can now use it to instantly bring up your recent +projects list, filter it and switch project. diff --git a/source/_posts/quickest-way-install-sublime-text-2-ubuntu.md b/source/_posts/quickest-way-install-sublime-text-2-ubuntu.md new file mode 100644 index 00000000..96b67336 --- /dev/null +++ b/source/_posts/quickest-way-install-sublime-text-2-ubuntu.md @@ -0,0 +1,25 @@ +--- +title: The Quickest way to Install Sublime Text 2 in Ubuntu +date: 2013-03-02 +excerpt: After reading numerous blog posts about how to install Sublime Text 2 in Ubuntu, this is definitely the quickest way! +tags: + - linux + - sublime-text + - ubuntu +--- + +After reading numerous blog posts about how to install +[Sublime Text 2](http://www.sublimetext.com/2 'Sublime Text 2') in +[Ubuntu](http://www.ubuntu.com/2 'Ubuntu'), this is definitely the quickest way! + +Just paste the following lines into your Terminal: + +```bash +$ sudo add-apt-repository ppa:webupd8team/sublime-text-2 +$ sudo apt-get update +$ sudo apt-get install sublime-text +``` + +After running this, Sublime Text 2 has been installed within the +_/usr/lib/sublime-text-2_ directory and can be launched from the Dashboard, or +by typing `subl`, `sublime-text` or `sublime-text-2` into a Terminal window. diff --git a/source/_posts/quickly-apply-patches-using-git-curl-or-wget.md b/source/_posts/quickly-apply-patches-using-git-curl-or-wget.md new file mode 100644 index 00000000..4af01720 --- /dev/null +++ b/source/_posts/quickly-apply-patches-using-git-curl-or-wget.md @@ -0,0 +1,29 @@ +--- +title: Quickly Apply Patches Using Git and curl or wget +date: 2013-12-24 +excerpt: How to quickly download a patch file and apply it to a Git repository in one line. +tags: + - drupal-planet + - git +--- + +Testing a patch file is usually a two-step process. First you download the patch +file from the source, and then you run a separate command to apply it. + +You can save time and typing by running the two commands on one line: + +```bash +$ curl http://drupal.org/files/[patch-name].patch | git apply -v +``` + +Or, if you don't have curl installed, you can use wget: + +```bash +$ wget -q -O - http://drupal.org/files/[patch-name].patch | git apply -v +``` + +These commands need to be run within the root of your Git repository (i.e. where +the .git directory is). + +These snippets were taken from +[Applying Patches with Git](https://drupal.org/node/1399218) on Drupal.org. diff --git a/source/_posts/quickly-import-multiples-images-using-imagefieldimport-module.md b/source/_posts/quickly-import-multiples-images-using-imagefieldimport-module.md new file mode 100644 index 00000000..7bcab6b6 --- /dev/null +++ b/source/_posts/quickly-import-multiples-images-using-imagefieldimport-module.md @@ -0,0 +1,44 @@ +--- +title: Quickly Import Multiples Images Using the Imagefield_Import Module +date: 2010-05-29 +excerpt: How to use the Imagefield Import module. +tags: + - cck + - drupal + - drupal-6 + - drupal-planet + - imagefield + - imagefield-import + - photo-gallery +--- + +**Thanks to Bob at [Mustardseed Media](http://mustardseedmedia.com) for +[tweeting](http://twitter.com/mustardseedinc/status/14713096905) about this +module. It's undoubtedly saved me hours of work today alone!** + +I've recently started a personal project converting a website to Drupal. It's +currently a static HTML/CSS site which also uses the +[Coppermine Photo Gallery](http://coppermine-gallery.net). As part of building +the new website, I wanted to move all the photos from the existing site onto the +new one. However, with 1260 photos in 17 albums, this could have been a lengthy +process! + +I created a new Drupal-powered Gallery as described in +[this screencast](http://lullabot.com/articles/photo-galleries-views-attach) by +[Jeff Eaton](http://twitter.com/eaton) - using the CCK and Imagefield modules, +and re-created each of my existing Gallery nodes. Using the +[Imagefield_Import](http://drupal.org/project/imagefield_import) module, I was +then able to quickly import the photos into the new Galleries. + +I downloaded all the photos from the previous Gallery via FTP, and installed and +configured the Imagefield_Import module. + +I created an 'Import' folder, selected the target field and mode. In this case, +I want each image to be imported into its own Photo node. I moved the photos for +the first album into the Import folder, and loaded the 'Import Images' screen +(admin/content/imagefield_import). + +After clicking 'Import', a node is created for each photo, the image is +uploaded, and added to the selected Gallery. + +Just another 1248 photos to go... diff --git a/source/_posts/rebuilding-acquia-dashboard-with-vuejs-tailwind-css.md b/source/_posts/rebuilding-acquia-dashboard-with-vuejs-tailwind-css.md new file mode 100644 index 00000000..799d442e --- /dev/null +++ b/source/_posts/rebuilding-acquia-dashboard-with-vuejs-tailwind-css.md @@ -0,0 +1,163 @@ +--- +title: Rebuilding Acquia’s Dashboard with Vue.js and Tailwind CSS +excerpt: How I rebuilt Acquia’s dashboard using Vue.js and Tailwind CSS. +tags: + - drupal + - tailwind-css + - tweet + - vuejs +draft: true +date: ~ +promoted: true +--- + +After +[rebuilding Drupal’s Bartik theme](/blog/rebuilding-bartik-with-vuejs-tailwind-css), +I’ve now used [Vue.js][vue] and [Tailwind CSS][tailwind] to rebuild another +Drupal related UI - this time it’s [Acquia’s](https://www.acquia.com) web +hosting dashboard. Again, you can [view the site on Netlify][netlify] and [the +code on GitHub][github]. + +## Why? + +The same as the Bartik rebuild, this was a good opportunity for me to gain more +experience with new technologies - Vue in particular - and to provide another +demonstration of how Tailwind CSS can be used. + +Like the Bartik clone, this was originally going to be another single page +rebuild, however after completing the first page I decided to expand it to +include three pages which also gave me the opportunity to use +[Vue Router](https://router.vuejs.org) - something that I had not used +previously - and to organise a multi-page Vue application. + +## Configuring Vue Router + +`src/router/index.js`: + +```js +import Vue from 'vue'; +import Router from 'vue-router'; +import Applications from '@/views/Applications'; +import Environment from '@/views/Environment'; +import Environments from '@/views/Environments'; + +Vue.use(Router); + +export default new Router({ + routes: [ + { + path: '/', + name: 'applications', + component: Applications, + }, + { + path: '/:id/environments', + name: 'environments', + component: Environments, + props: true, + }, + { + path: '/:id/environments/:environmentName', + name: 'environment', + component: Environment, + props: true, + }, + ], +}); +``` + +## Passing in data + +`src/data.json` + +```json +{ + "applications": { + "1": { + "id": 1, + "name": "Rebuilding Acquia", + "machineName": "rebuildingacquia", + "type": "Drupal", + "level": "Enterprise", + "environments": { + "dev": { + "name": "Dev", + "url": "dev.rebuilding-acquia.com", + "label": "develop" + }, + "stage": { + "name": "Stage", + "url": "stg.rebuilding-acquia.com", + "label": "master" + }, + "prod": { + "name": "Prod", + "url": "rebuilding-acquia.com", + "label": "tags/2018-12-21" + } + }, + "tasks": [ + { + "text": "Commit: fdac923 Merge branch 'update-password-policy' refs/heads/master", + "user": "system", + "times": { + "display": "Dec 19, 2018 3:48:29 PM UTC +0000", + "started": "Dec 19, 2018 3:48:29 PM UTC +0000", + "completed": "Dec 19, 2018 3:48:29 PM UTC +0000" + }, + "loading": false, + "success": true + } + ] + } + } +} +``` + +## The Environments page + +This was the first page that I rebuilt - the Environments page for an +application that shows the information of the different configured environments. + +Vue Router is configured to show the + +{% include 'figure' with { + image: { + src: '/images/blog/rebuilding-acquia-vue-tailwind/3-environments.png', + alt: 'A screenshot of the rebuilt Environments page.', + }, + caption: 'The rebuilt Environments page for an application.', +} %} + +## The applications page + +{% include 'figure' with { + image: { + src: '/images/blog/rebuilding-acquia-vue-tailwind/1-applications-grid.png', + alt: 'The rebuild Applications page, with applications displayed in a grid.', + }, + caption: 'The rebuilt Applications page - grid view', +} %} + +{% include 'figure' with { + image: { + src: '/images/blog/rebuilding-acquia-vue-tailwind/2-applications-list.png', + alt: 'The rebuild Applications page, with applications displayed as a list.', + }, + caption: 'The rebuilt Applications page - list view', +} %} + +## An environment page + +{% include 'figure' with { + image: { + src: '/images/blog/rebuilding-acquia-vue-tailwind/4-environment.png', + alt: 'A screenshot of the rebuilt Environment page.', + }, + caption: 'The rebuilt page for an environment within an application.', +} %} + +[github]: https://github.com/opdavies/rebuilding-acquia +[netlify]: https://rebuilding-acquia.oliverdavies.uk +[tailwind]: https://tailwindcss.com +[vue]: https://vuejs.org diff --git a/source/_posts/rebuilding-bartik-drupals-default-theme-vuejs-tailwind-css-part-2.md b/source/_posts/rebuilding-bartik-drupals-default-theme-vuejs-tailwind-css-part-2.md new file mode 100644 index 00000000..0a100145 --- /dev/null +++ b/source/_posts/rebuilding-bartik-drupals-default-theme-vuejs-tailwind-css-part-2.md @@ -0,0 +1,313 @@ +--- +title: Rebuilding Bartik (Drupal’s Default Theme) with Vue.js and Tailwind CSS - part 2 +date: 2018-12-27 +excerpt: A follow-up to my original post on rebuilding Bartik with Tailwind and Vue.js. +tags: + - drupal + - tailwind-css + - tweet + - vuejs +has_tweets: true +--- + +In [the original post](/blog/rebuilding-bartik-with-vuejs-tailwind-css) I +detailed how I built [a clone of Drupal’s Bartik theme][netlify] with +[Vue.js][vuejs] and [Tailwind CSS][tailwind]. This follow-up post details some +updates that I’ve made to it since then. + +## Customising Tailwind’s colours + +During the first version of the page, my thoughts were to not edit the Tailwind +configuration, however I changed my mind on this whilst working on the +subsequent updates and did make some changes and customisations to the +`tailwind.js` file. + +By default, Tailwind includes a full colour palette including colours such as +yellows, oranges, reds that weren’t being used in this page so they were +removed. This makes the file more readable as well as reduces the number of +classes that Tailwind generates. + +Whist I was changing the colours, I also took the opportunity to tweak the +values of the remaining colours to more closely match Bartik’s original colours. + +I also added a `black-60` class which uses +[RGBA](https://css-tricks.com/the-power-of-rgba) to provide a semi-transparent +background. I used this when adding the +[skip to main content link](#adding-the-skip-to-main-content-link). + +```js +let colors = { + transparent: 'transparent', + + black: '#22292f', + 'grey-darkest': '#3d4852', + 'grey-dark': '#8795a1', + grey: '#b8c2cc', + 'grey-light': '#dae1e7', + 'grey-lighter': '#f0f0f0', + 'grey-lightest': '#F6F6F2', + white: '#ffffff', + + 'black-60': 'rgba(0, 0, 0, .6)', + + 'blue-dark': '#2779bd', + blue: '#3490dc', + 'blue-light': '#bcdefa', + + 'green-dark': '#325E1C', + green: '#77B159', + 'green-light': '#CDE2C2', + 'green-lighter': '#F3FAEE', +}; +``` + +## Adding default styling for links + +In the first version, every link was individually styled which resulted in a lot +of duplicate classes and a potential maintenance issue. + +I added a `style` section within `Welcome.vue`, and added some default styling +for links based on their location on the page - +[extracting some Tailwind components](https://tailwindcss.com/docs/extracting-components). + +My block contents.
+I built a clone of Bartik, #Drupal's default theme, with @vuejs and @tailwindcss. See the result at https://t.co/nPsTt2cawL, and the code at https://t.co/Dn8eysV4gf.
Blog post coming soon... pic.twitter.com/7BgqjmkCX0
No front page content has been created yet.
+Follow the User Guide to start building your site.
+**Get the default configuration**. This is done using `require('tailwindcss/defaultConfig')()`. Essentially this has the same contents as the current `tailwind.js` file, though now it’s owned and maintained within Tailwind itself, and not by the user.
+Also any new or updated values within the default configuration will be automatically available.
+This line is present but commented out in the current generated `tailwind.js` file.
+**Create the colors object.** This will by default override Tailwind’s default colours, however you can add `...defaultConfig.colors` to include them and then add or edit values as needed.
+In this example, I’m overridding the value used for the `black` colour classes to match the existing colour in the other CSS.
+**Return the main configuration object.** For sites with no overrides, this could just be `module.exports = defaultConfig` for a minimal configuration.
+To extend the defaults, add `...defaultConfig` at the beginning.
+**Assign our colours.** Use them for `colors`, `textColors`, `backgroundColors` and `borderColours`.
+**Add any plugins**. I use plugins on most projects, in this case I’m using [tailwindcss-interaction-variants](https://www.npmjs.com/package/tailwindcss-interaction-variants) and [tailwindcss-spaced-items](https://www.npmjs.com/package/tailwindcss-spaced-items). Usually the default `container` plugin would be here too.
+**Add or override modules.** Here I’m adding the `hocus` (hover and focus) variant provided by the interaction variants plugin to the text style classes.
+**Add or override options.** As this markup was originally from a Drupal website, I needed to override some of the options values. I’ve added the `tw-` prefix to avoid Tailwind classes from clashing with Drupal’s default markup, and set all Tailwind classes to use `!important` so that they override any existing styles.
+I’ve attended numerous DrupalCons since my first in Prague in 2013, as a delegate, as a [{{site.companies.drupal_association.name}}]({{site.companies.drupal_association.url}}) staff member, and also as a contribution sprint mentor. I’m excited to be attending DrupalCon Amsterdam again this year - but as my first time as a DrupalCon speaker.
+ +{% include 'tweet' with { + content: 'Super excited to be giving my first @DrupalConEur talk!#drupal #drupalcon #php #ansible https://t.co/5ZOPClUjvC pic.twitter.com/TWih82Ny0P
— Oliver Davies (@opdavies) July 16, 2019' +} %} + +The session that I’m going to be presenting is a twenty minute version of my +[Deploying PHP applications with Ansible, Ansible Vault and Ansistrano](/talks/deploying-php-ansible-ansistrano) +talk. + +{% include 'figure' with { + image: { + src: '/images/blog/speaking-drupalcon-amsterdam/drupalcon-schedule.jpg', + alt: 'My session on the DrupalCon Amsterdam schedule.', + }, + caption: 'My session on the DrupalCon Amsterdam schedule.', +} %} + +I’ve been working with Drupal since 2007 (or maybe 2008), and it was the subject +of my [first meetup talk](/talks/so-what-is-this-drupal-thing) in 2012. Since +then I’ve given 48 more talks +(including one workshop) at various user groups and conferences on a range of +development and systems administration topics. So it’s a nice conincedence that +this will be my fiftieth (50th) talk. + +Thanks also to my employer, +[{{site.companies.inviqa.name}}]({{site.companies.inviqa.url}}), who are giving +me the time and covering my costs to attend the conference. diff --git a/source/_posts/speaking-drupalcon-europe-2020.md b/source/_posts/speaking-drupalcon-europe-2020.md new file mode 100644 index 00000000..8be4cab7 --- /dev/null +++ b/source/_posts/speaking-drupalcon-europe-2020.md @@ -0,0 +1,17 @@ +--- +title: Speaking at DrupalCon Europe 2020 +excerpt: I'm excited to be speaking again at DrupalCon, this time online at DrupalCon Europe. +tags: + - drupal + - conferences + - speaking +date: 2020-07-30 +--- + +After giving my [Ansible and Ansistrano talk](/talks/deploying-php-ansible-ansistrano) in Amsterdam, I'm excited that another of my talks has been accepted for DrupalCon! + +This year, my [TDD - Test-Driven Drupal](/talks/tdd-test-driven-drupal) talk has been accepted for DrupalCon Europe, will is being held online from December 8-11th. + +I first gave this talk at DrupalCamp London 2017 and again a number of times over the last few years including at Drupal Developer Days in Lisbon and most recently for the North West (UK) Drupal user group in May. I've always had good feedback from it, and enjoy teaching others about testing and hopefully continue to inspire people to start writing tests themselves. + +This is definitely one of my favourite topics. I've enjoyed updating and improving this talk over the years, and I'm looking forward to giving it at DrupalCon this year. diff --git a/source/_posts/speaking-remotely-during-covid-19.md b/source/_posts/speaking-remotely-during-covid-19.md new file mode 100644 index 00000000..c72af93c --- /dev/null +++ b/source/_posts/speaking-remotely-during-covid-19.md @@ -0,0 +1,84 @@ +--- +title: Speaking remotely during COVID-19 +excerpt: I've been quite busy during this lockdown, giving talks remotely at conferences and user groups. +date: 2020-07-07 +tags: + - speaking +--- + +I've been quite busy during COVID-19 and various lockdowns, giving talks remotely at conferences and user groups. + +In mid-April, I send a tweet with an open offer to any user groups that needed a speaker, with some suggestions for talks that I'd given recently that I could present remotely. + ++ +As well as this, I also applied to some open calls for papers for remote conferences, such as [CMS Philly](https://cmsphilly.org "The CMS Philly conference") (formerly Drupaldelphia) that was taking place online this year. + +At the time of writing, these are the talks that I've given remotely or are already planned for future dates. I'll be updating this list going forward as new talks are added, as well as my [talks page](/talks "My upcoming and past talks"). + +If you'd like me to speak at your online conference or user group and be added to this list, please contact me and we can see if we can find a suitable date. + +## Test-Driven Drupal + +An overview of automated testing in Drupal, and a demo of building a new Drupal 8 (or 9) module using test driven development. + +- [NWDUG](http://nwdrupal.org.uk) - 11th May +- [BADCamp 2020](https://2020.badcamp.org/session/tdd-test-driven-drupal) - 16th October +- [DrupalCon Europe 2020](https://events.drupal.org/europe2020/sessions/tdd-test-driven-drupal) - 8th December + +## Deploying PHP with Ansible and Ansistrano + +How to use Ansible, Ansible Vault and Ansistrano to deploy PHP applications, using a Drupal 8 application for a demo. + +- [Drupal Edinburgh](https://www.meetup.com/Drupal-Edinburgh/events/267905594) - 11th March +- [CMS Philly](https://cmsphilly.org) - 30th April +- [Drupal Yorkshire](https://www.meetup.com/DrupalYorkshire/events/zwzsfpybchbcc) - 20th May +- [PHP London](https://www.meetup.com/phplondon/events/270930524) - 4th June +- [PHP North East](https://www.meetup.com/phpnortheast) - 16th June +- [PHP Sussex](https://www.meetup.com/PHP-Sussex) - 1st July +- [Midwest PHP 2021](https://midwestphp.org/talks/1q5XUF2tTdXXLYOoujMkpF/Deploying_PHP_applications_with_Ansible,_Ansible_Vault_and_Ansistrano) - 23rd April 2021 + +## Taking Flight with Tailwind CSS + +An introduction to utility-based CSS and how to use Tailwind CSS in PHP projects using tools such as Webpack Encore and Laravel Mix. + +- [CMS Philly](https://cmsphilly.org) - 30th April +- [PHP Hampshire](https://www.meetup.com/meetup-group-yzpbvTYv) - 8th July +- [Drupal Yorkshire](https://www.meetup.com/DrupalYorkshire/events/zwzsfpybclbbc) - 20th August +- [DigitalCamp Atlanta](https://www.drupalcampatlanta.com/2020/sessions/taking-flight-tailwind-css) - 11th September +- [Bristol JS](https://techtalks.io/events/f8e26038-2561-484e-8a74-7a1e3a0369b8) - 30th September +- [Drupal Virtual Cafe](https://groups.drupal.org/node/536142) (Drupal Kyiv) - 15th October +- [PHP Cambridge](https://www.meetup.com/phpcambridge/events/273686561) - 19th January 2021 +- [Nashville PHP](https://www.meetup.com/nashvillephp/events/kzkdwryccdbmb) - 9th February 2021 + +## Updating to Drupal 9 + +How to update your site to Drupal 9, and why it's much different to any major Drupal version upgrade before! + +- [Drupal NYC](https://ti.to/drupalnyc/meetup-2020-08-05) - 2nd September +- [Leeds PHP](https://www.meetup.com/leedsphp/events/272504993) - 23rd September +- [Midwest PHP 2021](https://midwestphp.org/talks/7C0m4I87vq72cDoXvsHFRp/Upgrading_your_site_to_Drupal_9) - 22nd April 2021 + +## Working with Workspace + +- [NWDUG](https://www.meetup.com/nwdrupal/events/272098270) - 11th August (lightning talk) +- [PHP South West](https://www.meetup.com/php-sw/events/272787346) - 9th September (lightning talk) +- PHP North West - 2nd February 2021 + +## Automated Testing and Test-Driven Development in Drupal 8 (workshop) + +- [DrupalCamp London](https://drupalcamp.london/training/Automated-Testing-and-Test-Driven-Development-in-Drupal-8) - 13th March (in-person, just before UK lockdown) +- [DrupalCamp NYC](https://2020.drupalcamp.nyc/training/automated-testing-and-test-driven-development-drupal-8) - 14th November + +## Building Slides and Presenting with rst2pdf + +- [PHP South Wales](https://www.meetup.com/PHP-South-Wales/events/275625320) - January 28th 2021 + +## Soaring with Utility CSS and Tailwind (workshop) + +- [DrupalCamp Florida 2021](https://www.fldrupal.camp/training/soaring-utility-css-and-tailwind) - Feburary 18th 2021 + +## Building Static Websites with Sculpin + +- [Drupal Yorkshire](https://www.meetup.com/DrupalYorkshire/events/280100968) - 19th August 2021 +- PHP North West - 7th September diff --git a/source/_posts/splitting-new-drupal-project-from-repo.md b/source/_posts/splitting-new-drupal-project-from-repo.md new file mode 100644 index 00000000..4805617d --- /dev/null +++ b/source/_posts/splitting-new-drupal-project-from-repo.md @@ -0,0 +1,163 @@ +--- +title: How to split a new Drupal contrib project from within another repository +date: 2018-03-10 +excerpt: How to use Git to split a directory from within an existing repository into it’s own. +tags: + - drupal + - drupal-7 + - drupal-8 + - drupal-planet + - git + - open-source +--- + +Yay! You’ve written a new Drupal module, theme or installation profile as part +of your site, and now you’ve decided to open source it and upload it to +Drupal.org as a new contrib project. But how do you split it from the main site +repository into it’s own? + +Well, there are a couple of options. + +## Does it need to be part of the site repository? + +An interesting thing to consider is, does it _need_ to be a part of the site +repository in the first place? + +If from the beginning you intend to contribute the module, theme or distribution +and it’s written as generic and re-usable from the start, then it _could_ be +created as a separate project on Drupal.org or as a private repository on your +Git server from the beginning, and added as a dependency of the main project +rather than part of it. It could already have the correct branch name and adhere +to the Drupal.org release conventions and be managed as a separate project, then +there is no later need to "clean it up" or split it from the main repo at all. + +This is how I worked at the [Drupal Association][2] - with all of the modules +needed for Drupal.org hosted on Drupal.org itself, and managed as a dependency +of the site repository with Drush Make. + +Whether this is a viable option or not will depend on your processes. For +example, if your code needs to go through a peer review process before releasing +it, then pushing it straight to Drupal.org would either complicate that process +or bypass it completely. Pushing it to a separate private repository may depend +on your team's level of familiarity with [Composer][3], for example. + +It does though avoid the “we’ll clean it up and contribute it later” scenario +which probably happens less than people intend. + +## Create a new, empty repository + +If the project is already in the site repo, this is probably the most common +method - to create a new, empty repository for the new project, add everything +to it and push it. + +For example: + +```bash +cd web/modules/custom/my_new_module + +# Create a new Git repository. +git init + +# Add everything and make a new commit. +git add -A . +git commit -m 'Initial commit' + +# Rename the branch. +git branch -m 8.x-1.x + +# Add the new remote and push everything. +git remote add origin username@git.drupal.org:project/my_new_module.git +git push origin 8.x-1.x +``` + +There is a huge issue with this approach though - **you now have only one single +commit, and you’ve lost the commmit history!** + +This means that you lose the story and context of how the project was developed, +and what decisions and changes were made during the lifetime of the project so +far. Also, if multiple people developed it, now there is only one person being +attributed - the one who made the single new commit. + +Also, if I’m considering adding your module to my project, personally I’m less +likely to do so if I only see one "initial commit". I’d like to see the activity +from the days, weeks or months prior to it being released. + +What this does allow though is to easily remove references to client names etc +before pushing the code. + +## Use a subtree split + +An alternative method is to use [git-subtree][0], a Git command that "merges +subtrees together and split repository into subtrees". In this scenario, we can +use `split` to take a directory from within the site repo and split it into it’s +own separate repository, keeping the commit history intact. + +Here is the description for the `split` command from the Git project itself: + +> Extract a new, synthetic project history from the history of the#Drupal and #PHP usergroups (and others):
— Oliver Davies (@opdavies) April 16, 2020
I have three talks that I'd be happy to do for any remote meetups:
- Deploying PHP applications with Ansible, Ansible Vault and Ansistrano
- Taking Flight with Tailwind CSS
- TDD - Test Driven Drupal
Ping me if you need a speaker...
I'm going to write a book on automated testing in Drupal. Join the mailing list for updates, and I'm happy to take suggestions on what to cover. https://t.co/YXNpe6f8Ft #drupal
— Oliver Davies (@opdavies) May 15, 2018', +} %} + +Being a meetup and DrupalCamp conference organiser, after some thought I decided to build a website for an example conference, and that some of this code would then be included in the book as example content. This seemed to cover most of what I originally wanted, through features like a call for papers for potential speakers to propose sessions, allowing organisers to administer and moderate those proposals, automatically sending notification emails to submitters and displaying the accepted sessions. + +I've started building it with Drupal 8.8 and it is [now available on GitStore][gitstore] to purchase access to, including all future updates as I continue building the application - adding new features and upgrading to Drupal 9 once it is released. There are some other interesting things there too, such as using feature flags to enable or disable functionality, and using GitHub Actions to run the tests automatically. + +The book itself I've [added a page for on Leanpub][leanpub], and I'll be continuing to add content to it in parallel to building the example codebase. Once there is enough content, I will release the first draft for purchase. + +Any purchases that are made via Gitstore or Leanpub, an amount will be donated to the [Drupal Association][] and the [#DrupalCares campaign][drupalcares] to help sustain the Association during COVID-19. + +[drupal association]: https://www.drupal.org/association +[drupalcares]: https://www.drupal.org/association/drupal-cares-challenge +[gitstore]: https://enjoy.gitstore.app/repositories/opdavies/test-driven-drupal-conference-app +[landing page]: /test-driven-drupal +[leanpub]: https://leanpub.com/test-driven-drupal diff --git a/source/_posts/test-driven-drupal-presentation-drupalcon-europe.md b/source/_posts/test-driven-drupal-presentation-drupalcon-europe.md new file mode 100644 index 00000000..41393eaa --- /dev/null +++ b/source/_posts/test-driven-drupal-presentation-drupalcon-europe.md @@ -0,0 +1,22 @@ +--- +title: Test-Driven Drupal presentation from DrupalCon Europe +excerpt: Links to the video and slides from my automated testing session from DrupalCon Europe. +tags: + - drupal + - drupal-8 + - drupalcon + - speaking +date: 2021-01-12 +--- + +Today, the sessions from DrupalCon Europe were posted on the [Drupal Association YouTube channel](https://www.youtube.com/playlist?list=PLpeDXSh4nHjTP7vRC6LCak9adK2yp1P5S), including my session on automated testing and test-driven development in Drupal 8 (and 9): + +Here is the video of my presentation: + +View from outside #drupal pic.twitter.com/b8wGe40Gem
— DrupalCamp London (@DrupalCampLDN) March 1, 2018
This weekend at @DrupalCampLDN will be 5 years since my 1st #Drupal conference. It was life-changing, leading to travel across Europe learning, being inspired & speaking at 25 others. Thanks everyone who helped shape my journey, see you soon! #DCLondon pic.twitter.com/BeulE0XqET
— Chandeep Khosa (@ChandeepKhosa) March 2, 2018
Programmes and badges getting unpacked #Drupal pic.twitter.com/v84c1bgCHu
— DrupalCamp London (@DrupalCampLDN) March 1, 2018
.@MeganSanicki sharing the future plans of what the @drupalassoc is working on next at #DCLCXO #DCLondon pic.twitter.com/XKw7zNgItz
— Chandeep Khosa (@ChandeepKhosa) March 2, 2018
And it begins. @DrupalCampLDN kicks off. #DCLondon pic.twitter.com/XoNkdRmMpx
— hussainweb (@hussainweb) March 3, 2018
The unofficial group photo of @DrupalCampLDN #DCLondon pic.twitter.com/fntdWLfgCE
— hussainweb (@hussainweb) March 3, 2018
Invoking the spirit of @markconroy @gareth5mm and @keithjay before kicking of the Umami Demo at @DrupalCampLDN pic.twitter.com/Yd8UqNUEuk
— eli_t (@eli_t) March 3, 2018
Special mention of @navneet0693 by @eli_t for his contributions to Umami and winning a scholarship to attend @DrupalConNA. Contributing helps everyone and we should encourage that. #DCLondon pic.twitter.com/LJIxLC10Hx
— hussainweb (@hussainweb) March 3, 2018
Listening to @michelvanvelde session about the 5 benefits to get organised as a local Drupal community #DCLondon pic.twitter.com/r1k5yurZLu
— Baddy Breidert (@baddysonja) March 3, 2018
"Reporting and Analytics in Drupal Commerce 2.x" with @ryanszrama, exciting reporting modules for @drupalcommerce #dclondon pic.twitter.com/ouE1MfRAqC
— Tawny Bartlett (@littlepixiez) March 3, 2018
Here we go... pic.twitter.com/NnoEw1oSYH
— David Thorne (@cferthorney) March 3, 2018
"Listen up Drupal!" with slootjes from @MediaMonks, learning about and utilising the powerful #Symfony components inside and outside of #Drupal8 #dclondon pic.twitter.com/OuG5xT6tnf
— Tawny Bartlett (@littlepixiez) March 3, 2018
Huge thanks to @ThunderCoreTeam for sponsoring the social night. We went through £1000 in 40mins! #Drupal #DCLondon pic.twitter.com/lT86MBj6j3
— DrupalCamp London (@DrupalCampLDN) March 3, 2018
This @DrupalCampLDN keynote by @ryanszrama is going to be required watching for anyone building/running a OSS based business.
— Chris Teitzel (@technerdteitzel) March 3, 2018
“What good is it for someone to gain the world but forfeit their soul in the process?”
"Why would anybody not be using [Warden]?" - awesome feedback from the audience at Mike Davis's @DrupalCampLDN presentation. @DeesonAgency #dclondon #drupalcamplondon pic.twitter.com/qZO50GucpO
— James Ford (@psyked) March 3, 2018
Well this was ironic given I'm talking about #drupal 8 #migrate today at 1205 B200 @DrupalCampLDN #dclondon cc @softescu pic.twitter.com/CRVOp9bHVs
— eli_t (@eli_t) March 4, 2018
Final day #Drupal pic.twitter.com/gTbj8VoQw0
— DrupalCamp London (@DrupalCampLDN) March 4, 2018
My first post on d.o was a comment https://t.co/k1QSl8Rh81 #drupalcamp #drupalorigins
— Richard Sheppard (@siliconmeadow) March 4, 2018
Meet my #drupal family in #Manchester @nwdug @eli_t @iriinamacovei pic.twitter.com/3dVTpr4FvZ
— Rakesh James (@RAKESH_JAMES) March 4, 2018
Big thanks to the #dclondon team. It's been a great conference! pic.twitter.com/lq7r9QVSYd
— William Mortada (@wmortada) March 4, 2018
One of the best things at @DrupalCampLDN is how far away international friends come from to speak here. It’s always a pleasure meeting @baddysonja from Germany & @hussainweb from India #DCLondon pic.twitter.com/bNj8e8ZOY2
— Chandeep Khosa (@ChandeepKhosa) March 4, 2018
Was feeling tired but instantly reinspired with a wonderful keynote by @technerdteitzel... We are all superheroes! #drupal #drupalcommunity #dclondon 💧 pic.twitter.com/49j7DCJUpH
— Tawny Bartlett (@littlepixiez) March 4, 2018
Listening to @technerdteitzel remind us that what we’re able to do with #Drupal is magic to most people to kick off @DrupalCampLDN. 🧙🏼♀️ pic.twitter.com/826IdAQ31E
— Ryan Szrama (@ryanszrama) March 4, 2018
Hanging out with some of the most coolest people at @DrupalCampLDN #DCLondon pic.twitter.com/zXFZ7VkPUc
— Chandeep Khosa (@ChandeepKhosa) March 4, 2018
@HornCologne and @cyberswat on radically reducing infrastructure-related drudgery at @DrupalCampLDN pic.twitter.com/eUsTuZI46z
— Eugene Sia (@siaeugene) March 3, 2018
Happy, smiling people from the UK, USA & Finland hanging out & sharing knowledge at @DrupalCampLDN : @GreenyBeans84 @technerdteitzel @KmgLaura @ryanszrama #DCLondon pic.twitter.com/3o2iUCwoR0
— Chandeep Khosa (@ChandeepKhosa) March 4, 2018
Awesome #migrate presentation @eli_t and awesome monkeys @thedeptofdesign 🐵🙈🙉🙊 @DrupalCampLDN
— Timo Kirkkala (@kirkkala) March 4, 2018
Here are the slides from my session today @DrupalCampLDN - flexible content editing with paragraphs in Drupal 8 https://t.co/ozalJ06rL7 #dclondon #drupal
— Baddy Breidert (@baddysonja) March 3, 2018
Slides from yesterday's presentation "Learning Frontend as a Backender" at #dclondon https://t.co/IltzwIEcOF
— David Thorne Limited (@davidthorneltd) March 4, 2018
Sharing the awesome story of how @DrupalEurope has come about and will take place in September #DCLondon pic.twitter.com/yJj5i6tMxV
— Chandeep Khosa (@ChandeepKhosa) March 4, 2018
"Enable mentors to get credits." @baddysonja @DrupalCampLDN #DCLondon pic.twitter.com/QsWA61xpid
— hussainweb (@hussainweb) March 4, 2018
A bit of snow brings the UK to a halt but it doesn't stop Iceland! Closing keynote from @baddysonja #robotsoccerchampion #DCLondon @DrupalCampLDN pic.twitter.com/f7o4XA0rHo
— Orange Punch (@OrangePunchUK) March 4, 2018
Three steps to improve:
— hussainweb (@hussainweb) March 4, 2018
1. Improve training quality.
2. Create facilities for _everyone_.
3. Start early (Train children).@baddysonja @DrupalCampLDN #DCLondon pic.twitter.com/mJK3NctzG8
There were 50+ #Drupal events in 2017, just in Europe. Creating a template is extremely important to sustain the effort. It doesn't help anyone is every camp organiser ends up burnt out. @baddysonja @DrupalCampLDN #DCLondon pic.twitter.com/Diq1aid36a
— hussainweb (@hussainweb) March 4, 2018
.@railsgirls have an awesome set of resources & templates that help women to very easily create new local events around the world. We can learn a lot from them in the #Drupal community- @baddysonja at @DrupalCampLDN #DCLondon pic.twitter.com/jxT29YGAvy
— Chandeep Khosa (@ChandeepKhosa) March 4, 2018
Thank you all dear @DrupalCampLDN organizers and #keynote speakers... Amazing work.. great job #drupal @ryanszrama @baddysonja @aburrows pic.twitter.com/FiS2I5lBjs
— Rakesh James (@RAKESH_JAMES) March 4, 2018
A great weekend. Thanks to the organisers for putting on a great show. Thanks to @ReasonDigital for sending me. Lots of new stuff to try out on Monday! https://t.co/Xxvl7ZYcqj
— Tom Metcalfe (@blwd_uk) March 4, 2018
It’s really amazing news that #Drupal camp organisers will now finally be credited on their https://t.co/PO2gpSItnz profiles - @baddysonja at @DrupalCampLDN #DCLondon pic.twitter.com/dHI5X7zBHk
— Chandeep Khosa (@ChandeepKhosa) March 4, 2018
Discovering how to see the bigger picture! @DrupalCampLDN ends up with this amazing keynote delivered by @baddysonja. #dclondon pic.twitter.com/bo0vgAvwxI
— Laura Ionescu (@Laura_MStrategy) March 4, 2018
Thank you everyone who helped organise @DrupalCampLDN #DCLondon pic.twitter.com/hRtl7950CW
— hussainweb (@hussainweb) March 4, 2018
Great #DCLondon event again, so rewarding to see everyone there. Until next year! #Drupal @… https://t.co/DXCEaVzi9j
— Paul (@Paul_Rowell) March 4, 2018
A huge huge huge thank you to all our sponsors, speakers, volunteers, attendees and everyone else involved in that Camp. It was a fantastic weekend!
— DrupalCamp London (@DrupalCampLDN) March 4, 2018
Look out for the post Camp Survey and more! #DCLondon
Putting @laravelphp's Collection class to good use, cleaning up some of my @drupal 8 code. Thanks @TightenCo for the Collect library! pic.twitter.com/Bn1UfudGvp
— Oliver Davies (@opdavies) August 18, 2017', + } %} + + {% include 'tweet' with { + class: 'block mb-4 lg:w-1/2 lg:px-2 lg:mb-0', + data_cards: true, + content: 'Putting more @laravelphp Collections to work in my @drupal code today. 😁 pic.twitter.com/H8xDTT063X
— Oliver Davies (@opdavies) February 14, 2018', + } %} + + {% include 'tweet' with { + class: 'block mb-4 lg:w-1/2 lg:px-2 lg:mb-0', + data_cards: true, + content: 'I knew that you could specify a property like 'price' in Twig and it would also look for methods like 'getPrice()', but I didn't know (or had maybe forgotten) that @laravelphp Collections does it too.
This means that these two Collections return the same result.
Nice! 😎 pic.twitter.com/2g2IfThzdy
More @laravelphp Collection goodness, within my #Drupal8 project! pic.twitter.com/mWgpNbNIrh
— Oliver Davies (@opdavies) August 10, 2018', + } %} + + {% include 'tweet' with { + class: 'block mb-4 lg:w-1/2 lg:px-2 lg:mb-0', + data_cards: true, + content: 'Some more #Drupal 8 fun with Laravel Collections. Loading the tags for a post and generating a formatted string of tweetable hashtags. pic.twitter.com/GbyiRPzIRo
— Oliver Davies (@opdavies) August 23, 2018', + } %} + +@opdavies @DrupalCampLDN always had trouble with git. Your talk + Git flow has made it all very easy.
— James Tombs (@jtombs) March 2, 2014' +} %} + +{% include 'tweet' with { + content: 'Great presentation by @opdavies on git flow at #dclondon very well prepared and presented. pic.twitter.com/tDINp2Nsbn
— Greg Franklin (@gfranklin) March 2, 2014' +} %} + +{% include 'tweet' with { + content: 'Great talk on git flow @opdavies #dclondon
— Curve Agency (@CurveAgency) March 2, 2014' +} %} diff --git a/source/_posts/writing-article-linux-journal.md b/source/_posts/writing-article-linux-journal.md new file mode 100644 index 00000000..13b5dc7b --- /dev/null +++ b/source/_posts/writing-article-linux-journal.md @@ -0,0 +1,22 @@ +--- +title: Writing an Article for Linux Journal +date: 2012-07-27 +excerpt: I'm absolutely delighted to announce that I'm going to be writing an article for Linux Journal magazine's upcoming Drupal special. +tags: + - distributions + - drupal + - installation-profiles + - linux-journal + - writing +--- + +I'm absolutely delighted to announce that I'm going to be writing an article for +[Linux Journal](http://www.linuxjournal.com) magazine's upcoming Drupal special. + +The article is going to be entitled "Speeding Up Your Drupal Development Using +Installation Profiles and Distributions" and will be mentioning existing +distributions available on Drupal.org, but mainly focussing on the steps needed +to create your own custom distribution. Needless to say, I'm quite excited about +it! + +The article is expected to be published in October. diff --git a/source/_posts/writing-info-file-drupal-7-theme.md b/source/_posts/writing-info-file-drupal-7-theme.md new file mode 100644 index 00000000..e3d623c1 --- /dev/null +++ b/source/_posts/writing-info-file-drupal-7-theme.md @@ -0,0 +1,40 @@ +--- +title: Writing a .info file for a Drupal 7 theme +date: 2012-05-23 +excerpt: An example .info file for a Drupal 7 theme. +tags: + - code + - drupal + - drupal-theming + - theming +--- + +```ini +name = My Theme +description = A description of my theme +core = 7.x + +# Add a base theme, if you want to use one. +base = mybasetheme + +# Define regions, otherwise the default regions will be used. +regions[header] = Header +regions[navigation] = Navigation +regions[content] = Content +regions[sidebar] = Sidebar +regions[footer] = Footer + +# Define which features are available. If none are specified, all the default +# features will be available. +features[] = logo +features[] = name +features[] = favicon + +# Add stylesheets +stylesheets[all][] = css/reset.css +stylesheets[all][] = css/mytheme.css +stylesheets[print][] = css/print.css + +# Add javascript files +styles[] = js/mytheme.js +``` diff --git a/source/_posts/writing-new-drupal-8-module-using-test-driven-development-tdd.md b/source/_posts/writing-new-drupal-8-module-using-test-driven-development-tdd.md new file mode 100644 index 00000000..d3990975 --- /dev/null +++ b/source/_posts/writing-new-drupal-8-module-using-test-driven-development-tdd.md @@ -0,0 +1,661 @@ +--- +title: Writing a new Drupal 8 Module using Test-Driven Development (TDD) +date: 2017-11-07 +tags: + - drupal + - phpunit + - simpletest + - tdd + - testing +excerpt: How to write automated tests and follow test driven development for Drupal modules. +meta: + image: + url: /images/talks/test-driven-drupal-development.png + width: 2560 + height: 1440 + type: image/png +promoted: true +--- + +![](/images/blog/drupalcamp-dublin.jpg)
+ +I recently gave a [talk on automated testing in Drupal][0] talk at [DrupalCamp +Dublin][1] and as a lunch and learn session for my colleagues at Microserve. As +part of the talk, I gave an example of how to build a Drupal 8 module using a +test driven approach. I’ve released the [module code on GitHub][2], and this +post outlines the steps of the process. + +## Prerequisites + +You have created a `core/phpunit.xml` file based on `core/phpunit.xml.dist`, and +populated it with your database credentials so that PHPUnit can bootstrap the +Drupal database as part of the tests. [Here is an example][5]. + +## Acceptance Criteria + +For the module, we are going to satisfy this example acceptance criteria: + +> As a site visitor,