Move sculpin-old files to sculpin-old directory
This commit is contained in:
parent
50aa844492
commit
3f85249f3c
447 changed files with 0 additions and 0 deletions
2
sculpin-old/source/_headers
Normal file
2
sculpin-old/source/_headers
Normal file
|
@ -0,0 +1,2 @@
|
|||
/feed
|
||||
Content-Type: application/xml
|
35
sculpin-old/source/_layouts/app.html.twig
Normal file
35
sculpin-old/source/_layouts/app.html.twig
Normal file
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-GB" class="no-js">
|
||||
<head>
|
||||
<title>{% if page.title is defined and page.url != '//' %}{{ page.title }} | {% endif %}{{ site.title }} - {{ site.subtitle }}</title>
|
||||
{% include 'meta' %}
|
||||
{% block metas %}
|
||||
<meta name="twitter:site" content="@{{ site.twitter.user }}">
|
||||
<meta name="twitter:title" content="{{ page.title }}">
|
||||
<meta name="twitter:creator" content="@{{ site.twitter.user }}">
|
||||
{% endblock %}
|
||||
|
||||
<link rel="stylesheet" href="{{ asset('build/app.css') }}">
|
||||
{% block stylesheets %}{% endblock %}
|
||||
|
||||
{% for size in site.apple_touch_icon_sizes %}
|
||||
<link rel="apple-touch-icon" href="{{ site.favicon.url }}?s={{ size }}" sizes="{{ size }}x{{ size }}">
|
||||
{% endfor %}
|
||||
|
||||
{% for size in site.favicon_sizes %}
|
||||
<link rel="icon" href="{{ site.favicon.url }}?s={{ size }}" sizes="{{ size }}x{{ size }}">
|
||||
{% endfor %}
|
||||
</head>
|
||||
<body class="antialiased font-sans text-gray-800 leading-relaxed">
|
||||
<div class="min-h-screen flex flex-col">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{% if site.google_analytics.id %}
|
||||
<script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', '{{ site.google_analytics.id }}', 'auto'); ga('send', 'pageview');</script>
|
||||
{% endif %}
|
||||
|
||||
<script src="{{ asset('build/app.js') }}"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
33
sculpin-old/source/_layouts/default.html.twig
Normal file
33
sculpin-old/source/_layouts/default.html.twig
Normal file
|
@ -0,0 +1,33 @@
|
|||
{% extends 'app' %}
|
||||
|
||||
{% block body %}
|
||||
{% include 'layout/navbar' %}
|
||||
|
||||
<div class="mt-10 container">
|
||||
<main id="main-content" class="flex-1 wrap {{ page.layout == 'front' ? 'is-wide' : '' }}">
|
||||
{# {% if page.layout in ['post', 'talk'] %}
|
||||
{% include 'site-message' %}
|
||||
{% endif %} #}
|
||||
|
||||
{% block page_title_wrapper %}
|
||||
<h1 class="leading-tight mb-4">
|
||||
{% block page_title %}{{ page.title }}{% endblock %}
|
||||
</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content_wrapper %}
|
||||
{% block content %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
{% include 'layout/footer' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% if page.has_tweets or page.type == 'tweet' %}
|
||||
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
{% endif %}
|
||||
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
{% endblock %}
|
17
sculpin-old/source/_layouts/front.html.twig
Normal file
17
sculpin-old/source/_layouts/front.html.twig
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends 'default' %}
|
||||
|
||||
{% block content_wrapper %}
|
||||
<div class="md:flex -mx-6">
|
||||
<div class="wrap md:flex-1 px-6 mb-12 md:mb-0">
|
||||
<div class="markup spaced-y-4 mb-8">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{% include 'about/availability' %}
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-2/5 lg:w-1/3 px-6">
|
||||
{% include 'about/badges' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
12
sculpin-old/source/_layouts/helpers.html.twig
Normal file
12
sculpin-old/source/_layouts/helpers.html.twig
Normal file
|
@ -0,0 +1,12 @@
|
|||
{% macro talkEventName(event) %}
|
||||
{% if event.url %}
|
||||
<a href="{{ event.url }}">{{ event.name }}</a>
|
||||
{% else %}
|
||||
{{ event.name }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro talkEventLocation(event) %}
|
||||
{{ event.location ? 'in ' ~ event.location }}
|
||||
{{ event.remote ? ' (Remote)' }}
|
||||
{% endmacro %}
|
7
sculpin-old/source/_layouts/page.html.twig
Normal file
7
sculpin-old/source/_layouts/page.html.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
{% extends 'default' %}
|
||||
|
||||
{% block content_wrapper %}
|
||||
<div>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
26
sculpin-old/source/_layouts/post.html.twig
Normal file
26
sculpin-old/source/_layouts/post.html.twig
Normal file
|
@ -0,0 +1,26 @@
|
|||
{% extends 'default' %}
|
||||
|
||||
{% block page_title_wrapper %}{% endblock %}
|
||||
|
||||
{% block metas %}
|
||||
{{ parent() }}
|
||||
<meta name="og:description" content="{{ page.excerpt }}">
|
||||
<meta name="twitter:description" content="{{ page.excerpt }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content_wrapper %}
|
||||
{% include 'post/header' %}
|
||||
{% include 'post/intro-image' %}
|
||||
|
||||
<div class="markup post mb-6">
|
||||
{% include 'post/old-post-message' %}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
{% include 'post/questions-comments' %}
|
||||
{% include 'post/tags' %}
|
||||
</div>
|
||||
|
||||
{% include 'post/about-author' %}
|
||||
{% endblock %}
|
31
sculpin-old/source/_layouts/talk.html.twig
Normal file
31
sculpin-old/source/_layouts/talk.html.twig
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% extends 'default' %}
|
||||
|
||||
{% block metas %}
|
||||
{{ parent() }}
|
||||
<meta name="og:description" content="{{ page.description }}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:description" content="{{ page.description }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content_wrapper %}
|
||||
<div class="spaced-y-8">
|
||||
<div class="markup spaced-y-6">
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
<section>
|
||||
<h2 class="sr-only">Give me feedback</h2>
|
||||
|
||||
<p>
|
||||
<strong>Want to give me feedback for this talk?</strong>
|
||||
I’m <a href="{{ site.twitter.url }}" title="Leave me feedback on Twitter">@{{ site.twitter.user }}</a> on Twitter or <a href="/contact">send me an email</a>.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{% include 'talk/slides' %}
|
||||
{% include 'talk/video' %}
|
||||
{% include 'talk/tweets' %}
|
||||
{% include 'talk/events' with { events: get_events_for_talk(page, site.events) } only %}
|
||||
</div>
|
||||
{% endblock %}
|
7
sculpin-old/source/_pages/404.md
Normal file
7
sculpin-old/source/_pages/404.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: Page Not Found
|
||||
permalink: /404.html
|
||||
use: [posts]
|
||||
---
|
||||
|
||||
Please [contact me](/contact/) and let me know.
|
39
sculpin-old/source/_pages/about.md
Normal file
39
sculpin-old/source/_pages/about.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
layout: front
|
||||
title: About Me
|
||||
meta:
|
||||
og:
|
||||
title:
|
||||
'Oliver Davies - Full Stack Developer, System Administrator, PHP and
|
||||
Drupal specialist'
|
||||
description:
|
||||
'The personal website and blog of Oliver Davies, a Full Stack Developer
|
||||
and System Administrator from Wales, UK.'
|
||||
type: website
|
||||
permalink: /
|
||||
---
|
||||
|
||||
<div class="mb-4 w-32"><img src="/images/me-precedent.jpg" alt="Picture of Oliver" class="rounded-full border border-gray"/></div>
|
||||
|
||||
Hi, I'm Oliver Davies (aka [opdavies](https://www.google.com/#q=opdavies)). I’m
|
||||
a {{ site.work.role }} at
|
||||
[{{site.companies[site.work.company].name}}]({{site.companies[site.work.company].url}})
|
||||
and a part-time freelance Web Developer, based in Wales, UK.
|
||||
|
||||
I’ve been a Developer since 2007 and specialise in using Drupal, Symfony and
|
||||
Vue.js, though I also use other technologies including Laravel and Sculpin. I
|
||||
use Ansible for server provisioning and application deployments.
|
||||
|
||||
I’m an [Acquia certified](https://certification.acquia.com/user/1647756) Drupal
|
||||
8 Grand Master, and Webmaster and Documentation Maintainer on
|
||||
[Drupal.org](https://www.drupal.org). I was previously a Developer for the
|
||||
[{{site.companies.drupal_association.name}}]({{site.companies.drupal_association.url}}).
|
||||
|
||||
I regularly [give talks](/talks) at user groups and conferences, and am an
|
||||
organiser of the
|
||||
[{{site.events.php_south_wales.name}}]({{site.events.php_south_wales.url}}) user
|
||||
group.
|
||||
|
||||
I enjoy contributing to open source, and maintain a variety of different
|
||||
projects. You can see these on my [Drupal.org]({{site.drupalorg.url}}) and
|
||||
[GitHub]({{site.github.url}}) accounts.
|
11
sculpin-old/source/_pages/accessibility.md
Normal file
11
sculpin-old/source/_pages/accessibility.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: Accessibility
|
||||
use: [posts]
|
||||
---
|
||||
|
||||
# Accessibility
|
||||
|
||||
I have made as much effort as possible to make this website accessible to all
|
||||
users.
|
||||
|
||||
If you have any suggestions to improve the site, please [contact me](/contact/).
|
14
sculpin-old/source/_pages/company-information.md
Normal file
14
sculpin-old/source/_pages/company-information.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: Company Information
|
||||
meta:
|
||||
description: 'Company information for Oliver Davies Ltd.'
|
||||
use: [posts]
|
||||
---
|
||||
|
||||
# Company Information
|
||||
|
||||
Company name : Oliver Davies Ltd (previously Oliver Davies Web Development Ltd)
|
||||
|
||||
Registered address : 3 Westfield Close, Caerleon, Newport, NP18 3ED
|
||||
|
||||
Company number : 8017706
|
8
sculpin-old/source/_pages/contact-thanks.md
Normal file
8
sculpin-old/source/_pages/contact-thanks.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: Thanks!
|
||||
permalink: contact/thanks/
|
||||
use: [posts]
|
||||
---
|
||||
|
||||
Your email has been sent. You should receive a response within the next working
|
||||
day.
|
42
sculpin-old/source/_pages/contact.html.twig
Normal file
42
sculpin-old/source/_pages/contact.html.twig
Normal file
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
title: Contact
|
||||
use: [posts]
|
||||
honeypot_field: Qr4W7oB25C
|
||||
---
|
||||
{% block content %}
|
||||
<div class="mt-2 mb-6">
|
||||
<p>To send me an email, complete the form below.</p>
|
||||
</div>
|
||||
|
||||
<form name="contact" method="POST" netlify-honeypot="{{ page.honeypot_field }}" data-netlify="true" class="spaced-y-4">
|
||||
<div class="spaced-y-1">
|
||||
<label for="name" class="block">Name</label>
|
||||
<input type="text" class="w-full p-2 border border--grey" id="name" name="name" required />
|
||||
</div>
|
||||
|
||||
<div class="spaced-y-1">
|
||||
<label for="email" class="block">Email</label>
|
||||
<input type="email" class="w-full p-2 border border--grey" id="email" name="email" required />
|
||||
</div>
|
||||
|
||||
<div class="spaced-y-1">
|
||||
<label for="subject" class="block">Subject</label>
|
||||
<input type="text" class="w-full p-2 border border--grey" id="subject" name="subject" required />
|
||||
</div>
|
||||
|
||||
<div class="spaced-y-1">
|
||||
<label for="message" class="block">Message</label>
|
||||
<textarea id="message" class="w-full p-2 border border--grey" rows="5" name="message" required></textarea>
|
||||
</div>
|
||||
|
||||
<p class="hidden">
|
||||
<label>Don’t fill this out if you're human: <input name="{{ page.honeypot_field }}" /></label>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<button class="button" type="submit">Send email</button>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="_next" value="{{ site.url }}/contact/thanks" />
|
||||
</form>
|
||||
{% endblock %}
|
28
sculpin-old/source/_pages/dev.md
Normal file
28
sculpin-old/source/_pages/dev.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
title: Developing this Website (WIP)
|
||||
---
|
||||
|
||||
This site is built with [Sculpin][], a static site generator written in PHP,
|
||||
that generates a static HTML website from Markdown files and Twig templates. You
|
||||
can see the [source code on GitHub].
|
||||
|
||||
## Extending Sculpin
|
||||
|
||||
- Custom Twig extensions
|
||||
- https://github.com/opdavies/sculpin-twig-markdown-bundle
|
||||
- https://github.com/opdavies/sculpin-gist-embed-bundle
|
||||
- https://github.com/opdavies/sculpin-content-generator-bundle
|
||||
|
||||
## Front End
|
||||
|
||||
On the front-end, I have used [PostCSS][] rather than Sass or Less, and
|
||||
[Tailwind CSS] for all of the styling, and some [Vue.js][] for the navbar and
|
||||
toggling the navigation menu on mobile. These are compiled with [Webpack
|
||||
Encore][] - a wrapper around Webpack, provided by Symfony.
|
||||
|
||||
[postcss]: https://postcss.org
|
||||
[sculpin]: https://sculpin.io
|
||||
[source code on github]: https://github.com/opdavies/oliverdavies.uk
|
||||
[tailwind css]: https://tailwindcss.com
|
||||
[vue.js]: https://vuejs.org
|
||||
[webpack encore]: https://github.com/symfony/webpack-encore
|
168
sculpin-old/source/_pages/opensource.html.twig
Normal file
168
sculpin-old/source/_pages/opensource.html.twig
Normal file
|
@ -0,0 +1,168 @@
|
|||
---
|
||||
title: Open Source
|
||||
projects:
|
||||
drupal:
|
||||
- name: Accessibility (a11y) Checklist
|
||||
description: Similar to the SEO Checklist and QA Checklist modules, this module provides a checklist of accessibilty-related modules and tasks to perform on a Drupal site.
|
||||
url: https://www.drupal.org/project/a11y_checklist
|
||||
versions: [7]
|
||||
|
||||
- name: Block ARIA Landmark Roles
|
||||
description: Inspired by Block Class, this module adds additional elements to the block configuration forms that allow users to assign a ARIA landmark role to a block.
|
||||
url: https://www.drupal.org/project/block_aria_landmark_roles
|
||||
versions: [7]
|
||||
|
||||
- name: Breadcrumbs by Path
|
||||
description: A light-weight module which simply builds breadcrumb trails based on a URL alias.
|
||||
url: https://www.drupal.org/project/breadcrumbs_by_path
|
||||
versions: [7]
|
||||
|
||||
- name: Collection class
|
||||
description: Integrates Laravel’s Collection class into Drupal 7.
|
||||
url: https://www.drupal.org/project/collection_class
|
||||
versions: [7]
|
||||
|
||||
- name: Comment Hide Subject
|
||||
description: Hides the comment subject if the "Allow comment title" checkbox is unchecked for that node type.
|
||||
url: https://www.drupal.org/project/comment_hide_subject
|
||||
versions: [7]
|
||||
|
||||
- name: Commerce Cart Empty Paths
|
||||
description: This module allows site administrators to define multiple paths, and when a user visits a non-declared page, their shopping cart is emptied.
|
||||
url: https://www.drupal.org/project/commerce_cart_empty_paths
|
||||
versions: [7]
|
||||
|
||||
- name: Copyright Block
|
||||
description: Creates a block that allows you to configure a copyright message that automatically updates using the current year from the server that your site is hosted on.
|
||||
url: https://www.drupal.org/project/copyright_block
|
||||
versions: [7, 8]
|
||||
|
||||
- name: Image Style Class
|
||||
description: Adds classes onto each rendered image showing the image style name.
|
||||
url: https://www.drupal.org/project/image_style_class
|
||||
versions: [7]
|
||||
|
||||
- name: Mapify
|
||||
description: A module to make the implementation of mapify.js into Drupal 7 relatively simple.
|
||||
url: https://www.drupal.org/project/mapify
|
||||
versions: [7]
|
||||
|
||||
- name: Node Comment Block
|
||||
description: This module moves the comments for a node into a moveable block.
|
||||
url: https://www.drupal.org/project/node_comment_block
|
||||
versions: [7]
|
||||
|
||||
- name: Null User
|
||||
description: Adds a NullUser class for simpler code with less conditions.
|
||||
url: https://www.drupal.org/project/null_user
|
||||
versions: [8]
|
||||
|
||||
- name: Override Node Options
|
||||
description: Allows permissions to be set to each field within the Authoring information and Publishing options field sets on the node form.
|
||||
url: https://www.drupal.org/project/override_node_options
|
||||
versions: [7, 8]
|
||||
|
||||
- name: Pathauto Menu Link
|
||||
description: Forces an update of the Pathauto-generated path when a menu link item has been updated. Useful if you use parent menu links to generate paths.
|
||||
url: https://www.drupal.org/project/pathauto_menu_link
|
||||
versions: [7, 8]
|
||||
|
||||
- name: Private Message Queue
|
||||
description: Adds the ability to queue private messages for sending.
|
||||
url: https://www.drupal.org/project/private_message_queue
|
||||
versions: [8]
|
||||
|
||||
- name: 'Rules Block/Unblock User'
|
||||
description: Adds rules events, as well as some default rules, for when a user account is blocked or unblocked.
|
||||
url: https://www.drupal.org/project/rules_block_user
|
||||
versions: [7]
|
||||
|
||||
- name: SpeakerDeck Field
|
||||
description: Adds a field for embedding content from SpeakerDeck.
|
||||
url: https://www.drupal.org/project/speakerdeck_field
|
||||
versions: [8]
|
||||
|
||||
- name: System User
|
||||
description: Adds the ability to identify and retrieve system users.
|
||||
url: https://www.drupal.org/project/system_user
|
||||
versions: [7, 8]
|
||||
|
||||
- name: Toggle Optional Fields
|
||||
description: Creates a simplified node form by allowing for the hiding and toggling of optional fields.
|
||||
url: https://www.drupal.org/project/toggle_optional_fields
|
||||
versions: [7]
|
||||
|
||||
- name: Webform ARIA
|
||||
description: Adds ARIA support to forms created using the Webform module.
|
||||
url: https://www.drupal.org/project/webform_aria
|
||||
versions: [7]
|
||||
|
||||
- name: WP Blog Migrate
|
||||
description: Convert your existing Drupal blog into the WP Blog module by re-assigning the content type for existing nodes and moving any existing tags into the WP Blog taxonomy vocabulary.
|
||||
url: https://www.drupal.org/project/wp_blog_migrate
|
||||
versions: [7]
|
||||
|
||||
sculpin:
|
||||
- name: oliverdavies.uk
|
||||
description: The source code for this website, which is built with Sculpin.
|
||||
url: https://github.com/opdavies/oliverdavies.uk
|
||||
|
||||
- name: Sculpin Content Generator Bundle
|
||||
description: Provides new Sculpin commands for generating new content.
|
||||
url: https://github.com/opdavies/sculpin-content-generator-bundle
|
||||
|
||||
- name: Sculpin Gist Bundle
|
||||
description: Allows for embedding GitHub Gists into a Sculpin site.
|
||||
url: https://github.com/opdavies/sculpin-gist-embed-bundle
|
||||
|
||||
- name: Sculpin Skeleton
|
||||
description: A skeleton project for the Sculpin static site generator.
|
||||
url: https://github.com/opdavies/sculpin-skeleton
|
||||
|
||||
- name: Sculpin Twig Markdown Bundle
|
||||
description: Enables rendering content from markdown, using Sculpin’s core markdown parser.
|
||||
url: https://github.com/opdavies/sculpin-twig-markdown-bundle
|
||||
|
||||
- name: Sculpin Twig Sort by Field Bundle
|
||||
description: Enables sorting an array by a certain field on each item.
|
||||
url: https://github.com/opdavies/sculpin-twig-sort-by-field-bundle
|
||||
|
||||
php:
|
||||
- name: Drupal Meetups Twitterbot
|
||||
description: A Twitter bot that retweets posts about Drupal meetups.
|
||||
url: https://github.com/opdavies/drupal-meetups-twitterbot
|
||||
|
||||
- name: Drupal VM CLI
|
||||
description: A command line tool for Drupal VM.
|
||||
url: https://github.com/opdavies/drupal-vm-cli
|
||||
active: false
|
||||
|
||||
- name: Gmail Filter Builder
|
||||
description: Allows you to define Gmail filters in PHP using a fluent API, and then generate XML that you can import into Gmail's filter settings.
|
||||
url: https://github.com/opdavies/gmail-filter-builder
|
||||
---
|
||||
<p>I have written, open-sourced, and maintain a number of Drupal modules, Sculpin bundles and PHP projects - as well as the source code for this website! Here is some information about each project and a link to it’s project page.</p>
|
||||
|
||||
<div class="mb-4">
|
||||
<h2>Drupal</h2>
|
||||
|
||||
{% include 'opensource/projects' with {
|
||||
projects: page.projects.drupal
|
||||
} %}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<h2>Sculpin</h2>
|
||||
|
||||
{% include 'opensource/projects' with {
|
||||
projects: page.projects.sculpin
|
||||
} %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>PHP</h2>
|
||||
|
||||
{% include 'opensource/projects' with {
|
||||
projects: page.projects.php
|
||||
} %}
|
||||
</div>
|
99
sculpin-old/source/_pages/podcasts.html.twig
Normal file
99
sculpin-old/source/_pages/podcasts.html.twig
Normal file
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
title: Podcasts
|
||||
podcasts:
|
||||
howtocodewell:
|
||||
name: How to Code Well
|
||||
url: https://howtocodewell.fm
|
||||
talking_drupal:
|
||||
name: Talking Drupal
|
||||
url: http://talkingdrupal.com
|
||||
thatpodcast:
|
||||
name: That Podcast
|
||||
url: https://thatpodcast.io
|
||||
episodes:
|
||||
- title: 'Episode #175 - Automated Testing'
|
||||
description: I joined the Talking Drupal team to discuss automated testing.
|
||||
podcast: talking_drupal
|
||||
date: 2018-09-05
|
||||
url: http://talkingdrupal.com/175
|
||||
audio: true
|
||||
|
||||
- title: 'Episode #204 - A Few Things'
|
||||
description: I joined the Talking Drupal team again, where we discussed Drupal updates, conferences and more.
|
||||
podcast: talking_drupal
|
||||
date: 2019-03-25
|
||||
url: http://talkingdrupal.com/204
|
||||
audio: true
|
||||
|
||||
- title: What's new in Drupal 8+
|
||||
description: Talking about Drupal, open source, Drupal 7 vs Drupal 8, module development, testing and more.
|
||||
podcast: howtocodewell
|
||||
date: 2019-04-12
|
||||
url: https://howtocodewell.fm/episode/15-whats-new-in-drupal-8-plus-oliver-davies-interview
|
||||
audio: true
|
||||
video: true
|
||||
|
||||
- title: 'Episode 60 - The One Where We Talk Twig, Drupal, and Sculpin'
|
||||
description: I joined Beau and Dave to talk about various topics including Drupal, the Sculpin static site generator, Twig, Tailwind CSS and Symfony.
|
||||
podcast: thatpodcast
|
||||
date: 2019-07-01
|
||||
url: https://thatpodcast.io/episodes/episode-60-the-one-where-we-talk-twig-drupal-and-sculpin-with-oliver-davies
|
||||
audio: true
|
||||
|
||||
- title: Static Site Generators
|
||||
description: |
|
||||
Talking again with Peter Fisher, this time about static site generators. We talk about what they are, when you should and shouldn’t use them, and how to use them with a content management system like Drupal.
|
||||
podcast: howtocodewell
|
||||
date: 2019-07-19
|
||||
url: https://howtocodewell.fm/episode/29-what-are-static-site-generators-oliver-davies
|
||||
audio: true
|
||||
video: true
|
||||
---
|
||||
{% macro episodeTitle(podcast, episode) -%}
|
||||
{{ podcast.name }}: {{ episode.title }}
|
||||
{%- endmacro %}
|
||||
|
||||
{% import _self as helpers %}
|
||||
|
||||
<header class="markup">
|
||||
<p class="text-lg">As well as <a href="{{ site.menus.main.articles.href }}">writing posts</a> and <a href="{{ site.menus.main.talks.href }}">giving talks</a>, I also enjoy being on podcasts and speaking about interesting topics.</p>
|
||||
<p class="text-lg">Here are the podcasts that I’ve been on, including <a href="{{ page.podcasts.talking_drupal.url }}">{{ page.podcasts.talking_drupal.name }}</a> and <a href="{{ page.podcasts.howtocodewell.url }}">{{ page.podcasts.howtocodewell.name }}</a>.</p>
|
||||
</header>
|
||||
|
||||
<div class="mt-10 spaced-y-10">
|
||||
{% for episode in page.episodes|reverse %}
|
||||
{% set podcast = page.podcasts[episode.podcast] %}
|
||||
|
||||
<article>
|
||||
<h2 class="text-lg">
|
||||
{% if episode.url %}
|
||||
<a href="{{ episode.url }}" class="text-inherit" tabindex="-1">
|
||||
{{ helpers.episodeTitle(podcast, episode) }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ helpers.episodeTitle(podcast, episode) }}
|
||||
{% endif %}
|
||||
</h2>
|
||||
|
||||
<time class="text-sm text-gray-800 block mt-px mb-2" datetime="{{ episode.date|date('Y-m-d') }}">
|
||||
{{ episode.date|date('jS F Y') }}
|
||||
</time>
|
||||
|
||||
<div class="markup">
|
||||
{{ episode.description|markdown }}
|
||||
</div>
|
||||
|
||||
{% if episode.url %}
|
||||
<a href="{{ episode.url }}" class="inline-block mt-2 text-sm text-gray-600 hover:text-gray-800 focus:text-gray-800">
|
||||
{% if episode.video %}
|
||||
Watch {{ episode.video and episode.audio ? 'or listen to' }}
|
||||
{% elseif episode.audio %}
|
||||
Listen to
|
||||
{% endif %} this
|
||||
<span class="visuallyhidden">{{ podcast.name }}</span>
|
||||
episode →
|
||||
</a>
|
||||
{% endif %}
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
57
sculpin-old/source/_pages/projects.html.twig
Normal file
57
sculpin-old/source/_pages/projects.html.twig
Normal file
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
title: Projects
|
||||
projects:
|
||||
- title: Rebuilding Acquia
|
||||
description: A clone of Acquia’s hosting dashboard, built with Vue.js and Tailwind CSS.
|
||||
url: https://github.com/opdavies/rebuilding-acquia
|
||||
|
||||
- title: Rebuilding Bartik
|
||||
description: A clone of Bartik (Drupal’s default theme), built with Vue.js and Tailwind CSS.
|
||||
url: https://github.com/opdavies/rebuilding-bartik
|
||||
|
||||
- title: Joind.in Winner Picker
|
||||
description: A Symfony 4 application that determines a randomly selected winner from a section of feedback left on Joind.in. Developed on behalf of the PHP South West (PHPSW) user group.
|
||||
url: https://github.com/opdavies/joindin-winner-picker-new
|
||||
|
||||
- title: Drupal.org API library
|
||||
description: A PHP library for interacting with the Drupal.org API.
|
||||
url: https://github.com/opdavies/drupalorg-api-php
|
||||
|
||||
- title: Drupal Meetups Twitterbot
|
||||
description: A Symfony 4 application for retweeting tweets about Drupal meetups.
|
||||
url: https://github.com/opdavies/sculpin-skeleton
|
||||
|
||||
- title: Gmail Filter Builder
|
||||
description: A library for writing Gmail filters in PHP, then exporting them to importable XML.
|
||||
url: https://github.com/opdavies/gmail-filter-builder
|
||||
|
||||
- title: Sculpin Skeleton
|
||||
description: A skeleton project for the Sculpin static site generator.
|
||||
url: https://github.com/opdavies/sculpin-skeleton
|
||||
---
|
||||
<div class="spaced-y-4">
|
||||
<p class="lead">Here are some of the open source projects that I’ve written or currently maintain.</p>
|
||||
<p class="lead">For the full list, see my <a href="{{ site.github.url }}">GitHub</a> and <a href="{{ site.drupalorg.url_new }}">Drupal.org</a> profiles.</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 spaced-y-10">
|
||||
{% for project in page.projects %}
|
||||
<article>
|
||||
<h2 class="text-lg">
|
||||
<a class="text-inherit" href="{{ project.url }}" tabindex="-1">
|
||||
{{ project.title }}
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
<p class="mt-1">{{ project.description }}</p>
|
||||
|
||||
<p class="mt-2 text-sm">
|
||||
<a class="text-gray-600 hover:text-black" href="{{ project.url }}">
|
||||
Find out more
|
||||
<span class="visuallyhidden">about {{ project.title }}</span>
|
||||
→
|
||||
</a>
|
||||
</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
47
sculpin-old/source/_pages/speaker-info.md
Normal file
47
sculpin-old/source/_pages/speaker-info.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
---
|
||||
|
||||
<div class="markup" markdown="1">
|
||||
# Speaker Info
|
||||
|
||||
## Bio
|
||||
|
||||
[Oliver Davies][website] ([@opdavies][twitter]) has been building websites since
|
||||
2007, and speaking at meetups and conferences since 2012. He is a Full Stack
|
||||
Developer and an Acquia certified Drupal Grand Master, who also has experience
|
||||
developing with Symfony, Laravel, Sculpin and Vue.js, as well as with DevOps and
|
||||
systems administration.
|
||||
|
||||
He is a {{ site.work.role }} at
|
||||
[{{ site.companies[site.work.company].name }}][work], a Drupal core contributor
|
||||
and mentor, and an open source and contribution advocate.
|
||||
|
||||
He regularly blogs and gives talks on various topics, maintains and contributes
|
||||
to various open source projects, and co-organises the PHP South Wales user
|
||||
group.
|
||||
|
||||
[twitter]: {{site.twitter.url}}
|
||||
[website]: {{site.url}}
|
||||
|
||||
[work]: {{site.companies[site.work.company].url}}
|
||||
|
||||
## Photos
|
||||
|
||||
- <https://www.dropbox.com/s/den3ww3lpve08fa/precedent_thumb.jpg>
|
||||
- <https://www.dropbox.com/s/etrahx3hq2vpqcb/phpnw17.png>
|
||||
|
||||
## Some Events I’ve Spoken At
|
||||
|
||||
- BlueConf 2019
|
||||
- DrupalCamp Brighton 2015
|
||||
- DrupalCamp Bristol 2016
|
||||
- DrupalCamp Dublin 2017
|
||||
- DrupalCamp London (2014, 2015, 2016, 2017, 2019)
|
||||
- DrupalCamp North 2015
|
||||
- DrupalCon Amsterdam 2019
|
||||
- Nomad PHP
|
||||
- PHP North West 2017 (10 year anniversary)
|
||||
- PHP South Coast 2016
|
||||
- PHP UK Conference 2018
|
||||
- WordCamp Bristol 2019
|
||||
</div>
|
18
sculpin-old/source/_pages/stream.md
Normal file
18
sculpin-old/source/_pages/stream.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: Live Streaming
|
||||
---
|
||||
|
||||
I like try and do some live coding when I can, whether I’m working on a side
|
||||
project, my own website or some open source code. You can find my [stream link
|
||||
on YouTube][0], or view some previous streams below:
|
||||
|
||||
## Stream Playlists
|
||||
|
||||
- [Miscellaneous coding live streams][1] {# - [Building the PHP South Wales
|
||||
website][2] #} {# - [Using PHP to generate Gmail Filter XML with the Gmail
|
||||
Filter Builder][3] #}
|
||||
|
||||
[0]: {{site.youtube.stream.url}}
|
||||
[1]: https://www.youtube.com/playlist?list=PLHn41Ay7w7kcWbjrYaiqXlfrqi_teTJE8
|
||||
[2]: https://www.youtube.com/playlist?list=PLHn41Ay7w7keUxBa-6TNn0_xz27ZfLD5f
|
||||
[3]: https://www.youtube.com/playlist?list=PLHn41Ay7w7keRp47rXEx9his-oA8kk4-v
|
47
sculpin-old/source/_pages/talks.html.twig
Normal file
47
sculpin-old/source/_pages/talks.html.twig
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
title: Talks
|
||||
meta:
|
||||
description: 'Information about previous and upcoming talks that Oliver has presented at conferences and user groups'
|
||||
use: [talks]
|
||||
talks:
|
||||
- title: 'Drupal and the LDAP module'
|
||||
description: A review and demonstration of some of the recent single sign-on work that I did using Drupal’s LDAP module.
|
||||
events:
|
||||
- event: swdug
|
||||
date: 2013-07-10
|
||||
|
||||
- title: 'About the Drupal Association'
|
||||
description: An impromptu talk about what the Drupal Association is, and what work I’ve been doing since I joined the Association staff.
|
||||
events:
|
||||
- event: swdug
|
||||
date: 2014-08-19
|
||||
|
||||
- title: 'Automated testing and Test Driven Development in Drupal 8'
|
||||
description: A workshop that I gave about automated testing and test driven development in Drupal 8.
|
||||
type: Workshop
|
||||
events:
|
||||
- event: drupal_bristol
|
||||
date: 2018-06-27
|
||||
- event: drupalcamp_london_20
|
||||
date: 2020-03-13
|
||||
---
|
||||
{% block content %}
|
||||
{% set talks = page.talks|merge(data.talks) %}
|
||||
|
||||
<header>
|
||||
<p class="lead">
|
||||
After giving my first talk in September 2012, I have now given {{ get_past_talk_count(talks) }} presentations at various conferences and meetups,
|
||||
on topics including PHP, Drupal, Git, CSS and systems administration.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="spaced-y-10 mt-6">
|
||||
{% include 'talks/upcoming' with {
|
||||
talks: get_upcoming_talks(talks),
|
||||
} only %}
|
||||
|
||||
{% include 'talks/past' with {
|
||||
talks: get_past_talks(talks),
|
||||
} only %}
|
||||
</div>
|
||||
{% endblock %}
|
8
sculpin-old/source/_pages/terms.md
Normal file
8
sculpin-old/source/_pages/terms.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: Development Terms
|
||||
use: [posts]
|
||||
---
|
||||
|
||||
# Development Terms
|
||||
|
||||
TODO
|
61
sculpin-old/source/_pages/test-driven-drupal.md
Normal file
61
sculpin-old/source/_pages/test-driven-drupal.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
title: 'Test Driven Drupal: The Book'
|
||||
mailchimp_url: 'https://oliverdavi.us18.list-manage.com/subscribe/post?u=b4ac8dd177796d37b93f9c285&id=033c84e0d5'
|
||||
contact_email: 'oliver@testdrivendrupal.com'
|
||||
---
|
||||
|
||||
{% block content %}
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<div class="markup spaced-y-4 mb-6" markdown="1">
|
||||
Having [given talks][1] and [workshops][workshop], been a guest on podcasts and [written articles][0] about automated testing in Drupal, I’m currently in the planning phase of a book and potentially some accompanying screencasts about it, focussing on Drupal 8.
|
||||
|
||||
I’m still thinking about what use-cases to cover and examples to include, but
|
||||
here are some of the things I’m considering:
|
||||
|
||||
- What things to test, and what not to test
|
||||
- The different types of available tests, and when to use each
|
||||
- How to write testable code
|
||||
- What happens when I run a test?
|
||||
- How to run tests in the Drupal UI
|
||||
- How to run tests with the `run-tests.sh` script
|
||||
- How to install, configure and run tests with PHPUnit in Drupal 8
|
||||
- Viewing HTML from run tests
|
||||
- How to write your first test
|
||||
- Debugging tests
|
||||
- How to organise your test files
|
||||
- Selecting the right base class and using test traits
|
||||
- Writing your own base test classes, traits and assertions
|
||||
- Managing dependencies for your tests (fields, configuration)
|
||||
- Creating users, checking access with roles and permissions
|
||||
- Creating pages and blocks with Views and testing the output
|
||||
- Creating pages with routes and controllers and testing the output
|
||||
- Testing custom plugins
|
||||
- Testing queuing items and processing queues
|
||||
- Testing sending emails
|
||||
- Testing custom Twig filters and functions
|
||||
<!-- - Testing data migrations -->
|
||||
<!-- - Building and testing APIs using RESTful web services module -->
|
||||
- Running tests as part of your continuous integration pipeline
|
||||
|
||||
I’ll most likely be publishing it via Leanpub, and will be sending free
|
||||
chapters, early-bird discounts and links to screencasts and blog posts as I
|
||||
write the book to subscribers of the mailing list.
|
||||
|
||||
If you have questions or would like to suggest something for me to include in
|
||||
the book, please <a href="mailto:{{ page.contact_email }}">contact me</a>.
|
||||
|
||||
**Enter your email address to subscribe to the Test Driven Drupal mailing list
|
||||
and be notified of any updates.**
|
||||
|
||||
</div>
|
||||
|
||||
{% include 'pages/book/signup-form' %}
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
{% endblock %}
|
||||
|
||||
[0]: /articles/tags/testing
|
||||
[1]: /talks/tdd-test-driven-drupal
|
||||
[2]: /contact
|
||||
[workshop]: https://web.archive.org/web/20200422110605/https://drupalcamp.london/training/Automated-Testing-and-Test-Driven-Development-in-Drupal-8
|
100
sculpin-old/source/_pages/testimonials.html.twig
Normal file
100
sculpin-old/source/_pages/testimonials.html.twig
Normal file
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
title: Testimonials
|
||||
use: [posts]
|
||||
testimonials:
|
||||
- name: Ed Welsby
|
||||
image: ed-welsby.png
|
||||
role: Senior Developer at <a class="text-blue-600 inline-block" href="http://www.proctors.co.uk">Proctor & Stevenson</a>
|
||||
text: |
|
||||
Oliver was great to work with, he has a solid knowledge of the various aspects of web development and never minded helping me out with Linux commands!
|
||||
|
||||
- name: Brian Healy
|
||||
image: brian-healy.png
|
||||
role: Director of Business Development at <a class="text-blue-600 inline-block" href="http://tincan.co.uk">Tincan</a>.
|
||||
text: |
|
||||
Oliver was fantastic to work with - pro-active and highly responsive, he worked well remotely and as part of a project team. His understanding of the project requirement(s) and ability to translate it into working code was essential and he delivered.
|
||||
|
||||
- name: Marlon Duncanson
|
||||
role: 'Brand & Web Specialist'
|
||||
text: |
|
||||
Oliver is a great guy and really easy to work with. He really goes the extra mile to make sure the project is done properly. I would recommend him and will not hesitate to use him again in future.
|
||||
|
||||
- name: Brian Hartwell
|
||||
role: Interactive Creative Director
|
||||
text: |
|
||||
Oliver was great to work with. He has expert knowledge with Drupal and delivered exactly what we were looking for on time. He's understanding, friendly and easy to get along with. I would enjoy working with him again in the future.
|
||||
|
||||
- name: Daniel Easterbrook
|
||||
role: Digital Strategy Consultant
|
||||
text: |
|
||||
Oliver is seasoned Drupal and all round highly skilled and experienced web developer. I have worked with Oliver on an important project where he was reliable, prompt and ensured strict client deadline delivery and confidentiality at all times.
|
||||
|
||||
- name: James Chapman
|
||||
role: Director at <a class="text-blue-600 inline-block" href="http://www.developmentdoneright.co.uk">Development Done Right</a>
|
||||
image: james-chapman.png
|
||||
text: |
|
||||
We used Oliver on a number of occasions throughout 2012 and I have to say we've been delighted with his work. His skills working with Drupal are excellent particularly with custom module development and we wouldn’t hesitate to recommend him others.
|
||||
|
||||
- name: Léonie Watson
|
||||
role: >
|
||||
Director of Accessibility at <a class="text-blue-600 inline-block" href="http://www.nomensa.com">Nomensa</a>
|
||||
image: leonie-watson.jpg
|
||||
text: |
|
||||
Oliver is a flexible and hardworking developer, with a terrific knowledge of Drupal. He promotes accessibility best practice within the Drupal community, and is always happy to share his knowledge with other people.
|
||||
|
||||
- name: Holly Ross
|
||||
role: >
|
||||
Executive Director at the <a class="text-blue-600 inline-block" href="https://assoc.drupal.org">Drupal Association</a>
|
||||
image: holly-ross.png
|
||||
text: |
|
||||
Oliver has been an outstanding contributor to the Drupal Association team. He is a talented developer who writes great code and applies his curiosity and love of learning to every project. He is also a fantastic team member, who gives to the team as much as he gets.
|
||||
|
||||
Oliver is the embodiment of everything good about the Drupal community.
|
||||
|
||||
- name: Josh Mitchell
|
||||
role: CTO at the <a class="text-blue-600 inline-block" href="https://assoc.drupal.org">Drupal Association</a>
|
||||
image: josh-mitchell.png
|
||||
text: |
|
||||
Oliver is a skilled Drupal developer with a passion for the Drupal community. As his direct supervisor, I was able to watch Oliver grow with the Drupal Association and contribute an amazing amount of effort and integrity to all of his work.
|
||||
|
||||
Everything we have thrown at Oliver, he has approached with an open and flexible mind that has allowed him to work on a wide range of projects and features for Drupal products.
|
||||
|
||||
- name: Chris Jarvis
|
||||
image: chris-jarvis.jpg
|
||||
role: Developer at <a class="text-blue-600 inline-block" href="https://microserve.io">Microserve</a>
|
||||
text: |
|
||||
Oliver is an amazing colleague, he's professional, full of knowledge and I could not recommend him more.
|
||||
---
|
||||
{% block content %}
|
||||
<div class="spaced-y-12">
|
||||
{% for testimonial in page.testimonials|reverse %}
|
||||
<article class="flex flex-row-reverse items-center">
|
||||
{% if testimonial.image %}
|
||||
<div class="flex-none">
|
||||
<img
|
||||
src="{{ site.url }}/images/testimonials/{{ testimonial.image }}"
|
||||
alt="{{ testimonial.name }}"
|
||||
class="with-border with-padding rounded-full w-16 h-16 ml-4"
|
||||
>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="mb-3">
|
||||
<h2 class="mb-1 leading-none">{{ testimonial.name }}</h2>
|
||||
|
||||
{% if testimonial.role %}
|
||||
<div class="text-gray-600 text-sm">
|
||||
{{ testimonial.role|raw }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="markup">
|
||||
{{ testimonial.text|markdown }}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
11
sculpin-old/source/_partials/about/availability.html.twig
Normal file
11
sculpin-old/source/_partials/about/availability.html.twig
Normal file
|
@ -0,0 +1,11 @@
|
|||
<div class="widget">
|
||||
<h2 class="mb-2">Availability</h2>
|
||||
|
||||
<ul class="list-disc pl-5">
|
||||
{% for type, value in site.availability %}
|
||||
<li>
|
||||
{% include 'about/availability/' ~ value %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
Currently have limited {{ type }}-time capacity.
|
|
@ -0,0 +1 @@
|
|||
Currently no spare {{ type }}-time capacity.
|
|
@ -0,0 +1 @@
|
|||
Currently have available {{ type }}-time capacity.
|
60
sculpin-old/source/_partials/about/badges.html.twig
Normal file
60
sculpin-old/source/_partials/about/badges.html.twig
Normal file
|
@ -0,0 +1,60 @@
|
|||
<aside class="block sm:flex md:block -mx-4">
|
||||
<div class="px-4">
|
||||
<section class="widget">
|
||||
<div class="max-w-xs md:max-w-full mx-auto">
|
||||
<a href="https://assoc.drupal.org/membership" title="I’m a Drupal Association member." class="block mb-4 px-4 md:px-0 mx-10">
|
||||
<img src="/images/badges/da-individual-member.png" alt="Drupal Association Individual Member"/>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="px-4">
|
||||
<section class="widget bg-gray-300 p-4 rounded">
|
||||
<div class="-mb-3">
|
||||
<h2 class="visuallyhidden">Certifications</h2>
|
||||
|
||||
{% include 'about/certification' with {
|
||||
title: 'Acquia certified Grand Master - Drupal 8',
|
||||
url: 'http://certification.acquia.com/user/1647756',
|
||||
image: {
|
||||
filename: 'acquia-d8-grand-master.png',
|
||||
alt: 'Acquia Certified Grand Master - Drupal 8 Exam Badge',
|
||||
},
|
||||
} %}
|
||||
|
||||
{% include 'about/certification' with {
|
||||
title: 'Acquia certified Developer - Drupal 8',
|
||||
image: {
|
||||
filename: 'acquia-d8-developer.png',
|
||||
alt: 'Acquia Certified Developer - Drupal 8 Exam Badge',
|
||||
},
|
||||
} %}
|
||||
|
||||
{% include 'about/certification' with {
|
||||
title: 'Acquia certified Back End Specialist - Drupal 8',
|
||||
image: {
|
||||
filename: 'acquia-d8-back-end.png',
|
||||
alt: 'Acquia Certified Back End Specialist - Drupal 8 Exam Badge',
|
||||
},
|
||||
} %}
|
||||
|
||||
{% include 'about/certification' with {
|
||||
title: 'Acquia certified Front End Specialist - Drupal 8',
|
||||
image: {
|
||||
filename: 'acquia-d8-front-end.png',
|
||||
alt: 'Acquia Certified Front End Specialist - Drupal 8 Exam Badge',
|
||||
},
|
||||
} %}
|
||||
|
||||
{% include 'about/certification' with {
|
||||
title: 'Acquia certified Cloud Pro',
|
||||
image: {
|
||||
filename: 'acquia-cloud-pro.png',
|
||||
alt: 'Acquia Certified Cloud Pro Exam Badge',
|
||||
},
|
||||
} %}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</aside>
|
17
sculpin-old/source/_partials/about/certification.html.twig
Normal file
17
sculpin-old/source/_partials/about/certification.html.twig
Normal file
|
@ -0,0 +1,17 @@
|
|||
<div class="flex mb-3">
|
||||
<div class="w-16 mr-2 flex-none">
|
||||
<div class="flex flex-col items-center">
|
||||
<img src="/images/badges/{{ image.filename }}" alt="{{ image.alt }}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center ml-px">
|
||||
<p class="text-sm mb-0 mr-1">
|
||||
{% if url %}
|
||||
<a href="{{ url }}" class="no-underline hover:underline focus:underline">{{ title }}</a>
|
||||
{% else %}
|
||||
{{ title }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
31
sculpin-old/source/_partials/blog/post-summary.html.twig
Normal file
31
sculpin-old/source/_partials/blog/post-summary.html.twig
Normal file
|
@ -0,0 +1,31 @@
|
|||
<div>
|
||||
<div>
|
||||
<h2 class="text-inherit text-lg m-0 leading-snug">
|
||||
<a
|
||||
href="{{ post.external_url ?: post.url }}"
|
||||
class="text-inherit"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{ post.draft ? 'Draft: '|upper }}{{ post.title }}
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
{% if show_date %}
|
||||
{% include 'posts/post-date' with { class: 'mt-1 text-sm block' } %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<p>{{ post.excerpt }}</p>
|
||||
</div>
|
||||
|
||||
{% if not post.type == 'tweet' %}
|
||||
<div class="mt-1">
|
||||
<a href="{{ post.url }}" class="text-sm text-gray-600 hover:text-gray-800">
|
||||
Read more
|
||||
<span class="visuallyhidden">about '{{ post.title }}'</span>
|
||||
→
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
7
sculpin-old/source/_partials/cv/certifications.html.twig
Normal file
7
sculpin-old/source/_partials/cv/certifications.html.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
<h2>Certifications</h2>
|
||||
|
||||
<ul>
|
||||
{% for certification in page.certifications %}
|
||||
<li>{{ certification.title }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
21
sculpin-old/source/_partials/cv/experience.html.twig
Normal file
21
sculpin-old/source/_partials/cv/experience.html.twig
Normal file
|
@ -0,0 +1,21 @@
|
|||
<h2>Experience</h2>
|
||||
|
||||
<ul class="list-reset">
|
||||
{% for item in page.experience %}
|
||||
<li class="mb-12">
|
||||
{% if site.companies[item.company].url %}
|
||||
<h3><a href="{{ site.companies[item.company].url }}" class="text-inherit no-underline hover:underline focus:bg-gray-900">{{ site.companies[item.company].name }}</a></h3>
|
||||
{% else %}
|
||||
<h3>{{ site.companies[item.company].name }}</h3>
|
||||
{% endif %}
|
||||
|
||||
{% for role in item.roles %}
|
||||
<div>
|
||||
<h4 class="mb-3 text-gray-800">{{ role.title }} from {{ role.from }} to {{ role.to ?: 'present' }} ({{ role.location }})</h4>
|
||||
|
||||
{{ role.description|markdown }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
8
sculpin-old/source/_partials/figure.html.twig
Normal file
8
sculpin-old/source/_partials/figure.html.twig
Normal file
|
@ -0,0 +1,8 @@
|
|||
<figure class="block">
|
||||
<img src="{{ image.src }}" alt="{{ image.alt }}" class="p-1 border">
|
||||
{% if caption %}
|
||||
<figcaption class="mt-1 mb-0 italic text-sm text-center text-gray-800">
|
||||
{{ caption }}
|
||||
</figcaption>
|
||||
{% endif %}
|
||||
</figure>
|
17
sculpin-old/source/_partials/layout/footer.html.twig
Normal file
17
sculpin-old/source/_partials/layout/footer.html.twig
Normal file
|
@ -0,0 +1,17 @@
|
|||
<footer class="border-gray-300 border-t mb-8 mt-10 pt-5 text-sm">
|
||||
<div class="md:flex md:justify-between">
|
||||
<div>
|
||||
<p>© 2010-{{ 'now'|date('Y') }} {{ site.title }}</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 md:mt-0">
|
||||
<p>
|
||||
Built with <a href="https://sculpin.io">Sculpin</a>,
|
||||
<a href="https://vuejs.org">Vue.js</a>
|
||||
and <a href="https://tailwindcss.com">Tailwind CSS</a>,
|
||||
stored on <a href="https://github.com/opdavies/oliverdavies.uk">GitHub</a>
|
||||
and hosted on <a href="https://www.netlify.com">Netlify</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
47
sculpin-old/source/_partials/layout/navbar.html.twig
Normal file
47
sculpin-old/source/_partials/layout/navbar.html.twig
Normal file
|
@ -0,0 +1,47 @@
|
|||
<div id="nav" class="bg-blue-500 border-b-3 border-blue-700">
|
||||
<div class="container">
|
||||
<div class="md:flex" x-data="{ isOpen: false }">
|
||||
<div class="w-full flex items-center md:w-1/3 lg:w-1/4">
|
||||
<div class="w-3/4 py-6">
|
||||
<div class="text-sm">
|
||||
<a href="{{ site.menus.main.about.href }}" class="font-semibold text-white" tabindex="-1">
|
||||
{{ site.title }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-1/4 text-right flex items-center justify-end md:hidden">
|
||||
<button
|
||||
type="button"
|
||||
class="nav-toggle appearance-none text-white hover:text-gray-300 focus:outline-none"
|
||||
aria-label="Toggle main menu"
|
||||
@click="isOpen = !isOpen"
|
||||
>
|
||||
<svg class="js-hidden fill-current text-inherit w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path x-show="!isOpen" d="M16.4 9H3.6c-.552 0-.6.447-.6 1 0 .553.048 1 .6 1h12.8c.552 0 .6-.447.6-1 0-.553-.048-1-.6-1zm0 4H3.6c-.552 0-.6.447-.6 1 0 .553.048 1 .6 1h12.8c.552 0 .6-.447.6-1 0-.553-.048-1-.6-1zM3.6 7h12.8c.552 0 .6-.447.6-1 0-.553-.048-1-.6-1H3.6c-.552 0-.6.447-.6 1 0 .553.048 1 .6 1z"/>
|
||||
<path x-show="isOpen" d="M10 8.586L2.929 1.515 1.515 2.929 8.585 10l-7.07 7.071 1.414 1.414L10 11.415l7.071 7.07 1.414-1.414L11.415 10l7.07-7.071-1.414-1.414L10 8.585z" fill-rule="evenodd"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav
|
||||
class="mt-2px -mx-4 py-4 bg-blue-700 md:mt-0 md:mx-0 md:relative md:flex md:flex-wrap md:flex-1 md:justify-end md:bg-blue-500 md:border-b-0"
|
||||
:class="[ isOpen ? 'block' : 'hidden' ]"
|
||||
>
|
||||
{% for item in site.menus.main %}
|
||||
{% set currentPage = page.url matches '#' ~ item.pattern ~ '#' %}
|
||||
<a
|
||||
class="mx-4 mt-1 -mb-px py-3 px-4 block rounded text-sm text-white no-underline hover:underline focus:outline-none focus:underline md:py-1 md:px-2 md:mx-2 md:mt-0 md:mr-0 {{ currentPage ? 'cursor-default bg-blue-500 hover:border-blue-600 hover:no-underline md:bg-blue-700' }}"
|
||||
href="{{ item.href }}"
|
||||
{% if currentPage %}aria-current="page"{% endif %}
|
||||
>
|
||||
<span class="flex items-center h-full">
|
||||
{{- item.title -}}
|
||||
</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
20
sculpin-old/source/_partials/meta.html.twig
Normal file
20
sculpin-old/source/_partials/meta.html.twig
Normal file
|
@ -0,0 +1,20 @@
|
|||
{% macro canonicalUrl(site, page) -%}
|
||||
{{ site.url }}
|
||||
{{- page.url != '//' ? page.url }}
|
||||
{%- endmacro %}
|
||||
|
||||
{% import _self as helpers %}
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ site.title }} RSS" href="{{ site.url }}/feed">
|
||||
<link rel="canonical" href="{{ helpers.canonicalUrl(site, page) }}">
|
||||
|
||||
{% if page.meta.description %}
|
||||
<meta name="description" content="{{ page.meta.description|e('html') }}">
|
||||
{% else %}
|
||||
<meta name="description" content="Oliver Davies is a UK-based Web Developer and System Administrator. He is a {{ site.work.role }} at {{ site.companies[site.work.company].name }} and a part-time freelancer, specialising in Drupal and PHP.">
|
||||
{% endif %}
|
||||
|
||||
{% include 'og' with { og: page.meta.og } %}
|
40
sculpin-old/source/_partials/og.html.twig
Normal file
40
sculpin-old/source/_partials/og.html.twig
Normal file
|
@ -0,0 +1,40 @@
|
|||
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
|
||||
<meta property="og:title" content="{{ og.title ? og.title|raw : page.title|raw }}"/>
|
||||
|
||||
{% if og.description %}
|
||||
<meta property="og:description" content="{{ og.description|raw }}"/>
|
||||
{% endif %}
|
||||
|
||||
{% if og.type %}
|
||||
<meta property="og:type" content="{{ og.type }}"/>
|
||||
{% endif %}
|
||||
|
||||
{% if page.image.url %}
|
||||
<meta property="og:image" content="{{ site.url }}{{ page.image.url }}"/>
|
||||
{% if page.image.type %}
|
||||
<meta property="og:image:type" content="{{ page.image.type }}"/>
|
||||
{% endif %}
|
||||
{% if page.image.width %}
|
||||
<meta property="og:image:width" content="{{ page.image.width }}"/>
|
||||
{% endif %}
|
||||
{% if page.image.height %}
|
||||
<meta property="og:image:height" content="{{ page.image.height }}"/>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<meta property="og:image" content="{{ site.url }}{{ site.avatar.url }}"/>
|
||||
<meta property="og:image:height" content="327"/>
|
||||
<meta property="og:image:type" content="image/jpg">
|
||||
<meta property="og:image:width" content="327"/>
|
||||
{% endif %}
|
||||
|
||||
{% if page.image %}
|
||||
<meta name="twitter:card" content="summary_large_image"/>
|
||||
<meta name="twitter:image" content="{{ site.url }}{{ page.image.url }}">
|
||||
{% else %}
|
||||
<meta name="twitter:card" content="summary"/>
|
||||
<meta name="twitter:image" content="{{ site.url }}{{ site.avatar.url }}">
|
||||
{% endif %}
|
||||
|
||||
{% if page.hide_page %}
|
||||
<meta name="robots" content="no-index, no-follow">
|
||||
{% endif %}
|
27
sculpin-old/source/_partials/opensource/projects.html.twig
Normal file
27
sculpin-old/source/_partials/opensource/projects.html.twig
Normal file
|
@ -0,0 +1,27 @@
|
|||
<div class="md:flex md:flex-wrap md:-mx-2">
|
||||
{% for project in projects %}
|
||||
<div class="md:w-1/2 md:px-2 mb-4 flex">
|
||||
<div class="border p-3 w-full flex flex-col {{ project.versions ? 'justify-between' }}">
|
||||
<h3>
|
||||
<a href="{{ project.url }}" class="text-black no-underline hover:underline focus:underline">
|
||||
{{ project.name }}
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<div class="markup flex-1">
|
||||
{{ project.description }}
|
||||
</div>
|
||||
|
||||
{% if project.versions %}
|
||||
<div class="mt-4 text-right">
|
||||
{% for version in project.versions %}
|
||||
<span class="text-xs ml-1 px-1 py-1 bg-gray-300 text-gray-600 rounded">{% spaceless %}
|
||||
Drupal {{ version }}
|
||||
{% endspaceless %}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -0,0 +1,14 @@
|
|||
<div class="w-full lg:w-2/3">
|
||||
<form action="{{ page.mailchimp_url }}" method="post" name="mc-embedded-subscribe-form" novalidate="">
|
||||
<div id="mc_embed_signup_scroll">
|
||||
<div style="position: absolute; left: -5000px;" aria-hidden="true">
|
||||
<input type="text" name="b_46d1ff41a9918b3b7efb885dc_6df88a3d0f" tabindex="-1" value="">
|
||||
</div>
|
||||
|
||||
<div class="flex overflow-hidden">
|
||||
<input type="email" value="" name="EMAIL" class="required email block w-full border border-gray p-3 pl-5 rounded-l-full" placeholder="enter your email" aria-label="Email">
|
||||
<button type="submit" name="subscribe" class="block w-auto border border-gray border-l-0 py-3 pl-5 pr-6 rounded-r-full bg-blue-600 text-white">Subscribe</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
41
sculpin-old/source/_partials/post-pager.html.twig
Normal file
41
sculpin-old/source/_partials/post-pager.html.twig
Normal file
|
@ -0,0 +1,41 @@
|
|||
<div class="py-8 text-center">
|
||||
<ul class="list-reset inline-flex">
|
||||
<li>
|
||||
{% if page.pagination.page == 1 %}
|
||||
<span class="rounded-l-lg p-3 border border-gray text-gray -mr-px cursor-not-allowed">
|
||||
Previous
|
||||
</span>
|
||||
{% else %}
|
||||
<a href="{{ page.pagination.previous_page.url }}" class="rounded-l-lg link no-underline hover:underline hover:bg-gray-200 focus:underline p-3 border border-gray -mr-px">
|
||||
Previous
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
{% for i in range(1, page.pagination.total_pages) %}
|
||||
<li class="{{ i > (page.pagination.page + 2) or i < (page.pagination.page - 2) ? 'hidden sm:block' }}">
|
||||
{% if i == page.pagination.page %}
|
||||
<span class="p-3 border border-gray text-white bg-blue-600 -mr-px">
|
||||
{{ i }}
|
||||
</span>
|
||||
{% else %}
|
||||
<a href="{{ i == 1 ? '/blog' : '/blog/page/' ~ i ~ '.html' }}" class="link no-underline hover:underline hover:bg-gray-200 focus:underline p-3 border border-gray -mr-px">
|
||||
{{ i }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
<li>
|
||||
{% if page.pagination.next_page.url %}
|
||||
<a href="{{ page.pagination.next_page.url }}" class="rounded-r-lg link no-underline hover:underline hover:bg-gray-200 focus:underline p-3 border border-gray">
|
||||
Next
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="rounded-r-lg p-3 border border-gray text-gray cursor-not-allowed">
|
||||
Next
|
||||
</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
14
sculpin-old/source/_partials/post/about-author.html.twig
Normal file
14
sculpin-old/source/_partials/post/about-author.html.twig
Normal file
|
@ -0,0 +1,14 @@
|
|||
<div>
|
||||
<h2 class="mb-2">About the Author</h2>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="mr-4 flex-none leading-none">
|
||||
<img src="{{ site.avatar.url }}" alt="Picture of Oliver" class="w-16 rounded-full border border-gray">
|
||||
</div>
|
||||
|
||||
<p class="markup mb-0">
|
||||
Oliver Davies is a Full Stack Web Developer and System Administrator based in the UK.
|
||||
He is a {{ site.work.role }} at <a href="{{ site.companies[site.work.company].url }}?utm_source={{ site.short_url }}&utm_medium=about-author" class="link">{{ site.companies[site.work.company].name }}</a> and a part-time freelancer specialising in Drupal, Symfony and Laravel development and Linux systems administration.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
3
sculpin-old/source/_partials/post/feedback.html.twig
Normal file
3
sculpin-old/source/_partials/post/feedback.html.twig
Normal file
|
@ -0,0 +1,3 @@
|
|||
<p class="mb-4 italic text-gray-600">
|
||||
<b>Have feedback on this post?</b> <a href="mailto:{{ site.email }}?subject=Feedback: {{ page.title }}">Email me</a> or <a href="https://twitter.com/intent/tweet?text=@{{ site.twitter.user }}&url={{ site.url }}{{ page.url|url_encode }}">send me a tweet</a>.
|
||||
</p>
|
7
sculpin-old/source/_partials/post/header.html.twig
Normal file
7
sculpin-old/source/_partials/post/header.html.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
<div class="mb-8">
|
||||
<h1 class="leading-tight mb-2">{{ page.title }}</h1>
|
||||
|
||||
<p class="text-gray-600">
|
||||
{% include 'posts/post-date' with { post: page } %}
|
||||
</p>
|
||||
</div>
|
5
sculpin-old/source/_partials/post/intro-image.html.twig
Normal file
5
sculpin-old/source/_partials/post/intro-image.html.twig
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% if page.blocks.intro_image %}
|
||||
<div class="mv3 tc">
|
||||
{{ page.blocks.intro_image|raw }}
|
||||
</div>
|
||||
{% endif %}
|
|
@ -0,0 +1,8 @@
|
|||
{% if page.date|date('U') < 'now'|date_modify('-12 months')|date('U') %}
|
||||
<div class="border-2 border-blue-600 p-4 mb-6">
|
||||
<p class="text-sm mb-0">
|
||||
<span class="font-bold">Warning:</span>
|
||||
This post is over a year old. I don't always update old posts with new information, so some of this information may be out of date.
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
|
@ -0,0 +1,6 @@
|
|||
<section class="border-b border-t mb-4 mt-8 py-4">
|
||||
<p>
|
||||
Questions? Comments?
|
||||
I’m <a href="https://twitter.com/{{ site.twitter.user }}">@{{ site.twitter.user }}</a> on Twitter.
|
||||
</p>
|
||||
</section>
|
11
sculpin-old/source/_partials/post/related.html.twig
Normal file
11
sculpin-old/source/_partials/post/related.html.twig
Normal file
|
@ -0,0 +1,11 @@
|
|||
{% if page.related -%}
|
||||
<h2>Related Posts</h2>
|
||||
|
||||
<ul>
|
||||
{% for relate in page.related -%}
|
||||
<li>
|
||||
<a href="{{ relate.source.url }}">{{ relate.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{%- endif %}
|
13
sculpin-old/source/_partials/post/tags.html.twig
Normal file
13
sculpin-old/source/_partials/post/tags.html.twig
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% if page.tags %}
|
||||
<h2 class="visuallyhidden">Tags</h2>
|
||||
|
||||
<ul class="list-reset flex flex-wrap spaced-y-2 spaced-x-3">
|
||||
{% for tag in page.tags|sort %}
|
||||
<li>
|
||||
<a href="/blog/tags/{{ tag }}" class="text-xs py-1 px-3 border-l-2 border-gray-500 bg-gray-200 text-gray-800 leading-none no-underline hover:underline focus:bg-gray-800 focus:text-white focus:outline-none">
|
||||
{{- tag -}}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
15
sculpin-old/source/_partials/posts/latest.html.twig
Normal file
15
sculpin-old/source/_partials/posts/latest.html.twig
Normal file
|
@ -0,0 +1,15 @@
|
|||
{% if data.posts and page.url != '/blog' %}
|
||||
<div class="markup">
|
||||
<h2 class="mb-2">Latest Blog Posts</h2>
|
||||
|
||||
<ul class="pl-4">
|
||||
{% for post in data.posts|slice(0, site.latest_posts) %}
|
||||
<li>
|
||||
<a href="{{ post.url }}">
|
||||
{{- post.title -}}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
4
sculpin-old/source/_partials/posts/post-date.html.twig
Normal file
4
sculpin-old/source/_partials/posts/post-date.html.twig
Normal file
|
@ -0,0 +1,4 @@
|
|||
<time class="{{ class }}" datetime="{{ post.date|date('Y-m-d') }}">
|
||||
<span class="visuallyhidden">Posted on </span>
|
||||
{{ post.date|date('jS F Y') }}
|
||||
</time>
|
4
sculpin-old/source/_partials/site-message.html.twig
Normal file
4
sculpin-old/source/_partials/site-message.html.twig
Normal file
|
@ -0,0 +1,4 @@
|
|||
<div class="markup mb-8 px-5 py-3 text-sm border-b-2 border-yellow-300 bg-yellow-100">
|
||||
<p>Oliver is giving a workshop, <a href="https://opdavi.es/dclondon20">Automated Testing and Test Driven Development with Drupal 8</a>, on 13th March at DrupalCamp London.</p>
|
||||
<p><a href="http://opdavi.es/YilTZ" title="Find out more and register on the DrupalCamp London website">Find out more and register</a> on the DrupalCamp London website.</p>
|
||||
</div>
|
9
sculpin-old/source/_partials/speakerdeck.html.twig
Normal file
9
sculpin-old/source/_partials/speakerdeck.html.twig
Normal file
|
@ -0,0 +1,9 @@
|
|||
<div class="slides">
|
||||
<noscript>**Please enable JavaScript to view slides.**</noscript>
|
||||
<script
|
||||
class="speakerdeck-embed"
|
||||
data-id="{{ data.id }}"
|
||||
data-ratio="{{ data.ratio ?: '1.29456384323641' }}"
|
||||
src="//speakerdeck.com/assets/embed.js"
|
||||
></script>
|
||||
</div>
|
7
sculpin-old/source/_partials/svg/chevron-right.twig
Normal file
7
sculpin-old/source/_partials/svg/chevron-right.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svg viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="{{ class }}" style="{{ style }}">
|
||||
<g stroke="none" stroke-width="1" fill-rule="evenodd">
|
||||
<g id="icon-shape">
|
||||
<polygon id="Combined-Shape" points="12.9497475 10.7071068 13.6568542 10 8 4.34314575 6.58578644 5.75735931 10.8284271 10 6.58578644 14.2426407 8 15.6568542 12.9497475 10.7071068"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 476 B |
17
sculpin-old/source/_partials/talk/events.html.twig
Normal file
17
sculpin-old/source/_partials/talk/events.html.twig
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% import 'helpers' as helpers %}
|
||||
|
||||
{% if events %}
|
||||
<div>
|
||||
<h2>Presented at</h2>
|
||||
|
||||
<ul class="markup mt-2 list-disc ml-5">
|
||||
{% for event in events %}
|
||||
<li>
|
||||
{{ helpers.talkEventName(event) }}
|
||||
{{ helpers.talkEventLocation(event) }}
|
||||
<span class="text-gray-800">- {{ event.date|date('jS F Y') }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
9
sculpin-old/source/_partials/talk/image.html.twig
Normal file
9
sculpin-old/source/_partials/talk/image.html.twig
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% if talk.speakerdeck.id %}
|
||||
<div class="hidden sm:block sm:w-1/5 md:w-1/4 sm:mx-2">
|
||||
<img
|
||||
src="https://speakerd.s3.amazonaws.com/presentations/{{ talk.speakerdeck.id }}/slide_0.jpg"
|
||||
alt="{{ talk.title }}"
|
||||
class="border border-solid p-1 bg-white"
|
||||
>
|
||||
</div>
|
||||
{% endif %}
|
9
sculpin-old/source/_partials/talk/slides.html.twig
Normal file
9
sculpin-old/source/_partials/talk/slides.html.twig
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% if page.speakerdeck.id and page.speakerdeck.ratio %}
|
||||
<div>
|
||||
<h2 class="mb-2">Slides</h2>
|
||||
|
||||
{% include 'speakerdeck' with {
|
||||
data: page.speakerdeck,
|
||||
} %}
|
||||
</div>
|
||||
{% endif %}
|
7
sculpin-old/source/_partials/talk/tweets.html.twig
Normal file
7
sculpin-old/source/_partials/talk/tweets.html.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
{% if page.blocks.tweets %}
|
||||
<div class="mt-6">
|
||||
<h2>Tweets</h2>
|
||||
|
||||
{{ page.blocks.tweets|raw }}
|
||||
</div>
|
||||
{% endif %}
|
28
sculpin-old/source/_partials/talk/video.html.twig
Normal file
28
sculpin-old/source/_partials/talk/video.html.twig
Normal file
|
@ -0,0 +1,28 @@
|
|||
{% macro videoSrc(video) %}
|
||||
{% set srcUrls = {
|
||||
youtube: '//www.youtube.com/embed',
|
||||
videopress: 'https://videopress.com/embed',
|
||||
vimeo: 'https://player.vimeo.com/video',
|
||||
} %}
|
||||
|
||||
{{ srcUrls[video.type] ~ '/' ~ video.id }}
|
||||
{% endmacro %}
|
||||
|
||||
{% from _self import videoSrc %}
|
||||
|
||||
{% if page.video.id %}
|
||||
<div class="mt-4">
|
||||
<h2 class="mb-2">Video</h2>
|
||||
|
||||
<div class="video-full">
|
||||
<iframe
|
||||
width="678"
|
||||
height="408"
|
||||
src="{{ videoSrc(page.video) }}"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
9
sculpin-old/source/_partials/talks/past.html.twig
Normal file
9
sculpin-old/source/_partials/talks/past.html.twig
Normal file
|
@ -0,0 +1,9 @@
|
|||
<div class="spaced-y-4">
|
||||
<h2 class="font-bold">Previous Talks</h2>
|
||||
|
||||
<div class="spaced-y-10">
|
||||
{% for talk in talks|reverse %}
|
||||
{% include 'talks/talk' %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
82
sculpin-old/source/_partials/talks/table.html.twig
Normal file
82
sculpin-old/source/_partials/talks/table.html.twig
Normal file
|
@ -0,0 +1,82 @@
|
|||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-1/5">Date</th>
|
||||
{% if not talk_page %}<th class="w-1/3">Talk</th>{% endif %}
|
||||
<th>Event</th>
|
||||
{% if not upcoming %}<th class="w-1/6">Feedback</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for talk in talks %}
|
||||
<tr>
|
||||
<td>
|
||||
<span class="block md:hidden">
|
||||
{{ talk.event.date|date(talk.event.fuzzy_date ? 'M Y' : 'j M Y') }}
|
||||
</span>
|
||||
<span class="hidden md:block">
|
||||
{{ talk.event.date|date(talk.event.fuzzy_date ? 'F Y' : 'j F Y') }}
|
||||
</span>
|
||||
|
||||
{% if talk.event.time is defined %}
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ talk.event.time }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{% if not talk_page %}
|
||||
<td class="whitespace-no-wrap xl:whitespace-normal">
|
||||
{% if talk.talk.url is not empty %}
|
||||
<a href="{{ talk.talk.url }}">
|
||||
{{ talk.talk.title }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ talk.talk.title }}
|
||||
{% endif %}
|
||||
|
||||
<div class="text-xs text-gray-600">
|
||||
{% if talk.talk.type %}
|
||||
{{ talk.talk.type }}
|
||||
{% else %}
|
||||
{{ talk.event.type|default('Talk') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
|
||||
<td class="whitespace-no-wrap xl:whitespace-normal">
|
||||
{% if talk.event.url is not empty %}
|
||||
<a href="{{ talk.event.url }}">
|
||||
{{ talk.event.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ talk.event.name }}
|
||||
{% endif %}
|
||||
|
||||
{% if talk.event.location is defined %}
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ talk.event.location }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{% if not upcoming %}
|
||||
<td>
|
||||
{% if talk.event.joindin and talk.event.date <= 'today'|date('Y-m-d') %}
|
||||
<a
|
||||
href="{{ talk.event.joindin }}"
|
||||
class="button"
|
||||
title="Read or leave feedback for this talk"
|
||||
>
|
||||
<i class="fa fa-comment-o"></i> joind.in
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
21
sculpin-old/source/_partials/talks/talk.html.twig
Normal file
21
sculpin-old/source/_partials/talks/talk.html.twig
Normal file
|
@ -0,0 +1,21 @@
|
|||
<article>
|
||||
<h3 class="text-lg leading-snug text-gray-800">
|
||||
<a href="{{ talk.url }}" class="text-inherit" tabindex="-1">
|
||||
{{ talk.title }}
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<div class="mt-1">
|
||||
<p>{{ talk.description }}</p>
|
||||
</div>
|
||||
|
||||
{% if talk.url %}
|
||||
<footer class="mt-1">
|
||||
<a href="{{ talk.url }}" class="text-sm text-gray-600 hover:text-gray-900 focus:text-gray-900 no-underline hover:underline">
|
||||
Find out more
|
||||
<span class="visuallyhidden">about {{ talk.title }}</span>
|
||||
→
|
||||
</a>
|
||||
</footer>
|
||||
{% endif %}
|
||||
</article>
|
11
sculpin-old/source/_partials/talks/upcoming.html.twig
Normal file
11
sculpin-old/source/_partials/talks/upcoming.html.twig
Normal file
|
@ -0,0 +1,11 @@
|
|||
<div class="spaced-y-4">
|
||||
<h2 class="font-bold">Upcoming Talks</h2>
|
||||
|
||||
<div class="spaced-y-10">
|
||||
{% for talk in talks %}
|
||||
{% include 'talks/talk' %}
|
||||
{% else %}
|
||||
<p>Nothing scheduled at the moment.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
10
sculpin-old/source/_partials/tweet.html.twig
Normal file
10
sculpin-old/source/_partials/tweet.html.twig
Normal file
|
@ -0,0 +1,10 @@
|
|||
<div class="my-4 flex justify-center {{ class }}">
|
||||
<blockquote
|
||||
class="twitter-tweet"
|
||||
lang="en"
|
||||
{% if not data_cards %}data-cards="hidden"{% endif %}
|
||||
{% if no_parent %}data-conversation="none"{% endif %}
|
||||
>
|
||||
{{ content|raw }}
|
||||
</blockquote>
|
||||
</div>
|
9
sculpin-old/source/_partials/video-embed.html.twig
Normal file
9
sculpin-old/source/_partials/video-embed.html.twig
Normal file
|
@ -0,0 +1,9 @@
|
|||
<div class="{{ classes }}">
|
||||
<iframe
|
||||
src="https://www.youtube.com/embed/{{ video.id }}"
|
||||
height="{{ video.attr.height }}"
|
||||
width="{{ video.attr.width }}"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
84
sculpin-old/source/_posts/2014.md
Normal file
84
sculpin-old/source/_posts/2014.md
Normal file
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
title: 2014
|
||||
date: 2015-03-20
|
||||
excerpt: A look back at 2014.
|
||||
tags:
|
||||
- drupal-association
|
||||
- drupalcamp-london
|
||||
- personal
|
||||
tweets: true
|
||||
---
|
||||
|
||||
A lot happened in 2014. Here are some of the main things that I'd like to
|
||||
highlight.
|
||||
|
||||
## Joined the Drupal Association
|
||||
|
||||
This was the main thing for me this year, in May I left
|
||||
[Precedent](http://precedent.com) and joined the
|
||||
[Drupal Association](https://assoc.drupal.org). I work on the Engineering team,
|
||||
focused mainly on [Drupal.org](https://www.drupal.org) but I've also done some
|
||||
theming work on the DrupalCon [Amsterdam](http://amsterdam2014.drupal.org) and
|
||||
[Latin America](http://latinamerica2015.drupal.org) websites, and some
|
||||
pre-launch work on [Drupal Jobs](https://jobs.drupal.org).
|
||||
|
||||
Some of the tasks that I've worked on so far are:
|
||||
|
||||
- Fixing remaining issues from the Drupal.org Drupal 7 upgrade.
|
||||
- Improving pages for
|
||||
[Supporting Partners](https://www.drupal.org/supporters/partners),
|
||||
[Technology Supporters](https://www.drupal.org/supporters/technology) and
|
||||
[Hosting Partners](https://www.drupal.org/supporters/hosting). These
|
||||
previously were manually updated pages using HTML tables, which are now
|
||||
dynamic pages built with [Views](https://www.drupal.org/project/views) using
|
||||
organisation nodes.
|
||||
- Configuring human-readable paths for user profiles using
|
||||
[Pathauto](https://www.drupal.org/project/pathauto). Only a small change, but
|
||||
made a big difference to end-users.
|
||||
- Migration of user data from profile values to fields, and various user profile
|
||||
improvements. This was great because now we can do things like reference
|
||||
mentors by their username and display their picture on your profile, as well
|
||||
as show lists of peope listing a user as their mentor. This, I think, adds a
|
||||
more personal element to Drupal.org because we can see the actual people and
|
||||
not just a list of names on a page.
|
||||
|
||||
I've started keeping a list of tasks that I've been involved with on my
|
||||
[Work](/work/) page, and will be adding more things as I work on them.
|
||||
|
||||
### Portland
|
||||
|
||||
I was able to travel to Portland, Oregon twice last year to meet with the rest
|
||||
of the Association staff. Both times I met new people and it was great to spend
|
||||
some work and social time with everyone, and it was great to have everyone
|
||||
together as a team.
|
||||
|
||||
## My First DrupalCamp
|
||||
|
||||
In February, I attended [DrupalCamp London](http://2014.drupalcamplondon.co.uk).
|
||||
This was my first time attending a Camp, and I managed to attend some great
|
||||
sessions as well as meet people who I'd never previously met in person. I was
|
||||
also a volunteer and speaker, where I talked about
|
||||
[Git Flow](/blog/what-git-flow/) - a workflow for managing your Git projects.
|
||||
|
||||
{% include 'tweet' with {
|
||||
content: '<p>Great presentation by <a href="https://twitter.com/opdavies">@opdavies</a> on git flow at <a href="https://twitter.com/search?q=%23dclondon&src=hash">#dclondon</a> very well prepared and presented. <a href="http://t.co/tDINp2Nsbn">pic.twitter.com/tDINp2Nsbn</a></p>— Greg Franklin (@gfranklin) <a href="https://twitter.com/gfranklin/statuses/440104311276969984">March 2, 2014</a>'
|
||||
} %}
|
||||
|
||||
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,
|
30
sculpin-old/source/_posts/accessible-bristol-site.md
Normal file
30
sculpin-old/source/_posts/accessible-bristol-site.md
Normal file
|
@ -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
|
||||
<http://www.accessiblebristol.org.uk/events/accessible-bristol-launch> or go to
|
||||
<http://buytickets.at/accessiblebristol/6434> to register.
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: How to add a date popup calendar onto a custom form
|
||||
date: 2012-05-23
|
||||
excerpt: How to use a date popup calendar within your custom module.
|
||||
tags:
|
||||
- forms
|
||||
- form-api
|
||||
- date
|
||||
- calendar
|
||||
- drupal-7
|
||||
- drupal-planet
|
||||
- drupal
|
||||
---
|
||||
|
||||
How to use a date popup calendar within your custom module.
|
||||
|
||||
First, I need to download the
|
||||
[Date](http://drupal.org/project/date 'Date module on Drupal.org') module, and
|
||||
make my module dependent on date_popup by adding the following line into my
|
||||
module's .info file.
|
||||
|
||||
```language-ini
|
||||
dependencies[] = date_popup
|
||||
```
|
||||
|
||||
Within my form builder function:
|
||||
|
||||
```language-php
|
||||
$form['date'] = array(
|
||||
'#title' => t('Arrival date'),
|
||||
|
||||
// Provided by the date_popup module
|
||||
'#type' => 'date_popup',
|
||||
|
||||
// Uses the PHP date() format - http://php.net/manual/en/function.date.php
|
||||
'#date_format' => 'j F Y',
|
||||
|
||||
// Limits the year range to the next two upcoming years
|
||||
'#date_year_range' => '0:+2',
|
||||
|
||||
// Default value must be in 'Y-m-d' format.
|
||||
'#default_value' => date('Y-m-d', time()),
|
||||
);
|
||||
```
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
title: Add a Taxonomy Term to Multiple Nodes Using SQL
|
||||
date: 2010-07-07
|
||||
excerpt: How to add a new taxonomy term to multiple nodes in Drupal using SQL.
|
||||
tags:
|
||||
- taxonomy
|
||||
- drupal-planet
|
||||
- drupal-6
|
||||
- sql
|
||||
- sequal-pro
|
||||
- database
|
||||
---
|
||||
|
||||
In preparation for my Blog posts being added to
|
||||
[Drupal Planet](http://drupal.org/planet), I needed to create a new Taxonomy
|
||||
term (or, in this case, tag) called 'Drupal Planet', and assign it to new
|
||||
content to imported into their aggregator. After taking a quick look though my
|
||||
previous posts, I decided that 14 of my previous posts were relevant, and
|
||||
thought that it would be useful to also assign these the 'Drupal Planet' tag.
|
||||
|
||||
I didn't want to manually open each post and add the new tag, so I decided to
|
||||
make the changes myself directly into the database using SQL, and as a follow-up
|
||||
to a previous post -
|
||||
[Quickly Change the Content Type of Multiple Nodes using SQL](/blog/change-content-type-multiple-nodes-using-sql/).
|
||||
|
||||
**Again, before changing any values within the database, ensure that you have an
|
||||
up-to-date backup which you can restore if you encounter a problem!**
|
||||
|
||||
The first thing I did was create the 'Drupal Planet' term in my Tags vocabulary.
|
||||
I decided to do this via the administration area of my site, and not via the
|
||||
database. Then, using [Sequel Pro](http://www.sequelpro.com), I ran the
|
||||
following SQL query to give me a list of Blog posts on my site - showing just
|
||||
their titles and nid values.
|
||||
|
||||
```language-sql
|
||||
SELECT title, nid FROM node WHERE TYPE = 'blog' ORDER BY title ASC;
|
||||
```
|
||||
|
||||
I made a note of the nid's of the returned nodes, and kept them for later. I
|
||||
then ran a similar query against the term_data table. This returned a list of
|
||||
Taxonomy terms - showing the term's name, and it's unique tid value.
|
||||
|
||||
```language-sql
|
||||
SELECT NAME, tid FROM term_data ORDER BY NAME ASC;
|
||||
```
|
||||
|
||||
The term that I was interested in, Drupal Planet, had the tid of 84. To confirm
|
||||
that no nodes were already assigned a taxonomy term with this tid, I ran another
|
||||
query against the database. I'm using aliases within this query to link the
|
||||
node, term_node and term_data tables. For more information on SQL aliases, take
|
||||
a look at <http://w3schools.com/sql/sql_alias.asp>.
|
||||
|
||||
```language-sql
|
||||
SELECT * FROM node n, term_data td, term_node tn WHERE td.tid = 84 AND n.nid = tn.nid AND tn.tid = td.tid;
|
||||
```
|
||||
|
||||
As expected, it returned no rows.
|
||||
|
||||
The table that links node and term_data is called term_node, and is made up of
|
||||
the nid and vid columns from the node table, as well as the tid column from the
|
||||
term_data table. Is it is here that the additional rows would need to be
|
||||
entered.
|
||||
|
||||
To confirm everything, I ran a simple query against an old post. I know that the
|
||||
only taxonomy term associated with this post is 'Personal', which has a tid
|
||||
value of 44.
|
||||
|
||||
```language-sql
|
||||
SELECT nid, tid FROM term_node WHERE nid = 216;
|
||||
```
|
||||
|
||||
Once the query had confirmed the correct tid value, I began to write the SQL
|
||||
Insert statement that would be needed to add the new term to the required nodes.
|
||||
The nid and vid values were the same on each node, and the value of my taxonomy
|
||||
term would need to be 84.
|
||||
|
||||
Once this had completed with no errors, I returned to the administration area of
|
||||
my Drupal site to confirm whether or not the nodes had been assigned the new
|
||||
term.
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
title: Adding Custom Theme Templates in Drupal 7
|
||||
date: 2012-04-19
|
||||
excerpt: >
|
||||
Today, I had a situation where I was displaying a list of teasers for news
|
||||
article nodes. The article content type had several different fields assigned
|
||||
to it, including main and thumbnail images. In this case, I wanted to have
|
||||
different output and fields displayed when a teaser was displayed compared to
|
||||
when a complete node was displayed.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal
|
||||
---
|
||||
|
||||
Today, I had a situation where I was displaying a list of teasers for news
|
||||
article nodes. The article content type had several different fields assigned to
|
||||
it, including main and thumbnail images. In this case, I wanted to have
|
||||
different output and fields displayed when a teaser was displayed compared to
|
||||
when a complete node was displayed.
|
||||
|
||||
I have previously seen it done this way by adding this into in a node.tpl.php
|
||||
file:
|
||||
|
||||
```language-php
|
||||
if ($teaser) {
|
||||
// The teaser output.
|
||||
}
|
||||
else {
|
||||
// The whole node output.
|
||||
}
|
||||
```
|
||||
|
||||
However, I decided to do something different and create a separate template file
|
||||
just for teasers. This is done using the hook_preprocess_HOOK function that I
|
||||
can add into my theme's template.php file.
|
||||
|
||||
The function requires the node variables as an argument - one of which is
|
||||
theme_hook_suggestions. This is an array of suggested template files that Drupal
|
||||
looks for and attempts to use when displaying a node, and this is where I'll be
|
||||
adding a new suggestion for my teaser-specific template. Using the `debug()`
|
||||
function, I can easily see what's already there.
|
||||
|
||||
```language-php
|
||||
array (
|
||||
0 => 'node__article',
|
||||
1 => 'node__343',
|
||||
2 => 'node__view__latest_news',
|
||||
3 => 'node__view__latest_news__page',
|
||||
)
|
||||
```
|
||||
|
||||
So, within my theme's template.php file:
|
||||
|
||||
```language-php
|
||||
/**
|
||||
* Implementation of hook_preprocess_HOOK().
|
||||
*/
|
||||
function mytheme_preprocess_node(&$variables) {
|
||||
$node = $variables['node'];
|
||||
|
||||
if ($variables['teaser']) {
|
||||
// Add a new item into the theme_hook_suggestions array.
|
||||
$variables['theme_hook_suggestions'][] = 'node__' . $node->type . '_teaser';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After adding the new suggestion:
|
||||
|
||||
```language-php
|
||||
array (
|
||||
0 => 'node__article',
|
||||
1 => 'node__343',
|
||||
2 => 'node__view__latest_news',
|
||||
3 => 'node__view__latest_news__page',
|
||||
4 => 'node__article_teaser',
|
||||
)
|
||||
```
|
||||
|
||||
Now, within my theme I can create a new node--article-teaser.tpl.php template
|
||||
file and this will get called instead of the node--article.tpl.php when a teaser
|
||||
is loaded. As I'm not specifying the node type specifically and using the
|
||||
dynamic <em>\$node->type</em> value within my suggestion, this will also apply
|
||||
for all other content types on my site and not just news articles.
|
108
sculpin-old/source/_posts/announcing-the-drupal-vm-generator.md
Normal file
108
sculpin-old/source/_posts/announcing-the-drupal-vm-generator.md
Normal file
|
@ -0,0 +1,108 @@
|
|||
---
|
||||
title: Announcing the Drupal VM Generator
|
||||
date: 2016-02-15
|
||||
excerpt:
|
||||
For the past few weeks, I’ve been working on a personal side project based on
|
||||
Drupal VM - the Drupal VM Generator.
|
||||
tags:
|
||||
- drupal
|
||||
- drupal-planet
|
||||
- drupal-vm
|
||||
- drupal-vm-generator
|
||||
- symfony
|
||||
---
|
||||
|
||||
For the past few weeks, I’ve been working on a personal side project based on
|
||||
Drupal VM. It’s called the [Drupal VM Generator][1], and over the weekend I’ve
|
||||
added the final features and fixed the remaining issues, and tagged the 1.0.0
|
||||
release.
|
||||
|
||||

|
||||
|
||||
## What is Drupal VM?
|
||||
|
||||
[Drupal VM][2] is a project created and maintained by [Jeff Geerling][3]. It’s a
|
||||
[Vagrant][4] virtual machine for Drupal development that is provisioned using
|
||||
[Ansible][5].
|
||||
|
||||
What is different to a regular Vagrant VM is that uses a file called
|
||||
`config.yml` to configure the machine. Settings such as `vagrant_hostname`,
|
||||
`drupalvm_webserver` and `drupal_core_path` are stored as YAML and passed into
|
||||
the `Vagrantfile` and the `playbook.yml` file which is used when the Ansible
|
||||
provisioner runs.
|
||||
|
||||
In addition to some essential Ansible roles for installing and configuring
|
||||
packages such as Git, MySQL, PHP and Drush, there are also some roles that are
|
||||
conditional and only installed based on the value of other settings. These
|
||||
include Apache, Nginx, Solr, Varnish and Drupal Console.
|
||||
|
||||
## What does the Drupal VM Generator do?
|
||||
|
||||
> The Drupal VM Generator is a Symfony application that allows you to quickly
|
||||
> create configuration files that are minimal and use-case specific.
|
||||
|
||||
Drupal VM comes with an [example.config.yml file][6] that shows all of the
|
||||
default variables and their values. When I first started using it, I’d make a
|
||||
copy of `example.config.yml`, rename it to `config.yml` and edit it as needed,
|
||||
but a lot of the examples aren’t needed for every use case. If you’re using
|
||||
Nginx as your webserver, then you don’t need the Apache virtual hosts. If you
|
||||
are not using Solr on this project, then you don’t need the Solr variables.
|
||||
|
||||
For a few months, I’ve kept and used boilerplace versions of `config.yml` - one
|
||||
for Apache and one for Nginx. These are minimal, so have most of the comments
|
||||
removed and only the variables that I regularly need, but these can still be
|
||||
quite time consuming to edit each time, and if there are additions or changes
|
||||
upstream, then I have two versions to maintain.
|
||||
|
||||
The Drupal VM Generator is a Symfony application that allows you to quickly
|
||||
create configuration files that are minimal and use-case specific. It uses the
|
||||
[Console component][7] to collect input from the user, [Twig][8] to generate the
|
||||
file, the [Filesystem component][9] to write it.
|
||||
|
||||
Based on the options passed to it and/or answers that you provide, it generates
|
||||
a custom, minimal `config.yml` file for your project.
|
||||
|
||||
Here’s an example of it in action:
|
||||
|
||||

|
||||
|
||||
You can also define options when calling the command and skip any or all
|
||||
questions. Running the following would bypass all of the questions and create a
|
||||
new file with no interaction or additional steps.
|
||||
|
||||
{{ gist('24e569577ca4b72f049d', 'with-options.sh') }}
|
||||
|
||||
## Where do I get it?
|
||||
|
||||
The project is hosted on [GitHub][1], and there are installation instructions
|
||||
within the [README][10].
|
||||
|
||||
<div class="github-card" data-github="opdavies/drupal-vm-generator" data-width="400" data-height="" data-theme="default"></div>
|
||||
|
||||
The recommended method is via downloading the phar file (the same as Composer
|
||||
and Drupal Console). You can also clone the GitHub repository and run the
|
||||
command from there. I’m also wanting to upload it to Packagist so that it can be
|
||||
included if you manage your projects with Composer.
|
||||
|
||||
Please log any bugs or feature requests in the [GitHub issue tracker][11], and
|
||||
I’m more than happy to receive pull requests.
|
||||
|
||||
If you’re interested in contributing, please feel free to fork the repository
|
||||
and start doing so, or contact me with any questions.
|
||||
|
||||
**Update 17/02/16:** The autoloading issue is now fixed if you require the
|
||||
package via Composer, and this has been tagged as the [1.0.1 release][12]
|
||||
|
||||
[1]: https://github.com/opdavies/drupal-vm-generator
|
||||
[2]: http://www.drupalvm.com
|
||||
[3]: http://www.jeffgeerling.com
|
||||
[4]: http://www.vagrantup.com
|
||||
[5]: https://www.ansible.com
|
||||
[6]: https://github.com/geerlingguy/drupal-vm/blob/master/example.config.yml
|
||||
[7]: http://symfony.com/doc/current/components/console/introduction.html
|
||||
[8]: http://twig.sensiolabs.org
|
||||
[9]: http://symfony.com/doc/current/components/filesystem/introduction.html
|
||||
[10]:
|
||||
https://github.com/opdavies/drupal-vm-generator/blob/master/README.md#installation
|
||||
[11]: https://github.com/opdavies/drupal-vm-generator/issues
|
||||
[12]: https://github.com/opdavies/drupal-vm-generator/releases/tag/1.0.1
|
192
sculpin-old/source/_posts/automating-sculpin-jenkins.md
Normal file
192
sculpin-old/source/_posts/automating-sculpin-jenkins.md
Normal file
|
@ -0,0 +1,192 @@
|
|||
---
|
||||
title: Automating Sculpin Builds with Jenkins CI
|
||||
date: 2015-07-21
|
||||
excerpt: How to use Jenkins to automate building Sculpin websites.
|
||||
tags:
|
||||
- sculpin
|
||||
- jenkins
|
||||
---
|
||||
|
||||
As part of re-building this site with [Sculpin](http://sculpin.io), I wanted to
|
||||
automate the deployments, as in I wouldn't need to run a script like
|
||||
[publish.sh](https://raw.githubusercontent.com/sculpin/sculpin-blog-skeleton/master/publish.sh)
|
||||
locally and have that deploy my code onto my server. Not only did that mean that
|
||||
my local workflow was simpler (update, commit and push, rather than update,
|
||||
commit, push and deploy), but if I wanted to make a quick edit or hotfix, I
|
||||
could log into GitHub or Bitbucket (wherever I decided to host the source code)
|
||||
from any computer or my phone, make the change and have it deployed for me.
|
||||
|
||||
I'd started using [Jenkins CI](http://jenkins-ci.org) during my time at the
|
||||
Drupal Association, and had since built my own Jenkins server to handle
|
||||
deployments of Drupal websites, so that was the logical choice to use.
|
||||
|
||||
## Installing Jenkins and Sculpin
|
||||
|
||||
If you don’t already have Jenkins installed and configured, I'd suggest using
|
||||
[Jeff Geerling](http://jeffgeerling.com/) (aka geerlingguy)'s
|
||||
[Ansible role for Jenkins CI](https://galaxy.ansible.com/list#/roles/440).
|
||||
|
||||
I've also released an
|
||||
[Ansible role for Sculpin](https://galaxy.ansible.com/list#/roles/4063) that
|
||||
installs the executable so that the Jenkins server can run Sculpin commands.
|
||||
|
||||
## Triggering a Build from a Git Commit
|
||||
|
||||
I created a new Jenkins item for this task, and restricted where it could be run
|
||||
to `master` (i.e. the Jenkins server rather than any of the nodes).
|
||||
|
||||
### Polling from Git
|
||||
|
||||
I entered the url to the
|
||||
[GitHub repo](https://github.com/opdavies/oliverdavies.uk) into the **Source
|
||||
Code Management** section (the Git option _may_ have been added by the
|
||||
[Git plugin](https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin) that I have
|
||||
installed).
|
||||
|
||||
As we don’t need any write access back to the repo, using the HTTP URL rather
|
||||
than the SSH one was fine, and I didn’t need to provide any additional
|
||||
credentials.
|
||||
|
||||
Also, as I knew that I’d be working a lot with feature branches, I entered
|
||||
`*/master` as the only branch to build. This meant that pushing changes or
|
||||
making edits on any other branches would not trigger a build.
|
||||
|
||||

|
||||
|
||||
I also checked the **Poll SCM** option so that Jenkins would be routinely
|
||||
checking for updated code. This essentially uses the same syntax as cron,
|
||||
specifying minutes, hours etc. I entered `* * * * *` so that Jenkins would poll
|
||||
each minute, knowing that I could make this less frequent if needed.
|
||||
|
||||
This now that Jenkins would be checking for any updates to the repo each minute,
|
||||
and could execute tasks if needed.
|
||||
|
||||
### Building and Deploying
|
||||
|
||||
Within the **Builds** section of the item, I added an _Execute Shell_ step,
|
||||
where I could enter a command to execute. Here, I pasted a modified version of
|
||||
the original publish.sh script.
|
||||
|
||||
```language-bash
|
||||
#!/bin/bash
|
||||
|
||||
set -uex
|
||||
|
||||
sculpin generate --env=prod --quiet
|
||||
if [ $? -ne 0 ]; then echo "Could not generate the site"; exit 1; fi
|
||||
|
||||
rsync -avze 'ssh' --delete output_prod/ prodwww2:/var/www/html/oliverdavies.uk/htdocs
|
||||
if [ $? -ne 0 ]; then echo "Could not publish the site"; exit 1; fi
|
||||
```
|
||||
|
||||
This essentially is the same as the original file, in that Sculpin generates the
|
||||
site, and uses rsync to deploy it somewhere else. In my case, `prodwww2` is a
|
||||
Jenkins node (this alias is configured in `/var/lib/jenkins/.ssh/config`), and
|
||||
`/var/www/html/oliverdavies.uk/htdocs` is the directory from where my site is
|
||||
served.
|
||||
|
||||
## Building Periodically
|
||||
|
||||
There is some dynamic content on my site, specifically on the Talks page. Each
|
||||
talk has a date assigned to it, and within the Twig template, the talk is
|
||||
positoned within upcoming or previous talks based on whether this date is less
|
||||
or greater than the time of the build.
|
||||
|
||||
The YAML front matter:
|
||||
|
||||
```language-yaml
|
||||
---
|
||||
...
|
||||
talks:
|
||||
- title: Test Drive Twig with Sculpin
|
||||
location: DrupalCamp North
|
||||
---
|
||||
```
|
||||
|
||||
The Twig layout:
|
||||
|
||||
```language-twig
|
||||
{% verbatim -%}
|
||||
{% for talk in talks|reverse if talk.date >= now %}
|
||||
{# Upcoming talks #}
|
||||
{% endfor %}
|
||||
|
||||
{% for talk in talks if talk.date < now %}
|
||||
{# Previous talks #}
|
||||
{% endfor%}
|
||||
{%- endverbatim %}
|
||||
```
|
||||
|
||||
I also didn’t want to have to push an empty commit or manually trigger a job in
|
||||
Jenkins after doing a talk in order for it to be positioned in the correct place
|
||||
on the page, so I also wanted Jenkins to schedule a regular build regardless of
|
||||
whether or not code had been pushed, so ensure that my talks page would be up to
|
||||
date.
|
||||
|
||||
After originally thinking that I'd have to split the build steps into a separate
|
||||
item and trigger that from a scheduled item, and amend my git commit item
|
||||
accordingly, I found a **Build periodically** option that I could use within the
|
||||
same item, leaving it intact and not having to make amends.
|
||||
|
||||
I set this to `@daily` (the same `H H * * *` - `H` is a Jenkins thing), so that
|
||||
the build would be triggered automatically each day without a commit, and deploy
|
||||
any updates to the site.
|
||||
|
||||

|
||||
|
||||
## Next Steps
|
||||
|
||||
This workflow works great for one site, but as I roll out more Sculpin sites,
|
||||
I'd like to reduce duplication. I see this mainly as I’ll end up creating a
|
||||
separate `sculpin_build` item that’s decoupled from the site that it’s building,
|
||||
and instead passing variables such as environment, server name and docroot path
|
||||
as parameters in a parameterized build.
|
||||
|
||||
I'll probably also take the raw shell script out of Jenkins and save it in a
|
||||
text file that's stored locally on the server, and execute that via Jenkins.
|
||||
This means that I’d be able to store this file in a separate Git repository with
|
||||
my other Jenkins scripts and get the standard advantages of using version
|
||||
control.
|
||||
|
||||
## Update
|
||||
|
||||
Since publishing this post, I've added some more items to the original build
|
||||
script.
|
||||
|
||||
### Updating Composer
|
||||
|
||||
```language-bash
|
||||
if [ -f composer.json ]; then
|
||||
/usr/local/bin/composer install
|
||||
fi
|
||||
```
|
||||
|
||||
Updates project dependencies via
|
||||
[Composer](https://getcomposer.org/doc/00-intro.md#introduction) if
|
||||
composer.json exists.
|
||||
|
||||
### Updating Sculpin Dependencies
|
||||
|
||||
```language-bash
|
||||
if [ -f sculpin.json ]; then
|
||||
sculpin install
|
||||
fi
|
||||
```
|
||||
|
||||
Runs `sculpin install` on each build if the sculpin.json file exists, to ensure
|
||||
that the required custom bundles and dependencies are installed.
|
||||
|
||||
### Managing Redirects
|
||||
|
||||
```language-bash
|
||||
if [ -f scripts/redirects.php ]; then
|
||||
/usr/bin/php scripts/redirects.php
|
||||
fi
|
||||
```
|
||||
|
||||
I've been working on a `redirects.php` script that generates redirects from a
|
||||
.csv file, after seeing similar things in the
|
||||
[Pantheon Documentation](https://github.com/pantheon-systems/documentation) and
|
||||
[That Podcast](https://github.com/thatpodcast/thatpodcast.io) repositories. This
|
||||
checks if that file exists, and if so, runs it and generates the source file
|
||||
containing each redirect.
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
title: Back to the future with Git’s diff and apply commands
|
||||
date: 2018-04-23
|
||||
excerpt:
|
||||
How to revert files using Git, but as a new commit to prevent force pushing.
|
||||
tags:
|
||||
- git
|
||||
---
|
||||
|
||||
This is one of those “there’s probably already a better way to do this”
|
||||
situations, but it worked.
|
||||
|
||||
I was having some issues this past weekend where, despite everything working
|
||||
fine locally, a server was showing a “500 Internal Server” after I pushed some
|
||||
changes to a site. In order to bring the site back online, I needed to revert
|
||||
the site files back to the previous version, but as part of a new commit.
|
||||
|
||||
The `git reset` commands removed the interim commits which meant that I couldn’t
|
||||
push to the remote (force pushing, quite rightly, isn’t allowed for the
|
||||
production branch), and using `git revert` was resulting in merge conflicts in
|
||||
`composer.lock` that I’d rather have avoided if possible.
|
||||
|
||||
This is what `git log --oneline -n 4` was outputting:
|
||||
|
||||
```
|
||||
14e40bc Change webflo/drupal-core-require-dev version
|
||||
fc058bb Add services.yml
|
||||
60bcf33 Update composer.json and re-generate lock file
|
||||
722210c More styling
|
||||
```
|
||||
|
||||
`722210c` is the commit SHA that I needed to go back to.
|
||||
|
||||
## First Solution
|
||||
|
||||
My first solution was to use `git diff` to create a single patch file of all of
|
||||
the changes from the current point back to the original commit. In this case,
|
||||
I’m using `head~3` (four commits before `head`) as the original reference, I
|
||||
could have alternatively used a commit ID, tag or branch name.
|
||||
|
||||
```
|
||||
git diff head head~3 > temp.patch
|
||||
git apply -v temp.patch
|
||||
```
|
||||
|
||||
With the files are back in the former state, I can remove the patch, add the
|
||||
files as a new commit and push them to the remote.
|
||||
|
||||
```
|
||||
rm temp.patch
|
||||
|
||||
git add .
|
||||
git commit -m 'Back to the future'
|
||||
git push
|
||||
```
|
||||
|
||||
Although the files are back in their previous, working state, as this is a new
|
||||
commit with a new commit SHA reference, there is no issue with the remote
|
||||
rejecting the commit or needing to attempt to force push.
|
||||
|
||||
## Second Solution
|
||||
|
||||
The second solution is just a shorter, cleaner version of the first!
|
||||
|
||||
Rather than creating a patch file and applying it, the output from `git diff`
|
||||
can be piped straight into `git apply`.
|
||||
|
||||
```
|
||||
git diff head~3 head | git apply -v
|
||||
```
|
||||
|
||||
This means that there’s only one command to run and no leftover patch file, and
|
||||
I can go ahead and add and commit the changes straight away.
|
102
sculpin-old/source/_posts/building-gmail-filters-in-php.md
Normal file
102
sculpin-old/source/_posts/building-gmail-filters-in-php.md
Normal file
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
title: Building Gmail Filters with PHP
|
||||
date: 2016-07-15
|
||||
excerpt: How to use PHP to generate and export filters for Gmail.
|
||||
tags:
|
||||
- php
|
||||
- gmail
|
||||
promoted: true
|
||||
---
|
||||
|
||||
Earlier this week I wrote a small PHP library called [GmailFilterBuilder][0]
|
||||
that allows you to write Gmail filters in PHP and export them to XML.
|
||||
|
||||
I was already aware of a Ruby library called [gmail-britta][1] that does the
|
||||
same thing, but a) I’m not that familiar with Ruby so the syntax wasn’t that
|
||||
natural to me - it’s been a while since I wrote any Puppet manifests, and b) it
|
||||
seemed like a interesting little project to work on one evening.
|
||||
|
||||
The library contains two classes - `GmailFilter` which is used to create each
|
||||
filter, and `GmailFilterBuilder` that parses the filters and generates the XML
|
||||
using a [Twig][2] template.
|
||||
|
||||
## Usage
|
||||
|
||||
For example:
|
||||
|
||||
```language-php
|
||||
# test.php
|
||||
|
||||
require __DIR__ '/vendor/autoload.php';
|
||||
|
||||
use Opdavies\GmailFilterBuilder\Builder;
|
||||
use Opdavies\GmailFilterBuilder\Filter;
|
||||
|
||||
$filters = [];
|
||||
|
||||
$filters[] = Filter::create()
|
||||
->has('from:example@test.com')
|
||||
->labelAndArchive('Test')
|
||||
->neverSpam();
|
||||
|
||||
new Builder($filters);
|
||||
```
|
||||
|
||||
In this case, an email from `example@test.com` would be archived, never marked
|
||||
as spam, and have a label of "Test" added to it.
|
||||
|
||||
With this code written, and the GmailFilterBuilder library installed via
|
||||
Composer, I can run `php test.php` and have the XML written to the screen.
|
||||
|
||||
This can also be written to a file - `php test.php > filters.xml` - which can
|
||||
then be imported into Gmail.
|
||||
|
||||
## Twig Extensions
|
||||
|
||||
I also added a custom Twig extension that I moved into a separate
|
||||
[twig-extensions][5] library so that I and other people can re-use it in other
|
||||
projects.
|
||||
|
||||
It’s a simple filter that accepts a boolean and returns `true` or `false` as a
|
||||
string, but meant that I could remove three ternary operators from the template
|
||||
and replace them with the `boolean_string` filter.
|
||||
|
||||
Before:
|
||||
|
||||
<div v-pre markdown="1">
|
||||
```language-twig
|
||||
{% verbatim %}{{ filter.isArchive ? 'true' : 'false' }}{% endverbatim %}
|
||||
```
|
||||
</div>
|
||||
|
||||
After:
|
||||
|
||||
<div v-pre markdown="1">
|
||||
```language-twig
|
||||
{% verbatim %}{{ filter.isArchive|boolean_string }}{% endverbatim %}
|
||||
```
|
||||
</div>
|
||||
|
||||
This can then be used to generate output like this, whereas having blank values
|
||||
would have resulted in errors when importing to Gmail.
|
||||
|
||||
```language-xml
|
||||
<apps:property name='shouldArchive' value='true'/>
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
For a working example, see my personal [gmail-filters][3] repository on GitHub.
|
||||
|
||||
## Resources
|
||||
|
||||
- [The GmailFilterBuilder library on Packagist][4]
|
||||
- [My Gmail filters on GitHub][3]
|
||||
- [My Twig Extensions on Packagist][5]
|
||||
|
||||
[0]: https://github.com/opdavies/gmail-filter-builder
|
||||
[1]: https://github.com/antifuchs/gmail-britta
|
||||
[2]: http://twig.sensiolabs.org
|
||||
[3]: https://github.com/opdavies/gmail-filters
|
||||
[4]: https://packagist.org/packages/opdavies/gmail-filter-builder
|
||||
[5]: https://packagist.org/packages/opdavies/twig-extensions
|
37
sculpin-old/source/_posts/building-the-new-phpsw-website.md
Normal file
37
sculpin-old/source/_posts/building-the-new-phpsw-website.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
title: Building the new PHPSW Website
|
||||
date: 2018-02-28
|
||||
excerpt:
|
||||
Earlier this week we had another hack night, working on the new PHPSW user
|
||||
group website.
|
||||
tags:
|
||||
- phpsw
|
||||
- symfony
|
||||
- tailwind-css
|
||||
has_tweets: true
|
||||
---
|
||||
|
||||
Earlier this week we had another hack night, working on the new [PHPSW user
|
||||
group][0] website.
|
||||
|
||||
<div class="mb-4">
|
||||
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Hacking away on the new <a href="https://twitter.com/phpsw?ref_src=twsrc%5Etfw">@phpsw</a> website with <a href="https://twitter.com/DaveLiddament?ref_src=twsrc%5Etfw">@DaveLiddament</a> and <a href="https://twitter.com/kasiazien?ref_src=twsrc%5Etfw">@kasiazien</a>. <a href="https://t.co/kmfjdQSOUq">pic.twitter.com/kmfjdQSOUq</a></p>— Oliver Davies (@opdavies) <a href="https://twitter.com/opdavies/status/968224364129906688?ref_src=twsrc%5Etfw">February 26, 2018</a></blockquote>
|
||||
</div>
|
||||
|
||||
It’s built with Symfony so it’s naturally using Twig for templating. I’ve become
|
||||
a big fan of the utility based approach to CSS and [Tailwind CSS][1] in
|
||||
particular, so I’m using that for all of the styling, and using [Webpack
|
||||
Encore][2] to compile all of the assets.
|
||||
|
||||
We have an integration with Meetup.com which we’re using to pull all of our
|
||||
previous event data and store them as JSON files for Symfony to parse and
|
||||
render, which it then uses to generate static HTML to upload onto the server.
|
||||
|
||||
We’re in the process of populating all of the past data, but look out for a v1
|
||||
launch soon. In the meantime, feel free to take a peek at our [GitHub
|
||||
repository][3].
|
||||
|
||||
[0]: https://phpsw.uk
|
||||
[1]: https://tailwindcss.com
|
||||
[2]: https://github.com/symfony/webpack-encore
|
||||
[3]: https://github.com/phpsw/phpsw-ng
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
title: Change the Content Type of Multiple Nodes Using SQL
|
||||
date: 2010-07-01
|
||||
excerpt:
|
||||
In this post, I will be changing values within my Drupal 6 site's database to
|
||||
quickly change the content type of multiple nodes.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal-6
|
||||
- drupal
|
||||
- sql
|
||||
- sequel-pro
|
||||
- database
|
||||
- content-types
|
||||
---
|
||||
|
||||
In this post, I will be changing values within my Drupal 6 site's database to
|
||||
quickly change the content type of multiple nodes. I will be using a test
|
||||
development site with the core Blog module installed, and converting Blog posts
|
||||
to a custom content type called 'News article'.
|
||||
|
||||
**Before changing any values within the database, ensure that you have an
|
||||
up-to-date backup which you can restore if you encounter a problem!**
|
||||
|
||||
To begin with, I created the 'News article' content type, and then used the
|
||||
Devel Generate module to generate some Blog nodes.
|
||||
|
||||
Using [Sequel Pro](http://www.sequelpro.com), I can query the database to view
|
||||
the Blog posts (you can also do this via the
|
||||
[Terminal](http://guides.macrumors.com/Terminal) in a Mac OS X/Linux,
|
||||
[Oracle SQL Developer](http://www.oracle.com/technology/software/products/sql/index.html)
|
||||
on Windows, or directly within
|
||||
[phpMyAdmin](http://www.phpmyadmin.net/home_page/index.php)):
|
||||
|
||||
Using an SQL 'Update' command, I can change the type value from 'blog' to
|
||||
'article'. This will change every occurance of the value 'blog'. If I wanted to
|
||||
only change certain nodes, I could add a 'Where' clause to only affect nodes
|
||||
with a certain nid or title.
|
||||
|
||||
Now, when I query the database, the type is shown as 'article'.
|
||||
|
||||
Now, when I go back into the administration section of my site and view the
|
||||
content, the content type now shows at 'News article'.
|
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
title: Checking if a user is logged into Drupal (the right way)
|
||||
date: 2013-01-09
|
||||
excerpt: How to check if a user is logged in by using Drupal core API functions.
|
||||
tags:
|
||||
- drupal
|
||||
- drupal-6
|
||||
- drupal-7
|
||||
- drupal-planet
|
||||
- php
|
||||
---
|
||||
|
||||
I see this regularly when working on Drupal sites when someone wants to check
|
||||
whether the current user is logged in to Drupal (authenticated) or not
|
||||
(anonymous).
|
||||
|
||||
```language-php
|
||||
global $user;
|
||||
if ($user->uid) {
|
||||
// The user is logged in.
|
||||
}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```language-php
|
||||
global $user;
|
||||
if (!$user->uid) {
|
||||
// The user is not logged in.
|
||||
}
|
||||
```
|
||||
|
||||
The better way to do this is to use the
|
||||
[user_is_logged_in()](http://api.drupal.org/api/drupal/modules!user!user.module/function/user_is_logged_in/7)
|
||||
function.
|
||||
|
||||
```language-php
|
||||
if (user_is_logged_in()) {
|
||||
// Do something.
|
||||
}
|
||||
```
|
||||
|
||||
This returns a boolean (TRUE or FALSE) depending or not the user is logged in.
|
||||
Essentially, it does the same thing as the first example, but there's no need to
|
||||
load the global variable.
|
||||
|
||||
A great use case for this is within a `hook_menu()` implementation within a
|
||||
custom module.
|
||||
|
||||
```language-php
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function mymodule_menu() {
|
||||
$items['foo'] = array(
|
||||
'title' => 'Foo',
|
||||
'page callback' => 'mymodule_foo',
|
||||
'access callback' => 'user_is_logged_in',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
```
|
||||
|
||||
There is also a
|
||||
[user_is_anonymous()](http://api.drupal.org/api/drupal/modules!user!user.module/function/user_is_anonymous/7)
|
||||
function if you want the opposite result. Both of these functions are available
|
||||
in Drupal 6 and higher.
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
title: Checkout a specific revision from SVN from the command line
|
||||
date: 2012-05-23
|
||||
excerpt: How to checkout a specific revision from a SVN (Subversion) repository.
|
||||
tags:
|
||||
- svn
|
||||
- version-control
|
||||
---
|
||||
|
||||
How to checkout a specific revision from a SVN (Subversion) repository.
|
||||
|
||||
If you're checking out the repository for the first time:
|
||||
|
||||
```language-bash
|
||||
$ svn checkout -r 1234 url://repository/path
|
||||
```
|
||||
|
||||
If you already have the repository checked out:
|
||||
|
||||
```language-bash
|
||||
$ svn up -r 1234
|
||||
```
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
title: Conditional Email Addresses in a Webform
|
||||
date: 2010-05-06
|
||||
excerpt:
|
||||
How to send webform emails to a different email address based on another
|
||||
field.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal-6
|
||||
- conditional-email
|
||||
- webform
|
||||
---
|
||||
|
||||
I created a new Webform to serve as a simple Contact form, but left the main
|
||||
configuration until after I created the form components. I added 'Name',
|
||||
'Email', 'Subject' and 'Message' fields, as well as a 'Category' select list.
|
||||
Below 'Options', I entered each of my desired options in the following format:
|
||||
|
||||
```language-ini
|
||||
Email address|Visible name
|
||||
```
|
||||
|
||||
I went back to the form configuration page and expanded 'Conditional Email
|
||||
Recipients', and selected my Category. Note that the standard 'Email To' field
|
||||
above it needs to be empty. Originally, I made the mistake of leaving addresses
|
||||
in that field which resulted in people being sent emails regardles of which
|
||||
category was selected. I then configured the rest of the form.
|
||||
|
||||
Then, when I went to the finished form, the category selection was available.
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
title: Configuring the Reroute Email Module
|
||||
date: 2014-12-22
|
||||
excerpt:
|
||||
How to configure the Reroute Email module, to prevent sending emails to real
|
||||
users from your pre-production sites!
|
||||
tags:
|
||||
- drupal
|
||||
- drupal-6
|
||||
- drupal-7
|
||||
- drupal-planet
|
||||
- email
|
||||
draft: true
|
||||
---
|
||||
|
||||
[Reroute Email](https://www.drupal.org/project/reroute_email) module uses
|
||||
`hook_mail_alter()` to prevent emails from being sent to users from
|
||||
non-production sites. It allows you to enter one or more email addresses that
|
||||
will receive the emails instead of delivering them to the original user.
|
||||
|
||||
> This is useful in case where you do not want email sent from a Drupal site to
|
||||
> reach the users. For example, if you copy a live site to a test site for the
|
||||
> purpose of development, and you do not want any email sent to real users of
|
||||
> the original site. Or you want to check the emails sent for uniform
|
||||
> formatting, footers, ...etc.
|
||||
|
||||
As we don't need the module configured on production (we don't need to reroute
|
||||
any emails there), it's best to do this in code using settings.local.php (if you
|
||||
have one) or the standard settings.php file.
|
||||
|
||||
The first thing that we need to do is to enable rerouting. Without doing this,
|
||||
nothing will happen.
|
||||
|
||||
```language-php
|
||||
$conf['reroute_email_enable'] = TRUE;
|
||||
```
|
||||
|
||||
The next option is to whether to show rerouting description in mail body. I
|
||||
usually have this enabled. Set this to TRUE or FALSE depending on your
|
||||
preference.
|
||||
|
||||
```language-php
|
||||
$conf['reroute_email_enable_message'] = TRUE;
|
||||
```
|
||||
|
||||
The last setting is the email address to use. If you're entering a single
|
||||
address, you can add it as a simple string.
|
||||
|
||||
```language-php
|
||||
$conf['reroute_email_address'] = 'person1@example.com';
|
||||
```
|
||||
|
||||
In this example, all emails from the site will be rerouted to
|
||||
person1@example.com.
|
||||
|
||||
If you want to add multiple addresses, these should be added in a
|
||||
semicolon-delimited list. Whilst you could add these also as a string, I prefer
|
||||
to use an array of addresses and the `implode()` function.
|
||||
|
||||
```language-php
|
||||
$conf['reroute_email_address'] = implode(';', array(
|
||||
'person1@example.com',
|
||||
'person2@example.com',
|
||||
'person3@example.com',
|
||||
));
|
||||
```
|
||||
|
||||
In this example, person2@example.com and person3@example.com would receive their
|
||||
emails from the site as normal. Any emails to addresses not in the array would
|
||||
continue to be redirected to person1@example.com.
|
50
sculpin-old/source/_posts/create-and-apply-patches.md
Normal file
50
sculpin-old/source/_posts/create-and-apply-patches.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
title: How to Create and Apply Patches
|
||||
date: 2010-10-10
|
||||
excerpt: How to create and apply patches, ready for the Drupal.org issue queues.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal-6
|
||||
- modules
|
||||
- patches
|
||||
---
|
||||
|
||||
Earlier this year, I posted a solution to
|
||||
[an issue](http://drupal.org/node/753898) on the Drupal.org issue queue.
|
||||
Originally, I just posted the code back onto the issue, but have now created a
|
||||
patch that can easily be applied to any Drupal 6 installation. Here is a
|
||||
run-through of the process of creating and applying a patch. In this case, I
|
||||
made changes to the `user_pass_validate()` function that's found within
|
||||
`modules/user/user.pages.inc`.
|
||||
|
||||
To begin with, a download a fresh copy of Drupal 6.19 and created a copy of the
|
||||
original user.pages.inc file. Within the duplicate file, I made the same changes
|
||||
to the function that I did in earlier code, and saved the changes. Now, within
|
||||
my Terminal, I can navigate to Drupal's root directory and create the patch.
|
||||
|
||||
```language-bash
|
||||
diff -rup modules/user/user.pages.inc modules/user/user.pages2.inc > /Users/oliver/Desktop/different_messages_for_blocked_users.patch
|
||||
```
|
||||
|
||||
This command compares the differences between the two files, and creates the
|
||||
specified patch file.
|
||||
|
||||
To apply the patch to my Drupal installation, I go back to Terminal and run the
|
||||
following code:
|
||||
|
||||
```language-bash
|
||||
patch -p0 < /Users/oliver/Desktop/different_messages_for_blocked_users.patch
|
||||
```
|
||||
|
||||
If, for some reason, I need to reverse the patch, I can run this code:
|
||||
|
||||
```language-bash
|
||||
patch -p0 -R < /Users/oliver/Desktop/different_messages_for_blocked_users.patch
|
||||
```
|
||||
|
||||
And that's it!
|
||||
|
||||
There is also a Git patch creation workflow, which is described at
|
||||
<http://groups.drupal.org/node/91424>. Thanks to
|
||||
[Randy Fay](http://randyfay.com) for making me aware of this, and suggesting a
|
||||
slight change to my original patch creation command.
|
|
@ -0,0 +1,161 @@
|
|||
---
|
||||
title: Create a Better Photo Gallery in Drupal - Part 1
|
||||
date: 2010-08-11
|
||||
excerpt:
|
||||
How I started converting and migrating a Coppermine photo gallery into Drupal.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal-6
|
||||
- photo-gallery
|
||||
- sql
|
||||
- views
|
||||
- sequel-pro
|
||||
- cck
|
||||
- views-attach
|
||||
- drupal
|
||||
---
|
||||
|
||||
Recently, I converted a client's static HTML website, along with their
|
||||
Coppermine Photo Gallery, into a Drupal-powered website.
|
||||
|
||||
Over the next few posts, I'll be replicating the process that I used during the
|
||||
conversion, and how I added some additional features to my Drupal gallery.
|
||||
|
||||
To begin with, I created my photo gallery as described by
|
||||
[Jeff Eaton](http://www.lullabot.com/about/team/jeff-eaton) in
|
||||
[this screencast](http://www.lullabot.com/articles/photo-galleries-views-attach),
|
||||
downloaded all my client's previous photos via FTP, and quickly added them into
|
||||
the new gallery using the
|
||||
[Imagefield Import](http://drupal.org/project/imagefield_import) module (which I
|
||||
mentioned
|
||||
[previously](/blog/quickly-import-multiples-images-using-imagefieldimport-module/)).
|
||||
|
||||
When I compare this to the previous gallery, I can see several differences which
|
||||
I'd like to include. The first of which is the number of photos in each gallery,
|
||||
and the date that the most recent photo was added.
|
||||
|
||||
To do this, I'd need to query my website's database. To begin with, I wanted to
|
||||
have a list of all the galleries on my site which are published, and what
|
||||
they're unique node ID values are. To do this, I opened Sequel Pro and entered
|
||||
the following code:
|
||||
|
||||
```language-sql
|
||||
SELECT title
|
||||
AS title, nid
|
||||
AS gallery_idFROM node
|
||||
WHERE type = 'gallery'
|
||||
AND status = 1;
|
||||
```
|
||||
|
||||
As the nid value of each gallery corresponds with the 'field_gallery_nid' field
|
||||
within the content_type_photo field, I can now query the database and retrieve
|
||||
information about each specific gallery.
|
||||
|
||||
For example, using [aliasing](http://www.w3schools.com/sql/sql_alias.asp) within
|
||||
my SQL statement, I can retrieve a list of all the published photos within the
|
||||
'British Squad 2008' gallery by using the following code:
|
||||
|
||||
```language-sql
|
||||
SELECT n.title, n.nid, p.field_gallery_nid
|
||||
FROM node n, content_type_photo p
|
||||
WHERE p.field_gallery_nid = 105
|
||||
AND n.status = 1
|
||||
AND n.nid = p.nid;
|
||||
```
|
||||
|
||||
I can easily change this to count the number of published nodes by changing the
|
||||
first line of the query to read SELECT COUNT(\*).
|
||||
|
||||
```language-sql
|
||||
SELECT COUNT(*)
|
||||
FROM node n, content_type_photo p
|
||||
WHERE p.field_gallery_nid = 105
|
||||
AND n.status = 1
|
||||
AND n.nid = p.nid;
|
||||
```
|
||||
|
||||
As I've used the [Views Attach](http://drupal.org/project/views_attach) module,
|
||||
and I'm embedding the photos directly into the Gallery nodes, I easily add this
|
||||
to each gallery by creating a custom node-gallery.tpl.php file within my theme.
|
||||
I can then use the following PHP code to retrieve the node ID for that specific
|
||||
gallery:
|
||||
|
||||
```language-php
|
||||
<?php
|
||||
$selected_gallery = db_result(db_query("
|
||||
SELECT nid
|
||||
FROM {node}
|
||||
WHERE type = 'gallery'
|
||||
AND title = '$title'
|
||||
"));
|
||||
?>
|
||||
```
|
||||
|
||||
I can then use this variable as part of my next query to count the number of
|
||||
photos within that gallery, similar to what I did earlier.
|
||||
|
||||
```language-php
|
||||
<?php
|
||||
$gallery_total = db_result(db_query("
|
||||
SELECT COUNT(*)
|
||||
FROM {content_type_photo}
|
||||
WHERE field_gallery_nid = $selected_gallery
|
||||
"));
|
||||
?>
|
||||
```
|
||||
|
||||
Next, I wanted to display the date that the last photo was displayed within each
|
||||
album. This was done by using a similar query that also sorted the results in a
|
||||
descending order, and limited it to one result - effectively only returning the
|
||||
created date for the newest photo.
|
||||
|
||||
```language-php
|
||||
<?php
|
||||
$latest_photo = db_result(db_query("
|
||||
SELECT n.created
|
||||
FROM {node} n, {content_type_photo} p
|
||||
WHERE p.field_gallery_nid = $selected_gallery
|
||||
AND n.nid = p.nid
|
||||
ORDER BY n.created DESC LIMIT 1
|
||||
"));
|
||||
?>
|
||||
```
|
||||
|
||||
This was all then added into a 'print' statement which displayed it into the
|
||||
page.
|
||||
|
||||
```language-php
|
||||
<?php
|
||||
if ($selected_gallery_total != 0) {
|
||||
$output = '<i>There are currently ' . $selected_gallery_total . ' photos in this gallery.';
|
||||
$output .= 'Last one added on ' . $latest_photo . '</i>';
|
||||
print $output;
|
||||
}
|
||||
?>
|
||||
```
|
||||
|
||||
OK, so let's take a look at the Gallery so far:
|
||||
|
||||
You will notice that the returned date value for the latest photo added is
|
||||
displaying the UNIX timestamp instead of in a more readable format. This can be
|
||||
changed by altering the 'print' statement to include a PHP 'date' function:
|
||||
|
||||
```language-php
|
||||
<?php
|
||||
if ($selected_gallery_total != 0) {
|
||||
$output = '<i>There are currently ' . $selected_gallery_total . ' photos in this gallery.';
|
||||
$output .= 'Last one added on ' . date("l, jS F, Y", $latest_photo) . '.</i>';
|
||||
print $output;
|
||||
}
|
||||
?>
|
||||
```
|
||||
|
||||
The values that I've entered are from
|
||||
[this page](http://php.net/manual/en/function.date.php) on PHP.net, and can be
|
||||
changed according on how you want the date to be displayed.
|
||||
|
||||
As I've added all of these photos today, then the correct dates are being
|
||||
displayed. However, on the client's original website, the majority of these
|
||||
photos were pubished several months or years ago, and I'd like the new website
|
||||
to still reflect the original created dates. As opposed to modifying each
|
||||
individual photograph, I'll be doing this in bulk in my next post.
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
title: Create a Better Photo Gallery in Drupal - Part 2
|
||||
date: 2010-08-17
|
||||
excerpt: Updating the galleries’ created and modified dates.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal-6
|
||||
- photo-gallery
|
||||
- sql
|
||||
- sequel-pro
|
||||
---
|
||||
|
||||
At the end of my last post, I'd finished creating the first part of the new
|
||||
photo gallery, but I wanted to change the dates of the published photos to
|
||||
reflect the ones on the client's original website.
|
||||
|
||||
Firstly, I'll refer to the previous list of published galleries that I created
|
||||
before, and create something different that also displays the created and
|
||||
modified dates. Picking the node ID of the required gallery, I used the
|
||||
following SQL query to display a list of photos.
|
||||
|
||||
```language-sql
|
||||
SELECT n.title, n.nid, n.created, n.changed, p.field_gallery_nid
|
||||
FROM node n, content_type_photo pWHERE n.type = 'photo'
|
||||
AND p.field_gallery_nid = 103AND n.nid = p.nid
|
||||
ORDER BY n.nid ASC;
|
||||
```
|
||||
|
||||
When I look back at the old photo gallery, I can see that the previous 'last
|
||||
added' date was June 27, 2008. So, how do I update my new photos to reflect that
|
||||
date? Using <http://www.onlineconversion.com/unix_time.htm>, I can enter the
|
||||
required date in its readable format, and it will give me the equivilent UNIX
|
||||
timestamp. To keep things relatively simple, I'll set all photos within this
|
||||
gallery to the same time.
|
||||
|
||||
The result that I'm given is '1217149200'. I can now use an UPDATE statement
|
||||
within another SQL query to update the created and modified dates.
|
||||
|
||||
```language-sql
|
||||
UPDATE node
|
||||
INNER JOIN content_type_photo
|
||||
ON node.nid = content_type_photo.nid
|
||||
SET
|
||||
node.created = 1217149200,
|
||||
node.changed = 1217149200
|
||||
WHERE content_type_photo.field_gallery_nid = 103
|
||||
```
|
||||
|
||||
Now when I query the database, both the created and modified dates have been
|
||||
updated, and when I return to the new photo gallery, the updated value is being
|
||||
displayed.
|
||||
|
||||
Once the changes have been applied, it's a case of repeating the above process
|
||||
for each of the required galleries.
|
||||
|
||||
In the next post, I'll explain how to add a count of published galleries and
|
||||
photos on the main photo gallery page, as well as how to install and configure
|
||||
the [Shadowbox](http://drupal.org/project/shadowbox) module.
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
title: Create a Better Photo Gallery in Drupal - Part 2.1
|
||||
date: 2010-10-22
|
||||
excerpt: The missing code to get totals of galleries and photos.
|
||||
tags:
|
||||
- drupal
|
||||
---
|
||||
|
||||
Today, I realised that I hadn't published the code that I used to create the
|
||||
total figures of galleries and photos at the top of the gallery (I said at the
|
||||
end of
|
||||
[Part 2](/blog/create-better-photo-gallery-drupal-part-2/ 'Create a Better Photo Gallery in Drupal - Part 2')
|
||||
that I'd include it in
|
||||
[Part 3](/blog/create-better-photo-gallery-drupal-part-3/ 'Create a Better Photo Gallery in Drupal - Part 3'),
|
||||
but I forgot). So, here it is:
|
||||
|
||||
```language-php
|
||||
<?php
|
||||
|
||||
// Queries the database and returns a list of nids of published galleries.
|
||||
$galleries = db_query("SELECT nid FROM {node} WHERE type = 'gallery' AND status = 1");
|
||||
// Resets the number of photos.
|
||||
$output = 0;
|
||||
// Prints a list of nids of published galleries.
|
||||
while($gallery = db_fetch_array($galleries)) {
|
||||
$gallery_id = $gallery['nid'];
|
||||
$photos = $photos + db_result(db_query("SELECT COUNT(*) FROM node n, content_type_photo ctp WHERE n.status = 1 AND n.type = 'photo' AND ctp.field_gallery_nid = $gallery_id AND n.nid = ctp.nid"));
|
||||
}
|
||||
|
||||
// Prints the output.
|
||||
print 'There ';
|
||||
if($photos == 1) {
|
||||
print 'is';
|
||||
}
|
||||
else {
|
||||
print 'are';
|
||||
}
|
||||
print ' currently ';
|
||||
print $photos . ' ';
|
||||
if($photos == 1) {
|
||||
print 'photo';
|
||||
}
|
||||
else {
|
||||
print 'photos';
|
||||
}
|
||||
print ' in ';
|
||||
|
||||
// Counts the number of published galleries on the site.
|
||||
$galleries = db_result(db_query("SELECT COUNT(*) FROM {node} WHERE TYPE = 'gallery' AND STATUS = 1"));
|
||||
|
||||
// Prints the number of published galleries.
|
||||
print $galleries;
|
||||
if ($galleries == 1) {
|
||||
print ' gallery';
|
||||
}
|
||||
else {
|
||||
print ' galleries';
|
||||
}
|
||||
print '.';
|
||||
?>
|
||||
```
|
||||
|
||||
It was applied to the view as a header which had the input format set to PHP
|
||||
code.
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
title: Create a Better Photo Gallery in Drupal - Part 3
|
||||
date: 2010-10-13
|
||||
excerpt: Grouping galleries by category.
|
||||
tags:
|
||||
- drupal
|
||||
---
|
||||
|
||||
The next part of the new gallery that I want to implement is to group the
|
||||
galleries by their respective categories. The first step is to edit my original
|
||||
photo_gallery view and add an additional display.
|
||||
|
||||
I've called it 'Taxonomy', and it's similar to the original 'All Galleries'
|
||||
view. The differences are that I've added the taxonomy term as an argument,
|
||||
removed the header, and updated the path to be `gallery/%`. The other thing that
|
||||
I need to do is overwrite the output of the original 'All Galleries' View by
|
||||
creating a file called `views-view--photo-gallery--page-1.tpl.php` and placing
|
||||
it within my theme directory.
|
||||
|
||||
Within that file, I can remove the standard content output. This still outputs
|
||||
the heading information from the original View. I can now use the function
|
||||
called 'views_embed_view' to embed my taxonomy display onto the display. The
|
||||
views_embed_view function is as follows:
|
||||
|
||||
```language-php
|
||||
<?php views_embed_view('my_view', 'block_1', $arg1, $arg2); ?>
|
||||
```
|
||||
|
||||
So, to display the galleries that are assigned the taxonomy of 'tournaments', I
|
||||
can use the following:
|
||||
|
||||
```language-php
|
||||
<?php print views_embed_view('photo_gallery', 'page_2', 'tournaments'); ?>
|
||||
```
|
||||
|
||||
To reduce the amount of code needed, I can use the following 'while' loop to
|
||||
generate the same code for each taxonomy term. It dynamically retrieves the
|
||||
relevant taxonomy terms from the database, and uses each name as the argument
|
||||
for the view.
|
||||
|
||||
```language-php
|
||||
<?php
|
||||
$terms = db_query("SELECT * FROM {term_data} WHERE vid = 1");
|
||||
while ($term = db_fetch_array($terms)) {
|
||||
print '<h3>' . $term['name'] . '</h3>';
|
||||
print views_embed_view('gallery', 'page_2', $term['name']);
|
||||
}
|
||||
?>
|
||||
```
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: Create a Block of Social Media Icons using CCK, Views and Nodequeue
|
||||
date: 2010-06-23
|
||||
excerpt: How to create a block of social media icons in Drupal.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal-6
|
||||
- drupal
|
||||
- views
|
||||
- nodequeue
|
||||
- oliverdavies.co.uk
|
||||
---
|
||||
|
||||
I recently decided that I wanted to have a block displayed in a sidebar on my
|
||||
site containing icons and links to my social media profiles -
|
||||
[Twitter](http://twitter.com/opdavies), [Facebook](http://facebook.com/opdavies)
|
||||
etc. I tried the [Follow](http://drupal.org/project/follow) module, but it
|
||||
lacked the option to add extra networks such my
|
||||
[Drupal.org](http://drupal.org/user/381388) account, and my
|
||||
[RSS feed](http://oliverdavies.co.uk/rss.xml). I started to create my own
|
||||
version, and then found
|
||||
[this Blog post](http://www.hankpalan.com/blog/drupal-themes/add-your-social-connections-drupal-icons)
|
||||
by Hank Palan.
|
||||
|
||||
I created a 'Social icon' content type with the body field removed, and with
|
||||
fields for a link and image - then downloaded the favicons from the appropriate
|
||||
websites to use.
|
||||
|
||||
However, instead of using a custom template (node-custom.tpl.php) file, I used
|
||||
the Views module.
|
||||
|
||||
I added fields for the node titles, and the link from the node's content. Both
|
||||
of these are excluded from being displayed on the site. I then re-wrote the
|
||||
output of the Icon field to create the link using the URL, and using the node's
|
||||
title as the image's alternative text and the link's title.
|
||||
|
||||
I also used the [Nodequeue](http://drupal.org/project/nodequeue) module to
|
||||
create a nodequeue and arrange the icons in the order that I wanted them to be
|
||||
displayed. Once this was added as a relationship within my View, I was able to
|
||||
use node's position in the nodequeue as the sort criteria.
|
||||
|
||||
To complete the process, I used the
|
||||
[CSS Injector](http://drupal.org/project/css_injector) module to add some
|
||||
additional CSS styling to position and space out the icons.
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
title: Create a Flickr Photo Gallery Using Feeds, CCK and Views
|
||||
date: 2010-06-28
|
||||
excerpt:
|
||||
In this tutorial, I'll show you how to create a photo gallery which uses
|
||||
photos imported from Flickr.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal-6
|
||||
- photo-gallery
|
||||
- views
|
||||
- cck
|
||||
- imagecache
|
||||
- feeds
|
||||
- filefield
|
||||
- flickr
|
||||
- imagefield
|
||||
---
|
||||
|
||||
In this tutorial, I'll show you how to create a photo gallery which uses photos
|
||||
imported from [Flickr](http://www.flickr.com).
|
||||
|
||||
The modules that I'll use to create the Gallery are:
|
||||
|
||||
- [CCK](http://drupal.org/project/cck)
|
||||
- [Feeds](http://drupal.org/project/feeds)
|
||||
- [Feeds Image Grabber](http://drupal.org/project/feeds_imagegrabber)
|
||||
- [FileField](http://drupal.org/project/filefield)
|
||||
- [ImageAPI](http://drupal.org/project/imageapi)
|
||||
- [ImageCache](http://drupal.org/project/imagecache)
|
||||
- [ImageField](http://drupal.org/project/imagefield)
|
||||
- [Views](http://drupal.org/project/views)
|
||||
|
||||
The first thing that I did was to create a content type to store my imported
|
||||
images. I named it 'Photo', removed the Body field, and added an Image field.
|
||||
|
||||
Next, I installed and configured the Feeds and Image Grabber module. I used an
|
||||
overridden default Feed to import my photos from Flickr using the following
|
||||
settings:
|
||||
|
||||
- **Basic settings:** I changed the Refresh time to 15 minutes.
|
||||
- **Processor settings:** I changed the content type to 'Photo', and the
|
||||
author's name from 'anonymous'.
|
||||
- **Processor mapping:** I added a new mapping from 'Item URL (link)' to 'Photo
|
||||
(FIG)'. The Photo FIG target is added by the Image Grabber module.
|
||||
|
||||
Next, I needed to create the actual Feed, which I did by clicking 'Import'
|
||||
within the Navigation menu, and clicking 'Feed'. I gave it a title, entered the
|
||||
URL to my RSS feed from Flickr, and enabled the Image Grabber for this feed.
|
||||
|
||||
Once the Feed is created, the latest 20 images from the RSS feed are imported
|
||||
and 20 new Photos nodes are created. In the example below, the image with the
|
||||
'Photo' label is the Image field mapped by the Image Grabber module. It is this
|
||||
image that I'll be displaying within my Gallery.
|
||||
|
||||
With the new Photo nodes created, I then created the View to display them.
|
||||
|
||||
The View selects the image within the Photo content type, and displays in it a
|
||||
grid using an ImageCache preset. The View is limited to 20 nodes per page, and
|
||||
uses a full pager if this is exceeded. The nodes are sorted by the descending
|
||||
post date, and filtered by whether or not they are published, and only to
|
||||
include Photo nodes.
|
||||
|
||||
As an additional effect, I also included the 'Feeds Item - Item Link' field,
|
||||
which is basically the original link from the RSS feed. By checking the box the
|
||||
exclude the item from the display, it is not shown, but makes the link available
|
||||
to be used elsewhere. By checking the box 'Re-write the output for this field'
|
||||
on the 'Content: Photo' field, I was able to add the replacement token (in this
|
||||
case, [url]) as the path for a link around each image. This meant that when
|
||||
someone clicked a thumbnail of a photo, they were directed to the Flickr website
|
||||
instead of the node within my Drupal site.
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
title: Create Multigroups in Drupal 7 using Field Collections
|
||||
date: 2011-08-28
|
||||
excerpt:
|
||||
How to replicate CCK’s multigroups in Drupal 7 using the Field Collections
|
||||
module.
|
||||
tags:
|
||||
- drupal-7
|
||||
- drupal-planet
|
||||
- cck
|
||||
- fields
|
||||
- field-collection
|
||||
- entity-api
|
||||
- multigroup
|
||||
---
|
||||
|
||||
One of my favourite things lately in Drupal 6 has been CCK 3, and more
|
||||
specifically, the Content Multigroups sub-module. Basically this allows you to
|
||||
create a fieldset of various CCK fields, and then repeat that multiple times.
|
||||
For example, I use it on this site whist creating invoices for clients. I have a
|
||||
fieldset called 'Line Item', containing 'Description', 'Quantity' and 'Price'
|
||||
fields. With a standard fieldset, I could only have one instance of each field -
|
||||
however, using a multigroup, I can create multiple groups of line items which I
|
||||
then use within the invoice.
|
||||
|
||||
But at the time of writing this, there is no CCK 3 version for Drupal 7. So, I
|
||||
created the same thing using
|
||||
[Field Collection](http://drupal.org/project/field_collection) and
|
||||
[Entity](http://drupal.org/project/entity) modules.
|
||||
|
||||
With the modules uploaded and enabled, go to admin/structure/field-collections
|
||||
and create a field collection.
|
||||
|
||||
With the module enabled, you can go to your content type and add a Field
|
||||
Collection field. By default, the only available Widget type is 'Hidden'.
|
||||
|
||||
Next, go to admin/structure/field-collections and add some fields to the field
|
||||
collection - the same way that you would for a content type. For this collection
|
||||
is going to contain two node reference fields - Image and Link.
|
||||
|
||||
With the Field Collection created, I can now add it as a field within my content
|
||||
type.
|
||||
|
||||
Whilst this works perfectly, the field collection is not editable from the node
|
||||
edit form. You need to load the node, and the collection is displayed here with
|
||||
add, edit, and delete buttons. This wasn't an ideal solution, and I wanted to be
|
||||
able to edit the fields within the collection from the node edit form - the same
|
||||
way as I can using multigroups in Drupal 6.
|
||||
|
||||
After some searching I found
|
||||
[a link to a patch](http://drupal.org/node/977890#comment-4184524) which when
|
||||
applied adds a 'subform' widget type to the field collection field and allows
|
||||
for it to be embedded into, and editable from within the node form. Going back
|
||||
to the content type fields page, and clicking on 'Hidden' (the name of the
|
||||
current widget), I can change it to subform and save my changes.
|
||||
|
||||
With this change applied, when I go back to add or edit a node within this
|
||||
content type, my field collection will be easily editable directly within the
|
||||
form.
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
title:
|
||||
Create an Omega Subtheme with LESS CSS Preprocessor using Omega Tools and
|
||||
Drush
|
||||
date: 2012-04-16
|
||||
excerpt: How to create an Omega subtheme on the command line using Drush.
|
||||
tags:
|
||||
- drupal-7
|
||||
- omega
|
||||
- theming
|
||||
- less
|
||||
- drupal-planet
|
||||
- drupal
|
||||
---
|
||||
|
||||
In this tutorial I'll be showing how to create an
|
||||
[Omega](http://drupal.org/project/omega) subtheme using the
|
||||
[Omega Tools](http://drupal.org/project/omega_tools) module, and have it working
|
||||
with the [LESS CSS preprocessor](http://lesscss.org).
|
||||
|
||||
The first thing that I need to do is download the Omega theme and the Omega
|
||||
Tools and [LESS](http://drupal.org/project/less 'LESS module on drupal.org')
|
||||
modules, and then to enable both modules. I'm doing this using Drush, but you
|
||||
can of course do this via the admin interface at admin/modules.
|
||||
|
||||
```language-bash
|
||||
$ drush dl less omega omega_tools;
|
||||
$ drush en -y less omega_tools
|
||||
```
|
||||
|
||||
With the Omega Tools module enabled I get the drush omega-subtheme command that
|
||||
creates my Omega subtheme programatically. Using this command, I'm creating a
|
||||
new subtheme, enabling it and setting it as the default theme on my site.
|
||||
|
||||
```language-bash
|
||||
$ drush omega-subtheme "Oliver Davies" --machine_name="oliverdavies" --enable --set-default
|
||||
```
|
||||
|
||||
By default, four stylesheets are created within the subtheme's css directory.
|
||||
The first thing that I'm going to do is rename `global.css` to `global.less`.
|
||||
|
||||
```language-bash
|
||||
$ mv css/global.css css/global.less
|
||||
```
|
||||
|
||||
Now I need to find all references to global.css within my oliverdavies.info
|
||||
file. I did this using `$ nano oliverdavies.info`, pressing `Ctrl+W` to search,
|
||||
then `Ctrl+R` to replace, entering `global.css` as the search phrase, and then
|
||||
`global.less` as the replacement text. After making any changes to
|
||||
oliverdavies.info, I need to clear Drupal's caches for the changes to be
|
||||
applied.
|
||||
|
||||
```language-bash
|
||||
$ drush cc all
|
||||
```
|
||||
|
||||
I tested my changes by making some quick additions to my global.less file and
|
||||
reloading the page.
|
||||
|
||||
If your changes aren't applied, then confirm that your global.less file is
|
||||
enabled within your theme's configuration. I did this by going to
|
||||
admin/appearance/settings/oliverdavies, clicking on the Toggle styles tab within
|
||||
_Layout configuration_ and finding global.less at the bottom of _Enable optional
|
||||
stylesheets_.
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
title: Create a Slideshow of Multiple Images Using Fancy Slide
|
||||
date: 2010-05-25
|
||||
excerpt: How to create a slideshow of images using Drupal’s Fancy Slide module.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal
|
||||
- drupal-6
|
||||
- fancy-slide
|
||||
- slideshow
|
||||
---
|
||||
|
||||
Whilst updating my About page, I thought about creating a slideshow of several
|
||||
images instead of just the one static image. When I looking on Drupal.org, the
|
||||
only slideshow modules were to create slideshows of images that were attached to
|
||||
different nodes - not multiple images attached to one node. Then, I found the
|
||||
[Fancy Slide](http://drupal.org/project/fancy_slide) module. It's a jQuery
|
||||
Slideshow module with features that include integration with the
|
||||
[CCK](http://drupal.org/project/cck),
|
||||
[ImageCache](http://drupal.org/project/imagecache) and
|
||||
[Nodequeue](http://drupal.org/project/nodequeue) modules.
|
||||
|
||||
I added an CCK Image field to my Page content type, and set the number of values
|
||||
to 3, then uploaded my images to the Page.
|
||||
|
||||
Whilst updating my About page, I thought about creating a slideshow of several
|
||||
images instead of just the one static image. When I looking on Drupal.org, the
|
||||
only slideshow modules were to create slideshows of images that were attached to
|
||||
different nodes - not multiple images attached to one node. Then, I found the
|
||||
[Fancy Slide](http://drupal.org/project/fancy_slide) module. It's a jQuery
|
||||
Slideshow module with features that include integration with the
|
||||
[CCK](http://drupal.org/project/cck),
|
||||
[ImageCache](http://drupal.org/project/imagecache) and
|
||||
[Nodequeue](http://drupal.org/project/nodequeue) modules. Once the Images were
|
||||
added, I went to the Fancy Slide settings page and created the slideshow.
|
||||
|
||||
I added the dimensions of my images, the type of animation, specified the node
|
||||
that contained the images, the slideshow field, delay between slides and
|
||||
transition speed. With the slideshow created, it now needed embedding into the
|
||||
page.
|
||||
|
||||
I added the following code into my About page, as described in the Fancy Slide
|
||||
readme.txt file - the number representing the ID of the slideshow.
|
||||
|
||||
```language-php
|
||||
<?php print theme('fancy_slide', 1); ?>
|
||||
```
|
||||
|
||||
In my opinion, this adds a nice effect to the About page. I like it because it's
|
||||
easy to set up, and easy to add additional images later on if required.
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
title: Create Virtual Hosts on Mac OS X Using VirtualHostX
|
||||
date: 2010-07-02
|
||||
excerpt:
|
||||
How to use the VirtualHostX application to manage virtual hosts on Mac OS X.
|
||||
tags:
|
||||
- drupal-planet
|
||||
- drupal-6
|
||||
- mamp
|
||||
- virtual-hosts
|
||||
- virtualhostx
|
||||
---
|
||||
|
||||
This isn't a Drupal related topic per se, but it is a walk-through of one of the
|
||||
applications that I use whilst doing Drupal development work. I assume, like
|
||||
most Mac OS X users, I use [MAMP](http://www.mamp.info/en/index.html) to run
|
||||
Apache, MySQL and PHP locally whilst developing. I also use virtual hosts in
|
||||
Apache to create local .dev domains which are as close as possible to the actual
|
||||
live domains. For example, if I was developing a site called mysite.com, my
|
||||
local development version would be mysite.dev.
|
||||
|
||||
Normally, I would have to edit the hosts file and Apache's httpd.conf file to
|
||||
create a virtual host. The first to set the domain and it's associated IP
|
||||
address, and the other to configure the domain's directory, default index file
|
||||
etc. However, using [VirtualHostX](http://clickontyler.com/virtualhostx), I can
|
||||
quickly create a virtual host without having to edt any files. Enter the virtual
|
||||
domain name, the local path and the port, and apply the settings. VirtualHostX
|
||||
automatically restarts Apache, so the domain is ready to work straight away. You
|
||||
can also enter custom directives from within the GUI.
|
||||
|
||||
There's also an option to share the host over the local network. Next, I intend
|
||||
on configuring a virtual Windows PC within VMware Fusion to view these domains
|
||||
so that I can do cross-browser testing before putting a site live.
|
||||
|
||||
I ensured that my Apache configuration within MAMP was set to port 80, and that
|
||||
VirtualHostX was using Apache from MAMP instead of Apple's built-in Apache.
|
||||
|
||||
**Note:** One problem that I had after setting this up, was that I was receving
|
||||
an error when attempting to open a Drupal website which said _'No such file or
|
||||
directory'._
|
||||
|
||||
After some troubleshooting, I found out that Web Sharing on my Mac had become
|
||||
enabled (I don't know why, I've never enabled it), and that this was causing a
|
||||
conflict with Apache. Once I opened my System Preferences and disabled it,
|
||||
everything worked fine!
|
||||
|
||||
This, along with [MAMP](http://www.mamp.info/en/index.html),
|
||||
[Coda](http://www.panic.com/coda), [Sequel Pro](http://www.sequelpro.com), and
|
||||
[Transmit](http://www.panic.com/transmit), has become an essential tool within
|
||||
my development environment.
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
title: Create a Zen Sub-theme Using Drush
|
||||
date: 2013-09-06
|
||||
excerpt: How to quickly create a Zen sub-theme using Drush.
|
||||
tags:
|
||||
- drupal
|
||||
- drupal-planet
|
||||
- drush
|
||||
- zen
|
||||
- theming
|
||||
---
|
||||
|
||||
How to use [Drush](https://drupal.org/project/drush) to quickly build a new
|
||||
sub-theme of [Zen](https://drupal.org/project/zen).
|
||||
|
||||
First, download the [Zen](https://drupal.org/project/zen 'The Zen theme') theme
|
||||
if you haven't already done so.
|
||||
|
||||
```language-bash
|
||||
$ drush dl zen
|
||||
```
|
||||
|
||||
This will now enable you to use the "drush zen" command.
|
||||
|
||||
```language-bash
|
||||
$ drush zen "Oliver Davies" oliverdavies --description="A Zen sub-theme for oliverdavies.co.uk" --without-rtl
|
||||
```
|
||||
|
||||
The parameters that I'm passing it are:
|
||||
|
||||
1. The human-readable name of the theme.
|
||||
2. The machine-readable name of the theme.
|
||||
3. The description of the theme (optional).
|
||||
4. A flag telling Drush not to include any right-to-left elements within my
|
||||
sub-theme as these aren't needed (optional).
|
||||
|
||||
This will create a new theme in sites/all/themes/oliverdavies.
|
||||
|
||||
For further help, type `$ drush help zen` to see the Drush help page for the zen
|
||||
command.
|
|
@ -0,0 +1,162 @@
|
|||
---
|
||||
title: Creating and using custom tokens in Drupal 7
|
||||
date: 2013-02-16
|
||||
excerpt:
|
||||
This post outlines the steps required to create your own custom tokens in
|
||||
Drupal.
|
||||
tags:
|
||||
- drupal
|
||||
- drupal-planet
|
||||
- drupal-7
|
||||
- tokens
|
||||
---
|
||||
|
||||
This post outlines the steps required to create your own custom tokens in
|
||||
Drupal.
|
||||
|
||||
When writing the recent releases of the
|
||||
[Copyright Block](http://drupal.org/project/copyright_block) module, I used
|
||||
tokens to allow the user to edit and customise their copyright message and place
|
||||
the copyright_message:dates token in the desired position. When the block is
|
||||
rendered, the token is replaced by the necessary dates.
|
||||
|
||||
We will be using the fictional _foo_ module to demonstrate this.
|
||||
|
||||
## Requirements
|
||||
|
||||
- [Token module](http://drupal.org/project/token)
|
||||
|
||||
## Recommended
|
||||
|
||||
- [Devel module](http://drupal.org/project/devel) - useful to run `dpm()` and
|
||||
`kpr()` functions
|
||||
- [Copyright Block module](http://drupal.org/project/copyright_block) - 7.x-2.x
|
||||
and 6.x-1.x use tokens, handy as a reference
|
||||
|
||||
## Implementing hook_token_info()
|
||||
|
||||
The first thing that we need to do is define the new token type and/or the token
|
||||
itself, along with it's descriptive text. To view the existing tokens and types,
|
||||
use `dpm(token_get_info());`, assuming that you have the
|
||||
[Devel module](http://drupal.org/project/devel) installed.
|
||||
|
||||
```language-php
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function foo_token_info() {
|
||||
$info = array();
|
||||
|
||||
// Add any new tokens.
|
||||
$info['tokens']['foo']['bar'] = t('This is my new bar token within the foo type.');
|
||||
|
||||
// Return them.
|
||||
return $info;
|
||||
}
|
||||
```
|
||||
|
||||
In this case, the token called _bar_ resides within the _foo_ group.
|
||||
|
||||
If I needed to add a new token within an existing token type, such as 'node',
|
||||
the syntax would be `$info['tokens']['node']['bar']`.
|
||||
|
||||
## Implementing hook_tokens()
|
||||
|
||||
Now that the Token module is aware of our new token, we now need to determine
|
||||
what the token is replaced with. This is done using `hook_tokens()`. Here is the
|
||||
basic code needed for an implementation:
|
||||
|
||||
```language-php
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function foo_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$replacements = array();
|
||||
|
||||
// Code goes here...
|
||||
|
||||
// Return the replacements.
|
||||
return $replacements;
|
||||
}
|
||||
```
|
||||
|
||||
The first thing to check for is the type of token using an `if()` function, as
|
||||
this could be an existing type like 'node', 'user' or 'site', or a custom token
|
||||
type like 'foo'. Once we're sure that we're looking at the right type(s), we can
|
||||
use `foreach ($tokens as $name => $original)` to loop through each of the
|
||||
available tokens using a `switch()`. For each token, you can perform some logic
|
||||
to work out the replacement text and then add it into the replacements array
|
||||
using `$replacements[$original] = $new;`.
|
||||
|
||||
```language-php
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function foo_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$replacements = array();
|
||||
|
||||
// The first thing that we're going to check for is the type of token - node,
|
||||
// user etc...
|
||||
if ($type == 'foo') {
|
||||
// Loop through each of the available tokens.
|
||||
foreach ($tokens as $name => $original) {
|
||||
// Find the desired token by name
|
||||
switch ($name) {
|
||||
case 'bar':
|
||||
$new = '';
|
||||
|
||||
// Work out the value of $new...
|
||||
|
||||
// Add the new value into the replacements array.
|
||||
$replacements[$original] = $new;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the replacements.
|
||||
return $replacements;
|
||||
}
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
An example from Copyright Block module:
|
||||
|
||||
```language-php
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function copyright_block_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$replacements = array();
|
||||
|
||||
if ($type == 'copyright_statement') {
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
case 'dates':
|
||||
$start_year = variable_get('copyright_block_start_year', date('Y'));
|
||||
$current_year = date('Y');
|
||||
|
||||
$replacements[$original] = $start_year < $current_year ? $start_year . '-' . $current_year : $start_year;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
||||
```
|
||||
|
||||
## Using token_replace()
|
||||
|
||||
With everything defined, all that we now need to do is pass some text through
|
||||
the `token_replace()` function to replace it with the values defined within
|
||||
`hook_token()`.
|
||||
|
||||
```language-php
|
||||
$a = t('Something');
|
||||
// This would use any token type - node, user etc.
|
||||
$b = token_replace($a);
|
||||
// This would only use foo tokens.
|
||||
$c = token_replace($a, array('foo'));
|
||||
```
|
366
sculpin-old/source/_posts/creating-custom-docksal-commands.md
Normal file
366
sculpin-old/source/_posts/creating-custom-docksal-commands.md
Normal file
|
@ -0,0 +1,366 @@
|
|||
---
|
||||
title: Creating a Custom PHPUnit Command for Docksal
|
||||
date: 2018-05-06
|
||||
excerpt:
|
||||
How to write custom commands for Docksal, including one to easily run PHPUnit
|
||||
tests in Drupal 8.
|
||||
tags:
|
||||
- docksal
|
||||
- drupal
|
||||
- drupal-8
|
||||
- drupal-planet
|
||||
- phpunit
|
||||
- testing
|
||||
---
|
||||
|
||||
This week I’ve started writing some custom commands for my Drupal projects that
|
||||
use Docksal, including one to easily run PHPUnit tests in Drupal 8. This is the
|
||||
process of how I created this command.
|
||||
|
||||
## What is Docksal?
|
||||
|
||||
Docksal is a local Docker-based development environment for Drupal projects and
|
||||
other frameworks and CMSes. It is our standard tool for local environments for
|
||||
projects at [Microserve][0].
|
||||
|
||||
There was a [great talk][1] recently at Drupaldelphia about Docksal.
|
||||
|
||||
## Why write a custom command?
|
||||
|
||||
One of the things that Docksal offers (and is covered in the talk) is the
|
||||
ability to add custom commands to the Docksal’s `fin` CLI, either globally or as
|
||||
part of your project.
|
||||
|
||||
As an advocate of automated testing and TDD practitioner, I write a lot of tests
|
||||
and run PHPUnit numerous times a day. I’ve also given [talks][6] and have
|
||||
[written other posts][7] on this site relating to testing in Drupal.
|
||||
|
||||
There are a couple of ways to run PHPUnit with Docksal. The first is to use
|
||||
`fin bash` to open a shell into the container, move into the docroot directory
|
||||
if needed, and run the `phpunit` command.
|
||||
|
||||
```bash
|
||||
fin bash
|
||||
cd /var/www/docroot
|
||||
../vendor/bin/phpunit -c core modules/custom
|
||||
```
|
||||
|
||||
Alternatively, it can be run from the host machine using `fin exec`.
|
||||
|
||||
```
|
||||
cd docroot
|
||||
fin exec '../vendor/bin/phpunit -c core modules/custom'
|
||||
```
|
||||
|
||||
Both of these options require multiple steps as we need to be in the `docroot`
|
||||
directory where the Drupal code is located before the command can be run, and
|
||||
both have quite long commands to run PHPUnit itself - some of which is repeated
|
||||
every time.
|
||||
|
||||
By adding a custom command, I intend to:
|
||||
|
||||
1. Make it easier to get set up to run PHPUnit tests - i.e. setting up a
|
||||
`phpunit.xml` file.
|
||||
1. Make it easier to run the tests that we’d written by shortening the command
|
||||
and making it so it can be run anywhere within our project.
|
||||
|
||||
I also hoped to make it project agnostic so that I could add it onto any project
|
||||
and immediately run it.
|
||||
|
||||
## Creating the command
|
||||
|
||||
Each command is a file located within the `.docksal/commands` directory. The
|
||||
filename is the name of the command (e.g. `phpunit`) with no file extension.
|
||||
|
||||
To create the file, run this from the same directory where your `.docksal`
|
||||
directory is:
|
||||
|
||||
```bash
|
||||
mkdir -p .docksal/commands
|
||||
touch .docksal/commands/phpunit
|
||||
```
|
||||
|
||||
This will create a new, empty `.docksal/commands/phpunit` file, and now the
|
||||
`phpunit` command is now listed under "Custom commands" when we run `fin`.
|
||||
|
||||

|
||||
|
||||
You can write commands with any interpreter. I’m going to use bash, so I’ll add
|
||||
the shebang to the top of the file.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
```
|
||||
|
||||
With this in place, I can now run `fin phpunit`, though there is no output
|
||||
displayed or actions performed as the rest of the file is empty.
|
||||
|
||||
## Adding a description and help text
|
||||
|
||||
Currently the description for our command when we run `fin` is the default "No
|
||||
description" text. I’d like to add something more relevant, so I’ll start by
|
||||
adding a new description.
|
||||
|
||||
fin interprets lines starting with `##` as documentation - the first of which it
|
||||
uses as the description.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
## Run automated PHPUnit tests.
|
||||
```
|
||||
|
||||
Now when I run it, I see the new description.
|
||||
|
||||

|
||||
|
||||
Any additional lines are used as help text with running `fin help phpunit`. Here
|
||||
I’ll add an example command to demonstrate how to run it as well as some more
|
||||
in-depth text about what the command will do.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
## Run automated PHPUnit tests.
|
||||
##
|
||||
## Usage: fin phpunit <args>
|
||||
##
|
||||
## If a core/phpunit.xml file does not exist, copy one from elsewhere.
|
||||
## Then run the tests.
|
||||
```
|
||||
|
||||
Now when I run `fin help phpunit`, I see the new help text.
|
||||
|
||||

|
||||
|
||||
## Adding some content
|
||||
|
||||
### Setting the target
|
||||
|
||||
As I want the commands to be run within Docksal’s "cli" container, I can specify
|
||||
that with `exec_target`. If one isn’t specified, the commands are run locally on
|
||||
the host machine.
|
||||
|
||||
```
|
||||
#: exec_target = cli
|
||||
```
|
||||
|
||||
### Available variables
|
||||
|
||||
These variables are provided by fin and are available to use within any custom
|
||||
commands:
|
||||
|
||||
- `PROJECT_ROOT` - The absolute path to the nearest `.docksal` directory.
|
||||
- `DOCROOT` - name of the docroot folder.
|
||||
- `VIRTUAL_HOST` - the virtual host name for the project. Such as
|
||||
`myproject.docksal`.
|
||||
- `DOCKER_RUNNING` - (string) "true" or "false".
|
||||
|
||||
<div class="note" markdown="1">
|
||||
**Note:** If the `DOCROOT` variable is not defined within the cli container, ensure that it’s added to the environment variables in `.docksal/docksal.yml`. For example:
|
||||
|
||||
```
|
||||
version: "2.1"
|
||||
|
||||
services:
|
||||
cli:
|
||||
environment:
|
||||
- DOCROOT
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Running phpunit
|
||||
|
||||
When you run the `phpunit` command, there are number of options you can pass to
|
||||
it such as `--filter`, `--testsuite` and `--group`, as well as the path to the
|
||||
tests to execute, such as `modules/custom`.
|
||||
|
||||
I wanted to still be able to do this by running `fin phpunit <args>` so the
|
||||
commands can be customised when executed. However, as the first half of the
|
||||
command (`../vendor/bin/phpunit -c core`) is consistent, I can wrap that within
|
||||
my custom command and not need to type it every time.
|
||||
|
||||
By using `"$@"` I can capture any additional arguments, such as the test
|
||||
directory path, and append them to the command to execute.
|
||||
|
||||
I’m using `$PROJECT_ROOT` to prefix the command with the absolute path to
|
||||
`phpunit` so that I don’t need to be in that directory when I run the custom
|
||||
command, and `$DOCROOT` to always enter the sub-directory where Drupal is
|
||||
located. In this case, it’s "docroot" though I also use "web" and I’ve seen
|
||||
various others used.
|
||||
|
||||
```bash
|
||||
DOCROOT_PATH="${PROJECT_ROOT}/${DOCROOT}"
|
||||
DRUPAL_CORE_PATH="${DOCROOT_PATH}/core"
|
||||
|
||||
# If there is no phpunit.xml file, copy one from elsewhere.
|
||||
|
||||
# Otherwise run the tests.
|
||||
${PROJECT_ROOT}/vendor/bin/phpunit -c ${DRUPAL_CORE_PATH} "$@"
|
||||
```
|
||||
|
||||
For example, `fin phpunit modules/custom` would execute
|
||||
`/var/www/vendor/bin/phpunit -c /var/www/docroot/core modules/custom` within the
|
||||
container.
|
||||
|
||||
I can then wrap this within a condition so that the tests are only run when a
|
||||
`phpunit.xml` file exists, as it is required for them to run successfully.
|
||||
|
||||
```bash
|
||||
if [ ! -e ${DRUPAL_CORE_PATH}/phpunit.xml ]; then
|
||||
# If there is no phpunit.xml file, copy one from elsewhere.
|
||||
else
|
||||
${PROJECT_ROOT}/vendor/bin/phpunit -c ${DRUPAL_CORE_PATH} "$@"
|
||||
fi
|
||||
```
|
||||
|
||||
### Creating phpunit.xml - step 1
|
||||
|
||||
My first thought was that if a `phpunit.xml` file doesn’t exist was to duplicate
|
||||
core’s `phpunit.xml.dist` file. However this isn’t enough to run the tests, as
|
||||
values such as `SIMPLETEST_BASE_URL`, `SIMPLETEST_DB` and
|
||||
`BROWSERTEST_OUTPUT_DIRECTORY` need to be populated.
|
||||
|
||||
As the tests wouldn't run at this point, I’ve exited early and displayed a
|
||||
message to the user to edit the new `phpunit.xml` file and run `fin phpunit`
|
||||
again.
|
||||
|
||||
```bash
|
||||
if [ ! -e ${DRUPAL_CORE_PATH}/phpunit.xml ]; then
|
||||
echo "Copying ${DRUPAL_CORE_PATH}/phpunit.xml.dist to ${DRUPAL_CORE_PATH}/phpunit.xml."
|
||||
echo "Please edit it's values as needed and re-run 'fin phpunit'."
|
||||
cp ${DRUPAL_CORE_PATH}/phpunit.xml.dist ${DRUPAL_CORE_PATH}/phpunit.xml
|
||||
exit 1;
|
||||
else
|
||||
${PROJECT_ROOT}/vendor/bin/phpunit -c ${DRUPAL_CORE_PATH} "$@"
|
||||
fi
|
||||
```
|
||||
|
||||
However this isn’t as streamlined as I originally wanted as it still requires
|
||||
the user to perform an additional step before the tests can run.
|
||||
|
||||
### Creating phpunit.xml - step 2
|
||||
|
||||
My second idea was to keep a pre-configured file within the project repository,
|
||||
and to copy that into the expected location. That approach would mean that the
|
||||
project specific values would already be populated, as well as any
|
||||
customisations made to the default settings. I decided on
|
||||
`.docksal/drupal/core/phpunit.xml` to be the potential location.
|
||||
|
||||
Also, if this file is copied then we can go ahead and run the tests straight
|
||||
away rather than needing to exit early.
|
||||
|
||||
If a pre-configured file doesn’t exist, then we can default back to copying
|
||||
`phpunit.xml.dist`.
|
||||
|
||||
To avoid duplication, I created a reusable `run_tests()` function so it could be
|
||||
executed in either scenario.
|
||||
|
||||
```bash
|
||||
run_tests() {
|
||||
${PROJECT_ROOT}/vendor/bin/phpunit -c ${DRUPAL_CORE_PATH} "$@"
|
||||
}
|
||||
|
||||
if [ ! -e ${DRUPAL_CORE_PATH}/phpunit.xml ]; then
|
||||
if [ -e "${PROJECT_ROOT}/.docksal/drupal/core/phpunit.xml" ]; then
|
||||
echo "Copying ${PROJECT_ROOT}/.docksal/drupal/core/phpunit.xml to ${DRUPAL_CORE_PATH}/phpunit.xml"
|
||||
cp "${PROJECT_ROOT}/.docksal/drupal/core/phpunit.xml" ${DRUPAL_CORE_PATH}/phpunit.xml
|
||||
run_tests "$@"
|
||||
else
|
||||
echo "Copying ${DRUPAL_CORE_PATH}/phpunit.xml.dist to ${DRUPAL_CORE_PATH}/phpunit.xml."
|
||||
echo "Please edit it's values as needed and re-run 'fin phpunit'."
|
||||
cp ${DRUPAL_CORE_PATH}/phpunit.xml.dist ${DRUPAL_CORE_PATH}/phpunit.xml
|
||||
exit 1;
|
||||
fi
|
||||
else
|
||||
run_tests "$@"
|
||||
fi
|
||||
```
|
||||
|
||||
This means that I can execute less steps and run a much shorter command compared
|
||||
to the original, and even if someone didn’t have a `phpunit.xml` file created
|
||||
they could have copied into place and have tests running with only one command.
|
||||
|
||||
## The finished file
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#: exec_target = cli
|
||||
|
||||
## Run automated PHPUnit tests.
|
||||
##
|
||||
## Usage: fin phpunit <args>
|
||||
##
|
||||
## If a core/phpunit.xml file does not exist, one is copied from
|
||||
## .docksal/core/phpunit.xml if that file exists, or copied from the default
|
||||
## core/phpunit.xml.dist file.
|
||||
|
||||
DOCROOT_PATH="${PROJECT_ROOT}/${DOCROOT}"
|
||||
DRUPAL_CORE_PATH="${DOCROOT_PATH}/core"
|
||||
|
||||
run_tests() {
|
||||
${PROJECT_ROOT}/vendor/bin/phpunit -c ${DRUPAL_CORE_PATH} "$@"
|
||||
}
|
||||
|
||||
if [ ! -e ${DRUPAL_CORE_PATH}/phpunit.xml ]; then
|
||||
if [ -e "${PROJECT_ROOT}/.docksal/drupal/core/phpunit.xml" ]; then
|
||||
echo "Copying ${PROJECT_ROOT}/.docksal/drupal/core/phpunit.xml to ${DRUPAL_CORE_PATH}/phpunit.xml"
|
||||
cp "${PROJECT_ROOT}/.docksal/drupal/core/phpunit.xml" ${DRUPAL_CORE_PATH}/phpunit.xml
|
||||
run_tests "$@"
|
||||
else
|
||||
echo "Copying phpunit.xml.dist to phpunit.xml"
|
||||
echo "Please edit it's values as needed and re-run 'fin phpunit'."
|
||||
cp ${DRUPAL_CORE_PATH}/phpunit.xml.dist ${DRUPAL_CORE_PATH}/phpunit.xml
|
||||
exit 0;
|
||||
fi
|
||||
else
|
||||
run_tests "$@"
|
||||
fi
|
||||
```
|
||||
|
||||
It’s currently available as a [GitHub Gist][2], though I’m planning on moving it
|
||||
into a public GitHub repository either on my personal account or the [Microserve
|
||||
organisation][3], for people to either use as examples or to download and use
|
||||
directly.
|
||||
|
||||
I’ve also started to add other commands to projects such as `config-export` to
|
||||
standardise the way to export configuration from Drupal 8, run Drupal 7 tests
|
||||
with SimpleTest, and compile front-end assets like CSS within custom themes.
|
||||
|
||||
I think it’s a great way to shorten existing commands, or to group multiple
|
||||
commands into one like in this case, and I can see a lot of other potential uses
|
||||
for it during local development and continuous integration. Also being able to
|
||||
run one command like `fin init` and have it set up everything for your project
|
||||
is very convenient and a big time saver!
|
||||
|
||||
<div class="note" markdown="1">
|
||||
Since writing this post, I’ve had a [pull request][8] accepted for this command to be added as a [Docksal add-on][9]. This means that the command can be added to any Docksal project by running `fin addon install phpunit`. It will be installed into the `.docksal/addons/phpunit` directory, and displayed under "Addons" rather than "Custom commands" when you run `fin`.
|
||||
</div>
|
||||
|
||||
## Resources
|
||||
|
||||
- [PHPUnit](https://phpunit.de)
|
||||
- [PHPUnit in Drupal 8][4]
|
||||
- [Main Docksal website](https://docksal.io)
|
||||
- [Docksal documentation](https://docksal.readthedocs.io)
|
||||
- [Docksal: one tool to rule local and CI/CD environments][1] - Docksal talk
|
||||
from Drupaldelphia
|
||||
- [phpcs example custom command][5]
|
||||
- [phpunit command Gist][2]
|
||||
- [Docksal addons blog post][9]
|
||||
- [Docksal addons repository][10]
|
||||
|
||||
[0]: {{site.companies.microserve.url}}
|
||||
[1]: https://youtu.be/1sjsvnx1P7g
|
||||
[2]: https://gist.github.com/opdavies/72611f198ffd2da13f363ea65264b2a5
|
||||
[3]: {{site.companies.microserve.github}}
|
||||
[4]: https://www.drupal.org/docs/8/phpunit
|
||||
[5]:
|
||||
https://github.com/docksal/docksal/blob/develop/examples/.docksal/commands/phpcs
|
||||
[6]: /talks/tdd-test-driven-drupal
|
||||
[7]: /articles/tags/testing
|
||||
[8]: https://github.com/docksal/addons/pull/15
|
||||
[9]: https://blog.docksal.io/installing-addons-in-a-docksal-project-172a6c2d8a5b
|
||||
[10]: https://github.com/docksal/addons
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: Creating Local and Staging sites with Drupal's Domain Module Enabled
|
||||
date: 2013-07-17
|
||||
excerpt: How to use aliases within Domain module for pre-production sites.
|
||||
tags:
|
||||
- drupal
|
||||
- drupal-planet
|
||||
- databases
|
||||
- domain
|
||||
- table-prefixing
|
||||
---
|
||||
|
||||
The
|
||||
[Domain Access project](https://drupal.org/project/domain 'The Domain Access project on Drupal.org')
|
||||
is a suite of modules that provide tools for running a group of affiliated sites
|
||||
from one Drupal installation and a single shared database. The issue is that the
|
||||
domains are stored within the database so these are copied across when the data
|
||||
is migrated between environments, whereas the domains are obviously going to
|
||||
change.
|
||||
|
||||
Rather than changing the domain settings within the Domain module itself, the
|
||||
best solution I think is to use table prefixes and create a different domain
|
||||
table per environment. With a live, staging and local domains, the tables would
|
||||
be named as follows:
|
||||
|
||||
```language-bash
|
||||
live_domain
|
||||
local_domain
|
||||
staging_domain
|
||||
```
|
||||
|
||||
Within each site's settings.php file, define the prefix for the domain table
|
||||
within the databases array so that each site is looking at the correct table for
|
||||
its environment.
|
||||
|
||||
```language-php
|
||||
$databases['default']['default'] = array(
|
||||
'driver' => 'mysql',
|
||||
'database' => 'foobar',
|
||||
'username' => 'foo',
|
||||
'password' => 'bar',
|
||||
'host' => 'localhost',
|
||||
'prefix' => array(
|
||||
'default' => '',
|
||||
'domain' => 'local_', // This will use the local_domain table.
|
||||
// Add any other prefixed tables here.
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
Within each environment-specific domain table, update the subdomain column to
|
||||
contain the appropriate domain names.
|
83
sculpin-old/source/_posts/croeso-php-south-wales.md
Normal file
83
sculpin-old/source/_posts/croeso-php-south-wales.md
Normal file
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
title: Croeso PHP South Wales!
|
||||
date: 2018-08-01
|
||||
excerpt:
|
||||
Last night was the first meetup of Cardiff’s PHP South Wales user group.
|
||||
tags:
|
||||
- php
|
||||
- php-south-wales
|
||||
- meetups
|
||||
has_tweets: true
|
||||
---
|
||||
|
||||
Last night was the first meetup of Cardiff’s [PHP South Wales user group][0]! It
|
||||
was a great first event, and it was great to meet a lot of new people as well as
|
||||
catch up some familiars within the 36 (according to meetup.com) attendees -
|
||||
including some [PHP South West][9] regulars.
|
||||
|
||||
Organised by Steve and Amy McDougall, it was held in Barclays’ [Eagle Lab][1]
|
||||
which was a great space, and it was cool to be back in Brunel House having
|
||||
worked in that building previously whilst at Appnovation.
|
||||
|
||||
{% include 'tweet' with {
|
||||
class: 'my-6',
|
||||
data_cards: true,
|
||||
content: '<p lang="en" dir="ltr">Pretty cool being back in the centre of Cardiff. <a href="https://t.co/kh7Oi2tPDD">pic.twitter.com/kh7Oi2tPDD</a></p>— Oliver Davies (@opdavies) <a href="https://twitter.com/opdavies/status/1024377438611156992?ref_src=twsrc%5Etfw">July 31, 2018</a>',
|
||||
} %}
|
||||
|
||||
## 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: '<p lang="en" dir="ltr">Great to see <a href="https://twitter.com/akrabat?ref_src=twsrc%5Etfw">@akrabat</a> speaking about serverless PHP at the first <a href="https://twitter.com/phpSouthWales?ref_src=twsrc%5Etfw">@phpSouthWales</a> meetup. <a href="https://twitter.com/hashtag/php?src=hash&ref_src=twsrc%5Etfw">#php</a> <a href="https://twitter.com/hashtag/phpc?src=hash&ref_src=twsrc%5Etfw">#phpc</a> <a href="https://twitter.com/hashtag/cardiff?src=hash&ref_src=twsrc%5Etfw">#cardiff</a> <a href="https://t.co/Q9YaQ6O1fB">pic.twitter.com/Q9YaQ6O1fB</a></p>— Oliver Davies (@opdavies) <a href="https://twitter.com/opdavies/status/1024359937063956484?ref_src=twsrc%5Etfw">July 31, 2018</a>',
|
||||
} %}
|
||||
|
||||
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: '<p lang="en" dir="ltr">Lightning talk time, first <a href="https://twitter.com/IsmaelVelasco?ref_src=twsrc%5Etfw">@IsmaelVelasco</a> talking about <a href="https://twitter.com/hashtag/PWA?src=hash&ref_src=twsrc%5Etfw">#PWA</a> 😎🎉 <a href="https://t.co/KrJGZlIp7V">pic.twitter.com/KrJGZlIp7V</a></p>— PHP South Wales (@phpSouthWales) <a href="https://twitter.com/phpSouthWales/status/1024377906456420352?ref_src=twsrc%5Etfw">July 31, 2018</a>',
|
||||
} %}
|
||||
|
||||
## 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
|
|
@ -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
|
|
@ -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')
|
||||
)
|
||||
));
|
||||
```
|
|
@ -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.
|
|
@ -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
|
||||
<https://www.facebook.com/pages/edit/?id=143394365692197&sk=basic>.
|
||||
|
||||
I've also wrapped the output in a number_format() function so that it properly
|
||||
formatted with commas etc - like where I've used it within the
|
||||
[Gold Event listing](http://www.horseandcountry.tv/events/paid) on the Horse &
|
||||
Country TV website.
|
||||
|
||||
```language-php
|
||||
$page_id = "143394365692197";
|
||||
$xml = @simplexml_load_file("http://api.facebook.com/restserver.php?method=facebook.fql.query&query=SELECT%20fan_count%20FROM%20page%20WHERE%20page_id=".$page_id."") or die ("a lot");
|
||||
$fans = $xml->page->fan_count;
|
||||
print number_format($fans);
|
||||
```
|
||||
|
||||
This code was originally found at
|
||||
<http://wp-snippets.com/display-number-facebook-fans>.
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue