SlideShare a Scribd company logo
WORDPRESS REST API AND
ANGULARJS
FTW!
Roy Sivan and Josh Pollock
CalderaLearn.com
CalderaLabs.org
Join our Slack
LoopConf Slack: #workshop-wpapi
We will be posting links, taking
questions, and communicating
throughout the day via Slack
CalderaLabs.org
Hi I'm Josh
Lead Developer: Caldera Labs
I make WordPress plugins
I teach about WordPress
I wrote a book about the WordPress REST API
I am a core contributor to WordPress
I am a member of The WP Crowd
CalderaLearn.com
Hi, I'm Roy
Senior Software Engineer:
The Walt Disney Company
I am a member of The WP Crowd
I blog on roysivan.com
I teach on Lynda.com & CalderaLearn
People say Hi to me a lot
What We're Covering Today
REST API 101
Building Custom REST APIs
Unit Testing Custom REST APIs
LUNCH BREAK
AngularJS (1.x) Basics
Building Decoupled Front-ends
Building Plugin Admin Screens
Educational Philosophy
All code is or is based on real world projects
We will show different ways of doing the same
thing.
Please ask why it's different
◇ we may give you a good answer
Stop us and ask questions
Structure For Today
foreach ( $sections as $section ) :
Concepts / Lecture
Example Code Walkthrough (you will be cloning locally)
Hands-on
DIY Group
Walk Through Code Group
endforeach;
WARNING
This workshop is to help you understand the basics
and some advanced technologies. Nothing will be
production ready code.
What you need for today
IDE or text editor (PHPStorm, Sublime, etc.)
Local WP install (VVV, DesktopServer, etc.)
npm
PHPUnit (optional, included in VVV)
Composer (optional)
AngularJS Batarang Chrome Extension
https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehid
hipcmcojjgiloacoafjmpfk?hl=en
CalderaLabs.org
0.
WordPress REST API?
Do we even need it?
CalderaLabs.org
NO
But you can build cool stuff with it.
It’s all about the Data
Data!
Data!
Data!
“
CalderaLearn.com
The API allows you to take WP data and put it in a
bucket. What you do with that bucket is up to you.
-- Morten Rand Hendriksen
@mor10
CalderaLabs.org
What can we
build with it?
Facebooks? Myspaces?
API Powered Stuff
Things you can build that are powered by the API
Phone apps
Custom UI widgets on your site
Why use feed of another site, when you can API
Custom Dashboards in the admin
Custom WP Dashboard (YAS!)
User role based dashboard
CalderaLabs.org
1.
Custom REST APIs
With The WordPress REST API
CalderaLabs.org
Modify Default Routes
CalderaLabs.org
WordPress Gives Us
Routes
Which We Can Extend
CalderaLabs.org
Create Your Own Routes
CalderaLabs.org
Or We Can Make Our Own
With The Same Tools
How The REST API Works
WP_REST_Server ??WP_REST_Request WP_REST_Response
How The REST API Works
WP_REST_Server
Route
Callback
WP_REST_Request WP_REST_Response
ERROR!
ERROR!
CalderaLabs.org
Let's Talk About The
Orange Box
The Route Callback
Customizing The
Defaults
CalderaLabs.org
Extending Defaults
Adding Post Type
Support
add_action( 'init', 'my_book_cpt' );
function my_book_cpt() {
$labels = array(...);
$args = array(
...
'rest_controller_class' => 'WP_REST_Posts_Controller',
'show_in_rest' => true,
'rest_base' => 'books-api',
);
register_post_type( 'book', $args );
}
Add REST API Support To Post Type Registration
add_action( 'init', 'my_custom_post_type_rest_support', 25 );
function my_custom_post_type_rest_support() {
global $wp_post_types;
$post_type_name = 'book';
if( isset( $wp_post_types[ $post_type_name ] ) ) {
$wp_post_types[$post_type_name]->show_in_rest = true;
$wp_post_types[$post_type_name]->rest_base = $post_type_name;
$wp_post_types[$post_type_name]->rest_controller_class =
'WP_REST_Posts_Controller';
}
}
Add REST API Support To An Existing Post Type
CalderaLabs.org
Extending Defaults
Adding A Custom Field
CalderaLabs.org
Custom Field Is Any Data
Not just meta fields
function slug_get_meta_field( $object, $field_name, $request ) {
return get_post_meta( $object[ 'id' ], $field_name );
}
function slug_update_meta( $value, $object, $field_name ) {
if ( ! $value || ! is_string( $value ) ) {
return;
}
return update_post_meta( $object->ID, $field_name, strip_tags(
$value ) );
}
Adding Custom Fields To A Response: Callbacks
add_action( 'rest_api_init', 'slug_register_spaceship' );
function slug_register_spaceship() {
register_api_field( 'post',
'starship',
array(
'get_callback' => 'slug_get_meta_field',
'update_callback' => 'slug_update_meta_field',
)
);
}
Adding Custom Fields To A Response: Registration
CalderaLabs.org
Making Custom REST
APIs
The Basics
How It Works
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/author/(?P<id>d+)', array(
'methods' => 'GET',
'callback' => 'route_callback',
) );
} );
Registering A Route
CalderaLabs.org
Creating Callback Class
Register Route(s)
class my_simple_route {
public function register_routes(){
$namespace = 'my-api/v1';
register_rest_route( $namespace, '/items', [
'methods' => 'GET',
'callback' => [ $this, 'get_items' ],
'permissions_callback' => [ $this, 'get_items_permissions' ]
] );
...
}
}
Registering Route(s)
class my_simple_route {
public function register_routes(){
...
register_rest_route( $namespace, '/items/(?P<id>d+)', [
'methods' => 'GET',
'callback' => [ $this, 'get_item' ],
'permissions_callback' => [ $this,
'get_item_permissions' ]
] );
}
}
Registering Route(s)
CalderaLabs.org
Creating Callback Class
Defining Route Fields
register_rest_route( $this->namespace, '/items/(?P<id>d+)', [
...
'args' => [
'type' => [
'required' => true,
'validate_callback' => [ $this, 'validate_type' ]
],
'number' => [
'default' => 5,
'sanitize_callback' => 'absint'
]
]
] );
Registering Route(s) : Defining Fields
Registering Route(s) : Field Sanitization Callback
Use to ensure data is safe.
Defined using a callable.
Change data to a safe value.
Return prepared value
Registering Route(s) : Field Validation Callback
Use to ensure data is correct.
Defined using a callable.
Used to reject invalid requests
Return true or false
Registering Route(s) : Field Validation Callback
public function validate_type( $value ){
if( !in_array( $value, [ 'big', 'small',
'very-small' ] ) ){
return false;
}
return true;
}
CalderaLabs.org
Creating Callback Class
Permissions Callback
Registering Route(s) : Permissions Callback
Determine if user is authorized.
Return true or false
Registering Route(s) : Permissions Callback
Example: Make require login.
public function get_items_permissions(){
if( is_user_logged_in() ){
return true;
}
return false;
}
Registering Route(s) : Permissions Callback
Example: Limit To Admins
public function get_items_permissions(){
if( current_user_can( 'manage_options' ) ){
return true;
}
return false;
}
Registering Route(s) : Permissions Callback
Example: Allow Always
public function get_items_permissions(){
return true;
}
CalderaLabs.org
Creating Callback Class
Route Callback Function
Registering Route(s) : Callback
Do something with the request
Gets an object of WP_REST_Request
Should return WP_REST_Response or WP_Error
WP_Rest_Response
Represents current request
Contains:
◇ Parameters
◇ Headers
No need to access $_GET, $_POST, $_REQUEST
Implements Arrayaccess
$request->param( 'field_name' );
$request[ 'field_name' ];
Registering Route(s) : Callback
public function get_item( WP_REST_Request $request ){
$id = $request[ 'id' ];
$item = slug_crud_get( $id );
if( ! empty( $item ) && ! is_wp_error( $item ) ){
return rest_ensure_response( $item );
}elseif ( is_wp_error( $item ) ){
return $item;
}else{
$response = new WP_REST_Response( 'No items found', 404 );
return $response;
}
}
CalderaLabs.org
Initializing The Class
The rest_api_init action
CalderaLabs.org
rest_api_init
Use To Load Code Only
Needed In REST API Requests
Registering Route(s) : Initializing
add_action( 'rest_api_init', function(){
$route = new my_simple_route();
$route->register_routes();
});
CalderaLabs.org
2.
Unit Testing Custom
REST APIs
Subtitle
CalderaLearn.com
Goal
Only Test Our Endpoints
CalderaLabs.org
PHPUnit +
WP_UnitTestCase
CalderaLabs.org
Setting Up
Prepare To Test
Unit Testing 101
Make Sure Things Return What They Should Return
$this->assertEquals( 42, function_that_returns_42() );
$this->assertSame( '42', function_that_returns_42() );
$this->assertArrayHasKey( 'roy', array( 'roy' => 'hi' ) );
Google: "Pippin Williamson Unit Tests for WordPress Plugins"
Install PHPUnit
wget https://phar.phpunit.de/phpunit.phar
chmod +x phpunit.phar
mv phpunit.phar /usr/local/bin/phpunit
wget https://phar.phpunit.de/phpunit.phar
php phpunit.phar
Install WP CLI
curl -O
https://raw.githubusercontent.com/wp-cli/builds/gh-pages/
phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
Initialize Unit Tests
wp scaffold plugin-tests
CalderaLabs.org
Writing Tests
Testing!
CalderaLearn.com
Mock
Requests
An Example
public function test_get_items_author_query() {
$this->factory->post->create( array( 'post_author' => 4 ) );
$this->factory->post->create( array( 'post_author' => 4 ) );
$this->factory->post->create( array( 'post_author' => 2 );
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 3, count( $response->get_data() ) );
}
An Example
public function test_get_items_author_query() {
$this->factory->post->create( array( 'post_author' => 4 ) );
$this->factory->post->create( array( 'post_author' => 4 ) );
$this->factory->post->create( array( 'post_author' => 2 );
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
$request->set_param( 'author', 4 );
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 2, count( $response->get_data() ) );
}
CalderaLearn.com
Test Case
Unit Test Case For REST API Endpoints
Create a reusable class that:
Create Instance of WP_REST_Server
Boot routes
Make sure route is booted
Test Case Outline
class Test_API extends WP_UnitTestCase {
/** @var WP_REST_Server*/
protected $server;
protected $namespaced_route = 'caldera-forms/v1';
public function setUp() {}
public function test_register_route() {}
public function test_endpoints() {}
}
Test Case: Set Up
protected $server;
public function setUp() {
parent::setUp();
/** @var WP_REST_Server $wp_rest_server */
global $wp_rest_server;
$this->server = $wp_rest_server = new WP_REST_Server;
do_action( 'rest_api_init' );
}
Test Case: Test Route Is Registered
public function test_register_route() {
$routes = $this->server->get_routes();
$this->assertArrayHasKey( $this->namespaced_route,
$routes );
}
Test Case: Test Endpoints Exist
public function test_endpoints() {
$the_route = $this->namespaced_route;
$routes = $this->server->get_routes();
foreach( $routes as $route => $route_config ) {
if( 0 === strpos( $the_route, $route ) ) {
$this->assertTrue( is_array( $route_config ) );
foreach( $route_config as $i => $endpoint ) {
$this->assertArrayHasKey( 'callback', $endpoint );
$this->assertArrayHasKey( 0, $endpoint[ 'callback' ], get_class( $this ) );
$this->assertArrayHasKey( 1, $endpoint[ 'callback' ], get_class( $this ) );
$this->assertTrue( is_callable( array( $endpoint[ 'callback' ][0], $endpoint[ 'callbac
][1] ) ) );
}
}
}
CalderaLearn.com
Testing
Custom Routes
Test Your API Only!!!
Test internal logic elsewhere
Test control of that logic
Test response format
Trust core
Example Route Test
class Test_Hi extends Test_API {
protected $namespace = '/hi-api/v1/names';
public function test_list() {
$request = new WP_REST_Request( 'GET', $this->namespace );
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$data = $response->get_data();
$this->assertArrayHasKey( 'name', $data[0] );
$this->assertEquals( 'shawn', $data[0][ 'name' ] );
$this->assertArrayHasKey( 'name', $data[1] );
$this->assertEquals( 'roy', $data[1][ 'name' ] );
}
}
Example Route Test
class Test_Hi extends Test_API {
protected $namespace = '/hi-api/v1/names';
public function test_single(){
$request = new WP_REST_Request( 'GET', $this->namespace . '/roy' );
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$data = $response->get_data();
$this->assertArrayHasKey( 'name', $data );
$this->assertEquals( 'roy', $data[ 'name' ] );
}
}
CalderaLabs.org
3.
Getting Started With
AngularJS 1.x
Controllers, Templates, Services & Factories
CalderaLabs.org
3 con’t
Why Angular?
You know it’s in the title of the workshop, right?
“
CalderaLearn.com
AngularJS is the best JavaScript Framework.
-- Roy Sivan
Senior WordPress Engineer at Disney (so you know he is legit)
Not WordPress
To get you through the basics of
AngularJS we will be using
sample data, not WP
Our First Project
No npm or gulp
We aren’t using any build tools,
this is a pure sample
https://github.com/caldera-learn/angularjs-intro
Quicker & Easier
Quicker to get going to show
overall concepts.
Honest Truth
We already have a few projects
built in it that are ready to go
Why 1.x? 2 is in RC!
Roy is lazy
I am not lazy, but didn’t have
time to learn it deeply enough
yet to give a full workshop on it.
Josh Switched Teams
He used to be team NG1.
Now he is team VueJS.
HTML powered JavaScript
With Angular 1.x you can use
HTML to do most things reserved
for PHP.
Get the data using JS, template
with HTML. No PHP needed.
CalderaLearn.com
Step 1
Setup the APP
Setting up the app
All functionality lives within 1 app
We use ng-app to encapsulate the app in the DOM, it can be used on
any element including HTML.
Using it on the HTML encapsulates the whole DOM
Sample Data JSON
data.json
This file is going to be a sample of data, that we are going to use to build out
a simple Angular App, we will then replace it with the WordPress REST API
Injectables
Injectables are objects which can be injected and used in
controllers, directives, etc.
We will be creating our own later...
AngularJS Controllers
Controller is all about $scope
A Controller is defined by a JavaScript constructor function that is used to
augment the AngularJS Scope. When a Controller is attached to the DOM via
the ng-controller directive, AngularJS will instantiate a new Controller
object, using the specified Controller's constructor function
All new data to be used must be stored within $scope.your_key
ng-controller
All Together - JS
wpNG = {};
wpNG.app = ( function( app ){
console.log( 'initializing..' );
// define our app
app = angular.module('wpAngularApp', [])
.controller( 'listView', ['$scope', '$http', function( $scope, $http ) {
…
}]);
return app;
}(wpNG.app || {});
All Together - HTML
<div ng-app="wpAngularApp" id="app-container">
<div ng-controller="listView">
<!-- List View -->
</div>
</div>
CalderaLearn.com
Step 2
HTML! Looping through the data
First we must get data
$http is a jQuery AJAX wrapper & returns a promise
$http({method: ‘’, url: ‘’})
$http.get(url)
$http.post(url)
Because it returns a promise we use .then()
Say hello to JSON
JSON is the JavaScript Object Notation.
We display JSON with curly brackets
We work with JSON similar to PHP arrays
{{Object.key}} displays that key value
In PHP that would be $object[‘key’];
The loop HTML
<div ng-controller="listView">
<h2>Posts</h2>
<!-- Loop through $scope.posts -->
<article ng-repeat="post in posts">
<h2>{{post.title}}</h2>
</article>
</div>
Add more data
<article ng-repeat="post in posts">
<img ng-src="{{post.image}}" />
<h2>{{post.title}}</h2>
<div
class="post-content"
ng-bind-html="post.content | to_trusted">
</div>
</article>
CalderaLabs.org
4.
Decoupled Front-ends
With AngularJS
A site has no WordPress
Decoupled Front End
Decouple front end is a front end app that doesn’t live within
WordPress at all.
Your website: myawesomesite.com
Decoupled: myawesomeapp.com - but running on the same
data!
How cool is that!
Decoupled Use Cases
Phone App
Piece of functionality that communicates with WP data
Advanced, Unique UI
Combine with other APIs
Different Stack (decoupled app doesn’t need to be PHP)
Our decoupled App
npm
gulp
REST url that is publicly accessible (we will give you one)
Tacos, you always need tacos.
What you will need:
https://github.com/caldera-learn/decoupled-app
CalderaLabs.org
What About CORS?
Cross-Origin Resource Sharing
CORS
Allows browsers to make requests across
domains.
IS NOT SECURITY!!
By default WordPress REST API is same-origin
only.
Set at rest_pre_serve_request hook
Allow All Domains For All Methods
add_action( 'rest_api_init', function() {
remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
add_filter( 'rest_pre_serve_request', function( $value ) {
header( 'Access-Control-Allow-Origin: *' );
header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT,
DELETE' );
header( 'Access-Control-Allow-Credentials: true' );
return $value;
});
}, 15 );
Only Allow GET Requests
add_action( 'rest_api_init', function() {
remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
add_filter( 'rest_pre_serve_request', function( $value ) {
$origin = get_http_origin();
if ( $origin ) {
header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
}
header( 'Access-Control-Allow-Origin: ' . esc_url_raw( site_url() ) );
header( 'Access-Control-Allow-Methods: GET' );
return $value;
});
}, 15 );
Allows From Certain Origins
add_action( 'rest_api_init', function() {
remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
add_filter( 'rest_pre_serve_request', function( $value ) {
$origin = get_http_origin();
if ( $origin && in_array( $origin, array(
'https://hiroy.club'
) ) ) {
header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' );
header( 'Access-Control-Allow-Credentials: true' );
}
return $value;
});
}, 15 );
CalderaLabs.org
Factories, Storage,
UI-Router, and
Templates
All the things!
AngularJS Factories
Factories allow you to create injectable objects.
Inject $resource into the factory it to create an object that can automatically handle REST calls.
app.factory('Posts',function($resource){
return $resource( ngWP.config.api + ‘/:post_type/:ID?per_page=:per_page’, {
post_type: 'posts',
ID:'@id',
per_page: ngWP.config.posts_per_page,
});
})
AngularJS Factories cont’d
Posts.get()
Posts.query() - like get, but expects array
Posts.save()
Posts.delete()
Posts.remove()
Unlike $http these do not return promises
Local Storage
https://github.com/grevory/angular-local-storage
An AngularJS module that gives you access to the browser’s local storage with
cookie fallback.
Local storage is a key/value store which we call on. In our example the regular posts (blog) will check for
local storage first, before hitting the API.
UI-Router
https://github.com/angular-ui/ui-router
The Angular-UI project adds modules and functionality to Angular (think WP plugins for WP)
State Driven Routing
& a lot of other functionality we won’t need.
Shows its true power when you have 1 view within another (post.detail within post.list)
Templates
Separating out templates into templates directory
Cleaner code
Separation of views
1 controller can have 1 template
1 template can be powered by multiple controllers
Pure HTML (with JSON)
CalderaLabs.org
Now Entering
The Danger Zone
First thing is first
Clone the repo
You will need to run npm install and gulp
The config file
Create a file called config.js in /assets/js
var ngWP = ngWP || {};
ngWP.config = {
api: 'https://calderaforms.com/wp-json/',
posts_per_page: 5
// Not needed for our menu: 'app'
};
Stepping through the code
Main APP file
UI Router Definitions
Small controllers live here
Templates
Blog/Author List
Blog Detail
Product List
CalderaLabs.org
5.
Plugin Admin Screens
With AngularJS
The UI Router
Making Mini-Apps To Make WordPress Better
Very Similar To
Regular NG App
Basic
Use add_admin_menu() to print basic HTML
Use ui-router to switch routes inside admin
page.
Use wp_localize_script() for config
Questions?
Use PHP or HTML files for templates?
How to handle translations?
wp_localize_script()
PHP templates
Let's Look At
Some Code!
What’s Next?
Angular 2 Theme currently in development
github.com/royboy789/Angular-Wordpress-Theme/tree/v7
Vue.JS - The new simple JS framework
ReactJS - What everyone else wants you to learn
Admin Theme Boilerplate
github.com/WordPress-Admin-JavaScript-Boilerplate/ReactJS-Boilerplate
Want more Roy & Josh teachings?
Caldera Learn teaches through 4-week live classroom
style webinars. Teachers are Josh and/or Roy with future
guest teachers
CalderaLabs.org
Josh Pollock
JoshPress.net
CalderaForms.com
CalderaLearn.com
CalderaLabs.org
@Josh412
CalderaLabs.org
Roy Sivan
theWPCrowd.com
RoySivan.com
Lynda.com
CaldraLearn.com
@royboy789
CalderaLabs.org
Want To Learn More?
Coming Very Soon
CalderaLearn.com

More Related Content

PDF
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
PDF
Connecting Content Silos: One CMS, Many Sites With The WordPress REST API
PDF
Introduction to AngularJS For WordPress Developers
PDF
Introduction to plugin development
PDF
Using the new WordPress REST API
PDF
Extending the WordPress REST API - Josh Pollock
PPTX
Webinar: AngularJS and the WordPress REST API
ODP
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Single Page Web Apps As WordPress Admin Interfaces Using AngularJS & The Word...
Connecting Content Silos: One CMS, Many Sites With The WordPress REST API
Introduction to AngularJS For WordPress Developers
Introduction to plugin development
Using the new WordPress REST API
Extending the WordPress REST API - Josh Pollock
Webinar: AngularJS and the WordPress REST API
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani

What's hot (20)

PDF
AngularJS with Slim PHP Micro Framework
PDF
Bullet: The Functional PHP Micro-Framework
PDF
Keeping it Small: Getting to know the Slim Micro Framework
PDF
Bootstrat REST APIs with Laravel 5
KEY
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
PPT
Slim RedBeanPHP and Knockout
PPTX
Zend framework
PDF
Developing apps using Perl
PDF
Django Rest Framework and React and Redux, Oh My!
PDF
Getting Started-with-Laravel
PDF
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
PDF
[Bristol WordPress] Supercharging WordPress Development
PPTX
REST APIs in Laravel 101
PPT
Zend Framework 1.8 Features Webinar
PDF
Silex Cheat Sheet
PDF
REST in AngularJS
PDF
Keeping the frontend under control with Symfony and Webpack
PDF
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
PDF
Rails 3 overview
PPTX
Working with WP_Query in WordPress
AngularJS with Slim PHP Micro Framework
Bullet: The Functional PHP Micro-Framework
Keeping it Small: Getting to know the Slim Micro Framework
Bootstrat REST APIs with Laravel 5
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Slim RedBeanPHP and Knockout
Zend framework
Developing apps using Perl
Django Rest Framework and React and Redux, Oh My!
Getting Started-with-Laravel
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
[Bristol WordPress] Supercharging WordPress Development
REST APIs in Laravel 101
Zend Framework 1.8 Features Webinar
Silex Cheat Sheet
REST in AngularJS
Keeping the frontend under control with Symfony and Webpack
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
Rails 3 overview
Working with WP_Query in WordPress
Ad

Similar to Caldera Learn - LoopConf WP API + Angular FTW Workshop (20)

PDF
Old WP REST API, New Tricks
PDF
Don't screw it up! How to build durable API
PDF
Test Coverage for Your WP REST API Project
PDF
RESTful applications: The why and how by Maikel Mardjan
PDF
2019 WordCamp Orange County - An Introduction to the WordPress REST API
PPTX
The WordPress REST API as a Springboard for Website Greatness
PDF
REST in pieces
PDF
[drupalday2017] - REST in pieces
PDF
WordPress RESTful API & Amazon API Gateway (English version)
PPTX
Super simple introduction to REST-APIs (2nd version)
KEY
Using the SugarCRM REST API
PPTX
Building Software Backend (Web API)
PPTX
Rest WebAPI with OData
PPTX
A Deep Dive into RESTful API Design Part 2
PDF
the-10-rest-commandments.pdf
PPTX
RESTful modules in zf2
PPTX
Real world RESTful service development problems and solutions
PDF
JSON REST API for WordPress
PDF
Creating Restful Web Services with restish
PDF
Алексей Веркеенко "Symfony2 & REST API"
Old WP REST API, New Tricks
Don't screw it up! How to build durable API
Test Coverage for Your WP REST API Project
RESTful applications: The why and how by Maikel Mardjan
2019 WordCamp Orange County - An Introduction to the WordPress REST API
The WordPress REST API as a Springboard for Website Greatness
REST in pieces
[drupalday2017] - REST in pieces
WordPress RESTful API & Amazon API Gateway (English version)
Super simple introduction to REST-APIs (2nd version)
Using the SugarCRM REST API
Building Software Backend (Web API)
Rest WebAPI with OData
A Deep Dive into RESTful API Design Part 2
the-10-rest-commandments.pdf
RESTful modules in zf2
Real world RESTful service development problems and solutions
JSON REST API for WordPress
Creating Restful Web Services with restish
Алексей Веркеенко "Symfony2 & REST API"
Ad

Recently uploaded (20)

PPTX
A Presentation on Artificial Intelligence
PDF
cuic standard and advanced reporting.pdf
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
KodekX | Application Modernization Development
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
A Presentation on Artificial Intelligence
cuic standard and advanced reporting.pdf
The AUB Centre for AI in Media Proposal.docx
The Rise and Fall of 3GPP – Time for a Sabbatical?
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
NewMind AI Weekly Chronicles - August'25 Week I
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
“AI and Expert System Decision Support & Business Intelligence Systems”
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Digital-Transformation-Roadmap-for-Companies.pptx
Advanced methodologies resolving dimensionality complications for autism neur...
Mobile App Security Testing_ A Comprehensive Guide.pdf
Unlocking AI with Model Context Protocol (MCP)
KodekX | Application Modernization Development
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Dropbox Q2 2025 Financial Results & Investor Presentation
Building Integrated photovoltaic BIPV_UPV.pdf
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows

Caldera Learn - LoopConf WP API + Angular FTW Workshop

  • 1. WORDPRESS REST API AND ANGULARJS FTW! Roy Sivan and Josh Pollock CalderaLearn.com
  • 2. CalderaLabs.org Join our Slack LoopConf Slack: #workshop-wpapi We will be posting links, taking questions, and communicating throughout the day via Slack
  • 3. CalderaLabs.org Hi I'm Josh Lead Developer: Caldera Labs I make WordPress plugins I teach about WordPress I wrote a book about the WordPress REST API I am a core contributor to WordPress I am a member of The WP Crowd
  • 4. CalderaLearn.com Hi, I'm Roy Senior Software Engineer: The Walt Disney Company I am a member of The WP Crowd I blog on roysivan.com I teach on Lynda.com & CalderaLearn People say Hi to me a lot
  • 5. What We're Covering Today REST API 101 Building Custom REST APIs Unit Testing Custom REST APIs LUNCH BREAK AngularJS (1.x) Basics Building Decoupled Front-ends Building Plugin Admin Screens
  • 6. Educational Philosophy All code is or is based on real world projects We will show different ways of doing the same thing. Please ask why it's different ◇ we may give you a good answer Stop us and ask questions
  • 7. Structure For Today foreach ( $sections as $section ) : Concepts / Lecture Example Code Walkthrough (you will be cloning locally) Hands-on DIY Group Walk Through Code Group endforeach;
  • 8. WARNING This workshop is to help you understand the basics and some advanced technologies. Nothing will be production ready code.
  • 9. What you need for today IDE or text editor (PHPStorm, Sublime, etc.) Local WP install (VVV, DesktopServer, etc.) npm PHPUnit (optional, included in VVV) Composer (optional) AngularJS Batarang Chrome Extension https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehid hipcmcojjgiloacoafjmpfk?hl=en
  • 11. CalderaLabs.org NO But you can build cool stuff with it.
  • 12. It’s all about the Data Data! Data! Data!
  • 13. “ CalderaLearn.com The API allows you to take WP data and put it in a bucket. What you do with that bucket is up to you. -- Morten Rand Hendriksen @mor10
  • 14. CalderaLabs.org What can we build with it? Facebooks? Myspaces?
  • 15. API Powered Stuff Things you can build that are powered by the API Phone apps Custom UI widgets on your site Why use feed of another site, when you can API Custom Dashboards in the admin Custom WP Dashboard (YAS!) User role based dashboard
  • 20. CalderaLabs.org Or We Can Make Our Own With The Same Tools
  • 21. How The REST API Works WP_REST_Server ??WP_REST_Request WP_REST_Response
  • 22. How The REST API Works WP_REST_Server Route Callback WP_REST_Request WP_REST_Response ERROR! ERROR!
  • 23. CalderaLabs.org Let's Talk About The Orange Box The Route Callback
  • 26. add_action( 'init', 'my_book_cpt' ); function my_book_cpt() { $labels = array(...); $args = array( ... 'rest_controller_class' => 'WP_REST_Posts_Controller', 'show_in_rest' => true, 'rest_base' => 'books-api', ); register_post_type( 'book', $args ); } Add REST API Support To Post Type Registration
  • 27. add_action( 'init', 'my_custom_post_type_rest_support', 25 ); function my_custom_post_type_rest_support() { global $wp_post_types; $post_type_name = 'book'; if( isset( $wp_post_types[ $post_type_name ] ) ) { $wp_post_types[$post_type_name]->show_in_rest = true; $wp_post_types[$post_type_name]->rest_base = $post_type_name; $wp_post_types[$post_type_name]->rest_controller_class = 'WP_REST_Posts_Controller'; } } Add REST API Support To An Existing Post Type
  • 29. CalderaLabs.org Custom Field Is Any Data Not just meta fields
  • 30. function slug_get_meta_field( $object, $field_name, $request ) { return get_post_meta( $object[ 'id' ], $field_name ); } function slug_update_meta( $value, $object, $field_name ) { if ( ! $value || ! is_string( $value ) ) { return; } return update_post_meta( $object->ID, $field_name, strip_tags( $value ) ); } Adding Custom Fields To A Response: Callbacks
  • 31. add_action( 'rest_api_init', 'slug_register_spaceship' ); function slug_register_spaceship() { register_api_field( 'post', 'starship', array( 'get_callback' => 'slug_get_meta_field', 'update_callback' => 'slug_update_meta_field', ) ); } Adding Custom Fields To A Response: Registration
  • 33. add_action( 'rest_api_init', function () { register_rest_route( 'myplugin/v1', '/author/(?P<id>d+)', array( 'methods' => 'GET', 'callback' => 'route_callback', ) ); } ); Registering A Route
  • 35. class my_simple_route { public function register_routes(){ $namespace = 'my-api/v1'; register_rest_route( $namespace, '/items', [ 'methods' => 'GET', 'callback' => [ $this, 'get_items' ], 'permissions_callback' => [ $this, 'get_items_permissions' ] ] ); ... } } Registering Route(s)
  • 36. class my_simple_route { public function register_routes(){ ... register_rest_route( $namespace, '/items/(?P<id>d+)', [ 'methods' => 'GET', 'callback' => [ $this, 'get_item' ], 'permissions_callback' => [ $this, 'get_item_permissions' ] ] ); } } Registering Route(s)
  • 38. register_rest_route( $this->namespace, '/items/(?P<id>d+)', [ ... 'args' => [ 'type' => [ 'required' => true, 'validate_callback' => [ $this, 'validate_type' ] ], 'number' => [ 'default' => 5, 'sanitize_callback' => 'absint' ] ] ] ); Registering Route(s) : Defining Fields
  • 39. Registering Route(s) : Field Sanitization Callback Use to ensure data is safe. Defined using a callable. Change data to a safe value. Return prepared value
  • 40. Registering Route(s) : Field Validation Callback Use to ensure data is correct. Defined using a callable. Used to reject invalid requests Return true or false
  • 41. Registering Route(s) : Field Validation Callback public function validate_type( $value ){ if( !in_array( $value, [ 'big', 'small', 'very-small' ] ) ){ return false; } return true; }
  • 43. Registering Route(s) : Permissions Callback Determine if user is authorized. Return true or false
  • 44. Registering Route(s) : Permissions Callback Example: Make require login. public function get_items_permissions(){ if( is_user_logged_in() ){ return true; } return false; }
  • 45. Registering Route(s) : Permissions Callback Example: Limit To Admins public function get_items_permissions(){ if( current_user_can( 'manage_options' ) ){ return true; } return false; }
  • 46. Registering Route(s) : Permissions Callback Example: Allow Always public function get_items_permissions(){ return true; }
  • 48. Registering Route(s) : Callback Do something with the request Gets an object of WP_REST_Request Should return WP_REST_Response or WP_Error
  • 49. WP_Rest_Response Represents current request Contains: ◇ Parameters ◇ Headers No need to access $_GET, $_POST, $_REQUEST Implements Arrayaccess $request->param( 'field_name' ); $request[ 'field_name' ];
  • 50. Registering Route(s) : Callback public function get_item( WP_REST_Request $request ){ $id = $request[ 'id' ]; $item = slug_crud_get( $id ); if( ! empty( $item ) && ! is_wp_error( $item ) ){ return rest_ensure_response( $item ); }elseif ( is_wp_error( $item ) ){ return $item; }else{ $response = new WP_REST_Response( 'No items found', 404 ); return $response; } }
  • 52. CalderaLabs.org rest_api_init Use To Load Code Only Needed In REST API Requests
  • 53. Registering Route(s) : Initializing add_action( 'rest_api_init', function(){ $route = new my_simple_route(); $route->register_routes(); });
  • 58. Unit Testing 101 Make Sure Things Return What They Should Return $this->assertEquals( 42, function_that_returns_42() ); $this->assertSame( '42', function_that_returns_42() ); $this->assertArrayHasKey( 'roy', array( 'roy' => 'hi' ) ); Google: "Pippin Williamson Unit Tests for WordPress Plugins"
  • 59. Install PHPUnit wget https://phar.phpunit.de/phpunit.phar chmod +x phpunit.phar mv phpunit.phar /usr/local/bin/phpunit wget https://phar.phpunit.de/phpunit.phar php phpunit.phar
  • 60. Install WP CLI curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/ phar/wp-cli.phar php wp-cli.phar --info chmod +x wp-cli.phar sudo mv wp-cli.phar /usr/local/bin/wp
  • 61. Initialize Unit Tests wp scaffold plugin-tests
  • 64. An Example public function test_get_items_author_query() { $this->factory->post->create( array( 'post_author' => 4 ) ); $this->factory->post->create( array( 'post_author' => 4 ) ); $this->factory->post->create( array( 'post_author' => 2 ); $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( 3, count( $response->get_data() ) ); }
  • 65. An Example public function test_get_items_author_query() { $this->factory->post->create( array( 'post_author' => 4 ) ); $this->factory->post->create( array( 'post_author' => 4 ) ); $this->factory->post->create( array( 'post_author' => 2 ); $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $request->set_param( 'author', 4 ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( 2, count( $response->get_data() ) ); }
  • 67. Unit Test Case For REST API Endpoints Create a reusable class that: Create Instance of WP_REST_Server Boot routes Make sure route is booted
  • 68. Test Case Outline class Test_API extends WP_UnitTestCase { /** @var WP_REST_Server*/ protected $server; protected $namespaced_route = 'caldera-forms/v1'; public function setUp() {} public function test_register_route() {} public function test_endpoints() {} }
  • 69. Test Case: Set Up protected $server; public function setUp() { parent::setUp(); /** @var WP_REST_Server $wp_rest_server */ global $wp_rest_server; $this->server = $wp_rest_server = new WP_REST_Server; do_action( 'rest_api_init' ); }
  • 70. Test Case: Test Route Is Registered public function test_register_route() { $routes = $this->server->get_routes(); $this->assertArrayHasKey( $this->namespaced_route, $routes ); }
  • 71. Test Case: Test Endpoints Exist public function test_endpoints() { $the_route = $this->namespaced_route; $routes = $this->server->get_routes(); foreach( $routes as $route => $route_config ) { if( 0 === strpos( $the_route, $route ) ) { $this->assertTrue( is_array( $route_config ) ); foreach( $route_config as $i => $endpoint ) { $this->assertArrayHasKey( 'callback', $endpoint ); $this->assertArrayHasKey( 0, $endpoint[ 'callback' ], get_class( $this ) ); $this->assertArrayHasKey( 1, $endpoint[ 'callback' ], get_class( $this ) ); $this->assertTrue( is_callable( array( $endpoint[ 'callback' ][0], $endpoint[ 'callbac ][1] ) ) ); } } }
  • 73. Test Your API Only!!! Test internal logic elsewhere Test control of that logic Test response format Trust core
  • 74. Example Route Test class Test_Hi extends Test_API { protected $namespace = '/hi-api/v1/names'; public function test_list() { $request = new WP_REST_Request( 'GET', $this->namespace ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $data = $response->get_data(); $this->assertArrayHasKey( 'name', $data[0] ); $this->assertEquals( 'shawn', $data[0][ 'name' ] ); $this->assertArrayHasKey( 'name', $data[1] ); $this->assertEquals( 'roy', $data[1][ 'name' ] ); } }
  • 75. Example Route Test class Test_Hi extends Test_API { protected $namespace = '/hi-api/v1/names'; public function test_single(){ $request = new WP_REST_Request( 'GET', $this->namespace . '/roy' ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $data = $response->get_data(); $this->assertArrayHasKey( 'name', $data ); $this->assertEquals( 'roy', $data[ 'name' ] ); } }
  • 76. CalderaLabs.org 3. Getting Started With AngularJS 1.x Controllers, Templates, Services & Factories
  • 77. CalderaLabs.org 3 con’t Why Angular? You know it’s in the title of the workshop, right?
  • 78. “ CalderaLearn.com AngularJS is the best JavaScript Framework. -- Roy Sivan Senior WordPress Engineer at Disney (so you know he is legit)
  • 79. Not WordPress To get you through the basics of AngularJS we will be using sample data, not WP Our First Project No npm or gulp We aren’t using any build tools, this is a pure sample https://github.com/caldera-learn/angularjs-intro
  • 80. Quicker & Easier Quicker to get going to show overall concepts. Honest Truth We already have a few projects built in it that are ready to go Why 1.x? 2 is in RC! Roy is lazy I am not lazy, but didn’t have time to learn it deeply enough yet to give a full workshop on it. Josh Switched Teams He used to be team NG1. Now he is team VueJS.
  • 81. HTML powered JavaScript With Angular 1.x you can use HTML to do most things reserved for PHP. Get the data using JS, template with HTML. No PHP needed.
  • 83. Setting up the app All functionality lives within 1 app We use ng-app to encapsulate the app in the DOM, it can be used on any element including HTML. Using it on the HTML encapsulates the whole DOM
  • 84. Sample Data JSON data.json This file is going to be a sample of data, that we are going to use to build out a simple Angular App, we will then replace it with the WordPress REST API
  • 85. Injectables Injectables are objects which can be injected and used in controllers, directives, etc. We will be creating our own later...
  • 86. AngularJS Controllers Controller is all about $scope A Controller is defined by a JavaScript constructor function that is used to augment the AngularJS Scope. When a Controller is attached to the DOM via the ng-controller directive, AngularJS will instantiate a new Controller object, using the specified Controller's constructor function All new data to be used must be stored within $scope.your_key ng-controller
  • 87. All Together - JS wpNG = {}; wpNG.app = ( function( app ){ console.log( 'initializing..' ); // define our app app = angular.module('wpAngularApp', []) .controller( 'listView', ['$scope', '$http', function( $scope, $http ) { … }]); return app; }(wpNG.app || {});
  • 88. All Together - HTML <div ng-app="wpAngularApp" id="app-container"> <div ng-controller="listView"> <!-- List View --> </div> </div>
  • 90. First we must get data $http is a jQuery AJAX wrapper & returns a promise $http({method: ‘’, url: ‘’}) $http.get(url) $http.post(url) Because it returns a promise we use .then()
  • 91. Say hello to JSON JSON is the JavaScript Object Notation. We display JSON with curly brackets We work with JSON similar to PHP arrays {{Object.key}} displays that key value In PHP that would be $object[‘key’];
  • 92. The loop HTML <div ng-controller="listView"> <h2>Posts</h2> <!-- Loop through $scope.posts --> <article ng-repeat="post in posts"> <h2>{{post.title}}</h2> </article> </div>
  • 93. Add more data <article ng-repeat="post in posts"> <img ng-src="{{post.image}}" /> <h2>{{post.title}}</h2> <div class="post-content" ng-bind-html="post.content | to_trusted"> </div> </article>
  • 95. Decoupled Front End Decouple front end is a front end app that doesn’t live within WordPress at all. Your website: myawesomesite.com Decoupled: myawesomeapp.com - but running on the same data! How cool is that!
  • 96. Decoupled Use Cases Phone App Piece of functionality that communicates with WP data Advanced, Unique UI Combine with other APIs Different Stack (decoupled app doesn’t need to be PHP)
  • 97. Our decoupled App npm gulp REST url that is publicly accessible (we will give you one) Tacos, you always need tacos. What you will need: https://github.com/caldera-learn/decoupled-app
  • 99. CORS Allows browsers to make requests across domains. IS NOT SECURITY!! By default WordPress REST API is same-origin only. Set at rest_pre_serve_request hook
  • 100. Allow All Domains For All Methods add_action( 'rest_api_init', function() { remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' ); add_filter( 'rest_pre_serve_request', function( $value ) { header( 'Access-Control-Allow-Origin: *' ); header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' ); header( 'Access-Control-Allow-Credentials: true' ); return $value; }); }, 15 );
  • 101. Only Allow GET Requests add_action( 'rest_api_init', function() { remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' ); add_filter( 'rest_pre_serve_request', function( $value ) { $origin = get_http_origin(); if ( $origin ) { header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) ); } header( 'Access-Control-Allow-Origin: ' . esc_url_raw( site_url() ) ); header( 'Access-Control-Allow-Methods: GET' ); return $value; }); }, 15 );
  • 102. Allows From Certain Origins add_action( 'rest_api_init', function() { remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' ); add_filter( 'rest_pre_serve_request', function( $value ) { $origin = get_http_origin(); if ( $origin && in_array( $origin, array( 'https://hiroy.club' ) ) ) { header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) ); header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' ); header( 'Access-Control-Allow-Credentials: true' ); } return $value; }); }, 15 );
  • 104. AngularJS Factories Factories allow you to create injectable objects. Inject $resource into the factory it to create an object that can automatically handle REST calls. app.factory('Posts',function($resource){ return $resource( ngWP.config.api + ‘/:post_type/:ID?per_page=:per_page’, { post_type: 'posts', ID:'@id', per_page: ngWP.config.posts_per_page, }); })
  • 105. AngularJS Factories cont’d Posts.get() Posts.query() - like get, but expects array Posts.save() Posts.delete() Posts.remove() Unlike $http these do not return promises
  • 106. Local Storage https://github.com/grevory/angular-local-storage An AngularJS module that gives you access to the browser’s local storage with cookie fallback. Local storage is a key/value store which we call on. In our example the regular posts (blog) will check for local storage first, before hitting the API.
  • 107. UI-Router https://github.com/angular-ui/ui-router The Angular-UI project adds modules and functionality to Angular (think WP plugins for WP) State Driven Routing & a lot of other functionality we won’t need. Shows its true power when you have 1 view within another (post.detail within post.list)
  • 108. Templates Separating out templates into templates directory Cleaner code Separation of views 1 controller can have 1 template 1 template can be powered by multiple controllers Pure HTML (with JSON)
  • 110. First thing is first Clone the repo You will need to run npm install and gulp
  • 111. The config file Create a file called config.js in /assets/js var ngWP = ngWP || {}; ngWP.config = { api: 'https://calderaforms.com/wp-json/', posts_per_page: 5 // Not needed for our menu: 'app' };
  • 112. Stepping through the code Main APP file UI Router Definitions Small controllers live here Templates Blog/Author List Blog Detail Product List
  • 113. CalderaLabs.org 5. Plugin Admin Screens With AngularJS The UI Router Making Mini-Apps To Make WordPress Better
  • 115. Basic Use add_admin_menu() to print basic HTML Use ui-router to switch routes inside admin page. Use wp_localize_script() for config
  • 116. Questions? Use PHP or HTML files for templates? How to handle translations? wp_localize_script() PHP templates
  • 118. What’s Next? Angular 2 Theme currently in development github.com/royboy789/Angular-Wordpress-Theme/tree/v7 Vue.JS - The new simple JS framework ReactJS - What everyone else wants you to learn Admin Theme Boilerplate github.com/WordPress-Admin-JavaScript-Boilerplate/ReactJS-Boilerplate
  • 119. Want more Roy & Josh teachings? Caldera Learn teaches through 4-week live classroom style webinars. Teachers are Josh and/or Roy with future guest teachers
  • 122. CalderaLabs.org Want To Learn More? Coming Very Soon CalderaLearn.com