{"id":243932,"date":"2016-07-27T06:37:38","date_gmt":"2016-07-27T13:37:38","guid":{"rendered":"http:\/\/css-tricks.com\/?p=243932"},"modified":"2016-11-03T12:36:47","modified_gmt":"2016-11-03T19:36:47","slug":"remote-control-wordpress-scale","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/remote-control-wordpress-scale\/","title":{"rendered":"Remote Control WordPress at Scale"},"content":{"rendered":"
This is the third and final article in a series on “remote control WordPress”. That’s my nickname for this strategy of managing network settings on one “control” install, and then pulling those values into all your client installs. The advantage is that it saves staff members from having to toggle the same settings on the same network plugins, across many multisite installs.<\/p>\n
<\/p>\n
Part 1: The WP REST API for Remote Control WordPress<\/a> In the first article<\/a> I demonstrated how to expose network settings for querying via the WP API. In the second article<\/a>, I demonstrated how to query those settings, even when they require an OAuth header (which they always should). In this article, I’m going to put all of the pieces together and show you exactly how I’m using this in production.<\/p>\n If you’re familiar with the series, you know that we need two WordPress multisite installs to pull this off: <\/p>\n Just like in the last article, the control install should have the WP REST API V2 plugin<\/a> and also my CSS-Tricks WP API Control plugin<\/a> network activated. These are dissected in the earlier articles, although it’s worth re-pulling the control plugin, as I recently fixed a PHP warning.<\/p>\n Sort of new territory:<\/strong> both the control install and the client install should have my CSS-Tricks WP API Client<\/a> plugin network activated and fill-in-the-blanks’d<\/a>. That process is described for the client install in the second article. Just go ahead and copy your filled-in version of that plugin into the control now as well; there’s no need to re-do the OAuth drudgery for the control.<\/p>\n Also sort of new territory:<\/strong> Now that you’ve performed the steps in the second article, you can go ahead and uninstall the OAuth1 plugin<\/a> from the control. <\/p>\n Totally new territory:<\/strong> Both the control and the client need a feature plugin to demonstrate how to use the CSS-Tricks WP API Client<\/a> in a meaningful way. I have prepared such a feature plugin for you, it’s called CSS-Tricks Can He Loginz?<\/a>.<\/p>\n Got everything? Good. We’re about to get… abstract<\/em>!<\/p>\n Things in this article series have gotten a little complex. In the second article, we did a ton of work in order to query network settings from the control blog. We did so via a demo shortcode that simply logged the results of the API call. In real life, I run many “feature” plugins to do things like Google Analytics, admin notices, white-labeling, you name it. If we want to manage the settings for all of those feature plugins via remote control, it would be really troubling if we copy\/pasted all of that API work into each one. Therefore, we need one plugin that offers us a framework for getting network options from the control install, and all of our feature plugins can use that framework. We need an abstraction layer!<\/p>\n Wikipedia<\/a> tells us,<\/p>\n In software engineering and computer science, abstraction is a technique for managing complexity of computer systems.<\/p><\/blockquote>\n That’s exactly what I’m doing in my CSS-Tricks WP API Client plugin. Just browse the list of files and you’ll get a sense of where I’m going with this:<\/p>\n Alone, this plugin does absolutely nothing, which is perfect. Feature plugins will make use of it in order to create, review, update, or delete network settings, including settings that are managed on the control install.<\/p>\n To be perfectly clear, the CSS-Tricks WP API Client plugin would still be pretty cool even if we weren’t doing all of this remote control stuff: Part of it’s value is that it gives us a consistent framework for doing network settings pages across our codebase, which is great in its own right.<\/p>\n I’ll deep-dive into the relevant portions of the CSS-Tricks WP API Client plugin later. For now, I want to introduce our feature plugin which will use the abstraction layer.<\/p>\n Our feature plugin, CSS-Tricks Can He Loginz<\/a> does one thing, and it does it well: It adds a photograph of American singer-songwriter Kenny Loggins to the wp-login page. The Can He Loginz plugin requires<\/a> that the API Client plugin be active as well:<\/p>\n It carries two network options, one of which is managed on the control blog, the other is managed on each client install, just to demonstrate both scenarios. That’s an important point: A given plugin might have many settings, some of which are the same on all of your client installs, some of which differ. Our feature plugin has one of each.<\/p>\n The example obviously is farcical (if you don’t know who Kenny Loggins is, don’t worry. Just know that his music is pretty ironically good at this point, and that the plugin settings relate to some of his song lyrics) but the ease with which this feature plugin integrates with the CSS-Tricks WP API Client is really cool. Check out this code<\/a>, which registers our feature plugin with the abstraction layer. Done. The Can He Loginz plugin now has a settings page in network admin, with remote control functionality.<\/p>\n Let me emphasize that: Given the setup tasks we’ve accepted and performed thus far in the series, achieving remote control WordPress for a feature plugin is basically a one-liner<\/em>.<\/p>\n The settings, silly as they may be, are defined in an array here<\/a>. That’s where we could easily add, remove, or redefine settings.<\/p>\n Now, to see what the Can He Loginz plugin actually does with those settings, open a fresh browser and view the login page:<\/p>\n As promised, it’s displaying our photo and using our options in a couple of text blocks.<\/p>\n Right?<\/p>\n The crux of the matter occurs in my function here called To help illustrate this, notice a subtle difference between how the settings UI is displayed on the control vs the client:<\/p>\n In both screens, the first setting is a remote control setting, so when viewed from a client install, it’s disabled. That same setting, on the control, is enabled. Conversely, since the second setting happens to expect a differing value on each client install, it’s enabled on the client and disabled on the control. That logic occurs here<\/a>, in my class that draws the control panel UI.<\/p>\n Also of note is caching. The client caches<\/a> the result from the control, and it has the ability to break that cache<\/a> by re-saving the settings from the client UI.<\/p>\n In the comments section of the first article in the series, MF Simchock expressed concern<\/a> that, if the control install experiences downtime, then all of the client installs will be unable to call their settings. While that is true, there are a few things we can do to mitigate this. A couple of those things are outside the scope of this series:<\/p>\n What we can do from within our codebase is to never cache results that seem wonky. Check out this code<\/a>, which reviews the HTTP response from the control, and neglects to cache the result if it’s 40x or 50x.<\/p>\n In the first article, commenter “cidas” suggested<\/a> that we could more easily achieve remote control WordPress via WP-CLI. He’s not wrong. We could absolutely write a CLI script to either push or pull settings<\/a> between the clients and the control. That’s not something I’m qualified to write about, and I have no plans to pursue it in production. A few reasons:<\/p>\n The best thing that could possibly happen to this effort would be for the WP API to be fully merged into core, along with the OAuth1 plugin. Monitor those projects for updates if you embark on the remote control lifestyle.<\/p>\n Other than that, I’m not inclined to make this any fancier or complicated than it needs to be: I think it’s ready. My<\/em> next move is to continue implementing it on larger, more heavily trafficked, more important projects, and to continue to smooth out any rough edges.<\/p>\n Certainly, you could unit test the heck out of it, which I’m not digging into in this article. You could also wire in support for more field types (color picker, datetime picker, whatever). And I guess you could make it prettier. I have exactly zero CSS for the settings page, which I actually consider a good thing.<\/p>\n I had sort of expected for this third article to be the longest and most complicated in the series, but there really isn’t much to it. After all, our feature plugin is able to join this remote control lifestyle with just a small handful of code, all of which is a low-complexity affair of declaring a slug<\/a> and settings<\/a>. It feels like an extremely weird and overly complex way to get a network setting, but I think it’s far better than handling the same setting many times across many installs. I’m ready to go full-speed ahead with this paradigm, and I want to make it even better. If you try working through it, I’d love to hear what the pain points are.<\/p>\n Part 1: The WP REST API for Remote Control WordPress<\/a> This is the third and final article in a series on “remote control WordPress”. That’s my nickname for this strategy […]<\/p>\n","protected":false},"author":229300,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"_share_on_mastodon":"0","_share_on_mastodon_status":"%title% %permalink%"},"categories":[4],"tags":[800,792,264,900],"class_list":["post-243932","post","type-post","status-publish","format-standard","hentry","category-articles","tag-network-settings","tag-oauth","tag-wordpress","tag-wordpress-json-api"],"acf":{"show_toc":"No"},"share_on_mastodon":{"url":"","error":""},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":243538,"url":"https:\/\/css-tricks.com\/wp-rest-api-remote-control-wordpress\/","url_meta":{"origin":243932,"position":0},"title":"The WP REST API for Remote Control WordPress","author":"Scott Fennell","date":"July 13, 2016","format":false,"excerpt":"At my day job, we have about 1,000 sites spread across 30 WordPress multisite installs. The installs all run many of the same plugins and settings, especially at the network level. This causes a lot of wasted time for our staff: They have to manually repeat the same settings across\u2026","rel":"","context":"In "Articles"","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":243702,"url":"https:\/\/css-tricks.com\/oauth-fun-oauth1\/","url_meta":{"origin":243932,"position":1},"title":"OAuth Fun with OAuth1","author":"Scott Fennell","date":"July 21, 2016","format":false,"excerpt":"This is the second article in a three-part series about using the WP API to achieve something I'm calling \"Remote Control Wordpress\", a lifestyle where you'd manage network settings on a \"control\" install, and have other \"client\" installs pull their settings from the control. The advantage of this is that\u2026","rel":"","context":"In "Articles"","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":305335,"url":"https:\/\/css-tricks.com\/apis-and-authentication-on-the-jamstack\/","url_meta":{"origin":243932,"position":2},"title":"APIs and Authentication on the Jamstack","author":"Divya Sasidharan","date":"March 31, 2020","format":false,"excerpt":"The first \u201cA\u201d in the Jamstack stands for \u201cAPIs\u201d and is a key contributor to what makes working with static sites so powerful. APIs give developers the freedom to offload complexity and provide avenues for including dynamic functionality to an otherwise static site. Often, accessing an API requires validating the\u2026","rel":"","context":"In "Articles"","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/03\/oauth-flow-featured.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/03\/oauth-flow-featured.png?fit=1200%2C600&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/03\/oauth-flow-featured.png?fit=1200%2C600&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/03\/oauth-flow-featured.png?fit=1200%2C600&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/03\/oauth-flow-featured.png?fit=1200%2C600&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":235047,"url":"https:\/\/css-tricks.com\/getting-started-wordpress-customizer\/","url_meta":{"origin":243932,"position":3},"title":"Getting Started with the WordPress Customizer","author":"Scott Fennell","date":"December 8, 2015","format":false,"excerpt":"The following is a guest post by Scott Fennell, a WordPress theme & plugin developer in Anchorage, AK and a regular contributor around here. Let's say you have a client whose business is large enough to have several departments. Now let's say that this client wants each of their departments\u2026","rel":"","context":"In "Articles"","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":244280,"url":"https:\/\/css-tricks.com\/everything-need-know-instagram-api-integration\/","url_meta":{"origin":243932,"position":4},"title":"Everything You Need to Know About Instagram API Integration","author":"Emerson This","date":"August 8, 2016","format":false,"excerpt":"The following is a guest post by Emerson This. This is a guide for web developers interested in integrating Instagram content on websites. It was only a few months ago when Instagram changed what was possible with their API, so this serves to explain that, what is possible now, and\u2026","rel":"","context":"In "Articles"","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":375098,"url":"https:\/\/css-tricks.com\/creating-a-settings-ui-for-a-custom-wordpress-block\/","url_meta":{"origin":243932,"position":5},"title":"Creating a Settings UI for a Custom WordPress Block","author":"Manoj Kumar","date":"November 17, 2022","format":false,"excerpt":"So far, we\u2019ve covered how to work with data from an external API in a custom WordPress block. We walked through the process of fetching that data for use on the front end of a WordPress site, and how to render it directly in the WordPress Block Editor when placing\u2026","rel":"","context":"In "Articles"","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/09\/wordpress-blocks-api.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/09\/wordpress-blocks-api.jpg?fit=1200%2C600&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/09\/wordpress-blocks-api.jpg?fit=1200%2C600&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/09\/wordpress-blocks-api.jpg?fit=1200%2C600&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/09\/wordpress-blocks-api.jpg?fit=1200%2C600&ssl=1&resize=1050%2C600 3x"},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/243932","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/229300"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=243932"}],"version-history":[{"count":5,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/243932\/revisions"}],"predecessor-version":[{"id":243946,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/243932\/revisions\/243946"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=243932"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=243932"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=243932"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}
\nPart 2: OAuth Fun with OAuth1 <\/a>
\nPart 3: Remote Control WordPress at Scale<\/strong> (You are here!)\n<\/div>\nWhat You’ll Need to Follow Along<\/h3>\n
\n
The Abstraction Layer<\/h3>\n
\n
wp-admin\/network\/<\/code>, which our feature plugin will instantiate. It has a handful of nuances that are crucial to this article, which I’ll detail below.<\/li>\nThis Is It<\/h3>\n




Whoa…<\/h3>\n
merge()<\/code><\/a>, from my class that calls the control install. First, it grabs your client settings – the ones that you’d want to be able to customize on each blog. In our example, that’s the “shoes” checkbox. Next, it loops through all of the remote settings on the control install, such as the “danger zone” setting from our example, and adds them to the output as well. The result is sort of like if you’d called get_site_option()<\/code> on your client and on the control, and merged the results – in fact that’s exactly what happens behind the scenes via the WP API.<\/p>\n
What is the Eggs:Basket Ratio Here?<\/h3>\n
\n
What About WP-CLI?<\/h3>\n
\n
Next Steps<\/h3>\n
Article Series<\/h4>\n
\nPart 2: OAuth Fun with OAuth1 <\/a>
\nPart 3: Remote Control WordPress at Scale<\/strong> (You are here!)\n<\/div>\n","protected":false},"excerpt":{"rendered":"