Friday, June 26, 2015

Conclusion

As this was written as a blog over the last several days, it will only make sense if the posts are read from the bottom up. But, you are encouraged to read the conclusion here before proceeding (to see if it is worth the read).

I started this blog with the following sentence:
I, as others that I know, have been struggling to figure out a good solution for handling user-managed content embedded in a MEAN (MongoDB, Express, AngularJS, and Node.js) stack application.
What I did not elaborate on was criteria for a good solution:
  • Community Support:  Expected to be maintained over time.
  • Widespread Use: Has name-brand recognition in its category.
  • Functional: Provides sufficient functionality for user-managed content.
  • Ease-of- Use (User): Easy for users to use.
  • Ease-of-Use (Developer): Ease for me to use.
  • Ease-of-Use (Operations): Easy for operations to support.
  • Scaleability: Can be scaled.
  • Cost: Low cost.
Let me preface the conclusion below with:
  • I am not advocating WordPress as a development platform; just as the CMS component. 
  • I strongly prefer the MEAN stack as a development platform.
  • I do not prefer the PHP language (but stop short as I know that others say the same thing about JavaScript).
  • Like one of the founding fathers (David Clark) said, I believe in: "We reject kings, presidents and voting. We believe in rough consensus and running code."  That is why I have this longish blog with actual running code demonstrated below.

Why I Am Leaning Towards WordPress as a Solution for a CMS Component

A part of me cringes at the thought of what I am about to advocate; i.e., a month ago I was pretty firmly against this conclusion.
  • Community Support: WordPress is an open source project with hundreds of people working on it. It has been existence since 2003 (12 years).*
  • Widespread Use: 22% of new U.S. registered domains run on WordPress.**
  • Functional: WordPress has become synonymous with the term CMS; essentially defining the software category.
  • Ease-of-Use (User): There is a reason why 22% of new U.S. registered domains run on WordPress.
  • Ease-of-Use (Developer): With the right guidance (will not name the guilty), one can quickly cut through the tangle of themes and plug-ins and create highly customized user-content driven sites. I will use me as proof (3 days ago, I had not touched PHP or WordPress and now I feel confident that I could build such a site quickly).
  • Ease-of-Use (Operations): WordPress platform as a service (PaaS) has become a commodity item with lots of options available, e.g., https://www.siteground.com/ or https://pagely.com.

    note: Because, I am advocating only using WordPress as a CMS component, this solution has a limited number of plug-in in use and the theme is bare bones.  Plug-in and theme (and core upgrades) represent a significant problem.
  •  Scaleability: This is an area that I am less comfortable but with some big companies using it, e.g., Best Buy***, and companies like Pagely that specialize in performance I feel more comfortable.
  • Cost: While one can host this software oneself (it is open source), Pagely offers a range of solutions that start off at only $64 / month and can be scaled up as necessary.
Love to hear your feedback as I am very interested in alternative solutions.

Implementing the Authorization Handling in Node.js

The code implements two API end points. Implementation can be found at https://github.com/larkintuckerllc/meanwp.

GET /login

This simply redirects the browser to correct WordPress authorization endpoint.

GET /authorize?code=XXXX

This is the WordPress callback that
  1. Accepts the authorization code
  2. Contacts WordPress to trade the authorization code for an access token
  3. Redirect the browser back to /?token=XXXX

Thursday, June 25, 2015

MEAN Stack as Solution for "Hard Stuff"

We are going to change gears here and let WordPress do all of the management (and display) of user-managed content and then use the MEAN stack for the "hard stuff", i.e., custom functionality that normally would be executed in WordPress through a jumble of WordPress plug-ins.

The difficult piece of making this work is to a single-sign-on (SSO) experience between the WordPress and the MEAN stack application.  The second piece will be to leverage the WP REST API plugin to get at data that is stored in WP, e.g., the logged in user to start of with.

SSO with WordPress as Authentication Source

WordPress (with the WP OAuth Server plugin) uses OAuth2 to remotely authenticate users against the WordPress authentication system.  For more information on OAuth2, Google has an excellent explanation as it relates to Google APIs (in particular the documentation on using it for web server applications): https://developers.google.com/identity/protocols/OAuth2

Install Plugin

To get started, install the WP OAuth Server plugin: https://wp-oauth.com/

Setup Client (Essentially is the MEAN Stack Application)

Then next step is to setup a client in WP OAuth Server, e.g.,:

  • Client Name: demo
  • Redirect URI: http://localhost:3000/authorize (need to implement this end-point in the Node.js server code).
WP OAuth Server then will provide two values:
  • Client ID (this is what AngularJS will use to initiate the request)
  • Secret (this is what the Node.js application will use to exchange the authorization code for an access token).
Manually Test

Going to use a combination of Web Browser and a tool to handle more advanced HTTP messages (e.g., Chrome extension called Postman) to manually test the OAuth2 service.  We are essentially manually walking through the process documented at: https://wp-oauth.com/knowledge-base/using-authorization-code/

While there will differences between setups, e.g., client_id will be different, the following is the general flow:

1. Using Browser: Open the following 

http://local.wordpress.dev/oauth/authorize?response_type=code&client_id=tY168glCRAKR0qX1CrHMcR6H2Z0JeY&redirect_uri=http://localhost:3000/authorize

note: The redirect_uri provided needs to match what was setup in the client setup step.

Assuming that one is not already logged into WordPress, Wordpress will prompt with the login window.

2. Once logged in, WordPress will redirect the browser to the redirect_uri provided with the authorization code. Normally, the browser goes to this URL and the Node.js server would handle the rest of the steps (providing the access token to the browser).

At this point, with no implementation in Node.js the browser will error with a connection refused; but we can extract the authorization code from the URL.

3. Using Postman, post the following:

  • URL: http://local.wordpress.dev/oauth/token
  • Header: Authorization: Basic XXXXX (this is an encoded version of username - Client ID and password - Secret).
  • form-data: grant_type: authorization_code
  • form-data: code: XXXXX (this is the authorization code obtained in step 2).
  • form-data: redirect_uri: http://localhost:3000/authorize (this is the same redirect_uri used all along).
If all works out, WordPress will respond with:

{"access_token":"7c4d59622fe3c4196356d6758d2c27491c2c14ac","expires_in":86400,"token_type":"Bearer","scope":"basic","refresh_token":"222ef3fc78d5eb1809f237038a0ff860b9417245"}

4. Access a Protected API Endpoint Using Access Token

Using Postman, get the following:

  • URL: http://local.wordpress.dev/users/me
  • Header: Authorization: Bearer XXXXX (this is the access token gotten in the previous step)

If all works out, WordPress will respond with:

{"ID":1,"username":"admin","name":"admin","first_name":"","last_name":"","nickname":"admin","slug":"admin","URL":"","avatar":"http:\/\/0.gravatar.com\/avatar\/06e92fdf4a9a63441dff65945114b47f?s=96","description":"","registered":"2015-06-22T20:13:15+00:00","roles":["administrator"],"capabilities":{"switch_themes":true,"edit_themes":true,"activate_plugins":true,"edit_plugins":true,"edit_users":true,"edit_files":true,"manage_options":true,"moderate_comments":true,"manage_categories":true,"manage_links":true,"upload_files":true,"import":true,"unfiltered_html":true,"edit_posts":true,"edit_others_posts":true,"edit_published_posts":true,"publish_posts":true,"edit_pages":true,"read":true,"level_10":true,"level_9":true,"level_8":true,"level_7":true,"level_6":true,"level_5":true,"level_4":true,"level_3":true,"level_2":true,"level_1":true,"level_0":true,"edit_others_pages":true,"edit_published_pages":true,"publish_pages":true,"delete_pages":true,"delete_others_pages":true,"delete_published_pages":true,"delete_posts":true,"delete_others_posts":true,"delete_published_posts":true,"delete_private_posts":true,"edit_private_posts":true,"read_private_posts":true,"delete_private_pages":true,"edit_private_pages":true,"read_private_pages":true,"delete_users":true,"create_users":true,"unfiltered_upload":true,"edit_dashboard":true,"update_plugins":true,"delete_plugins":true,"install_plugins":true,"update_themes":true,"install_themes":true,"update_core":true,"list_users":true,"remove_users":true,"add_users":true,"promote_users":true,"edit_theme_options":true,"delete_themes":true,"export":true,"administrator":true},"email":false,"meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","archives":"http:\/\/local.wordpress.dev\/wp-json\/users\/1\/posts"},"0":false}}

At this point, one can continue to make WordPress protected API endpoints as necessary.

Structuring the User-Managed Content

One of the issues with using the post's content is that it is free-form, i.e., the user can format the content any number of ways. In some cases this is acceptable and in others it can break the screen's design.

Leveraging Advanced Custom Fields

We are going to leverage a popular plugin called Advanced Custom Fields (ACF): http://www.advancedcustomfields.com/.  This example uses version 4.4.2. This plugin will allow us to change tactics and use a single WordPress post (as opposed to posts grouped by category) to store one or more user-editable content elements for an AngularJS screen.

With ACF in place, we create a "home" post and create a new ACF field group called home.  In it, we create two WYSIWSY Editor fields called "top" and "bottom".  We apply the field group to the home post. To remove unused clutter from the page's administration screen, we hide all the other controls.

Now when editing the home post, one will only see the two fields "top" and "bottom".

Now we install yet another plug-in called WP REST API Custom Fields: https://wordpress.org/plugins/wp-rest-api-custom-fields/. This example uses version 0.2.

With this plug-in in place, a request to http://local.wordpress.dev/wp-json/posts/68 (post ID is 68) results in:

{"ID":68,"title":"home","status":"publish","type":"post","author":{"ID":1,"username":"admin","name":"admin","first_name":"","last_name":"","nickname":"admin","slug":"admin","URL":"","avatar":"http:\/\/0.gravatar.com\/avatar\/06e92fdf4a9a63441dff65945114b47f?s=96","description":"","registered":"2015-06-22T20:13:15+00:00","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","archives":"http:\/\/local.wordpress.dev\/wp-json\/users\/1\/posts"},"0":false}},"content":"","parent":null,"link":"http:\/\/local.wordpress.dev\/2015\/06\/25\/home\/","date":"2015-06-25T11:44:46","modified":"2015-06-25T11:46:51","format":"standard","slug":"home","guid":"http:\/\/local.wordpress.dev\/?p=68","excerpt":null,"menu_order":0,"comment_status":"closed","ping_status":"closed","sticky":false,"date_tz":"UTC","date_gmt":"2015-06-25T11:44:46","modified_tz":"UTC","modified_gmt":"2015-06-25T11:46:51","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/posts\/68","author":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","collection":"http:\/\/local.wordpress.dev\/wp-json\/posts","replies":"http:\/\/local.wordpress.dev\/wp-json\/posts\/68\/comments","version-history":"http:\/\/local.wordpress.dev\/wp-json\/posts\/68\/revisions"},"top":"TOP<\/p>\n","bottom":"BOTTOM<\/p>\n"},"featured_image":null,"terms":{"category":[{"ID":1,"name":"Uncategorized","slug":"uncategorized","description":"","taxonomy":"category","parent":null,"count":3,"link":"http:\/\/local.wordpress.dev\/category\/uncategorized\/","meta":{"links":{"collection":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms","self":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms\/1"},"0":false}}]}}


If one looks carefully, the "top" and "bottom" fields are injected under the "meta" object.

note: Looks like out of the box, WP REST API Custom Fields breaks the WP REST API for posts that do not have custom fields associated with it.

With this strategy, we can build our AngularJS app as in the example: http://jsfiddle.net/sckmkny/qp72hyf1/.

note: Because this example was developed to run against my local WordPress installation it will not work outside of it.

The Big Win with Advanced Custom Fields

As we indicated at the top of this post, the goal was to get away from free-form (or the WYSIWYG) entry by the user.  Here, we will change it so that we only deliver an image URL and a HTML encoded string through the API.

We create a post called "home structured".  We then create a ACF field group called "home structured" in a similar fashion as we did above, except the fields are called "image" and "text" and they are typed as such.

note: Because we are trying to limit the user, we set the text formatting option to "No formatting"; this HTML encodes the content.

{"ID":73,"title":"home structured","status":"publish","type":"post","author":{"ID":1,"username":"admin","name":"admin","first_name":"","last_name":"","nickname":"admin","slug":"admin","URL":"","avatar":"http:\/\/0.gravatar.com\/avatar\/06e92fdf4a9a63441dff65945114b47f?s=96","description":"","registered":"2015-06-22T20:13:15+00:00","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","archives":"http:\/\/local.wordpress.dev\/wp-json\/users\/1\/posts"},"0":false}},"content":"","parent":null,"link":"http:\/\/local.wordpress.dev\/2015\/06\/25\/home-structured\/","date":"2015-06-25T12:23:35","modified":"2015-06-25T12:27:30","format":"standard","slug":"home-structured","guid":"http:\/\/local.wordpress.dev\/?p=73","excerpt":null,"menu_order":0,"comment_status":"closed","ping_status":"closed","sticky":false,"date_tz":"UTC","date_gmt":"2015-06-25T12:23:35","modified_tz":"UTC","modified_gmt":"2015-06-25T12:27:30","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/posts\/73","author":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","collection":"http:\/\/local.wordpress.dev\/wp-json\/posts","replies":"http:\/\/local.wordpress.dev\/wp-json\/posts\/73\/comments","version-history":"http:\/\/local.wordpress.dev\/wp-json\/posts\/73\/revisions"},"image":"http:\/\/local.wordpress.dev\/wp-content\/uploads\/2015\/06\/frog.png","text":"test with html <b>"},"featured_image":null,"terms":{"category":[{"ID":1,"name":"Uncategorized","slug":"uncategorized","description":"","taxonomy":"category","parent":null,"count":4,"link":"http:\/\/local.wordpress.dev\/category\/uncategorized\/","meta":{"links":{"collection":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms","self":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms\/1"},"0":false}}]}}

With this strategy, we can build our AngularJS app as in the example: http://jsfiddle.net/sckmkny/mndq7wrj/7/.

Since we are not rendering HTML directly to the screen, we can get rid of ngSanitize.

note: Standard disclaimer... because this example was developed to run against my local WordPress installation it will not work outside of it.

Wednesday, June 24, 2015

WordPress as Source of User-Managed Content

In this example, we will start with a MEAN stack application and then pull in user-managed content into a screen, e.g., an "About Us" screen.  For simplicity, we only really need a client app (AngularJS and none of MongoDB, Express, or NodeJS). To make this completely transparent, I will write the client app in JSFIDDLE.

note: I will assume a basic familiarity of WordPress, i.e., pretty much what one would pick up through an introductory tutorial.

Install and Test the WP REST API Plugin

First, install the WP REST API plugin: http://wp-api.org/. This example was written with version 1.2.2.

By the way, one of the guidelines of this overall effort is to minimize the number of plugins and customization of the WordPress environment.

At this point, the unauthenticated API endpoints are ready to go, e.g., browsing to:

http://local.wordpress.dev/wp-json/posts

results in an empty array.

To make it more interesting, we will put in two posts with titles and content and reloading results in something more substantial:

[{"ID":44,"title":"The Title of Post 2","status":"publish","type":"post","author":{"ID":1,"username":"admin","name":"admin","first_name":"","last_name":"","nickname":"admin","slug":"admin","URL":"","avatar":"http:\/\/0.gravatar.com\/avatar\/06e92fdf4a9a63441dff65945114b47f?s=96","description":"","registered":"2015-06-22T20:13:15+00:00","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","archives":"http:\/\/local.wordpress.dev\/wp-json\/users\/1\/posts"}}},"content":"The content\u00a0of post 2<\/p>\n","parent":null,"link":"http:\/\/local.wordpress.dev\/2015\/06\/24\/the-title-of-post-2\/","date":"2015-06-24T21:09:07","modified":"2015-06-24T21:21:11","format":"standard","slug":"the-title-of-post-2","guid":"http:\/\/local.wordpress.dev\/?p=44","excerpt":"

The content\u00a0of post 2<\/p>\n","menu_order":0,"comment_status":"closed","ping_status":"closed","sticky":false,"date_tz":"UTC","date_gmt":"2015-06-24T21:09:07","modified_tz":"UTC","modified_gmt":"2015-06-24T21:21:11","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/posts\/44","author":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","collection":"http:\/\/local.wordpress.dev\/wp-json\/posts","replies":"http:\/\/local.wordpress.dev\/wp-json\/posts\/44\/comments","version-history":"http:\/\/local.wordpress.dev\/wp-json\/posts\/44\/revisions"}},"featured_image":null,"terms":{"category":[{"ID":1,"name":"Uncategorized","slug":"uncategorized","description":"","taxonomy":"category","parent":null,"count":2,"link":"http:\/\/local.wordpress.dev\/category\/uncategorized\/","meta":{"links":{"collection":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms","self":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms\/1"}}}]}},{"ID":42,"title":"The Title of Post 1","status":"publish","type":"post","author":{"ID":1,"username":"admin","name":"admin","first_name":"","last_name":"","nickname":"admin","slug":"admin","URL":"","avatar":"http:\/\/0.gravatar.com\/avatar\/06e92fdf4a9a63441dff65945114b47f?s=96","description":"","registered":"2015-06-22T20:13:15+00:00","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","archives":"http:\/\/local.wordpress.dev\/wp-json\/users\/1\/posts"}}},"content":"

The content\u00a0of post 1.<\/p>\n","parent":null,"link":"http:\/\/local.wordpress.dev\/2015\/06\/24\/the-title-of-post-1\/","date":"2015-06-24T21:08:52","modified":"2015-06-24T21:08:52","format":"standard","slug":"the-title-of-post-1","guid":"http:\/\/local.wordpress.dev\/?p=42","excerpt":"

The content\u00a0of post 1.<\/p>\n","menu_order":0,"comment_status":"closed","ping_status":"closed","sticky":false,"date_tz":"UTC","date_gmt":"2015-06-24T21:08:52","modified_tz":"UTC","modified_gmt":"2015-06-24T21:08:52","meta":{"links":{"self":"http:\/\/local.wordpress.dev\/wp-json\/posts\/42","author":"http:\/\/local.wordpress.dev\/wp-json\/users\/1","collection":"http:\/\/local.wordpress.dev\/wp-json\/posts","replies":"http:\/\/local.wordpress.dev\/wp-json\/posts\/42\/comments","version-history":"http:\/\/local.wordpress.dev\/wp-json\/posts\/42\/revisions"}},"featured_image":null,"terms":{"category":[{"ID":1,"name":"Uncategorized","slug":"uncategorized","description":"","taxonomy":"category","parent":null,"count":2,"link":"http:\/\/local.wordpress.dev\/category\/uncategorized\/","meta":{"links":{"collection":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms","self":"http:\/\/local.wordpress.dev\/wp-json\/taxonomies\/category\/terms\/1"}}}]}}]
albeit a little difficult to read.

Organizing Posts for a Screen

If the screen requires a single post, we can simply get the single post, e.g.,

http://local.wordpress.dev/wp-json/posts/44

If the screen requires multiple posts, we can organize the posts with categories and get them by category, e.g.,

http://local.wordpress.dev/wp-json/posts?filter[category_name]=home&filter[orderby]=ID&filter[order]=ASC

With this strategy, we can build our AngularJS app as in the example: http://jsfiddle.net/sckmkny/hsafmsbe/.

note: Because this example was developed to run against my local WordPress installation it will not work outside of it.

Setting the Stage

First, I will acknowledge the irony that this blog is written on Blogger instead of on a WordPress site; am old-school and have been using Blogger and really have no need to change.

Motivation

I, as others that I know, have been struggling to figure out a good solution for handling user-managed content embedded in a MEAN (MongoDB, Express, AngularJS, and Node.js) stack application.

One solution that I have considered was writing my own MEAN stack CMS (Content Management System). However, every time I think to do this I feel like I am re-inventing the wheel.

note: By the way, I do have a limited implementation of such a MEAN stack CMS called NarrowCMS https://github.com/larkintuckerllc/narrow-cms-example.

Why WordPress? Simply because it is popular: https://managewp.com/14-surprising-statistics-about-wordpress-usage.

Why not just use WordPress for application development?  First, it is WordPress and second it is built on PHP.  Enough said.

General Overview

First, I have developed this blog based on WordPress 4.4.2 running in a local development environment provided by Varying-Vagrant-Vagrants: https://github.com/Varying-Vagrant-Vagrants/VVV.  Also, Variable VVV to quickly spin up new environments: https://github.com/bradp/vv.

Second, as a disclaimer, I am neither very proficient in WordPress nor PHP but have enough general software development knowledge to get things working.  I, however, am pretty handy at MEAN stack development.

The first approach that I will explore is simply using WordPress as a source of user-managed content and pull in content into a MEAN stack application.

The second approach is to use WordPress to serve up the user-managed content and then hand the user off to the MEAN stack application for the "hard stuff". In this case, will want to be able to transparently hand off the WordPress authenticated user.

Not sure if I will get to another scenario in this blog, but it would be interesting to explore using WordPress and the MEAN Stack application as above with the authentication happening on the MEAN stack application.