Zackio
5/26/2015 - 10:19 AM

Envato PHP library by @sowailem

Envato PHP library by @sowailem

<?php

/**
 * @file
 * Integration layer to communicate with the Envato API.
 * 
 * @see https://build.envato.com/api
 *
 * @outher Abdullah Sowailem.
 */


/**
 * The hole story
 *-----------------------------------------------------------------------------
 * +--------+                                           +---------------+
 * |        |--(A)------- Authorization Grant --------->|               |
 * |        |                                           |               |
 * |        |<-(B)----------- Access Token -------------|               |
 * |        |               & Refresh Token             |               |
 * |        |                                           |               |
 * |        |                            +----------+   |               |
 * |        |--(C)---- Access Token ---->|          |   |               |
 * |        |                            |          |   |               |
 * |        |<-(D)- Protected Resource --| Resource |   | Authorization |
 * | Client |                            |  Server  |   |     Server    |
 * |        |--(E)---- Access Token ---->|          |   |               |
 * |        |                            |          |   |               |
 * |        |<-(F)- Invalid Token Error -|          |   |               |
 * |        |                            +----------+   |               |
 * |        |                                           |               |
 * |        |--(G)----------- Refresh Token ----------->|               |
 * |        |                                           |               |
 * |        |<-(H)----------- Access Token -------------|               |
 * +--------+           & Optional Refresh Token        +---------------+
 * 
 *                 ---------------------------------
 * The flow illustrated in Figure includes the following steps:
 *
 * (A)  The client requests an access token by authenticating with the
 *       authorization server, and presenting an authorization grant.
 *  (B)  The authorization server authenticates the client and validates
 *       the authorization grant, and if valid issues an access token and
 *       a refresh token.
 *  (C)  The client makes a protected resource request to the resource
 *       server by presenting the access token.
 *  (D)  The resource server validates the access token, and if valid,
 *       serves the request.
 *  (E)  Steps (C) and (D) repeat until the access token expires.  If the
 *       client knows the access token expired, it skips to step (G),
 *       otherwise it makes another protected resource request.
 *  (F)  Since the access token is invalid, the resource server returns
 *       an invalid token error.
 *  (G)  The client requests a new access token by authenticating with
 *       the authorization server and presenting the refresh token.  The
 *       client authentication requirements are based on the client type
 *       and on the authorization server policies.
 *  (H)  The authorization server authenticates the client and validates
 *       the refresh token, and if valid issues a new access token (and
 *       optionally, a new refresh token).
 * ----------------------------------------------------------------------------
 *
 * -------------------------------------
 * 1) Request the authorization code
 *
 * https://api.envato.com/authorization?response_type=code&client_id=[CLIENT_ID]&redirect_uri=[REDIRECT_URI]
 *
 * --------------------------------------
 * 2) Approve the authorization access
 *
 * After the user approval, the URL will change to http://[REDIRECT_URI]?code=[CODE]
 * And now we have the [CODE] .
 *
 * --------------------------------------
 * 3) Request the Bearer token
 *
 * <POSTRequest>
 * https://api.envato.com/token
 *    grant_type=authorization_code&
 *    code=[CODE]&
 *    redirect_uri=[REDIRECT_URI]&
 *    client_id=[CLIENT_ID]&
 *    client_secret=[CLIENT_SECRET]
 * </POSTRequest>
 * 
 * The server will respond with an access token as JSON format:
 * 
 * <Respond>
 * {
 *	  "refresh_token": "GBdxWsxo1CqAK9yCneH75wgkXw1q7bio",
 *	  "token_type": "bearer",
 *	  "access_token": "c0lQ2WLYW9qAZ9RH12cH1fJPzVWSscXP",
 *	  "expires_in": 3600
 * }
 * </Respond>
 *  
 * --------------------------------------
 * 4) Access the API using the Bearer token
 *
 * <POSTRequest>
 * https://api.envato.com/v1/market/private/user/account.json
 * "Authorization: Bearer c0lQ2WLYW9qAZ9RH12cH1fJPzVWSscXP"
 * </POSTRequest>
 * 
 * The respond of this request is:
 * 
 * <Respond>
 * {
 *	  "account": {
 *		"image": "https://0.s3.envato.com/files/100000009/0006893824_192.jpg",
 *		"firstname": "Test",
 *		"surname": "User",
 *		"available_earnings": "0.00",
 *		"total_deposits": "0.00",
 *		"balance": "0.00",
 *		"country": "Australia"
 *	  }
 *	}
 * </Respond>
 * 
 * 
 */

/**
 * Exception handling class.
 */
class EnvatoException extends Exception {}

/**
 * Primary Envato API implementation class
 */
class Envato {

  /**
   *
   * @var
   * string
   */
  //public $client_id;
  
  /**
   *
   * @var
   * string
   */
  //public $client_secret;
    
  /**
   *
   * @var
   * JSON
   */
  public $token;
    
  
  
  /************************************************
   * Authentication
   ***********************************************/
  
  /**
   * Constructor for the Envato class
   * $client_id
   * $client_secret
   */
  public function __construct() {
  }
  
  /**
   *
   *
   */
  public function get_authorization_url($redirect_uri) {
    //https://api.envato.com/authorization?response_type=code&client_id=[CLIENT ID]&redirect_uri=[REDIRECT URI]
    $client_id                   = variable_get('envato_client_id', '');
    //$client_secret               = variable_get('envato_client_secret', '');
    $url = variable_get('envato_api', ENVATO_API) . '/authorization';
    $url .= "?response_type=code&client_id=".$client_id."&redirect_uri=".$redirect_uri;
    return $url;
  }

  /**
   *
   *
   */
  public function get_authentication_url() {
    //https://api.envato.com/token
    $url = variable_get('envato_api', ENVATO_API) . '/token';
    return $url;
  }
  
  
  //is_authorization
  /**
   * Just set boolean value to the $this->is_authorization.
   *
   * @param $boolean
   *   Boolean to assign to the $this->is_authorization.
   */
  public function set_is_authorization($boolean) {
    $this->is_authorization = $boolean;
  }
  /**
   * Authorization for the Envato APP.
   * @see https://build.envato.com/api
   *
   * @param string $redirect_uri
   *   String to append to the request.
   */
  public function get_authorization($redirect_uri) {
    $url = $this->get_authorization_url($redirect_uri);
    drupal_goto($url);
  }
  
  /**
   * Request an "access token" for the Envato API.
   * @see https://build.envato.com/api
   *
   * @param string $redirect_uri
   *   String to append to the request.
   * @return
   *   JSON object that has variable "access token" on it, or FALSE when there was an error.
   */
  public function get_authentication($code, $redirect_uri) {
    $url = $this->get_authentication_url();

    /** 
    * Adding parameters to request()
    *
    * grant_type=authorization_code&
    * code=[CODE]&
    * redirect_uri=[REDIRECT_URI]&
    * client_id=[CLIENT_ID]&
    * client_secret=[CLIENT_SECRET]
    */
    $parameters = array();
    
  	$parameters['grant_type']    = "authorization_code";
  	$parameters['code']          = $code;
  	$parameters['redirect_uri']  = $redirect_uri;
  	$parameters['client_id']     = variable_get('envato_client_id', '');
  	$parameters['client_secret'] = variable_get('envato_client_secret', '');
		$is_authorization            = FALSE;
    $method = 'POST';
	
    try {
      $response = $this->request($url, $parameters, $method,$is_authorization );
    }
    catch (EnvatoException $e) {
      watchdog('envato', '!message', array('!message' => $e->__toString()), WATCHDOG_ERROR);
      return FALSE;
    }
    $token = drupal_json_decode($response);
    return $token;
  }
  
  
  /**
   * Performs a request.
   *
   * @throws EnvatoException
   */
  protected function request($url, $params = array(), $method = 'GET', $is_authorization = TRUE) {
        
    $data = '';
    if (count($params) > 0) {
      if ($method == 'GET') {
        $url .= '?'. http_build_query($params, '', '&');
      }
      else {
        $data = http_build_query($params, '', '&');
      }
    }

    $headers = array();
  	$headers['User-Agent'] = variable_get('envato_app_name', ENVATO_APP_NAME);
  	$headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

  	if($is_authorization){
      $headers['Authorization'] = "bearer {$this->token['access_token']}";
  	}
    
    $response = $this->doRequest($url, $headers, $method, $data);
    ///dsm($response);
	
    if (!isset($response->error)) {

      return $response->data;
    }
    else {
      $error = $response->error;
      $check = drupal_json_decode($response->data);
      
      if($check['error_description'] == "Token already expired"){
        $this->refresh_token();
        return array('refreshed'=>TRUE);
      }else{
        throw new EnvatoException($error);
      }
    }
	
  }

  /**
   * Actually performs a request.
   *
   * This method can be easily overriden through inheritance.
   *
   * @param string $url
   *   The url of the endpoint.
   * @param array $headers
   *   Array of headers.
   * @param string $method
   *   The HTTP method to use (normally POST or GET).
   * @param array $data
   *   An array of parameters
   * @return
   *   stdClass response object.
   */
  protected function doRequest($url, $headers, $method, $data) {
    $params = array('headers' => $headers, 'method' => $method, 'data' => $data);
    return drupal_http_request($url, $params);
  }
  
  /**
   * Creates an API endpoint URL.
   *
   * @param string $path
   *   The path of the endpoint.
   * @param string $format
   *   The format of the endpoint to be appended at the end of the path.
   * @return
   *   The complete path to the endpoint.
   */
  protected function create_url($path, $format = '.json') {
    $url =  variable_get('envato_api', ENVATO_API) .'/v1/'. $path . $format;
    return $url;
  }
  
  
  protected function call_parameters($url, $params, $method, $default_value = NULL, $refresh = FALSE){
    $variables = &drupal_static(__FUNCTION__,$default_value,$refresh);
    if (!isset($variables)) {
      // generate contents of static variable
      $variables['url']     = $url;
      $variables['params']  = $params;
      $variables['method']  = $method;
    }
    return $variables;
  }
  

  /**
   *    REFRESH TOKEN
   */
  protected function refresh_token(){
    ///global $user;
    // . '/token?grant_type=refresh_token&refresh_token='.$this->token['refresh_token'];
    $url = variable_get('envato_api', ENVATO_API).'/token'; 

    $parameters = array();
    $parameters['grant_type'] = "refresh_token";

    ///$parameters['refresh_token']  = $user->refresh_token;
    $parameters['refresh_token']  = $this->token['refresh_token'];
    $parameters['redirect_uri']   = variable_get('envato_app_redirect_uri', ENVATO_APP_REDIRECT_URI);//$redirect_uri;
    
    $parameters['client_id']      = variable_get('envato_client_id', '');
    $parameters['client_secret']  = variable_get('envato_client_secret', '');
    
    $method = 'POST';
    $is_authorization = FALSE;
    try {
      $response = $this->request($url, $parameters, $method,$is_authorization);
    }
    catch (EnvatoException $e) {
      watchdog('envato', '!message', array('!message' => $e->__toString()), WATCHDOG_ERROR);
      return FALSE;
    }
    $new_token = drupal_json_decode($response);
    
    $this->token['access_token'] = $new_token['access_token'];
        
    module_invoke_all('envato_refresh_token', $new_token);
    
  }

  /**
   * Calls a Envato API endpoint.
   * 
   * @return
   *   JSON data as respond.
   */
  public function call($path, $params = array(), $method = 'GET') {
    
    $url = $this->create_url($path);
    $call_params  = $this->call_parameters($url, $params, $method);   

    $response = '';
    try {
      $response = $this->request($call_params['url'], $call_params['params'], $call_params['method']);
      if(isset($response['refreshed'])){
        $response = $this->request($call_params['url'], $call_params['params'], $call_params['method']);
      }
    }
    catch (EnvatoException $e) {
      watchdog('envato', '!message', array('!message' => $e->__toString()), WATCHDOG_ERROR);
      return FALSE;
    }
    
    if (!$response) {
      return FALSE;
    }

    return drupal_json_decode($response);
  }





  /* ----------------------------------------------------------------------------------------------- */
  /* --------------- https://build.envato.com/api -------------------------------------------------- */
  /* ----------------------------------------------------------------------------------------------- */

  /****************************************************
  * Search Envato Market                              *
  *****************************************************/


  /****************************************************
  * User Details                                      *
  *****************************************************/
  
  /**
   * User account details.
   * 
   * @return
   *   JSON data as respond.
   */
  public function get_user($user_name){
    return $this->call("market/user:".$user_name);
  }
  
  /**
   * Get a user's username.
   * 
   * @return
   *   JSON data as respond.
   */
  public function get_user_name(){
    return $this->call("market/private/user/username");
  }
  
  /**
   * Get a user's email.
   * 
   * @return
   *   JSON data as respond.
   */
  public function get_user_email(){
	 return $this->call("market/private/user/email");
  }
  
  /**
   * List a user's badges.
   * 
   * @return
   *   JSON data as respond.
   */
  public function get_user_badges($user_name){
	 return $this->call("market/user-badges:".$user_name);
  }
  
  /**
   * A user's items by site.
   * 
   * Show the number of items an author has for sale on each site. Requires a username, e.g. collis
   *
   * @return
   *   JSON data as respond.
   */
  public function get_user_items_by_site($user_name){
	 return $this->call("market/user-items-by-site:".$user_name);
  }
  
  /**
   * New items by user.
   * 
   * Shows the newest 25 files a user has uploaded to a particular site. 
   * Requires username and site parameters, e.g. new-files-from-user:collis,themeforest
   *
   * @return
   *   JSON data as respond.
   */
  public function get_new_files_from_user($user_name,$site){
	 return $this->call("market/new-files-from-user:".$user_name.",".$site);
  }
  
  /****************************************************
  * Private User Details                              *
  *****************************************************/ 
  
  /**
   * User account details.
   * 
   * Returns the first name, surname, earnings available to withdraw,
   * total deposits, balance (deposits + earnings) and country.
   *
   * @return
   *   JSON data as respond.
   */
  public function get_private_user_account(){
   return $this->call("market/private/user/account");
  }

  /**
   * Sales by month.
   * 
   * Returns the monthly sales data, as displayed on the user's earnings page.
   *
   * @return
   *   JSON data as respond.
   */
  public function get_private_user_earnings_and_sales_by_month(){
   return $this->call("market/private/user/earnings-and-sales-by-month");
  }

  /**
   * Statement data.
   * 
   * Returns the last 100 events as seen on the user's statement page. Only shows data from the last 28 days.
   *
   * @return
   *   JSON data as respond.
   */
  public function get_private_user_statement(){
   return $this->call("market/private/user/statement");
  }

  /**
   * Most recent sales.
   * 
   * Shows the 50 most recent sales of the user's items.
   *
   * @return
   *   JSON data as respond.
   */
  public function get_private_user_recent_sales(){
   return $this->call("market/private/user/recent-sales");
  }

  /**
   * Download a purchase.
   * 
   * URL to download an item you have purchased. Requires a purchase code,
   * e.g. download-purchase:550e8400-e29b-41d4-a716-446655440000.
   *
   * @return
   *   JSON data as respond.
   */
  public function get_private_user_download_purchase($purchase_code){
   return $this->call("market/private/user/download-purchase:".$purchase_code);
  }

  /**
   * Verify purchase code.
   * 
   * Details of an item you have sold. Requires a purchase code,
   * e.g. verify-purchase:550e8400-e29b-41d4-a716-446655440000..
   *
   * @return
   *   JSON data as respond.
   */
  public function get_private_user_verify_purchase($purchase_code){
   return $this->call("market/private/user/verify-purchase:".$purchase_code);
  }

  /****************************************************
  * Envato Market Stats                               *
  *****************************************************/

  /****************************************************
  * Envato Market Stats                               *
  *****************************************************/

  /****************************************************
  * Envato Market Stats                               *
  *****************************************************/

}