3. INTRO
‣ WORDPRESS PLUGIN & THEME DEVELOPER FOR PIXEL JAR
‣ WORDCAMP ORANGE COUNTY ORGANIZER
‣ LEAD DEVELOPER OF THE PREMIUM PLUGIN ADSANITY
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
4. WHAT WE’RE GOING TO COVER
‣ ONE DEVELOPER'S SOLUTION TO THIS PROBLEM (MINE)
‣ AUTHENTICATION AGAINST A “MOTHERSHIP” USING XML-RPC
‣ REAL, ACTUAL CODE – YOU'VE BEEN WARNED
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
5. SO, WHAT IS A PREMIUM PLUGIN?
A PLUGIN THAT
REQUIRES YOU TO PAY
FOR ACCESS TO CODE OR
THE DEVELOPER
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
6. JUST CAUSE YOU PAY FOR SOMETHING...
PREMIUM DOES NOT
ALWAYS MEAN BETTER
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
7. BUT WHAT ABOUT THE GPL?
DON’T ALL WORDPRESS
PLUGINS HAVE TO BE
OPEN SOURCE?
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
8. BUT WHAT ABOUT THE GPL?
< INSERT PHILOSOPHICAL
DEBATE HERE >
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
9. BUT WHAT ABOUT THE GPL?
YES*
*UNLESS YOU’RE A DIRTBAG
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
10. BUT WHAT ABOUT THE GPL?
UNDER THE GPL, CODE
CAN BE REDISTRIBUTED
AND/OR MODIFIED BY
ANYONE. END OF STORY.
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
11. MORE THAN ONE WAY TO SKIN A KITTEH
‣ DONATIONWARE
‣ FREEMIUM
‣ PAY ONCE FOR CODE
‣ SUBSCRIBE TO UPDATES & SUPPORT
‣ PROBABLY OTHERS...
HTTP://WHYEVOLUTIONISTRUE.WORDPRESS.COM/2010/05/29/CATURDAY-FELIDS-COOL-CATS/SHAVED-CAT-2/
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
12. SOME ASSUMPTIONS ON MY PART
‣ ENCODING = BAD
‣ YOU RESPECT THE GPL
‣ SUPPORTING USERS IS A FULL TIME JOB
‣ YOU NEED TO MAKE A LIVING
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
13. PREMIUM PLUGINS SHOULDN’T TAKE AWAY YOUR RIGHTS
ENCODING MAKES IT
HARD FOR USERS TO
CUSTOMIZE YOUR CODE
YOU ARE USING ACTIONS & FILTERS, RIGHT?
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
14. STOPPING PIRACY IS HARD
SOPA & PIPA SUCK
HTTP://WWW.MEMBERGUIDE.GPOACCESS.GOV/112/RP/SMITHLAMAR
JUST ASK LAMAR SMITH
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
15. AUTHENTICATION IS EASY
WITH AUTHENTICATION, WE CAN:
‣ CHARGE FOR SUPPORT & ACCESS TO UPDATES
‣ CHARGE FOR ADD-ONS
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
16. STANDARD PLUGIN WORKFLOW
‣ USER DOWNLOADS THE PLUGIN FROM THE WORDPRESS REPO
‣ USER INSTALLS/ACTIVATES THE PLUGIN
‣ DEVELOPER RELEASES AN UPDATE
‣ USER IS NOTIFIED OF THE UPDATE IN THE WORDPRESS DASHBOARD
‣ USER UPDATES THE PLUGIN
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
17. THE WORKFLOW
‣ CUSTOMER PURCHASES THE PLUGIN
‣ CUSTOMER DOWNLOADS THE PLUGIN FROM DEVELOPER’S SITE
‣ CUSTOMER INSTALLS/ACTIVATES THE PLUGIN
‣ DEVELOPER RELEASES AN UPDATE
‣ CUSTOMER IS NOTIFIED OF THE UPDATE IN THE WORDPRESS DASHBOARD
‣ IF CUSTOMER IS AUTHENTICATED
‣ CUSTOMER IS ALLOWED TO UPDATE THE PLUGIN
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
18. IN PRACTICAL TERMS,
HOW DO WE DO THAT?
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
19. STEP 1: MAKE A SWEET LOOKIN’ CHART
Plugin Authentication
2A
email Mothership
XMLRPC POST
password
Submit
auth
Success! 2B Oh Noes!
API Key IXR_Error
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
20. XML-RPC
COMMUNICATION
CHANNEL TO A REMOTE
SERVER USING XML
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
21. STEP 2A: SUBMIT CREDENTIALS TO THE MOTHERSHIP
gist.github.com/f3e2a1cbd7a3a7c693f2
// check against remote server
if( !class_exists( 'IXR_Client' ) )
require_once( ABSPATH.WPINC.'/class-IXR.php' );
$client = new IXR_Client( 'http://mothership.com/xmlrpc.php' );
$client_request_args = array(
'username' => $_POST['U'],
'password' => $_POST['P'],
'plugin' => 'plugin-slug.php',
'url' => site_url()
);
if ( !$client->query( 'pue.is_user_authorized', $client_request_args ) ) :
add_action( 'admin_notices', 'oh_noes' );
else :
// store the unique api key, and enable auto-updates
endif;
// handle the error
function oh_noes() {
global $client;
echo '<div class="error"><p>' . esc_html( $client->message->faultString ) . '</p></div>';
}
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
22. XML-RPC (PLUGIN)
if( !class_exists( 'IXR_Client' ) )
require_once( ABSPATH . WPINC . '/class-IXR.php' );
$client = new IXR_Client( 'http://mothership.com/xmlrpc.php' );
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
23. XML-RPC (PLUGIN)
$client_request_args = array(
'username' => $_POST['u'],
'password' => $_POST['p'],
'plugin' => 'plugin-slug.php',
'url' => site_url()
);
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
24. XML-RPC (PLUGIN)
$client->query(
'pue.is_user_authorized',
$client_request_args
);
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
25. STEP 2B: HAVE MOTHERSHIP VALIDATE CREDENTIALS
github.com/brandondove/plugin-update-engine
/*
* Adding XMLRPC methods for the client plugin to call
/**/
add_filter( 'xmlrpc_methods', 'custom_xmlrpc_methods' );
function custom_xmlrpc_methods( $api_methods ) {
$api_methods['pue.is_user_authorized'] = 'custom_auth';
return $api_methods;
}
function custom_auth( $args = array() ) {
extract( $args ), EXTR_OVERWRITE );
if( !$username || !$password || !$plugin || !$url ) :
return new IXR_Error( 401, "OH NOES! You're doin' it wrong!" );
endif;
if( !user_pass_ok( $username, $password ) ) :
return new IXR_Error( 401, 'Sorry, Username and/or Password is Incorrect' );
endif;
require_once( PUENGINE_CORE.'models/pue-authenticated-user.php' );
$user = new pue_authenticated_user( $username );
if( !$user->is_authorized( $plugin ) )
return new IXR_Error( 401, __( "You don't have an active license for this plugin." ) );
if( !$user->has_available_licenses( $plugin, $url ) )
return new IXR_Error( 401, __( 'NOT AUTHORIZED! You have used up all of your licenses.' ) );
return md5( $username.$password.$plugin.$url.PUENGINE_SALT );
}
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
26. XML-RPC (MOTHERSHIP)
add_filter( 'xmlrpc_methods', 'custom_xmlrpc_methods' );
function custom_xmlrpc_methods( $api ) {
$api['pue.is_user_authorized'] = 'custom_auth';
return $api;
}
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
27. XML-RPC (MOTHERSHIP)
extract( $args ), EXTR_OVERWRITE );
// ensure the correct data was submitted
if( !$username || !$password || !$plugin || !$url ) :
return new IXR_Error( 401, "OH NOES! You're doin' it wrong!" );
endif;
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
28. XML-RPC (MOTHERSHIP)
// ensure the user/pass combo is real
if( !user_pass_ok( $username, $password ) ) :
return new IXR_Error( 401, 'Username/Password is Incorrect' );
endif;
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
30. XML-RPC (MOTHERSHIP)
if( !$user->is_authorized( $plugin ) )
return new IXR_Error( 401, __( "No license for this plugin." ) );
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
31. XML-RPC (MOTHERSHIP)
if( !$user->has_available_licenses( $plugin, $url ) )
return new IXR_Error( 401, __( 'No more your licenses.' ) );
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
32. XML-RPC (MOTHERSHIP)
return md5(
$username . $password .
$plugin . $url .
PUENGINE_SALT
);
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
33. THE PLUGIN IS
AUTHENTICATED.
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
34. NEXT STEPS...
REPO HOSTING
“PROFESSIONAL WORDPRESS PLUGIN DEVELOPMENT”
PAGES 263-269
WRITTEN BY THE AWESOME
BRAD WILLIAMS, OZH RICHARD & JUSTIN TADLOCK
HTTP://BDOVE.ME/PWPPD
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP
35. TWITTER WEB
BRANDONDOVE@PIXELJAR.NET
EMAIL
@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL 2012 · SAN DIEGO · WORDCAMP