Welcome to {{ title }}
+No front page content has been created yet.
+Follow the User Guide to start building your site.
+diff --git a/app/SculpinKernel.php b/app/SculpinKernel.php
new file mode 100644
index 00000000..55fdc1e3
--- /dev/null
+++ b/app/SculpinKernel.php
@@ -0,0 +1,19 @@
+=5.3.3"
+ },
+ "suggest": {
+ "sculpin/sculpin": "@stable"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Opdavies\\Sculpin\\Bundle\\GistEmbedBundle\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Oliver Davies",
+ "email": "oliver@oliverdavies.uk",
+ "homepage": "https://www.oliverdavies.uk"
+ }
+ ],
+ "description": "Allows for embedding GitHub Gists into a Sculpin site.",
+ "keywords": [
+ "sculpin"
+ ],
+ "support": {
+ "issues": "https://github.com/opdavies/sculpin-gist-embed-bundle/issues",
+ "source": "https://github.com/opdavies/sculpin-gist-embed-bundle/tree/master"
+ },
+ "time": "2016-04-05T19:57:22+00:00"
+ },
{
"name": "psr/container",
"version": "1.1.1",
diff --git a/source/_layouts/post.html.twig b/source/_layouts/post.html.twig
new file mode 100644
index 00000000..b9b6e07a
--- /dev/null
+++ b/source/_layouts/post.html.twig
@@ -0,0 +1,7 @@
+{% extends 'page' %}
+
+{% block body %}
+ {{ parent() }}
+
+ {% include 'about-author' %}
+{% endblock %}
diff --git a/source/_pages/blog.html.twig b/source/_pages/blog.html.twig
new file mode 100644
index 00000000..bd254689
--- /dev/null
+++ b/source/_pages/blog.html.twig
@@ -0,0 +1,10 @@
+---
+title: Blog
+use:
+ - posts
+---
+
+{% for post in data.posts %}
+ {{ post.title }}
+
+{% endfor %}
diff --git a/source/_partials/figure.html.twig b/source/_partials/figure.html.twig
new file mode 100644
index 00000000..c8de55c1
--- /dev/null
+++ b/source/_partials/figure.html.twig
@@ -0,0 +1,8 @@
+
diff --git a/source/_partials/talk/speakerdeck.html.twig b/source/_partials/speakerdeck.html.twig
similarity index 100%
rename from source/_partials/talk/speakerdeck.html.twig
rename to source/_partials/speakerdeck.html.twig
diff --git a/source/_partials/talk/slides.html.twig b/source/_partials/talk/slides.html.twig
index e3f101b9..2a2124f0 100644
--- a/source/_partials/talk/slides.html.twig
+++ b/source/_partials/talk/slides.html.twig
@@ -2,7 +2,7 @@
+ {{ content|raw }}
+
+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..1ec09c36 --- /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-promotions-illiminate-collections.md b/source/_posts/debugging-drupal-commerce-promotions-illiminate-collections.md new file mode 100644 index 00000000..a2094378 --- /dev/null +++ b/source/_posts/debugging-drupal-commerce-promotions-illiminate-collections.md @@ -0,0 +1,144 @@ +--- +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/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..69f290df --- /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: + - drupal + - drupal-7 + - drupal-planet + - php + - aria +--- + +For reference, this is the code needed to display a menu in a Drupal 7 template +file, including the navigation ARIA role. + +```language-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..db8d2eaf --- /dev/null +++ b/source/_posts/display-git-branch-or-tag-names-your-bash-prompt.md @@ -0,0 +1,70 @@ +--- +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): + +```language-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, + +```language-bash +vim ~/.bash/git-prompt +``` + +Paste the following code into the file, and save it. + +```language-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. + +```language-bash +vim ~/.bashrc +``` + +Add the following code at the bottom of the file, and save it. + +```language-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..56c3e49d --- /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 'video-embed' with { + classes: 'video-full', + video: { + id: 'honnav4YlAA', + attr: { + height: '315', + width: '560', + } + } +} %} + +[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..bd204fbc --- /dev/null +++ b/source/_posts/drupalcamp-bristol-early-bird-tickets-sessions-sponsors.md @@ -0,0 +1,70 @@ +--- +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..95b4afcd --- /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..8ec1d9b5 --- /dev/null +++ b/source/_posts/drupalcamp-london-2019-tickets.md @@ -0,0 +1,41 @@ +--- +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.md b/source/_posts/drupalcamp-london-testing-workshop.md new file mode 100644 index 00000000..7f57df8e --- /dev/null +++ b/source/_posts/drupalcamp-london-testing-workshop.md @@ -0,0 +1,26 @@ +--- +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 +--- + +![]({{page.lead_image.url}}){.p-1 .border} + +{{ page.excerpt }}
+ +The subject will be automated testing and test driven development in Drupal 8, +and it will be on Friday 13th March 2020, between 1pm and 5pm. + +In the workshop, I’ll cover the methodology, approaches and terminology involved +with automated testing, look at some examples and work through some exercises, +and then take a test driven development approach to creating a new Drupal +module. + +There are also other workshops on topics including Composer, Drupal Commerce, +profiling, and chatbots. + +For more information and to register, go to the +[DrupalCamp London website](https://opdavi.es/dclondon20 'Find out more and register on the DrupalCamp London website'). diff --git a/source/_posts/easier-git-repository-cloning-with-insteadof.md b/source/_posts/easier-git-repository-cloning-with-insteadof.md new file mode 100644 index 00000000..5f782e74 --- /dev/null +++ b/source/_posts/easier-git-repository-cloning-with-insteadof.md @@ -0,0 +1,83 @@ +--- +title: Easier Git Repository Cloning with insteadOf +date: 2019-03-07 +excerpt: + How to simplify 'git clone' commands by using the insteadOf configuration + option within your .gitconfig file. +tags: [git] +--- + +When working on client or open source projects, I clone a lot of +[Git](https://git-scm.com) repositories - either from GitHub, GitLab, Bitbucket +or Drupal.org. The standard `git clone` commands though provided by these sites +can be quite verbose with long repository URLs and use a mixture of different +protocols, and I’d regularly need to go back to each website and look up the +necessary command every time. + +For example, here is the command provided to clone Drupal’s +[Override Node Options module](https://www.drupal.org/project/override_node_options): + +```plain +git clone --branch 8.x-2.x https://git.drupal.org/project/override_node_options.git +``` + +We can though simplify the command to make it easier and quicker to type, using +a Git configuration option called `insteadOf`. + +## What is insteadOf? + +From the +[Git documentation](https://git-scm.com/docs/git-config#git-config-urlltbasegtinsteadOf): + +> **url.[base].insteadOf:** +> +> Any URL that starts with this value will be rewritten to start, instead, with +> [base]. In cases where some site serves a large number of repositories, and +> serves them with multiple access methods, and some users need to use different +> access methods, this feature allows people to specify any of the equivalent +> URLs and have Git automatically rewrite the URL to the best alternative for +> the particular user, even for a never-before-seen repository on the site. When +> more than one insteadOf strings match a given URL, the longest match is used. + +Whilst examples are sparse, +[it seems like](https://stackoverflow.com/questions/1722807/how-to-convert-git-urls-to-http-urls) +insteadOf is used for resolving protocol issues with repository URLs. However, +we can use it to simplify our clone commands, as mentioned above. + +## Example: cloning Drupal contrib projects + +When working on Drupal core, or on a module, theme or distribution, you need to +have a cloned version of that repository to generate patch files from, and apply +patches to. + +Again, here is the provided command to clone the Override Node Options module: + +```plain +git clone --branch 8.x-2.x https://git.drupal.org/project/override_node_options.git +``` + +At the time of writing, the Git repository URL follow this same format - +`https://git.drupal.org/project/{name}.git` (also the `.git` file extension is +optional). + +To shorten and simplify this, I can add this snippet to my `~/.gitconfig` file: + +``` +[url "https://git.drupal.org/project/"] + insteadOf = do: + insteadOf = drupal: +``` + +With that added, I can now instead run `git clone drupal:{name}` or +`git clone do:{name}` to clone the repository, specifying the project’s machine +name. + +For example, to clone the Override Node Options module, I can now do this using +just `git clone drupal:override_node_options`. + +This, I think, is definitely quicker and easier! + +## Resources + +You can view my entire `.gitconfig` file, as well as my other dotfiles, in +[my dotfiles repository on GitHub](https://github.com/opdavies/dotfiles/blob/master/.gitconfig). diff --git a/source/_posts/easier-sculpin-commands-with-composer-and-npm-scripts.md b/source/_posts/easier-sculpin-commands-with-composer-and-npm-scripts.md new file mode 100644 index 00000000..b813491d --- /dev/null +++ b/source/_posts/easier-sculpin-commands-with-composer-and-npm-scripts.md @@ -0,0 +1,77 @@ +--- +title: Easier Sculpin Commands with Composer and NPM Scripts +date: 2017-01-07 +excerpt: + In this video, I show you how I've simplied my Sculpin and Gulp workflow using + custom Composer and NPM scripts. +tags: [composer, gulp, sculpin] +--- + +In this video, I show you how I've simplied my Sculpin and Gulp workflow using +custom Composer and NPM scripts. + +My website includes several various command line tools - e.g. [Sculpin][4], +[Gulp][5] and [Behat][6] - each needing different arguments and options, +depending on the command being run. For example, for Sculpin, I normally include +several additional options when viewing the site locally - the full command that +I use is +`./vendor/bin/sculpin generate --watch --server --clean --no-interaction`. +Typing this repeatedly is time consuming and could be easily mis-typed, +forgotten or confused with other commands. + +In this video, I show you how I've simplied my Sculpin and Gulp workflow using +custom Composer and NPM scripts. + + + +## Scripts + +Here are the scripts that I’m using - they are slightly different from those in +the video. I use the `--generate` and `--watch` options for Sculpin and the +`gulp watch` command for NPM. I had to change these before the recording as I +was using the [demo magic][0] script to run the commands, and existing from a +watch session was also ending the script process. + +### composer.json + +```language-json +"scripts": { + "clean": "rm -rf output_*/", + "dev": "sculpin generate --clean --no-interaction --server --watch", + "production": "sculpin generate --clean --no-interaction --env='prod' --quiet" +} +``` + +Run with `composer runPutting @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', + } %} + +![](/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..49ccb1b2 --- /dev/null +++ b/source/_posts/leaving-nomensa-joining-precedent.md @@ -0,0 +1,34 @@ +--- +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 + - precedent + - personal +--- + +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.md b/source/_posts/live-blogging-symfonylive-london.md new file mode 100644 index 00000000..e37a7d86 --- /dev/null +++ b/source/_posts/live-blogging-symfonylive-london.md @@ -0,0 +1,713 @@ +--- +title: Live Blogging From SymfonyLive London 2019 +date: 2019-09-13 +tags: + - conference + - symfony + - symfonylive + - php +--- + +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 + +```yml +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..f6d4703a --- /dev/null +++ b/source/_posts/looking-forward-to-drupalcamp-london.md @@ -0,0 +1,68 @@ +--- +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..8851118f --- /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-npm-package.md b/source/_posts/published-my-first-npm-package.md new file mode 100644 index 00000000..dfb70af7 --- /dev/null +++ b/source/_posts/published-my-first-npm-package.md @@ -0,0 +1,119 @@ +--- +title: Published my first NPM package +date: 2018-12-16 +excerpt: + Yesterday I published my first module onto NPM, and it’s a plugin for Tailwind + CSS to be used alongside Vue.js. +tags: + - npm + - tailwind-css + - vuejs +--- + +Yesterday I published my first module onto NPM, and it’s a plugin for [Tailwind +CSS][tailwind] to be used alongside [Vue.js](https://vuejs.org). + +The plugin adds classes for showing and hiding elements in different display +variations in combination with Vue's +[v-cloak directive](https://vuejs.org/v2/api/#v-cloak), which I originally saw +in [the first 'Building Kitetail' video](https://youtu.be/XUXpcbYQ_iQ?t=2360). +These are useful for when you want an element to be visible whilst Vue is +compiling, and hidden afterwards. + +Here is the compiled CSS that is added by the plugin: + +```css +[v-cloak] .v-cloak-block { + display: block; +} + +[v-cloak] .v-cloak-flex { + display: flex; +} + +[v-cloak] .v-cloak-hidden { + display: none; +} + +[v-cloak] .v-cloak-inline { + display: inline; +} + +[v-cloak] .v-cloak-inline-block { + display: inline-block; +} + +[v-cloak] .v-cloak-inline-flex { + display: inline-flex; +} + +[v-cloak] .v-cloak-invisible { + visibility: hidden; +} + +.v-cloak-block, +.v-cloak-flex, +.v-cloak-inline, +.v-cloak-inline-block, +.v-cloak-inline-flex { + display: none; +} +``` + +The `v-cloak` directive exists on an element until Vue finishes compiling, after +which it is removed. Therefore adding a `v-cloak-block` class to an element will +make it `display: block` whilst Vue is compiling and the element is cloaked, and +`display: none` afterwards when the Vue markup is compiled and rendered. + +In my `base.html.twig` template, I’ve added `v-cloak` to the wrapper div within +the `body`. + +{% verbatim %}![](/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. + + + +This means that I can simply re-generate the site after making changes and add +it as an additional commit to my main branch with no need to switch branches or +perform a merge. + +To simplify this, I’ve added a new [publish.sh script][3] into my repository to +automate the sites. This is how it currently looks: + +```language-bash +#!/usr/bin/env bash + +SITE_ENV="prod" + +# Remove the existing docs directory, build the site and create the new +# docs directory. +rm -rf ./docs +vendor/bin/sculpin generate --no-interaction --clean --env=${SITE_ENV} +touch output_${SITE_ENV}/.nojekyll +mv output_${SITE_ENV} docs + +# Ensure the correct Git variables are used. +git config --local user.name 'Oliver Davies' +git config --local user.email oliver@oliverdavies.uk + +# Add, commit and push the changes. +git add --all docs +git commit -m 'Build.' +git push origin HEAD +``` + +This begins by removing the deleting the existing `docs` directory and +re-generating the site with the specified environment. Then I add a `.nojekyll` +file and rename the output directory to replace `docs`. + +Now the changes can be added, committed and pushed. Once pushed, the new code is +automatically served by GitHub Pages. + +## HTTPS + +GitHub Pages unfortunately does [not support HTTPS for custom domains][7]. + +As the site was previously using HTTPS, I didn’t want to have to go back to +HTTP, break any incoming links and lose any potential traffic. To continue using +HTTPS, I decided to [use Cloudflare][6] to serve the site via their CDN which +does allow for HTTPS traffic. + +## Next Steps + +- Enable automatically running `publish.sh` when new changes are pushed to + GitHub rather than running it manually. I was previously [using Jenkins][4] + and Fabric for this, though I’m also going to look into using Travis to + accomplish this. +- Add the pre-build steps such as running `composer install` and `yarn` to + install dependencies, and `gulp` to create the front-end assets. This was + previously done by Jenkins in my previous setup. + +## Resources + +- [Publishing your GitHub Pages site from a /docs folder on your master + branch][2] +- [Bypassing Jekyll on GitHub Pages][5] +- [Secure and fast GitHub Pages with CloudFlare][6] + +[0]: https://pages.github.com +[1]: https://github.com/opdavies/oliverdavies.uk +[2]: + https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/#publishing-your-github-pages-site-from-a-docs-folder-on-your-master-branch +[3]: https://github.com/opdavies/oliverdavies.uk/blob/master/publish.sh +[4]: /articles/2015/07/21/automating-sculpin-jenkins +[5]: https://github.com/blog/572-bypassing-jekyll-on-github-pages +[6]: https://blog.cloudflare.com/secure-and-fast-github-pages-with-cloudflare +[7]: https://github.com/blog/2186-https-for-github-pages diff --git a/source/_posts/queuing-private-messages-in-drupal-8.md b/source/_posts/queuing-private-messages-in-drupal-8.md new file mode 100644 index 00000000..180b79a1 --- /dev/null +++ b/source/_posts/queuing-private-messages-in-drupal-8.md @@ -0,0 +1,84 @@ +--- +title: Queuing Private Messages in Drupal 8 +date: 2018-02-27 +excerpt: Introducing the Private Message Queue module for Drupal 8. +tags: + - drupal + - drupal-8 + - drupal-modules + - drupal-planet + - open-source +--- + +My current project at [Microserve][0] is a Drupal 8 website that uses the +[Private Message][1] module for users to send messages to each other. + +In some cases though, the threads could contain hundreds of recipients so I +decided that it would be good to queue the message requests so that they can be +processed as part of a background process for better performance. The Private +Message module does not include this, so I've written and released a separate +[Private Message Queue][2] module. + +## Queuing a Message + +The module provices a `PrivateMessageQueuer` service +(`private_message_queue.queuer`) which queues the items via the `queue()` +method. + +The method accepts an array of `User` objects as the messsage recipients, the +message body text and another user as the message owner. (I’m currently +considering [whether to make the owner optional][4], and default to the current +user if one is not specified) + +Here is an example: + +```php +$recipients = $this->getRecipients(); // An array of User objects. +$message = 'Some message text'; +$owner = \Drupal::currentUser(); + +$queuer = \Drupal::service('private_message_queue.queuer'); +$queuer->queue($recipients, $message, $owner); +``` + +These three pieces of data are then saved as part of the queued item. You can +see these by checking the "queue" table in the database or by running +`drush queue-list`. + +![](/images/blog/private-message-queue.png) + +``` +$ drush queue-list +Queue Items Class +private_message_queue 19 Drupal\Core\Queue\DatabaseQueue +``` + +## Processing the Queue + +The module also provides a `PrivateMessageQueue` queue worker, which processes +the queued items. For each item, it creates a new private message setting the +owner and the message body. + +It uses the `PrivateMessageThread` class from the Private Message module to find +for an existing thread for the specified recipients, or creates a new thread if +one isn't found. The new message is then added to the thread. + +The queue is processed on each cron run, so I recommend adding a module like +[Ultimate Cron][3] so that you can process the queued items frequently (e.g. +every 15 minutes) and run the heavier tasks like checking for updates etc less +frequently (e.g. once a day). + +You can also process the queue manually with Drush using the +`drush queue-runI 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..2095a7c5 --- /dev/null +++ b/source/_posts/quickest-way-install-sublime-text-2-ubuntu.md @@ -0,0 +1,27 @@ +--- +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: + +```language-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-and-curl-or-wget.md b/source/_posts/quickly-apply-patches-using-git-and-curl-or-wget.md new file mode 100644 index 00000000..27bab751 --- /dev/null +++ b/source/_posts/quickly-apply-patches-using-git-and-curl-or-wget.md @@ -0,0 +1,31 @@ +--- +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: + - git + - drupal-planet +--- + +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: + +```language-bash +$ curl http://drupal.org/files/[patch-name].patch | git apply -v +``` + +Or, if you don't have curl installed, you can use wget: + +```language-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..a5269667 --- /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: + - drupal-planet + - imagefield-import + - drupal + - drupal-6 + - photo-gallery + - cck + - imagefield +--- + +**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..376c2dd3 --- /dev/null +++ b/source/_posts/rebuilding-acquia-dashboard-with-vuejs-tailwind-css.md @@ -0,0 +1,162 @@ +--- +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 +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-with-vuejs-tailwind-css-part-2.md b/source/_posts/rebuilding-bartik-with-vuejs-tailwind-css-part-2.md new file mode 100644 index 00000000..7ec5c37a --- /dev/null +++ b/source/_posts/rebuilding-bartik-with-vuejs-tailwind-css-part-2.md @@ -0,0 +1,316 @@ +--- +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/splitting-new-drupal-project-from-repo.md b/source/_posts/splitting-new-drupal-project-from-repo.md new file mode 100644 index 00000000..afa76553 --- /dev/null +++ b/source/_posts/splitting-new-drupal-project-from-repo.md @@ -0,0 +1,165 @@ +--- +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: + +```language-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 theI'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/testing-tailwindcss-plugins-with-jest.md b/source/_posts/testing-tailwindcss-plugins-with-jest.md new file mode 100644 index 00000000..23c39a1d --- /dev/null +++ b/source/_posts/testing-tailwindcss-plugins-with-jest.md @@ -0,0 +1,284 @@ +--- +title: Testing Tailwind CSS plugins with Jest +date: 2019-04-29 +excerpt: How to write tests for Tailwind CSS plugins using Jest. +tags: + - javascript + - jest + - tailwind-css + - testing +promoted: true +--- + +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
@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..f54b71e6 --- /dev/null +++ b/source/_posts/writing-article-linux-journal.md @@ -0,0 +1,24 @@ +--- +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: + - drupal + - distributions + - installation-profiles + - writing + - linux-journal +--- + +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-drupal-module-test-driven-development-tdd.md b/source/_posts/writing-drupal-module-test-driven-development-tdd.md new file mode 100644 index 00000000..50045d9e --- /dev/null +++ b/source/_posts/writing-drupal-module-test-driven-development-tdd.md @@ -0,0 +1,658 @@ +--- +title: Writing a new Drupal 8 Module using Test Driven Development (TDD) +date: 2017-11-07 +tags: [drupal, testing, tdd, simpletest, phpunit] +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,