<?xml version="1.0" encoding="UTF-8"?><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><title><![CDATA[Code for humans]]></title><description><![CDATA[This blog aims to educate developers by sharing perspectives, experiences, and knowledge about code.]]></description><link>https://blog.millertchris.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1627677240422/ZcZLP9_YZ.png</url><title>Code for humans</title><link>https://blog.millertchris.com</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 09 Oct 2026 23:19:52 GMT</lastBuildDate><atom:link href="https://blog.millertchris.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Price of Web3]]></title><description><![CDATA[If you can sum up Web3 in one word — I think most people would say "decentralized."
While I do enjoy the idea of a decentralized web — I doubt that it will truly be "decentralized" as massive whales, corporations, and governments move in to invest th...]]></description><link>https://blog.millertchris.com/the-price-of-web3</link><guid isPermaLink="true">https://blog.millertchris.com/the-price-of-web3</guid><category><![CDATA[Web3]]></category><category><![CDATA[Cryptocurrency]]></category><category><![CDATA[NFT]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Wed, 29 Dec 2021 13:20:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1640783844111/HNaJzDr3H.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you can sum up Web3 in one word — I think most people would say "decentralized."</p>
<p>While I do enjoy the idea of a decentralized web — I doubt that it will truly be "decentralized" as massive whales, corporations, and governments move in to invest their wealth for a stake in the game and begin lobbying and regulating.</p>
<p>Coinbase and OpenSea are examples where crypto and NFTs are traded in a centralized space which is counter-culture to the whole decentralized movement — right?</p>
<p>We should also recognize that their success comes from a very old business model that works and panders to society.</p>
<p>Convenience.</p>
<p>As long as societies are willing to "pay" for convenience, we will continue to give our data freely to social media platforms, purchase food from a drive-thru, have companies monitoring sound and visuals inside our homes, and allow institutions to do so centralize our wealth.</p>
<p>Web3 wants to change that — but it comes with a price.</p>
<p>Mining crypto is a critical part of the process that requires computers to solve complex problems, which can consume an unbelievable amount of energy when you scale it across the globe to billions of people.</p>
<p>As the value of any crypto rises, more and more people are incentivized to become miners. And because the difficulty of solving each cryptographic problem grows with the network, more and more energy is then used by miners.</p>
<blockquote>
<p>Bitcoin's carbon footprint is bigger than that of many countries.</p>
</blockquote>
<p>The argument could be made that Bitcoin is our biggest offender and that it's inefficient when it comes to mining— however — a more efficient crypto that consumes less energy can still be harmful — at scale and over time.</p>
<p>Here are a few other examples of how crypto can be bad for our environment.</p>
<blockquote>
<p>Currently, a single Ethereum transaction consumes as much electricity as an average U.S. household uses in a workweek — and has a carbon footprint equivalent to 140,893 Visa credit card transactions or 10,595 hours of watching YouTube.</p>
</blockquote>
<p>While the above examples focus specifically on Bitcoin and Ethereum — any other crypto at scale is capable of doing better or worse — but they will require mining and energy consumption at the end of the day.</p>
<p>All of that said — Web3 is not bad.</p>
<p>While centralization and energy consumption appear to be unavoidable looming clouds over the Web3 — that's where I see an incredible opportunity for us to solve these problems.</p>
<p>Just remember — it has to be convenient.</p>
]]></content:encoded></item><item><title><![CDATA[Pro A/V setup for developers]]></title><description><![CDATA[Whether it's a Zoom meeting or recorded content — I've been asked on several occasions what gear I use — and each time, I piecemeal a list and send it.
So — I've decided to break down each piece of audio gear that I use and its purpose so anyone inte...]]></description><link>https://blog.millertchris.com/pro-av-setup-for-developers</link><guid isPermaLink="true">https://blog.millertchris.com/pro-av-setup-for-developers</guid><category><![CDATA[video]]></category><category><![CDATA[video streaming]]></category><category><![CDATA[streaming]]></category><category><![CDATA[Developer]]></category><category><![CDATA[content]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Thu, 30 Sep 2021 15:54:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1632963804125/VmwfA8H3o.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Whether it's a Zoom meeting or recorded content — I've been asked on several occasions what gear I use — and each time, I piecemeal a list and send it.</p>
<p>So — I've decided to break down each piece of audio gear that I use and its purpose so anyone interested will have a jumping-off point, and it'll save me from having to make this list again.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633011356315/jer99DglZ.jpeg" alt="IMG_0093.jpg" /></p>
<h2 id="gear-outline">Gear outline</h2>
<ul>
<li>Camera - Blackmagic Cinema Camera 6K</li>
<li>Lens - 24mm T1.5 Rokinon Cinema Lenses</li>
<li>Lighting - Elgato Key Lights</li>
<li>Microphone - Shure SM7B</li>
<li>Audio interface - Focusrite Scarlett 2i2</li>
<li>Signal booster - Cloudlifter</li>
<li>Mic stand - Suspension Boom Stand</li>
<li>Audio cables - LyxPro XLR Microphone Cable (3 feet &amp; 1.5 feet)</li>
<li>Controller - Stream Deck Mini</li>
</ul>
<h2 id="camera-blackmagic-cinema-camera-6k">Camera - Blackmagic Cinema Camera 6K</h2>
<p>This piece of gear may arguably be overkill for Zoom calls and live streaming — but it comes in clutch for recorded content that you plan to publish on YouTube or Vimeo.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633011466011/9VLNwTOOm.jpeg" alt="IMG_0080.jpg" /></p>
<p>It's also one of the things that people comment on the most during a Zoom call, so there's something to be said about the video quality this puts out. If you're balling on a budget, though, there's no shame in spending less and going with another DSLR just as long as it shoots video and is capable of remaining on for long periods.</p>
<p>I'm not going to get all geeked out about this fantastic camera because that can be an article of its own — and you'll find later in this article that your lens and lighting are just as critical as the camera body.</p>
<p><a target="_blank" href="https://www.blackmagicdesign.com/products/blackmagicpocketcinemacamera">Blackmagic Pocket Cinema Camera 6K</a></p>
<h2 id="lens-24mm-t15-rokinon-cinema-lens">Lens - 24mm T1.5 Rokinon Cinema Lens</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633011874283/SH0XJrkdq.jpeg" alt="IMG_0077.jpg" /></p>
<p>One of the most budget-friendly cinema lenses on the market — Rokinon prime lenses produce fantastic images and are capable of letting in tons of light with a t-stop of 1.5.</p>
<p>I love these lenses because they do not have autofocus, so whatever focal length I set, it stays, which is ideal in a controlled setting. You might find this ideal too, so that every time you sit down in front of the computer — you're set and ready to go.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633011926912/Ga-Nzd69F.jpeg" alt="IMG_0083.jpg" /></p>
<p>When I purchased these lenses about 8 years ago — the cost was half of what they are today. It comes as no shock that they doubled in price since they are a great piece of gear that lasts a long time.</p>
<p>I'm not recommending that you go and grab these lenses, but I luckily still had mine from my early cinematography days.</p>
<p>If the price is too steep — start with any glass and upgrade later, or if you can find a camera body lens combo, that should do you just fine.</p>
<p><a target="_blank" href="https://www.amazon.com/gp/product/B00MZCCGXA/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B00MZCCGXA&amp;linkCode=as2&amp;tag=millertchris-20&amp;linkId=ec7329b73a79aa6d08d8f4ca8a2aabaa">Rokinon Cine DS DS24M-C 24mm T1.5 ED AS IF UMC Full Frame Cine Wide Angle Lens for Canon EF</a></p>
<h2 id="video-interface-cam-link-4k">Video Interface - Cam Link 4k</h2>
<p>Piping your video signal into your computer is not as straightforward as you might think. At first, I purchased a cheap HDMI adapter, but sadly, the video signal did not show as an option under the video settings in Zoom, QuickTime, or OBS.</p>
<p>Then I found the Elgato Cam Link, and it was plug-and-play right out of the box.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633012123626/of5UPeyDD.jpeg" alt="IMG_0088.jpg" /></p>
<h2 id="lighting-elgato-key-lights">Lighting - Elgato Key Lights</h2>
<p>Ok — so you've got the camera and the lens, and you turn it on, only to find out that the image it produces is too dim, and when you crank up the shutter or ISO on the camera, it becomes too grainy.</p>
<p>That's because you need to light yourself with a proper amount of foreground lighting — and for that, I use the Elgato Key Lights by Corsair.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633011955036/NJn_jrPpw.jpeg" alt="IMG_0087.jpg" /></p>
<p>I like these lights because they come with c-clamps and telescopic poles that allow me to mount them directly to the desk and extend them above my monitor to light the entire scene properly.</p>
<p>You could honestly go gorilla hear and grab two lamps from around the house and place them in front of you, and it'll work — but the light it produces will not only be harsh on your eyes but will also be difficult to distribute the light evenly across the scene.</p>
<p>The Elgato Key Lights come with a diffuser, and when you raise them above your head, they become out of sight and much easier on the eyes.</p>
<p>The only thing I don't like about these lights is how they can sometimes "disappear" since they rely on wireless connectivity to control brightness and color, but with some of their most recent firmware updates — I see less of that happening now.</p>
<p><a target="_blank" href="https://www.amazon.com/gp/product/B07L755X9G/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07L755X9G&amp;linkCode=as2&amp;tag=millertchris-20&amp;linkId=bbabb805feab8751750ad2b82cc9b3b0">Elgato Key Light, Professional Studio LED Panel With 2800 Lumens, Color Adjustable, App-Enabled, for Mac/Windows/iPhone/Android, Metal Desk Mount Copy</a></p>
<h2 id="microphone-shure-sm7b">Microphone - Shure SM7B</h2>
<p>Video is important — but I would argue that audio is more important since it's a medium that can be used without video. In my setup, I'm using the Shure SM7B, which, hands down, is a fantastic piece of hardware that'll be around for a very long time. </p>
<p>This is an absolute beast, and it exceeded all of my expectations when I first received it and tested it against some of my other microphones.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633011999493/yCcMigJvf.jpeg" alt="IMG_0072.jpg" /></p>
<p>You will find that the Shure SM7B is tried and true microphone in every industry, including music, radio, and podcast.</p>
<p><a target="_blank" href="https://www.amazon.com/gp/product/B0002E4Z8M/ref=as_li_tl?ie=UTF8&amp;tag=millertchris-20&amp;camp=1789&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B0002E4Z8M&amp;linkId=1e2fd4b8cd27e81cb4a625a038b1a44b">Shure SM7B Dynamic Smooth Vocal Microphone for Radio/TV/Podcast Appllications</a></p>
<h2 id="audio-interface-focusrite-scarlett-2i2">Audio interface - Focusrite Scarlett 2i2</h2>
<p>There's a ton of audio interfaces to choose from out there, but around the time I settled on the Focusrite Scarlett 2i2, it had the best price point for the features that it offers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633012023174/DxdwGH2IB.jpeg" alt="IMG_0076.jpg" /></p>
<p>Let's not kid ourselves though, once you plugin this thing in and dial in your settings — it just sits there on your desk and becomes a forgotten piece of equipment, which is fine because it serves its purpose.</p>
<p>That said, this is a required part of the setup because without it, well, you won't be able to pipe audio into your computer.</p>
<p><a target="_blank" href="https://www.amazon.com/gp/product/B07QR73T66/ref=as_li_tl?ie=UTF8&amp;tag=millertchris-20&amp;camp=1789&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B07QR73T66&amp;linkId=ce9f7364151adabda2826dfb55d2d062">Focusrite Scarlett 2i2 (3rd Gen) USB Audio Interface with Pro Tools</a></p>
<h2 id="signal-booster-cloudlifter">Signal booster - Cloudlifter</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633012044977/D5fVJwwtZ.jpeg" alt="IMG_0075.jpg" /></p>
<p>I noticed that the gain had to be cranked to hear anything coming from the Scarlet 2i2.</p>
<p>Even then, it was low.</p>
<p>This is no fault or flaw of the equipment; in fact, it's the Shure SM7B that requires a signal boost before it gets to the Scarlet 2i2. This way, when the Scarlet interface receives the signal, it's closer to a "normal" signal, and you should be able to dial back the gain quite a bit.</p>
<p><a target="_blank" href="https://www.amazon.com/gp/product/B004MQSV04/ref=as_li_tl?ie=UTF8&amp;tag=millertchris-20&amp;camp=1789&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B004MQSV04&amp;linkId=501d6677af65b4df3748baaa66e12cd2">Cloud Microphones Cloudlifter CL-1 Mic Activator</a></p>
<h2 id="mic-stand-suspension-boom-stand">Mic stand - Suspension Boom Stand</h2>
<p>There are a few different flavors when it comes to mic stands. You could go with a base that sits on your desk, or you could free up space by going with a suspension book stand.</p>
<p>I went with the latter because it allows me to pull it near me when I need it and push it out of the way when I'm done with it — all while keeping the clutter clear and free from the desk.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633013069147/H7oVJfucq.jpeg" alt="IMG_0098.jpg" /></p>
<p><a target="_blank" href="https://www.amazon.com/gp/product/B07CN2C93T/ref=as_li_tl?ie=UTF8&amp;tag=millertchris-20&amp;camp=1789&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B07CN2C93T&amp;linkId=d32b03b4225acc58215b45e7b3c5c235">InnoGear Mic Stand, Heavy Duty Microphone Stand with Microphone Windscreen and Dual Layered Mic Pop Filter Suspension Boom Scissor Arm Stands</a></p>
<h2 id="audio-cables-lyxpro-xlr-microphone-cable-3-feet-and-15-feet">Audio cables - LyxPro XLR Microphone Cable (3 feet &amp; 1.5 feet)</h2>
<p>XLR cables are a dime a dozen with tons of brands and types. You do not need an expensive or fancy XLR cable, but if you want to add extra flair, LyxPro has cables that come in all sorts of colors.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633012083101/q2NN_1oHG.jpeg" alt="IMG_0089.jpg" /></p>
<p>I went with black, which matches my other equipment and helps keep things hidden at the back of my desk.</p>
<p>The 3-foot cable goes from the mic to the Cloudlifter, while the 1.5-foot cable goes from the Cloudlifter to the Scarlet interface.</p>
<p><a target="_blank" href="https://www.amazon.com/gp/product/B00KWIM2FG/ref=as_li_tl?ie=UTF8&amp;tag=millertchris-20&amp;camp=1789&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B00KWIM2FG&amp;linkId=6131c8a88668f31743541a984cbcb311">LyxPro 3 Feet XLR Microphone Cable Balanced Male to Female 3 Pin Mic Cord for Powered Speakers Audio Interface Professional Pro Audio Performance and Recording Devices - Black</a></p>
<p><a target="_blank" href="https://www.amazon.com/gp/product/B00KWIM0K8/ref=as_li_tl?ie=UTF8&amp;tag=millertchris-20&amp;camp=1789&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B00KWIM0K8&amp;linkId=bbd33516c1122e87120772232eab7f4d">LyxPro 1.5 Feet Right Angle XLR Female to Male 3 Pin Mic Cord for Powered Speakers Audio Interface Professional Pro Audio Performance Camcorders DSLR Video Cameras and Recording Devices - Black</a></p>
<h2 id="controller-stream-deck-mini">Controller - Stream Deck Mini</h2>
<p>The Stream Deck Mini is optional but nice to have. It allows me to toggle both of my lights on and off — and also gives me the option to preset brightness, color temps, and opportunities to create additional "paged" presets that I can cycle through.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633012869071/bUoGM8Z5f.jpeg" alt="IMG_0069.jpg" /></p>
<p><a target="_blank" href="https://www.amazon.com/gp/product/B07DYRS1WH/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07DYRS1WH&amp;linkCode=as2&amp;tag=millertchris-20&amp;linkId=5dbeed29a73002d68ae74c3787416045">Elgato Stream Deck Mini - Compact Live Production Controller with 6 Customizable LCD keys, Trigger Actions in OBS Studio, Streamlabs, Twitch, YouTube and More (10GAI9901), Black, 6 Keys</a></p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>So there you have it — this is my professional A/V setup that works for anything from online meetings to podcasts and video recordings. </p>
<p>I found this to be one of the best investments I've ever made.</p>
]]></content:encoded></item><item><title><![CDATA[OOP plugin development using wppb.me generator]]></title><description><![CDATA[In previous articles, we stepped through making a plugin from the folder to the file and adding some practical functionality.
This article will dive in much deeper and learn how to create a plugin and prepare it to release in the world for production...]]></description><link>https://blog.millertchris.com/oop-plugin-development-using-wppbme-generator</link><guid isPermaLink="true">https://blog.millertchris.com/oop-plugin-development-using-wppbme-generator</guid><category><![CDATA[WordPress]]></category><category><![CDATA[wordpress plugins]]></category><category><![CDATA[plugins]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Sat, 31 Jul 2021 01:51:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1627694841680/ZUoxoVxVm.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In previous articles, we stepped through making a plugin from the folder to the file and adding some practical functionality.</p>
<p>This article will dive in much deeper and learn how to create a plugin and prepare it to release in the world for production-ready sites.</p>
<h2 id="fair-warning">Fair warning</h2>
<p>Unlike some of my other posts, this one will get a bit deeper into the technical weeds by exploring WordPress plugin development writing object-oriented PHP.</p>
<p>The good news is, there's a pretty sweet generator that creates a nice baseline for you. The challenging news is that you need to be familiar with OOP or object-oriented programming to understand what the hell is going on.</p>
<p>It can get a bit overwhelming, but if you stick with it — it'll make sense in the end.</p>
<h2 id="the-scope">The scope</h2>
<p>We're going to keep this scope pretty narrow since there's going to be a lot to cover by creating a plugin that will add a new "Movie" custom post type to the backend of WordPress.</p>
<h2 id="lets-build">Let's build!</h2>
<p>Here's an outline of what we will need to do:</p>
<ul>
<li>Generate the plugin boilerplate</li>
<li>Update the plugin description</li>
<li>Add our custom post type code</li>
<li>Register our custom post type using a hook</li>
</ul>
<h2 id="generate-the-plugin-boilerplate">Generate the plugin boilerplate</h2>
<p>There's a nifty and swifty plugin generator called, you guessed it, WordPress plugin boilerplate generator.</p>
<p>Head over to  <a target="_blank" href="https://wppb.me/">https://wppb.me/</a> and fill out the following:</p>
<ul>
<li>Plugin Name - Movies</li>
<li>Plugin Slug - movies</li>
<li>Plugin URL - https://millertchris.com</li>
<li>Author Name - Chris Miller</li>
<li>Author Email - @millertchris</li>
<li>Author URL - https://millertchris.com</li>
</ul>
<p>It would be ideal if there's a dedicated landing page for the plugin, but I'm using my personal domain as a placeholder in the example above.</p>
<p>Once you have all of that filled out, click "Build Plugin," and it'll download a .zip file with the name of your plugin.</p>
<h2 id="update-the-plugin-description">Update the plugin description</h2>
<p>Go ahead and unzip the .zip file and open the folder in your preferred code editor.</p>
<p>The first thing we are going to do is open the <code>movies.php</code> file and edit the description to read "Adds a new post type called movies."</p>
<p>When you're done, the top of the file should look something like this.</p>
<pre><code><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">/**
 * The plugin bootstrap file
 *
 * This file is read by WordPress to generate the plugin information in the plugin
 * admin area. This file also includes all of the dependencies used by the plugin,
 * registers the activation and deactivation functions and defines a function
 * that starts the plugin.
 *
 * <span class="hljs-doctag">@link</span>              http://millertchris.com
 * <span class="hljs-doctag">@since</span>             1.0.0
 * <span class="hljs-doctag">@package</span>           Movies
 *
 * <span class="hljs-doctag">@wordpress</span>-plugin
 * Plugin Name:       Movies
 * Plugin URI:        http://millertchris.com
 * Description:       Adds a new post type called movies.
 * Version:           1.0.0
 * Author:            Chris Miller
 * Author URI:        http://millertchris.com
 * License:           GPL-2.0+
 * License URI:       http://www.gnu.org/licenses/gpl-2.0.txt
 * Text Domain:       movies
 * Domain Path:       /languages
 */</span>
</code></pre><p>Once that's done, we should see it reflected in the backend of WordPress when we navigate to the plugins page.</p>
<h2 id="add-our-custom-post-type-code">Add our custom post type code</h2>
<p>Next, let's navigate the <code>public</code> folder and open the <code>class-movies-public.php</code> file to create our custom post type by repurposing an existing function.</p>
<p>Don't worry; some of these functions are there as placeholders, so it's totally fine that we are refactoring ourselves.</p>
<p>Let's go with the last one called <code>public function enqueue_scripts()</code> on line 85.</p>
<p>Start by deleting lines 87 through 99 and renaming the function to <code>register_movies()</code>.</p>
<p>When you're done, it should look something like this:</p>
<pre><code><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">register_movies</span>(<span class="hljs-params"></span>) </span>{

}
</code></pre><p>Now we can grab the code from the previous article and paste it into this function.</p>
<pre><code>  $labels = <span class="hljs-keyword">array</span>(
    <span class="hljs-string">'name'</span>                  =&gt; _x(<span class="hljs-string">'Movies'</span>, <span class="hljs-string">'Post Type General Name'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'singular_name'</span>         =&gt; _x(<span class="hljs-string">'Movie'</span>, <span class="hljs-string">'Post Type Singular Name'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'menu_name'</span>             =&gt; __(<span class="hljs-string">'Movies'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'name_admin_bar'</span>        =&gt; __(<span class="hljs-string">'Movie'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'archives'</span>              =&gt; __(<span class="hljs-string">'Item Archives'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'attributes'</span>            =&gt; __(<span class="hljs-string">'Item Attributes'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'parent_item_colon'</span>     =&gt; __(<span class="hljs-string">'Parent Item:'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'all_items'</span>             =&gt; __(<span class="hljs-string">'All Items'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'add_new_item'</span>          =&gt; __(<span class="hljs-string">'Add New Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'add_new'</span>               =&gt; __(<span class="hljs-string">'Add New'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'new_item'</span>              =&gt; __(<span class="hljs-string">'New Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'edit_item'</span>             =&gt; __(<span class="hljs-string">'Edit Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'update_item'</span>           =&gt; __(<span class="hljs-string">'Update Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'view_item'</span>             =&gt; __(<span class="hljs-string">'View Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'view_items'</span>            =&gt; __(<span class="hljs-string">'View Items'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'search_items'</span>          =&gt; __(<span class="hljs-string">'Search Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'not_found'</span>             =&gt; __(<span class="hljs-string">'Not found'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'not_found_in_trash'</span>    =&gt; __(<span class="hljs-string">'Not found in Trash'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'featured_image'</span>        =&gt; __(<span class="hljs-string">'Featured Image'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'set_featured_image'</span>    =&gt; __(<span class="hljs-string">'Set featured image'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'remove_featured_image'</span> =&gt; __(<span class="hljs-string">'Remove featured image'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'use_featured_image'</span>    =&gt; __(<span class="hljs-string">'Use as featured image'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'insert_into_item'</span>      =&gt; __(<span class="hljs-string">'Insert into item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'uploaded_to_this_item'</span> =&gt; __(<span class="hljs-string">'Uploaded to this item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'items_list'</span>            =&gt; __(<span class="hljs-string">'Items list'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'items_list_navigation'</span> =&gt; __(<span class="hljs-string">'Items list navigation'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'filter_items_list'</span>     =&gt; __(<span class="hljs-string">'Filter items list'</span>, <span class="hljs-string">'text_domain'</span>),
  );
  $args = <span class="hljs-keyword">array</span>(
    <span class="hljs-string">'label'</span>                 =&gt; __(<span class="hljs-string">'Movie'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'description'</span>           =&gt; __(<span class="hljs-string">'Movie Description'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'labels'</span>                =&gt; $labels,
    <span class="hljs-string">'supports'</span>              =&gt; <span class="hljs-literal">false</span>,
    <span class="hljs-string">'taxonomies'</span>            =&gt; <span class="hljs-keyword">array</span>(<span class="hljs-string">'category'</span>, <span class="hljs-string">'post_tag'</span>),
    <span class="hljs-string">'hierarchical'</span>          =&gt; <span class="hljs-literal">false</span>,
    <span class="hljs-string">'public'</span>                =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'show_ui'</span>               =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'show_in_menu'</span>          =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'menu_position'</span>         =&gt; <span class="hljs-number">5</span>,
    <span class="hljs-string">'show_in_admin_bar'</span>     =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'show_in_nav_menus'</span>     =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'can_export'</span>            =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'has_archive'</span>           =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'exclude_from_search'</span>   =&gt; <span class="hljs-literal">false</span>,
    <span class="hljs-string">'publicly_queryable'</span>    =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'capability_type'</span>       =&gt; <span class="hljs-string">'page'</span>,
  );
  register_post_type(<span class="hljs-string">'movie'</span>, $args);
</code></pre><p>Notice how we left out the add_action hook and the wrapping function from our previous code example in the previous article. That's because the <code>add_action</code> hooks will be used in a separate file, and our wrapping function is not needed since we are placing all of our code into the newly defined function.</p>
<h3 id="register-our-custom-post-type-using-a-hooked">Register our custom post type using a hooked</h3>
<p>We are almost done — the last thing we need to do is navigate to the includes folder and edit our <code>class-movies.php</code> file.</p>
<p>On line 174, you will notice the following code:</p>
<pre><code><span class="hljs-keyword">$this</span>-&gt;loader-&gt;add_action( <span class="hljs-string">'wp_enqueue_scripts'</span>, $plugin_public, <span class="hljs-string">'enqueue_scripts'</span> );
</code></pre><p>This is referencing the file and the function we were editing, so what we will need to do is update this line to use our function and the proper action hook to the following.</p>
<pre><code><span class="hljs-keyword">$this</span>-&gt;loader-&gt;add_action( <span class="hljs-string">'init'</span>, $plugin_public, <span class="hljs-string">'register_movies'</span> );
</code></pre><p>That's it! You can now navigate to the backend of your WordPress site, and as long as your plugin is active — you should see the new "Movies" custom post type.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627695378803/TkvDAkcAi.gif" alt="giphy (3).gif" /></p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>So there you have it; in this series, we covered that making a WordPress plugin can be as easy as creating a folder with a file and as complex as using a tried and true boilerplate to build an OOP plugin and ship nice, neat code that's ready for the marketplace.</p>
<p>Now I didn't get into the nitty-gritty about all of the wonderful things this boilerplate does for you — but suffice it to say that if you explore the boilerplate and read their amazing comments, you'll begin to learn and see the value it adds to the developer experience.</p>
<p>We're currently brewing a new Zoom plugin for WordPress that I plan to write about in the near future that will go into greater detail in how to build a real-world plugin. </p>
<p>I want to remind you that a plugin does not need to be complicated, complex, or feature-rich to add value to your client's sites or to any open-source community.</p>
<p>Just remember that the plugin ecosystem helps every one, no matter how big or small your idea might be.</p>
<hr />
<h2 id="keep-reading">Keep reading</h2>
<p>This is a series that will teach you how to build a plugin from scratch.</p>
<p>We will get out hands dirty, and by the end of this series, you should be able to take an idea that you have and turn it into a plugin yourself!</p>
<p>Scroll down to find the other articles in this series.</p>
]]></content:encoded></item><item><title><![CDATA[Learn how to create a plugin that registers a custom post type]]></title><description><![CDATA[In the previous article — we walked through creating a plugin by only adding a folder, a file, and the plugin header information at the top.
This article will create functionality for our plugin to help demonstrate how you can build your own WordPres...]]></description><link>https://blog.millertchris.com/learn-how-to-create-a-plugin-that-registers-a-custom-post-type</link><guid isPermaLink="true">https://blog.millertchris.com/learn-how-to-create-a-plugin-that-registers-a-custom-post-type</guid><category><![CDATA[WordPress]]></category><category><![CDATA[wordpress plugins]]></category><category><![CDATA[plugins]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Sat, 31 Jul 2021 01:49:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1627694832483/u7s2XJox_.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous article — we walked through creating a plugin by only adding a folder, a file, and the plugin header information at the top.</p>
<p>This article will create functionality for our plugin to help demonstrate how you can build your own WordPress plugin.</p>
<p>If you've been following along — welcome back!</p>
<h2 id="the-scope">The scope</h2>
<p>Let's keep this small and simple in scope by writing some functionality that will display a message at the bottom of the page — appending it to the footer.</p>
<h2 id="planning-the-plugin">Planning the plugin</h2>
<p>Understanding that the scope is to display a message at the bottom of the page, we will need to find a WordPress hook that will allow us to do that. </p>
<p>Luckily, WordPress has an action hook for just this called "wp_footer."</p>
<p>If you're not familiar with hooks — they allow you to do things at a particular place and time during the loading sequence of the CMS. </p>
<p>I'll expand on hooks in a future post.</p>
<h2 id="lets-build">Let's build!</h2>
<p>If we take a look at <a target="_blank" href="https://developer.wordpress.org/reference/hooks/wp_footer/">the wp_footer documentation</a> it will tell us how to use this hook along with a few great examples.</p>
<p>First, let's create our function.</p>
<pre><code><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">/**
 * Plugin Name: Pulp Fiction Quote
 */</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">display_message</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;div&gt;What does Marcellus Wallace look like?&lt;/div&gt;'</span>;
}
</code></pre><p>Now that we've created our function let's add it to the <code>wp_footer</code> action hook.</p>
<pre><code><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">/**
 * Plugin Name: Pulp Fiction Quote
 */</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">display_message</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;div&gt;What does Marcellus Wallace look like?&lt;/div&gt;'</span>;
}
add_action(<span class="hljs-string">'wp_footer'</span>, <span class="hljs-string">'display_message'</span>);
</code></pre><p>Boom — that's it! If you activate the plugin and visit any page on the site, you will see our message appear at the bottom.</p>
<h2 id="creating-more-functionality">Creating more functionality</h2>
<p>If you're already familiar with building a WordPress theme, then you'll know that the functions.php file is a pretty important file when you need to do things like register custom post types, use hooks, etc.</p>
<p>Guess what? You can paste all of that right into your plugin, and it will work all the same!</p>
<p>You could be sitting on a ton of useful code that you've already written and easily turn it into a plugin — crazy, I know.</p>
<p>Let's try creating a custom post type — which is a fairly common thing to do with any custom WordPress build.</p>
<h2 id="create-a-custom-post-type">Create a custom post type</h2>
<p>Okie dokes — let's start by wiping the slate clean and renaming our plugin to "Movies Post Type".</p>
<pre><code><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">/**
 * Plugin Name: Movies Post Type
 */</span>
</code></pre><p>Let's drop in all of the code we would need to <a target="_blank" href="https://developer.wordpress.org/reference/functions/register_post_type/">register a custom post type</a>.</p>
<p>Note: If you're not familiar with registering custom post types, I will cover this in a separate article.</p>
<pre><code><span class="hljs-comment">/**
 * Plugin Name: Movies Post Type
 */</span>

<span class="hljs-comment">// Register Custom Post Type</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">custom_post_type</span>(<span class="hljs-params"></span>) </span>{

  $labels = <span class="hljs-keyword">array</span>(
    <span class="hljs-string">'name'</span>                  =&gt; _x(<span class="hljs-string">'Movies'</span>, <span class="hljs-string">'Post Type General Name'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'singular_name'</span>         =&gt; _x(<span class="hljs-string">'Movie'</span>, <span class="hljs-string">'Post Type Singular Name'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'menu_name'</span>             =&gt; __(<span class="hljs-string">'Movies'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'name_admin_bar'</span>        =&gt; __(<span class="hljs-string">'Movie'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'archives'</span>              =&gt; __(<span class="hljs-string">'Item Archives'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'attributes'</span>            =&gt; __(<span class="hljs-string">'Item Attributes'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'parent_item_colon'</span>     =&gt; __(<span class="hljs-string">'Parent Item:'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'all_items'</span>             =&gt; __(<span class="hljs-string">'All Items'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'add_new_item'</span>          =&gt; __(<span class="hljs-string">'Add New Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'add_new'</span>               =&gt; __(<span class="hljs-string">'Add New'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'new_item'</span>              =&gt; __(<span class="hljs-string">'New Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'edit_item'</span>             =&gt; __(<span class="hljs-string">'Edit Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'update_item'</span>           =&gt; __(<span class="hljs-string">'Update Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'view_item'</span>             =&gt; __(<span class="hljs-string">'View Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'view_items'</span>            =&gt; __(<span class="hljs-string">'View Items'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'search_items'</span>          =&gt; __(<span class="hljs-string">'Search Item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'not_found'</span>             =&gt; __(<span class="hljs-string">'Not found'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'not_found_in_trash'</span>    =&gt; __(<span class="hljs-string">'Not found in Trash'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'featured_image'</span>        =&gt; __(<span class="hljs-string">'Featured Image'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'set_featured_image'</span>    =&gt; __(<span class="hljs-string">'Set featured image'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'remove_featured_image'</span> =&gt; __(<span class="hljs-string">'Remove featured image'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'use_featured_image'</span>    =&gt; __(<span class="hljs-string">'Use as featured image'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'insert_into_item'</span>      =&gt; __(<span class="hljs-string">'Insert into item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'uploaded_to_this_item'</span> =&gt; __(<span class="hljs-string">'Uploaded to this item'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'items_list'</span>            =&gt; __(<span class="hljs-string">'Items list'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'items_list_navigation'</span> =&gt; __(<span class="hljs-string">'Items list navigation'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'filter_items_list'</span>     =&gt; __(<span class="hljs-string">'Filter items list'</span>, <span class="hljs-string">'text_domain'</span>),
  );
  $args = <span class="hljs-keyword">array</span>(
    <span class="hljs-string">'label'</span>                 =&gt; __(<span class="hljs-string">'Movie'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'description'</span>           =&gt; __(<span class="hljs-string">'Movie Description'</span>, <span class="hljs-string">'text_domain'</span>),
    <span class="hljs-string">'labels'</span>                =&gt; $labels,
    <span class="hljs-string">'supports'</span>              =&gt; <span class="hljs-literal">false</span>,
    <span class="hljs-string">'taxonomies'</span>            =&gt; <span class="hljs-keyword">array</span>(<span class="hljs-string">'category'</span>, <span class="hljs-string">'post_tag'</span>),
    <span class="hljs-string">'hierarchical'</span>          =&gt; <span class="hljs-literal">false</span>,
    <span class="hljs-string">'public'</span>                =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'show_ui'</span>               =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'show_in_menu'</span>          =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'menu_position'</span>         =&gt; <span class="hljs-number">5</span>,
    <span class="hljs-string">'show_in_admin_bar'</span>     =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'show_in_nav_menus'</span>     =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'can_export'</span>            =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'has_archive'</span>           =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'exclude_from_search'</span>   =&gt; <span class="hljs-literal">false</span>,
    <span class="hljs-string">'publicly_queryable'</span>    =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'capability_type'</span>       =&gt; <span class="hljs-string">'page'</span>,
  );
  register_post_type(<span class="hljs-string">'movie'</span>, $args);
}
add_action(<span class="hljs-string">'init'</span>, <span class="hljs-string">'custom_post_type'</span>, <span class="hljs-number">0</span>);
</code></pre><p>If you go back to your plugins in the backend of WordPress and activate this plugin — you should now see a "Movies" menu appear in the backend of WordPress, similar to "Pages", that will allow you to begin adding movies!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627695747835/QKrwGYgXz.gif" alt="giphy (6).gif" /></p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>I hope you take away from this article that creating a WordPress plugin is not a difficult thing to do — once you understand why and how.</p>
<p>Continue to the next article and learn how to use a WordPress plugin boilerplate generator to create a well-structured, object-oriented framework to begin building production-ready plugins.</p>
<hr />
<h2 id="keep-reading">Keep reading</h2>
<p>This is a series that will teach you how to build a plugin from scratch.</p>
<p>We will get out hands dirty, and by the end of this series, you should be able to take an idea that you have and turn it into a plugin yourself!</p>
<p>Scroll down to find the other articles in this series.</p>
]]></content:encoded></item><item><title><![CDATA[Learn how to create a plugin in less than 2 minutes]]></title><description><![CDATA[WordPress plugins can be as simple or complex as you want them to be — and they are ridiculously easy to create, like, super easy.
In this article — I will walk you through creating a WordPress plugin in a way that I've come to learn, and hopefully, ...]]></description><link>https://blog.millertchris.com/learn-how-to-create-a-plugin-in-less-than-2-minutes</link><guid isPermaLink="true">https://blog.millertchris.com/learn-how-to-create-a-plugin-in-less-than-2-minutes</guid><category><![CDATA[WordPress]]></category><category><![CDATA[wordpress plugins]]></category><category><![CDATA[plugins]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Sat, 31 Jul 2021 01:48:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1627694776279/zdCMJP32P.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>WordPress plugins can be as simple or complex as you want them to be — and they are ridiculously easy to create, like, super easy.</p>
<p>In this article — I will walk you through creating a WordPress plugin in a way that I've come to learn, and hopefully, by the end of this article, you, too, will be able to start creating your own WordPress plugins.</p>
<h2 id="prep-work">Prep work</h2>
<p>There's minimum prep work involved in creating a plugin:</p>
<ul>
<li>Create a folder</li>
<li>Create a file (within that folder)</li>
<li>Add the plugin header</li>
</ul>
<h3 id="create-a-folder">Create a folder</h3>
<p>In the plugins directory, create a folder and call it whatever you want to call it. As a best practice, make sure it's lowercase, only letters, and no spaces.</p>
<h3 id="create-a-file">Create a file</h3>
<p>Now — create a PHP file and as a best practice, name it the same way you did with the folder.</p>
<h3 id="add-the-plugin-header">Add the plugin header</h3>
<p>For WordPress to detect your plugin, you must include the plugin header at the top of the file you just created. You can use the example below and replace "YOUR PLUGIN NAME" with anything.</p>
<pre><code><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/**
 * Plugin Name: YOUR PLUGIN NAME
 */</span>
</code></pre><h3 id="activate-the-plugin">Activate the plugin</h3>
<p>Navigate to your plugins menu in the backend of WordPress, and you should see the new plugin appear in the list — go ahead and activate it!</p>
<p><strong>There — that's it, you've created a plugin!</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627680660769/vZFkbIJCE.gif" alt="giphy (1).gif" /></p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Although plugins require a lot more than that to be of any use — the point I want to illustrate to you here is that a plugin is no more than a folder that will end up containing one or more PHP files and other dependencies that extend WordPress.</p>
<p>This concept is true in almost any CMS platform and JavaScript frameworks such as React, Vue, Angular, Svelte, etc.</p>
<hr />
<h2 id="keep-reading">Keep reading</h2>
<p>This is a series that will teach you how to build a plugin from scratch.</p>
<p>We will get out hands dirty, and by the end of this series, you should be able to take an idea that you have and turn it into a plugin yourself!</p>
<p>Scroll down to find the other articles in this series.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding the plugin concept]]></title><description><![CDATA[Plugins are the bedrock for any thriving open-source ecosystem that allows developers to extend the platform's core functionality.
Look around, and you'll find that nearly all platforms and frameworks have plugins, although some might call them modul...]]></description><link>https://blog.millertchris.com/understanding-the-plugin-concept</link><guid isPermaLink="true">https://blog.millertchris.com/understanding-the-plugin-concept</guid><category><![CDATA[WordPress]]></category><category><![CDATA[wordpress plugins]]></category><category><![CDATA[plugins]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Sat, 31 Jul 2021 01:46:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1627696118946/jlPNvUyPh.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Plugins are the bedrock for any thriving open-source ecosystem that allows developers to extend the platform's core functionality.</p>
<p>Look around, and you'll find that nearly all platforms and frameworks have plugins, although some might call them modules, extensions, etc.</p>
<p>In this article — we will explore the basic concepts of plugins and why they exist.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627695659451/BxARMik8q.gif" alt="giphy (4).gif" /></p>
<h2 id="so-what-are-plugins">So, what are plugins?</h2>
<p>Plugins are wonderful, agnostic, isolated pieces of code that can work independently or harmonize with other plugins. When you add a plugin — it should extend what the platform or framework is capable of doing.</p>
<h2 id="why-create-a-plugin">Why create a plugin?</h2>
<p>A great use case for creating a plugin is if you've identified a need for the same functionality across any project. You'll usually identify this after you're done with a project, as a retrospect, and find that you've created several useful bits of functionality that could be packaged up into a plugin.</p>
<p>The same reason you would create a CSS class, a function, or even a React component — if you find yourself needing to reuse the same bit of functionality, then chances are you've got a strong case for creating a plugin.</p>
<hr />
<h2 id="keep-reading">Keep reading</h2>
<p>This is a series that will teach you how to build a plugin from scratch.</p>
<p>We will get out hands dirty, and by the end of this series, you should be able to take an idea that you have and turn it into a plugin yourself!</p>
<p>Scroll down to find the other articles in this series.</p>
]]></content:encoded></item><item><title><![CDATA[Deploying with GitHub Actions.]]></title><description><![CDATA[When I started learning about this whole CI/CD thing over a year ago — it was honestly confusing. I mean — I understood what deployment meant and saw tons of value in it, but the rest of it was greek to me.
Reading, tinkering, and 30 hours later — I'...]]></description><link>https://blog.millertchris.com/deploying-with-github-actions</link><guid isPermaLink="true">https://blog.millertchris.com/deploying-with-github-actions</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[deployment]]></category><category><![CDATA[continuous deployment]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Sat, 19 Jun 2021 22:48:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1624142642379/AtQzwkjtF.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When I started learning about this whole CI/CD thing over a year ago — it was honestly confusing. I mean — I understood what deployment meant and saw tons of value in it, but the rest of it was greek to me.</p>
<p>Reading, tinkering, and 30 hours later — I've got some things figured out, and what I aim to do in this article is share how I understand it all and how you can set up an auto deploy process using a GitHub Action.</p>
<h2 id="primer">Primer</h2>
<p>First — I should warn you that setting up a GitHub Action requires more than a few steps. You will need access to your hosting environment and have FTP, SFTP, or SSH access in many cases. </p>
<p>This article will focus strictly on SSH.</p>
<h2 id="ssh-keys">SSH Keys</h2>
<p>Rather than excite you by diving right into the GitHub Action .yml file, let's get the hard stuff out of the way first and address the SSH Keypair thingy ma-bob.</p>
<p>Using SSH keys is a different way of authenticating into a server. Instead of using a username and password, you will supply a username and an SSH key instead.</p>
<p>SSH keys come in a pair — one is public and the other private.</p>
<p>Depending on what you're configuring, it may require you to provide one or the other — and in our case, to set up this auto deploy process, we will be using the private key.</p>
<h3 id="ssh-key-generator">SSH Key Generator</h3>
<p>First — you'll need to log in to your server over SSH.</p>
<p><code>ssh username@ip-address</code></p>
<p>Next, enter your password and once you're in, run the following command.</p>
<p><code>ssh-keygen -m PEM -t rsa -b 4096.</code></p>
<p>You will then be asked a series of questions; it's ok to smash that enter button several times till the end.</p>
<p>Now, go ahead and add your public key to your "authorized key" on the server. This step is crucial because I spent a ton of time troubleshooting why my auto deploy didn't work, and it's because I missed this step.</p>
<p><code>cat id_rsa.pub &gt;&gt; ~/.ssh/authorized_keys</code></p>
<p>Once that's done, run the following command, which will provide you with the secret SSH key. This will be the key we need when setting up the deployment using GitHub Actions.</p>
<p><code>cat ~/.ssh/id_rsa</code></p>
<p>If you're interested in learning more about SSH Keys, check out the link below.</p>
<p>https://www.ssh.com/academy/ssh/public-key-authentication</p>
<h2 id="create-deploy-steps">Create Deploy Steps</h2>
<p>Now for the fun part — head over to your GitHub repo and click on the "Actions" tab located at the top. This will bring you to a screen with tons of options, but don't fear, find the "Node.js" option and click "Set up this workflow."</p>
<p>That will bring you to a screen with the following code.</p>
<pre><code><span class="hljs-comment"># This workflow will do a clean install of node dependencies, build the source code, and run tests across different versions of the node</span>
<span class="hljs-comment"># For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">CI</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">master</span> ]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">master</span> ]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>

    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">strategy:</span>
      <span class="hljs-attr">matrix:</span>
        <span class="hljs-attr">node-version:</span> [<span class="hljs-number">12.</span><span class="hljs-string">x</span>, <span class="hljs-number">14.</span><span class="hljs-string">x</span>, <span class="hljs-number">16.</span><span class="hljs-string">x</span>]
        <span class="hljs-comment"># See supported Node.js release schedule at https://nodejs.org/en/about/releases/</span>

    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Use</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v2</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">node-version:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span> <span class="hljs-string">--if-present</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>
</code></pre><p>This can seem overwhelming at first, but I'll break it down the way I understood it.</p>
<p>First, we need an environment or operating system that will allow us to run all of our fancy build process stuff. So, it needs to install Ubuntu first.</p>
<p>Then, we need Node.js installed to do things like "npm install" or "npm run build," or else it would'nt know what to do. When that is all set up and ready to go, it will install our dependencies and build the files we want to deploy.</p>
<p>Okay — so now our files are built, but we haven't told it where we want them to go.</p>
<p>If we look to our right, we will see a panel with a search bar and a list of options. Search for "ssh deploy," and you should see the option below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1624140113315/mI-eDSZbz.png" alt="Screen Shot 2021-06-19 at 6.01.49 PM.png" /></p>
<p>Select "ssh deploy," scroll down, then copy and paste the following code at the bottom of your .yml file.</p>
<pre><code><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">ssh</span> <span class="hljs-string">deploy</span>
   <span class="hljs-attr">uses:</span> <span class="hljs-string">easingthemes/ssh-deploy@v2.2.11</span>
   <span class="hljs-attr">with:</span>
      <span class="hljs-attr">SSH_PRIVATE_KEY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SSH_PRIVATE_KEY</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">REMOTE_HOST:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SERVER</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">REMOTE_USER:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.USERNAME</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">TARGET:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.PRODUCTION_DIR</span> <span class="hljs-string">}}</span>
</code></pre><h2 id="secrets">Secrets</h2>
<p>You may have noticed that we added some variables above; those are called "secrets," which we will need to create by visiting the repo settings page within GitHub and selecting "Secrets" at the bottom left of the menu.</p>
<p>Select "New repo secret" and begin creating the variables we set above. As you create your variables, pay close attention to these important things:</p>
<ul>
<li>SSH_PRIVATE_KEY: You will need to get your private SSH key by logging into your server through the terminal and running <code>cat ~/.ssh/id_rsa</code>, which will print out your private SSH key on the screen. Copy the entire block including -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----</li>
<li>REMOTE_HOST: The server IP address</li>
<li>REMOTE_USER: The same username you use to login through SSH</li>
<li>TARGET: Target will be the full directory path where you want to deploy your files i.e. /var/www/vhosts//httpdocs/staging/web/app/</li>
</ul>
<p><em>Make sure that you create the folder on your server ahead of time, or the deployment process will fail.</em></p>
<p>Okie dokes! With all of that now out of the way, it's time to start testing!</p>
<h2 id="testing-your-auto-deploy">Testing your auto deploy</h2>
<p>To test your auto deploy, commit a change and push it up to your main branch. Visit the actions tab within your repo, and you should see a build in progress. Click on the build process to see it all in action. (no pun intended)</p>
<h2 id="setting-up-different-branches">Setting up different branches</h2>
<p>There are many different ways to set up and configure how and where your deployments go. In a fully fleshed-out example below, you will see that we deploy to a staging site whenever a push occurs on the staging branch and deploying to the production site whenever a push occurs on the main branch.</p>
<pre><code><span class="hljs-comment"># This workflow will do a clean install of node dependencies, build the source code, and run tests across different versions of node</span>
<span class="hljs-comment"># For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Auto</span> <span class="hljs-string">Deploy</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>, <span class="hljs-string">staging</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">strategy:</span>
      <span class="hljs-attr">matrix:</span>
        <span class="hljs-attr">node-version:</span> [<span class="hljs-number">14.</span><span class="hljs-string">x</span>]
        <span class="hljs-comment"># See supported Node.js release schedule at https://nodejs.org/en/about/releases/</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">files</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span> <span class="hljs-string">--if-present</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">staging</span>
        <span class="hljs-attr">if:</span> <span class="hljs-string">github.ref</span> <span class="hljs-string">==</span> <span class="hljs-string">'refs/heads/staging'</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">easingthemes/ssh-deploy@v2.2.11</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">SSH_PRIVATE_KEY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SSH_PRIVATE_KEY</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">REMOTE_HOST:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SERVER</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">REMOTE_USER:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.USERNAME</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">TARGET:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.STAGING_DIR</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">EXCLUDE:</span> <span class="hljs-string">".git*, .github*, node_modules*, src*, webpack.config.js"</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">production</span>
        <span class="hljs-attr">if:</span> <span class="hljs-string">github.ref</span> <span class="hljs-string">==</span> <span class="hljs-string">'refs/heads/main'</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">easingthemes/ssh-deploy@v2.2.11</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">SSH_PRIVATE_KEY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SSH_PRIVATE_KEY</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">REMOTE_HOST:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SERVER</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">REMOTE_USER:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.USERNAME</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">TARGET:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.PRODUCTION_DIR</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">EXCLUDE:</span> <span class="hljs-string">".git*, .github*, node_modules*, src*, webpack.config.js"</span>
</code></pre><h2 id="wrapping-up">Wrapping up</h2>
<p>If you made it this far in the article, that means you love reading, or it's not working. </p>
<p>If it's the latter — go back and read some of the beginning steps about generating and copying the private key.</p>
<p>Pay close attention to the "authorized keys" part as well — as it turns out — this was the part where I spent a lot of time on and stumbled quite a bit.</p>
<p>Like or comment if you found this helpful, and say hello on Twitter!</p>
]]></content:encoded></item><item><title><![CDATA[How to record Twitter Spaces on Mac.]]></title><description><![CDATA[Twitter Spaces bring people together to share thoughts and ideas in an open and public way — and as a host or a speaker of a Twitter Space — you may want to record the talk.
This article will walk you through how to do this using an iPhone and a Mac....]]></description><link>https://blog.millertchris.com/how-to-record-twitter-spaces-on-mac</link><guid isPermaLink="true">https://blog.millertchris.com/how-to-record-twitter-spaces-on-mac</guid><category><![CDATA[Twitter]]></category><category><![CDATA[macOS]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Tue, 25 May 2021 04:29:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1621915723098/-83GqsSBD.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Twitter Spaces bring people together to share thoughts and ideas in an open and public way — and as a host or a speaker of a Twitter Space — you may want to record the talk.</p>
<p>This article will walk you through how to do this using an iPhone and a Mac. I'm sure there's a way to achieve this on a PC, but I won't be covering that in this article.</p>
<h2 id="record-twitter-space-as-a-listener">Record Twitter Space as a listener</h2>
<p>Here's what you need:</p>
<ol>
<li>iPhone</li>
<li>Mac</li>
<li>Lightning cable</li>
</ol>
<p>Here are the steps:</p>
<ol>
<li>Connect your iPhone to the Mac</li>
<li>Open up QuickTime player</li>
<li>Select "New Audio Recording" from the File menu</li>
<li>Select the dropdown arrow to the right of the menu</li>
<li>Choose your iPhone</li>
<li>Start recording</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621916734194/XDvG7IRTq.png" alt="Screenshot of Quicktime iPhone audio recording.png" /></p>
<p>At this point — you should start to see the audio indicator reacting. Any audio that's playing on your iPhone, including Twitter Space, will be recorded.</p>
<h2 id="record-twitter-space-as-a-host-or-speaker">Record Twitter Space as a host or speaker</h2>
<p>If you plan to host or speak — you'll quickly find out that QuickTime will not detect the audio. I'm not sure what this is, but I assume it has something to do with the audio hardware in active use while you're a speaker.</p>
<p>To get around this — you'll need to use another iPhone or ask a friend to record the Twitter Space for you. In my case — I used my wife's phone to record the Space — while using my phone to be a speaker inside of the Space.</p>
]]></content:encoded></item><item><title><![CDATA[How to import Google Font into MailChimp.]]></title><description><![CDATA[Recently, I converted a custom-designed email template into a native MailChimp newsletter that needs to support a specific Google Font.
The email template, a 1 column design, used a font not supported by MailChimp. At this current time, MailChimp sup...]]></description><link>https://blog.millertchris.com/how-to-import-google-font-into-mailchimp</link><guid isPermaLink="true">https://blog.millertchris.com/how-to-import-google-font-into-mailchimp</guid><category><![CDATA[email]]></category><category><![CDATA[HTML Emails]]></category><category><![CDATA[email marketing]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Mon, 17 May 2021 00:52:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619449267467/l5zd4UwGX.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, I converted a custom-designed email template into a native MailChimp newsletter that needs to support a specific Google Font.</p>
<p>The email template, a 1 column design, used a font not supported by MailChimp. At this current time, MailChimp supports only a handful of base operating system fonts and Google Fonts for excellent reasons.</p>
<p>You can learn more about their reasons here: <br /> <a target="_blank" href="https://templates.mailchimp.com/design/typography/">Typography | Email Design Reference</a> </p>
<p>Searching high and low, I found an article that demonstrated how a user could use a custom Google Font by using a WYSIWYG block to toggle HTML editing and importing the font that way.</p>
<p>That was helpful — but I felt there had to be a better solution; after all, the user could delete that block or add a new block at the font would be lost.</p>
<p>With this in mind, I explored all of the blocks available to me and noticed that there was an HTML block that allows you to use custom code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619449330472/dK9Hx22tQ8.png" alt="mailchimp-block.png" /></p>
<p>I decided to place the block above the header to be the first block that loads in the email. Within the block, I dropped in Google Font provided @import statement and targeted nearly every element and assigning the font-family with !important to ensure it overrides anything MailChimp might be doing.</p>
<p>It worked!</p>
<p>Here’s the code for your copy and paste convenience.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="7edb9f66ae394069428c1d78e21764a0"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/millertchris/7edb9f66ae394069428c1d78e21764a0" class="embed-card">https://gist.github.com/millertchris/7edb9f66ae394069428c1d78e21764a0</a></div><p>This solution makes it pretty difficult for a user to delete the block since it’s nearly collapsed on itself and it lives above the header, which helps to ensure it loads early and protects it.</p>
<p>What are your thoughts?</p>
<p>Have you found another approach that works well for your client?</p>
]]></content:encoded></item><item><title><![CDATA[Tried and True; Troubleshooting.]]></title><description><![CDATA[The key to becoming a great developer is learning how to troubleshoot effectively.
Every language and platform has built-in methods for troubleshooting. For example — Javascript has console.log() for printing things into the console within dev tools ...]]></description><link>https://blog.millertchris.com/tried-and-true-troubleshooting</link><guid isPermaLink="true">https://blog.millertchris.com/tried-and-true-troubleshooting</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[PHP]]></category><category><![CDATA[React]]></category><category><![CDATA[Vue.js]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Mon, 03 May 2021 00:02:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619999776263/5T0zobDfu.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The key to becoming a great developer is learning how to troubleshoot effectively.</p>
<p>Every language and platform has built-in methods for troubleshooting. For example — Javascript has <code>console.log()</code> for printing things into the console within dev tools so you can tell whether a function or variable is working correctly.</p>
<p>In PHP — you can use things like <code>var_dump()</code> and <code>print_r()</code> to do something similar.</p>
<p>In this article, I will go further with examples of how you can quickly and effectively troubleshoot in any situation you might find yourself in.</p>
<h2 id="troubleshooting-javascript">Troubleshooting Javascript</h2>
<p>Regardless if you're using a JS framework or good 'ole vanilla Javascript — <code>console.log()</code> is hands down your best friend when it comes to figuring out problems.</p>
<p>Are the variable settings? Is it a string or an integer? Is it an object or an array? Is it NULL?</p>
<p>Just <code>console.log()</code> it, and it'll keep you from aimlessly guessing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619998959022/cwCVbv_FQ.png" alt="Demonstrating console.log() in Javascript" /></p>
<p>If you are using a JS framework like Vue or React — you'll undoubtedly find a ton of value in downloading and installing the following Chrome extensions.</p>
<p><a target="_blank" href="https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en">Vue Dev Tools</a> </p>
<p><a target="_blank" href="https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en">React Dev Tools</a></p>
<p>Once installed — you'll need to close and reopen your Chrome dev tools to see the new panel. Within this panel are loads of helpful tools that allow you to see what's happening in your Vue and React app.</p>
<h2 id="troubleshooting-php">Troubleshooting PHP</h2>
<p>There are tons of fancy environment setups for getting error logs to print out on the screen. However, two extremely powerful functions allow you to diagnose what might be happening with your code quickly.</p>
<p>The for one is <code>var_dump()</code>, which can be a little misleading because it does more than handle variables. It also handles functions too!</p>
<pre><code><span class="hljs-meta">&lt;?php</span>
    $name = <span class="hljs-string">'Duder McDuderson'</span>
    var_dump($name);
<span class="hljs-meta">?&gt;</span>
</code></pre><p>Passing a variable function or object in <code>var_dump()</code> — the next time you refresh your webpage — it will display what's happening.</p>
<p>I'll often use it when setting up loops and conditional with something like <code>var_dump('test')</code> to check if the conditional logic works** the way I want it to.</p>
<p>The other function — <code>print_r()</code> — does the same, but in some environments, it will print the data out in a human-readable way which can be extremely beneficial when working with arrays and objects.</p>
<h2 id="conclusion">Conclusion</h2>
<p>When you find yourself scratching your head — do not hesitate to reach for any of these built-in functions. They will help you save loads of time and get you through some frustrating moments as you're out there building out there in the wild.</p>
]]></content:encoded></item><item><title><![CDATA[A new Advanced Custom Field type lets you select any WordPress menu.]]></title><description><![CDATA[I faced a recent challenge where a site needed to support different main menus on different parent pages. I'm a big fan of Advanced Custom Fields and immediately knew that it would be the perfect tool for the job.
There were 40 parent pages that each...]]></description><link>https://blog.millertchris.com/a-new-advanced-custom-field-type-lets-you-select-any-wordpress-menu</link><guid isPermaLink="true">https://blog.millertchris.com/a-new-advanced-custom-field-type-lets-you-select-any-wordpress-menu</guid><category><![CDATA[WordPress]]></category><category><![CDATA[wordpress plugins]]></category><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Thu, 29 Apr 2021 20:28:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619727169301/zg0wGb4WC.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I faced a recent challenge where a site needed to support different main menus on different parent pages. I'm a big fan of Advanced Custom Fields and immediately knew that it would be the perfect tool for the job.</p>
<p>There were 40 parent pages that each required their own navigation. Under each of the 40 parent pages would be several children pages and probably children of children and so on.</p>
<p>I grappled with the ACF Relationship field first, but it wouldn’t allow me to nest pages natively. There was a thought that “Maybe the user could select the parent pages and the menu will build itself,” but the project requirements dictated otherwise.</p>
<p>Ultimately the user would need to control both nesting and order.</p>
<p>Briefly — I performed research but quickly pivoted to, “Hey, I bet it wouldn’t be too difficult to make my own ACF field.”</p>
<p>Then I found this;  <a target="_blank" href="https://www.advancedcustomfields.com/resources/creating-a-new-field-type/">ACF | Creating a new field type.</a> </p>
<p>Of course — Elliot Condon and his team would make it dead simple for developers to create their fields; after all, they’ve done such an amazing job doing just that for years now.</p>
<p>I dived right into their boilerplate repo and had to figure out where I would be writing my code.</p>
<p>Here’s what I found.</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render_field</span>(<span class="hljs-params"> $field </span>) </span>{

}
</code></pre><p>Turns out — this is where I needed to write the code. The idea was to expose a new field type where an array of menu ids would power the data.</p>
<p>Here what I did to get the menu ids:</p>
<pre><code>$menus = wp_get_nav_menus();

<span class="hljs-keyword">foreach</span> ($menus <span class="hljs-keyword">as</span> $key =&gt; $value) {
    $field[<span class="hljs-string">'choices'</span>][$value-&gt;term_id] = $value-&gt;name;
}
</code></pre><p>That was pretty easy! However — this next part, not so much.</p>
<p>I wanted to use the select2 dropdown field that ACF uses — you know — that slick-looking dropdown with a search field inside it?</p>
<p>The reason was not purely based on bias; remember, there will be 40 pages that need 40 different menus, and that’s a lot to scroll through!</p>
<p>After doing some Google searching, I was able to find this little gem.</p>
<pre><code>$field[<span class="hljs-string">'type'</span>]  = <span class="hljs-string">'select'</span>;
$field[<span class="hljs-string">'ui'</span>]    = <span class="hljs-number">1</span>;
$field[<span class="hljs-string">'label'</span>] = <span class="hljs-string">''</span>;

acf_render_field_wrap($field);
</code></pre><p>Turns out — there are a few things that are required if you want to use the select2 or “fancy select dropdown.”</p>
<p>Setting both the type to “select” and the UI to “1” is required. Then <code>acf_render_field_wrap($field)</code> brings the magic sauce.</p>
<p>Here’s what it all looks like put together.</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render_field</span>(<span class="hljs-params"> $field </span>) </span>{
        $field[<span class="hljs-string">'type'</span>]  = <span class="hljs-string">'select'</span>;
        $field[<span class="hljs-string">'ui'</span>]    = <span class="hljs-number">1</span>;
        $field[<span class="hljs-string">'label'</span>]    = <span class="hljs-string">''</span>;

        $menus = wp_get_nav_menus();

        <span class="hljs-keyword">foreach</span> ($menus <span class="hljs-keyword">as</span> $key =&gt; $value) {
            $field[<span class="hljs-string">'choices'</span>][$value-&gt;term_id] = $value-&gt;name;
        }

        acf_render_field_wrap($field);
    }
</code></pre><p>With this in place — we now have the ability to create a new menu field for our pages. When the user makes a selection — the ACF function <code>get_field()</code> will return the menu ID. You can use this ID to display the menu using the WP function<code>wp_nav_menu()</code>.</p>
<p>Here's what it looks like in action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619726173018/2ito-HDZr.gif" alt="Demonstrating new ACF menu field type" /></p>
<p>Pretty sweet — yea? Let's see what it looks like when editing a page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619726384813/OMO1XNWB8.gif" alt="Demonstrating the new ACF menu field in action when editing a page" /></p>
<p>If you find yourself needing this type of functionality — hopefully, this saves you some time.</p>
<p>You can download the <a target="_blank" href="https://github.com/millertchris/acf-menu-field">plugin here</a>.</p>
<p>Cheers! 🎉</p>
]]></content:encoded></item><item><title><![CDATA[Abstraction. Distraction.]]></title><description><![CDATA[We have witnessed a proliferation of frameworks over the past decade (and continue to) — build processes to CSS, JS, HTML, PHP, the list goes on.
Now don't get me wrong — I enjoy tinkering and trying new things; it's fun and helps me advance my knowl...]]></description><link>https://blog.millertchris.com/abstraction-distraction</link><guid isPermaLink="true">https://blog.millertchris.com/abstraction-distraction</guid><category><![CDATA[javascript framework]]></category><category><![CDATA[CSS Frameworks]]></category><category><![CDATA[framework]]></category><category><![CDATA[frameworks]]></category><dc:creator><![CDATA[Chris Miller]]></dc:creator><pubDate>Tue, 27 Apr 2021 00:50:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619484060321/-gEB1EBaJ.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We have witnessed a proliferation of frameworks over the past decade (and continue to) — build processes to CSS, JS, HTML, PHP, the list goes on.</p>
<p>Now don't get me wrong — I enjoy tinkering and trying new things; it's fun and helps me advance my knowledge around all things web. But if you're trying to build a freelance career or join a large tech company — this may not be the best use of your time.</p>
<p>Why, you might ask?</p>
<p>Let's say that you've settled into a popular CSS framework like Bootstrap or Tailwind and have gotten comfortable cranking out tons of sites using their class-based approach.</p>
<p>Did it help you advance your CSS/SASS skills at all?</p>
<p>If I had to guess — that answer is probably no, and it's due to the abstraction they are putting in front of you. It sort of traps you into their system, and it becomes all that you know.</p>
<p>This can put you at a huge disadvantage when interviewing for jobs or taking on freelance gigs if you do not have strong CSS/SASS knowledge or experience. </p>
<p>Rather than learning a framework — learn the core language instead and graduate up to SASS, giving you access to things like loops and mixins — which are just like functions if you're familiar with that concept.</p>
<p>We've only covered CSS in this article, but the same applies to any language.</p>
<p>Once you've become proficient in the core language — it makes understanding and grasping abstract concepts a whole easier. You will become more flexible, comfortable, and better positioned on whatever the gig requires.</p>
<p>It will also help to make you an all-around better developer.</p>
]]></content:encoded></item></channel></rss>