Poplar ProductivityWare Articleshttp://poplarware.com/articles.rss
en Introduction to Entities in Drupal 8http://poplarware.com/articles/intro-entities-d8
<span class="field field--name-title field--type-string field--label-hidden"> Introduction to Entities in Drupal 8</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Mon, 02/05/2018 - 14:49</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>On February 4, 2018, I presented a session at the <a href="http://pnwdrupalsummit.org">Pacific Northwest Drupal Summit</a> entitled "Anatomy of a Drupal Page Request. The session covered:</p>
<ul><li>The difference between Content and Configuration entities</li>
<li>The difference between Configuration entities and simple configuration</li>
<li>Why you should store your module's data in entities</li>
<li>Which entities are in Core</li>
<li>Basics of entity programming (querying, creating, updating)</li>
</ul><p>Slides are attached below.</p></div>
<div class="field field--name-field-downloads field--type-file field--label-above">
<div class="field__label">Downloads</div>
<div class="field__items">
<div class="field__item">
<span class="file file--mime-application-pdf file--application-pdf"> <a href="http://poplarware.com/sites/default/files/downloads/IntroEntities.pdf" type="application/pdf; length=443568">IntroEntities.pdf</a></span>
</div>
</div>
</div>
Mon, 05 Feb 2018 22:49:34 +0000jhodgdon37 at http://poplarware.comNew and Favorite Features of api.drupal.orghttp://poplarware.com/blog/api-drupal-org-features
<span class="field field--name-title field--type-string field--label-hidden">New and Favorite Features of api.drupal.org</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Wed, 06/28/2017 - 12:45</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>I have been volunteering my time with the open-source <a href="https://www.drupal.org">Drupal</a> web software project for many years, helping at various times with code writing, reviewing, and testing; documentation writing and editing; and managing subsets of the project. One of the volunteer roles I am most proud of is that for the past five years or so, I've been co-maintainer of the <a href="https://www.drupal.org/project/api">API module</a>, which is the software behind the Drupal API documentation site, <a href="https://api.drupal.org">api.drupal.org</a>. If you're a Drupal programmer, you're probably familiar with this site, where you can go to find API documentation for the PHP classes and functions in even very old versions of Drupal. But you may not be aware of all of its useful features, some of which were added to the site quite recently... so I thought I would write up a list of my favorite features and ways to use the site. Warning: I haven't made any attempt to explain technical terms here -- this list is aimed at Drupal programmers. For an introduction to Drupal programming... of course I would suggest you get my book, <a href="http://shop.oreilly.com/product/0636920027799.do">Programmers Guide to Drupal</a>.</p>
<dl><dt>Searching</dt>
<dd>On every page of the site, there is a search box at the top of the sidebar, where you can type in the name of a class, function, etc. It has an autocomplete feature, so you can see matches as you type, but once you finish typing and search, you can search for both partial and complete matches. I think most people know about this way of searching, which is a great way to find documentation for a function or class that you know the name of. What you might not have noticed is that there is a second search form in the header of the site. That search form performs a full-text search on documentation, so it is useful for finding functions and classes when you don't even know their names. You can also access these search results from the search form on the main <a href="https://www.drupal.org">drupal.org</a> web site, mixed in with other Drupal documentation, forums, etc. Try it out!</dd>
<dt>Topics</dt>
<dd>Starting with Drupal 8 (and to a lesser extend in previous versions of Drupal), if you go to the landing page for the branch of interest (such as <a href="https://api.drupal.org/api/drupal/8.3.x" title="Landing page for Drupal 8.3.x on api.drupal.org">8.3.x</a>, <a href="https://api.drupal.org/api/drupal/8.4.x" title="Landing page for Drupal 8.4.x on api.drupal.org">8.4.x</a>, etc.), you will find a list of topics, which will guide you through learning the basics of the Drupal API. (This is another Drupal volunteer effort I'm quite proud of: early in the Drupal 8 cycle, Angie "webchick" Byron and I sat down at a Drupal conference and decided on a preliminary list of topics that needed to be on that page, and by the time Drupal 8.0.0 was released, several other volunteers and I had managed to get them written. They've since been expanded and revised.)</dd>
<dt>Form/render elements page</dt>
<dd>If you've been using Drupal for a while, you've probably visited the <a href="https://api.drupal.org/api/drupal/developer%21topics%21forms_api_reference.html/7.x">Form API reference page</a> at least once. It lists most or all of the "elements" you can put into Drupal forms, and documents them... to some extent. Unfortunately, it was written as a very large, flat HTML file, and it's nearly impossible to maintain, so it's not complete or totally accurate (it is such a maintenance nightmare, that I almost wrote "the dreaded Form API reference page" earlier in this paragraph). In Drupal 8, this has been replaced by an <a href="https://api.drupal.org/api/drupal/elements">Elements page</a>, which you can find by going to the landing page for a branch (8.2.x, 8.3.x, etc.) and clicking on "Elements" in the right sidebar. This page lists all the form elements, plus render elements, and you can click through to get more documentation. Since the documentation is maintained with the form/render element classes, it's much more likely to be correct than the old reference page.</dd>
<dt>Listing pages</dt>
<dd>Each branch on the site has a collection of "listing" pages, such as Functions, Classes, etc. There are several new listing pages for Drupal 8, including a Services page and a listing of classes and functions that have been marked as Deprecated. Also, relatively recently, these listing pages were converted to use the Views module, which allowed the addition of many sorting and filtering options. For instance, if you go to the <a href="https://api.drupal.org/api/drupal/classes/8.4.x">Drupal 8.4.x Classes listing page</a>, you can filter by name or namespace (with partial matching), or sort by number of uses, to find a list of the most popular classes. Another useful example: You can go to the <a href="https://api.drupal.org/api/drupal/deprecated/8.4.x">Deprecated listing page</a>, and sort by one of the four right columns to find deprecated functions, methods, and classes that are still being used in the codebase.</dd>
<dt>Cross-linking</dt>
<dd>Some documentation includes See Also links to relevant topics and other functions, classes, etc. Also, as much as possible, automatic links are made in both documentation text and code listings.</dd>
<dt>N functions call this, etc.</dt>
<dd>It's been possible on the site for many years to see a list of which functions call a given function. For instance, if you're on the page for the <a href="https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/check_plain/7.x">Drupal 7 check_plain() function</a>, you can see a list of all 186 functions that call it. What you might not realize is that there are now many other "references" listings on the site, including:
<ul><li>Strings containing the names of functions, classes, form/render element IDs, YML configuration files, and Drupal 8 services</li>
<li>Use statements for classes, interfaces, and traits</li>
<li>Implementations and invocations of hooks, including theme hooks</li>
<li>Classes that implement interfaces</li>
<li>Full class hierarchy for classes, including both "parent" and "child" classes</li>
<li>Overrides of class methods</li>
<li>Usage of constants in code</li>
<li>Drupal 8 plugin classes that are declared to be of a given annotation type</li>
</ul></dd>
</dl><p>I hope everyone learned something new about api.drupal.org from this article -- happy programming!</p>
</div>
Wed, 28 Jun 2017 19:45:09 +0000jhodgdon36 at http://poplarware.comAnatomy of a Drupal Page Requesthttp://poplarware.com/articles/anatomy-page-request
<span class="field field--name-title field--type-string field--label-hidden">Anatomy of a Drupal Page Request</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Mon, 02/27/2017 - 14:49</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>On February 26, 2017, I presented a session at the <a href="http://pnwdrupalsummit.org">Pacific Northwest Drupal Summit</a> entitled "Anatomy of a Drupal Page Request. Description:</p>
<p>Have you ever wondered how Drupal responds to a request for a URL within a site? This session will describe how the request process works in Drupal. Some background on how web servers work will be included, and both an overview and some of the gritty details of Drupal's part in serving the request will be covered.</p>
<p>The focus will be on Drupal 8, but Drupal 7's process will also be covered. Non-programmers should come away with a better understanding of how Drupal works "under the hood"; programmers should come away with a better understanding of how their modules and themes can interact with and influence the process.</p>
<p>Slides are attached below.</p></div>
<div class="field field--name-field-downloads field--type-file field--label-above">
<div class="field__label">Downloads</div>
<div class="field__items">
<div class="field__item">
<span class="file file--mime-application-pdf file--application-pdf"> <a href="http://poplarware.com/sites/default/files/downloads/AnatomyPageRequest.pdf" type="application/pdf; length=303336">AnatomyPageRequest.pdf</a></span>
</div>
</div>
</div>
Mon, 27 Feb 2017 22:49:25 +0000jhodgdon35 at http://poplarware.comPartially Multilingual Site and SEOhttp://poplarware.com/articles/partially-multilingual-seo
<span class="field field--name-title field--type-string field--label-hidden">Partially Multilingual Site and SEO</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Fri, 02/17/2017 - 07:56</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Between 2006 and 2010, I wrote a series of blog posts that I translated into Spanish. At the time, they were posted on a dedicated WordPress site, using a somewhat-hacked-together "Language Switcher" plugin that I'd found and fixed up so that it actually worked. But at some point, I decided that I didn't want to continue to maintain the plugin, and it was a bit klunky to use, so I migrated the content into my then-existing Drupal 6 poplarware.com site (using some custom code, along with the Drupal <a href="https://www.drupal.org/project/migrate">Migrate</a> and <a href="https://www.drupal.org/project/wordpress_migrate">WordPress Migrate</a> modules -- I needed the custom code to deal with the multilingual articles).</p>
<p>This worked out fairly well, but I ran into a search engine optimization (SEO) issue: the blog posts had a language switcher block, to allow readers to view them in either Spanish or English. But once they had switched into Spanish, if they clicked through to other pages in the site (outside the blog), they would still be trying to view the site in Spanish, although the rest of the site was only in English. So, for example, if they went to the About page on the site, they might be on URL <em>/es/node/2</em>, whereas the URL (alias) for the page in English (with the same content) was <em>/about</em>. Having two URLs with the same content on the same site is not good for SEO; besides which, the Spanish URL was ugly, and the page header would have indicated that the page was in Spanish rather than its actual English, which is an accessibility bug.</p>
<p>So, in September, when I <a href="http://poplarware.com/news/drupal-8-site">redid my web site using Drupal 8</a>, I didn't include my old blog posts, not wanting to continue this SEO and accessibility problem. But that was only a temporary solution. What I really wanted:</p>
<ul><li>All of the old English and Spanish blog content to be available. I put a lot of work into the Spanish translations, as well as the writing of the blog articles, and with recent political developments, they were seeming quite relevant again.</li>
<li>When on a page with both English and Spanish content (limited to my older blog posts), have links that let the viewer switch languages.</li>
<li>When on an English-only page, don't have a link for Spanish. The standard Language Switcher blocks provided by Drupal 8 show all the languages defined on the site, which would be confusing on an English-only page.</li>
<li>Redirect ugly and misleading URLs like <em>/es/node/2</em> for pages that only exist in English, back to the English (aliased) URL for the page (like <em>/about</em>).</li>
</ul><p>Yesterday, I got this all working -- you can <a href="http://jhodgdon.com/HodgBlog/spanish-posts-back">see it in action on this blog post</a> (which I translated into Spanish for the occasion). So... how did I get this to work?</p>
<h2>Content Migration</h2>
<p>I had tried earlier to use the core Migrate module to migrate the Drupal 6 site to Drupal 8, and this didn't work. The <a href="https://www.drupal.org/node/2793947">issue I created at the time is still open</a>, so I think it's probably still broken -- many of my pages came through with their latest revision completely blank... not very useful! So, to get the blog posts and tags imported to the new site, I did the following:</p>
<ul><li>On a local development copy of the old Drupal 6 site, I used the Views module to make an RSS feed output containing the full content of all the blog posts, in both languages, including the tags.</li>
<li>On my live Drupal 8 site (after testing on a development site first!), I used a <a href="https://www.drupal.org/project/feeds">development version of the Feeds module</a> for Drupal 8 to import the RSS feed. (I would have preferred to use a CSV export/import, but Feeds doesn't yet support CSV files in Drupal 8.)</li>
<li>After importing, I had to manually go through the posts and turn the Spanish ones into translations of the English ones. This was tedious, but I didn't have a huge number, so it was not impossible. (If I'd been able to use a CSV export/import, I would have been able to tell Feeds that the Spanish posts were translations of particular English ones, and this would have been handled automatically. Or, if the Drupal 6 to 8 Migrate module had worked, this also would have been handled. Alas!)</li>
<li>I also translated the tags to Spanish (they were created in English during the Feeds import), by copying the translations I had on my Drupal 6 site. Again, a manual process.</li>
<li>I uninstalled and removed the unsupported, development version of Feeds.</li>
</ul><h2>Languages Block</h2>
<p>I created the Languages block using the Views module. Brief notes:</p>
<ul><li>It's a Content view, filtered to published content of my blog post content type.</li>
<li>A bit of background information: When you create a Content view on a multilingual site, each output row in the view (unless you filter) represents a translation of a content item into one language (or the source language version).</li>
<li>The Block display uses fields. The only field is the Translation Language field, and it's set to "Link to the content" and "Display in native language".</li>
<li>There's a Contextual Filter (in the Advanced section of the Views UI) on the Content ID field, with "Provide a default value" selected, and "Content ID from URL".</li>
</ul><p>Taken together, what this block does is that if you're on a page displaying one of my blog articles, it detects the content ID (contextual filter), finds all available language versions of that content item (views rows are individual translations), and for each one, displays a link to the content item, whose link text is the language. Using the standard Block Layout user interface in Drupal 8, I displayed this block on just the pages of my blog content type.</p>
<h2>Redirecting</h2>
<p>The last piece of the puzzle was the most fun to work out (yes, programming is fun!). I wanted a site visitor who had previously switched to Spanish to view a blog article, to be redirected back into English if they visited an English-only page on the site. The solution to this turned out to be a module with under 200 lines of code (including comments and help) -- fairly straightforward! The way it works is that in Drupal 8, near the beginning of the process of handling a page request, the embedded Symfony framework sends out a "Request" event, which allows modules to respond to the fact that there is a page being requested. (If you are interested in more details on this, I'm giving a <a href="http://pnwdrupalsummit.org/2017/sessions/anatomy-drupal-page-request">talk next week at the Pacific Northwest Drupal Summit about this topic</a>, and will be posting my presentation here afterwards. Or, you can read the <a href="http://symfony.com/doc/current/components/http_kernel.html">Symfony documentation about the HTTP request framework</a>.)</p>
<p>My module, in responding to this event:</p>
<ul><li>Checks to see if the request is for a content item page (base URL node/12345 for example).</li>
<li>If so, loads the content item and finds out what languages are available for the content.</li>
<li>Compares the content language list to the language of the request.</li>
<li>If the request is for a language without a translation, the request is redirected to the source language for the content.</li>
</ul><p>I've attached open-source code for the module in an archive below -- use at your own risk of course (just install the module; there is no configuration). If you're browsing the code for learning purposes, the two important bits are the <em>partial_multi.services.yml</em> file, which defines the service so that Symfony and Drupal know that this module wants to respond to the event, and <em>src/EventSubscriber/PartialMultiRequestSubscriber.php</em>, which is the class that implements the event listener.</p>
<p>Update 19 May 2017: I have also put this module up as an <a href="https://www.drupal.org/project/partial_multi">official Drupal 8 module</a>.</p></div>
<div class="field field--name-field-downloads field--type-file field--label-above">
<div class="field__label">Downloads</div>
<div class="field__items">
<div class="field__item">
<span class="file file--mime-application-x-gtar file--general"> <a href="http://poplarware.com/sites/default/files/downloads/partial_multi_0.tgz" type="application/x-gtar; length=9237">partial_multi.tgz</a></span>
</div>
</div>
</div>
Fri, 17 Feb 2017 15:56:11 +0000jhodgdon34 at http://poplarware.comDisaster Recovery Exercise: Testing Backupshttp://poplarware.com/news/recover-backups
<span class="field field--name-title field--type-string field--label-hidden">Disaster Recovery Exercise: Testing Backups</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Sun, 10/02/2016 - 12:17</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Several years ago, I set up daily automated backups for both my Linux desktop machines and of my web sites -- see my <a href="http://poplarware.com/articles/linux-backups">article on backups</a> for details. I recently made a <a href="http://poplarware.com/news/drupal-8-site">bunch of updates and changes to my web sites</a>, at the same time as I switched to a less expensive web hosting account at the same web host I've been using for years (<a href="https://www.pair.com/">pair networks</a>), so I thought it was probably a good idea to verify that the web site backups I have been making were actually working. This is known in the information technology industry as a "Disaster Recovery Exercise" -- in my case, on a small scale.</p>
<p>The first question to ask is: what do I mean by saying that the backups are "working"? Well... The reason for making backups is to be able to recover from problems. In the case of remote-hosted web sites, the types of problems I can imagine include:</p>
<ul><li>I could make a mistake that damages or destroys some pages or all of one of my sites, such as deleting some key files, mangling a content edit, or the like. Some of these errors I could probably take care of by suitable use of revision history on individual pages, but for some I would need to restore the files or data from my backups.</li>
<li>Some kind of hacking could occur.</li>
<li>The web hosting company could completely go out of business, or for some other reason I might not able to access the files and/or databases.</li>
</ul><p>If my backups are "working", that means I should be able to recover any or all of my web sites to a reasonably current state, when faced with any of these types of problems. (Your definition of "reasonably current" may differ from mine... I don't make frequent updates to my web sites, so for me, "reasonably current" means within a few days. I make backups of the web content to my local machine every evening, and the individual sites save database backups every day or so.)</p>
<p>Here's how I tested my ability to recover from disasters:</p>
<ol><li>For each web site I tested recovering, I set up the web hosting and URL resolution environment on my local machine so that if I went to the site's URL, instead of my browser going out to the Internet to find the content, it would instead send the request to my local computer's web server, which would look in a local directory for the content. (I'm using Ubuntu and Apache, so this involves editing the /etc/hosts file to tell it that this URL maps to a local IP address, making a new "site" file in /etc/apache2/sites-available that tells my local Apache server where to find the content for this URL, making a symbolic link to this site file in /etc/apache2/sites-enabled to enable it, and then restarting the Apache server to make the changes live. There are a couple of <a href="http://askubuntu.com/questions/500472/apache-server-not-resolving-by-etc-hosts-if-i-have-not-internet-connection">examples of how to do this in a Stack Exchange post</a>, and I'm sure in many other places on the Internet.)</li>
<li>At this point in each test, I visited the URL in my browser and got the standard Apache "the site is there but there is no content" message, so I was able to verify that I was pointed at the local directory and not my live, working site.</li>
<li>I made a "disaster" database and database user, on my local MySQL server, using <a href="http://www.phpmyadmin.net/">PHPMyAdmin</a>.</li>
<li>Then I recovered each site's content.</li>
</ol><h3>Recovering a WordPress Site</h3>
<p>First, I recovered the content for my one WordPress site, where my backup is a copy of the wp-content directory from that site (see the above-referenced article for details). To recover this site, I performed the following steps:</p>
<ol><li>Downloaded the current version of WordPress, and unpacked it to the local web space for my disaster recovery site.</li>
<li>Replaced the wp-content directory with my backup.</li>
<li>Found the latest database backup file inside the wp-content directory (these are made automatically by my site; again, see the article), and restored this to the "disaster" database in PHPMyAdmin. I ran into a <a href="http://stackoverflow.com/questions/29916610/1273-unknown-collation-utf8mb4-unicode-ci-cpanel">problem with database collations similar to this article</a>, due to differences between my MySQL server version and the one where my site is running, so I had to edit the database dump file in one spot to change "utf8mb4_unicode_520_ci" collation to "utf8mb4_unicode_ci".</li>
<li>Copied the wp-config-sample.php file to wp-config.php and put in the database information.</li>
<li>At this point, I could go to my site URL and see the home page, but none of the other pages worked, due to a missing .htaccess file. To restore that, I logged in and went to the Permalinks settings page, which told me what I needed to put into my .htaccess file.</li>
<li>Now I could see all the pages of my site. If I were actually rebuilding it, I would need to update some file permissions, so that new files could be uploaded and new posts written, but I didn't bother to do this for the disaster recovery exercise.</li>
</ol><h3>Recovering Drupal Sites</h3>
<p>I have two Drupal 7.x sites, and two Drupal 8.x sites. The backups in this case were the "sites" directories (or so I thought!), plus the separate private files area. To recover each of these, I used the following steps:</p>
<ol><li>Downloaded the current version of Drupal 7.x or 8.x, and unpacked it to the local web space for my disaster recovery site.</li>
<li>Replaced the sites directory with my backup.</li>
<li>Found the latest database backup inside the private files backup directory (these are made by the sites automatically; again, see article), and imported it into the "disaster" database.</li>
<li>Edited the sites/default/settings.php file from the backup to point to my local "disaster" database.</li>
<li>At this point, on the first Drupal site test, I found out that my backups for these Drupal sites only contained the sites/default directory, and not the sites/all directories. For the Drupal 8 sites, the top-level modules directories were also missing. That was a mistake! I could have recovered from this by downloading the modules that sites/all contained, but it was easier and better to go and edit my backup script so it would contain the right directories, and run the backup again.</li>
<li>Now, I could see all the pages of each site I was testing. As in the case of WordPress site, if I had needed to make a fully functional site that I could add content and file uploads to, I would have needed to work on file permissions, but I did not bother with that for this test.</li>
</ol></div>
Sun, 02 Oct 2016 19:17:58 +0000jhodgdon33 at http://poplarware.comNew Drupal 8 website!http://poplarware.com/news/drupal-8-site
<span class="field field--name-title field--type-string field--label-hidden">New Drupal 8 website!</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Wed, 09/07/2016 - 10:16</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>I have finally updated poplarware.com to Drupal 8! (There's the old joke about the shoemaker's children not having any shoes... the same applies to the web programmer having a lame, outdated website... who has time to work on their own site?)</p>
<p>I first tried using the Drupal 8 "Migrate" feature, which is in Drupal Core. This is supposed to take all of the content and settings from your old Drupal 6 site and put it into a new Drupal 8 site. But it didn't work very well -- a lot of the content did not come through correctly.</p>
<p>So, my second attempt was a more manual approach. I started by building a new site structure, and setting up the core Bartik theme with my company's colors and logo... a custom theme would probably be a bit nicer, but since I'm a web programmer and not a graphic designer, I am not too concerned about the exact look and feel of the site.</p>
<p>Then I migrated content into the new site, manually: basically copy-and-paste from pages that I wanted to preserve from my old site. I chose not to migrate everything -- there were some old outdated articles and pages that I decided I didn't need on the new site. I also combined the News, Articles, and Downloads sections of my old site into one section on the new site, since there was a lot of overlap anyway. And I moved some content into a separate personal site, at <a href="http://jhodgdon.com">http://jhodgdon.com</a></p>
<p>So... enjoy! I know I will be happy not to be using Drupal 6 any more... you may not care, but I do.</p></div>
Wed, 07 Sep 2016 17:16:38 +0000jhodgdon32 at http://poplarware.comDrupal User Guidehttp://poplarware.com/downloads/drupal-user-guide
<span class="field field--name-title field--type-string field--label-hidden">Drupal User Guide</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Thu, 09/01/2016 - 08:58</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>The User Guide for Drupal is co-maintained by Jennifer Hodgdon, and is available from <a href="https://www.drupal.org/project/user_guide">https://www.drupal.org/project/user_guide</a></p></div>
Thu, 01 Sep 2016 15:58:32 +0000jhodgdon24 at http://poplarware.comDrupal Configuration Update Manager modulehttp://poplarware.com/downloads/drupal-config-update-manager
<span class="field field--name-title field--type-string field--label-hidden">Drupal Configuration Update Manager module</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Tue, 03/01/2016 - 09:06</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>The Configuration Update Manager module for Drupal 8, co-maintained by Jennifer Hodgdon, allows you to understand and manage updates to configuration that is shipped with new versions of Drupal Core and contributed modules and themes. You can download it from <a href="https://www.drupal.org/project/config_update">https://www.drupal.org/project/config_update</a></p></div>
Tue, 01 Mar 2016 17:06:00 +0000jhodgdon26 at http://poplarware.comServices and Containers in Drupal 8http://poplarware.com/articles/containers-services
<span class="field field--name-title field--type-string field--label-hidden">Services and Containers in Drupal 8</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Mon, 10/20/2014 - 14:08</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>On October 18, 2014, I gave at talk at the <a href="http://pnwdrupalsummit.org">Pacific Northwest Drupal Summit</a> entitled "Services, Dependency Injection, and Containers, oh my!". Description:</p>
<p>Drupal 8 introduces the concepts of Services, Dependency Injection, and the Container to Drupal. They my seem a bit mysterious to programmers who are used to working with Drupal 7 or earlier versions. To help clear up the mysteries, this session will answer these questions:</p>
<ul><li>What is a service?</li>
<li>What are those $container variables all over the code?</li>
<li>What are dependencies, and what is injection?</li>
<li>What's the easiest way to interact with services, and ... since you're about to tell me how wrong it is to do it that way, how should I really be doing it in my code?</li>
<li>How can I override a service?</li>
<li>Why is this all important (why did Drupal adopt this conceptual framework in the first place)?</li>
</ul><p>Slides are attached below.</p></div>
<div class="field field--name-field-downloads field--type-file field--label-above">
<div class="field__label">Downloads</div>
<div class="field__items">
<div class="field__item">
<span class="file file--mime-application-pdf file--application-pdf"> <a href="http://poplarware.com/sites/default/files/downloads/ServicesContainer.pdf" type="application/pdf; length=298795">ServicesContainer.pdf</a></span>
</div>
</div>
</div>
Mon, 20 Oct 2014 21:08:04 +0000jhodgdon11 at http://poplarware.comThe case for a small Drupal shop contributing to Drupalhttp://poplarware.com/news/freelancer-contrib
<span class="field field--name-title field--type-string field--label-hidden">The case for a small Drupal shop contributing to Drupal</span>
<span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="http://poplarware.com/user/1" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">jhodgdon</span></span>
<span class="field field--name-created field--type-created field--label-hidden">Thu, 05/08/2014 - 10:05</span>
<div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Dries Buytaert, the CTO of Aquia and head of the open-source Drupal project, recently wrote <a href="http://buytaert.net/the-investment-case-for-employing-a-drupal-core-contributor">a blog post about the business case for hiring a Drupal core contributor</a>. Dries wrote about the measurable effect that a larger Drupal shop can realize from hiring a contributor full-time.</p>
<p>But what if you're a smaller shop, consisting of 1-5 Drupal experts? You obviously can't afford to hire someone full-time to work on Drupal Core development -- but you can still reap benefits from contributing some of your own time, or (if you have employees) paying them for a few hours a week to contribute to Drupal Core or another Drupal sub-project (module, theme, etc.) that many people use, or volunteering in another way (providing free training in your local area, organizing a Drupal meet-up, etc.). The main benefits I see:</p>
<ul><li>Your own expertise in Drupal will grow, making you a better Drupal shop.</li>
<li>Your reputation among members of the international Drupal community will grow, possibly leading to concrete benefits, such as referrals.</li>
<li>Your reputation among members of your local Drupal community will grow, almost certainly leading to greater possibility of referrals and project partnernships, which a small company always needs.</li>
<li>When a client is looking for a Drupal contractor to hire, the fact that you are a contributor to the Drupal project or the organizer of a local meeting may tip the balance towards you over another Drupal shop. (A little marketing is in order: publicize your contributions on your web site, and make sure, when talking to clients, to mention your work -- it will add to the sense of expertise that a prospective client is looking for.)</li>
</ul><p>And if your company doesn't have time to contribute volunteer hours to the Drupal project, you can also reap some benefits (good will in the community and good feelings from clients) by contributing financially (again, if you do a little marketing). I know of several great ways to do this:</p>
<ul><li>Join the <a href="https://association.drupal.org">Drupal Association</a>, the non-profit that does so much for the Drupal open-source project (such as paying for the servers that drupal.org and associated sites run on). Once you become a member, you can display a DA badge on your web site.</li>
<li>Donate regularly to Drupal developers via <a href="https://www.gittip.com/for/drupal/">Gittip</a> -- you could donate to <a href="https://www.gittip.com/DrupalCoreGittipTeam/">the Drupal Core team</a> or to one or more developers that works on key modules that your company uses. Once you start donating, you can display a Gittip badge on your web site.</li>
<li>Fund particular Drupal projects via <a href="https://www.drupalfund.us/">Drupalfund</a>, a crowdsourcing platform specifically for Drupal projects. Unlike Gittip, which is meant to be used for weekly contributions to individuals and teams, Drupalfund is for one-time contributions to particular projects (adding features to a module, porting a theme to a new Drupal version, etc.).</li>
</ul><p>So... Pick your method, and do something to help the open-source project that your livelihood depends on!</p>
</div>
Thu, 08 May 2014 17:05:25 +0000jhodgdon28 at http://poplarware.com