<?xml version="1.0"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel/><atom:link href="https://jan.henckens.be/" rel="self" type="application/rss+xml"/><item><title>Reconnecting</title><link>https://jan.henckens.be/blog/2026-06-10-reconnecting</link><guid isPermaLink="true">4c9f8769-e25a-47d5-a7f7-4c0635f2b709</guid><description>&lt;![CDATA[Back it my first job, I had a coworker with whom I got along really well - I became a regular at the family home, got to know her daughter, we works at festivals together, etc, etc. &lt;br /&gt;
&lt;br /&gt;
After leaving that job, which I did a little over 13 years ago, I lost touch with them. Sad, but that's how it goes.&lt;br /&gt;
&lt;br /&gt;
3 years ago I moved out of the city and in with my girlfriend. A couple of months later Instagram - through god knows which algorithm - suggested that I follow her daughter.  There I discovered that she ran a restaurant in the next village over from where our house is. &lt;br /&gt;
&lt;br /&gt;
Time goes on, I sort of forget about it again. Until a couple of days ago when I get a text from that colleague - telling me they're retired now, they moved out of the city years ago so well and that she had heard that we now live close by. A couple of messages later and we're having dinner next week.&lt;br /&gt;
&lt;br /&gt;
I'm really happy she got in touch and I'm really looking forward to catching up them all of them &#x2764;&#xFE0F;]]&gt;</description><pubDate>2026-06-10T21:44:00+02:00</pubDate></item><item><title>There's a War Going On But No One Can See It</title><link>https://jan.henckens.be/blog/2026-01-02-theres-a-war-going-on-but-no-one-can-see-it</link><guid isPermaLink="true">99aea185-4d9f-47a4-868d-af5c34c51f94</guid><description>&lt;![CDATA[I came across this one while making a Christmas list to send to my family and my Dad gifted it to me. &lt;br /&gt;
&lt;br /&gt;
Huib Modderkolk, a dutch investigative journalist, writes about his experiences covering the world of cybercrime, digital espionage and hacking. The book centers around a couple of stories, happening in or around The Netherlands between 2012 and 2022. &lt;br /&gt;
&lt;br /&gt;
It goes in depth into the hacking of Belgium's national telecommunications provider Belgacom by CIA &amp; GCHQ (link to The Guardian story about it) and gets pretty detailed and technical at times. &lt;br /&gt;
&lt;br /&gt;
I'm quite technical and am interested in these topics so I knew some things before reading this book. Still it was a good refresher on  the general premise of cybercrime and hacking, and it highlights the fact that it isn't something happening only in movies or fiction books. It is happening everyday and It is happening here as well.]]&gt;</description><pubDate>2026-01-02T12:39:00+01:00</pubDate></item><item><title>European Running Championships 2025 Preview</title><link>https://jan.henckens.be/blog/2025-04-05-european-running-championships-2025-preview</link><guid isPermaLink="true">38837339-4785-4ae9-8529-52842d9c0551</guid><description>&lt;![CDATA[It'll be my road marathon 8 or 9 years, and in the intervening time I haven't been doing much running at all. I've been cycling a lot, I've helped my girlfriend build a house - but running wasn't really on the schedule. &lt;br /&gt;
&lt;br /&gt;
Until the fall of '24, when I read that the above mentioned championships were coming to what I consider my hometown. Straight away a bunch of friends and coworkers were signing up and it seemed like a good opportunity to try and find my love for running again.&lt;br /&gt;
&lt;br /&gt;
I started building up to 10km and running 2 or 3 times each week - Promising myself a new running watch (the battery of my Garmin FR945 had seen better days) once I could comfortably run 10km again.&lt;br /&gt;
&lt;br /&gt;
## Winter training&lt;br /&gt;
By the end of the year, my shape was on the way up but the actual marathon training still had to start. I had a training plan set to start the first week of January, but by that point the combination of cold weather, running in the dark after work and pretty stressful period at work, running moved to the back of my mind for a while.By the end of February temperatures were still near freezing but I knew I had to get a couple of longer runs in  to seen how my legs &amp; feet handled that. &lt;br /&gt;
I got a half marathon in on March 2nd and did 28km the week after that. On that Sunday it was 7&#xB0;C but the sun was out in the full force for what felt like the first time this year. I arrived back home covered in salt and I had a mild sunburn on my neck. Later in March I ran another 30km and 21km again the week after that. &lt;br /&gt;
&lt;br /&gt;
My legs felt good, my feet weren't really blistering like they used to on my long runs years ago - only my left hip was consistently feeling stiff and somewhat painful during running longer than 15km... Not a very encouraging sign but I could push through it without any lasting damage during each of my long runs.&lt;br /&gt;
&lt;br /&gt;
A bigger issue is/was that other than my long runs on the weekend, I was doing maybe one 30 - 45 minute run a week - not nearly enough volume to properly train for a marathon.It's one week out now and even though my training &amp; preparation has been far from ideal, I feel ready to go. I've done this distance a bunch of times before - both when I was better prepared and when I got sick 2 weeks before race day - so I have a load experience to lean on, especially on the mental side. There will be lows, there will be highs and it's probably going to hurt :)&lt;br /&gt;
&lt;br /&gt;
I also have 4 colleagues also running with 1 or possibly 2 targeting my pace/finish time, so it'll be fun to go through this with them. I don't have a target time in mind, finishing is all that matters.]]&gt;</description><pubDate>2025-04-05T08:30:00+02:00</pubDate></item><item><title>Spending my time more consciously</title><link>https://jan.henckens.be/blog/2023-01-01-spending-my-time-more-consciously</link><guid isPermaLink="true">6f0f6e4b-dc2a-4a0e-8e92-5a0344383963</guid><description>&lt;![CDATA[The past couple of months have been really busy and stressful, both at work and at home and that left me feeling burned out on the things I like to spend my downtime on: riding my bike, running, reading, building side projects. &lt;br /&gt;
&lt;br /&gt;
Not that I didn't have any time for those, I just ended up spent on the couch more evenings than I'd like to do, doing much of nothing in particular. To then beat myself up about sitting around all evening doing nothing. That might sound familiar to some of you &#x1F642;.&lt;br /&gt;
&lt;br /&gt;
Since the first months of 2023 are looking equally exciting and stressful (finishing our house and then I'm moving in with my girlfriend), I'm going to try to get back to some basics and see where that takes me.The most important thing I (can) do to improve my overall state of mind and my mental health is sports. Right now that mostly translates to riding my bike, be it indoors on rollers or outside when the weather gets better. But I would love to add some running back into the mix. &lt;br /&gt;
&lt;br /&gt;
As a first "mini" goal, I want to join a [200km ride](https://randonneursleuven.cc/2022/the-leuven-herselt-connection/) with some friends in early March, that gives me 2 months to build up my shape again.I've build and I maintain a bunch of [Craft CMS plugins](https://github.com/studioespresso) and most of them are in sort of a maintenance-only-mode. &lt;br /&gt;
&lt;br /&gt;
I have some improvements in mind for a couple of them that could really take them to the next level so I want to get back into building out new ideas instead of just patching minor issues.I was taking about all this with my girlfriend and that prompted her getting me a planner/journal for Christmas &#x1F642; (which I may write something about once I start using it).&lt;br /&gt;
&lt;br /&gt;
Off course I'm not going to be time-blocking every 5 minutes of my free time, but having a tool that I can use to look ahead (yes, any calendar does this as well, I know &#x1F609;) and to reflect on the days past, will hopefully help me to get a little bit more structure in my weeks.]]&gt;</description><pubDate>2023-01-01T20:00:00+01:00</pubDate></item><item><title>The case for non-technical meetups</title><link>https://jan.henckens.be/blog/2022-12-13-the-case-for-non-technical-meetups</link><guid isPermaLink="true">cbf53b04-fd4d-45f4-a6b2-cbc130bfd830</guid><description>&lt;![CDATA[A couple of weeks ago, we held an event at the office that we organized together with [an agency from London](https://www.wholegraindigital.com/). They work 100% remotely and decided to take the entire team on a trip to Antwerp to work from there and combine it with a little getaway.&lt;br /&gt;
&lt;br /&gt;
The event itself was a success but that&#x2019;s not really what I wanted to write about today..&lt;br /&gt;
&lt;br /&gt;
Afterward, a couple of co-workers and myself got to talking with different people on their team. During the conversations, we slowly came to the realization that both our companies do *very* similar things, and even more interestingly: we run into the same problems and struggle with very similar issues.Whether it&#x2019;s more technical stuff like scaffolding packages from which we start projects, CSS frameworks we prefer, to how we handle support and planning, &#x2026;We&#x2019;ve all tried a bunch of frameworks/methods/tools, and have found things that don&#x2019;t work for us and things to do. And if we shared our experiences, we both would learn a ton.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
Reflecting on the conversions I had, it felt very much like what you would talk about at a meetup, for like a CMS, a programming language, or a UX thing. &lt;br /&gt;
&lt;br /&gt;
Only here we mainly talked about how we run projects, managed teams, and run the actual business, and that&#x2019;s not something you regularly discuss at a meetup. Maybe it&#x2019;s because they are based in London and are in no way a competitor to us and the client base we target (which is probably different when you attend a local meetup), but it felt very natural to share with their team.&lt;br /&gt;
&lt;br /&gt;
It would be interesting to see if we can do things with other, more local, agencies as well.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
At the end of the evening, we said our goodbyes and promised to stay in touch (easier said than done). &lt;br /&gt;
&lt;br /&gt;
The following Monday we set up a shared Slack channel between both our companies, we've chatted back and forth a bunch and we're also planning a sort of exchange of the CMS'es we work with (they're on WordPress, we're on Craft CMS). &lt;br /&gt;
&lt;br /&gt;
So we'll try to keep this going and maybe we can visit them in London in 2023 &#x1F600;]]&gt;</description><pubDate>2022-12-13T20:21:00+01:00</pubDate></item><item><title>On mechanical keyboards</title><link>https://jan.henckens.be/blog/2021-05-02-on-mechanical-keyboards</link><guid isPermaLink="true">34aa286c-b622-4258-9614-99fc49eabbb2</guid><description>&lt;![CDATA[I mainly wanted to stick to azerty because my personal and work laptops are on that layout. Plus when someone else has to use my keyboard (the girlfriend when we live together and share an office space eg), I don't want it to a hassle. Plus I'm quite happy with AZERTY and I can touch type on it at decent speed.&lt;br /&gt;
&lt;br /&gt;
In researching different types of keyboards I discovered that there are the main 2 layouts are [ANSI and ISO](https://en.wikipedia.org/wiki/Keyboard_layout). And AZERTY fits in an ISO layout. So that's that.In the fall of 2020, I borrowed said coworkers [Keychron K2](https://www.keychron.com/products/keychron-k2-wireless-mechanical-keyboard) (after he stepped up to a custom board). It came with Gateron Red switches and those were really quite loud. So much so that people noticed my typing during video meetings and I had to mute myself while taking notes. And since, with the whole pandemic going on, video meetings are where I spend have my work days, that was an issue.So I went searching for a more quiet switch and found the [Gateron Aliaz Silent MX Mechanical Switch](https://kbdfans.com/products/pre-orderaliaz-silent-switch-tactile). From both written and YouTube reviews, these looked like a good starting point for someone building a first keyboard like myself.For a case, I went with for an [Idobao ID80v2 75% keyboard](https://www.idobao.net/collections/75-layout/products/idobao-id80v2-75-hot-swappable-mechanical-keyboard-kit).&lt;br /&gt;
&lt;br /&gt;
The first set of keycaps I ordered was a very cheap blank white DSA set from AliExpress and was great to get me started. But then a couple of weeks later [Oblotzky](https://oblotzky.industries) had [PBT Notion](https://oblotzky.industries/products/pbt-notion) in stock and I had to get it &#x1F642;. Here's the finished board so far:## Tiny details:&lt;br /&gt;
&lt;br /&gt;
- Escape key is an artisan from Drop, [part of a charity set for St. Jude](https://drop.com/buy/streamcaps-keycaps-for-charity)&lt;br /&gt;
- Cable is from kbdfans.com&lt;br /&gt;
- Desk mat is [Chenyi solar system galaxy](https://drop.com/buy/chenyi-solar-system-stitched-cloth-desk-mat) from Drop&lt;br /&gt;
- Stabilzers have been replaces with Durock V2's&lt;br /&gt;
&lt;br /&gt;
I'm very happy with the result so far &#x1F642;]]&gt;</description><pubDate>2021-05-02T11:24:00+02:00</pubDate></item><item><title>Running Composer 2 alongside Composer 1</title><link>https://jan.henckens.be/blog/2021-02-10-running-composer-2-along-side-composer-1</link><guid isPermaLink="true">0bdc7de0-640d-494f-8a35-55c0213411b2</guid><description>&lt;![CDATA[*I'm using Mac OS so that' what I'll be referencing here. The setup should work everywhere but you'll have to adjust the paths and commands as needed*&lt;br /&gt;
&lt;br /&gt;
Upgrading to 2.0 can be done quickly and easily by running `composer self-update --2`.&lt;br /&gt;
&lt;br /&gt;
But running Composer at 2.0 also means your packages and plugins should support Composer's new plugin architecture. If you're working on an older codebase and updating your dependencies simply isn't an option, you'll need Composer 1.x to install the project.Downgrading from 2.x to 1.x can be done just as easily as upgrading: run `composer self-update --1` and you're back on the latest 1.x release. But if you need both versions somewhat regularly, downgrading and upgrading again each time is probably not the best solution.In the setup I'm using right now, I'm running both versions alongside each other. I have `composer` with is the latest and greatest and `composer1` which is the last 1.x release. Here how to do that.&lt;br /&gt;
&lt;br /&gt;
First, we download a new copy using the install script you can find [here](https://getcomposer.org/download/). Then we move the new `composer.phar` file to `/usr/local/bin` but we also rename it in the process:That gives us both options:]]&gt;</description><pubDate>2021-02-10T22:42:00+01:00</pubDate></item><item><title>Using template hooks to enhance Craft CMS</title><link>https://jan.henckens.be/blog/2021-02-07-using-template-hooks-to-enhance-craft-cms</link><guid isPermaLink="true">e2fb9133-29a2-4711-86d7-9420c6640dae</guid><description>&lt;![CDATA[First up, figure out which template the view you want to insert your template, JS or CSS into is using. For pages in Craft core, you can look in `/vendor/craftcms/cms/src/templates`.&lt;br /&gt;
&lt;br /&gt;
Say you want to add a custom button to the sidebar for entries of a specific section, you'll need `/vendor/craftcms/cms/src/templates/entries/_edit.html` and the `cp.entries.edit.details` hook [here](https://github.com/craftcms/cms/blob/develop/src/templates/entries/_edit.html#L145).&lt;br /&gt;
&lt;br /&gt;
You can find an overview of all hooks available in Craft [here](https://craftcms.com/docs/3.x/extend/template-hooks.html#control-panel-template-hooks).In the example below, we're hooking into said entry page, in the sidebar specifically. First, we check if the entry is in the section we're targeting and we return if it's not. Then we render a template and pass the current entry to it.&lt;br /&gt;
&lt;br /&gt;
This code goes into your main module or plugin class.One of the reasons I've been using this specific template hook is to make related entries of the entry you're viewing easily exportable. An example:&lt;br /&gt;
&lt;br /&gt;
We have an "Events" section and a "Registrations" section. Registrations belong to an event and contain all the data for said registration. A couple of days before the event, the client wants to make an export of all registrations.  Viewing the full list of registrations isn't super useful here, since there's no quick way to just view entries for a specific event.&lt;br /&gt;
&lt;br /&gt;
So we use a template hook to insert a button into the event entry sidebar. That button links to a controller action (to which we pass the entry ID). In the controller, we then get the related registrations, parse them to a preferred file format and return them as a file to be downloaded.&lt;br /&gt;
&lt;br /&gt;
That way, the client can find the registrations with the event itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
Hope you found this useful, feel free to share the post if you did. &lt;br /&gt;
&lt;br /&gt;
Have suggestions or remarks? [Get in touch!](https://www.twitter.com/jannemans)]]&gt;</description><pubDate>2021-02-07T19:10:00+01:00</pubDate></item><item><title>Adventure Inspiration: Iron Curtain Bike Trail</title><link>https://jan.henckens.be/blog/2020-11-13-adventure-inspiration-iron-curtain-bike-trail</link><guid isPermaLink="true">1901576e-93c8-4ea2-ac44-6840ca8414c3</guid><description>&lt;![CDATA[&lt;p&gt;Today's post is brought to you by me watching this YouTube video from Salomon during my lunch break earlier today:&lt;/p&gt;
&lt;iframe width="720" height="400" src="https://www.youtube.com/embed/3JvJ_TTzS0E" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;To summarize: a group of trailrunners run the length of the iron curtain wall across Germany, 1400km in total.&lt;/p&gt;
&lt;p&gt;That got me thinking that the route/trail would be great for a cycling/bikepacking trip. A quick bit of Google'ing got me to the &lt;a href="https://en.eurovelo.com/ev13"&gt;EuroVelo 13&lt;/a&gt;, aka the &amp;quot;Iron Curtain Trail&amp;quot;. A cycling route, 9950km across 20 countries, from the Black Sea to the Barents Sea.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.flickr.com/photos/eurovelo/albums/72157680450750042"&gt;Here&lt;/a&gt; are some image from along the route. Maybe not the entire route but just the German part perhaps, looks like a fun trip &#x1F642;.&lt;/p&gt;]]&gt;</description><pubDate>2020-11-13T21:39:00+01:00</pubDate></item><item><title>Onboarding new coworkers with Craft CMS</title><link>https://jan.henckens.be/blog/2020-03-12-2020-03-12-onboarding-new-coworkers-with-craft-cms</link><guid isPermaLink="true">1ac9d1ee-2cbd-4436-946c-0b020ee956b1</guid><description>&lt;![CDATA[&lt;hr /&gt;
&lt;p&gt;This is the blogpost version of the presentation I gave at last week&#x2019;s Craft CMS meetup in Ghent. &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;At the office, the team I&#x2019;m on consists of a project manager, backend developers and frontend developers. The number of developers has grown and shrunk over the past year, from 4 to 6 back to 4 to 3 and back to 4. Each addition is someone new to the company and someone new to Craft CMS. &lt;/p&gt;
&lt;p&gt;So we&#x2019;ve had to run through the &#x201C;getting started with Craft CMS&#x201D; thing a bunch of time. Here&#x2019;s a summary of our experiences, the resources we used and a bunch of things that aren&#x2019;t specific to Craft but that can apply to any new developer joining your team.&lt;/p&gt;
&lt;h2&gt;Experience level&lt;/h2&gt;
&lt;p&gt;The first very important thing to take stock of is the level of experience (and/or formal development/CS eduction) the new team member has. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is it their first job out of college? &lt;/li&gt;
&lt;li&gt;Have they been building React apps for the past 4 years? &lt;/li&gt;
&lt;li&gt;Do they have a background in Drupal/Expression Engine/another CMS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also have a couple of interns every year, which mostly fall into the &#x201C;fresh out of college&#x201D; group, but for interns we do things a bit differently, more on that later.&lt;/p&gt;
&lt;h2&gt;Before we get to Craft CMS&lt;/h2&gt;
&lt;p&gt;The first must-have dealbreaker basic skill that we need (if not require) is knowledge  of git and version control. Literally every line of code we write ends up in git, and it is how a manage and deploy any of our projects so if there is 1 prerequisite, this is it.&lt;/p&gt;
&lt;h2&gt;Craft CMS&lt;/h2&gt;
&lt;p&gt;The first resource we turn to for specific Craft CMS is &lt;a href="https://craftquest.io/"&gt;CraftQuest&lt;/a&gt;. Ryan is a great teacher and has a massive amount of content available on Craft. &lt;/p&gt;
&lt;p&gt;So we start off new co-workers with his &#x201C;The Craft mindset&#x201D; course. This covers the Craft specific idioms like section types, assets, field, users, elements, matrix fields, etc. There&#x2019;s also a demo site they can build along with the course, to get a feel for the CP and twig templating.&lt;/p&gt;
&lt;h2&gt;Give them something &lt;strong&gt;real&lt;/strong&gt; to build&lt;/h2&gt;
&lt;p&gt;Having to come up with and idea or a subject to build a site around isn&#x2019;t always easy, and it probably wouldn&#x2019;t be very representative of the types of projects that the team is build. &lt;/p&gt;
&lt;p&gt;So the next &#x201C;task&#x201D; we tend to give a new team member is is to build (part of) a project that the team recently completed. Something small and simple, but a real project. That means it has clear requirements, it was wireframes and a design and someone on the has actually built it already. We start them off with a fresh Craft install with nothing in it and give them a week (or 2, depending on the scope) to try and build the project. &lt;/p&gt;
&lt;p&gt;This gives a way to feel out a couple of things, but most importantly: are they ok with asking for help when they inevitably get stuck on something.&lt;/p&gt;
&lt;p&gt;Another important note here is that whatever they end up building is ok. This is not a test and &lt;strong&gt;there are no wrong answers&lt;/strong&gt;. We&#x2019;ll do code review every other day, but more the steer them in the right direction to the explain things than to point out errors.&lt;/p&gt;
&lt;p&gt;Up next is building a project or a bigger feature along side another team-member. Their code won&#x2019;t be used but it&#x2019;s a good exercise to go through the entire project flow: project kickoff, wireframes, requirements, testing, client training&#x2026;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;We&#x2019;ll repeat these last 2 steps as often as is needed. And they could take anywhere from 2 weeks to 3 months, depending and the person and their experience. &lt;/p&gt;
&lt;p&gt;Next is of course a real client project. With that comes doing estimates, building it on time and on budget, launching it, the works.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Maintenance &amp;amp; support&lt;/h2&gt;
&lt;p&gt;Aside from building new projects, we also like to a roll new hires into doing support for existing projects. These could be recent projects in Craft, older projects in Craft 2 or even Expression Engine sites, custom PHP stuff, email templates, &#x2026;.&lt;/p&gt;
&lt;p&gt;Working on these projects brings along a whole new set of constraints and requirements and being able to adapt to this is very useful.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1-on-1&lt;/h2&gt;
&lt;p&gt;After the new hire is integrated into to the team and in the project rotation, it can be easy to quickly forget how new they still are, even after 4 or 6 months on the job.
To keep tabs with how things are going we like to do 1-on-1 meetings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;With the project manager / team lead. &lt;strong&gt;Honestly, everyone should be doing these, not just new people.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;With a fellow team-member, rotating which one and every 2 weeks. This is a time where they can ask questions about projects, issues, something they didn&#x2019;t understand very well, something they&#x2019;d like the know more about, etc&#x2026; These can definitely tail off when both parties feel they aren&#x2019;t needed anymore. &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;To summarize&lt;/h2&gt;
&lt;p&gt;All this depends on the person you have joining your team. This could take 2 weeks and they could be working on an actual client project in week 3 on the job. But it could just as well take 2 months, depending on their level of experience, with Craft, web development work in general and overal work experience. &lt;/p&gt;
&lt;p&gt;Thoughts, remarks or want to share your own experience with this? &lt;a href="https://www.twitter.com/jannemans"&gt;Find me on twitter&lt;/a&gt;&lt;/p&gt;]]&gt;</description><pubDate>2020-03-12T10:29:00+01:00</pubDate></item><item><title>Building a Craft CMS scaffolding package</title><link>https://jan.henckens.be/blog/2020-01-02-2020-01-02-building-a-craft-cms-scaffolding-package</link><guid isPermaLink="true">702f3710-ec1f-4120-87d1-99210a258a58</guid><description>&lt;![CDATA[&lt;p&gt;When I posted about the &lt;a href="https://github.com/statikbe/craft"&gt;Craft CMS starter repo&lt;/a&gt; we created at work, a couple of people were very interested in how to build something like that for themselves.&lt;/p&gt;
&lt;p&gt;So&#x2019;s have a look at how we&#x2019;ve done things. &lt;/p&gt;
&lt;p&gt;Not that this is &lt;strong&gt;not a step-by-step or line-by-line walkthrough&lt;/strong&gt; but more a high-level overview with some examples. If you're trying this out for yourself and you're stuck on something, feel free to get in touch and I'll try help you out &#x1F642;&lt;/p&gt;
&lt;img src="uploads/2019/b74330e871.png" width="597" height="372" alt="Screenshot of our custom Craft install script" /&gt;
&lt;p&gt;(We call our starter repo our &#x201C;base install&#x201D; so that&#x2019;s what I&#x2019;ll be calling it in the post)&lt;/p&gt;
&lt;h2&gt;composer create-project&lt;/h2&gt;
&lt;p&gt;We&#x2019;ll be relying on Composer&#x2019;s &lt;a href="https://getcomposer.org/doc/03-cli.md#create-project"&gt;create-project command&lt;/a&gt; to turn our site repository in our &#x201C;base install&#x201D; repository. Running the command, (eg &lt;code&gt;composer create-project statikbe/craft&lt;/code&gt;) will clone the repository  and run &lt;code&gt;composer install&lt;/code&gt;  afterwards.&lt;/p&gt;
&lt;p&gt;That means that the repository you start from should have all the  plugins, templates, etc you want to have installed when starting a new project.&lt;/p&gt;
&lt;p&gt;For your repository to work with composer, you have to register it with &lt;a href="https://packagist.org/"&gt;Packagist&lt;/a&gt;. In our case, that means it's a public &lt;a href="https://github.com/statikbe/craft"&gt;Github repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Project config&lt;/h2&gt;
&lt;p&gt;In Craft CMS 3.1, the &lt;a href="https://docs.craftcms.com/v3/project-config.html"&gt;project config&lt;/a&gt; feature was added.  When project config is enabled, site &amp;amp; plugin settings are written out to a yaml file, making it easier to share settings between environments. &lt;/p&gt;
&lt;p&gt;In the case of our base install, we&#x2019;re using project config to seed our new project with the settings/sections/fields we have in that base install. That way, we can change/fix/improve things in the CP and commit those changes, to use them in the next project.&lt;/p&gt;
&lt;h2&gt;How to install&lt;/h2&gt;
&lt;p&gt;Let&#x2019;s take this step by step.
&lt;code&gt;composer create-project statikbe/craft path/to/folder&lt;/code&gt;
This will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone the base install in the folder of our choosing&lt;/li&gt;
&lt;li&gt;Run composer install, based on the composer.json we have in our base install&lt;/li&gt;
&lt;li&gt;Run the commands we&#x2019;ve specified in the &lt;a href="https://github.com/statikbe/craft/blob/master/composer.json#L46"&gt;post-create-project-cmd&lt;/a&gt;. In our case that is:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Copy over &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Copy over &lt;code&gt;.gitignore.example&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;composer dum-autoload&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;./craft setup/welcome&lt;/code&gt; to remind users what to do next&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then we install Craft CMS, like we would in any other project. This can be done in the browser or through the command line. We&#x2019;ll use the later since that&#x2019;s what we&#x2019;re using already.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./craft setup&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will run through the regular Craft installer (asking you for database credentials, installing Craft, creating a user). &lt;/p&gt;
&lt;p&gt;At the very end of this process, the install will check if you have project config enabled (&lt;code&gt;'useProjectConfigFile' =&amp;gt; true&lt;/code&gt;  in &lt;code&gt;config/general.php&lt;/code&gt; and if a &lt;code&gt;project.yaml&lt;/code&gt; file is present, both are true in our case). It will then try to apply the settings from that project config file to the newly installed Craft site you just created, meaning it will install plugins, create sections, create fields, etc, making it so that our new project now has all the same settings/sections/fields/plugins as our base install.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&#x1F4E3; If the settings from your project config are not being applied, search Craft&#x2019;s logs for &lt;code&gt;can't apply existing project config:&lt;/code&gt; and that should tell you what went wrong.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;After Craft&#x2019;s install script, we added one of our own. Let&#x2019;s have a look at that now.&lt;/p&gt;
&lt;h2&gt;Our custom install script&lt;/h2&gt;
&lt;p&gt;The install script we have in our repository can be run by entering &lt;code&gt;./craft statik/setup&lt;/code&gt; , which translates to &#x201C;running &lt;code&gt;actionIndex&lt;/code&gt; from &lt;code&gt;console\controllers\SetupController&lt;/code&gt; in the &lt;code&gt;Statik&lt;/code&gt; module&#x201D;. &lt;/p&gt;
&lt;p&gt;The first important thing to keep in mind is that in this part of the installation process of our new site, &lt;strong&gt;Craft CMS is already installed&lt;/strong&gt; and we can leverage it where needed. Like we do by running a console command from a module.&lt;/p&gt;
&lt;p&gt;And the second trick up our sleeve is environment variables.  More on that later.&lt;/p&gt;
&lt;p&gt;You can fellow along with our controller &lt;a href="https://github.com/statikbe/craft/blob/master/modules/statik/src/console/controllers/SetupController.php"&gt;here&lt;/a&gt; as we have a look at a couple of examples of command you can do.&lt;/p&gt;
&lt;p&gt;We could add these options and values in 1 large script, but part of making this reusable across projects (and for possibly for other people) means that we wanted to have options as to what we use where &amp;amp; how.&lt;/p&gt;
&lt;h3&gt;Disabling project config.&lt;/h3&gt;
&lt;p&gt;While we use project config to manage settings in our base install and we have to have it enabled there, we don&#x2019;t use it in our actual projects. So in our setup script we have an option to disable it.&lt;/p&gt;
&lt;p&gt;Step 1 is to have to config setting set to an environment variable, like so:
&lt;code&gt;'useProjectConfigFile' =&amp;gt; getenv("PROJECT_CONFIG") ?? false&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Step 2 is asking the question in our setup script.&lt;/p&gt;
&lt;script src="https://gist.github.com/janhenckens/9aedc989ba3437cbc491c68770b68bf1.js"&gt;&lt;/script&gt;
&lt;p&gt;In our case, we ask if you want to disable the setting, with the default being true. You can phrase it the other way around or ask just about anything you can think of. You&#x2019;ll get a boolean value in return and based on that you can proceed or do your thing. In our case that thing is setting an environment variable.&lt;/p&gt;
&lt;h3&gt;Adding placeholder images&lt;/h3&gt;
&lt;p&gt;The other example we&#x2019;ll look at is the function we use to add our placeholder images:&lt;/p&gt;
&lt;script src="https://gist.github.com/janhenckens/0d42cdb4d741fb81cc448e7523e77e12.js"&gt;&lt;/script&gt;
&lt;p&gt;(You can find the &lt;code&gt;$this-&amp;gt;executeShellCommand&lt;/code&gt; we&#x2019;re using in this function in the same class right &lt;a href="https://github.com/statikbe/craft/blob/master/modules/statik/src/console/controllers/SetupController.php#L269"&gt;here&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The idea behind this function/question in our installer is that we want to add a set of placeholder images to our new site.  As you can see &lt;a href="https://github.com/statikbe/craft/tree/master/placeholders"&gt;in the repository&lt;/a&gt;, we have 4 placeholders in there. When you choose to add the placeholders, the command will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;test&lt;/code&gt; folder in our &lt;code&gt;/files&lt;/code&gt; volume&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy over the placeholder files to that test folder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Removed the original placeholders folder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;./craft index-assets/all&lt;/code&gt; to re-index all assets, thereby adding the files we just copied to the CMS&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;hr /&gt;
&lt;p&gt;These are just 2 example of questions we ask during our installation script, you can find the entire script/controller &lt;a href="https://github.com/statikbe/craft/blob/master/modules/statik/src/console/controllers/SetupController.php"&gt;here&lt;/a&gt;. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Moving the different questions/functions to a service is probably cleaner and something that is on our to-do list&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I hope this write-up helped out a couple of people that want to try setting up something like this &#x1F642;. If you have any feedback or questions, feel free to &lt;a href="https://twitter.com/jannemans"&gt;tweet me&lt;/a&gt;, &lt;a href="mailto:jan@henckens.be"&gt;email me&lt;/a&gt; or ping me on the Craft Discord server.&lt;/p&gt;]]&gt;</description><pubDate>2020-01-02T21:35:00+01:00</pubDate></item><item><title>Craft CMS - Making user name fields required</title><link>https://jan.henckens.be/blog/2019-12-05-2019-12-05-craft-cms-making-user-name-fields-required</link><guid isPermaLink="true">131d7d59-26e3-426e-b37f-2ff3a0cd5aac</guid><description>&lt;![CDATA[&lt;p&gt;When dealing with fields on the User element in Craft, you can make the custom fields required just like you would any other custom field on an entry element. But a user also comes with a &lt;code&gt;firstName&lt;/code&gt; and &lt;code&gt;lastName&lt;/code&gt; field, which you can't make required.&lt;/p&gt;
&lt;p&gt;When building a site that relied on theses name fields a coupld of weeks ago, I decided to have try and solve this. Here's what I came up with.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;When listening for the &lt;code&gt;Element::EVENT_BEFORE_SAVE&lt;/code&gt; event on the &lt;code&gt;User::class&lt;/code&gt; class, we can check if we have a first &amp;amp; last name before saving, and mark the element as &lt;code&gt;invalid&lt;/code&gt; .&lt;/p&gt;
&lt;script src="https://gist.github.com/janhenckens/f634c072d8e7512430281809f5154a5c.js"&gt;&lt;/script&gt;
&lt;p&gt;Then we add an error to the appropriate field using &lt;code&gt;-&amp;gt;addError()&lt;/code&gt;. That will make validation fail, returning the user to the add/edit user screen, with the add alert under each field.&lt;/p&gt;
&lt;p&gt;You can add this piece of code to the &lt;code&gt;init()&lt;/code&gt; function of a module to make it work.&lt;/p&gt;
&lt;img src="/uploads/2019/c58bbd6d10.jpg" width="1000"  alt="" /&gt;]]&gt;</description><pubDate>2019-12-05T20:24:00+01:00</pubDate></item></rss>
