init: add v 7.15.0
This commit is contained in:
commit
80a9592cea
3727 changed files with 866442 additions and 0 deletions
BIN
MEC-Installation-Guide.pdf
Executable file
BIN
MEC-Installation-Guide.pdf
Executable file
Binary file not shown.
59
app/addons/ACF.php
Executable file
59
app/addons/ACF.php
Executable file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC ACF addon class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_ACF extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the ACF addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->factory->action('mec_wc_product_created', [$this, 'sync_acf_fields'], 10, 2);
|
||||
$this->factory->action('mec_wc_product_updated', [$this, 'sync_acf_fields'], 10, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $product_id
|
||||
* @param int $event_id
|
||||
* @return void
|
||||
*/
|
||||
public function sync_acf_fields($product_id, $event_id)
|
||||
{
|
||||
// ACF Plugin is not installed and activated
|
||||
if(!class_exists('ACF')) return;
|
||||
|
||||
// Event ACF Data
|
||||
$data = get_fields($event_id, false);
|
||||
|
||||
// Data is invalid
|
||||
if(!is_array($data)) return;
|
||||
if(!count($data)) return;
|
||||
|
||||
// Store data for Product
|
||||
foreach($data as $key => $value)
|
||||
{
|
||||
update_field($key, $value, $product_id);
|
||||
}
|
||||
}
|
||||
}
|
85
app/addons/KC.php
Executable file
85
app/addons/KC.php
Executable file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC King Composer addon class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_KC extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* @var MEC_main
|
||||
*/
|
||||
public $main;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
|
||||
// MEC Main class
|
||||
$this->main = $this->getMain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the KC addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
* @return boolean
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// King Composer is not installed
|
||||
if(!function_exists('kc_add_map')) return false;
|
||||
|
||||
$this->factory->action('init', array($this, 'map'));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the addon in KC
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function map()
|
||||
{
|
||||
$calendar_posts = get_posts(array('post_type'=>'mec_calendars', 'posts_per_page'=>'-1'));
|
||||
|
||||
$calendars_name = $calendars_number = [];
|
||||
foreach($calendar_posts as $calendar_post)
|
||||
{
|
||||
$calendars_name[] = $calendar_post->post_title;
|
||||
$calendars_number[] = $calendar_post->ID;
|
||||
}
|
||||
|
||||
$calendars_array = array_combine($calendars_number, $calendars_name);
|
||||
|
||||
kc_add_map(array
|
||||
(
|
||||
'MEC' => array(
|
||||
'name' => esc_html__('Modern Events Calendar', 'modern-events-calendar-lite'),
|
||||
'icon' => 'mec-kingcomposer-icon',
|
||||
'category' => esc_html__('Content', 'modern-events-calendar-lite'),
|
||||
'params' => array(
|
||||
'General' => array(
|
||||
array(
|
||||
'name' => 'id',
|
||||
'label' => esc_html__('Shortcode', 'modern-events-calendar-lite'),
|
||||
'type' => 'select',
|
||||
'options' => $calendars_array,
|
||||
'description' => esc_html__('Select from predefined shortcodes', 'modern-events-calendar-lite'),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
201
app/addons/PMP.php
Executable file
201
app/addons/PMP.php
Executable file
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC Paid Membership Pro addon class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_PMP extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* @var MEC_main
|
||||
*/
|
||||
public $main;
|
||||
public $settings;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
|
||||
// MEC Main class
|
||||
$this->main = $this->getMain();
|
||||
|
||||
// MEC Settings
|
||||
$this->settings = $this->main->get_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the PMP addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
* @return boolean
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$event_restriction = isset($this->settings['pmp_status']) && $this->settings['pmp_status'];
|
||||
$booking_restriction = isset($this->settings['pmp_booking_restriction']) && $this->settings['pmp_booking_restriction'];
|
||||
$ticket_restriction = isset($this->settings['pmp_ticket_restrictions']) && $this->settings['pmp_ticket_restrictions'];
|
||||
|
||||
// Module is not enabled
|
||||
if(!$event_restriction && !$booking_restriction && !$ticket_restriction) return false;
|
||||
|
||||
// Event Restriction
|
||||
if($event_restriction)
|
||||
{
|
||||
// Metabox
|
||||
add_action('admin_menu', [$this, 'metabox']);
|
||||
|
||||
// Display Access Error
|
||||
add_filter('mec_show_event_details_page', [$this, 'check'], 10, 2);
|
||||
}
|
||||
|
||||
// Booking Restriction
|
||||
if($booking_restriction)
|
||||
{
|
||||
add_filter('mec_booking_module_abort', [$this, 'booking_abort'], 10, 2);
|
||||
}
|
||||
|
||||
// Ticket Restrictions
|
||||
if($ticket_restriction)
|
||||
{
|
||||
add_action('mec_ticket_extra_options', [$this, 'ticket_plans_fields'], 10, 3);
|
||||
add_filter('mec_get_tickets_availability', [$this, 'ticket_availability'], 10, 5);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function metabox()
|
||||
{
|
||||
if(!defined('PMPRO_VERSION')) return;
|
||||
|
||||
// Register
|
||||
add_meta_box('pmpro_page_meta', esc_html__('Require Membership', 'modern-events-calendar-lite'), 'pmpro_page_meta', $this->main->get_main_post_type(), 'side', 'high');
|
||||
}
|
||||
|
||||
public function check($status, $event_id)
|
||||
{
|
||||
if(!defined('PMPRO_VERSION')) return $status;
|
||||
|
||||
// Has Access
|
||||
if(function_exists('pmpro_has_membership_access'))
|
||||
{
|
||||
$response = pmpro_has_membership_access($event_id, NULL, true);
|
||||
$available = $response[0] ?? true;
|
||||
|
||||
if(!$available)
|
||||
{
|
||||
$post_membership_levels_ids = $response[1];
|
||||
$post_membership_levels_names = $response[2];
|
||||
|
||||
$content = pmpro_get_no_access_message('', $post_membership_levels_ids, $post_membership_levels_names);
|
||||
$status = '<div class="mec-wrap mec-no-access-error"><h1>'.get_the_title($event_id).'</h1>'.MEC_kses::page($content).'</div>';
|
||||
}
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function booking_abort($abort, $event)
|
||||
{
|
||||
if(!function_exists('pmpro_hasMembershipLevel')) return $abort;
|
||||
|
||||
// Event ID
|
||||
$event_id = $event->ID;
|
||||
if(!$event_id) return $abort;
|
||||
|
||||
// Event Categories
|
||||
$categories = (isset($event->data) and isset($event->data->categories) and is_array($event->data->categories)) ? $event->data->categories : [];
|
||||
|
||||
// Event has no category
|
||||
if(!count($categories)) return $abort;
|
||||
|
||||
// User ID
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// Booking Restriction Options
|
||||
$options = isset($this->settings['pmp_booking']) && is_array($this->settings['pmp_booking']) ? $this->settings['pmp_booking'] : [];
|
||||
|
||||
$needed_levels = [];
|
||||
foreach($options as $level_id => $cats)
|
||||
{
|
||||
foreach($categories as $category)
|
||||
{
|
||||
if(in_array($category['id'], $cats)) $needed_levels[] = $level_id;
|
||||
}
|
||||
}
|
||||
|
||||
$needed_levels = array_unique($needed_levels);
|
||||
if($needed_levels and !pmpro_hasMembershipLevel($needed_levels, $user_id))
|
||||
{
|
||||
return pmpro_get_no_access_message('', $needed_levels);
|
||||
}
|
||||
|
||||
return $abort;
|
||||
}
|
||||
|
||||
public function ticket_plans_fields($ticket_id, $data, $args = [])
|
||||
{
|
||||
if(!function_exists('pmpro_getAllLevels')) return;
|
||||
|
||||
$name_prefix = $args['name_prefix'] ?? 'mec[tickets]';
|
||||
$advanced_class = $args['advanced_class'] ?? 'mec-basvanced-advanced w-hidden';
|
||||
|
||||
// Levels
|
||||
$levels = pmpro_getAllLevels();
|
||||
?>
|
||||
<div id="mec_ticket_pmp_plans_container" class="<?php echo $advanced_class; ?>">
|
||||
<div class="mec-form-row">
|
||||
<h5><?php esc_html_e('Membership Levels', 'modern-events-calendar-lite'); ?></h5>
|
||||
<p class="description"><?php esc_html_e('If you leave the following fields empty then the ticket will be available to all plans.'); ?></p><br />
|
||||
<ul>
|
||||
<?php foreach($levels as $level): ?>
|
||||
<li>
|
||||
<label>
|
||||
<input value="<?php echo esc_attr($level->id); ?>" type="checkbox" name="<?php echo $name_prefix; ?>[<?php echo esc_attr($ticket_id); ?>][pmp][]" <?php if(in_array($level->id, $data['pmp'] ?? [])) echo 'checked="checked"'; ?>>
|
||||
<span><?php echo esc_html($level->name); ?></span>
|
||||
</label>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function ticket_availability($availability, $event_id, $timestamp, $mode, $tickets)
|
||||
{
|
||||
if(!function_exists('pmpro_hasMembershipLevel') || !$event_id) return $availability;
|
||||
|
||||
// User Id
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
foreach ($tickets as $ticket)
|
||||
{
|
||||
// Required Level for Ticket
|
||||
$levels = $ticket['pmp'] ?? [];
|
||||
|
||||
// Available to All
|
||||
if(!count($levels)) continue;
|
||||
|
||||
if(!pmpro_hasMembershipLevel($levels, $user_id))
|
||||
{
|
||||
$availability[$ticket['id']] = 0;
|
||||
$availability['stop_selling_'.$ticket['id']] = true;
|
||||
$availability['stop_selling_'.$ticket['id'].'_message'] = esc_html__("You do not have access to book %s ticket.", 'modern-events-calendar-lite');
|
||||
}
|
||||
}
|
||||
|
||||
return $availability;
|
||||
}
|
||||
}
|
57
app/addons/TNP.php
Executable file
57
app/addons/TNP.php
Executable file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC The Newsletter Plugin addon class
|
||||
* @link https://www.thenewsletterplugin.com/
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_TNP extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* @var MEC_main
|
||||
*/
|
||||
public $main;
|
||||
public $settings;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
|
||||
// MEC Main class
|
||||
$this->main = $this->getMain();
|
||||
|
||||
// MEC Settings
|
||||
$this->settings = $this->main->get_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the TNP addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
* @return boolean
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->factory->action('newsletter_register_blocks', array($this, 'register'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if(!class_exists('TNP_Composer')) return;
|
||||
|
||||
TNP_Composer::register_block(MEC_ABSPATH.'app'.DS.'addons'.DS.'tnp'.DS.'simple');
|
||||
}
|
||||
}
|
78
app/addons/VC.php
Executable file
78
app/addons/VC.php
Executable file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC VC addon class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_VC extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* @var MEC_main
|
||||
*/
|
||||
public $main;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
|
||||
// MEC Main class
|
||||
$this->main = $this->getMain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the VC addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// Visual Composer is not installed
|
||||
if(!function_exists('vc_map')) return false;
|
||||
|
||||
$this->factory->action('vc_before_init', array($this, 'map'));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the addon in VC
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function map()
|
||||
{
|
||||
$calendar_posts = get_posts(array('post_type'=>'mec_calendars', 'posts_per_page'=>'-1'));
|
||||
|
||||
$calendars = [];
|
||||
foreach($calendar_posts as $calendar_post) $calendars[$calendar_post->post_title] = $calendar_post->ID;
|
||||
|
||||
vc_map(array(
|
||||
'name'=>esc_html__('Modern Events Calendar', 'modern-events-calendar-lite'),
|
||||
'base'=>'MEC',
|
||||
'class'=>'',
|
||||
'controls'=>'full',
|
||||
'icon'=>$this->main->asset('img/ico-mec-vc.png'),
|
||||
'category'=>esc_html__('Content', 'modern-events-calendar-lite'),
|
||||
'params'=>array(
|
||||
array(
|
||||
'type'=>'dropdown',
|
||||
'holder'=>'div',
|
||||
'class'=>'',
|
||||
'heading'=>esc_html__('Shortcode', 'modern-events-calendar-lite'),
|
||||
'param_name'=>'id',
|
||||
'value'=>$calendars,
|
||||
'description'=>esc_html__('Select from predefined shortcodes', 'modern-events-calendar-lite')
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
72
app/addons/avada.php
Executable file
72
app/addons/avada.php
Executable file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC Avada Builder addon class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_avada extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* @var MEC_main
|
||||
*/
|
||||
public $main;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
|
||||
// MEC Main class
|
||||
$this->main = $this->getMain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Elementor addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->factory->action('fusion_builder_before_init', array($this, 'register'));
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
$calendar_posts = get_posts(array('post_type'=>'mec_calendars', 'posts_per_page'=>'-1'));
|
||||
|
||||
$shortcodes = [];
|
||||
foreach($calendar_posts as $calendar_post)
|
||||
{
|
||||
$shortcodes[$calendar_post->ID] = $calendar_post->post_title;
|
||||
}
|
||||
|
||||
fusion_builder_map([
|
||||
'name' => esc_attr__('MEC', 'modern-events-calendar-lite'),
|
||||
'shortcode' => 'MEC',
|
||||
'icon' => 'fusiona-code',
|
||||
'preview' => MEC_ABSPATH.'app/addons/avada/preview.php',
|
||||
'preview_id' => 'mec-avada-shortcode-element',
|
||||
'allow_generator' => true,
|
||||
'params' => [
|
||||
[
|
||||
'type' => 'select',
|
||||
'heading' => esc_attr__('Shortcode', 'modern-events-calendar-lite'),
|
||||
'description' => esc_attr__('Select one of created shortcodes.', 'modern-events-calendar-lite'),
|
||||
'param_name' => 'id',
|
||||
'value' => $shortcodes,
|
||||
'default' => '',
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
11
app/addons/avada/preview.php
Executable file
11
app/addons/avada/preview.php
Executable file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
/**
|
||||
* Underscore.js template.
|
||||
*
|
||||
* @package fusion-builder
|
||||
*/
|
||||
|
||||
?>
|
||||
<script type="text/template" id="mec-avada-shortcode-element">
|
||||
<h4 class="fusion_module_title">MEC Shortcode ({{ params.id }})</h4>
|
||||
</script>
|
54
app/addons/beaver.php
Executable file
54
app/addons/beaver.php
Executable file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC Beaver Builder addon class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_beaver extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* @var MEC_main
|
||||
*/
|
||||
public $main;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
|
||||
// MEC Main class
|
||||
$this->main = $this->getMain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Elementor addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// Beaver Builder is not installed
|
||||
if(!class_exists( 'FLBuilder' ) ) return false;
|
||||
define( 'MEC_BEAVER_DIR', plugin_dir_path( __FILE__ ) );
|
||||
define( 'MEC_BEAVER_URL', plugins_url( '/', __FILE__ ) );
|
||||
add_action( 'init', array($this,'mec_beaver_builder_shortcode') );
|
||||
return true;
|
||||
}
|
||||
|
||||
public function mec_beaver_builder_shortcode() {
|
||||
if ( class_exists( 'FLBuilder' ) ) {
|
||||
require_once MEC_ABSPATH.'app/addons/mec-beaver-builder/mec-beaver-builder.php';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
67
app/addons/divi.php
Executable file
67
app/addons/divi.php
Executable file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC Divi addon class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_divi extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* @var MEC_main
|
||||
*/
|
||||
public $main;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
|
||||
// MEC Main class
|
||||
$this->main = $this->getMain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Elementor addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// Divi is not installed
|
||||
$theme = wp_get_theme(); // gets the current theme
|
||||
|
||||
if('Divi' != $theme->get_template()) return false;
|
||||
|
||||
add_action('divi_extensions_init', array($this, 'mecdivi_initialize_extension'));
|
||||
add_filter('et_builder_load_actions', array($this, 'add_ajax_actions'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the extension's main class instance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function mecdivi_initialize_extension()
|
||||
{
|
||||
require_once plugin_dir_path( __FILE__ ) . 'divi/includes/Divi.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . 'divi/includes/MECShortcodesForDivi.php';
|
||||
}
|
||||
|
||||
public function add_ajax_actions($actions)
|
||||
{
|
||||
$actions[] = 'mec_load_single_page';
|
||||
return $actions;
|
||||
}
|
||||
}
|
45
app/addons/divi/includes/Divi.php
Executable file
45
app/addons/divi/includes/Divi.php
Executable file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
class MECDIVI_Divi extends DiviExtension {
|
||||
|
||||
/**
|
||||
* The gettext domain for the extension's translations.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $gettext_domain = 'mecdivi-divi';
|
||||
|
||||
/**
|
||||
* The extension's WP Plugin name.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = 'divi';
|
||||
|
||||
/**
|
||||
* The extension's version
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $version = '1.0.0';
|
||||
|
||||
/**
|
||||
* MECDIVI_Divi constructor.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $args
|
||||
*/
|
||||
public function __construct( $name = 'divi', $args = array() ) {
|
||||
$this->plugin_dir = plugin_dir_path( __FILE__ );
|
||||
$this->plugin_dir_url = plugin_dir_url( $this->plugin_dir );
|
||||
|
||||
parent::__construct( $name, $args );
|
||||
}
|
||||
}
|
||||
|
||||
new MECDIVI_Divi;
|
20
app/addons/divi/includes/MECShortcodesForDivi.php
Executable file
20
app/addons/divi/includes/MECShortcodesForDivi.php
Executable file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
if(! function_exists( 'MECDIVI_et_builder_load_actions' )) {
|
||||
function MECDIVI_et_builder_load_actions( $actions ) {
|
||||
$actions[] = 'MECDIVI_load_mec_shortcode';
|
||||
|
||||
return $actions;
|
||||
}
|
||||
}
|
||||
|
||||
add_filter( 'et_builder_load_actions', 'MECDIVI_et_builder_load_actions' );
|
||||
|
||||
if(! function_exists( 'MECDIVI_load_mec_shortcode' )) {
|
||||
function MECDIVI_load_mec_shortcode() {
|
||||
if(!current_user_can('manage_options')) return;
|
||||
$post_id = sanitize_text_field($_POST['shortcode_id']);
|
||||
echo do_shortcode( '[MEC id="'.$post_id.'"]' );
|
||||
wp_die();
|
||||
}
|
||||
}
|
||||
add_action( 'wp_ajax_MECDIVI_load_mec_shortcode', 'MECDIVI_load_mec_shortcode' );
|
9
app/addons/divi/includes/loader.js
Executable file
9
app/addons/divi/includes/loader.js
Executable file
|
@ -0,0 +1,9 @@
|
|||
// External Dependencies
|
||||
import $ from 'jquery';
|
||||
|
||||
// Internal Dependencies
|
||||
import modules from './modules';
|
||||
|
||||
$(window).on('et_builder_api_ready', (event, API) => {
|
||||
API.registerModules(modules);
|
||||
});
|
14
app/addons/divi/includes/loader.php
Executable file
14
app/addons/divi/includes/loader.php
Executable file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
if ( ! class_exists( 'ET_Builder_Element' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$module_files = glob( __DIR__ . '/modules/*/*.php' );
|
||||
|
||||
// Load custom Divi Builder modules
|
||||
foreach ( (array) $module_files as $module_file ) {
|
||||
if ( $module_file && preg_match( "/\/modules\/\b([^\/]+)\/\\1\.php$/", $module_file ) ) {
|
||||
require_once $module_file;
|
||||
}
|
||||
}
|
27
app/addons/divi/includes/modules/MECShortcodes/MECShortcodes.jsx
Executable file
27
app/addons/divi/includes/modules/MECShortcodes/MECShortcodes.jsx
Executable file
|
@ -0,0 +1,27 @@
|
|||
// External Dependencies
|
||||
import React, { Component } from 'react';
|
||||
import $ from 'jquery';
|
||||
|
||||
class MECShortcodes extends Component {
|
||||
|
||||
static slug = 'mecdivi_MECShortcodes';
|
||||
render() {
|
||||
$.ajax({
|
||||
url: window.ETBuilderBackend.ajaxUrl,
|
||||
type: 'post',
|
||||
data: {
|
||||
action: 'MECDIVI_load_mec_shortcode',
|
||||
nonce: 'et_admin_load_nonce',
|
||||
shortcode_id: this.props.shortcode_id,
|
||||
},
|
||||
success: function (response) {
|
||||
$('.mec-shortcode').html(response);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div class="mec-shortcode"></div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MECShortcodes;
|
37
app/addons/divi/includes/modules/MECShortcodes/MECShortcodes.php
Executable file
37
app/addons/divi/includes/modules/MECShortcodes/MECShortcodes.php
Executable file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
class MECDIVI_MECShortcodes extends ET_Builder_Module {
|
||||
|
||||
public $slug = 'mecdivi_MECShortcodes';
|
||||
public $vb_support = 'on';
|
||||
|
||||
protected $module_credits = array(
|
||||
'module_uri' => 'https://webnus.net',
|
||||
'author' => 'Webnus',
|
||||
'author_uri' => 'https://webnus.net',
|
||||
);
|
||||
|
||||
public function init() {
|
||||
$this->name = esc_html__( 'MEC Shortcodes', 'mecdivi-divi' );
|
||||
}
|
||||
|
||||
public function get_fields() {
|
||||
$calendar_posts = get_posts(array('post_type'=>'mec_calendars', 'posts_per_page'=>'-1'));
|
||||
$calendars = [];
|
||||
foreach($calendar_posts as $calendar_post) $calendars[$calendar_post->ID] = $calendar_post->post_title;
|
||||
|
||||
return array(
|
||||
'shortcode_id' => array(
|
||||
'label' => esc_html__( 'MEC Shortcodes', 'mecdivi-divi' ),
|
||||
'type' => 'select',
|
||||
'options' => $calendars,
|
||||
'description' => esc_html__( 'Enter the shortcode_id of your choosing here.', 'mecdivi-divi' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
public function render( $attrs, $content = NULL, $render_slug = NULL ) {
|
||||
return do_shortcode('[MEC id="'.$this->props['shortcode_id'].'"]');
|
||||
}
|
||||
}
|
||||
|
||||
new MECDIVI_MECShortcodes;
|
5
app/addons/divi/includes/modules/index.js
Executable file
5
app/addons/divi/includes/modules/index.js
Executable file
|
@ -0,0 +1,5 @@
|
|||
import MECShortcodes from './MECShortcodes/MECShortcodes';
|
||||
|
||||
export default [
|
||||
MECShortcodes,
|
||||
];
|
201
app/addons/divi/scripts/builder-bundle.min.js
vendored
Executable file
201
app/addons/divi/scripts/builder-bundle.min.js
vendored
Executable file
|
@ -0,0 +1,201 @@
|
|||
!(function(e) {
|
||||
var t = {};
|
||||
function n(o) {
|
||||
if (t[o]) return t[o].exports;
|
||||
var r = (t[o] = { i: o, l: !1, exports: {} });
|
||||
return e[o].call(r.exports, r, r.exports, n), (r.l = !0), r.exports;
|
||||
}
|
||||
(n.m = e),
|
||||
(n.c = t),
|
||||
(n.d = function(e, t, o) {
|
||||
n.o(e, t) ||
|
||||
Object.defineProperty(e, t, {
|
||||
configurable: !1,
|
||||
enumerable: !0,
|
||||
get: o
|
||||
});
|
||||
}),
|
||||
(n.n = function(e) {
|
||||
var t =
|
||||
e && e.__esModule
|
||||
? function() {
|
||||
return e.default;
|
||||
}
|
||||
: function() {
|
||||
return e;
|
||||
};
|
||||
return n.d(t, "a", t), t;
|
||||
}),
|
||||
(n.o = function(e, t) {
|
||||
return Object.prototype.hasOwnProperty.call(e, t);
|
||||
}),
|
||||
(n.p = "/"),
|
||||
n((n.s = 1));
|
||||
})([
|
||||
function(e, t) {
|
||||
e.exports = jQuery;
|
||||
},
|
||||
function(e, t, n) {
|
||||
n(2), (e.exports = n(3));
|
||||
},
|
||||
function(e, t, n) {
|
||||
"use strict";
|
||||
},
|
||||
function(e, t, n) {
|
||||
"use strict";
|
||||
Object.defineProperty(t, "__esModule", { value: !0 });
|
||||
var o = n(0),
|
||||
r = n.n(o),
|
||||
c = n(4);
|
||||
r()(window).on("et_builder_api_ready", function(e, t) {
|
||||
t.registerModules(c.a);
|
||||
});
|
||||
},
|
||||
function(e, t, n) {
|
||||
"use strict";
|
||||
var o = n(5);
|
||||
t.a = [o.a];
|
||||
},
|
||||
function(e, t, n) {
|
||||
"use strict";
|
||||
var o = n(6),
|
||||
r = n.n(o),
|
||||
c = n(0),
|
||||
u = n.n(c);
|
||||
function i(e) {
|
||||
return (i =
|
||||
"function" === typeof Symbol && "symbol" === typeof Symbol.iterator
|
||||
? function(e) {
|
||||
return typeof e;
|
||||
}
|
||||
: function(e) {
|
||||
return e &&
|
||||
"function" === typeof Symbol &&
|
||||
e.constructor === Symbol &&
|
||||
e !== Symbol.prototype
|
||||
? "symbol"
|
||||
: typeof e;
|
||||
})(e);
|
||||
}
|
||||
function a(e, t) {
|
||||
for (var n = 0; n < t.length; n++) {
|
||||
var o = t[n];
|
||||
(o.enumerable = o.enumerable || !1),
|
||||
(o.configurable = !0),
|
||||
"value" in o && (o.writable = !0),
|
||||
Object.defineProperty(e, o.key, o);
|
||||
}
|
||||
}
|
||||
function f(e, t) {
|
||||
return !t || ("object" !== i(t) && "function" !== typeof t)
|
||||
? (function(e) {
|
||||
if (void 0 === e)
|
||||
throw new ReferenceError(
|
||||
"this hasn't been initialised - super() hasn't been called"
|
||||
);
|
||||
return e;
|
||||
})(e)
|
||||
: t;
|
||||
}
|
||||
var s = (function(e) {
|
||||
function t() {
|
||||
return (
|
||||
(function(e, t) {
|
||||
if (!(e instanceof t))
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
})(this, t),
|
||||
f(
|
||||
this,
|
||||
(t.__proto__ || Object.getPrototypeOf(t)).apply(this, arguments)
|
||||
)
|
||||
);
|
||||
}
|
||||
var n, c, i;
|
||||
return (
|
||||
(function(e, t) {
|
||||
if ("function" !== typeof t && null !== t)
|
||||
throw new TypeError(
|
||||
"Super expression must either be null or a function"
|
||||
);
|
||||
(e.prototype = Object.create(t && t.prototype, {
|
||||
constructor: {
|
||||
value: e,
|
||||
enumerable: !1,
|
||||
writable: !0,
|
||||
configurable: !0
|
||||
}
|
||||
})),
|
||||
t &&
|
||||
(Object.setPrototypeOf
|
||||
? Object.setPrototypeOf(e, t)
|
||||
: (e.__proto__ = t));
|
||||
})(t, o["Component"]),
|
||||
(n = t),
|
||||
(c = [
|
||||
{
|
||||
key: "render",
|
||||
value: function() {
|
||||
return (
|
||||
u.a.ajax({
|
||||
url: window.ETBuilderBackend.ajaxUrl,
|
||||
type: "post",
|
||||
data: {
|
||||
action: "MECDIVI_load_mec_shortcode",
|
||||
nonce: "et_admin_load_nonce",
|
||||
shortcode_id: this.props.shortcode_id
|
||||
},
|
||||
success: function(e) {
|
||||
u()(".mec-shortcode").html(e);
|
||||
var node = jQuery(".mec-event-masonry");
|
||||
if(typeof node !== 'undefined')
|
||||
{
|
||||
// var masonryShuffle = window.Shuffle;
|
||||
|
||||
if (node === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// var masonryShuffle = new Shuffle(node, {
|
||||
// itemSelector: '.mec-masonry-item-wrap',
|
||||
// });
|
||||
// masonryShuffle.sort({
|
||||
// by: node.data('created')
|
||||
// });
|
||||
// var $container = $("#mec_skin_" + settings.id + " .mec-event-masonry");
|
||||
var $grid = node.isotope({
|
||||
filter: '*',
|
||||
itemSelector: '.mec-masonry-item-wrap',
|
||||
getSortData: {
|
||||
date: '[data-sort-masonry]',
|
||||
},
|
||||
sortBy: 'date',
|
||||
animationOptions: {
|
||||
duration: 750,
|
||||
easing: 'linear',
|
||||
queue: false
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
r.a.createElement("div", { class: "mec-shortcode" })
|
||||
);
|
||||
}
|
||||
}
|
||||
]) && a(n.prototype, c),
|
||||
i && a(n, i),
|
||||
t
|
||||
);
|
||||
})();
|
||||
Object.defineProperty(s, "slug", {
|
||||
configurable: !0,
|
||||
enumerable: !0,
|
||||
writable: !0,
|
||||
value: "mecdivi_MECShortcodes"
|
||||
}),
|
||||
(t.a = s);
|
||||
},
|
||||
function(e, t) {
|
||||
e.exports = React;
|
||||
}
|
||||
]);
|
1
app/addons/divi/scripts/frontend-bundle.min.js
vendored
Executable file
1
app/addons/divi/scripts/frontend-bundle.min.js
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
!function(n){var t={};function r(e){if(t[e])return t[e].exports;var o=t[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=n,r.c=t,r.d=function(n,t,e){r.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:e})},r.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return r.d(t,"a",t),t},r.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},r.p="/",r(r.s=7)}({7:function(n,t,r){n.exports=r(8)},8:function(n,t){jQuery(function(n){})}});
|
3
app/addons/divi/scripts/frontend.js
Executable file
3
app/addons/divi/scripts/frontend.js
Executable file
|
@ -0,0 +1,3 @@
|
|||
// This script is loaded both on the frontend page and in the Visual Builder.
|
||||
|
||||
jQuery(function($) {});
|
0
app/addons/divi/styles/style-dbp.min.css
vendored
Executable file
0
app/addons/divi/styles/style-dbp.min.css
vendored
Executable file
1
app/addons/divi/styles/style.min.css
vendored
Executable file
1
app/addons/divi/styles/style.min.css
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
.mec-owl-carousel {display: none;width: 100%;-webkit-tap-highlight-color: transparent;position: relative;z-index: 1;}.mec-owl-carousel .owl-stage {position: relative;-ms-touch-action: pan-Y;-moz-backface-visibility: hidden;}.mec-owl-carousel .owl-stage:after {content: ".";display: block;clear: both;visibility: hidden;line-height: 0;height: 0;}.mec-owl-carousel .owl-stage-outer {position: relative;overflow: hidden;-webkit-transform: translate3d(0px, 0px, 0px);}.mec-owl-carousel .owl-wrapper, .mec-owl-carousel .owl-item {-webkit-backface-visibility: hidden;-moz-backface-visibility: hidden;-ms-backface-visibility: hidden;-webkit-transform: translate3d(0, 0, 0);-moz-transform: translate3d(0, 0, 0);-ms-transform: translate3d(0, 0, 0);}.mec-owl-carousel .owl-item {position: relative;min-height: 1px;float: left;-webkit-backface-visibility: hidden;-webkit-tap-highlight-color: transparent;-webkit-touch-callout: none;}.mec-owl-carousel .owl-item img {display: block;width: 100%;}.mec-owl-carousel .owl-nav.disabled, .mec-owl-carousel .owl-dots.disabled {display: none;}.mec-owl-carousel .owl-nav .owl-prev, .mec-owl-carousel .owl-nav .owl-next, .mec-owl-carousel .owl-dot {cursor: pointer;cursor: hand;-webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}.mec-owl-carousel.owl-loaded {display: block;}.mec-owl-carousel.owl-loading {opacity: 0;display: block;}.mec-owl-carousel.owl-hidden {opacity: 0;}.mec-owl-carousel.owl-refresh .owl-item {visibility: hidden;}.mec-owl-carousel.owl-drag .owl-item {-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}.mec-owl-carousel.owl-grab {cursor: move;cursor: grab;}.mec-owl-carousel.owl-rtl {direction: rtl;}.mec-owl-carousel.owl-rtl .owl-item {float: right;}.no-js .mec-owl-carousel {display: block;}.mec-owl-carousel .animated {animation-duration: 1000ms;animation-fill-mode: both;}.mec-owl-carousel .owl-animated-in {z-index: 0;}.mec-owl-carousel .owl-animated-out {z-index: 1;}.mec-owl-carousel .fadeOut {animation-name: fadeOut;}@keyframes fadeOut {0% {opacity: 1;}100% {opacity: 0;}}.owl-height {transition: height 500ms ease-in-out;}.mec-owl-carousel .owl-item .owl-lazy {opacity: 0;transition: opacity 400ms ease;}.mec-owl-carousel .owl-item img.owl-lazy {transform-style: preserve-3d;}.mec-owl-carousel .owl-video-wrapper {position: relative;height: 100%;background: #000;}.mec-owl-carousel .owl-video-play-icon {position: absolute;height: 80px;width: 80px;left: 50%;top: 50%;margin-left: -40px;margin-top: -40px;background: url("owl.video.play.png") no-repeat;cursor: pointer;z-index: 1;-webkit-backface-visibility: hidden;transition: transform 100ms ease;}.mec-owl-carousel .owl-video-play-icon:hover {-ms-transform: scale(1.3, 1.3);transform: scale(1.3, 1.3);}.mec-owl-carousel .owl-video-playing .owl-video-tn, .mec-owl-carousel .owl-video-playing .owl-video-play-icon {display: none;}.mec-owl-carousel .owl-video-tn {opacity: 0;height: 100%;background-position: center center;background-repeat: no-repeat;background-size: contain;transition: opacity 400ms ease;}.mec-owl-carousel .owl-video-frame {position: relative;z-index: 1;height: 100%;width: 100%;}
|
77
app/addons/elementor.php
Executable file
77
app/addons/elementor.php
Executable file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC elementor addon class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_elementor extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* @var MEC_main
|
||||
*/
|
||||
public $main;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
|
||||
// MEC Main class
|
||||
$this->main = $this->getMain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Elementor addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// Elementor is not installed
|
||||
if(!did_action('elementor/loaded')) return false;
|
||||
|
||||
add_action('elementor/widgets/register', array($this, 'register_shortcode'));
|
||||
|
||||
add_action( 'elementor/preview/enqueue_styles', function()
|
||||
{
|
||||
wp_enqueue_style( 'mec-owl-carousel-style' );
|
||||
wp_enqueue_style( 'mec-frontend-style' );
|
||||
});
|
||||
|
||||
add_action('elementor/editor/after_enqueue_scripts', function()
|
||||
{
|
||||
wp_enqueue_script('mec-owl-carousel-script');
|
||||
wp_enqueue_script('mec-frontend-script');
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register MEC Elementor Shortcode
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function register_shortcode()
|
||||
{
|
||||
require_once MEC_ABSPATH.'app/addons/elementor/shortcode.php';
|
||||
\Elementor\Plugin::instance()->widgets_manager->register(new \Elementor\MEC_addon_elementor_shortcode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the addon in Elementor
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function map()
|
||||
{
|
||||
}
|
||||
}
|
0
app/addons/elementor/index.html
Executable file
0
app/addons/elementor/index.html
Executable file
110
app/addons/elementor/shortcode.php
Executable file
110
app/addons/elementor/shortcode.php
Executable file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
namespace Elementor;
|
||||
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC elementor addon shortcode class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_elementor_shortcode extends \Elementor\Widget_Base
|
||||
{
|
||||
/**
|
||||
* Retrieve MEC widget name.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string Widget name.
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return 'MEC';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve MEC widget title.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string Widget title.
|
||||
*/
|
||||
public function get_title()
|
||||
{
|
||||
return esc_html__('Modern Events Calendar (MEC)', 'modern-events-calendar-lite');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve MEC widget icon.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string Widget icon.
|
||||
*/
|
||||
public function get_icon()
|
||||
{
|
||||
return 'eicon-archive-posts';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set widget category.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return array Widget category.
|
||||
*/
|
||||
public function get_categories()
|
||||
{
|
||||
return array('general');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register MEC widget controls.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function register_controls()
|
||||
{
|
||||
$calendar_posts = get_posts(array('post_type'=>'mec_calendars', 'posts_per_page'=>'-1'));
|
||||
|
||||
$calendars = [];
|
||||
foreach($calendar_posts as $calendar_post) $calendars[$calendar_post->ID] = $calendar_post->post_title;
|
||||
|
||||
// Content Tab
|
||||
$this->start_controls_section(
|
||||
'content_section',
|
||||
array(
|
||||
'label' => esc_html__('General', 'modern-events-calendar-lite'),
|
||||
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
|
||||
)
|
||||
);
|
||||
|
||||
// Select Type Section
|
||||
$this->add_control(
|
||||
'type',
|
||||
array(
|
||||
'label' => esc_html__('Select Type', 'modern-events-calendar-lite'),
|
||||
'type' => \Elementor\Controls_Manager::SELECT,
|
||||
'options' => $calendars,
|
||||
)
|
||||
);
|
||||
|
||||
$this->end_controls_section();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render MEC widget output on the frontend.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function render()
|
||||
{
|
||||
$settings = $this->get_settings_for_display();
|
||||
if(!empty($settings['type']))
|
||||
{
|
||||
echo do_shortcode('[MEC id="'.$settings['type'].'"]');
|
||||
}
|
||||
}
|
||||
}
|
0
app/addons/index.html
Executable file
0
app/addons/index.html
Executable file
158
app/addons/learndash.php
Executable file
158
app/addons/learndash.php
Executable file
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
/** no direct access **/
|
||||
defined('MECEXEC') or die();
|
||||
|
||||
/**
|
||||
* Webnus MEC LearnDash addon class
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
class MEC_addon_learndash extends MEC_base
|
||||
{
|
||||
/**
|
||||
* @var MEC_factory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
/**
|
||||
* @var MEC_main
|
||||
*/
|
||||
public $main;
|
||||
public $settings;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @author Webnus <info@webnus.net>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// MEC Factory class
|
||||
$this->factory = $this->getFactory();
|
||||
|
||||
// MEC Main class
|
||||
$this->main = $this->getMain();
|
||||
|
||||
// MEC Settings
|
||||
$this->settings = $this->main->get_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the LD addon
|
||||
* @author Webnus <info@webnus.net>
|
||||
* @return boolean
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// Module is not enabled
|
||||
if(!isset($this->settings['ld_status']) or (isset($this->settings['ld_status']) and !$this->settings['ld_status'])) return false;
|
||||
|
||||
// Tickets
|
||||
add_action('custom_field_ticket', array($this, 'add_courses_dropdown_to_tickets'), 10, 2);
|
||||
add_action('custom_field_dynamic_ticket', array($this, 'add_courses_dropdown_to_raw_tickets'));
|
||||
|
||||
// Enrollment Method
|
||||
$enroll_method = (isset($this->settings['ld_enrollment_method']) and trim($this->settings['ld_enrollment_method'])) ? $this->settings['ld_enrollment_method'] : 'booking';
|
||||
|
||||
// Enroll
|
||||
if($enroll_method === 'booking') add_action('mec_booking_completed', array($this, 'assign'), 10, 1);
|
||||
elseif($enroll_method === 'confirm') add_action('mec_booking_confirmed', array($this, 'assign'), 10, 1);
|
||||
elseif($enroll_method === 'verification') add_action('mec_booking_verified', array($this, 'assign'), 10, 1);
|
||||
elseif($enroll_method === 'confirm_verification')
|
||||
{
|
||||
add_action('mec_booking_confirmed', array($this, 'pre_enroll'), 10, 1);
|
||||
add_action('mec_booking_verified', array($this, 'pre_enroll'), 10, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function add_courses_dropdown_to_tickets($ticket, $key)
|
||||
{
|
||||
// LearnDash is not installed
|
||||
if(!defined('LEARNDASH_VERSION')) return;
|
||||
|
||||
$courses = $this->get_courses();
|
||||
if(!count($courses)) return;
|
||||
?>
|
||||
<div class="mec-form-row">
|
||||
<label for="mec_tickets_<?php echo esc_attr($key); ?>_ld_course"><?php esc_html_e('LearnDash Course', 'modern-events-calendar-lite'); ?></label>
|
||||
<select name="mec[tickets][<?php echo esc_attr($key); ?>][ld_course]" id="mec_tickets_<?php echo esc_attr($key); ?>_ld_course">
|
||||
<option>-----</option>
|
||||
<?php foreach($courses as $course_id => $course_name): ?>
|
||||
<option value="<?php echo esc_attr($course_id); ?>"<?php echo ((isset($ticket['ld_course']) and $course_id == $ticket['ld_course']) ? 'selected="selected"' : ''); ?>><?php echo esc_html($course_name); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function add_courses_dropdown_to_raw_tickets()
|
||||
{
|
||||
// LearnDash is not installed
|
||||
if(!defined('LEARNDASH_VERSION')) return;
|
||||
|
||||
$this->add_courses_dropdown_to_tickets(array(), ':i:');
|
||||
}
|
||||
|
||||
public function get_courses()
|
||||
{
|
||||
$courses = [];
|
||||
|
||||
$args = ['post_type' => 'sfwd-courses', 'posts_per_page' => -1];
|
||||
if(!current_user_can('manage_options') and isset($this->settings['ld_course_access']) and $this->settings['ld_course_access'] === 'user') $args['author'] = get_current_user_id();
|
||||
|
||||
$posts = get_posts($args);
|
||||
if($posts) foreach($posts as $post) $courses[$post->ID] = $post->post_title;
|
||||
|
||||
return $courses;
|
||||
}
|
||||
|
||||
public function assign($book_id)
|
||||
{
|
||||
// LearnDash is not installed
|
||||
if(!defined('LEARNDASH_VERSION')) return;
|
||||
|
||||
// MEC User
|
||||
$u = $this->getUser();
|
||||
|
||||
$event_id = get_post_meta($book_id, 'mec_event_id', true);
|
||||
$ticket_ids = explode(',', get_post_meta($book_id, 'mec_ticket_id', true));
|
||||
|
||||
$attendees = get_post_meta($book_id, 'mec_attendees', true);
|
||||
if(!is_array($attendees)) $attendees = [];
|
||||
|
||||
$tickets = get_post_meta($event_id, 'mec_tickets', true);
|
||||
|
||||
foreach($attendees as $key => $attendee)
|
||||
{
|
||||
if($key === 'attachments') continue;
|
||||
if(!isset($attendee['id'])) continue;
|
||||
|
||||
$ticket_id = $attendee['id'];
|
||||
|
||||
if(!is_numeric($ticket_id)) continue;
|
||||
if(!in_array($ticket_id, $ticket_ids)) continue;
|
||||
if(!isset($tickets[$ticket_id])) continue;
|
||||
|
||||
$ticket = $tickets[$ticket_id];
|
||||
|
||||
// Course ID
|
||||
$course_id = $ticket['ld_course'];
|
||||
|
||||
// User ID
|
||||
$user_id = $u->register($attendee, [
|
||||
'event_id' => $event_id,
|
||||
]);
|
||||
|
||||
// Associate Course
|
||||
ld_update_course_access($user_id, $course_id, false);
|
||||
}
|
||||
}
|
||||
|
||||
public function pre_enroll($booking_id)
|
||||
{
|
||||
$confirmed = get_post_meta($booking_id, 'mec_confirmed', true);
|
||||
$verified = get_post_meta($booking_id, 'mec_verified', true);
|
||||
|
||||
if($confirmed == 1 and $verified == 1) $this->assign($booking_id);
|
||||
}
|
||||
}
|
1
app/addons/mec-beaver-builder/calendar.svg
Executable file
1
app/addons/mec-beaver-builder/calendar.svg
Executable file
|
@ -0,0 +1 @@
|
|||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20"><path d="M15 4h3v14h-16v-14h3v-1c0-0.83 0.67-1.5 1.5-1.5s1.5 0.67 1.5 1.5v1h4v-1c0-0.83 0.67-1.5 1.5-1.5s1.5 0.67 1.5 1.5v1zM6 3v2.5c0 0.28 0.22 0.5 0.5 0.5s0.5-0.22 0.5-0.5v-2.5c0-0.28-0.22-0.5-0.5-0.5s-0.5 0.22-0.5 0.5zM13 3v2.5c0 0.28 0.22 0.5 0.5 0.5s0.5-0.22 0.5-0.5v-2.5c0-0.28-0.22-0.5-0.5-0.5s-0.5 0.22-0.5 0.5zM17 17v-9h-14v9h14zM7 16v-7h-2v7h2zM11 16v-7h-2v7h2zM15 16v-7h-2v7h2z"></path></svg>
|
After Width: | Height: | Size: 545 B |
3
app/addons/mec-beaver-builder/includes/frontend.php
Executable file
3
app/addons/mec-beaver-builder/includes/frontend.php
Executable file
|
@ -0,0 +1,3 @@
|
|||
<div class="fl-example-text">
|
||||
<?php echo do_shortcode('[MEC id="'.$settings->mec_shortcode.'"]') ?>
|
||||
</div>
|
39
app/addons/mec-beaver-builder/mec-beaver-builder.php
Executable file
39
app/addons/mec-beaver-builder/mec-beaver-builder.php
Executable file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
class mecBeaverBuilderShortcode extends FLBuilderModule {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(array(
|
||||
'name' => esc_html__( 'Modern Events Calendar (MEC)', 'modern-events-calendar-lite'),
|
||||
'description' => esc_html__( 'MEC Shortcodes', 'modern-events-calendar-lite'),
|
||||
'category' => esc_html__( 'Basic', 'modern-events-calendar-lite'),
|
||||
'dir' => MEC_BEAVER_DIR . 'mec-beaver-builder/',
|
||||
'url' => MEC_BEAVER_URL . 'mec-beaver-builder/',
|
||||
'icon' => 'button.svg',
|
||||
'editor_export' => true, // Defaults to true and can be omitted.
|
||||
'enabled' => true, // Defaults to true and can be omitted.
|
||||
'partial_refresh' => false, // Defaults to false and can be omitted.
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$calendar_posts = get_posts(array('post_type'=>'mec_calendars', 'posts_per_page'=>'-1'));
|
||||
$calendars = [];
|
||||
foreach($calendar_posts as $calendar_post) $calendars[$calendar_post->ID] = $calendar_post->post_title;
|
||||
FLBuilder::register_module( 'mecBeaverBuilderShortcode', array(
|
||||
'my-tab-1' => array(
|
||||
'title' => esc_html__( 'Content', 'modern-events-calendar-lite'),
|
||||
'sections' => array(
|
||||
'my-section-1' => array(
|
||||
'title' => esc_html__( 'Select Shortcode', 'modern-events-calendar-lite'),
|
||||
'fields' => array(
|
||||
'mec_shortcode' => array(
|
||||
'type' => 'select',
|
||||
'label' => esc_html__( 'Select Shortcode', 'modern-events-calendar-lite'),
|
||||
'options' => $calendars,
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
) );
|
0
app/addons/tnp/index.html
Executable file
0
app/addons/tnp/index.html
Executable file
1
app/addons/tnp/simple/block-empty.php
Executable file
1
app/addons/tnp/simple/block-empty.php
Executable file
|
@ -0,0 +1 @@
|
|||
<p><?php esc_html_e('Please select an event.', 'modern-events-calendar-lite'); ?></p>
|
54
app/addons/tnp/simple/block-full.php
Executable file
54
app/addons/tnp/simple/block-full.php
Executable file
|
@ -0,0 +1,54 @@
|
|||
<style>
|
||||
.title {
|
||||
font-family: <?php echo $title_style->font_family ?>;
|
||||
font-size: <?php echo $title_style->font_size ?>px;
|
||||
font-weight: <?php echo $title_style->font_weight ?>;
|
||||
color: <?php echo $title_style->font_color ?>;
|
||||
line-height: normal;
|
||||
margin: 0;
|
||||
}
|
||||
.text {
|
||||
font-family: <?php echo $text_style->font_family ?>;
|
||||
font-size: <?php echo $text_style->font_size ?>px;
|
||||
font-weight: <?php echo $text_style->font_weight ?>;
|
||||
color: <?php echo $text_style->font_color ?>;
|
||||
padding: 20px 0 0 0;
|
||||
line-height: 1.5em;
|
||||
margin: 0;
|
||||
}
|
||||
.image {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.button {
|
||||
padding-top: 15px;
|
||||
}
|
||||
</style>
|
||||
<table width="100%" class="responsive" border="0" cellspacing="0" cellpadding="0">
|
||||
<?php if($media): ?>
|
||||
<tr>
|
||||
<td align="center" inline-class="image">
|
||||
<?php echo TNP_Composer::image($media); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
|
||||
<tr>
|
||||
<td align="center" inline-class="title">
|
||||
<?php echo $event->post_title; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php if($event->post_content): ?>
|
||||
<tr>
|
||||
<td align="center" inline-class="text">
|
||||
<?php echo $event->post_content; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
|
||||
<tr>
|
||||
<td align="center" inline-class="button">
|
||||
<?php echo TNP_Composer::button($button_options) ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
47
app/addons/tnp/simple/block-left.php
Executable file
47
app/addons/tnp/simple/block-left.php
Executable file
|
@ -0,0 +1,47 @@
|
|||
<style>
|
||||
.title {
|
||||
<?php $title_style->echo_css(0.8)?>
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
line-height: normal;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.text {
|
||||
<?php $text_style->echo_css()?>
|
||||
padding: 10px 0;
|
||||
line-height: 1.5em;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.button {
|
||||
padding: 10px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<table width="<?php echo $td_width ?>" align="left" class="responsive" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" valign="top">
|
||||
<?php echo $media ? TNP_Composer::image($media) : ''; ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table width="<?php echo $td_width ?>" align="right" class="responsive" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td inline-class="title">
|
||||
<?php echo $event->post_title; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if($event->post_content): ?>
|
||||
<tr>
|
||||
<td inline-class="text">
|
||||
<?php echo $event->post_content; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr>
|
||||
<td align="center" inline-class="button">
|
||||
<?php echo TNP_Composer::button($button_options) ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
51
app/addons/tnp/simple/block-right.php
Executable file
51
app/addons/tnp/simple/block-right.php
Executable file
|
@ -0,0 +1,51 @@
|
|||
<style>
|
||||
.title {
|
||||
<?php $title_style->echo_css(0.8)?>
|
||||
line-height: normal!important;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.text {
|
||||
<?php $text_style->echo_css()?>
|
||||
padding: 10px 0;
|
||||
line-height: 1.5em!important;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
.button {
|
||||
padding: 10px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div dir="rtl">
|
||||
|
||||
<table width="<?php echo $td_width ?>" align="right" class="responsive" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td align="center" valign="top">
|
||||
<?php echo $media ? TNP_Composer::image($media) : ''; ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table width="<?php echo $td_width ?>" align="left" class="responsive" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td inline-class="title" dir="ltr">
|
||||
<?php echo $event->post_title; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if($event->post_content): ?>
|
||||
<tr>
|
||||
<td inline-class="text" dir="ltr">
|
||||
<?php echo $event->post_content; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr>
|
||||
<td align="center" inline-class="button" dir="ltr">
|
||||
<?php echo TNP_Composer::button($button_options) ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
71
app/addons/tnp/simple/block.php
Executable file
71
app/addons/tnp/simple/block.php
Executable file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
/*
|
||||
* Name: Modern Events Calendar
|
||||
* Section: content
|
||||
* Description: Include MEC events into your newsletter
|
||||
*/
|
||||
|
||||
/* @var $options array */
|
||||
|
||||
$default_options = array(
|
||||
'event_id' => NULL,
|
||||
'layout' => 'full',
|
||||
|
||||
// block_ prefixed options are reserved for Newsletter and the ones below managed directly by Newsletter
|
||||
'block_padding_left' => 15,
|
||||
'block_padding_right' => 15,
|
||||
'block_padding_top' => 15,
|
||||
'block_padding_bottom' => 15,
|
||||
'block_background' => '#ffffff',
|
||||
);
|
||||
|
||||
$options = array_merge($default_options, $options);
|
||||
|
||||
/** @var MEC_main $main */
|
||||
$main = MEC_base::getInstance('app.libraries.main');
|
||||
|
||||
$event_id = $options['event_id'];
|
||||
|
||||
$layout = $options['layout'];
|
||||
if(!$event_id) $layout = 'empty';
|
||||
|
||||
$td_width = round((600 - $options['block_padding_left'] - $options['block_padding_right'] - 20)/2);
|
||||
|
||||
$featured_image_id = $event_id ? get_post_thumbnail_id($event_id) : NULL;
|
||||
if($event_id and $featured_image_id)
|
||||
{
|
||||
if($layout === 'full')
|
||||
{
|
||||
$image_width = 600 - $options['block_padding_left'] - $options['block_padding_right'];
|
||||
$media = tnp_resize_2x($featured_image_id, [$image_width, 0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$media = tnp_resize_2x($featured_image_id, [$td_width, 0]);
|
||||
}
|
||||
}
|
||||
else $media = false;
|
||||
|
||||
$title_style = TNP_Composer::get_title_style($options, 'title', $composer);
|
||||
$text_style = TNP_Composer::get_text_style($options, '', $composer);
|
||||
|
||||
$event = $event_id ? get_post($event_id) : NULL;
|
||||
|
||||
$button_options = $options;
|
||||
$button_options['button_url'] = $event_id ? get_permalink($event) : NULL;
|
||||
$button_options['button_label'] = esc_html__('Click Here', 'modern-events-calendar-lite');
|
||||
|
||||
switch ($layout) {
|
||||
case 'left':
|
||||
include __DIR__ . '/block-left.php';
|
||||
return;
|
||||
case 'right':
|
||||
include __DIR__ . '/block-right.php';
|
||||
return;
|
||||
case 'empty':
|
||||
include __DIR__ . '/block-empty.php';
|
||||
return;
|
||||
default:
|
||||
include __DIR__ . '/block-full.php';
|
||||
return;
|
||||
}
|
BIN
app/addons/tnp/simple/icon.png
Executable file
BIN
app/addons/tnp/simple/icon.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
0
app/addons/tnp/simple/index.html
Executable file
0
app/addons/tnp/simple/index.html
Executable file
26
app/addons/tnp/simple/options.php
Executable file
26
app/addons/tnp/simple/options.php
Executable file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/* @var $options array containing all the options of the current block */
|
||||
/* @var $fields NewsletterFields */
|
||||
|
||||
/** @var MEC_main $main */
|
||||
$main = MEC_base::getInstance('app.libraries.main');
|
||||
?>
|
||||
|
||||
<?php
|
||||
$posts = get_posts(array(
|
||||
'post_type' => $main->get_main_post_type(),
|
||||
'posts_per_page' => -1
|
||||
));
|
||||
|
||||
$options = array('' => '-----');
|
||||
foreach($posts as $post) $options['' . $post->ID] = $post->post_title;
|
||||
|
||||
$fields->select('event_id', esc_html__('Event', 'modern-events-calendar-lite'), $options); ?>
|
||||
|
||||
<?php $fields->select('layout', esc_html__('Layout', 'modern-events-calendar-lite'), array(
|
||||
'full' => 'Full',
|
||||
'left' => 'Image left',
|
||||
'right' => 'Image right'
|
||||
)); ?>
|
||||
|
||||
<?php $fields->block_commons();
|
317
app/api/Campaign_Monitor/class/base_classes.php
Executable file
317
app/api/Campaign_Monitor/class/base_classes.php
Executable file
|
@ -0,0 +1,317 @@
|
|||
<?php
|
||||
|
||||
require_once dirname(__FILE__).'/serialisation.php';
|
||||
require_once dirname(__FILE__).'/transport.php';
|
||||
require_once dirname(__FILE__).'/log.php';
|
||||
|
||||
defined('CS_REST_WRAPPER_VERSION') or define('CS_REST_WRAPPER_VERSION', '6.0.1');
|
||||
defined('CS_HOST') or define('CS_HOST', 'api.createsend.com');
|
||||
defined('CS_OAUTH_BASE_URI') or define('CS_OAUTH_BASE_URI', 'https://'.CS_HOST.'/oauth');
|
||||
defined('CS_OAUTH_TOKEN_URI') or define('CS_OAUTH_TOKEN_URI', CS_OAUTH_BASE_URI.'/token');
|
||||
defined('CS_REST_WEBHOOK_FORMAT_JSON') or define('CS_REST_WEBHOOK_FORMAT_JSON', 'json');
|
||||
defined('CS_REST_WEBHOOK_FORMAT_XML') or define('CS_REST_WEBHOOK_FORMAT_XML', 'xml');
|
||||
|
||||
/**
|
||||
* A general result object returned from all Campaign Monitor API calls.
|
||||
* @author tobyb
|
||||
*
|
||||
*/
|
||||
if (!class_exists('CS_REST_Wrapper_Result')) {
|
||||
class CS_REST_Wrapper_Result {
|
||||
/**
|
||||
* The deserialised result of the API call
|
||||
* @var mixed
|
||||
*/
|
||||
var $response;
|
||||
|
||||
/**
|
||||
* The http status code of the API call
|
||||
* @var int
|
||||
*/
|
||||
var $http_status_code;
|
||||
|
||||
function __construct($response, $code) {
|
||||
$this->response = $response;
|
||||
$this->http_status_code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if a call to the api resulted in a successful response.
|
||||
* @return boolean False if the call failed. Check the response property for the failure reason.
|
||||
* @access public
|
||||
*/
|
||||
function was_successful() {
|
||||
return $this->http_status_code >= 200 && $this->http_status_code < 300;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Base class for the create send PHP wrapper.
|
||||
* This class includes functions to access the general data,
|
||||
* i.e timezones, clients and getting your API Key from username and password
|
||||
* @author tobyb
|
||||
*
|
||||
*/
|
||||
if (!class_exists('CS_REST_Wrapper_Base')) {
|
||||
class CS_REST_Wrapper_Base {
|
||||
/**
|
||||
* The protocol to use while accessing the api
|
||||
* @var string http or https
|
||||
* @access private
|
||||
*/
|
||||
var $_protocol;
|
||||
|
||||
/**
|
||||
* The base route of the create send api.
|
||||
* @var string
|
||||
* @access private
|
||||
*/
|
||||
var $_base_route;
|
||||
|
||||
/**
|
||||
* The serialiser to use for serialisation and deserialisation
|
||||
* of API request and response data
|
||||
* @var CS_REST_JsonSerialiser or CS_REST_XmlSerialiser
|
||||
* @access private
|
||||
*/
|
||||
var $_serialiser;
|
||||
|
||||
/**
|
||||
* The transport to use to send API requests
|
||||
* @var CS_REST_CurlTransport or CS_REST_SocketTransport or your own custom transport.
|
||||
* @access private
|
||||
*/
|
||||
var $_transport;
|
||||
|
||||
/**
|
||||
* The logger to use for debugging of all API requests
|
||||
* @var CS_REST_Log
|
||||
* @access private
|
||||
*/
|
||||
var $_log;
|
||||
|
||||
/**
|
||||
* The default options to use for each API request.
|
||||
* These can be overridden by passing in an array as the call_options argument
|
||||
* to a single api request.
|
||||
* Valid options are:
|
||||
*
|
||||
* deserialise boolean:
|
||||
* Set this to false if you want to get the raw response.
|
||||
* This can be useful if your passing json directly to javascript.
|
||||
*
|
||||
* While there are clearly other options there is no need to change them.
|
||||
* @var array
|
||||
* @access private
|
||||
*/
|
||||
var $_default_call_options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param $auth_details array Authentication details to use for API calls.
|
||||
* This array must take one of the following forms:
|
||||
* If using OAuth to authenticate:
|
||||
* array(
|
||||
* 'access_token' => 'your access token',
|
||||
* 'refresh_token' => 'your refresh token')
|
||||
*
|
||||
* Or if using an API key:
|
||||
* array('api_key' => 'your api key')
|
||||
*
|
||||
* Note that this method will continue to work in the deprecated
|
||||
* case when $auth_details is passed in as a string containing an
|
||||
* API key.
|
||||
* @param $protocol string The protocol to use for requests (http|https)
|
||||
* @param $debug_level int The level of debugging required CS_REST_LOG_NONE | CS_REST_LOG_ERROR | CS_REST_LOG_WARNING | CS_REST_LOG_VERBOSE
|
||||
* @param $host string The host to send API requests to. There is no need to change this
|
||||
* @param $log CS_REST_Log The logger to use. Used for dependency injection
|
||||
* @param $serialiser The serialiser to use. Used for dependency injection
|
||||
* @param $transport The transport to use. Used for dependency injection
|
||||
* @access public
|
||||
*/
|
||||
function __construct(
|
||||
$auth_details,
|
||||
$protocol = 'https',
|
||||
$debug_level = CS_REST_LOG_NONE,
|
||||
$host = CS_HOST,
|
||||
$log = NULL,
|
||||
$serialiser = NULL,
|
||||
$transport = NULL) {
|
||||
|
||||
if (is_string($auth_details)) {
|
||||
# If $auth_details is a string, assume it is an API key
|
||||
$auth_details = array('api_key' => $auth_details);
|
||||
}
|
||||
|
||||
$this->_log = is_null($log) ? new CS_REST_Log($debug_level) : $log;
|
||||
|
||||
$this->_protocol = $protocol;
|
||||
$this->_base_route = $protocol.'://'.$host.'/api/v3.2/';
|
||||
|
||||
$this->_log->log_message('Creating wrapper for '.$this->_base_route, get_class($this), CS_REST_LOG_VERBOSE);
|
||||
|
||||
$this->_transport = is_null($transport) ?
|
||||
CS_REST_TRANSPORT_get_available($this->is_secure(), $this->_log) :
|
||||
$transport;
|
||||
|
||||
$transport_type = method_exists($this->_transport, 'get_type') ? $this->_transport->get_type() : 'Unknown';
|
||||
$this->_log->log_message('Using '.$transport_type.' for transport', get_class($this), CS_REST_LOG_WARNING);
|
||||
|
||||
$this->_serialiser = is_null($serialiser) ?
|
||||
CS_REST_SERIALISATION_get_available($this->_log) : $serialiser;
|
||||
|
||||
$this->_log->log_message('Using '.$this->_serialiser->get_type().' json serialising', get_class($this), CS_REST_LOG_WARNING);
|
||||
|
||||
$this->_default_call_options = array (
|
||||
'authdetails' => $auth_details,
|
||||
'userAgent' => 'createsend-php v'.CS_REST_WRAPPER_VERSION.
|
||||
' PHPv'.phpversion().' over '.$transport_type.' with '.$this->_serialiser->get_type(),
|
||||
'contentType' => 'application/json; charset=utf-8',
|
||||
'deserialise' => true,
|
||||
'host' => $host,
|
||||
'protocol' => $protocol
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the current OAuth token using the current refresh token.
|
||||
* @access public
|
||||
*/
|
||||
function refresh_token() {
|
||||
if (!isset($this->_default_call_options['authdetails']) ||
|
||||
!isset($this->_default_call_options['authdetails']['refresh_token'])) {
|
||||
trigger_error(
|
||||
'Error refreshing token. There is no refresh token set on this object.',
|
||||
E_USER_ERROR);
|
||||
return array(NULL, NULL, NULL);
|
||||
}
|
||||
$body = "grant_type=refresh_token&refresh_token=".urlencode(
|
||||
$this->_default_call_options['authdetails']['refresh_token']);
|
||||
$options = array('contentType' => 'application/x-www-form-urlencoded');
|
||||
$wrap = new CS_REST_Wrapper_Base(
|
||||
NULL, 'https', CS_REST_LOG_NONE, CS_HOST, NULL,
|
||||
new CS_REST_DoNothingSerialiser(), NULL);
|
||||
|
||||
$result = $wrap->post_request(CS_OAUTH_TOKEN_URI, $body, $options);
|
||||
if ($result->was_successful()) {
|
||||
$access_token = $result->response->access_token;
|
||||
$expires_in = $result->response->expires_in;
|
||||
$refresh_token = $result->response->refresh_token;
|
||||
$this->_default_call_options['authdetails'] = array(
|
||||
'access_token' => $access_token,
|
||||
'refresh_token' => $refresh_token
|
||||
);
|
||||
return array($access_token, $expires_in, $refresh_token);
|
||||
} else {
|
||||
trigger_error(
|
||||
'Error refreshing token. '.$result->response->error.': '.$result->response->error_description,
|
||||
E_USER_ERROR);
|
||||
return array(NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean True if the wrapper is using SSL.
|
||||
* @access public
|
||||
*/
|
||||
function is_secure() {
|
||||
return $this->_protocol === 'https';
|
||||
}
|
||||
|
||||
function put_request($route, $data, $call_options = array()) {
|
||||
return $this->_call($call_options, CS_REST_PUT, $route, $data);
|
||||
}
|
||||
|
||||
function post_request($route, $data, $call_options = array()) {
|
||||
return $this->_call($call_options, CS_REST_POST, $route, $data);
|
||||
}
|
||||
|
||||
function delete_request($route, $call_options = array()) {
|
||||
return $this->_call($call_options, CS_REST_DELETE, $route);
|
||||
}
|
||||
|
||||
function get_request($route, $include_tracking_pref = NULL, $call_options = array()) {
|
||||
|
||||
if(isset($include_tracking_pref)
|
||||
&& is_bool($include_tracking_pref)) {
|
||||
$route .= '&includeTrackingPreference='.($include_tracking_pref ? "true" : "false");
|
||||
}
|
||||
|
||||
return $this->_call($call_options, CS_REST_GET, $route);
|
||||
}
|
||||
|
||||
function get_request_with_params($route, $params) {
|
||||
if(!is_null($params)) {
|
||||
# http_build_query coerces booleans to 1 and 0, not helpful
|
||||
foreach($params as $key=>$value) {
|
||||
if(is_bool($value)) {
|
||||
$params[$key] = ($value) ? 'true' : 'false';
|
||||
}
|
||||
}
|
||||
$route = $route . '?' . http_build_query($params);
|
||||
}
|
||||
return $this->get_request($route);
|
||||
}
|
||||
|
||||
function get_request_paged($route, $page_number, $page_size, $order_field, $order_direction, $include_tracking_pref = NULL,
|
||||
$join_char = 'deprecated') {
|
||||
// Stores our query values
|
||||
$query = [];
|
||||
// Extract any initial queries in the route into our local query
|
||||
if(strpos($route, '?') !== false) {
|
||||
$parts = parse_url($route);
|
||||
$route = current(explode('?', $route));
|
||||
if(array_key_exists('query', $parts) && !empty($parts['query'])) {
|
||||
parse_str($parts['query'], $query);
|
||||
}
|
||||
}
|
||||
// Now selectively add supplied vars to the query
|
||||
if(!is_null($page_number)) {
|
||||
$query['page'] = $page_number;
|
||||
}
|
||||
if(!is_null($page_size)) {
|
||||
$query['pageSize'] = $page_size;
|
||||
}
|
||||
if(!is_null($order_field)) {
|
||||
$query['orderField'] = $order_field;
|
||||
}
|
||||
if(!is_null($order_direction)) {
|
||||
$query['orderDirection'] = $order_direction;
|
||||
}
|
||||
// If we ended up with a query, add it back to the route
|
||||
if(!empty($query)) {
|
||||
$route .= '?'.http_build_query($query);
|
||||
}
|
||||
return $this->get_request($route, $include_tracking_pref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to make a general API request based on the provided options
|
||||
* @param $call_options
|
||||
* @access private
|
||||
*/
|
||||
function _call($call_options, $method, $route, $data = NULL) {
|
||||
$call_options['route'] = $route;
|
||||
$call_options['method'] = $method;
|
||||
|
||||
if(!is_null($data)) {
|
||||
$call_options['data'] = $this->_serialiser->serialise($data);
|
||||
}
|
||||
|
||||
$call_options = array_merge($this->_default_call_options, $call_options);
|
||||
$this->_log->log_message('Making '.$call_options['method'].' call to: '.$call_options['route'], get_class($this), CS_REST_LOG_WARNING);
|
||||
|
||||
$call_result = $this->_transport->make_call($call_options);
|
||||
|
||||
$this->_log->log_message('Call result: <pre>'.var_export($call_result, true).'</pre>',
|
||||
get_class($this), CS_REST_LOG_VERBOSE);
|
||||
|
||||
if($call_options['deserialise']) {
|
||||
$call_result['response'] = $this->_serialiser->deserialise($call_result['response']);
|
||||
}
|
||||
|
||||
return new CS_REST_Wrapper_Result($call_result['response'], $call_result['code']);
|
||||
}
|
||||
}
|
||||
}
|
3849
app/api/Campaign_Monitor/class/cacert.pem
Executable file
3849
app/api/Campaign_Monitor/class/cacert.pem
Executable file
File diff suppressed because it is too large
Load diff
10
app/api/Campaign_Monitor/class/exceptions.php
Executable file
10
app/api/Campaign_Monitor/class/exceptions.php
Executable file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
if (!class_exists('CurlException')) {
|
||||
class CurlException extends \RuntimeException
|
||||
{
|
||||
public function __construct($curlMessage, $errorCode)
|
||||
{
|
||||
parent::__construct('Error making request with curl_error: ' . $curlMessage, $errorCode);
|
||||
}
|
||||
}
|
||||
}
|
21
app/api/Campaign_Monitor/class/log.php
Executable file
21
app/api/Campaign_Monitor/class/log.php
Executable file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
defined('CS_REST_LOG_VERBOSE') or define('CS_REST_LOG_VERBOSE', 1000);
|
||||
defined('CS_REST_LOG_WARNING') or define('CS_REST_LOG_WARNING', 500);
|
||||
defined('CS_REST_LOG_ERROR') or define('CS_REST_LOG_ERROR', 250);
|
||||
defined('CS_REST_LOG_NONE') or define('CS_REST_LOG_NONE', 0);
|
||||
|
||||
if (!class_exists('CS_REST_Log')) {
|
||||
class CS_REST_Log {
|
||||
var $_level;
|
||||
|
||||
function __construct($level) {
|
||||
$this->_level = $level;
|
||||
}
|
||||
|
||||
function log_message($message, $module, $level) {
|
||||
if($this->_level >= $level) {
|
||||
echo date('G:i:s').' - '.$module.': '.$message."<br />\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
136
app/api/Campaign_Monitor/class/serialisation.php
Executable file
136
app/api/Campaign_Monitor/class/serialisation.php
Executable file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
if(!function_exists("CS_REST_SERIALISATION_get_available")) {
|
||||
function CS_REST_SERIALISATION_get_available($log) {
|
||||
$log->log_message('Getting serialiser', __FUNCTION__, CS_REST_LOG_VERBOSE);
|
||||
if(function_exists('json_decode') && function_exists('json_encode')) {
|
||||
return new CS_REST_NativeJsonSerialiser($log);
|
||||
} else {
|
||||
return new CS_REST_ServicesJsonSerialiser($log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('CS_REST_BaseSerialiser')) {
|
||||
class CS_REST_BaseSerialiser {
|
||||
|
||||
var $_log;
|
||||
|
||||
function __construct($log) {
|
||||
$this->_log = $log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively ensures that all data values are utf-8 encoded.
|
||||
* @param array $data All values of this array are checked for utf-8 encoding.
|
||||
*/
|
||||
function check_encoding($data) {
|
||||
|
||||
foreach($data as $k => $v) {
|
||||
// If the element is a sub-array then recusively encode the array
|
||||
if(is_array($v)) {
|
||||
$data[$k] = $this->check_encoding($v);
|
||||
// Otherwise if the element is a string then we need to check the encoding
|
||||
} else if(is_string($v)) {
|
||||
if((function_exists('mb_detect_encoding') && mb_detect_encoding($v) !== 'UTF-8') ||
|
||||
(function_exists('mb_check_encoding') && !mb_check_encoding($v, 'UTF-8'))) {
|
||||
// The string is using some other encoding, make sure we utf-8 encode
|
||||
$v = utf8_encode($v);
|
||||
}
|
||||
|
||||
$data[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('CS_REST_DoNothingSerialiser')) {
|
||||
class CS_REST_DoNothingSerialiser extends CS_REST_BaseSerialiser {
|
||||
function __construct() {}
|
||||
function get_type() { return 'do_nothing'; }
|
||||
function serialise($data) { return $data; }
|
||||
function deserialise($text) {
|
||||
$data = json_decode($text);
|
||||
return is_null($data) ? $text : $data;
|
||||
}
|
||||
function check_encoding($data) { return $data; }
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('CS_REST_NativeJsonSerialiser')) {
|
||||
class CS_REST_NativeJsonSerialiser extends CS_REST_BaseSerialiser {
|
||||
|
||||
function get_format() {
|
||||
return 'json';
|
||||
}
|
||||
|
||||
function get_type() {
|
||||
return 'native';
|
||||
}
|
||||
|
||||
function serialise($data) {
|
||||
if(is_null($data) || $data == '') return '';
|
||||
return json_encode($this->check_encoding($data));
|
||||
}
|
||||
|
||||
function deserialise($text) {
|
||||
$data = json_decode($text);
|
||||
|
||||
return $this->strip_surrounding_quotes(is_null($data) ? $text : $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* We've had sporadic reports of people getting ID's from create routes with the surrounding quotes present.
|
||||
* There is no case where these should be present. Just get rid of it.
|
||||
*/
|
||||
function strip_surrounding_quotes($data) {
|
||||
if(is_string($data)) {
|
||||
return trim($data, '"');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('CS_REST_ServicesJsonSerialiser')) {
|
||||
class CS_REST_ServicesJsonSerialiser extends CS_REST_BaseSerialiser {
|
||||
|
||||
var $_serialiser;
|
||||
|
||||
function __construct($log) {
|
||||
parent::__construct($log);
|
||||
if (!class_exists('Services_JSON', false)) {
|
||||
require_once dirname(__FILE__).'/services_json.php';
|
||||
}
|
||||
|
||||
$this->_serialiser = new Services_JSON();
|
||||
}
|
||||
|
||||
function get_content_type() {
|
||||
return 'application/json';
|
||||
}
|
||||
|
||||
function get_format() {
|
||||
return 'json';
|
||||
}
|
||||
|
||||
function get_type() {
|
||||
return 'services_json';
|
||||
}
|
||||
|
||||
function serialise($data) {
|
||||
if(is_null($data) || $data == '') return '';
|
||||
return $this->_serialiser->encode($this->check_encoding($data));
|
||||
}
|
||||
|
||||
function deserialise($text) {
|
||||
$data = $this->_serialiser->decode($text);
|
||||
|
||||
return is_null($data) ? $text : $data;
|
||||
}
|
||||
}
|
||||
}
|
774
app/api/Campaign_Monitor/class/services_json.php
Executable file
774
app/api/Campaign_Monitor/class/services_json.php
Executable file
|
@ -0,0 +1,774 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Converts to and from JSON format.
|
||||
*
|
||||
* JSON (JavaScript Object Notation) is a lightweight data-interchange
|
||||
* format. It is easy for humans to read and write. It is easy for machines
|
||||
* to parse and generate. It is based on a subset of the JavaScript
|
||||
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
|
||||
* This feature can also be found in Python. JSON is a text format that is
|
||||
* completely language independent but uses conventions that are familiar
|
||||
* to programmers of the C-family of languages, including C, C++, C#, Java,
|
||||
* JavaScript, Perl, TCL, and many others. These properties make JSON an
|
||||
* ideal data-interchange language.
|
||||
*
|
||||
* This package provides a simple encoder and decoder for JSON notation. It
|
||||
* is intended for use with client-side Javascript applications that make
|
||||
* use of HTTPRequest to perform server communication functions - data can
|
||||
* be encoded into JSON notation for use in a client-side javascript, or
|
||||
* decoded from incoming Javascript requests. JSON format is native to
|
||||
* Javascript, and can be directly eval()'ed with no further parsing
|
||||
* overhead
|
||||
*
|
||||
* All strings should be in ASCII or UTF-8 format!
|
||||
*
|
||||
* LICENSE: Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met: Redistributions of source code must retain the
|
||||
* above copyright notice, this list of conditions and the following
|
||||
* disclaimer. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* @category
|
||||
* @package Services_JSON
|
||||
* @author Michal Migurski <mike-json@teczno.com>
|
||||
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
|
||||
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
|
||||
* @copyright 2005 Michal Migurski
|
||||
* @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php
|
||||
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
defined('SERVICES_JSON_SLICE') or define('SERVICES_JSON_SLICE', 1);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
defined('SERVICES_JSON_IN_STR') or define('SERVICES_JSON_IN_STR', 2);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
defined('SERVICES_JSON_IN_ARR') or define('SERVICES_JSON_IN_ARR', 3);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
defined('SERVICES_JSON_IN_OBJ') or define('SERVICES_JSON_IN_OBJ', 4);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
defined('SERVICES_JSON_IN_CMT') or define('SERVICES_JSON_IN_CMT', 5);
|
||||
|
||||
/**
|
||||
* Behavior switch for Services_JSON::decode()
|
||||
*/
|
||||
defined('SERVICES_JSON_LOOSE_TYPE') or define('SERVICES_JSON_LOOSE_TYPE', 16);
|
||||
|
||||
/**
|
||||
* Behavior switch for Services_JSON::decode()
|
||||
*/
|
||||
defined('SERVICES_JSON_SUPPRESS_ERRORS') or define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
|
||||
|
||||
/**
|
||||
* Converts to and from JSON format.
|
||||
*
|
||||
* Brief example of use:
|
||||
*
|
||||
* <code>
|
||||
* // create a new instance of Services_JSON
|
||||
* $json = new Services_JSON();
|
||||
*
|
||||
* // convert a complexe value to JSON notation, and send it to the browser
|
||||
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
|
||||
* $output = $json->encode($value);
|
||||
*
|
||||
* print($output);
|
||||
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
|
||||
*
|
||||
* // accept incoming POST data, assumed to be in JSON notation
|
||||
* $input = file_get_contents('php://input', 1000000);
|
||||
* $value = $json->decode($input);
|
||||
* </code>
|
||||
*/
|
||||
if (!class_exists('Services_JSON')) {
|
||||
class Services_JSON
|
||||
{
|
||||
/**
|
||||
* constructs a new JSON instance
|
||||
*
|
||||
* @param int $use object behavior flags; combine with boolean-OR
|
||||
*
|
||||
* possible values:
|
||||
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
|
||||
* "{...}" syntax creates associative arrays
|
||||
* instead of objects in decode().
|
||||
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
|
||||
* Values which can't be encoded (e.g. resources)
|
||||
* appear as NULL instead of throwing errors.
|
||||
* By default, a deeply-nested resource will
|
||||
* bubble up with an error, so all return values
|
||||
* from encode() should be checked with isError()
|
||||
*/
|
||||
function __construct($use = 0)
|
||||
{
|
||||
$this->use = $use;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a string from one UTF-16 char to one UTF-8 char
|
||||
*
|
||||
* Normally should be handled by mb_convert_encoding, but
|
||||
* provides a slower PHP-only method for installations
|
||||
* that lack the multibye string extension.
|
||||
*
|
||||
* @param string $utf16 UTF-16 character
|
||||
* @return string UTF-8 character
|
||||
* @access private
|
||||
*/
|
||||
function utf162utf8($utf16)
|
||||
{
|
||||
// oh please oh please oh please oh please oh please
|
||||
if(function_exists('mb_convert_encoding')) {
|
||||
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
|
||||
}
|
||||
|
||||
$bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);
|
||||
|
||||
switch(true) {
|
||||
case ((0x7F & $bytes) == $bytes):
|
||||
// this case should never be reached, because we are in ASCII range
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0x7F & $bytes);
|
||||
|
||||
case (0x07FF & $bytes) == $bytes:
|
||||
// return a 2-byte UTF-8 character
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0xC0 | (($bytes >> 6) & 0x1F))
|
||||
. chr(0x80 | ($bytes & 0x3F));
|
||||
|
||||
case (0xFFFF & $bytes) == $bytes:
|
||||
// return a 3-byte UTF-8 character
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0xE0 | (($bytes >> 12) & 0x0F))
|
||||
. chr(0x80 | (($bytes >> 6) & 0x3F))
|
||||
. chr(0x80 | ($bytes & 0x3F));
|
||||
}
|
||||
|
||||
// ignoring UTF-32 for now, sorry
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a string from one UTF-8 char to one UTF-16 char
|
||||
*
|
||||
* Normally should be handled by mb_convert_encoding, but
|
||||
* provides a slower PHP-only method for installations
|
||||
* that lack the multibye string extension.
|
||||
*
|
||||
* @param string $utf8 UTF-8 character
|
||||
* @return string UTF-16 character
|
||||
* @access private
|
||||
*/
|
||||
function utf82utf16($utf8)
|
||||
{
|
||||
// oh please oh please oh please oh please oh please
|
||||
if(function_exists('mb_convert_encoding')) {
|
||||
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
|
||||
}
|
||||
|
||||
switch(strlen($utf8)) {
|
||||
case 1:
|
||||
// this case should never be reached, because we are in ASCII range
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return $utf8;
|
||||
|
||||
case 2:
|
||||
// return a UTF-16 character from a 2-byte UTF-8 char
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0x07 & (ord($utf8[0]) >> 2))
|
||||
. chr((0xC0 & (ord($utf8[0]) << 6))
|
||||
| (0x3F & ord($utf8[1])));
|
||||
|
||||
case 3:
|
||||
// return a UTF-16 character from a 3-byte UTF-8 char
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr((0xF0 & (ord($utf8[0]) << 4))
|
||||
| (0x0F & (ord($utf8[1]) >> 2)))
|
||||
. chr((0xC0 & (ord($utf8[1]) << 6))
|
||||
| (0x7F & ord($utf8[2])));
|
||||
}
|
||||
|
||||
// ignoring UTF-32 for now, sorry
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* encodes an arbitrary variable into JSON format
|
||||
*
|
||||
* @param mixed $var any number, boolean, string, array, or object to be encoded.
|
||||
* see argument 1 to Services_JSON() above for array-parsing behavior.
|
||||
* if var is a strng, note that encode() always expects it
|
||||
* to be in ASCII or UTF-8 format!
|
||||
*
|
||||
* @return mixed JSON string representation of input var or an error if a problem occurs
|
||||
* @access public
|
||||
*/
|
||||
function encode($var)
|
||||
{
|
||||
switch (gettype($var)) {
|
||||
case 'boolean':
|
||||
return $var ? 'true' : 'false';
|
||||
|
||||
case 'NULL':
|
||||
return 'null';
|
||||
|
||||
case 'integer':
|
||||
return (int) $var;
|
||||
|
||||
case 'double':
|
||||
case 'float':
|
||||
return (float) $var;
|
||||
|
||||
case 'string':
|
||||
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
|
||||
$ascii = '';
|
||||
$strlen_var = strlen($var);
|
||||
|
||||
/*
|
||||
* Iterate over every character in the string,
|
||||
* escaping with a slash or encoding to UTF-8 where necessary
|
||||
*/
|
||||
for ($c = 0; $c < $strlen_var; ++$c) {
|
||||
|
||||
$ord_var_c = ord($var[$c]);
|
||||
|
||||
switch (true) {
|
||||
case $ord_var_c == 0x08:
|
||||
$ascii .= '\b';
|
||||
break;
|
||||
case $ord_var_c == 0x09:
|
||||
$ascii .= '\t';
|
||||
break;
|
||||
case $ord_var_c == 0x0A:
|
||||
$ascii .= '\n';
|
||||
break;
|
||||
case $ord_var_c == 0x0C:
|
||||
$ascii .= '\f';
|
||||
break;
|
||||
case $ord_var_c == 0x0D:
|
||||
$ascii .= '\r';
|
||||
break;
|
||||
|
||||
case $ord_var_c == 0x22:
|
||||
case $ord_var_c == 0x2F:
|
||||
case $ord_var_c == 0x5C:
|
||||
// double quote, slash, slosh
|
||||
$ascii .= '\\'.$var[$c];
|
||||
break;
|
||||
|
||||
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
|
||||
// characters U-00000000 - U-0000007F (same as ASCII)
|
||||
$ascii .= $var[$c];
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xE0) == 0xC0):
|
||||
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c, ord($var[$c + 1]));
|
||||
$c += 1;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xF0) == 0xE0):
|
||||
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var[$c + 1]),
|
||||
ord($var[$c + 2]));
|
||||
$c += 2;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xF8) == 0xF0):
|
||||
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var[$c + 1]),
|
||||
ord($var[$c + 2]),
|
||||
ord($var[$c + 3]));
|
||||
$c += 3;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xFC) == 0xF8):
|
||||
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var[$c + 1]),
|
||||
ord($var[$c + 2]),
|
||||
ord($var[$c + 3]),
|
||||
ord($var[$c + 4]));
|
||||
$c += 4;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xFE) == 0xFC):
|
||||
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var[$c + 1]),
|
||||
ord($var[$c + 2]),
|
||||
ord($var[$c + 3]),
|
||||
ord($var[$c + 4]),
|
||||
ord($var[$c + 5]));
|
||||
$c += 5;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return '"'.$ascii.'"';
|
||||
|
||||
case 'array':
|
||||
/*
|
||||
* As per JSON spec if any array key is not an integer
|
||||
* we must treat the the whole array as an object. We
|
||||
* also try to catch a sparsely populated associative
|
||||
* array with numeric keys here because some JS engines
|
||||
* will create an array with empty indexes up to
|
||||
* max_index which can cause memory issues and because
|
||||
* the keys, which may be relevant, will be remapped
|
||||
* otherwise.
|
||||
*
|
||||
* As per the ECMA and JSON specification an object may
|
||||
* have any string as a property. Unfortunately due to
|
||||
* a hole in the ECMA specification if the key is a
|
||||
* ECMA reserved word or starts with a digit the
|
||||
* parameter is only accessible using ECMAScript's
|
||||
* bracket notation.
|
||||
*/
|
||||
|
||||
// treat as a JSON object
|
||||
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
|
||||
$properties = array_map(array($this, 'name_value'),
|
||||
array_keys($var),
|
||||
array_values($var));
|
||||
|
||||
foreach($properties as $property) {
|
||||
if($this->isError($property)) {
|
||||
return $property;
|
||||
}
|
||||
}
|
||||
|
||||
return '{' . join(',', $properties) . '}';
|
||||
}
|
||||
|
||||
// treat it like a regular array
|
||||
$elements = array_map(array($this, 'encode'), $var);
|
||||
|
||||
foreach($elements as $element) {
|
||||
if($this->isError($element)) {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
return '[' . join(',', $elements) . ']';
|
||||
|
||||
case 'object':
|
||||
$vars = get_object_vars($var);
|
||||
|
||||
$properties = array_map(array($this, 'name_value'),
|
||||
array_keys($vars),
|
||||
array_values($vars));
|
||||
|
||||
foreach($properties as $property) {
|
||||
if($this->isError($property)) {
|
||||
return $property;
|
||||
}
|
||||
}
|
||||
|
||||
return '{' . join(',', $properties) . '}';
|
||||
|
||||
default:
|
||||
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
|
||||
? 'null'
|
||||
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* array-walking function for use in generating JSON-formatted name-value pairs
|
||||
*
|
||||
* @param string $name name of key to use
|
||||
* @param mixed $value reference to an array element to be encoded
|
||||
*
|
||||
* @return string JSON-formatted name-value pair, like '"name":value'
|
||||
* @access private
|
||||
*/
|
||||
function name_value($name, $value)
|
||||
{
|
||||
$encoded_value = $this->encode($value);
|
||||
|
||||
if($this->isError($encoded_value)) {
|
||||
return $encoded_value;
|
||||
}
|
||||
|
||||
return $this->encode(strval($name)) . ':' . $encoded_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* reduce a string by removing leading and trailing comments and whitespace
|
||||
*
|
||||
* @param $str string string value to strip of comments and whitespace
|
||||
*
|
||||
* @return string string value stripped of comments and whitespace
|
||||
* @access private
|
||||
*/
|
||||
function reduce_string($str)
|
||||
{
|
||||
$str = preg_replace(array(
|
||||
|
||||
// eliminate single line comments in '// ...' form
|
||||
'#^\s*//(.+)$#m',
|
||||
|
||||
// eliminate multi-line comments in '/* ... */' form, at start of string
|
||||
'#^\s*/\*(.+)\*/#Us',
|
||||
|
||||
// eliminate multi-line comments in '/* ... */' form, at end of string
|
||||
'#/\*(.+)\*/\s*$#Us'
|
||||
|
||||
), '', $str);
|
||||
|
||||
// eliminate extraneous space
|
||||
return trim($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* decodes a JSON string into appropriate variable
|
||||
*
|
||||
* @param string $str JSON-formatted string
|
||||
*
|
||||
* @return mixed number, boolean, string, array, or object
|
||||
* corresponding to given JSON input string.
|
||||
* See argument 1 to Services_JSON() above for object-output behavior.
|
||||
* Note that decode() always returns strings
|
||||
* in ASCII or UTF-8 format!
|
||||
* @access public
|
||||
*/
|
||||
function decode($str)
|
||||
{
|
||||
$str = $this->reduce_string($str);
|
||||
|
||||
switch (strtolower($str)) {
|
||||
case 'true':
|
||||
return true;
|
||||
|
||||
case 'false':
|
||||
return false;
|
||||
|
||||
case 'null':
|
||||
return null;
|
||||
|
||||
default:
|
||||
$m = [];
|
||||
|
||||
if (is_numeric($str)) {
|
||||
// Lookie-loo, it's a number
|
||||
|
||||
// This would work on its own, but I'm trying to be
|
||||
// good about returning integers where appropriate:
|
||||
// return (float)$str;
|
||||
|
||||
// Return float or int, as appropriate
|
||||
return ((float)$str == (integer)$str)
|
||||
? (integer)$str
|
||||
: (float)$str;
|
||||
|
||||
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
|
||||
// STRINGS RETURNED IN UTF-8 FORMAT
|
||||
$delim = substr($str, 0, 1);
|
||||
$chrs = substr($str, 1, -1);
|
||||
$utf8 = '';
|
||||
$strlen_chrs = strlen($chrs);
|
||||
|
||||
for ($c = 0; $c < $strlen_chrs; ++$c) {
|
||||
|
||||
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
||||
$ord_chrs_c = ord($chrs[$c]);
|
||||
|
||||
switch (true) {
|
||||
case $substr_chrs_c_2 == '\b':
|
||||
$utf8 .= chr(0x08);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\t':
|
||||
$utf8 .= chr(0x09);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\n':
|
||||
$utf8 .= chr(0x0A);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\f':
|
||||
$utf8 .= chr(0x0C);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\r':
|
||||
$utf8 .= chr(0x0D);
|
||||
++$c;
|
||||
break;
|
||||
|
||||
case $substr_chrs_c_2 == '\\"':
|
||||
case $substr_chrs_c_2 == '\\\'':
|
||||
case $substr_chrs_c_2 == '\\\\':
|
||||
case $substr_chrs_c_2 == '\\/':
|
||||
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
|
||||
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
|
||||
$utf8 .= $chrs[++$c];
|
||||
}
|
||||
break;
|
||||
|
||||
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
|
||||
// single, escaped unicode character
|
||||
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
|
||||
. chr(hexdec(substr($chrs, ($c + 4), 2)));
|
||||
$utf8 .= $this->utf162utf8($utf16);
|
||||
$c += 5;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
|
||||
$utf8 .= $chrs[$c];
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xE0) == 0xC0:
|
||||
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
||||
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 2);
|
||||
++$c;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xF0) == 0xE0:
|
||||
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 3);
|
||||
$c += 2;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xF8) == 0xF0:
|
||||
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 4);
|
||||
$c += 3;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xFC) == 0xF8:
|
||||
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 5);
|
||||
$c += 4;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xFE) == 0xFC:
|
||||
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 6);
|
||||
$c += 5;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $utf8;
|
||||
|
||||
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
|
||||
// array, or object notation
|
||||
|
||||
if ($str[0] == '[') {
|
||||
$stk = array(SERVICES_JSON_IN_ARR);
|
||||
$arr = [];
|
||||
} else {
|
||||
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
||||
$stk = array(SERVICES_JSON_IN_OBJ);
|
||||
$obj = [];
|
||||
} else {
|
||||
$stk = array(SERVICES_JSON_IN_OBJ);
|
||||
$obj = new stdClass();
|
||||
}
|
||||
}
|
||||
|
||||
array_push($stk, array('what' => SERVICES_JSON_SLICE,
|
||||
'where' => 0,
|
||||
'delim' => false));
|
||||
|
||||
$chrs = substr($str, 1, -1);
|
||||
$chrs = $this->reduce_string($chrs);
|
||||
|
||||
if ($chrs == '') {
|
||||
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
||||
return $arr;
|
||||
|
||||
} else {
|
||||
return $obj;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$strlen_chrs = strlen($chrs);
|
||||
|
||||
for ($c = 0; $c <= $strlen_chrs; ++$c) {
|
||||
|
||||
$top = end($stk);
|
||||
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
||||
|
||||
if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
|
||||
// found a comma that is not inside a string, array, etc.,
|
||||
// OR we've reached the end of the character list
|
||||
$slice = substr($chrs, $top['where'], ($c - $top['where']));
|
||||
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
|
||||
|
||||
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
||||
// we are in an array, so just push an element onto the stack
|
||||
array_push($arr, $this->decode($slice));
|
||||
|
||||
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
||||
// we are in an object, so figure
|
||||
// out the property name and set an
|
||||
// element in an associative array,
|
||||
// for now
|
||||
$parts = [];
|
||||
|
||||
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
||||
// "name":value pair
|
||||
$key = $this->decode($parts[1]);
|
||||
$val = $this->decode($parts[2]);
|
||||
|
||||
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
||||
$obj[$key] = $val;
|
||||
} else {
|
||||
$obj->$key = $val;
|
||||
}
|
||||
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
||||
// name:value pair, where name is unquoted
|
||||
$key = $parts[1];
|
||||
$val = $this->decode($parts[2]);
|
||||
|
||||
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
||||
$obj[$key] = $val;
|
||||
} else {
|
||||
$obj->$key = $val;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
|
||||
// found a quote, and we are not inside a string
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c]));
|
||||
|
||||
} elseif (($chrs[$c] == $top['delim']) &&
|
||||
($top['what'] == SERVICES_JSON_IN_STR) &&
|
||||
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
|
||||
// found a quote, we're in a string, and it's not escaped
|
||||
// we know that it's not escaped becase there is _not_ an
|
||||
// odd number of backslashes at the end of the string so far
|
||||
array_pop($stk);
|
||||
|
||||
} elseif (($chrs[$c] == '[') &&
|
||||
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
||||
// found a left-bracket, and we are in an array, object, or slice
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
|
||||
|
||||
} elseif (($chrs[$c] == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
|
||||
// found a right-bracket, and we're in an array
|
||||
array_pop($stk);
|
||||
|
||||
} elseif (($chrs[$c] == '{') &&
|
||||
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
||||
// found a left-brace, and we are in an array, object, or slice
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
|
||||
|
||||
} elseif (($chrs[$c] == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
|
||||
// found a right-brace, and we're in an object
|
||||
array_pop($stk);
|
||||
|
||||
} elseif (($substr_chrs_c_2 == '/*') &&
|
||||
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
||||
// found a comment start, and we are in an array, object, or slice
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
|
||||
$c++;
|
||||
|
||||
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
|
||||
// found a comment end, and we're in one now
|
||||
array_pop($stk);
|
||||
$c++;
|
||||
|
||||
for ($i = $top['where']; $i <= $c; ++$i)
|
||||
$chrs = substr_replace($chrs, ' ', $i, 1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
||||
return $arr;
|
||||
|
||||
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
||||
return $obj;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isError($data, $code = null)
|
||||
{
|
||||
if (is_object($data) && (get_class($data) == 'services_json_error' ||
|
||||
is_subclass_of($data, 'services_json_error'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('Services_JSON_Error')) {
|
||||
class Services_JSON_Error
|
||||
{
|
||||
function __construct($message = 'unknown error', $code = null,
|
||||
$mode = null, $options = null, $userinfo = null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
352
app/api/Campaign_Monitor/class/transport.php
Executable file
352
app/api/Campaign_Monitor/class/transport.php
Executable file
|
@ -0,0 +1,352 @@
|
|||
<?php
|
||||
|
||||
defined('CS_REST_GET') or define('CS_REST_GET', 'GET');
|
||||
defined('CS_REST_POST') or define('CS_REST_POST', 'POST');
|
||||
defined('CS_REST_PUT') or define('CS_REST_PUT', 'PUT');
|
||||
defined('CS_REST_DELETE') or define('CS_REST_DELETE', 'DELETE');
|
||||
if (false === defined('CS_REST_SOCKET_TIMEOUT')) {
|
||||
define('CS_REST_SOCKET_TIMEOUT', 20);
|
||||
}
|
||||
if (false === defined('CS_REST_CALL_TIMEOUT')) {
|
||||
define('CS_REST_CALL_TIMEOUT', 20);
|
||||
}
|
||||
|
||||
if(!function_exists("CS_REST_TRANSPORT_get_available")) {
|
||||
function CS_REST_TRANSPORT_get_available($requires_ssl, $log) {
|
||||
if(function_exists('curl_init') && function_exists('curl_exec')) {
|
||||
return new CS_REST_CurlTransport($log);
|
||||
} else if(CS_REST_TRANSPORT_can_use_raw_socket($requires_ssl)) {
|
||||
return new CS_REST_SocketTransport($log);
|
||||
} else {
|
||||
$log->log_message('No transport is available', __FUNCTION__, CS_REST_LOG_ERROR);
|
||||
trigger_error('No transport is available.'.
|
||||
($requires_ssl ? ' Try using non-secure (http) mode or ' : ' Please ').
|
||||
'ensure the cURL extension is loaded', E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!function_exists("CS_REST_TRANSPORT_can_use_raw_socket")) {
|
||||
function CS_REST_TRANSPORT_can_use_raw_socket($requires_ssl) {
|
||||
if(function_exists('fsockopen')) {
|
||||
if($requires_ssl) {
|
||||
return extension_loaded('openssl');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('CS_REST_BaseTransport')) {
|
||||
class CS_REST_BaseTransport {
|
||||
|
||||
var $_log;
|
||||
|
||||
function __construct($log) {
|
||||
$this->_log = $log;
|
||||
}
|
||||
|
||||
function split_and_inflate($response, $may_be_compressed) {
|
||||
$ra = explode("\r\n\r\n", $response);
|
||||
|
||||
$result = array_pop($ra);
|
||||
$headers = array_pop($ra);
|
||||
|
||||
if($may_be_compressed && preg_match('/^Content-Encoding:\s+gzip\s+$/im', $headers)) {
|
||||
$original_length = strlen($response);
|
||||
$result = gzinflate(substr($result, 10, -8));
|
||||
|
||||
$this->_log->log_message('Inflated gzipped response: '.$original_length.' bytes ->'.
|
||||
strlen($result).' bytes', get_class(), CS_REST_LOG_VERBOSE);
|
||||
}
|
||||
|
||||
return array($headers, $result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Provide HTTP request functionality via cURL extensions
|
||||
*
|
||||
* @author tobyb
|
||||
* @since 1.0
|
||||
*/
|
||||
if (!class_exists('CS_REST_CurlTransport')) {
|
||||
class CS_REST_CurlTransport extends CS_REST_BaseTransport {
|
||||
|
||||
var $_curl_zlib;
|
||||
|
||||
function __construct($log) {
|
||||
parent::__construct($log);
|
||||
|
||||
$curl_version = curl_version();
|
||||
$this->_curl_zlib = isset($curl_version['libz_version']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The type of transport used
|
||||
*/
|
||||
function get_type() {
|
||||
return 'cURL';
|
||||
}
|
||||
|
||||
function make_call($call_options) {
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $call_options['route']);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
$headers = [];
|
||||
$headers[] = 'Content-Type: '.$call_options['contentType'];
|
||||
|
||||
|
||||
if (array_key_exists('authdetails', $call_options) &&
|
||||
isset($call_options['authdetails'])) {
|
||||
if (array_key_exists('username', $call_options['authdetails']) &&
|
||||
array_key_exists('password', $call_options['authdetails'])) {
|
||||
# Authenticating using basic auth for retrieving user's API key.
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $call_options['authdetails']['username'].':'.$call_options['authdetails']['password']);
|
||||
} elseif (array_key_exists('access_token', $call_options['authdetails'])) {
|
||||
# Authenticating using OAuth.
|
||||
$access_token = $call_options['authdetails']['access_token'];
|
||||
$headers[] = 'Authorization: Bearer '.$access_token;
|
||||
} elseif (array_key_exists('api_key', $call_options['authdetails'])) {
|
||||
# Authenticating using an API key.
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
$api_key = $call_options['authdetails']['api_key'];
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $api_key.':nopass');
|
||||
}
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, $call_options['userAgent']);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, CS_REST_SOCKET_TIMEOUT);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, CS_REST_CALL_TIMEOUT);
|
||||
|
||||
$inflate_response = false;
|
||||
if($this->_curl_zlib) {
|
||||
$this->_log->log_message('curl+zlib support available. Requesting gzipped response.',
|
||||
get_class($this), CS_REST_LOG_VERBOSE);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
|
||||
} else if(function_exists('gzinflate')) {
|
||||
$headers[] = 'Accept-Encoding: gzip';
|
||||
$inflate_response = true;
|
||||
}
|
||||
|
||||
if($call_options['protocol'] === 'https') {
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
|
||||
if(strlen(ini_get('curl.cainfo')) === 0) {
|
||||
curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__).'/cacert.pem');
|
||||
}
|
||||
}
|
||||
|
||||
switch($call_options['method']) {
|
||||
case CS_REST_PUT:
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, CS_REST_PUT);
|
||||
$headers[] = 'Content-Length: '.strlen($call_options['data']);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $call_options['data']);
|
||||
break;
|
||||
case CS_REST_POST:
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, isset($call_options['data']) ? $call_options['data'] : '');
|
||||
break;
|
||||
case CS_REST_DELETE:
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, CS_REST_DELETE);
|
||||
break;
|
||||
}
|
||||
|
||||
if(count($headers) > 0) {
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if(!$response && $response !== '') {
|
||||
$this->_log->log_message('Error making request with curl_error: '.curl_errno($ch),
|
||||
get_class($this), CS_REST_LOG_ERROR);
|
||||
|
||||
require_once dirname(__FILE__).'/exceptions.php';
|
||||
throw new CurlException(curl_error($ch), curl_errno($ch));
|
||||
}
|
||||
|
||||
list( $headers, $result ) = $this->split_and_inflate($response, $inflate_response);
|
||||
|
||||
$this->_log->log_message('API Call Info for '.$call_options['method'].' '.
|
||||
curl_getinfo($ch, CURLINFO_EFFECTIVE_URL).': '.curl_getinfo($ch, CURLINFO_SIZE_UPLOAD).
|
||||
' bytes uploaded. '.curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD).' bytes downloaded'.
|
||||
' Total time (seconds): '.curl_getinfo($ch, CURLINFO_TOTAL_TIME),
|
||||
get_class($this), CS_REST_LOG_VERBOSE);
|
||||
|
||||
$result = array(
|
||||
'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
|
||||
'response' => $result
|
||||
);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('CS_REST_SocketWrapper')) {
|
||||
class CS_REST_SocketWrapper {
|
||||
var $socket;
|
||||
|
||||
function open($domain, $port) {
|
||||
$this->socket = fsockopen($domain, $port, $errno, $errstr, CS_REST_SOCKET_TIMEOUT);
|
||||
|
||||
if(!$this->socket) {
|
||||
die('Error making request with '.$errno.': '.$errstr);
|
||||
return false;
|
||||
} else if(function_exists('stream_set_timeout')) {
|
||||
stream_set_timeout($this->socket, CS_REST_SOCKET_TIMEOUT);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function write($data) {
|
||||
fwrite($this->socket, $data);
|
||||
}
|
||||
|
||||
function read() {
|
||||
ob_start();
|
||||
fpassthru($this->socket);
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
function close() {
|
||||
fclose($this->socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('CS_REST_SocketTransport')) {
|
||||
class CS_REST_SocketTransport extends CS_REST_BaseTransport {
|
||||
|
||||
var $_socket_wrapper;
|
||||
|
||||
function __construct($log, $socket_wrapper = NULL) {
|
||||
parent::__construct($log);
|
||||
|
||||
if(is_null($socket_wrapper)) {
|
||||
$socket_wrapper = new CS_REST_SocketWrapper();
|
||||
}
|
||||
|
||||
$this->_socket_wrapper = $socket_wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The type of transport used
|
||||
*/
|
||||
function get_type() {
|
||||
return 'Socket';
|
||||
}
|
||||
|
||||
function make_call($call_options) {
|
||||
$start_host = strpos($call_options['route'], $call_options['host']);
|
||||
$host_len = strlen($call_options['host']);
|
||||
|
||||
$domain = substr($call_options['route'], $start_host, $host_len);
|
||||
$host = $domain;
|
||||
$path = substr($call_options['route'], $start_host + $host_len);
|
||||
$protocol = substr($call_options['route'], 0, $start_host);
|
||||
$port = 80;
|
||||
|
||||
$this->_log->log_message('Creating socket to '.$domain.' over '.$protocol.' for request to '.$path,
|
||||
get_class($this), CS_REST_LOG_VERBOSE);
|
||||
|
||||
if($protocol === 'https://') {
|
||||
$domain = 'ssl://'.$domain;
|
||||
$port = 443;
|
||||
}
|
||||
|
||||
if($this->_socket_wrapper->open($domain, $port)) {
|
||||
$inflate_response = function_exists('gzinflate');
|
||||
|
||||
$request = $this->_build_request($call_options, $host, $path, $inflate_response);
|
||||
$this->_log->log_message('Sending <pre>'.$request.'</pre> down the socket',
|
||||
get_class($this), CS_REST_LOG_VERBOSE);
|
||||
|
||||
$this->_socket_wrapper->write($request);
|
||||
$response = $this->_socket_wrapper->read();
|
||||
$this->_socket_wrapper->close();
|
||||
|
||||
$this->_log->log_message('API Call Info for '.$call_options['method'].' '.
|
||||
$call_options['route'].': '.strlen($request).
|
||||
' bytes uploaded. '.strlen($response).' bytes downloaded',
|
||||
get_class($this), CS_REST_LOG_VERBOSE);
|
||||
|
||||
list( $headers, $result ) = $this->split_and_inflate($response, $inflate_response);
|
||||
|
||||
$this->_log->log_message('Received headers <pre>'.$headers.'</pre>',
|
||||
get_class($this), CS_REST_LOG_VERBOSE);
|
||||
|
||||
return array(
|
||||
'code' => $this->_get_status_code($headers),
|
||||
'response' => trim($result)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function _get_status_code($headers) {
|
||||
if (preg_match('%^\s*HTTP/1\.1 (?P<code>\d{3})%', $headers, $regs)) {
|
||||
$this->_log->log_message('Got HTTP Status Code: '.$regs['code'],
|
||||
get_class($this), CS_REST_LOG_VERBOSE);
|
||||
return $regs['code'];
|
||||
}
|
||||
|
||||
$this->_log->log_message('Failed to get HTTP status code from request headers <pre>'.$headers.'</pre>',
|
||||
get_class($this), CS_REST_LOG_ERROR);
|
||||
trigger_error('Failed to get HTTP status code from request', E_USER_ERROR);
|
||||
}
|
||||
|
||||
function _build_request($call_options, $host, $path, $accept_gzip) {
|
||||
$request_auth_details = '';
|
||||
|
||||
if (array_key_exists('authdetails', $call_options)) {
|
||||
if (array_key_exists('username', $call_options['authdetails']) &&
|
||||
array_key_exists('password', $call_options['authdetails'])) {
|
||||
# Authenticating using basic auth for retrieving user's API key.
|
||||
$request_auth_details .= 'Authorization: Basic '.base64_encode($call_options['authdetails']['username'].':'.$call_options['authdetails']['password'])."\n";
|
||||
} elseif (array_key_exists('access_token', $call_options['authdetails'])) {
|
||||
# Authenticating using OAuth.
|
||||
$access_token = $call_options['authdetails']['access_token'];
|
||||
$request_auth_details .= 'Authorization: Bearer '.$access_token."\n";
|
||||
} elseif (array_key_exists('api_key', $call_options['authdetails'])) {
|
||||
# Authenticating using an API key.
|
||||
$api_key = $call_options['authdetails']['api_key'];
|
||||
$request_auth_details .= 'Authorization: Basic '.base64_encode($api_key.':nopass')."\n";
|
||||
}
|
||||
}
|
||||
|
||||
$request =
|
||||
$call_options['method'].' '.$path." HTTP/1.1\n".
|
||||
'Host: '.$host."\n".
|
||||
$request_auth_details.
|
||||
'User-Agent: '.$call_options['userAgent']."\n".
|
||||
"Connection: Close\n".
|
||||
'Content-Type: '.$call_options['contentType']."\n";
|
||||
|
||||
if($accept_gzip) {
|
||||
$request .=
|
||||
"Accept-Encoding: gzip\n";
|
||||
}
|
||||
|
||||
if(isset($call_options['data'])) {
|
||||
$request .=
|
||||
'Content-Length: '.strlen($call_options['data'])."\n\n".
|
||||
$call_options['data'];
|
||||
}
|
||||
|
||||
return $request."\n\n";
|
||||
}
|
||||
}
|
||||
}
|
235
app/api/Campaign_Monitor/csrest_subscribers.php
Executable file
235
app/api/Campaign_Monitor/csrest_subscribers.php
Executable file
|
@ -0,0 +1,235 @@
|
|||
<?php
|
||||
require_once dirname(__FILE__).'/class/base_classes.php';
|
||||
|
||||
/**
|
||||
* Class to access a subscribers resources from the create send API.
|
||||
* This class includes functions to add and remove subscribers ,
|
||||
* along with accessing statistics for a single subscriber
|
||||
* @author tobyb
|
||||
*
|
||||
*/
|
||||
if (!class_exists('CS_REST_Subscribers')) {
|
||||
class CS_REST_Subscribers extends CS_REST_Wrapper_Base {
|
||||
|
||||
/**
|
||||
* The base route of the subscriber resource.
|
||||
* @var string
|
||||
* @access private
|
||||
*/
|
||||
var $_subscribers_base_route;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param $list_id string The list id to access (Ignored for create requests)
|
||||
* @param $auth_details array Authentication details to use for API calls.
|
||||
* This array must take one of the following forms:
|
||||
* If using OAuth to authenticate:
|
||||
* array(
|
||||
* 'access_token' => 'your access token',
|
||||
* 'refresh_token' => 'your refresh token')
|
||||
*
|
||||
* Or if using an API key:
|
||||
* array('api_key' => 'your api key')
|
||||
* @param string $protocol The protocol to use for requests (http|https)
|
||||
* @param int $debug_level The level of debugging required CS_REST_LOG_NONE | CS_REST_LOG_ERROR | CS_REST_LOG_WARNING | CS_REST_LOG_VERBOSE
|
||||
* @param string $host The host to send API requests to. There is no need to change this
|
||||
* @param CS_REST_Log $log The logger to use. Used for dependency injection
|
||||
* @param object|null $serialiser The serialiser to use. Used for dependency injection
|
||||
* @param object|null $transport The transport to use. Used for dependency injection
|
||||
* @access public
|
||||
*/
|
||||
function __construct (
|
||||
$list_id,
|
||||
$auth_details,
|
||||
$protocol = 'https',
|
||||
$debug_level = CS_REST_LOG_NONE,
|
||||
$host = 'api.createsend.com',
|
||||
$log = NULL,
|
||||
$serialiser = NULL,
|
||||
$transport = NULL) {
|
||||
|
||||
parent::__construct($auth_details, $protocol, $debug_level, $host, $log, $serialiser, $transport);
|
||||
$this->set_list_id($list_id);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the list id used for calls after construction
|
||||
* @param $list_id
|
||||
* @access public
|
||||
*/
|
||||
function set_list_id($list_id) {
|
||||
$this->_subscribers_base_route = $this->_base_route.'subscribers/'.$list_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new subscriber to the specified list
|
||||
* @param array $subscriber The subscriber details to use during creation.
|
||||
* This array should be of the form
|
||||
* array (
|
||||
* 'EmailAddress' => The new subscribers email address
|
||||
* 'Name' => The name of the new subscriber
|
||||
* 'CustomFields' => array(
|
||||
* array(
|
||||
* 'Key' => The custom fields personalisation tag
|
||||
* 'Value' => The value for this subscriber
|
||||
* )
|
||||
* )
|
||||
* 'ConsentToTrack' => Subscriber tracking preference ("yes", "no")
|
||||
* 'Resubscribe' => Whether we should resubscribe this subscriber if they already exist in the list
|
||||
* 'RestartSubscriptionBasedAutoResponders' => Whether we should restart subscription based auto responders which are sent when the subscriber first subscribes to a list.
|
||||
* )
|
||||
* @access public
|
||||
* @return CS_REST_Wrapper_Result A successful response will be empty
|
||||
*/
|
||||
function add($subscriber) {
|
||||
return $this->post_request($this->_subscribers_base_route.'.json', $subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing subscriber (email, name, state, or custom fields) in the specified list.
|
||||
* The update is performed even for inactive subscribers, but will return an error in the event of the
|
||||
* given email not existing in the list.
|
||||
* @param string $email The email address of the susbcriber to be updated
|
||||
* @param array $subscriber The subscriber details to use for the update. Empty parameters will remain unchanged
|
||||
* This array should be of the form
|
||||
* array (
|
||||
* 'EmailAddress' => The new email address
|
||||
* 'Name' => The name of the subscriber
|
||||
* 'CustomFields' => array(
|
||||
* array(
|
||||
* 'Key' => The custom fields personalisation tag
|
||||
* 'Value' => The value for this subscriber
|
||||
* 'Clear' => true/false (pass true to remove this custom field. in the case of a [multi-option, select many] field, pass an option in the 'Value' field to clear that option or leave Value blank to remove all options)
|
||||
* )
|
||||
* )
|
||||
* 'ConsentToTrack' => Subscriber tracking preference ("yes", "no")
|
||||
* 'Resubscribe' => Whether we should resubscribe this subscriber if they already exist in the list
|
||||
* 'RestartSubscriptionBasedAutoResponders' => Whether we should restart subscription based auto responders which are sent when the subscriber first subscribes to a list.
|
||||
* )
|
||||
* @access public
|
||||
* @return CS_REST_Wrapper_Result A successful response will be empty
|
||||
*/
|
||||
function update($email, $subscriber) {
|
||||
return $this->put_request($this->_subscribers_base_route.'.json?email='.urlencode($email), $subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports an array of subscribers into the current list
|
||||
* @param array $subscribers An array of subscribers to import.
|
||||
* This array should be of the form
|
||||
* array (
|
||||
* array (
|
||||
* 'EmailAddress' => The new subscribers email address
|
||||
* 'Name' => The name of the new subscriber
|
||||
* 'CustomFields' => array(
|
||||
* array(
|
||||
* 'Key' => The custom fields personalisation tag
|
||||
* 'Value' => The value for this subscriber
|
||||
* 'Clear' => true/false (pass true to remove this custom field. in the case of a [multi-option, select many] field, pass an option in the 'Value' field to clear that option or leave Value blank to remove all options)
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* @param bool $resubscribe Whether we should resubscribe any existing subscribers
|
||||
* @param bool $queueSubscriptionBasedAutoResponders By default, subscription based auto responders do not trigger during an import. Pass a value of true to override this behaviour
|
||||
* @param bool $restartSubscriptionBasedAutoResponders By default, subscription based auto responders will not be restarted
|
||||
* @access public
|
||||
* @return CS_REST_Wrapper_Result A successful response will be an object of the form
|
||||
* {
|
||||
* 'TotalUniqueEmailsSubmitted' => The number of unique emails submitted in the call
|
||||
* 'TotalExistingSubscribers' => The number of subscribers who already existed in the list
|
||||
* 'TotalNewSubscribers' => The number of new subscriptions to the list
|
||||
* 'DuplicateEmailsInSubmission' => array<string> The emails which appeared more than once in the batch
|
||||
* 'FailureDetails' => array (
|
||||
* {
|
||||
* 'EmailAddress' => The email address which failed
|
||||
* 'Code' => The Create Send API Error code
|
||||
* 'Message' => The reason for the failure
|
||||
* }
|
||||
* )
|
||||
* }
|
||||
*
|
||||
*/
|
||||
function import($subscribers, $resubscribe, $queueSubscriptionBasedAutoResponders = false, $restartSubscriptionBasedAutoResponders = false) {
|
||||
$subscribers = array(
|
||||
'Resubscribe' => $resubscribe,
|
||||
'QueueSubscriptionBasedAutoResponders' => $queueSubscriptionBasedAutoResponders,
|
||||
'Subscribers' => $subscribers,
|
||||
'RestartSubscriptionBasedAutoresponders' => $restartSubscriptionBasedAutoResponders
|
||||
);
|
||||
|
||||
return $this->post_request($this->_subscribers_base_route.'/import.json', $subscribers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a subscriber details, including custom fields
|
||||
* @access public
|
||||
* @return CS_REST_Wrapper_Result A successful response will be an object of the form
|
||||
* {
|
||||
* 'EmailAddress' => The subscriber email address
|
||||
* 'Name' => The subscribers name
|
||||
* 'Date' => The date the subscriber was added to the list
|
||||
* 'State' => The current state of the subscriber
|
||||
* 'CustomFields' => array(
|
||||
* {
|
||||
* 'Key' => The custom fields personalisation tag
|
||||
* 'Value' => The custom field value for this subscriber
|
||||
* }
|
||||
* )
|
||||
* }
|
||||
*/
|
||||
function get($email, $include_tracking_pref = NULL) {
|
||||
return $this->get_request($this->_subscribers_base_route.'.json?email='.urlencode($email), $include_tracking_pref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sending history to a specific subscriber
|
||||
* @access public
|
||||
* @return CS_REST_Wrapper_Result A successful response will be an object of the form
|
||||
* array(
|
||||
* {
|
||||
* ID => The id of the email which was sent
|
||||
* Type => 'Campaign'
|
||||
* Name => The name of the email
|
||||
* Actions => array(
|
||||
* {
|
||||
* Event => The type of action (Click, Open, Unsubscribe etc)
|
||||
* Date => The date the event occurred
|
||||
* IPAddress => The IP that the event originated from
|
||||
* Detail => Any available details about the event i.e the URL for clicks
|
||||
* }
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
function get_history($email) {
|
||||
return $this->get_request($this->_subscribers_base_route.'/history.json?email='.urlencode($email));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes the given subscriber from the current list
|
||||
* @param string $email The email address to unsubscribe
|
||||
* @access public
|
||||
* @return CS_REST_Wrapper_Result A successful response will be empty
|
||||
*/
|
||||
function unsubscribe($email) {
|
||||
// We need to build the subscriber data structure.
|
||||
$email = array(
|
||||
'EmailAddress' => $email
|
||||
);
|
||||
|
||||
return $this->post_request($this->_subscribers_base_route.'/unsubscribe.json', $email);
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes the given subscriber from the current list
|
||||
* @param string $email The email address to delete
|
||||
* @access public
|
||||
* @return CS_REST_Wrapper_Result A successful response will be empty
|
||||
*/
|
||||
function delete($email) {
|
||||
return $this->delete_request($this->_subscribers_base_route.'.json?email='.urlencode($email));
|
||||
}
|
||||
}
|
||||
}
|
38
app/api/Google/Auth/Abstract.php
Executable file
38
app/api/Google/Auth/Abstract.php
Executable file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class for the Authentication in the API client
|
||||
* @author Chris Chabot <chabotc@google.com>
|
||||
*
|
||||
*/
|
||||
abstract class Google_Auth_Abstract
|
||||
{
|
||||
/**
|
||||
* An utility function that first calls $this->auth->sign($request) and then
|
||||
* executes makeRequest() on that signed request. Used for when a request
|
||||
* should be authenticated
|
||||
* @param Google_Http_Request $request
|
||||
* @return Google_Http_Request $request
|
||||
*/
|
||||
abstract public function authenticatedRequest(Google_Http_Request $request);
|
||||
abstract public function sign(Google_Http_Request $request);
|
||||
}
|
120
app/api/Google/Auth/AppIdentity.php
Executable file
120
app/api/Google/Auth/AppIdentity.php
Executable file
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* WARNING - this class depends on the Google App Engine PHP library
|
||||
* which is 5.3 and above only, so if you include this in a PHP 5.2
|
||||
* setup or one without 5.3 things will blow up.
|
||||
*/
|
||||
use google\appengine\api\app_identity\AppIdentityService;
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication via the Google App Engine App Identity service.
|
||||
*/
|
||||
class Google_Auth_AppIdentity extends Google_Auth_Abstract
|
||||
{
|
||||
const CACHE_PREFIX = "Google_Auth_AppIdentity::";
|
||||
private $client;
|
||||
private $token = false;
|
||||
private $tokenScopes = false;
|
||||
|
||||
public function __construct(Google_Client $client, $config = null)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an access token for the scopes supplied.
|
||||
*/
|
||||
public function authenticateForScope($scopes)
|
||||
{
|
||||
if ($this->token && $this->tokenScopes == $scopes) {
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
$cacheKey = self::CACHE_PREFIX;
|
||||
if (is_string($scopes)) {
|
||||
$cacheKey .= $scopes;
|
||||
} else if (is_array($scopes)) {
|
||||
$cacheKey .= implode(":", $scopes);
|
||||
}
|
||||
|
||||
$this->token = $this->client->getCache()->get($cacheKey);
|
||||
if (!$this->token) {
|
||||
$this->retrieveToken($scopes, $cacheKey);
|
||||
} else if ($this->token['expiration_time'] < time()) {
|
||||
$this->client->getCache()->delete($cacheKey);
|
||||
$this->retrieveToken($scopes, $cacheKey);
|
||||
}
|
||||
|
||||
$this->tokenScopes = $scopes;
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a new access token and store it in cache
|
||||
* @param mixed $scopes
|
||||
* @param string $cacheKey
|
||||
*/
|
||||
private function retrieveToken($scopes, $cacheKey)
|
||||
{
|
||||
$this->token = AppIdentityService::getAccessToken($scopes);
|
||||
if ($this->token) {
|
||||
$this->client->getCache()->set(
|
||||
$cacheKey,
|
||||
$this->token
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an authenticated / signed apiHttpRequest.
|
||||
* This function takes the apiHttpRequest, calls apiAuth->sign on it
|
||||
* (which can modify the request in what ever way fits the auth mechanism)
|
||||
* and then calls apiCurlIO::makeRequest on the signed request
|
||||
*
|
||||
* @param Google_Http_Request $request
|
||||
* @return Google_Http_Request The resulting HTTP response including the
|
||||
* responseHttpCode, responseHeaders and responseBody.
|
||||
*/
|
||||
public function authenticatedRequest(Google_Http_Request $request)
|
||||
{
|
||||
$request = $this->sign($request);
|
||||
return $this->client->getIo()->makeRequest($request);
|
||||
}
|
||||
|
||||
public function sign(Google_Http_Request $request)
|
||||
{
|
||||
if (!$this->token) {
|
||||
// No token, so nothing to do.
|
||||
return $request;
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug('App Identity authentication');
|
||||
|
||||
// Add the OAuth2 header to the request
|
||||
$request->setRequestHeaders(
|
||||
array('Authorization' => 'Bearer ' . $this->token['access_token'])
|
||||
);
|
||||
|
||||
return $request;
|
||||
}
|
||||
}
|
136
app/api/Google/Auth/AssertionCredentials.php
Executable file
136
app/api/Google/Auth/AssertionCredentials.php
Executable file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Credentials object used for OAuth 2.0 Signed JWT assertion grants.
|
||||
*/
|
||||
class Google_Auth_AssertionCredentials
|
||||
{
|
||||
const MAX_TOKEN_LIFETIME_SECS = 3600;
|
||||
|
||||
public $serviceAccountName;
|
||||
public $scopes;
|
||||
public $privateKey;
|
||||
public $privateKeyPassword;
|
||||
public $assertionType;
|
||||
public $sub;
|
||||
/**
|
||||
* @deprecated
|
||||
* @link http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06
|
||||
*/
|
||||
public $prn;
|
||||
private $useCache;
|
||||
|
||||
/**
|
||||
* @param $serviceAccountName
|
||||
* @param $scopes array List of scopes
|
||||
* @param $privateKey
|
||||
* @param string $privateKeyPassword
|
||||
* @param string $assertionType
|
||||
* @param bool|string $sub The email address of the user for which the
|
||||
* application is requesting delegated access.
|
||||
* @param bool useCache Whether to generate a cache key and allow
|
||||
* automatic caching of the generated token.
|
||||
*/
|
||||
public function __construct(
|
||||
$serviceAccountName,
|
||||
$scopes,
|
||||
$privateKey,
|
||||
$privateKeyPassword = 'notasecret',
|
||||
$assertionType = 'http://oauth.net/grant_type/jwt/1.0/bearer',
|
||||
$sub = false,
|
||||
$useCache = true
|
||||
) {
|
||||
$this->serviceAccountName = $serviceAccountName;
|
||||
$this->scopes = is_string($scopes) ? $scopes : implode(' ', $scopes);
|
||||
$this->privateKey = $privateKey;
|
||||
$this->privateKeyPassword = $privateKeyPassword;
|
||||
$this->assertionType = $assertionType;
|
||||
$this->sub = $sub;
|
||||
$this->prn = $sub;
|
||||
$this->useCache = $useCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique key to represent this credential.
|
||||
* @return string
|
||||
*/
|
||||
public function getCacheKey()
|
||||
{
|
||||
if (!$this->useCache) {
|
||||
return false;
|
||||
}
|
||||
$h = $this->sub;
|
||||
$h .= $this->assertionType;
|
||||
$h .= $this->privateKey;
|
||||
$h .= $this->scopes;
|
||||
$h .= $this->serviceAccountName;
|
||||
return md5($h);
|
||||
}
|
||||
|
||||
public function generateAssertion()
|
||||
{
|
||||
$now = time();
|
||||
|
||||
$jwtParams = array(
|
||||
'aud' => Google_Auth_OAuth2::OAUTH2_TOKEN_URI,
|
||||
'scope' => $this->scopes,
|
||||
'iat' => $now,
|
||||
'exp' => $now + self::MAX_TOKEN_LIFETIME_SECS,
|
||||
'iss' => $this->serviceAccountName,
|
||||
);
|
||||
|
||||
if ($this->sub !== false) {
|
||||
$jwtParams['sub'] = $this->sub;
|
||||
} else if ($this->prn !== false) {
|
||||
$jwtParams['prn'] = $this->prn;
|
||||
}
|
||||
|
||||
return $this->makeSignedJwt($jwtParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a signed JWT.
|
||||
* @param array $payload
|
||||
* @return string The signed JWT.
|
||||
*/
|
||||
private function makeSignedJwt($payload)
|
||||
{
|
||||
$header = array('typ' => 'JWT', 'alg' => 'RS256');
|
||||
|
||||
$payload = json_encode($payload);
|
||||
// Handle some overzealous escaping in PHP json that seemed to cause some errors
|
||||
// with claimsets.
|
||||
$payload = str_replace('\/', '/', $payload);
|
||||
|
||||
$segments = array(
|
||||
Google_Utils::urlSafeB64Encode(json_encode($header)),
|
||||
Google_Utils::urlSafeB64Encode($payload)
|
||||
);
|
||||
|
||||
$signingInput = implode('.', $segments);
|
||||
$signer = new Google_Signer_P12($this->privateKey, $this->privateKeyPassword);
|
||||
$signature = $signer->sign($signingInput);
|
||||
$segments[] = Google_Utils::urlSafeB64Encode($signature);
|
||||
|
||||
return implode(".", $segments);
|
||||
}
|
||||
}
|
146
app/api/Google/Auth/ComputeEngine.php
Executable file
146
app/api/Google/Auth/ComputeEngine.php
Executable file
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication via built-in Compute Engine service accounts.
|
||||
* The instance must be pre-configured with a service account
|
||||
* and the appropriate scopes.
|
||||
* @author Jonathan Parrott <jon.wayne.parrott@gmail.com>
|
||||
*/
|
||||
class Google_Auth_ComputeEngine extends Google_Auth_Abstract
|
||||
{
|
||||
const METADATA_AUTH_URL =
|
||||
'http://metadata/computeMetadata/v1/instance/service-accounts/default/token';
|
||||
private $client;
|
||||
private $token;
|
||||
|
||||
public function __construct(Google_Client $client, $config = null)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an authenticated / signed apiHttpRequest.
|
||||
* This function takes the apiHttpRequest, calls apiAuth->sign on it
|
||||
* (which can modify the request in what ever way fits the auth mechanism)
|
||||
* and then calls apiCurlIO::makeRequest on the signed request
|
||||
*
|
||||
* @param Google_Http_Request $request
|
||||
* @return Google_Http_Request The resulting HTTP response including the
|
||||
* responseHttpCode, responseHeaders and responseBody.
|
||||
*/
|
||||
public function authenticatedRequest(Google_Http_Request $request)
|
||||
{
|
||||
$request = $this->sign($request);
|
||||
return $this->client->getIo()->makeRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @throws Google_Auth_Exception
|
||||
*/
|
||||
public function setAccessToken($token)
|
||||
{
|
||||
$token = json_decode($token, true);
|
||||
if ($token == null) {
|
||||
throw new Google_Auth_Exception('Could not json decode the token');
|
||||
}
|
||||
if (! isset($token['access_token'])) {
|
||||
throw new Google_Auth_Exception("Invalid token format");
|
||||
}
|
||||
$token['created'] = time();
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function getAccessToken()
|
||||
{
|
||||
return json_encode($this->token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a new access token from the compute engine metadata server.
|
||||
* @throws Google_Auth_Exception
|
||||
*/
|
||||
public function acquireAccessToken()
|
||||
{
|
||||
$request = new Google_Http_Request(
|
||||
self::METADATA_AUTH_URL,
|
||||
'GET',
|
||||
array(
|
||||
'Metadata-Flavor' => 'Google'
|
||||
)
|
||||
);
|
||||
$request->disableGzip();
|
||||
$response = $this->client->getIo()->makeRequest($request);
|
||||
|
||||
if ($response->getResponseHttpCode() == 200) {
|
||||
$this->setAccessToken($response->getResponseBody());
|
||||
$this->token['created'] = time();
|
||||
return $this->getAccessToken();
|
||||
} else {
|
||||
throw new Google_Auth_Exception(
|
||||
sprintf(
|
||||
"Error fetching service account access token, message: '%s'",
|
||||
$response->getResponseBody()
|
||||
),
|
||||
$response->getResponseHttpCode()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Include an accessToken in a given apiHttpRequest.
|
||||
* @param Google_Http_Request $request
|
||||
* @return Google_Http_Request
|
||||
* @throws Google_Auth_Exception
|
||||
*/
|
||||
public function sign(Google_Http_Request $request)
|
||||
{
|
||||
if ($this->isAccessTokenExpired()) {
|
||||
$this->acquireAccessToken();
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug('Compute engine service account authentication');
|
||||
|
||||
$request->setRequestHeaders(
|
||||
array('Authorization' => 'Bearer ' . $this->token['access_token'])
|
||||
);
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the access_token is expired.
|
||||
* @return bool Returns True if the access_token is expired.
|
||||
*/
|
||||
public function isAccessTokenExpired()
|
||||
{
|
||||
if (!$this->token || !isset($this->token['created'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the token is set to expire in the next 30 seconds.
|
||||
$expired = ($this->token['created']
|
||||
+ ($this->token['expires_in'] - 30)) < time();
|
||||
|
||||
return $expired;
|
||||
}
|
||||
}
|
24
app/api/Google/Auth/Exception.php
Executable file
24
app/api/Google/Auth/Exception.php
Executable file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
class Google_Auth_Exception extends Google_Exception
|
||||
{
|
||||
}
|
71
app/api/Google/Auth/LoginTicket.php
Executable file
71
app/api/Google/Auth/LoginTicket.php
Executable file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to hold information about an authenticated login.
|
||||
*
|
||||
* @author Brian Eaton <beaton@google.com>
|
||||
*/
|
||||
class Google_Auth_LoginTicket
|
||||
{
|
||||
const USER_ATTR = "sub";
|
||||
|
||||
// Information from id token envelope.
|
||||
private $envelope;
|
||||
|
||||
// Information from id token payload.
|
||||
private $payload;
|
||||
|
||||
/**
|
||||
* Creates a user based on the supplied token.
|
||||
*
|
||||
* @param string $envelope Header from a verified authentication token.
|
||||
* @param string $payload Information from a verified authentication token.
|
||||
*/
|
||||
public function __construct($envelope, $payload)
|
||||
{
|
||||
$this->envelope = $envelope;
|
||||
$this->payload = $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the numeric identifier for the user.
|
||||
* @throws Google_Auth_Exception
|
||||
* @return
|
||||
*/
|
||||
public function getUserId()
|
||||
{
|
||||
if (array_key_exists(self::USER_ATTR, $this->payload)) {
|
||||
return $this->payload[self::USER_ATTR];
|
||||
}
|
||||
throw new Google_Auth_Exception("No user_id in token");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attributes from the login ticket. This can contain
|
||||
* various information about the user session.
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
return array("envelope" => $this->envelope, "payload" => $this->payload);
|
||||
}
|
||||
}
|
646
app/api/Google/Auth/OAuth2.php
Executable file
646
app/api/Google/Auth/OAuth2.php
Executable file
|
@ -0,0 +1,646 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication class that deals with the OAuth 2 web-server authentication flow
|
||||
*
|
||||
*/
|
||||
class Google_Auth_OAuth2 extends Google_Auth_Abstract
|
||||
{
|
||||
const OAUTH2_REVOKE_URI = 'https://accounts.google.com/o/oauth2/revoke';
|
||||
const OAUTH2_TOKEN_URI = 'https://accounts.google.com/o/oauth2/token';
|
||||
const OAUTH2_AUTH_URL = 'https://accounts.google.com/o/oauth2/auth';
|
||||
const CLOCK_SKEW_SECS = 300; // five minutes in seconds
|
||||
const AUTH_TOKEN_LIFETIME_SECS = 300; // five minutes in seconds
|
||||
const MAX_TOKEN_LIFETIME_SECS = 86400; // one day in seconds
|
||||
const OAUTH2_ISSUER = 'accounts.google.com';
|
||||
const OAUTH2_ISSUER_HTTPS = 'https://accounts.google.com';
|
||||
|
||||
/** @var Google_Auth_AssertionCredentials $assertionCredentials */
|
||||
private $assertionCredentials;
|
||||
|
||||
/**
|
||||
* @var string The state parameters for CSRF and other forgery protection.
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/**
|
||||
* @var array The token bundle.
|
||||
*/
|
||||
private $token = [];
|
||||
|
||||
/**
|
||||
* @var Google_Client the base client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* Instantiates the class, but does not initiate the login flow, leaving it
|
||||
* to the discretion of the caller.
|
||||
*/
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an authenticated / signed apiHttpRequest.
|
||||
* This function takes the apiHttpRequest, calls apiAuth->sign on it
|
||||
* (which can modify the request in what ever way fits the auth mechanism)
|
||||
* and then calls apiCurlIO::makeRequest on the signed request
|
||||
*
|
||||
* @param Google_Http_Request $request
|
||||
* @return Google_Http_Request The resulting HTTP response including the
|
||||
* responseHttpCode, responseHeaders and responseBody.
|
||||
*/
|
||||
public function authenticatedRequest(Google_Http_Request $request)
|
||||
{
|
||||
$request = $this->sign($request);
|
||||
return $this->client->getIo()->makeRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
* @param boolean $crossClient
|
||||
* @throws Google_Auth_Exception
|
||||
* @return string
|
||||
*/
|
||||
public function authenticate($code, $crossClient = false)
|
||||
{
|
||||
if (strlen($code) == 0) {
|
||||
throw new Google_Auth_Exception("Invalid code");
|
||||
}
|
||||
|
||||
$arguments = array(
|
||||
'code' => $code,
|
||||
'grant_type' => 'authorization_code',
|
||||
'client_id' => $this->client->getClassConfig($this, 'client_id'),
|
||||
'client_secret' => $this->client->getClassConfig($this, 'client_secret')
|
||||
);
|
||||
|
||||
if ($crossClient !== true) {
|
||||
$arguments['redirect_uri'] = $this->client->getClassConfig($this, 'redirect_uri');
|
||||
}
|
||||
|
||||
// We got here from the redirect from a successful authorization grant,
|
||||
// fetch the access token
|
||||
$request = new Google_Http_Request(
|
||||
self::OAUTH2_TOKEN_URI,
|
||||
'POST',
|
||||
array(),
|
||||
$arguments
|
||||
);
|
||||
$request->disableGzip();
|
||||
$response = $this->client->getIo()->makeRequest($request);
|
||||
|
||||
if ($response->getResponseHttpCode() == 200) {
|
||||
$this->setAccessToken($response->getResponseBody());
|
||||
$this->token['created'] = time();
|
||||
return $this->getAccessToken();
|
||||
} else {
|
||||
$decodedResponse = json_decode($response->getResponseBody(), true);
|
||||
if ($decodedResponse != null && $decodedResponse['error']) {
|
||||
$errorText = $decodedResponse['error'];
|
||||
if (isset($decodedResponse['error_description'])) {
|
||||
$errorText .= ": " . $decodedResponse['error_description'];
|
||||
}
|
||||
}
|
||||
throw new Google_Auth_Exception(
|
||||
sprintf(
|
||||
"Error fetching OAuth2 access token, message: '%s'",
|
||||
$errorText
|
||||
),
|
||||
$response->getResponseHttpCode()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a URL to obtain user authorization.
|
||||
* The authorization endpoint allows the user to first
|
||||
* authenticate, and then grant/deny the access request.
|
||||
* @param string $scope The scope is expressed as a list of space-delimited strings.
|
||||
* @return string
|
||||
*/
|
||||
public function createAuthUrl($scope)
|
||||
{
|
||||
$params = array(
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => $this->client->getClassConfig($this, 'redirect_uri'),
|
||||
'client_id' => $this->client->getClassConfig($this, 'client_id'),
|
||||
'scope' => $scope,
|
||||
'access_type' => $this->client->getClassConfig($this, 'access_type'),
|
||||
);
|
||||
|
||||
// Prefer prompt to approval prompt.
|
||||
if ($this->client->getClassConfig($this, 'prompt')) {
|
||||
$params = $this->maybeAddParam($params, 'prompt');
|
||||
} else {
|
||||
$params = $this->maybeAddParam($params, 'approval_prompt');
|
||||
}
|
||||
$params = $this->maybeAddParam($params, 'login_hint');
|
||||
$params = $this->maybeAddParam($params, 'hd');
|
||||
$params = $this->maybeAddParam($params, 'openid.realm');
|
||||
$params = $this->maybeAddParam($params, 'include_granted_scopes');
|
||||
|
||||
// If the list of scopes contains plus.login, add request_visible_actions
|
||||
// to auth URL.
|
||||
$rva = $this->client->getClassConfig($this, 'request_visible_actions');
|
||||
if (strpos($scope, 'plus.login') && strlen($rva) > 0) {
|
||||
$params['request_visible_actions'] = $rva;
|
||||
}
|
||||
|
||||
if (isset($this->state)) {
|
||||
$params['state'] = $this->state;
|
||||
}
|
||||
|
||||
return self::OAUTH2_AUTH_URL . "?" . http_build_query($params, '', '&');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @throws Google_Auth_Exception
|
||||
*/
|
||||
public function setAccessToken($token)
|
||||
{
|
||||
$token = json_decode($token, true);
|
||||
if ($token == null) {
|
||||
throw new Google_Auth_Exception('Could not json decode the token');
|
||||
}
|
||||
if (! isset($token['access_token'])) {
|
||||
throw new Google_Auth_Exception("Invalid token format");
|
||||
}
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function getAccessToken()
|
||||
{
|
||||
return json_encode($this->token);
|
||||
}
|
||||
|
||||
public function getRefreshToken()
|
||||
{
|
||||
if (array_key_exists('refresh_token', $this->token)) {
|
||||
return $this->token['refresh_token'];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function setState($state)
|
||||
{
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
public function setAssertionCredentials(Google_Auth_AssertionCredentials $creds)
|
||||
{
|
||||
$this->assertionCredentials = $creds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include an accessToken in a given apiHttpRequest.
|
||||
* @param Google_Http_Request $request
|
||||
* @return Google_Http_Request
|
||||
* @throws Google_Auth_Exception
|
||||
*/
|
||||
public function sign(Google_Http_Request $request)
|
||||
{
|
||||
// add the developer key to the request before signing it
|
||||
if ($this->client->getClassConfig($this, 'developer_key')) {
|
||||
$request->setQueryParam('key', $this->client->getClassConfig($this, 'developer_key'));
|
||||
}
|
||||
|
||||
// Cannot sign the request without an OAuth access token.
|
||||
if (null == $this->token && null == $this->assertionCredentials) {
|
||||
return $request;
|
||||
}
|
||||
|
||||
// Check if the token is set to expire in the next 30 seconds
|
||||
// (or has already expired).
|
||||
if ($this->isAccessTokenExpired()) {
|
||||
if ($this->assertionCredentials) {
|
||||
$this->refreshTokenWithAssertion();
|
||||
} else {
|
||||
$this->client->getLogger()->debug('OAuth2 access token expired');
|
||||
if (! array_key_exists('refresh_token', $this->token)) {
|
||||
$error = "The OAuth 2.0 access token has expired,"
|
||||
." and a refresh token is not available. Refresh tokens"
|
||||
." are not returned for responses that were auto-approved.";
|
||||
|
||||
$this->client->getLogger()->error($error);
|
||||
throw new Google_Auth_Exception($error);
|
||||
}
|
||||
$this->refreshToken($this->token['refresh_token']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug('OAuth2 authentication');
|
||||
|
||||
// Add the OAuth2 header to the request
|
||||
$request->setRequestHeaders(
|
||||
array('Authorization' => 'Bearer ' . $this->token['access_token'])
|
||||
);
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a fresh access token with the given refresh token.
|
||||
* @param string $refreshToken
|
||||
* @return void
|
||||
*/
|
||||
public function refreshToken($refreshToken)
|
||||
{
|
||||
$this->refreshTokenRequest(
|
||||
array(
|
||||
'client_id' => $this->client->getClassConfig($this, 'client_id'),
|
||||
'client_secret' => $this->client->getClassConfig($this, 'client_secret'),
|
||||
'refresh_token' => $refreshToken,
|
||||
'grant_type' => 'refresh_token'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a fresh access token with a given assertion token.
|
||||
* @param Google_Auth_AssertionCredentials $assertionCredentials optional.
|
||||
* @return void
|
||||
*/
|
||||
public function refreshTokenWithAssertion($assertionCredentials = null)
|
||||
{
|
||||
if (!$assertionCredentials) {
|
||||
$assertionCredentials = $this->assertionCredentials;
|
||||
}
|
||||
|
||||
$cacheKey = $assertionCredentials->getCacheKey();
|
||||
|
||||
if ($cacheKey) {
|
||||
// We can check whether we have a token available in the
|
||||
// cache. If it is expired, we can retrieve a new one from
|
||||
// the assertion.
|
||||
$token = $this->client->getCache()->get($cacheKey);
|
||||
if ($token) {
|
||||
$this->setAccessToken($token);
|
||||
}
|
||||
if (!$this->isAccessTokenExpired()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug('OAuth2 access token expired');
|
||||
$this->refreshTokenRequest(
|
||||
array(
|
||||
'grant_type' => 'assertion',
|
||||
'assertion_type' => $assertionCredentials->assertionType,
|
||||
'assertion' => $assertionCredentials->generateAssertion(),
|
||||
)
|
||||
);
|
||||
|
||||
if ($cacheKey) {
|
||||
// Attempt to cache the token.
|
||||
$this->client->getCache()->set(
|
||||
$cacheKey,
|
||||
$this->getAccessToken()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function refreshTokenRequest($params)
|
||||
{
|
||||
if (isset($params['assertion'])) {
|
||||
$this->client->getLogger()->info(
|
||||
'OAuth2 access token refresh with Signed JWT assertion grants.'
|
||||
);
|
||||
} else {
|
||||
$this->client->getLogger()->info('OAuth2 access token refresh');
|
||||
}
|
||||
|
||||
$http = new Google_Http_Request(
|
||||
self::OAUTH2_TOKEN_URI,
|
||||
'POST',
|
||||
array(),
|
||||
$params
|
||||
);
|
||||
$http->disableGzip();
|
||||
$request = $this->client->getIo()->makeRequest($http);
|
||||
|
||||
$code = $request->getResponseHttpCode();
|
||||
$body = $request->getResponseBody();
|
||||
if (200 == $code) {
|
||||
$token = json_decode($body, true);
|
||||
if ($token == null) {
|
||||
throw new Google_Auth_Exception("Could not json decode the access token");
|
||||
}
|
||||
|
||||
if (! isset($token['access_token']) || ! isset($token['expires_in'])) {
|
||||
throw new Google_Auth_Exception("Invalid token format");
|
||||
}
|
||||
|
||||
if (isset($token['id_token'])) {
|
||||
$this->token['id_token'] = $token['id_token'];
|
||||
}
|
||||
$this->token['access_token'] = $token['access_token'];
|
||||
$this->token['expires_in'] = $token['expires_in'];
|
||||
$this->token['created'] = time();
|
||||
} else {
|
||||
throw new Google_Auth_Exception("Error refreshing the OAuth2 token, message: '$body'", $code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke an OAuth2 access token or refresh token. This method will revoke the current access
|
||||
* token, if a token isn't provided.
|
||||
* @throws Google_Auth_Exception
|
||||
* @param string|null $token The token (access token or a refresh token) that should be revoked.
|
||||
* @return boolean Returns True if the revocation was successful, otherwise False.
|
||||
*/
|
||||
public function revokeToken($token = null)
|
||||
{
|
||||
if (!$token) {
|
||||
if (!$this->token) {
|
||||
// Not initialized, no token to actually revoke
|
||||
return false;
|
||||
} elseif (array_key_exists('refresh_token', $this->token)) {
|
||||
$token = $this->token['refresh_token'];
|
||||
} else {
|
||||
$token = $this->token['access_token'];
|
||||
}
|
||||
}
|
||||
$request = new Google_Http_Request(
|
||||
self::OAUTH2_REVOKE_URI,
|
||||
'POST',
|
||||
array(),
|
||||
"token=$token"
|
||||
);
|
||||
$request->disableGzip();
|
||||
$response = $this->client->getIo()->makeRequest($request);
|
||||
$code = $response->getResponseHttpCode();
|
||||
if ($code == 200) {
|
||||
$this->token = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the access_token is expired.
|
||||
* @return bool Returns True if the access_token is expired.
|
||||
*/
|
||||
public function isAccessTokenExpired()
|
||||
{
|
||||
if (!$this->token || !isset($this->token['created'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the token is set to expire in the next 30 seconds.
|
||||
$expired = ($this->token['created']
|
||||
+ ($this->token['expires_in'] - 30)) < time();
|
||||
|
||||
return $expired;
|
||||
}
|
||||
|
||||
// Gets federated sign-on certificates to use for verifying identity tokens.
|
||||
// Returns certs as array structure, where keys are key ids, and values
|
||||
// are PEM encoded certificates.
|
||||
private function getFederatedSignOnCerts()
|
||||
{
|
||||
return $this->retrieveCertsFromLocation(
|
||||
$this->client->getClassConfig($this, 'federated_signon_certs_url')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve and cache a certificates file.
|
||||
*
|
||||
* @param $url string location
|
||||
* @throws Google_Auth_Exception
|
||||
* @return array certificates
|
||||
*/
|
||||
public function retrieveCertsFromLocation($url)
|
||||
{
|
||||
// If we're retrieving a local file, just grab it.
|
||||
if ("http" != substr($url, 0, 4)) {
|
||||
$file = file_get_contents($url);
|
||||
if ($file) {
|
||||
return json_decode($file, true);
|
||||
} else {
|
||||
throw new Google_Auth_Exception(
|
||||
"Failed to retrieve verification certificates: '" .
|
||||
$url . "'."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This relies on makeRequest caching certificate responses.
|
||||
$request = $this->client->getIo()->makeRequest(
|
||||
new Google_Http_Request(
|
||||
$url
|
||||
)
|
||||
);
|
||||
if ($request->getResponseHttpCode() == 200) {
|
||||
$certs = json_decode($request->getResponseBody(), true);
|
||||
if ($certs) {
|
||||
return $certs;
|
||||
}
|
||||
}
|
||||
throw new Google_Auth_Exception(
|
||||
"Failed to retrieve verification certificates: '" .
|
||||
$request->getResponseBody() . "'.",
|
||||
$request->getResponseHttpCode()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies an id token and returns the authenticated apiLoginTicket.
|
||||
* Throws an exception if the id token is not valid.
|
||||
* The audience parameter can be used to control which id tokens are
|
||||
* accepted. By default, the id token must have been issued to this OAuth2 client.
|
||||
*
|
||||
* @param $id_token
|
||||
* @param $audience
|
||||
* @return Google_Auth_LoginTicket
|
||||
*/
|
||||
public function verifyIdToken($id_token = null, $audience = null)
|
||||
{
|
||||
if (!$id_token) {
|
||||
$id_token = $this->token['id_token'];
|
||||
}
|
||||
$certs = $this->getFederatedSignonCerts();
|
||||
if (!$audience) {
|
||||
$audience = $this->client->getClassConfig($this, 'client_id');
|
||||
}
|
||||
|
||||
return $this->verifySignedJwtWithCerts(
|
||||
$id_token,
|
||||
$certs,
|
||||
$audience,
|
||||
array(self::OAUTH2_ISSUER, self::OAUTH2_ISSUER_HTTPS)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the id token, returns the verified token contents.
|
||||
*
|
||||
* @param $jwt string the token
|
||||
* @param $certs array of certificates
|
||||
* @param $required_audience string the expected consumer of the token
|
||||
* @param [$issuer] the expected issues, defaults to Google
|
||||
* @param [$max_expiry] the max lifetime of a token, defaults to MAX_TOKEN_LIFETIME_SECS
|
||||
* @throws Google_Auth_Exception
|
||||
* @return mixed token information if valid, false if not
|
||||
*/
|
||||
public function verifySignedJwtWithCerts(
|
||||
$jwt,
|
||||
$certs,
|
||||
$required_audience,
|
||||
$issuer = null,
|
||||
$max_expiry = null
|
||||
) {
|
||||
if (!$max_expiry) {
|
||||
// Set the maximum time we will accept a token for.
|
||||
$max_expiry = self::MAX_TOKEN_LIFETIME_SECS;
|
||||
}
|
||||
|
||||
$segments = explode(".", $jwt);
|
||||
if (count($segments) != 3) {
|
||||
throw new Google_Auth_Exception("Wrong number of segments in token: $jwt");
|
||||
}
|
||||
$signed = $segments[0] . "." . $segments[1];
|
||||
$signature = Google_Utils::urlSafeB64Decode($segments[2]);
|
||||
|
||||
// Parse envelope.
|
||||
$envelope = json_decode(Google_Utils::urlSafeB64Decode($segments[0]), true);
|
||||
if (!$envelope) {
|
||||
throw new Google_Auth_Exception("Can't parse token envelope: " . $segments[0]);
|
||||
}
|
||||
|
||||
// Parse token
|
||||
$json_body = Google_Utils::urlSafeB64Decode($segments[1]);
|
||||
$payload = json_decode($json_body, true);
|
||||
if (!$payload) {
|
||||
throw new Google_Auth_Exception("Can't parse token payload: " . $segments[1]);
|
||||
}
|
||||
|
||||
// Check signature
|
||||
$verified = false;
|
||||
foreach ($certs as $keyName => $pem) {
|
||||
$public_key = new Google_Verifier_Pem($pem);
|
||||
if ($public_key->verify($signed, $signature)) {
|
||||
$verified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$verified) {
|
||||
throw new Google_Auth_Exception("Invalid token signature: $jwt");
|
||||
}
|
||||
|
||||
// Check issued-at timestamp
|
||||
$iat = 0;
|
||||
if (array_key_exists("iat", $payload)) {
|
||||
$iat = $payload["iat"];
|
||||
}
|
||||
if (!$iat) {
|
||||
throw new Google_Auth_Exception("No issue time in token: $json_body");
|
||||
}
|
||||
$earliest = $iat - self::CLOCK_SKEW_SECS;
|
||||
|
||||
// Check expiration timestamp
|
||||
$now = time();
|
||||
$exp = 0;
|
||||
if (array_key_exists("exp", $payload)) {
|
||||
$exp = $payload["exp"];
|
||||
}
|
||||
if (!$exp) {
|
||||
throw new Google_Auth_Exception("No expiration time in token: $json_body");
|
||||
}
|
||||
if ($exp >= $now + $max_expiry) {
|
||||
throw new Google_Auth_Exception(
|
||||
sprintf("Expiration time too far in future: %s", $json_body)
|
||||
);
|
||||
}
|
||||
|
||||
$latest = $exp + self::CLOCK_SKEW_SECS;
|
||||
if ($now < $earliest) {
|
||||
throw new Google_Auth_Exception(
|
||||
sprintf(
|
||||
"Token used too early, %s < %s: %s",
|
||||
$now,
|
||||
$earliest,
|
||||
$json_body
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($now > $latest) {
|
||||
throw new Google_Auth_Exception(
|
||||
sprintf(
|
||||
"Token used too late, %s > %s: %s",
|
||||
$now,
|
||||
$latest,
|
||||
$json_body
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// support HTTP and HTTPS issuers
|
||||
// @see https://developers.google.com/identity/sign-in/web/backend-auth
|
||||
$iss = $payload['iss'];
|
||||
if ($issuer && !in_array($iss, (array) $issuer)) {
|
||||
throw new Google_Auth_Exception(
|
||||
sprintf(
|
||||
"Invalid issuer, %s not in %s: %s",
|
||||
$iss,
|
||||
"[".implode(",", (array) $issuer)."]",
|
||||
$json_body
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Check audience
|
||||
$aud = $payload["aud"];
|
||||
if ($aud != $required_audience) {
|
||||
throw new Google_Auth_Exception(
|
||||
sprintf(
|
||||
"Wrong recipient, %s != %s:",
|
||||
$aud,
|
||||
$required_audience,
|
||||
$json_body
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// All good.
|
||||
return new Google_Auth_LoginTicket($envelope, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a parameter to the auth params if not empty string.
|
||||
*/
|
||||
private function maybeAddParam($params, $name)
|
||||
{
|
||||
$param = $this->client->getClassConfig($this, $name);
|
||||
if ($param != '') {
|
||||
$params[$name] = $param;
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
}
|
63
app/api/Google/Auth/Simple.php
Executable file
63
app/api/Google/Auth/Simple.php
Executable file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple API access implementation. Can either be used to make requests
|
||||
* completely unauthenticated, or by using a Simple API Access developer
|
||||
* key.
|
||||
*/
|
||||
class Google_Auth_Simple extends Google_Auth_Abstract
|
||||
{
|
||||
private $client;
|
||||
|
||||
public function __construct(Google_Client $client, $config = null)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an authenticated / signed apiHttpRequest.
|
||||
* This function takes the apiHttpRequest, calls apiAuth->sign on it
|
||||
* (which can modify the request in what ever way fits the auth mechanism)
|
||||
* and then calls apiCurlIO::makeRequest on the signed request
|
||||
*
|
||||
* @param Google_Http_Request $request
|
||||
* @return Google_Http_Request The resulting HTTP response including the
|
||||
* responseHttpCode, responseHeaders and responseBody.
|
||||
*/
|
||||
public function authenticatedRequest(Google_Http_Request $request)
|
||||
{
|
||||
$request = $this->sign($request);
|
||||
return $this->io->makeRequest($request);
|
||||
}
|
||||
|
||||
public function sign(Google_Http_Request $request)
|
||||
{
|
||||
$key = $this->client->getClassConfig($this, 'developer_key');
|
||||
if ($key) {
|
||||
$this->client->getLogger()->debug(
|
||||
'Simple API Access developer key authentication'
|
||||
);
|
||||
$request->setQueryParam('key', $key);
|
||||
}
|
||||
return $request;
|
||||
}
|
||||
}
|
53
app/api/Google/Cache/Abstract.php
Executable file
53
app/api/Google/Cache/Abstract.php
Executable file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract storage class
|
||||
*
|
||||
* @author Chris Chabot <chabotc@google.com>
|
||||
*/
|
||||
abstract class Google_Cache_Abstract
|
||||
{
|
||||
|
||||
abstract public function __construct(Google_Client $client);
|
||||
|
||||
/**
|
||||
* Retrieves the data for the given key, or false if they
|
||||
* key is unknown or expired
|
||||
*
|
||||
* @param String $key The key who's data to retrieve
|
||||
* @param boolean|int $expiration Expiration time in seconds
|
||||
*
|
||||
*/
|
||||
abstract public function get($key, $expiration = false);
|
||||
|
||||
/**
|
||||
* Store the key => $value set. The $value is serialized
|
||||
* by this function so can be of any type
|
||||
*
|
||||
* @param string $key Key of the data
|
||||
* @param string $value data
|
||||
*/
|
||||
abstract public function set($key, $value);
|
||||
|
||||
/**
|
||||
* Removes the key/data pair for the given $key
|
||||
*
|
||||
* @param String $key
|
||||
*/
|
||||
abstract public function delete($key);
|
||||
}
|
113
app/api/Google/Cache/Apc.php
Executable file
113
app/api/Google/Cache/Apc.php
Executable file
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* A persistent storage class based on the APC cache, which is not
|
||||
* really very persistent, as soon as you restart your web server
|
||||
* the storage will be wiped, however for debugging and/or speed
|
||||
* it can be useful, and cache is a lot cheaper then storage.
|
||||
*
|
||||
* @author Chris Chabot <chabotc@google.com>
|
||||
*/
|
||||
class Google_Cache_Apc extends Google_Cache_Abstract
|
||||
{
|
||||
/**
|
||||
* @var Google_Client the current client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
if (! function_exists('apc_add') ) {
|
||||
$error = "Apc functions not available";
|
||||
|
||||
$client->getLogger()->error($error);
|
||||
throw new Google_Cache_Exception($error);
|
||||
}
|
||||
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get($key, $expiration = false)
|
||||
{
|
||||
$ret = apc_fetch($key);
|
||||
if ($ret === false) {
|
||||
$this->client->getLogger()->debug(
|
||||
'APC cache miss',
|
||||
array('key' => $key)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (is_numeric($expiration) && (time() - $ret['time'] > $expiration)) {
|
||||
$this->client->getLogger()->debug(
|
||||
'APC cache miss (expired)',
|
||||
array('key' => $key, 'var' => $ret)
|
||||
);
|
||||
$this->delete($key);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'APC cache hit',
|
||||
array('key' => $key, 'var' => $ret)
|
||||
);
|
||||
|
||||
return $ret['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$var = array('time' => time(), 'data' => $value);
|
||||
$rc = apc_store($key, $var);
|
||||
|
||||
if ($rc == false) {
|
||||
$this->client->getLogger()->error(
|
||||
'APC cache set failed',
|
||||
array('key' => $key, 'var' => $var)
|
||||
);
|
||||
throw new Google_Cache_Exception("Couldn't store data");
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'APC cache set',
|
||||
array('key' => $key, 'var' => $var)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @param String $key
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
$this->client->getLogger()->debug(
|
||||
'APC cache delete',
|
||||
array('key' => $key)
|
||||
);
|
||||
apc_delete($key);
|
||||
}
|
||||
}
|
24
app/api/Google/Cache/Exception.php
Executable file
24
app/api/Google/Cache/Exception.php
Executable file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
class Google_Cache_Exception extends Google_Exception
|
||||
{
|
||||
}
|
209
app/api/Google/Cache/File.php
Executable file
209
app/api/Google/Cache/File.php
Executable file
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/*
|
||||
* This class implements a basic on disk storage. While that does
|
||||
* work quite well it's not the most elegant and scalable solution.
|
||||
* It will also get you into a heap of trouble when you try to run
|
||||
* this in a clustered environment.
|
||||
*
|
||||
* @author Chris Chabot <chabotc@google.com>
|
||||
*/
|
||||
class Google_Cache_File extends Google_Cache_Abstract
|
||||
{
|
||||
const MAX_LOCK_RETRIES = 10;
|
||||
private $path;
|
||||
private $fh;
|
||||
|
||||
/**
|
||||
* @var Google_Client the current client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->path = $this->client->getClassConfig($this, 'directory');
|
||||
}
|
||||
|
||||
public function get($key, $expiration = false)
|
||||
{
|
||||
$storageFile = $this->getCacheFile($key);
|
||||
$data = false;
|
||||
|
||||
if (!file_exists($storageFile)) {
|
||||
$this->client->getLogger()->debug(
|
||||
'File cache miss',
|
||||
array('key' => $key, 'file' => $storageFile)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($expiration) {
|
||||
$mtime = filemtime($storageFile);
|
||||
if ((time() - $mtime) >= $expiration) {
|
||||
$this->client->getLogger()->debug(
|
||||
'File cache miss (expired)',
|
||||
array('key' => $key, 'file' => $storageFile)
|
||||
);
|
||||
$this->delete($key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->acquireReadLock($storageFile)) {
|
||||
if (filesize($storageFile) > 0) {
|
||||
$data = fread($this->fh, filesize($storageFile));
|
||||
$data = unserialize($data);
|
||||
} else {
|
||||
$this->client->getLogger()->debug(
|
||||
'Cache file was empty',
|
||||
array('file' => $storageFile)
|
||||
);
|
||||
}
|
||||
$this->unlock($storageFile);
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'File cache hit',
|
||||
array('key' => $key, 'file' => $storageFile, 'var' => $data)
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function set($key, $value)
|
||||
{
|
||||
$storageFile = $this->getWriteableCacheFile($key);
|
||||
if ($this->acquireWriteLock($storageFile)) {
|
||||
// We serialize the whole request object, since we don't only want the
|
||||
// responseContent but also the postBody used, headers, size, etc.
|
||||
$data = serialize($value);
|
||||
$result = fwrite($this->fh, $data);
|
||||
$this->unlock($storageFile);
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'File cache set',
|
||||
array('key' => $key, 'file' => $storageFile, 'var' => $value)
|
||||
);
|
||||
} else {
|
||||
$this->client->getLogger()->notice(
|
||||
'File cache set failed',
|
||||
array('key' => $key, 'file' => $storageFile)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
$file = $this->getCacheFile($key);
|
||||
if (file_exists($file) && !unlink($file)) {
|
||||
$this->client->getLogger()->error(
|
||||
'File cache delete failed',
|
||||
array('key' => $key, 'file' => $file)
|
||||
);
|
||||
throw new Google_Cache_Exception("Cache file could not be deleted");
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'File cache delete',
|
||||
array('key' => $key, 'file' => $file)
|
||||
);
|
||||
}
|
||||
|
||||
private function getWriteableCacheFile($file)
|
||||
{
|
||||
return $this->getCacheFile($file, true);
|
||||
}
|
||||
|
||||
private function getCacheFile($file, $forWrite = false)
|
||||
{
|
||||
return $this->getCacheDir($file, $forWrite) . '/' . md5($file);
|
||||
}
|
||||
|
||||
private function getCacheDir($file, $forWrite)
|
||||
{
|
||||
// use the first 2 characters of the hash as a directory prefix
|
||||
// this should prevent slowdowns due to huge directory listings
|
||||
// and thus give some basic amount of scalability
|
||||
$storageDir = $this->path . '/' . substr(md5($file), 0, 2);
|
||||
if ($forWrite && ! is_dir($storageDir)) {
|
||||
if (! mkdir($storageDir, 0700, true)) {
|
||||
$this->client->getLogger()->error(
|
||||
'File cache creation failed',
|
||||
array('dir' => $storageDir)
|
||||
);
|
||||
throw new Google_Cache_Exception("Could not create storage directory: $storageDir");
|
||||
}
|
||||
}
|
||||
return $storageDir;
|
||||
}
|
||||
|
||||
private function acquireReadLock($storageFile)
|
||||
{
|
||||
return $this->acquireLock(LOCK_SH, $storageFile);
|
||||
}
|
||||
|
||||
private function acquireWriteLock($storageFile)
|
||||
{
|
||||
$rc = $this->acquireLock(LOCK_EX, $storageFile);
|
||||
if (!$rc) {
|
||||
$this->client->getLogger()->notice(
|
||||
'File cache write lock failed',
|
||||
array('file' => $storageFile)
|
||||
);
|
||||
$this->delete($storageFile);
|
||||
}
|
||||
return $rc;
|
||||
}
|
||||
|
||||
private function acquireLock($type, $storageFile)
|
||||
{
|
||||
$mode = $type == LOCK_EX ? "w" : "r";
|
||||
$this->fh = fopen($storageFile, $mode);
|
||||
if (!$this->fh) {
|
||||
$this->client->getLogger()->error(
|
||||
'Failed to open file during lock acquisition',
|
||||
array('file' => $storageFile)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if ($type == LOCK_EX) {
|
||||
chmod($storageFile, 0600);
|
||||
}
|
||||
$count = 0;
|
||||
while (!flock($this->fh, $type | LOCK_NB)) {
|
||||
// Sleep for 10ms.
|
||||
usleep(10000);
|
||||
if (++$count < self::MAX_LOCK_RETRIES) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function unlock($storageFile)
|
||||
{
|
||||
if ($this->fh) {
|
||||
flock($this->fh, LOCK_UN);
|
||||
}
|
||||
}
|
||||
}
|
184
app/api/Google/Cache/Memcache.php
Executable file
184
app/api/Google/Cache/Memcache.php
Executable file
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* A persistent storage class based on the memcache, which is not
|
||||
* really very persistent, as soon as you restart your memcache daemon
|
||||
* the storage will be wiped.
|
||||
*
|
||||
* Will use either the memcache or memcached extensions, preferring
|
||||
* memcached.
|
||||
*
|
||||
* @author Chris Chabot <chabotc@google.com>
|
||||
*/
|
||||
class Google_Cache_Memcache extends Google_Cache_Abstract
|
||||
{
|
||||
private $connection = false;
|
||||
private $mc = false;
|
||||
private $host;
|
||||
private $port;
|
||||
|
||||
/**
|
||||
* @var Google_Client the current client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
if (!function_exists('memcache_connect') && !class_exists("Memcached")) {
|
||||
$error = "Memcache functions not available";
|
||||
|
||||
$client->getLogger()->error($error);
|
||||
throw new Google_Cache_Exception($error);
|
||||
}
|
||||
|
||||
$this->client = $client;
|
||||
|
||||
if ($client->isAppEngine()) {
|
||||
// No credentials needed for GAE.
|
||||
$this->mc = new Memcached();
|
||||
$this->connection = true;
|
||||
} else {
|
||||
$this->host = $client->getClassConfig($this, 'host');
|
||||
$this->port = $client->getClassConfig($this, 'port');
|
||||
if (empty($this->host) || (empty($this->port) && (string) $this->port != "0")) {
|
||||
$error = "You need to supply a valid memcache host and port";
|
||||
|
||||
$client->getLogger()->error($error);
|
||||
throw new Google_Cache_Exception($error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get($key, $expiration = false)
|
||||
{
|
||||
$this->connect();
|
||||
$ret = false;
|
||||
if ($this->mc) {
|
||||
$ret = $this->mc->get($key);
|
||||
} else {
|
||||
$ret = memcache_get($this->connection, $key);
|
||||
}
|
||||
if ($ret === false) {
|
||||
$this->client->getLogger()->debug(
|
||||
'Memcache cache miss',
|
||||
array('key' => $key)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (is_numeric($expiration) && (time() - $ret['time'] > $expiration)) {
|
||||
$this->client->getLogger()->debug(
|
||||
'Memcache cache miss (expired)',
|
||||
array('key' => $key, 'var' => $ret)
|
||||
);
|
||||
$this->delete($key);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'Memcache cache hit',
|
||||
array('key' => $key, 'var' => $ret)
|
||||
);
|
||||
|
||||
return $ret['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @throws Google_Cache_Exception
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->connect();
|
||||
// we store it with the cache_time default expiration so objects will at
|
||||
// least get cleaned eventually.
|
||||
$data = array('time' => time(), 'data' => $value);
|
||||
$rc = false;
|
||||
if ($this->mc) {
|
||||
$rc = $this->mc->set($key, $data);
|
||||
} else {
|
||||
$rc = memcache_set($this->connection, $key, $data, false);
|
||||
}
|
||||
if ($rc == false) {
|
||||
$this->client->getLogger()->error(
|
||||
'Memcache cache set failed',
|
||||
array('key' => $key, 'var' => $data)
|
||||
);
|
||||
|
||||
throw new Google_Cache_Exception("Couldn't store data in cache");
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'Memcache cache set',
|
||||
array('key' => $key, 'var' => $data)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @param String $key
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
$this->connect();
|
||||
if ($this->mc) {
|
||||
$this->mc->delete($key, 0);
|
||||
} else {
|
||||
memcache_delete($this->connection, $key, 0);
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'Memcache cache delete',
|
||||
array('key' => $key)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialiser for memcache connection. Uses pconnect for to take
|
||||
* advantage of the persistence pool where possible.
|
||||
*/
|
||||
private function connect()
|
||||
{
|
||||
if ($this->connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (class_exists("Memcached")) {
|
||||
$this->mc = new Memcached();
|
||||
$this->mc->addServer($this->host, $this->port);
|
||||
$this->connection = true;
|
||||
} else {
|
||||
$this->connection = memcache_pconnect($this->host, $this->port);
|
||||
}
|
||||
|
||||
if (! $this->connection) {
|
||||
$error = "Couldn't connect to memcache server";
|
||||
|
||||
$this->client->getLogger()->error($error);
|
||||
throw new Google_Cache_Exception($error);
|
||||
}
|
||||
}
|
||||
}
|
57
app/api/Google/Cache/Null.php
Executable file
57
app/api/Google/Cache/Null.php
Executable file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* A blank storage class, for cases where caching is not
|
||||
* required.
|
||||
*/
|
||||
class Google_Cache_Null extends Google_Cache_Abstract
|
||||
{
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get($key, $expiration = false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
// Nop.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @param String $key
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
// Nop.
|
||||
}
|
||||
}
|
715
app/api/Google/Client.php
Executable file
715
app/api/Google/Client.php
Executable file
|
@ -0,0 +1,715 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* The Google API Client
|
||||
* https://github.com/google/google-api-php-client
|
||||
*/
|
||||
class Google_Client
|
||||
{
|
||||
const LIBVER = "1.1.5";
|
||||
const USER_AGENT_SUFFIX = "google-api-php-client/";
|
||||
/**
|
||||
* @var Google_Auth_Abstract $auth
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
/**
|
||||
* @var Google_IO_Abstract $io
|
||||
*/
|
||||
private $io;
|
||||
|
||||
/**
|
||||
* @var Google_Cache_Abstract $cache
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @var Google_Config $config
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var Google_Logger_Abstract $logger
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @var boolean $deferExecution
|
||||
*/
|
||||
private $deferExecution = false;
|
||||
|
||||
/** @var array $scopes */
|
||||
// Scopes requested by the client
|
||||
protected $requestedScopes = [];
|
||||
|
||||
// definitions of services that are discovered.
|
||||
protected $services = [];
|
||||
|
||||
// Used to track authenticated state, can't discover services after doing authenticate()
|
||||
private $authenticated = false;
|
||||
|
||||
/**
|
||||
* Construct the Google Client.
|
||||
*
|
||||
* @param $config Google_Config or string for the ini file to load
|
||||
*/
|
||||
public function __construct($config = null)
|
||||
{
|
||||
if (is_string($config) && strlen($config)) {
|
||||
$config = new Google_Config($config);
|
||||
} else if ( !($config instanceof Google_Config)) {
|
||||
$config = new Google_Config();
|
||||
|
||||
if ($this->isAppEngine()) {
|
||||
// Automatically use Memcache if we're in AppEngine.
|
||||
$config->setCacheClass('Google_Cache_Memcache');
|
||||
}
|
||||
|
||||
if (version_compare(phpversion(), "5.3.4", "<=") || $this->isAppEngine()) {
|
||||
// Automatically disable compress.zlib, as currently unsupported.
|
||||
$config->setClassConfig('Google_Http_Request', 'disable_gzip', true);
|
||||
}
|
||||
}
|
||||
|
||||
if ($config->getIoClass() == Google_Config::USE_AUTO_IO_SELECTION) {
|
||||
if (function_exists('curl_version') && function_exists('curl_exec')
|
||||
&& !$this->isAppEngine()) {
|
||||
$config->setIoClass("Google_IO_Curl");
|
||||
} else {
|
||||
$config->setIoClass("Google_IO_Stream");
|
||||
}
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string containing the version of the library.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLibraryVersion()
|
||||
{
|
||||
return self::LIBVER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to exchange a code for an valid authentication token.
|
||||
* If $crossClient is set to true, the request body will not include
|
||||
* the request_uri argument
|
||||
* Helper wrapped around the OAuth 2.0 implementation.
|
||||
*
|
||||
* @param $code string code from accounts.google.com
|
||||
* @param $crossClient boolean, whether this is a cross-client authentication
|
||||
* @return string token
|
||||
*/
|
||||
public function authenticate($code, $crossClient = false)
|
||||
{
|
||||
$this->authenticated = true;
|
||||
return $this->getAuth()->authenticate($code, $crossClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a service account key and parameters from a JSON
|
||||
* file from the Google Developer Console. Uses that and the
|
||||
* given array of scopes to return an assertion credential for
|
||||
* use with refreshTokenWithAssertionCredential.
|
||||
*
|
||||
* @param string $jsonLocation File location of the project-key.json.
|
||||
* @param array $scopes The scopes to assert.
|
||||
* @return Google_Auth_AssertionCredentials.
|
||||
* @
|
||||
*/
|
||||
public function loadServiceAccountJson($jsonLocation, $scopes)
|
||||
{
|
||||
$data = json_decode(file_get_contents($jsonLocation));
|
||||
if (isset($data->type) && $data->type == 'service_account') {
|
||||
// Service Account format.
|
||||
$cred = new Google_Auth_AssertionCredentials(
|
||||
$data->client_email,
|
||||
$scopes,
|
||||
$data->private_key
|
||||
);
|
||||
return $cred;
|
||||
} else {
|
||||
throw new Google_Exception("Invalid service account JSON file.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auth config from the JSON string provided.
|
||||
* This structure should match the file downloaded from
|
||||
* the "Download JSON" button on in the Google Developer
|
||||
* Console.
|
||||
* @param string $json the configuration json
|
||||
* @throws Google_Exception
|
||||
*/
|
||||
public function setAuthConfig($json)
|
||||
{
|
||||
$data = json_decode($json);
|
||||
$key = isset($data->installed) ? 'installed' : 'web';
|
||||
if (!isset($data->$key)) {
|
||||
throw new Google_Exception("Invalid client secret JSON file.");
|
||||
}
|
||||
$this->setClientId($data->$key->client_id);
|
||||
$this->setClientSecret($data->$key->client_secret);
|
||||
if (isset($data->$key->redirect_uris)) {
|
||||
$this->setRedirectUri($data->$key->redirect_uris[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auth config from the JSON file in the path
|
||||
* provided. This should match the file downloaded from
|
||||
* the "Download JSON" button on in the Google Developer
|
||||
* Console.
|
||||
* @param string $file the file location of the client json
|
||||
*/
|
||||
public function setAuthConfigFile($file)
|
||||
{
|
||||
$this->setAuthConfig(file_get_contents($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Google_Auth_Exception
|
||||
* @return array
|
||||
* @visible For Testing
|
||||
*/
|
||||
public function prepareScopes()
|
||||
{
|
||||
if (empty($this->requestedScopes)) {
|
||||
throw new Google_Auth_Exception("No scopes specified");
|
||||
}
|
||||
$scopes = implode(' ', $this->requestedScopes);
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OAuth 2.0 access token using the string that resulted from calling createAuthUrl()
|
||||
* or Google_Client#getAccessToken().
|
||||
* @param string $accessToken JSON encoded string containing in the following format:
|
||||
* {"access_token":"TOKEN", "refresh_token":"TOKEN", "token_type":"Bearer",
|
||||
* "expires_in":3600, "id_token":"TOKEN", "created":1320790426}
|
||||
*/
|
||||
public function setAccessToken($accessToken)
|
||||
{
|
||||
if ($accessToken == 'null') {
|
||||
$accessToken = null;
|
||||
}
|
||||
$this->getAuth()->setAccessToken($accessToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the authenticator object
|
||||
* @param Google_Auth_Abstract $auth
|
||||
*/
|
||||
public function setAuth(Google_Auth_Abstract $auth)
|
||||
{
|
||||
$this->config->setAuthClass(get_class($auth));
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IO object
|
||||
* @param Google_IO_Abstract $io
|
||||
*/
|
||||
public function setIo(Google_IO_Abstract $io)
|
||||
{
|
||||
$this->config->setIoClass(get_class($io));
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Cache object
|
||||
* @param Google_Cache_Abstract $cache
|
||||
*/
|
||||
public function setCache(Google_Cache_Abstract $cache)
|
||||
{
|
||||
$this->config->setCacheClass(get_class($cache));
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Logger object
|
||||
* @param Google_Logger_Abstract $logger
|
||||
*/
|
||||
public function setLogger(Google_Logger_Abstract $logger)
|
||||
{
|
||||
$this->config->setLoggerClass(get_class($logger));
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the OAuth 2.0 authorization request URI.
|
||||
* @return string
|
||||
*/
|
||||
public function createAuthUrl()
|
||||
{
|
||||
$scopes = $this->prepareScopes();
|
||||
return $this->getAuth()->createAuthUrl($scopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OAuth 2.0 access token.
|
||||
* @return string $accessToken JSON encoded string in the following format:
|
||||
* {"access_token":"TOKEN", "refresh_token":"TOKEN", "token_type":"Bearer",
|
||||
* "expires_in":3600,"id_token":"TOKEN", "created":1320790426}
|
||||
*/
|
||||
public function getAccessToken()
|
||||
{
|
||||
$token = $this->getAuth()->getAccessToken();
|
||||
// The response is json encoded, so could be the string null.
|
||||
// It is arguable whether this check should be here or lower
|
||||
// in the library.
|
||||
return (null == $token || 'null' == $token || '[]' == $token) ? null : $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OAuth 2.0 refresh token.
|
||||
* @return string $refreshToken refresh token or null if not available
|
||||
*/
|
||||
public function getRefreshToken()
|
||||
{
|
||||
return $this->getAuth()->getRefreshToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the access_token is expired.
|
||||
* @return bool Returns True if the access_token is expired.
|
||||
*/
|
||||
public function isAccessTokenExpired()
|
||||
{
|
||||
return $this->getAuth()->isAccessTokenExpired();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set OAuth 2.0 "state" parameter to achieve per-request customization.
|
||||
* @see http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.1.2.2
|
||||
* @param string $state
|
||||
*/
|
||||
public function setState($state)
|
||||
{
|
||||
$this->getAuth()->setState($state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessType Possible values for access_type include:
|
||||
* {@code "offline"} to request offline access from the user.
|
||||
* {@code "online"} to request online access from the user.
|
||||
*/
|
||||
public function setAccessType($accessType)
|
||||
{
|
||||
$this->config->setAccessType($accessType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $approvalPrompt Possible values for approval_prompt include:
|
||||
* {@code "force"} to force the approval UI to appear. (This is the default value)
|
||||
* {@code "auto"} to request auto-approval when possible.
|
||||
*/
|
||||
public function setApprovalPrompt($approvalPrompt)
|
||||
{
|
||||
$this->config->setApprovalPrompt($approvalPrompt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the login hint, email address or sub id.
|
||||
* @param string $loginHint
|
||||
*/
|
||||
public function setLoginHint($loginHint)
|
||||
{
|
||||
$this->config->setLoginHint($loginHint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the application name, this is included in the User-Agent HTTP header.
|
||||
* @param string $applicationName
|
||||
*/
|
||||
public function setApplicationName($applicationName)
|
||||
{
|
||||
$this->config->setApplicationName($applicationName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OAuth 2.0 Client ID.
|
||||
* @param string $clientId
|
||||
*/
|
||||
public function setClientId($clientId)
|
||||
{
|
||||
$this->config->setClientId($clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OAuth 2.0 Client Secret.
|
||||
* @param string $clientSecret
|
||||
*/
|
||||
public function setClientSecret($clientSecret)
|
||||
{
|
||||
$this->config->setClientSecret($clientSecret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OAuth 2.0 Redirect URI.
|
||||
* @param string $redirectUri
|
||||
*/
|
||||
public function setRedirectUri($redirectUri)
|
||||
{
|
||||
$this->config->setRedirectUri($redirectUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* If 'plus.login' is included in the list of requested scopes, you can use
|
||||
* this method to define types of app activities that your app will write.
|
||||
* You can find a list of available types here:
|
||||
* @link https://developers.google.com/+/api/moment-types
|
||||
*
|
||||
* @param array $requestVisibleActions Array of app activity types
|
||||
*/
|
||||
public function setRequestVisibleActions($requestVisibleActions)
|
||||
{
|
||||
if (is_array($requestVisibleActions)) {
|
||||
$requestVisibleActions = join(" ", $requestVisibleActions);
|
||||
}
|
||||
$this->config->setRequestVisibleActions($requestVisibleActions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the developer key to use, these are obtained through the API Console.
|
||||
* @see http://code.google.com/apis/console-help/#generatingdevkeys
|
||||
* @param string $developerKey
|
||||
*/
|
||||
public function setDeveloperKey($developerKey)
|
||||
{
|
||||
$this->config->setDeveloperKey($developerKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hd (hosted domain) parameter streamlines the login process for
|
||||
* Google Apps hosted accounts. By including the domain of the user, you
|
||||
* restrict sign-in to accounts at that domain.
|
||||
* @param $hd string - the domain to use.
|
||||
*/
|
||||
public function setHostedDomain($hd)
|
||||
{
|
||||
$this->config->setHostedDomain($hd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the prompt hint. Valid values are none, consent and select_account.
|
||||
* If no value is specified and the user has not previously authorized
|
||||
* access, then the user is shown a consent screen.
|
||||
* @param $prompt string
|
||||
*/
|
||||
public function setPrompt($prompt)
|
||||
{
|
||||
$this->config->setPrompt($prompt);
|
||||
}
|
||||
|
||||
/**
|
||||
* openid.realm is a parameter from the OpenID 2.0 protocol, not from OAuth
|
||||
* 2.0. It is used in OpenID 2.0 requests to signify the URL-space for which
|
||||
* an authentication request is valid.
|
||||
* @param $realm string - the URL-space to use.
|
||||
*/
|
||||
public function setOpenidRealm($realm)
|
||||
{
|
||||
$this->config->setOpenidRealm($realm);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is provided with the value true, and the authorization request is
|
||||
* granted, the authorization will include any previous authorizations
|
||||
* granted to this user/application combination for other scopes.
|
||||
* @param $include boolean - the URL-space to use.
|
||||
*/
|
||||
public function setIncludeGrantedScopes($include)
|
||||
{
|
||||
$this->config->setIncludeGrantedScopes($include);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a fresh OAuth 2.0 access token with the given refresh token.
|
||||
* @param string $refreshToken
|
||||
*/
|
||||
public function refreshToken($refreshToken)
|
||||
{
|
||||
$this->getAuth()->refreshToken($refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke an OAuth2 access token or refresh token. This method will revoke the current access
|
||||
* token, if a token isn't provided.
|
||||
* @throws Google_Auth_Exception
|
||||
* @param string|null $token The token (access token or a refresh token) that should be revoked.
|
||||
* @return boolean Returns True if the revocation was successful, otherwise False.
|
||||
*/
|
||||
public function revokeToken($token = null)
|
||||
{
|
||||
return $this->getAuth()->revokeToken($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify an id_token. This method will verify the current id_token, if one
|
||||
* isn't provided.
|
||||
* @throws Google_Auth_Exception
|
||||
* @param string|null $token The token (id_token) that should be verified.
|
||||
* @return Google_Auth_LoginTicket Returns an apiLoginTicket if the verification was
|
||||
* successful.
|
||||
*/
|
||||
public function verifyIdToken($token = null)
|
||||
{
|
||||
return $this->getAuth()->verifyIdToken($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a JWT that was signed with your own certificates.
|
||||
*
|
||||
* @param $id_token string The JWT token
|
||||
* @param $cert_location array of certificates
|
||||
* @param $audience string the expected consumer of the token
|
||||
* @param $issuer string the expected issuer, defaults to Google
|
||||
* @param [$max_expiry] the max lifetime of a token, defaults to MAX_TOKEN_LIFETIME_SECS
|
||||
* @return mixed token information if valid, false if not
|
||||
*/
|
||||
public function verifySignedJwt($id_token, $cert_location, $audience, $issuer, $max_expiry = null)
|
||||
{
|
||||
$auth = new Google_Auth_OAuth2($this);
|
||||
$certs = $auth->retrieveCertsFromLocation($cert_location);
|
||||
return $auth->verifySignedJwtWithCerts($id_token, $certs, $audience, $issuer, $max_expiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $creds Google_Auth_AssertionCredentials
|
||||
*/
|
||||
public function setAssertionCredentials(Google_Auth_AssertionCredentials $creds)
|
||||
{
|
||||
$this->getAuth()->setAssertionCredentials($creds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the scopes to be requested. Must be called before createAuthUrl().
|
||||
* Will remove any previously configured scopes.
|
||||
* @param array $scopes, ie: array('https://www.googleapis.com/auth/plus.login',
|
||||
* 'https://www.googleapis.com/auth/moderator')
|
||||
*/
|
||||
public function setScopes($scopes)
|
||||
{
|
||||
$this->requestedScopes = [];
|
||||
$this->addScope($scopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions adds a scope to be requested as part of the OAuth2.0 flow.
|
||||
* Will append any scopes not previously requested to the scope parameter.
|
||||
* A single string will be treated as a scope to request. An array of strings
|
||||
* will each be appended.
|
||||
* @param $scope_or_scopes string|array e.g. "profile"
|
||||
*/
|
||||
public function addScope($scope_or_scopes)
|
||||
{
|
||||
if (is_string($scope_or_scopes) && !in_array($scope_or_scopes, $this->requestedScopes)) {
|
||||
$this->requestedScopes[] = $scope_or_scopes;
|
||||
} else if (is_array($scope_or_scopes)) {
|
||||
foreach ($scope_or_scopes as $scope) {
|
||||
$this->addScope($scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of scopes requested by the client
|
||||
* @return array the list of scopes
|
||||
*
|
||||
*/
|
||||
public function getScopes()
|
||||
{
|
||||
return $this->requestedScopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare whether batch calls should be used. This may increase throughput
|
||||
* by making multiple requests in one connection.
|
||||
*
|
||||
* @param boolean $useBatch True if the batch support should
|
||||
* be enabled. Defaults to False.
|
||||
*/
|
||||
public function setUseBatch($useBatch)
|
||||
{
|
||||
// This is actually an alias for setDefer.
|
||||
$this->setDefer($useBatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare whether making API calls should make the call immediately, or
|
||||
* return a request which can be called with ->execute();
|
||||
*
|
||||
* @param boolean $defer True if calls should not be executed right away.
|
||||
*/
|
||||
public function setDefer($defer)
|
||||
{
|
||||
$this->deferExecution = $defer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to execute deferred HTTP requests.
|
||||
*
|
||||
* @param $request Google_Http_Request|Google_Http_Batch
|
||||
* @throws Google_Exception
|
||||
* @return object of the type of the expected class or array.
|
||||
*/
|
||||
public function execute($request)
|
||||
{
|
||||
if ($request instanceof Google_Http_Request) {
|
||||
$request->setUserAgent(
|
||||
$this->getApplicationName()
|
||||
. " " . self::USER_AGENT_SUFFIX
|
||||
. $this->getLibraryVersion()
|
||||
);
|
||||
if (!$this->getClassConfig("Google_Http_Request", "disable_gzip")) {
|
||||
$request->enableGzip();
|
||||
}
|
||||
$request->maybeMoveParametersToBody();
|
||||
return Google_Http_REST::execute($this, $request);
|
||||
} else if ($request instanceof Google_Http_Batch) {
|
||||
return $request->execute();
|
||||
} else {
|
||||
throw new Google_Exception("Do not know how to execute this type of object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not to return raw requests
|
||||
* @return boolean
|
||||
*/
|
||||
public function shouldDefer()
|
||||
{
|
||||
return $this->deferExecution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Google_Auth_Abstract Authentication implementation
|
||||
*/
|
||||
public function getAuth()
|
||||
{
|
||||
if (!isset($this->auth)) {
|
||||
$class = $this->config->getAuthClass();
|
||||
$this->auth = new $class($this);
|
||||
}
|
||||
return $this->auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Google_IO_Abstract IO implementation
|
||||
*/
|
||||
public function getIo()
|
||||
{
|
||||
if (!isset($this->io)) {
|
||||
$class = $this->config->getIoClass();
|
||||
$this->io = new $class($this);
|
||||
}
|
||||
return $this->io;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Google_Cache_Abstract Cache implementation
|
||||
*/
|
||||
public function getCache()
|
||||
{
|
||||
if (!isset($this->cache)) {
|
||||
$class = $this->config->getCacheClass();
|
||||
$this->cache = new $class($this);
|
||||
}
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Google_Logger_Abstract Logger implementation
|
||||
*/
|
||||
public function getLogger()
|
||||
{
|
||||
if (!isset($this->logger)) {
|
||||
$class = $this->config->getLoggerClass();
|
||||
$this->logger = new $class($this);
|
||||
}
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve custom configuration for a specific class.
|
||||
* @param $class string|object - class or instance of class to retrieve
|
||||
* @param $key string optional - key to retrieve
|
||||
* @return array
|
||||
*/
|
||||
public function getClassConfig($class, $key = null)
|
||||
{
|
||||
if (!is_string($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
return $this->config->getClassConfig($class, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set configuration specific to a given class.
|
||||
* $config->setClassConfig('Google_Cache_File',
|
||||
* array('directory' => '/tmp/cache'));
|
||||
* @param $class string|object - The class name for the configuration
|
||||
* @param $config string key or an array of configuration values
|
||||
* @param $value string optional - if $config is a key, the value
|
||||
*
|
||||
*/
|
||||
public function setClassConfig($class, $config, $value = null)
|
||||
{
|
||||
if (!is_string($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
$this->config->setClassConfig($class, $config, $value);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the base URL to use for calls to the APIs
|
||||
*/
|
||||
public function getBasePath()
|
||||
{
|
||||
return $this->config->getBasePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the name of the application
|
||||
*/
|
||||
public function getApplicationName()
|
||||
{
|
||||
return $this->config->getApplicationName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we running in Google AppEngine?
|
||||
* return bool
|
||||
*/
|
||||
public function isAppEngine()
|
||||
{
|
||||
return (isset($_SERVER['SERVER_SOFTWARE']) &&
|
||||
strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine') !== false);
|
||||
}
|
||||
}
|
123
app/api/Google/Collection.php
Executable file
123
app/api/Google/Collection.php
Executable file
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension to the regular Google_Model that automatically
|
||||
* exposes the items array for iteration, so you can just
|
||||
* iterate over the object rather than a reference inside.
|
||||
*/
|
||||
class Google_Collection extends Google_Model implements Iterator, Countable
|
||||
{
|
||||
protected $collection_key = 'items';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
if (isset($this->modelData[$this->collection_key])
|
||||
&& is_array($this->modelData[$this->collection_key])) {
|
||||
reset($this->modelData[$this->collection_key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
$this->coerceType($this->key());
|
||||
if (is_array($this->modelData[$this->collection_key])) {
|
||||
return current($this->modelData[$this->collection_key]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|float|int|string|null
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
if (isset($this->modelData[$this->collection_key])
|
||||
&& is_array($this->modelData[$this->collection_key])) {
|
||||
return key($this->modelData[$this->collection_key]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|void
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->modelData[$this->collection_key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
$key = $this->key();
|
||||
return $key !== null && $key !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
if (!isset($this->modelData[$this->collection_key])) {
|
||||
return 0;
|
||||
}
|
||||
return count($this->modelData[$this->collection_key]);
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
if (!is_numeric($offset)) {
|
||||
return parent::offsetExists($offset);
|
||||
}
|
||||
return isset($this->modelData[$this->collection_key][$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if (!is_numeric($offset)) {
|
||||
return parent::offsetGet($offset);
|
||||
}
|
||||
$this->coerceType($offset);
|
||||
return $this->modelData[$this->collection_key][$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (!is_numeric($offset)) {
|
||||
return parent::offsetSet($offset, $value);
|
||||
}
|
||||
$this->modelData[$this->collection_key][$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
if (!is_numeric($offset)) {
|
||||
return parent::offsetUnset($offset);
|
||||
}
|
||||
unset($this->modelData[$this->collection_key][$offset]);
|
||||
}
|
||||
|
||||
private function coerceType($offset)
|
||||
{
|
||||
$typeKey = $this->keyType($this->collection_key);
|
||||
if (isset($this->$typeKey) && !is_object($this->modelData[$this->collection_key][$offset])) {
|
||||
$type = $this->$typeKey;
|
||||
$this->modelData[$this->collection_key][$offset] =
|
||||
new $type($this->modelData[$this->collection_key][$offset]);
|
||||
}
|
||||
}
|
||||
}
|
456
app/api/Google/Config.php
Executable file
456
app/api/Google/Config.php
Executable file
|
@ -0,0 +1,456 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class to contain the library configuration for the Google API client.
|
||||
*/
|
||||
class Google_Config
|
||||
{
|
||||
const GZIP_DISABLED = true;
|
||||
const GZIP_ENABLED = false;
|
||||
const GZIP_UPLOADS_ENABLED = true;
|
||||
const GZIP_UPLOADS_DISABLED = false;
|
||||
const USE_AUTO_IO_SELECTION = "auto";
|
||||
const TASK_RETRY_NEVER = 0;
|
||||
const TASK_RETRY_ONCE = 1;
|
||||
const TASK_RETRY_ALWAYS = -1;
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* Create a new Google_Config. Can accept an ini file location with the
|
||||
* local configuration. For example:
|
||||
* application_name="My App"
|
||||
*
|
||||
* @param [$ini_file_location] - optional - The location of the ini file to load
|
||||
*/
|
||||
public function __construct($ini_file_location = null)
|
||||
{
|
||||
$this->configuration = array(
|
||||
// The application_name is included in the User-Agent HTTP header.
|
||||
'application_name' => '',
|
||||
|
||||
// Which Authentication, Storage and HTTP IO classes to use.
|
||||
'auth_class' => 'Google_Auth_OAuth2',
|
||||
'io_class' => self::USE_AUTO_IO_SELECTION,
|
||||
'cache_class' => 'Google_Cache_File',
|
||||
'logger_class' => 'Google_Logger_Null',
|
||||
|
||||
// Don't change these unless you're working against a special development
|
||||
// or testing environment.
|
||||
'base_path' => 'https://www.googleapis.com',
|
||||
|
||||
// Definition of class specific values, like file paths and so on.
|
||||
'classes' => array(
|
||||
'Google_IO_Abstract' => array(
|
||||
'request_timeout_seconds' => 100,
|
||||
),
|
||||
'Google_IO_Curl' => array(
|
||||
'disable_proxy_workaround' => false,
|
||||
'options' => null,
|
||||
),
|
||||
'Google_Logger_Abstract' => array(
|
||||
'level' => 'debug',
|
||||
'log_format' => "[%datetime%] %level%: %message% %context%\n",
|
||||
'date_format' => 'd/M/Y:H:i:s O',
|
||||
'allow_newlines' => true
|
||||
),
|
||||
'Google_Logger_File' => array(
|
||||
'file' => 'php://stdout',
|
||||
'mode' => 0640,
|
||||
'lock' => false,
|
||||
),
|
||||
'Google_Http_Request' => array(
|
||||
// Disable the use of gzip on calls if set to true. Defaults to false.
|
||||
'disable_gzip' => self::GZIP_ENABLED,
|
||||
|
||||
// We default gzip to disabled on uploads even if gzip is otherwise
|
||||
// enabled, due to some issues seen with small packet sizes for uploads.
|
||||
// Please test with this option before enabling gzip for uploads in
|
||||
// a production environment.
|
||||
'enable_gzip_for_uploads' => self::GZIP_UPLOADS_DISABLED,
|
||||
),
|
||||
// If you want to pass in OAuth 2.0 settings, they will need to be
|
||||
// structured like this.
|
||||
'Google_Auth_OAuth2' => array(
|
||||
// Keys for OAuth 2.0 access, see the API console at
|
||||
// https://developers.google.com/console
|
||||
'client_id' => '',
|
||||
'client_secret' => '',
|
||||
'redirect_uri' => '',
|
||||
|
||||
// Simple API access key, also from the API console. Ensure you get
|
||||
// a Server key, and not a Browser key.
|
||||
'developer_key' => '',
|
||||
|
||||
// Other parameters.
|
||||
'hd' => '',
|
||||
'prompt' => '',
|
||||
'openid.realm' => '',
|
||||
'include_granted_scopes' => '',
|
||||
'login_hint' => '',
|
||||
'request_visible_actions' => '',
|
||||
'access_type' => 'online',
|
||||
'approval_prompt' => 'auto',
|
||||
'federated_signon_certs_url' =>
|
||||
'https://www.googleapis.com/oauth2/v1/certs',
|
||||
),
|
||||
'Google_Task_Runner' => array(
|
||||
// Delays are specified in seconds
|
||||
'initial_delay' => 1,
|
||||
'max_delay' => 60,
|
||||
// Base number for exponential backoff
|
||||
'factor' => 2,
|
||||
// A random number between -jitter and jitter will be added to the
|
||||
// factor on each iteration to allow for better distribution of
|
||||
// retries.
|
||||
'jitter' => .5,
|
||||
// Maximum number of retries allowed
|
||||
'retries' => 0
|
||||
),
|
||||
'Google_Service_Exception' => array(
|
||||
'retry_map' => array(
|
||||
'500' => self::TASK_RETRY_ALWAYS,
|
||||
'503' => self::TASK_RETRY_ALWAYS,
|
||||
'rateLimitExceeded' => self::TASK_RETRY_ALWAYS,
|
||||
'userRateLimitExceeded' => self::TASK_RETRY_ALWAYS
|
||||
)
|
||||
),
|
||||
'Google_IO_Exception' => array(
|
||||
'retry_map' => !extension_loaded('curl') ? array() : array(
|
||||
CURLE_COULDNT_RESOLVE_HOST => self::TASK_RETRY_ALWAYS,
|
||||
CURLE_COULDNT_CONNECT => self::TASK_RETRY_ALWAYS,
|
||||
CURLE_OPERATION_TIMEOUTED => self::TASK_RETRY_ALWAYS,
|
||||
CURLE_SSL_CONNECT_ERROR => self::TASK_RETRY_ALWAYS,
|
||||
CURLE_GOT_NOTHING => self::TASK_RETRY_ALWAYS
|
||||
)
|
||||
),
|
||||
// Set a default directory for the file cache.
|
||||
'Google_Cache_File' => array(
|
||||
'directory' => sys_get_temp_dir() . '/Google_Client'
|
||||
)
|
||||
),
|
||||
);
|
||||
if ($ini_file_location) {
|
||||
$ini = parse_ini_file($ini_file_location, true);
|
||||
if (is_array($ini) && count($ini)) {
|
||||
$merged_configuration = $ini + $this->configuration;
|
||||
if (isset($ini['classes']) && isset($this->configuration['classes'])) {
|
||||
$merged_configuration['classes'] = $ini['classes'] + $this->configuration['classes'];
|
||||
}
|
||||
$this->configuration = $merged_configuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set configuration specific to a given class.
|
||||
* $config->setClassConfig('Google_Cache_File',
|
||||
* array('directory' => '/tmp/cache'));
|
||||
* @param $class string The class name for the configuration
|
||||
* @param $config string key or an array of configuration values
|
||||
* @param $value string optional - if $config is a key, the value
|
||||
*/
|
||||
public function setClassConfig($class, $config, $value = null)
|
||||
{
|
||||
if (!is_array($config)) {
|
||||
if (!isset($this->configuration['classes'][$class])) {
|
||||
$this->configuration['classes'][$class] = [];
|
||||
}
|
||||
$this->configuration['classes'][$class][$config] = $value;
|
||||
} else {
|
||||
$this->configuration['classes'][$class] = $config;
|
||||
}
|
||||
}
|
||||
|
||||
public function getClassConfig($class, $key = null)
|
||||
{
|
||||
if (!isset($this->configuration['classes'][$class])) {
|
||||
return null;
|
||||
}
|
||||
if ($key === null) {
|
||||
return $this->configuration['classes'][$class];
|
||||
} else {
|
||||
return $this->configuration['classes'][$class][$key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured cache class.
|
||||
* @return string
|
||||
*/
|
||||
public function getCacheClass()
|
||||
{
|
||||
return $this->configuration['cache_class'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured logger class.
|
||||
* @return string
|
||||
*/
|
||||
public function getLoggerClass()
|
||||
{
|
||||
return $this->configuration['logger_class'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured Auth class.
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthClass()
|
||||
{
|
||||
return $this->configuration['auth_class'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auth class.
|
||||
*
|
||||
* @param $class string the class name to set
|
||||
*/
|
||||
public function setAuthClass($class)
|
||||
{
|
||||
$prev = $this->configuration['auth_class'];
|
||||
if (!isset($this->configuration['classes'][$class]) &&
|
||||
isset($this->configuration['classes'][$prev])) {
|
||||
$this->configuration['classes'][$class] =
|
||||
$this->configuration['classes'][$prev];
|
||||
}
|
||||
$this->configuration['auth_class'] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IO class.
|
||||
*
|
||||
* @param $class string the class name to set
|
||||
*/
|
||||
public function setIoClass($class)
|
||||
{
|
||||
$prev = $this->configuration['io_class'];
|
||||
if (!isset($this->configuration['classes'][$class]) &&
|
||||
isset($this->configuration['classes'][$prev])) {
|
||||
$this->configuration['classes'][$class] =
|
||||
$this->configuration['classes'][$prev];
|
||||
}
|
||||
$this->configuration['io_class'] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cache class.
|
||||
*
|
||||
* @param $class string the class name to set
|
||||
*/
|
||||
public function setCacheClass($class)
|
||||
{
|
||||
$prev = $this->configuration['cache_class'];
|
||||
if (!isset($this->configuration['classes'][$class]) &&
|
||||
isset($this->configuration['classes'][$prev])) {
|
||||
$this->configuration['classes'][$class] =
|
||||
$this->configuration['classes'][$prev];
|
||||
}
|
||||
$this->configuration['cache_class'] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the logger class.
|
||||
*
|
||||
* @param $class string the class name to set
|
||||
*/
|
||||
public function setLoggerClass($class)
|
||||
{
|
||||
$prev = $this->configuration['logger_class'];
|
||||
if (!isset($this->configuration['classes'][$class]) &&
|
||||
isset($this->configuration['classes'][$prev])) {
|
||||
$this->configuration['classes'][$class] =
|
||||
$this->configuration['classes'][$prev];
|
||||
}
|
||||
$this->configuration['logger_class'] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured IO class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIoClass()
|
||||
{
|
||||
return $this->configuration['io_class'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the application name, this is included in the User-Agent HTTP header.
|
||||
* @param string $name
|
||||
*/
|
||||
public function setApplicationName($name)
|
||||
{
|
||||
$this->configuration['application_name'] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the name of the application
|
||||
*/
|
||||
public function getApplicationName()
|
||||
{
|
||||
return $this->configuration['application_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the client ID for the auth class.
|
||||
* @param $clientId string - the API console client ID
|
||||
*/
|
||||
public function setClientId($clientId)
|
||||
{
|
||||
$this->setAuthConfig('client_id', $clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the client secret for the auth class.
|
||||
* @param $secret string - the API console client secret
|
||||
*/
|
||||
public function setClientSecret($secret)
|
||||
{
|
||||
$this->setAuthConfig('client_secret', $secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the redirect uri for the auth class. Note that if using the
|
||||
* Javascript based sign in flow, this should be the string 'postmessage'.
|
||||
*
|
||||
* @param $uri string - the URI that users should be redirected to
|
||||
*/
|
||||
public function setRedirectUri($uri)
|
||||
{
|
||||
$this->setAuthConfig('redirect_uri', $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the app activities for the auth class.
|
||||
* @param $rva string a space separated list of app activity types
|
||||
*/
|
||||
public function setRequestVisibleActions($rva)
|
||||
{
|
||||
$this->setAuthConfig('request_visible_actions', $rva);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the the access type requested (offline or online.)
|
||||
* @param $access string - the access type
|
||||
*/
|
||||
public function setAccessType($access)
|
||||
{
|
||||
$this->setAuthConfig('access_type', $access);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when to show the approval prompt (auto or force)
|
||||
* @param $approval string - the approval request
|
||||
*/
|
||||
public function setApprovalPrompt($approval)
|
||||
{
|
||||
$this->setAuthConfig('approval_prompt', $approval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the login hint (email address or sub identifier)
|
||||
* @param $hint string
|
||||
*/
|
||||
public function setLoginHint($hint)
|
||||
{
|
||||
$this->setAuthConfig('login_hint', $hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the developer key for the auth class. Note that this is separate value
|
||||
* from the client ID - if it looks like a URL, its a client ID!
|
||||
* @param $key string - the API console developer key
|
||||
*/
|
||||
public function setDeveloperKey($key)
|
||||
{
|
||||
$this->setAuthConfig('developer_key', $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hd (hosted domain) parameter streamlines the login process for
|
||||
* Google Apps hosted accounts. By including the domain of the user, you
|
||||
* restrict sign-in to accounts at that domain.
|
||||
*
|
||||
* This should not be used to ensure security on your application - check
|
||||
* the hd values within an id token (@see Google_Auth_LoginTicket) after sign
|
||||
* in to ensure that the user is from the domain you were expecting.
|
||||
*
|
||||
* @param $hd string - the domain to use.
|
||||
*/
|
||||
public function setHostedDomain($hd)
|
||||
{
|
||||
$this->setAuthConfig('hd', $hd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the prompt hint. Valid values are none, consent and select_account.
|
||||
* If no value is specified and the user has not previously authorized
|
||||
* access, then the user is shown a consent screen.
|
||||
* @param $prompt string
|
||||
*/
|
||||
public function setPrompt($prompt)
|
||||
{
|
||||
$this->setAuthConfig('prompt', $prompt);
|
||||
}
|
||||
|
||||
/**
|
||||
* openid.realm is a parameter from the OpenID 2.0 protocol, not from OAuth
|
||||
* 2.0. It is used in OpenID 2.0 requests to signify the URL-space for which
|
||||
* an authentication request is valid.
|
||||
* @param $realm string - the URL-space to use.
|
||||
*/
|
||||
public function setOpenidRealm($realm)
|
||||
{
|
||||
$this->setAuthConfig('openid.realm', $realm);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is provided with the value true, and the authorization request is
|
||||
* granted, the authorization will include any previous authorizations
|
||||
* granted to this user/application combination for other scopes.
|
||||
* @param $include boolean - the URL-space to use.
|
||||
*/
|
||||
public function setIncludeGrantedScopes($include)
|
||||
{
|
||||
$this->setAuthConfig(
|
||||
'include_granted_scopes',
|
||||
$include ? "true" : "false"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the base URL to use for API calls
|
||||
*/
|
||||
public function getBasePath()
|
||||
{
|
||||
return $this->configuration['base_path'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auth configuration for the current auth class.
|
||||
* @param $key - the key to set
|
||||
* @param $value - the parameter value
|
||||
*/
|
||||
private function setAuthConfig($key, $value)
|
||||
{
|
||||
if (!isset($this->configuration['classes'][$this->getAuthClass()])) {
|
||||
$this->configuration['classes'][$this->getAuthClass()] = [];
|
||||
}
|
||||
$this->configuration['classes'][$this->getAuthClass()][$key] = $value;
|
||||
}
|
||||
}
|
20
app/api/Google/Exception.php
Executable file
20
app/api/Google/Exception.php
Executable file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Google_Exception extends Exception
|
||||
{
|
||||
}
|
145
app/api/Google/Http/Batch.php
Executable file
145
app/api/Google/Http/Batch.php
Executable file
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to handle batched requests to the Google API service.
|
||||
*/
|
||||
class Google_Http_Batch
|
||||
{
|
||||
/** @var string Multipart Boundary. */
|
||||
private $boundary;
|
||||
|
||||
/** @var array service requests to be executed. */
|
||||
private $requests = [];
|
||||
|
||||
/** @var Google_Client */
|
||||
private $client;
|
||||
|
||||
private $expected_classes = [];
|
||||
|
||||
private $root_url;
|
||||
|
||||
private $batch_path;
|
||||
|
||||
public function __construct(Google_Client $client, $boundary = false, $rootUrl = '', $batchPath = '')
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->root_url = rtrim($rootUrl ? $rootUrl : $this->client->getBasePath(), '/');
|
||||
$this->batch_path = $batchPath ? $batchPath : 'batch';
|
||||
$this->expected_classes = [];
|
||||
$boundary = (false == $boundary) ? mt_rand() : $boundary;
|
||||
$this->boundary = str_replace('"', '', $boundary);
|
||||
}
|
||||
|
||||
public function add(Google_Http_Request $request, $key = false)
|
||||
{
|
||||
if (false == $key) {
|
||||
$key = mt_rand();
|
||||
}
|
||||
|
||||
$this->requests[$key] = $request;
|
||||
}
|
||||
|
||||
public function execute()
|
||||
{
|
||||
$body = '';
|
||||
|
||||
/** @var Google_Http_Request $req */
|
||||
foreach ($this->requests as $key => $req) {
|
||||
$body .= "--{$this->boundary}\n";
|
||||
$body .= $req->toBatchString($key) . "\n\n";
|
||||
$this->expected_classes["response-" . $key] = $req->getExpectedClass();
|
||||
}
|
||||
|
||||
$body .= "--{$this->boundary}--";
|
||||
|
||||
$url = $this->root_url . '/' . $this->batch_path;
|
||||
$httpRequest = new Google_Http_Request($url, 'POST');
|
||||
$httpRequest->setRequestHeaders(
|
||||
array('Content-Type' => 'multipart/mixed; boundary=' . $this->boundary)
|
||||
);
|
||||
|
||||
$httpRequest->setPostBody($body);
|
||||
$response = $this->client->getIo()->makeRequest($httpRequest);
|
||||
|
||||
return $this->parseResponse($response);
|
||||
}
|
||||
|
||||
public function parseResponse(Google_Http_Request $response)
|
||||
{
|
||||
$contentType = $response->getResponseHeader('content-type');
|
||||
$contentType = explode(';', $contentType);
|
||||
$boundary = false;
|
||||
foreach ($contentType as $part) {
|
||||
$part = (explode('=', $part, 2));
|
||||
if (isset($part[0]) && 'boundary' == trim($part[0])) {
|
||||
$boundary = $part[1];
|
||||
}
|
||||
}
|
||||
|
||||
$body = $response->getResponseBody();
|
||||
if ($body) {
|
||||
$body = str_replace("--$boundary--", "--$boundary", $body);
|
||||
$parts = explode("--$boundary", $body);
|
||||
$responses = [];
|
||||
|
||||
foreach ($parts as $part) {
|
||||
$part = trim($part);
|
||||
if (!empty($part)) {
|
||||
list($metaHeaders, $part) = explode("\r\n\r\n", $part, 2);
|
||||
$metaHeaders = $this->client->getIo()->getHttpResponseHeaders($metaHeaders);
|
||||
|
||||
$status = substr($part, 0, strpos($part, "\n"));
|
||||
$status = explode(" ", $status);
|
||||
$status = $status[1];
|
||||
|
||||
list($partHeaders, $partBody) = $this->client->getIo()->ParseHttpResponse($part, false);
|
||||
$response = new Google_Http_Request("");
|
||||
$response->setResponseHttpCode($status);
|
||||
$response->setResponseHeaders($partHeaders);
|
||||
$response->setResponseBody($partBody);
|
||||
|
||||
// Need content id.
|
||||
$key = $metaHeaders['content-id'];
|
||||
|
||||
if (isset($this->expected_classes[$key]) &&
|
||||
strlen($this->expected_classes[$key]) > 0) {
|
||||
$class = $this->expected_classes[$key];
|
||||
$response->setExpectedClass($class);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Google_Http_REST::decodeHttpResponse($response, $this->client);
|
||||
$responses[$key] = $response;
|
||||
} catch (Google_Service_Exception $e) {
|
||||
// Store the exception as the response, so successful responses
|
||||
// can be processed.
|
||||
$responses[$key] = $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
185
app/api/Google/Http/CacheParser.php
Executable file
185
app/api/Google/Http/CacheParser.php
Executable file
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the caching directives specified in rfc2616. This
|
||||
* implementation is guided by the guidance offered in rfc2616-sec13.
|
||||
*/
|
||||
class Google_Http_CacheParser
|
||||
{
|
||||
public static $CACHEABLE_HTTP_METHODS = array('GET', 'HEAD');
|
||||
public static $CACHEABLE_STATUS_CODES = array('200', '203', '300', '301');
|
||||
|
||||
/**
|
||||
* Check if an HTTP request can be cached by a private local cache.
|
||||
*
|
||||
* @static
|
||||
* @param Google_Http_Request $resp
|
||||
* @return bool True if the request is cacheable.
|
||||
* False if the request is uncacheable.
|
||||
*/
|
||||
public static function isRequestCacheable(Google_Http_Request $resp)
|
||||
{
|
||||
$method = $resp->getRequestMethod();
|
||||
if (! in_array($method, self::$CACHEABLE_HTTP_METHODS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't cache authorized requests/responses.
|
||||
// [rfc2616-14.8] When a shared cache receives a request containing an
|
||||
// Authorization field, it MUST NOT return the corresponding response
|
||||
// as a reply to any other request...
|
||||
if ($resp->getRequestHeader("authorization")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an HTTP response can be cached by a private local cache.
|
||||
*
|
||||
* @static
|
||||
* @param Google_Http_Request $resp
|
||||
* @return bool True if the response is cacheable.
|
||||
* False if the response is un-cacheable.
|
||||
*/
|
||||
public static function isResponseCacheable(Google_Http_Request $resp)
|
||||
{
|
||||
// First, check if the HTTP request was cacheable before inspecting the
|
||||
// HTTP response.
|
||||
if (false == self::isRequestCacheable($resp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$code = $resp->getResponseHttpCode();
|
||||
if (! in_array($code, self::$CACHEABLE_STATUS_CODES)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The resource is uncacheable if the resource is already expired and
|
||||
// the resource doesn't have an ETag for revalidation.
|
||||
$etag = $resp->getResponseHeader("etag");
|
||||
if (self::isExpired($resp) && $etag == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// [rfc2616-14.9.2] If [no-store is] sent in a response, a cache MUST NOT
|
||||
// store any part of either this response or the request that elicited it.
|
||||
$cacheControl = $resp->getParsedCacheControl();
|
||||
if (isset($cacheControl['no-store'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pragma: no-cache is an http request directive, but is occasionally
|
||||
// used as a response header incorrectly.
|
||||
$pragma = $resp->getResponseHeader('pragma');
|
||||
if ($pragma == 'no-cache' || strpos($pragma, 'no-cache') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// [rfc2616-14.44] Vary: * is extremely difficult to cache. "It implies that
|
||||
// a cache cannot determine from the request headers of a subsequent request
|
||||
// whether this response is the appropriate representation."
|
||||
// Given this, we deem responses with the Vary header as uncacheable.
|
||||
$vary = $resp->getResponseHeader('vary');
|
||||
if ($vary) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @param Google_Http_Request $resp
|
||||
* @return bool True if the HTTP response is considered to be expired.
|
||||
* False if it is considered to be fresh.
|
||||
*/
|
||||
public static function isExpired(Google_Http_Request $resp)
|
||||
{
|
||||
// HTTP/1.1 clients and caches MUST treat other invalid date formats,
|
||||
// especially including the value “0”, as in the past.
|
||||
$parsedExpires = false;
|
||||
$responseHeaders = $resp->getResponseHeaders();
|
||||
|
||||
if (isset($responseHeaders['expires'])) {
|
||||
$rawExpires = $responseHeaders['expires'];
|
||||
// Check for a malformed expires header first.
|
||||
if (empty($rawExpires) || (is_numeric($rawExpires) && $rawExpires <= 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// See if we can parse the expires header.
|
||||
$parsedExpires = strtotime($rawExpires);
|
||||
if (false == $parsedExpires || $parsedExpires <= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the freshness of an http response.
|
||||
$freshnessLifetime = false;
|
||||
$cacheControl = $resp->getParsedCacheControl();
|
||||
if (isset($cacheControl['max-age'])) {
|
||||
$freshnessLifetime = $cacheControl['max-age'];
|
||||
}
|
||||
|
||||
$rawDate = $resp->getResponseHeader('date');
|
||||
$parsedDate = strtotime($rawDate);
|
||||
|
||||
if (empty($rawDate) || false == $parsedDate) {
|
||||
// We can't default this to now, as that means future cache reads
|
||||
// will always pass with the logic below, so we will require a
|
||||
// date be injected if not supplied.
|
||||
throw new Google_Exception("All cacheable requests must have creation dates.");
|
||||
}
|
||||
|
||||
if (false == $freshnessLifetime && isset($responseHeaders['expires'])) {
|
||||
$freshnessLifetime = $parsedExpires - $parsedDate;
|
||||
}
|
||||
|
||||
if (false == $freshnessLifetime) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate the age of an http response.
|
||||
$age = max(0, time() - $parsedDate);
|
||||
if (isset($responseHeaders['age'])) {
|
||||
$age = max($age, strtotime($responseHeaders['age']));
|
||||
}
|
||||
|
||||
return $freshnessLifetime <= $age;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a cache entry should be revalidated with by the origin.
|
||||
*
|
||||
* @param Google_Http_Request $response
|
||||
* @return bool True if the entry is expired, else return false.
|
||||
*/
|
||||
public static function mustRevalidate(Google_Http_Request $response)
|
||||
{
|
||||
// [13.3] When a cache has a stale entry that it would like to use as a
|
||||
// response to a client's request, it first has to check with the origin
|
||||
// server to see if its cached entry is still usable.
|
||||
return self::isExpired($response);
|
||||
}
|
||||
}
|
341
app/api/Google/Http/MediaFileUpload.php
Executable file
341
app/api/Google/Http/MediaFileUpload.php
Executable file
|
@ -0,0 +1,341 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage large file uploads, which may be media but can be any type
|
||||
* of sizable data.
|
||||
*/
|
||||
class Google_Http_MediaFileUpload
|
||||
{
|
||||
const UPLOAD_MEDIA_TYPE = 'media';
|
||||
const UPLOAD_MULTIPART_TYPE = 'multipart';
|
||||
const UPLOAD_RESUMABLE_TYPE = 'resumable';
|
||||
|
||||
/** @var string $mimeType */
|
||||
private $mimeType;
|
||||
|
||||
/** @var string $data */
|
||||
private $data;
|
||||
|
||||
/** @var bool $resumable */
|
||||
private $resumable;
|
||||
|
||||
/** @var int $chunkSize */
|
||||
private $chunkSize;
|
||||
|
||||
/** @var int $size */
|
||||
private $size;
|
||||
|
||||
/** @var string $resumeUri */
|
||||
private $resumeUri;
|
||||
|
||||
/** @var int $progress */
|
||||
private $progress;
|
||||
|
||||
/** @var Google_Client */
|
||||
private $client;
|
||||
|
||||
/** @var Google_Http_Request */
|
||||
private $request;
|
||||
|
||||
/** @var string */
|
||||
private $boundary;
|
||||
|
||||
/**
|
||||
* Result code from last HTTP call
|
||||
* @var int
|
||||
*/
|
||||
private $httpResultCode;
|
||||
|
||||
/**
|
||||
* @param $mimeType string
|
||||
* @param $data string The bytes you want to upload.
|
||||
* @param $resumable bool
|
||||
* @param bool $chunkSize File will be uploaded in chunks of this many bytes.
|
||||
* only used if resumable=True
|
||||
*/
|
||||
public function __construct(
|
||||
Google_Client $client,
|
||||
Google_Http_Request $request,
|
||||
$mimeType,
|
||||
$data,
|
||||
$resumable = false,
|
||||
$chunkSize = false,
|
||||
$boundary = false
|
||||
) {
|
||||
$this->client = $client;
|
||||
$this->request = $request;
|
||||
$this->mimeType = $mimeType;
|
||||
$this->data = $data;
|
||||
$this->size = strlen($this->data);
|
||||
$this->resumable = $resumable;
|
||||
if (!$chunkSize) {
|
||||
$chunkSize = 256 * 1024;
|
||||
}
|
||||
$this->chunkSize = $chunkSize;
|
||||
$this->progress = 0;
|
||||
$this->boundary = $boundary;
|
||||
|
||||
// Process Media Request
|
||||
$this->process();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the size of the file that is being uploaded.
|
||||
* @param $size - int file size in bytes
|
||||
*/
|
||||
public function setFileSize($size)
|
||||
{
|
||||
$this->size = $size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the progress on the upload
|
||||
* @return int progress in bytes uploaded.
|
||||
*/
|
||||
public function getProgress()
|
||||
{
|
||||
return $this->progress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HTTP result code from the last call made.
|
||||
* @return int code
|
||||
*/
|
||||
public function getHttpResultCode()
|
||||
{
|
||||
return $this->httpResultCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a PUT-Request to google drive and parses the response,
|
||||
* setting the appropiate variables from the response()
|
||||
*
|
||||
* @param Google_Http_Request $httpRequest the Reuqest which will be send
|
||||
*
|
||||
* @return false|mixed false when the upload is unfinished or the decoded http response
|
||||
*
|
||||
*/
|
||||
private function makePutRequest(Google_Http_Request $httpRequest)
|
||||
{
|
||||
if ($this->client->getClassConfig("Google_Http_Request", "enable_gzip_for_uploads")) {
|
||||
$httpRequest->enableGzip();
|
||||
} else {
|
||||
$httpRequest->disableGzip();
|
||||
}
|
||||
|
||||
$response = $this->client->getIo()->makeRequest($httpRequest);
|
||||
$response->setExpectedClass($this->request->getExpectedClass());
|
||||
$code = $response->getResponseHttpCode();
|
||||
$this->httpResultCode = $code;
|
||||
|
||||
if (308 == $code) {
|
||||
// Track the amount uploaded.
|
||||
$range = explode('-', $response->getResponseHeader('range'));
|
||||
$this->progress = $range[1] + 1;
|
||||
|
||||
// Allow for changing upload URLs.
|
||||
$location = $response->getResponseHeader('location');
|
||||
if ($location) {
|
||||
$this->resumeUri = $location;
|
||||
}
|
||||
|
||||
// No problems, but upload not complete.
|
||||
return false;
|
||||
} else {
|
||||
return Google_Http_REST::decodeHttpResponse($response, $this->client);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the next part of the file to upload.
|
||||
* @param [$chunk] the next set of bytes to send. If false will used $data passed
|
||||
* at construct time.
|
||||
*/
|
||||
public function nextChunk($chunk = false)
|
||||
{
|
||||
if (false == $this->resumeUri) {
|
||||
$this->resumeUri = $this->fetchResumeUri();
|
||||
}
|
||||
|
||||
if (false == $chunk) {
|
||||
$chunk = substr($this->data, $this->progress, $this->chunkSize);
|
||||
}
|
||||
$lastBytePos = $this->progress + strlen($chunk) - 1;
|
||||
$headers = array(
|
||||
'content-range' => "bytes $this->progress-$lastBytePos/$this->size",
|
||||
'content-type' => $this->request->getRequestHeader('content-type'),
|
||||
'content-length' => $this->chunkSize,
|
||||
'expect' => '',
|
||||
);
|
||||
|
||||
$httpRequest = new Google_Http_Request(
|
||||
$this->resumeUri,
|
||||
'PUT',
|
||||
$headers,
|
||||
$chunk
|
||||
);
|
||||
return $this->makePutRequest($httpRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume a previously unfinished upload
|
||||
* @param $resumeUri the resume-URI of the unfinished, resumable upload.
|
||||
*/
|
||||
public function resume($resumeUri)
|
||||
{
|
||||
$this->resumeUri = $resumeUri;
|
||||
$headers = array(
|
||||
'content-range' => "bytes */$this->size",
|
||||
'content-length' => 0,
|
||||
);
|
||||
$httpRequest = new Google_Http_Request(
|
||||
$this->resumeUri,
|
||||
'PUT',
|
||||
$headers
|
||||
);
|
||||
return $this->makePutRequest($httpRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|bool
|
||||
* @visible for testing
|
||||
*/
|
||||
private function process()
|
||||
{
|
||||
$postBody = false;
|
||||
$contentType = false;
|
||||
|
||||
$meta = $this->request->getPostBody();
|
||||
$meta = is_string($meta) ? json_decode($meta, true) : $meta;
|
||||
|
||||
$uploadType = $this->getUploadType($meta);
|
||||
$this->request->setQueryParam('uploadType', $uploadType);
|
||||
$this->transformToUploadUrl();
|
||||
$mimeType = $this->mimeType ?
|
||||
$this->mimeType :
|
||||
$this->request->getRequestHeader('content-type');
|
||||
|
||||
if (self::UPLOAD_RESUMABLE_TYPE == $uploadType) {
|
||||
$contentType = $mimeType;
|
||||
$postBody = is_string($meta) ? $meta : json_encode($meta);
|
||||
} else if (self::UPLOAD_MEDIA_TYPE == $uploadType) {
|
||||
$contentType = $mimeType;
|
||||
$postBody = $this->data;
|
||||
} else if (self::UPLOAD_MULTIPART_TYPE == $uploadType) {
|
||||
// This is a multipart/related upload.
|
||||
$boundary = $this->boundary ? $this->boundary : mt_rand();
|
||||
$boundary = str_replace('"', '', $boundary);
|
||||
$contentType = 'multipart/related; boundary=' . $boundary;
|
||||
$related = "--$boundary\r\n";
|
||||
$related .= "Content-Type: application/json; charset=UTF-8\r\n";
|
||||
$related .= "\r\n" . json_encode($meta) . "\r\n";
|
||||
$related .= "--$boundary\r\n";
|
||||
$related .= "Content-Type: $mimeType\r\n";
|
||||
$related .= "Content-Transfer-Encoding: base64\r\n";
|
||||
$related .= "\r\n" . base64_encode($this->data) . "\r\n";
|
||||
$related .= "--$boundary--";
|
||||
$postBody = $related;
|
||||
}
|
||||
|
||||
$this->request->setPostBody($postBody);
|
||||
|
||||
if (isset($contentType) && $contentType) {
|
||||
$contentTypeHeader['content-type'] = $contentType;
|
||||
$this->request->setRequestHeaders($contentTypeHeader);
|
||||
}
|
||||
}
|
||||
|
||||
private function transformToUploadUrl()
|
||||
{
|
||||
$base = $this->request->getBaseComponent();
|
||||
$this->request->setBaseComponent($base . '/upload');
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid upload types:
|
||||
* - resumable (UPLOAD_RESUMABLE_TYPE)
|
||||
* - media (UPLOAD_MEDIA_TYPE)
|
||||
* - multipart (UPLOAD_MULTIPART_TYPE)
|
||||
* @param $meta
|
||||
* @return string
|
||||
* @visible for testing
|
||||
*/
|
||||
public function getUploadType($meta)
|
||||
{
|
||||
if ($this->resumable) {
|
||||
return self::UPLOAD_RESUMABLE_TYPE;
|
||||
}
|
||||
|
||||
if (false == $meta && $this->data) {
|
||||
return self::UPLOAD_MEDIA_TYPE;
|
||||
}
|
||||
|
||||
return self::UPLOAD_MULTIPART_TYPE;
|
||||
}
|
||||
|
||||
public function getResumeUri()
|
||||
{
|
||||
return ( $this->resumeUri !== null ? $this->resumeUri : $this->fetchResumeUri() );
|
||||
}
|
||||
|
||||
private function fetchResumeUri()
|
||||
{
|
||||
$result = null;
|
||||
$body = $this->request->getPostBody();
|
||||
if ($body) {
|
||||
$headers = array(
|
||||
'content-type' => 'application/json; charset=UTF-8',
|
||||
'content-length' => Google_Utils::getStrLen($body),
|
||||
'x-upload-content-type' => $this->mimeType,
|
||||
'x-upload-content-length' => $this->size,
|
||||
'expect' => '',
|
||||
);
|
||||
$this->request->setRequestHeaders($headers);
|
||||
}
|
||||
|
||||
$response = $this->client->getIo()->makeRequest($this->request);
|
||||
$location = $response->getResponseHeader('location');
|
||||
$code = $response->getResponseHttpCode();
|
||||
|
||||
if (200 == $code && true == $location) {
|
||||
return $location;
|
||||
}
|
||||
$message = $code;
|
||||
$body = @json_decode($response->getResponseBody());
|
||||
if (!empty($body->error->errors) ) {
|
||||
$message .= ': ';
|
||||
foreach ($body->error->errors as $error) {
|
||||
$message .= "{$error->domain}, {$error->message};";
|
||||
}
|
||||
$message = rtrim($message, ';');
|
||||
}
|
||||
|
||||
$error = "Failed to start the resumable upload (HTTP {$message})";
|
||||
$this->client->getLogger()->error($error);
|
||||
throw new Google_Exception($error);
|
||||
}
|
||||
|
||||
public function setChunkSize($chunkSize)
|
||||
{
|
||||
$this->chunkSize = $chunkSize;
|
||||
}
|
||||
}
|
178
app/api/Google/Http/REST.php
Executable file
178
app/api/Google/Http/REST.php
Executable file
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements the RESTful transport of apiServiceRequest()'s
|
||||
*/
|
||||
class Google_Http_REST
|
||||
{
|
||||
/**
|
||||
* Executes a Google_Http_Request and (if applicable) automatically retries
|
||||
* when errors occur.
|
||||
*
|
||||
* @param Google_Client $client
|
||||
* @param Google_Http_Request $req
|
||||
* @return array decoded result
|
||||
* @throws Google_Service_Exception on server side error (ie: not authenticated,
|
||||
* invalid or malformed post body, invalid url)
|
||||
*/
|
||||
public static function execute(Google_Client $client, Google_Http_Request $req)
|
||||
{
|
||||
$runner = new Google_Task_Runner(
|
||||
$client,
|
||||
sprintf('%s %s', $req->getRequestMethod(), $req->getUrl()),
|
||||
array(get_class(), 'doExecute'),
|
||||
array($client, $req)
|
||||
);
|
||||
|
||||
return $runner->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Google_Http_Request
|
||||
*
|
||||
* @param Google_Client $client
|
||||
* @param Google_Http_Request $req
|
||||
* @return array decoded result
|
||||
* @throws Google_Service_Exception on server side error (ie: not authenticated,
|
||||
* invalid or malformed post body, invalid url)
|
||||
*/
|
||||
public static function doExecute(Google_Client $client, Google_Http_Request $req)
|
||||
{
|
||||
$httpRequest = $client->getIo()->makeRequest($req);
|
||||
$httpRequest->setExpectedClass($req->getExpectedClass());
|
||||
return self::decodeHttpResponse($httpRequest, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an HTTP Response.
|
||||
* @static
|
||||
* @throws Google_Service_Exception
|
||||
* @param Google_Http_Request $response The http response to be decoded.
|
||||
* @param Google_Client $client
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function decodeHttpResponse($response, Google_Client $client = null)
|
||||
{
|
||||
$code = $response->getResponseHttpCode();
|
||||
$body = $response->getResponseBody();
|
||||
$decoded = null;
|
||||
|
||||
if ((intVal($code)) >= 300) {
|
||||
$decoded = json_decode($body, true);
|
||||
$err = 'Error calling ' . $response->getRequestMethod() . ' ' . $response->getUrl();
|
||||
if (isset($decoded['error']) &&
|
||||
isset($decoded['error']['message']) &&
|
||||
isset($decoded['error']['code'])) {
|
||||
// if we're getting a json encoded error definition, use that instead of the raw response
|
||||
// body for improved readability
|
||||
$err .= ": ({$decoded['error']['code']}) {$decoded['error']['message']}";
|
||||
} else {
|
||||
$err .= ": ($code) $body";
|
||||
}
|
||||
|
||||
$errors = null;
|
||||
// Specific check for APIs which don't return error details, such as Blogger.
|
||||
if (isset($decoded['error']) && isset($decoded['error']['errors'])) {
|
||||
$errors = $decoded['error']['errors'];
|
||||
}
|
||||
|
||||
$map = null;
|
||||
if ($client) {
|
||||
$client->getLogger()->error(
|
||||
$err,
|
||||
array('code' => $code, 'errors' => $errors)
|
||||
);
|
||||
|
||||
$map = $client->getClassConfig(
|
||||
'Google_Service_Exception',
|
||||
'retry_map'
|
||||
);
|
||||
}
|
||||
throw new Google_Service_Exception($err, $code, null, $errors, $map);
|
||||
}
|
||||
|
||||
// Only attempt to decode the response, if the response code wasn't (204) 'no content'
|
||||
if ($code != '204') {
|
||||
if ($response->getExpectedRaw()) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
$decoded = json_decode($body, true);
|
||||
if ($decoded === null || $decoded === "") {
|
||||
$error = "Invalid json in service response: $body";
|
||||
if ($client) {
|
||||
$client->getLogger()->error($error);
|
||||
}
|
||||
throw new Google_Service_Exception($error);
|
||||
}
|
||||
|
||||
if ($response->getExpectedClass()) {
|
||||
$class = $response->getExpectedClass();
|
||||
$decoded = new $class($decoded);
|
||||
}
|
||||
}
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse/expand request parameters and create a fully qualified
|
||||
* request uri.
|
||||
* @static
|
||||
* @param string $servicePath
|
||||
* @param string $restPath
|
||||
* @param array $params
|
||||
* @return string $requestUrl
|
||||
*/
|
||||
public static function createRequestUri($servicePath, $restPath, $params)
|
||||
{
|
||||
$requestUrl = $servicePath . $restPath;
|
||||
$uriTemplateVars = [];
|
||||
$queryVars = [];
|
||||
foreach ($params as $paramName => $paramSpec) {
|
||||
if ($paramSpec['type'] == 'boolean') {
|
||||
$paramSpec['value'] = ($paramSpec['value']) ? 'true' : 'false';
|
||||
}
|
||||
if ($paramSpec['location'] == 'path') {
|
||||
$uriTemplateVars[$paramName] = $paramSpec['value'];
|
||||
} else if ($paramSpec['location'] == 'query') {
|
||||
if (isset($paramSpec['repeated']) && is_array($paramSpec['value'])) {
|
||||
foreach ($paramSpec['value'] as $value) {
|
||||
$queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($value));
|
||||
}
|
||||
} else {
|
||||
$queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($paramSpec['value']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($uriTemplateVars)) {
|
||||
$uriTemplateParser = new Google_Utils_URITemplate();
|
||||
$requestUrl = $uriTemplateParser->parse($requestUrl, $uriTemplateVars);
|
||||
}
|
||||
|
||||
if (count($queryVars)) {
|
||||
$requestUrl .= '?' . implode('&', $queryVars);
|
||||
}
|
||||
|
||||
return $requestUrl;
|
||||
}
|
||||
}
|
504
app/api/Google/Http/Request.php
Executable file
504
app/api/Google/Http/Request.php
Executable file
|
@ -0,0 +1,504 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Request to be executed by IO classes. Upon execution, the
|
||||
* responseHttpCode, responseHeaders and responseBody will be filled in.
|
||||
*
|
||||
* @author Chris Chabot <chabotc@google.com>
|
||||
* @author Chirag Shah <chirags@google.com>
|
||||
*
|
||||
*/
|
||||
class Google_Http_Request
|
||||
{
|
||||
const GZIP_UA = " (gzip)";
|
||||
|
||||
private $batchHeaders = array(
|
||||
'Content-Type' => 'application/http',
|
||||
'Content-Transfer-Encoding' => 'binary',
|
||||
'MIME-Version' => '1.0',
|
||||
);
|
||||
|
||||
protected $queryParams;
|
||||
protected $requestMethod;
|
||||
protected $requestHeaders;
|
||||
protected $baseComponent = null;
|
||||
protected $path;
|
||||
protected $postBody;
|
||||
protected $userAgent = '';
|
||||
protected $canGzip = null;
|
||||
|
||||
protected $responseHttpCode;
|
||||
protected $responseHeaders;
|
||||
protected $responseBody;
|
||||
|
||||
protected $expectedClass;
|
||||
protected $expectedRaw = false;
|
||||
|
||||
public $accessKey;
|
||||
|
||||
public function __construct(
|
||||
$url,
|
||||
$method = 'GET',
|
||||
$headers = array(),
|
||||
$postBody = null
|
||||
) {
|
||||
$this->setUrl($url);
|
||||
$this->setRequestMethod($method);
|
||||
$this->setRequestHeaders($headers);
|
||||
$this->setPostBody($postBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Misc function that returns the base url component of the $url
|
||||
* used by the OAuth signing class to calculate the base string
|
||||
* @return string The base url component of the $url.
|
||||
*/
|
||||
public function getBaseComponent()
|
||||
{
|
||||
return $this->baseComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base URL that path and query parameters will be added to.
|
||||
* @param $baseComponent string
|
||||
*/
|
||||
public function setBaseComponent($baseComponent)
|
||||
{
|
||||
$this->baseComponent = rtrim($baseComponent, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable support for gzipped responses with this request.
|
||||
*/
|
||||
public function enableGzip()
|
||||
{
|
||||
$this->setRequestHeaders(array("Accept-Encoding" => "gzip"));
|
||||
$this->canGzip = true;
|
||||
$this->setUserAgent($this->userAgent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable support for gzip responses with this request.
|
||||
*/
|
||||
public function disableGzip()
|
||||
{
|
||||
if (
|
||||
isset($this->requestHeaders['accept-encoding']) &&
|
||||
$this->requestHeaders['accept-encoding'] == "gzip"
|
||||
) {
|
||||
unset($this->requestHeaders['accept-encoding']);
|
||||
}
|
||||
$this->canGzip = false;
|
||||
$this->userAgent = str_replace(self::GZIP_UA, "", $this->userAgent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can this request accept a gzip response?
|
||||
* @return bool
|
||||
*/
|
||||
public function canGzip()
|
||||
{
|
||||
return $this->canGzip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Misc function that returns an array of the query parameters of the current
|
||||
* url used by the OAuth signing class to calculate the signature
|
||||
* @return array Query parameters in the query string.
|
||||
*/
|
||||
public function getQueryParams()
|
||||
{
|
||||
return $this->queryParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new query parameter.
|
||||
* @param $key - string to set, does not need to be URL encoded
|
||||
* @param $value - string to set, does not need to be URL encoded
|
||||
*/
|
||||
public function setQueryParam($key, $value)
|
||||
{
|
||||
$this->queryParams[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string HTTP Response Code.
|
||||
*/
|
||||
public function getResponseHttpCode()
|
||||
{
|
||||
return (int) $this->responseHttpCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $responseHttpCode HTTP Response Code.
|
||||
*/
|
||||
public function setResponseHttpCode($responseHttpCode)
|
||||
{
|
||||
$this->responseHttpCode = $responseHttpCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $responseHeaders (array) HTTP Response Headers.
|
||||
*/
|
||||
public function getResponseHeaders()
|
||||
{
|
||||
return $this->responseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string HTTP Response Body
|
||||
*/
|
||||
public function getResponseBody()
|
||||
{
|
||||
return $this->responseBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class the response to this request should expect.
|
||||
*
|
||||
* @param $class string the class name
|
||||
*/
|
||||
public function setExpectedClass($class)
|
||||
{
|
||||
$this->expectedClass = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the expected class the response should expect.
|
||||
* @return string class name
|
||||
*/
|
||||
public function getExpectedClass()
|
||||
{
|
||||
return $this->expectedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable expected raw response
|
||||
*/
|
||||
public function enableExpectedRaw()
|
||||
{
|
||||
$this->expectedRaw = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable expected raw response
|
||||
*/
|
||||
public function disableExpectedRaw()
|
||||
{
|
||||
$this->expectedRaw = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expected raw response or not.
|
||||
* @return boolean expected raw response
|
||||
*/
|
||||
public function getExpectedRaw()
|
||||
{
|
||||
return $this->expectedRaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $headers The HTTP response headers
|
||||
* to be normalized.
|
||||
*/
|
||||
public function setResponseHeaders($headers)
|
||||
{
|
||||
$headers = Google_Utils::normalize($headers);
|
||||
if ($this->responseHeaders) {
|
||||
$headers = array_merge($this->responseHeaders, $headers);
|
||||
}
|
||||
|
||||
$this->responseHeaders = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return array|boolean Returns the requested HTTP header or
|
||||
* false if unavailable.
|
||||
*/
|
||||
public function getResponseHeader($key)
|
||||
{
|
||||
return isset($this->responseHeaders[$key])
|
||||
? $this->responseHeaders[$key]
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $responseBody The HTTP response body.
|
||||
*/
|
||||
public function setResponseBody($responseBody)
|
||||
{
|
||||
$this->responseBody = $responseBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string $url The request URL.
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->baseComponent . $this->path .
|
||||
(count($this->queryParams) ?
|
||||
"?" . $this->buildQuery($this->queryParams) :
|
||||
'');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string $method HTTP Request Method.
|
||||
*/
|
||||
public function getRequestMethod()
|
||||
{
|
||||
return $this->requestMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array $headers HTTP Request Headers.
|
||||
*/
|
||||
public function getRequestHeaders()
|
||||
{
|
||||
return $this->requestHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return array|boolean Returns the requested HTTP header or
|
||||
* false if unavailable.
|
||||
*/
|
||||
public function getRequestHeader($key)
|
||||
{
|
||||
return isset($this->requestHeaders[$key])
|
||||
? $this->requestHeaders[$key]
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string $postBody HTTP Request Body.
|
||||
*/
|
||||
public function getPostBody()
|
||||
{
|
||||
return $this->postBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url the url to set
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
if (substr($url, 0, 4) != 'http') {
|
||||
// Force the path become relative.
|
||||
if (substr($url, 0, 1) !== '/') {
|
||||
$url = '/' . $url;
|
||||
}
|
||||
}
|
||||
$parts = parse_url($url);
|
||||
if (isset($parts['host'])) {
|
||||
$this->baseComponent = sprintf(
|
||||
"%s%s%s",
|
||||
isset($parts['scheme']) ? $parts['scheme'] . "://" : '',
|
||||
isset($parts['host']) ? $parts['host'] : '',
|
||||
isset($parts['port']) ? ":" . $parts['port'] : ''
|
||||
);
|
||||
}
|
||||
$this->path = isset($parts['path']) ? $parts['path'] : '';
|
||||
$this->queryParams = [];
|
||||
if (isset($parts['query'])) {
|
||||
$this->queryParams = $this->parseQuery($parts['query']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method Set he HTTP Method and normalize
|
||||
* it to upper-case, as required by HTTP.
|
||||
*
|
||||
*/
|
||||
public function setRequestMethod($method)
|
||||
{
|
||||
$this->requestMethod = strtoupper($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $headers The HTTP request headers
|
||||
* to be set and normalized.
|
||||
*/
|
||||
public function setRequestHeaders($headers)
|
||||
{
|
||||
$headers = Google_Utils::normalize($headers);
|
||||
if ($this->requestHeaders) {
|
||||
$headers = array_merge($this->requestHeaders, $headers);
|
||||
}
|
||||
$this->requestHeaders = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $postBody the postBody to set
|
||||
*/
|
||||
public function setPostBody($postBody)
|
||||
{
|
||||
$this->postBody = $postBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the User-Agent Header.
|
||||
* @param string $userAgent The User-Agent.
|
||||
*/
|
||||
public function setUserAgent($userAgent)
|
||||
{
|
||||
$this->userAgent = $userAgent;
|
||||
if ($this->canGzip) {
|
||||
$this->userAgent = $userAgent . self::GZIP_UA;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The User-Agent.
|
||||
*/
|
||||
public function getUserAgent()
|
||||
{
|
||||
return $this->userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cache key depending on if this was an OAuth signed request
|
||||
* in which case it will use the non-signed url and access key to make this
|
||||
* cache key unique per authenticated user, else use the plain request url
|
||||
* @return string The md5 hash of the request cache key.
|
||||
*/
|
||||
public function getCacheKey()
|
||||
{
|
||||
$key = $this->getUrl();
|
||||
|
||||
if (isset($this->accessKey)) {
|
||||
$key .= $this->accessKey;
|
||||
}
|
||||
|
||||
if (isset($this->requestHeaders['authorization'])) {
|
||||
$key .= $this->requestHeaders['authorization'];
|
||||
}
|
||||
|
||||
return md5($key);
|
||||
}
|
||||
|
||||
public function getParsedCacheControl()
|
||||
{
|
||||
$parsed = [];
|
||||
$rawCacheControl = $this->getResponseHeader('cache-control');
|
||||
if ($rawCacheControl) {
|
||||
$rawCacheControl = str_replace(', ', '&', $rawCacheControl);
|
||||
parse_str($rawCacheControl, $parsed);
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return string A string representation of the HTTP Request.
|
||||
*/
|
||||
public function toBatchString($id)
|
||||
{
|
||||
$str = '';
|
||||
$path = parse_url($this->getUrl(), PHP_URL_PATH) . "?" .
|
||||
http_build_query($this->queryParams);
|
||||
$str .= $this->getRequestMethod() . ' ' . $path . " HTTP/1.1\n";
|
||||
|
||||
foreach ($this->getRequestHeaders() as $key => $val) {
|
||||
$str .= $key . ': ' . $val . "\n";
|
||||
}
|
||||
|
||||
if ($this->getPostBody()) {
|
||||
$str .= "\n";
|
||||
$str .= $this->getPostBody();
|
||||
}
|
||||
|
||||
$headers = '';
|
||||
foreach ($this->batchHeaders as $key => $val) {
|
||||
$headers .= $key . ': ' . $val . "\n";
|
||||
}
|
||||
|
||||
$headers .= "Content-ID: $id\n";
|
||||
$str = $headers . "\n" . $str;
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our own version of parse_str that allows for multiple variables
|
||||
* with the same name.
|
||||
* @param $string - the query string to parse
|
||||
*/
|
||||
private function parseQuery($string)
|
||||
{
|
||||
$return = [];
|
||||
$parts = explode("&", $string);
|
||||
foreach ($parts as $part) {
|
||||
list($key, $value) = explode('=', $part, 2);
|
||||
$value = urldecode($value);
|
||||
if (isset($return[$key])) {
|
||||
if (!is_array($return[$key])) {
|
||||
$return[$key] = array($return[$key]);
|
||||
}
|
||||
$return[$key][] = $value;
|
||||
} else {
|
||||
$return[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of build query that allows for multiple
|
||||
* duplicate keys.
|
||||
* @param $parts array of key value pairs
|
||||
*/
|
||||
private function buildQuery($parts)
|
||||
{
|
||||
$return = [];
|
||||
foreach ($parts as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $v) {
|
||||
$return[] = urlencode($key) . "=" . urlencode($v);
|
||||
}
|
||||
} else {
|
||||
$return[] = urlencode($key) . "=" . urlencode($value);
|
||||
}
|
||||
}
|
||||
return implode('&', $return);
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're POSTing and have no body to send, we can send the query
|
||||
* parameters in there, which avoids length issues with longer query
|
||||
* params.
|
||||
*/
|
||||
public function maybeMoveParametersToBody()
|
||||
{
|
||||
if ($this->getRequestMethod() == "POST" && empty($this->postBody)) {
|
||||
$this->setRequestHeaders(
|
||||
array(
|
||||
"content-type" =>
|
||||
"application/x-www-form-urlencoded; charset=UTF-8"
|
||||
)
|
||||
);
|
||||
$this->setPostBody($this->buildQuery($this->queryParams));
|
||||
$this->queryParams = [];
|
||||
}
|
||||
}
|
||||
}
|
339
app/api/Google/IO/Abstract.php
Executable file
339
app/api/Google/IO/Abstract.php
Executable file
|
@ -0,0 +1,339 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract IO base class
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
abstract class Google_IO_Abstract
|
||||
{
|
||||
const UNKNOWN_CODE = 0;
|
||||
const FORM_URLENCODED = 'application/x-www-form-urlencoded';
|
||||
private static $CONNECTION_ESTABLISHED_HEADERS = array(
|
||||
"HTTP/1.0 200 Connection established\r\n\r\n",
|
||||
"HTTP/1.1 200 Connection established\r\n\r\n",
|
||||
);
|
||||
private static $ENTITY_HTTP_METHODS = array("POST" => null, "PUT" => null);
|
||||
private static $HOP_BY_HOP = array(
|
||||
'connection' => true,
|
||||
'keep-alive' => true,
|
||||
'proxy-authenticate' => true,
|
||||
'proxy-authorization' => true,
|
||||
'te' => true,
|
||||
'trailers' => true,
|
||||
'transfer-encoding' => true,
|
||||
'upgrade' => true
|
||||
);
|
||||
|
||||
|
||||
/** @var Google_Client */
|
||||
protected $client;
|
||||
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
$timeout = $client->getClassConfig('Google_IO_Abstract', 'request_timeout_seconds');
|
||||
if ($timeout > 0) {
|
||||
$this->setTimeout($timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Google_Http_Request
|
||||
* @param Google_Http_Request $request the http request to be executed
|
||||
* @return array containing response headers, body, and http code
|
||||
* @throws Google_IO_Exception on curl or IO error
|
||||
*/
|
||||
abstract public function executeRequest(Google_Http_Request $request);
|
||||
|
||||
/**
|
||||
* Set options that update the transport implementation's behavior.
|
||||
* @param $options
|
||||
*/
|
||||
abstract public function setOptions($options);
|
||||
|
||||
/**
|
||||
* Set the maximum request time in seconds.
|
||||
* @param $timeout in seconds
|
||||
*/
|
||||
abstract public function setTimeout($timeout);
|
||||
|
||||
/**
|
||||
* Get the maximum request time in seconds.
|
||||
* @return timeout in seconds
|
||||
*/
|
||||
abstract public function getTimeout();
|
||||
|
||||
/**
|
||||
* Test for the presence of a cURL header processing bug
|
||||
*
|
||||
* The cURL bug was present in versions prior to 7.30.0 and caused the header
|
||||
* length to be miscalculated when a "Connection established" header added by
|
||||
* some proxies was present.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract protected function needsQuirk();
|
||||
|
||||
/**
|
||||
* @visible for testing.
|
||||
* Cache the response to an HTTP request if it is cacheable.
|
||||
* @param Google_Http_Request $request
|
||||
* @return bool Returns true if the insertion was successful.
|
||||
* Otherwise, return false.
|
||||
*/
|
||||
public function setCachedRequest(Google_Http_Request $request)
|
||||
{
|
||||
// Determine if the request is cacheable.
|
||||
if (Google_Http_CacheParser::isResponseCacheable($request)) {
|
||||
$this->client->getCache()->set($request->getCacheKey(), $request);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an HTTP Request
|
||||
*
|
||||
* @param Google_Http_Request $request the http request to be executed
|
||||
* @return Google_Http_Request http request with the response http code,
|
||||
* response headers and response body filled in
|
||||
* @throws Google_IO_Exception on curl or IO error
|
||||
*/
|
||||
public function makeRequest(Google_Http_Request $request)
|
||||
{
|
||||
// First, check to see if we have a valid cached version.
|
||||
$cached = $this->getCachedRequest($request);
|
||||
if ($cached !== false && $cached instanceof Google_Http_Request) {
|
||||
if (!$this->checkMustRevalidateCachedRequest($cached, $request)) {
|
||||
return $cached;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($request->getRequestMethod(), self::$ENTITY_HTTP_METHODS)) {
|
||||
$request = $this->processEntityRequest($request);
|
||||
}
|
||||
|
||||
list($responseData, $responseHeaders, $respHttpCode) = $this->executeRequest($request);
|
||||
|
||||
if ($respHttpCode == 304 && $cached) {
|
||||
// If the server responded NOT_MODIFIED, return the cached request.
|
||||
$this->updateCachedRequest($cached, $responseHeaders);
|
||||
return $cached;
|
||||
}
|
||||
|
||||
if (!isset($responseHeaders['Date']) && !isset($responseHeaders['date'])) {
|
||||
$responseHeaders['date'] = date("r");
|
||||
}
|
||||
|
||||
$request->setResponseHttpCode($respHttpCode);
|
||||
$request->setResponseHeaders($responseHeaders);
|
||||
$request->setResponseBody($responseData);
|
||||
// Store the request in cache (the function checks to see if the request
|
||||
// can actually be cached)
|
||||
$this->setCachedRequest($request);
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @visible for testing.
|
||||
* @param Google_Http_Request $request
|
||||
* @return Google_Http_Request|bool Returns the cached object or
|
||||
* false if the operation was unsuccessful.
|
||||
*/
|
||||
public function getCachedRequest(Google_Http_Request $request)
|
||||
{
|
||||
if (false === Google_Http_CacheParser::isRequestCacheable($request)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->client->getCache()->get($request->getCacheKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* @visible for testing
|
||||
* Process an http request that contains an enclosed entity.
|
||||
* @param Google_Http_Request $request
|
||||
* @return Google_Http_Request Processed request with the enclosed entity.
|
||||
*/
|
||||
public function processEntityRequest(Google_Http_Request $request)
|
||||
{
|
||||
$postBody = $request->getPostBody();
|
||||
$contentType = $request->getRequestHeader("content-type");
|
||||
|
||||
// Set the default content-type as application/x-www-form-urlencoded.
|
||||
if (false == $contentType) {
|
||||
$contentType = self::FORM_URLENCODED;
|
||||
$request->setRequestHeaders(array('content-type' => $contentType));
|
||||
}
|
||||
|
||||
// Force the payload to match the content-type asserted in the header.
|
||||
if ($contentType == self::FORM_URLENCODED && is_array($postBody)) {
|
||||
$postBody = http_build_query($postBody, '', '&');
|
||||
$request->setPostBody($postBody);
|
||||
}
|
||||
|
||||
// Make sure the content-length header is set.
|
||||
if (!$postBody || is_string($postBody)) {
|
||||
$postsLength = strlen($postBody);
|
||||
$request->setRequestHeaders(array('content-length' => $postsLength));
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an already cached request must be revalidated, and if so update
|
||||
* the request with the correct ETag headers.
|
||||
* @param Google_Http_Request $cached A previously cached response.
|
||||
* @param Google_Http_Request $request The outbound request.
|
||||
* return bool If the cached object needs to be revalidated, false if it is
|
||||
* still current and can be re-used.
|
||||
*/
|
||||
protected function checkMustRevalidateCachedRequest($cached, $request)
|
||||
{
|
||||
if (Google_Http_CacheParser::mustRevalidate($cached)) {
|
||||
$addHeaders = [];
|
||||
if ($cached->getResponseHeader('etag')) {
|
||||
// [13.3.4] If an entity tag has been provided by the origin server,
|
||||
// we must use that entity tag in any cache-conditional request.
|
||||
$addHeaders['If-None-Match'] = $cached->getResponseHeader('etag');
|
||||
} elseif ($cached->getResponseHeader('date')) {
|
||||
$addHeaders['If-Modified-Since'] = $cached->getResponseHeader('date');
|
||||
}
|
||||
|
||||
$request->setRequestHeaders($addHeaders);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a cached request, using the headers from the last response.
|
||||
* @param Google_Http_Request $cached A previously cached response.
|
||||
* @param mixed Associative array of response headers from the last request.
|
||||
*/
|
||||
protected function updateCachedRequest($cached, $responseHeaders)
|
||||
{
|
||||
$hopByHop = self::$HOP_BY_HOP;
|
||||
if (!empty($responseHeaders['connection'])) {
|
||||
$connectionHeaders = array_map(
|
||||
'strtolower',
|
||||
array_filter(
|
||||
array_map('trim', explode(',', $responseHeaders['connection']))
|
||||
)
|
||||
);
|
||||
$hopByHop += array_fill_keys($connectionHeaders, true);
|
||||
}
|
||||
|
||||
$endToEnd = array_diff_key($responseHeaders, $hopByHop);
|
||||
$cached->setResponseHeaders($endToEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the IO lib and also the batch processing.
|
||||
*
|
||||
* @param $respData
|
||||
* @param $headerSize
|
||||
* @return array
|
||||
*/
|
||||
public function parseHttpResponse($respData, $headerSize)
|
||||
{
|
||||
// check proxy header
|
||||
foreach (self::$CONNECTION_ESTABLISHED_HEADERS as $established_header) {
|
||||
if (stripos($respData, $established_header) !== false) {
|
||||
// existed, remove it
|
||||
$respData = str_ireplace($established_header, '', $respData);
|
||||
// Subtract the proxy header size unless the cURL bug prior to 7.30.0
|
||||
// is present which prevented the proxy header size from being taken into
|
||||
// account.
|
||||
if (!$this->needsQuirk()) {
|
||||
$headerSize -= strlen($established_header);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($headerSize) {
|
||||
$responseBody = substr($respData, $headerSize);
|
||||
$responseHeaders = substr($respData, 0, $headerSize);
|
||||
} else {
|
||||
$responseSegments = explode("\r\n\r\n", $respData, 2);
|
||||
$responseHeaders = $responseSegments[0];
|
||||
$responseBody = isset($responseSegments[1]) ? $responseSegments[1] :
|
||||
null;
|
||||
}
|
||||
|
||||
$responseHeaders = $this->getHttpResponseHeaders($responseHeaders);
|
||||
return array($responseHeaders, $responseBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse out headers from raw headers
|
||||
* @param rawHeaders array or string
|
||||
* @return array
|
||||
*/
|
||||
public function getHttpResponseHeaders($rawHeaders)
|
||||
{
|
||||
if (is_array($rawHeaders)) {
|
||||
return $this->parseArrayHeaders($rawHeaders);
|
||||
} else {
|
||||
return $this->parseStringHeaders($rawHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseStringHeaders($rawHeaders)
|
||||
{
|
||||
$headers = [];
|
||||
$responseHeaderLines = explode("\r\n", $rawHeaders);
|
||||
foreach ($responseHeaderLines as $headerLine) {
|
||||
if ($headerLine && strpos($headerLine, ':') !== false) {
|
||||
list($header, $value) = explode(': ', $headerLine, 2);
|
||||
$header = strtolower($header);
|
||||
if (isset($headers[$header])) {
|
||||
$headers[$header] .= "\n" . $value;
|
||||
} else {
|
||||
$headers[$header] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
private function parseArrayHeaders($rawHeaders)
|
||||
{
|
||||
$header_count = count($rawHeaders);
|
||||
$headers = [];
|
||||
|
||||
for ($i = 0; $i < $header_count; $i++) {
|
||||
$header = $rawHeaders[$i];
|
||||
// Times will have colons in - so we just want the first match.
|
||||
$header_parts = explode(': ', $header, 2);
|
||||
if (count($header_parts) == 2) {
|
||||
$headers[strtolower($header_parts[0])] = $header_parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
}
|
194
app/api/Google/IO/Curl.php
Executable file
194
app/api/Google/IO/Curl.php
Executable file
|
@ -0,0 +1,194 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Curl based implementation of Google_IO.
|
||||
*
|
||||
* @author Stuart Langley <slangley@google.com>
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
class Google_IO_Curl extends Google_IO_Abstract
|
||||
{
|
||||
// cURL hex representation of version 7.30.0
|
||||
const NO_QUIRK_VERSION = 0x071E00;
|
||||
|
||||
private $options = [];
|
||||
|
||||
/** @var bool $disableProxyWorkaround */
|
||||
private $disableProxyWorkaround;
|
||||
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
if (!extension_loaded('curl')) {
|
||||
$error = 'The cURL IO handler requires the cURL extension to be enabled';
|
||||
$client->getLogger()->critical($error);
|
||||
throw new Google_IO_Exception($error);
|
||||
}
|
||||
|
||||
parent::__construct($client);
|
||||
|
||||
$this->disableProxyWorkaround = $this->client->getClassConfig(
|
||||
'Google_IO_Curl',
|
||||
'disable_proxy_workaround'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an HTTP Request
|
||||
*
|
||||
* @param Google_Http_Request $request the http request to be executed
|
||||
* @return array containing response headers, body, and http code
|
||||
* @throws Google_IO_Exception on curl or IO error
|
||||
*/
|
||||
public function executeRequest(Google_Http_Request $request)
|
||||
{
|
||||
$curl = curl_init();
|
||||
|
||||
if ($request->getPostBody()) {
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getPostBody());
|
||||
}
|
||||
|
||||
$requestHeaders = $request->getRequestHeaders();
|
||||
if ($requestHeaders && is_array($requestHeaders)) {
|
||||
$curlHeaders = [];
|
||||
foreach ($requestHeaders as $k => $v) {
|
||||
$curlHeaders[] = "$k: $v";
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $curlHeaders);
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_URL, $request->getUrl());
|
||||
|
||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $request->getRequestMethod());
|
||||
curl_setopt($curl, CURLOPT_USERAGENT, $request->getUserAgent());
|
||||
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
|
||||
|
||||
// The SSL version will be determined by the underlying library
|
||||
// @see https://github.com/google/google-api-php-client/pull/644
|
||||
//curl_setopt($curl, CURLOPT_SSLVERSION, 1);
|
||||
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_HEADER, true);
|
||||
|
||||
if ($request->canGzip()) {
|
||||
curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
|
||||
}
|
||||
|
||||
$options = $this->client->getClassConfig('Google_IO_Curl', 'options');
|
||||
if (is_array($options)) {
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
foreach ($this->options as $key => $var) {
|
||||
curl_setopt($curl, $key, $var);
|
||||
}
|
||||
|
||||
if (!isset($this->options[CURLOPT_CAINFO])) {
|
||||
curl_setopt($curl, CURLOPT_CAINFO, dirname(__FILE__) . '/cacerts.pem');
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'cURL request',
|
||||
array(
|
||||
'url' => $request->getUrl(),
|
||||
'method' => $request->getRequestMethod(),
|
||||
'headers' => $requestHeaders,
|
||||
'body' => $request->getPostBody()
|
||||
)
|
||||
);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
if ($response === false) {
|
||||
$error = curl_error($curl);
|
||||
$code = curl_errno($curl);
|
||||
$map = $this->client->getClassConfig('Google_IO_Exception', 'retry_map');
|
||||
|
||||
$this->client->getLogger()->error('cURL ' . $error);
|
||||
throw new Google_IO_Exception($error, $code, null, $map);
|
||||
}
|
||||
$headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
|
||||
|
||||
list($responseHeaders, $responseBody) = $this->parseHttpResponse($response, $headerSize);
|
||||
$responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'cURL response',
|
||||
array(
|
||||
'code' => $responseCode,
|
||||
'headers' => $responseHeaders,
|
||||
'body' => $responseBody,
|
||||
)
|
||||
);
|
||||
|
||||
return array($responseBody, $responseHeaders, $responseCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options that update the transport implementation's behavior.
|
||||
* @param $options
|
||||
*/
|
||||
public function setOptions($options)
|
||||
{
|
||||
$this->options = $options + $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum request time in seconds.
|
||||
* @param $timeout in seconds
|
||||
*/
|
||||
public function setTimeout($timeout)
|
||||
{
|
||||
// Since this timeout is really for putting a bound on the time
|
||||
// we'll set them both to the same. If you need to specify a longer
|
||||
// CURLOPT_TIMEOUT, or a higher CONNECTTIMEOUT, the best thing to
|
||||
// do is use the setOptions method for the values individually.
|
||||
$this->options[CURLOPT_CONNECTTIMEOUT] = $timeout;
|
||||
$this->options[CURLOPT_TIMEOUT] = $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum request time in seconds.
|
||||
* @return timeout in seconds
|
||||
*/
|
||||
public function getTimeout()
|
||||
{
|
||||
return $this->options[CURLOPT_TIMEOUT];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the presence of a cURL header processing bug
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function needsQuirk()
|
||||
{
|
||||
if ($this->disableProxyWorkaround) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ver = curl_version();
|
||||
$versionNum = $ver['version_number'];
|
||||
return $versionNum < Google_IO_Curl::NO_QUIRK_VERSION;
|
||||
}
|
||||
}
|
69
app/api/Google/IO/Exception.php
Executable file
69
app/api/Google/IO/Exception.php
Executable file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
class Google_IO_Exception extends Google_Exception implements Google_Task_Retryable
|
||||
{
|
||||
/**
|
||||
* @var array $retryMap Map of errors with retry counts.
|
||||
*/
|
||||
private $retryMap = [];
|
||||
|
||||
/**
|
||||
* Creates a new IO exception with an optional retry map.
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Exception|null $previous
|
||||
* @param array|null $retryMap Map of errors with retry counts.
|
||||
*/
|
||||
public function __construct(
|
||||
$message,
|
||||
$code = 0,
|
||||
Exception $previous = null,
|
||||
array $retryMap = null
|
||||
) {
|
||||
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
} else {
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
if (is_array($retryMap)) {
|
||||
$this->retryMap = $retryMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of times the associated task can be retried.
|
||||
*
|
||||
* NOTE: -1 is returned if the task can be retried indefinitely
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function allowedRetries()
|
||||
{
|
||||
if (isset($this->retryMap[$this->code])) {
|
||||
return $this->retryMap[$this->code];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
243
app/api/Google/IO/Stream.php
Executable file
243
app/api/Google/IO/Stream.php
Executable file
|
@ -0,0 +1,243 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Http Streams based implementation of Google_IO.
|
||||
*
|
||||
* @author Stuart Langley <slangley@google.com>
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
class Google_IO_Stream extends Google_IO_Abstract
|
||||
{
|
||||
const TIMEOUT = "timeout";
|
||||
const ZLIB = "compress.zlib://";
|
||||
private $options = [];
|
||||
private $trappedErrorNumber;
|
||||
private $trappedErrorString;
|
||||
|
||||
private static $DEFAULT_HTTP_CONTEXT = array(
|
||||
"follow_location" => 0,
|
||||
"ignore_errors" => 1,
|
||||
);
|
||||
|
||||
private static $DEFAULT_SSL_CONTEXT = array(
|
||||
"verify_peer" => true,
|
||||
);
|
||||
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
if (!ini_get('allow_url_fopen')) {
|
||||
$error = 'The stream IO handler requires the allow_url_fopen runtime ' .
|
||||
'configuration to be enabled';
|
||||
$client->getLogger()->critical($error);
|
||||
throw new Google_IO_Exception($error);
|
||||
}
|
||||
|
||||
parent::__construct($client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an HTTP Request
|
||||
*
|
||||
* @param Google_Http_Request $request the http request to be executed
|
||||
* @return array containing response headers, body, and http code
|
||||
* @throws Google_IO_Exception on curl or IO error
|
||||
*/
|
||||
public function executeRequest(Google_Http_Request $request)
|
||||
{
|
||||
$default_options = stream_context_get_options(stream_context_get_default());
|
||||
|
||||
$requestHttpContext = array_key_exists('http', $default_options) ?
|
||||
$default_options['http'] : [];
|
||||
|
||||
if ($request->getPostBody()) {
|
||||
$requestHttpContext["content"] = $request->getPostBody();
|
||||
}
|
||||
|
||||
$requestHeaders = $request->getRequestHeaders();
|
||||
if ($requestHeaders && is_array($requestHeaders)) {
|
||||
$headers = "";
|
||||
foreach ($requestHeaders as $k => $v) {
|
||||
$headers .= "$k: $v\r\n";
|
||||
}
|
||||
$requestHttpContext["header"] = $headers;
|
||||
}
|
||||
|
||||
$requestHttpContext["method"] = $request->getRequestMethod();
|
||||
$requestHttpContext["user_agent"] = $request->getUserAgent();
|
||||
|
||||
$requestSslContext = array_key_exists('ssl', $default_options) ?
|
||||
$default_options['ssl'] : [];
|
||||
|
||||
if (!$this->client->isAppEngine() && !array_key_exists("cafile", $requestSslContext)) {
|
||||
$requestSslContext["cafile"] = dirname(__FILE__) . '/cacerts.pem';
|
||||
}
|
||||
|
||||
$options = array(
|
||||
"http" => array_merge(
|
||||
self::$DEFAULT_HTTP_CONTEXT,
|
||||
$requestHttpContext
|
||||
),
|
||||
"ssl" => array_merge(
|
||||
self::$DEFAULT_SSL_CONTEXT,
|
||||
$requestSslContext
|
||||
)
|
||||
);
|
||||
|
||||
$context = stream_context_create($options);
|
||||
|
||||
$url = $request->getUrl();
|
||||
|
||||
if ($request->canGzip()) {
|
||||
$url = self::ZLIB . $url;
|
||||
}
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'Stream request',
|
||||
array(
|
||||
'url' => $url,
|
||||
'method' => $request->getRequestMethod(),
|
||||
'headers' => $requestHeaders,
|
||||
'body' => $request->getPostBody()
|
||||
)
|
||||
);
|
||||
|
||||
// We are trapping any thrown errors in this method only and
|
||||
// throwing an exception.
|
||||
$this->trappedErrorNumber = null;
|
||||
$this->trappedErrorString = null;
|
||||
|
||||
// START - error trap.
|
||||
set_error_handler(array($this, 'trapError'));
|
||||
$fh = fopen($url, 'r', false, $context);
|
||||
restore_error_handler();
|
||||
// END - error trap.
|
||||
|
||||
if ($this->trappedErrorNumber) {
|
||||
$error = sprintf(
|
||||
"HTTP Error: Unable to connect: '%s'",
|
||||
$this->trappedErrorString
|
||||
);
|
||||
|
||||
$this->client->getLogger()->error('Stream ' . $error);
|
||||
throw new Google_IO_Exception($error, $this->trappedErrorNumber);
|
||||
}
|
||||
|
||||
$response_data = false;
|
||||
$respHttpCode = self::UNKNOWN_CODE;
|
||||
if ($fh) {
|
||||
if (isset($this->options[self::TIMEOUT])) {
|
||||
stream_set_timeout($fh, $this->options[self::TIMEOUT]);
|
||||
}
|
||||
|
||||
$response_data = stream_get_contents($fh);
|
||||
fclose($fh);
|
||||
|
||||
$respHttpCode = $this->getHttpResponseCode($http_response_header);
|
||||
}
|
||||
|
||||
if (false === $response_data) {
|
||||
$error = sprintf(
|
||||
"HTTP Error: Unable to connect: '%s'",
|
||||
$respHttpCode
|
||||
);
|
||||
|
||||
$this->client->getLogger()->error('Stream ' . $error);
|
||||
throw new Google_IO_Exception($error, $respHttpCode);
|
||||
}
|
||||
|
||||
$responseHeaders = $this->getHttpResponseHeaders($http_response_header);
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'Stream response',
|
||||
array(
|
||||
'code' => $respHttpCode,
|
||||
'headers' => $responseHeaders,
|
||||
'body' => $response_data,
|
||||
)
|
||||
);
|
||||
|
||||
return array($response_data, $responseHeaders, $respHttpCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options that update the transport implementation's behavior.
|
||||
* @param $options
|
||||
*/
|
||||
public function setOptions($options)
|
||||
{
|
||||
$this->options = $options + $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to handle errors, used for error handling around
|
||||
* stream connection methods.
|
||||
*/
|
||||
public function trapError($errno, $errstr)
|
||||
{
|
||||
$this->trappedErrorNumber = $errno;
|
||||
$this->trappedErrorString = $errstr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum request time in seconds.
|
||||
* @param $timeout in seconds
|
||||
*/
|
||||
public function setTimeout($timeout)
|
||||
{
|
||||
$this->options[self::TIMEOUT] = $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum request time in seconds.
|
||||
* @return timeout in seconds
|
||||
*/
|
||||
public function getTimeout()
|
||||
{
|
||||
return $this->options[self::TIMEOUT];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the presence of a cURL header processing bug
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function needsQuirk()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getHttpResponseCode($response_headers)
|
||||
{
|
||||
$header_count = count($response_headers);
|
||||
|
||||
for ($i = 0; $i < $header_count; $i++) {
|
||||
$header = $response_headers[$i];
|
||||
if (strncasecmp("HTTP", $header, strlen("HTTP")) == 0) {
|
||||
$response = explode(' ', $header);
|
||||
return $response[1];
|
||||
}
|
||||
}
|
||||
return self::UNKNOWN_CODE;
|
||||
}
|
||||
}
|
2183
app/api/Google/IO/cacerts.pem
Executable file
2183
app/api/Google/IO/cacerts.pem
Executable file
File diff suppressed because it is too large
Load diff
408
app/api/Google/Logger/Abstract.php
Executable file
408
app/api/Google/Logger/Abstract.php
Executable file
|
@ -0,0 +1,408 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract logging class based on the PSR-3 standard.
|
||||
*
|
||||
* NOTE: We don't implement `Psr\Log\LoggerInterface` because we need to
|
||||
* maintain PHP 5.2 support.
|
||||
*
|
||||
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
|
||||
*/
|
||||
abstract class Google_Logger_Abstract
|
||||
{
|
||||
/**
|
||||
* Default log format
|
||||
*/
|
||||
const DEFAULT_LOG_FORMAT = "[%datetime%] %level%: %message% %context%\n";
|
||||
/**
|
||||
* Default date format
|
||||
*
|
||||
* Example: 16/Nov/2014:03:26:16 -0500
|
||||
*/
|
||||
const DEFAULT_DATE_FORMAT = 'd/M/Y:H:i:s O';
|
||||
|
||||
/**
|
||||
* System is unusable
|
||||
*/
|
||||
const EMERGENCY = 'emergency';
|
||||
/**
|
||||
* Action must be taken immediately
|
||||
*
|
||||
* Example: Entire website down, database unavailable, etc. This should
|
||||
* trigger the SMS alerts and wake you up.
|
||||
*/
|
||||
const ALERT = 'alert';
|
||||
/**
|
||||
* Critical conditions
|
||||
*
|
||||
* Example: Application component unavailable, unexpected exception.
|
||||
*/
|
||||
const CRITICAL = 'critical';
|
||||
/**
|
||||
* Runtime errors that do not require immediate action but should typically
|
||||
* be logged and monitored.
|
||||
*/
|
||||
const ERROR = 'error';
|
||||
/**
|
||||
* Exceptional occurrences that are not errors.
|
||||
*
|
||||
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||
* that are not necessarily wrong.
|
||||
*/
|
||||
const WARNING = 'warning';
|
||||
/**
|
||||
* Normal but significant events.
|
||||
*/
|
||||
const NOTICE = 'notice';
|
||||
/**
|
||||
* Interesting events.
|
||||
*
|
||||
* Example: User logs in, SQL logs.
|
||||
*/
|
||||
const INFO = 'info';
|
||||
/**
|
||||
* Detailed debug information.
|
||||
*/
|
||||
const DEBUG = 'debug';
|
||||
|
||||
/**
|
||||
* @var array $levels Logging levels
|
||||
*/
|
||||
protected static $levels = array(
|
||||
self::EMERGENCY => 600,
|
||||
self::ALERT => 550,
|
||||
self::CRITICAL => 500,
|
||||
self::ERROR => 400,
|
||||
self::WARNING => 300,
|
||||
self::NOTICE => 250,
|
||||
self::INFO => 200,
|
||||
self::DEBUG => 100,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var integer $level The minimum logging level
|
||||
*/
|
||||
protected $level = self::DEBUG;
|
||||
|
||||
/**
|
||||
* @var string $logFormat The current log format
|
||||
*/
|
||||
protected $logFormat = self::DEFAULT_LOG_FORMAT;
|
||||
/**
|
||||
* @var string $dateFormat The current date format
|
||||
*/
|
||||
protected $dateFormat = self::DEFAULT_DATE_FORMAT;
|
||||
|
||||
/**
|
||||
* @var boolean $allowNewLines If newlines are allowed
|
||||
*/
|
||||
protected $allowNewLines = false;
|
||||
|
||||
/**
|
||||
* @param Google_Client $client The current Google client
|
||||
*/
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
$this->setLevel(
|
||||
$client->getClassConfig('Google_Logger_Abstract', 'level')
|
||||
);
|
||||
|
||||
$format = $client->getClassConfig('Google_Logger_Abstract', 'log_format');
|
||||
$this->logFormat = $format ? $format : self::DEFAULT_LOG_FORMAT;
|
||||
|
||||
$format = $client->getClassConfig('Google_Logger_Abstract', 'date_format');
|
||||
$this->dateFormat = $format ? $format : self::DEFAULT_DATE_FORMAT;
|
||||
|
||||
$this->allowNewLines = (bool) $client->getClassConfig(
|
||||
'Google_Logger_Abstract',
|
||||
'allow_newlines'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum logging level that this logger handles.
|
||||
*
|
||||
* @param integer $level
|
||||
*/
|
||||
public function setLevel($level)
|
||||
{
|
||||
$this->level = $this->normalizeLevel($level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the logger should handle messages at the provided level.
|
||||
*
|
||||
* @param integer $level
|
||||
* @return boolean
|
||||
*/
|
||||
public function shouldHandle($level)
|
||||
{
|
||||
return $this->normalizeLevel($level) >= $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* System is unusable.
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param array $context The log context
|
||||
*/
|
||||
public function emergency($message, array $context = array())
|
||||
{
|
||||
$this->log(self::EMERGENCY, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action must be taken immediately.
|
||||
*
|
||||
* Example: Entire website down, database unavailable, etc. This should
|
||||
* trigger the SMS alerts and wake you up.
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param array $context The log context
|
||||
*/
|
||||
public function alert($message, array $context = array())
|
||||
{
|
||||
$this->log(self::ALERT, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical conditions.
|
||||
*
|
||||
* Example: Application component unavailable, unexpected exception.
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param array $context The log context
|
||||
*/
|
||||
public function critical($message, array $context = array())
|
||||
{
|
||||
$this->log(self::CRITICAL, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime errors that do not require immediate action but should typically
|
||||
* be logged and monitored.
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param array $context The log context
|
||||
*/
|
||||
public function error($message, array $context = array())
|
||||
{
|
||||
$this->log(self::ERROR, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptional occurrences that are not errors.
|
||||
*
|
||||
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||
* that are not necessarily wrong.
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param array $context The log context
|
||||
*/
|
||||
public function warning($message, array $context = array())
|
||||
{
|
||||
$this->log(self::WARNING, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal but significant events.
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param array $context The log context
|
||||
*/
|
||||
public function notice($message, array $context = array())
|
||||
{
|
||||
$this->log(self::NOTICE, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interesting events.
|
||||
*
|
||||
* Example: User logs in, SQL logs.
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param array $context The log context
|
||||
*/
|
||||
public function info($message, array $context = array())
|
||||
{
|
||||
$this->log(self::INFO, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed debug information.
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param array $context The log context
|
||||
*/
|
||||
public function debug($message, array $context = array())
|
||||
{
|
||||
$this->log(self::DEBUG, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs with an arbitrary level.
|
||||
*
|
||||
* @param mixed $level The log level
|
||||
* @param string $message The log message
|
||||
* @param array $context The log context
|
||||
*/
|
||||
public function log($level, $message, array $context = array())
|
||||
{
|
||||
if (!$this->shouldHandle($level)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$levelName = is_int($level) ? array_search($level, self::$levels) : $level;
|
||||
$message = $this->interpolate(
|
||||
array(
|
||||
'message' => $message,
|
||||
'context' => $context,
|
||||
'level' => strtoupper($levelName),
|
||||
'datetime' => new DateTime(),
|
||||
)
|
||||
);
|
||||
|
||||
$this->write($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates log variables into the defined log format.
|
||||
*
|
||||
* @param array $variables The log variables.
|
||||
* @return string
|
||||
*/
|
||||
protected function interpolate(array $variables = array())
|
||||
{
|
||||
$template = $this->logFormat;
|
||||
|
||||
if (!$variables['context']) {
|
||||
$template = str_replace('%context%', '', $template);
|
||||
unset($variables['context']);
|
||||
} else {
|
||||
$this->reverseJsonInContext($variables['context']);
|
||||
}
|
||||
|
||||
foreach ($variables as $key => $value) {
|
||||
if (strpos($template, '%'. $key .'%') !== false) {
|
||||
$template = str_replace(
|
||||
'%' . $key . '%',
|
||||
$this->export($value),
|
||||
$template
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses JSON encoded PHP arrays and objects so that they log better.
|
||||
*
|
||||
* @param array $context The log context
|
||||
*/
|
||||
protected function reverseJsonInContext(array &$context)
|
||||
{
|
||||
if (!$context) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($context as $key => $val) {
|
||||
if (!$val || !is_string($val) || !($val[0] == '{' || $val[0] == '[')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$json = @json_decode($val);
|
||||
if (is_object($json) || is_array($json)) {
|
||||
$context[$key] = $json;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports a PHP value for logging to a string.
|
||||
*
|
||||
* @param mixed $value The value to
|
||||
*/
|
||||
protected function export($value)
|
||||
{
|
||||
if (is_string($value)) {
|
||||
if ($this->allowNewLines) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return preg_replace('/[\r\n]+/', ' ', $value);
|
||||
}
|
||||
|
||||
if (is_resource($value)) {
|
||||
return sprintf(
|
||||
'resource(%d) of type (%s)',
|
||||
$value,
|
||||
get_resource_type($value)
|
||||
);
|
||||
}
|
||||
|
||||
if ($value instanceof DateTime) {
|
||||
return $value->format($this->dateFormat);
|
||||
}
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
|
||||
$options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
|
||||
|
||||
if ($this->allowNewLines) {
|
||||
$options |= JSON_PRETTY_PRINT;
|
||||
}
|
||||
|
||||
return @json_encode($value, $options);
|
||||
}
|
||||
|
||||
return str_replace('\\/', '/', @json_encode($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given log level to the integer form.
|
||||
*
|
||||
* @param mixed $level The logging level
|
||||
* @return integer $level The normalized level
|
||||
* @throws Google_Logger_Exception If $level is invalid
|
||||
*/
|
||||
protected function normalizeLevel($level)
|
||||
{
|
||||
if (is_int($level) && array_search($level, self::$levels) !== false) {
|
||||
return $level;
|
||||
}
|
||||
|
||||
if (is_string($level) && isset(self::$levels[$level])) {
|
||||
return self::$levels[$level];
|
||||
}
|
||||
|
||||
throw new Google_Logger_Exception(
|
||||
sprintf("Unknown LogLevel: '%s'", $level)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a message to the current log implementation.
|
||||
*
|
||||
* @param string $message The message
|
||||
*/
|
||||
abstract protected function write($message);
|
||||
}
|
24
app/api/Google/Logger/Exception.php
Executable file
24
app/api/Google/Logger/Exception.php
Executable file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
class Google_Logger_Exception extends Google_Exception
|
||||
{
|
||||
}
|
158
app/api/Google/Logger/File.php
Executable file
158
app/api/Google/Logger/File.php
Executable file
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* File logging class based on the PSR-3 standard.
|
||||
*
|
||||
* This logger writes to a PHP stream resource.
|
||||
*/
|
||||
class Google_Logger_File extends Google_Logger_Abstract
|
||||
{
|
||||
/**
|
||||
* @var string|resource $file Where logs are written
|
||||
*/
|
||||
private $file;
|
||||
/**
|
||||
* @var integer $mode The mode to use if the log file needs to be created
|
||||
*/
|
||||
private $mode = 0640;
|
||||
/**
|
||||
* @var boolean $lock If a lock should be attempted before writing to the log
|
||||
*/
|
||||
private $lock = false;
|
||||
|
||||
/**
|
||||
* @var integer $trappedErrorNumber Trapped error number
|
||||
*/
|
||||
private $trappedErrorNumber;
|
||||
/**
|
||||
* @var string $trappedErrorString Trapped error string
|
||||
*/
|
||||
private $trappedErrorString;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
parent::__construct($client);
|
||||
|
||||
$file = $client->getClassConfig('Google_Logger_File', 'file');
|
||||
if (!is_string($file) && !is_resource($file)) {
|
||||
throw new Google_Logger_Exception(
|
||||
'File logger requires a filename or a valid file pointer'
|
||||
);
|
||||
}
|
||||
|
||||
$mode = $client->getClassConfig('Google_Logger_File', 'mode');
|
||||
if (!$mode) {
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
$this->lock = (bool) $client->getClassConfig('Google_Logger_File', 'lock');
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function write($message)
|
||||
{
|
||||
if (is_string($this->file)) {
|
||||
$this->open();
|
||||
} elseif (!is_resource($this->file)) {
|
||||
throw new Google_Logger_Exception('File pointer is no longer available');
|
||||
}
|
||||
|
||||
if ($this->lock) {
|
||||
flock($this->file, LOCK_EX);
|
||||
}
|
||||
|
||||
fwrite($this->file, (string) $message);
|
||||
|
||||
if ($this->lock) {
|
||||
flock($this->file, LOCK_UN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the log for writing.
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
private function open()
|
||||
{
|
||||
// Used for trapping `fopen()` errors.
|
||||
$this->trappedErrorNumber = null;
|
||||
$this->trappedErrorString = null;
|
||||
|
||||
$old = set_error_handler(array($this, 'trapError'));
|
||||
|
||||
$needsChmod = !file_exists($this->file);
|
||||
$fh = fopen($this->file, 'a');
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
// Handles trapped `fopen()` errors.
|
||||
if ($this->trappedErrorNumber) {
|
||||
throw new Google_Logger_Exception(
|
||||
sprintf(
|
||||
"Logger Error: '%s'",
|
||||
$this->trappedErrorString
|
||||
),
|
||||
$this->trappedErrorNumber
|
||||
);
|
||||
}
|
||||
|
||||
if ($needsChmod) {
|
||||
@chmod($this->file, $this->mode & ~umask());
|
||||
}
|
||||
|
||||
return $this->file = $fh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the log stream resource.
|
||||
*/
|
||||
private function close()
|
||||
{
|
||||
if (is_resource($this->file)) {
|
||||
fclose($this->file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traps `fopen()` errors.
|
||||
*
|
||||
* @param integer $errno The error number
|
||||
* @param string $errstr The error string
|
||||
*/
|
||||
private function trapError($errno, $errstr)
|
||||
{
|
||||
$this->trappedErrorNumber = $errno;
|
||||
$this->trappedErrorString = $errstr;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
}
|
43
app/api/Google/Logger/Null.php
Executable file
43
app/api/Google/Logger/Null.php
Executable file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Null logger based on the PSR-3 standard.
|
||||
*
|
||||
* This logger simply discards all messages.
|
||||
*/
|
||||
class Google_Logger_Null extends Google_Logger_Abstract
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function shouldHandle($level)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function write($message, array $context = array())
|
||||
{
|
||||
}
|
||||
}
|
93
app/api/Google/Logger/Psr.php
Executable file
93
app/api/Google/Logger/Psr.php
Executable file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Psr logging class based on the PSR-3 standard.
|
||||
*
|
||||
* This logger will delegate all logging to a PSR-3 compatible logger specified
|
||||
* with the `Google_Logger_Psr::setLogger()` method.
|
||||
*/
|
||||
class Google_Logger_Psr extends Google_Logger_Abstract
|
||||
{
|
||||
/**
|
||||
* @param Psr\Log\LoggerInterface $logger The PSR-3 logger
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @param Google_Client $client The current Google client
|
||||
* @param Psr\Log\LoggerInterface $logger PSR-3 logger where logging will be delegated.
|
||||
*/
|
||||
public function __construct(Google_Client $client, /*Psr\Log\LoggerInterface*/ $logger = null)
|
||||
{
|
||||
parent::__construct($client);
|
||||
|
||||
if ($logger) {
|
||||
$this->setLogger($logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the PSR-3 logger where logging will be delegated.
|
||||
*
|
||||
* NOTE: The `$logger` should technically implement
|
||||
* `Psr\Log\LoggerInterface`, but we don't explicitly require this so that
|
||||
* we can be compatible with PHP 5.2.
|
||||
*
|
||||
* @param Psr\Log\LoggerInterface $logger The PSR-3 logger
|
||||
*/
|
||||
public function setLogger(/*Psr\Log\LoggerInterface*/ $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function shouldHandle($level)
|
||||
{
|
||||
return isset($this->logger) && parent::shouldHandle($level);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function log($level, $message, array $context = array())
|
||||
{
|
||||
if (!$this->shouldHandle($level)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($context) {
|
||||
$this->reverseJsonInContext($context);
|
||||
}
|
||||
|
||||
$levelName = is_int($level) ? array_search($level, self::$levels) : $level;
|
||||
$this->logger->log($levelName, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function write($message, array $context = array())
|
||||
{
|
||||
}
|
||||
}
|
312
app/api/Google/Model.php
Executable file
312
app/api/Google/Model.php
Executable file
|
@ -0,0 +1,312 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class defines attributes, valid values, and usage which is generated
|
||||
* from a given json schema.
|
||||
* http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5
|
||||
*
|
||||
*/
|
||||
class Google_Model implements ArrayAccess
|
||||
{
|
||||
/**
|
||||
* If you need to specify a NULL JSON value, use Google_Model::NULL_VALUE
|
||||
* instead - it will be replaced when converting to JSON with a real null.
|
||||
*/
|
||||
const NULL_VALUE = "{}gapi-php-null";
|
||||
protected $internal_gapi_mappings = [];
|
||||
protected $modelData = [];
|
||||
protected $processed = [];
|
||||
|
||||
/**
|
||||
* Polymorphic - accepts a variable number of arguments dependent
|
||||
* on the type of the model subclass.
|
||||
*/
|
||||
final public function __construct()
|
||||
{
|
||||
if (func_num_args() == 1 && is_array(func_get_arg(0))) {
|
||||
// Initialize the model with the array's contents.
|
||||
$array = func_get_arg(0);
|
||||
$this->mapTypes($array);
|
||||
}
|
||||
$this->gapiInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter that handles passthrough access to the data array, and lazy object creation.
|
||||
* @param string $key Property name.
|
||||
* @return mixed The value if any, or null.
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
$keyTypeName = $this->keyType($key);
|
||||
$keyDataType = $this->dataType($key);
|
||||
if (isset($this->$keyTypeName) && !isset($this->processed[$key])) {
|
||||
if (isset($this->modelData[$key])) {
|
||||
$val = $this->modelData[$key];
|
||||
} else if (isset($this->$keyDataType) &&
|
||||
($this->$keyDataType == 'array' || $this->$keyDataType == 'map')) {
|
||||
$val = [];
|
||||
} else {
|
||||
$val = null;
|
||||
}
|
||||
|
||||
if ($this->isAssociativeArray($val)) {
|
||||
if (isset($this->$keyDataType) && 'map' == $this->$keyDataType) {
|
||||
foreach ($val as $arrayKey => $arrayItem) {
|
||||
$this->modelData[$key][$arrayKey] =
|
||||
$this->createObjectFromName($keyTypeName, $arrayItem);
|
||||
}
|
||||
} else {
|
||||
$this->modelData[$key] = $this->createObjectFromName($keyTypeName, $val);
|
||||
}
|
||||
} else if (is_array($val)) {
|
||||
$arrayObject = [];
|
||||
foreach ($val as $arrayIndex => $arrayItem) {
|
||||
$arrayObject[$arrayIndex] =
|
||||
$this->createObjectFromName($keyTypeName, $arrayItem);
|
||||
}
|
||||
$this->modelData[$key] = $arrayObject;
|
||||
}
|
||||
$this->processed[$key] = true;
|
||||
}
|
||||
|
||||
return isset($this->modelData[$key]) ? $this->modelData[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this object's properties from an array.
|
||||
*
|
||||
* @param array $array Used to seed this object's properties.
|
||||
* @return void
|
||||
*/
|
||||
protected function mapTypes($array)
|
||||
{
|
||||
// Hard initialise simple types, lazy load more complex ones.
|
||||
foreach ($array as $key => $val) {
|
||||
if ( !property_exists($this, $this->keyType($key)) &&
|
||||
property_exists($this, $key)) {
|
||||
$this->$key = $val;
|
||||
unset($array[$key]);
|
||||
} elseif (property_exists($this, $camelKey = Google_Utils::camelCase($key))) {
|
||||
// This checks if property exists as camelCase, leaving it in array as snake_case
|
||||
// in case of backwards compatibility issues.
|
||||
$this->$camelKey = $val;
|
||||
}
|
||||
}
|
||||
$this->modelData = $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blank initialiser to be used in subclasses to do post-construction initialisation - this
|
||||
* avoids the need for subclasses to have to implement the variadics handling in their
|
||||
* constructors.
|
||||
*/
|
||||
protected function gapiInit()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a simplified object suitable for straightforward
|
||||
* conversion to JSON. This is relatively expensive
|
||||
* due to the usage of reflection, but shouldn't be called
|
||||
* a whole lot, and is the most straightforward way to filter.
|
||||
*/
|
||||
public function toSimpleObject()
|
||||
{
|
||||
$object = new stdClass();
|
||||
|
||||
// Process all other data.
|
||||
foreach ($this->modelData as $key => $val) {
|
||||
$result = $this->getSimpleValue($val);
|
||||
if ($result !== null) {
|
||||
$object->$key = $this->nullPlaceholderCheck($result);
|
||||
}
|
||||
}
|
||||
|
||||
// Process all public properties.
|
||||
$reflect = new ReflectionObject($this);
|
||||
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
|
||||
foreach ($props as $member) {
|
||||
$name = $member->getName();
|
||||
$result = $this->getSimpleValue($this->$name);
|
||||
if ($result !== null) {
|
||||
$name = $this->getMappedName($name);
|
||||
$object->$name = $this->nullPlaceholderCheck($result);
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle different types of values, primarily
|
||||
* other objects and map and array data types.
|
||||
*/
|
||||
private function getSimpleValue($value)
|
||||
{
|
||||
if ($value instanceof Google_Model) {
|
||||
return $value->toSimpleObject();
|
||||
} else if (is_array($value)) {
|
||||
$return = [];
|
||||
foreach ($value as $key => $a_value) {
|
||||
$a_value = $this->getSimpleValue($a_value);
|
||||
if ($a_value !== null) {
|
||||
$key = $this->getMappedName($key);
|
||||
$return[$key] = $this->nullPlaceholderCheck($a_value);
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the value is the null placeholder and return true null.
|
||||
*/
|
||||
private function nullPlaceholderCheck($value)
|
||||
{
|
||||
if ($value === self::NULL_VALUE) {
|
||||
return null;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is an internal name mapping, use that.
|
||||
*/
|
||||
private function getMappedName($key)
|
||||
{
|
||||
if (isset($this->internal_gapi_mappings) &&
|
||||
isset($this->internal_gapi_mappings[$key])) {
|
||||
$key = $this->internal_gapi_mappings[$key];
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true only if the array is associative.
|
||||
* @param array $array
|
||||
* @return bool True if the array is associative.
|
||||
*/
|
||||
protected function isAssociativeArray($array)
|
||||
{
|
||||
if (!is_array($array)) {
|
||||
return false;
|
||||
}
|
||||
$keys = array_keys($array);
|
||||
foreach ($keys as $key) {
|
||||
if (is_string($key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a variable name, discover its type.
|
||||
*
|
||||
* @param $name
|
||||
* @param $item
|
||||
* @return object The object from the item.
|
||||
*/
|
||||
private function createObjectFromName($name, $item)
|
||||
{
|
||||
$type = $this->$name;
|
||||
return new $type($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if $obj is an array.
|
||||
* @throws Google_Exception Thrown if $obj isn't an array.
|
||||
* @param array $obj Items that should be validated.
|
||||
* @param string $method Method expecting an array as an argument.
|
||||
*/
|
||||
public function assertIsArray($obj, $method)
|
||||
{
|
||||
if ($obj && !is_array($obj)) {
|
||||
throw new Google_Exception(
|
||||
"Incorrect parameter type passed to $method(). Expected an array."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->$offset) || isset($this->modelData[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return isset($this->$offset) ?
|
||||
$this->$offset :
|
||||
$this->__get($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (property_exists($this, $offset)) {
|
||||
$this->$offset = $value;
|
||||
} else {
|
||||
$this->modelData[$offset] = $value;
|
||||
$this->processed[$offset] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->modelData[$offset]);
|
||||
}
|
||||
|
||||
protected function keyType($key)
|
||||
{
|
||||
return $key . "Type";
|
||||
}
|
||||
|
||||
protected function dataType($key)
|
||||
{
|
||||
return $key . "DataType";
|
||||
}
|
||||
|
||||
public function __isset($key)
|
||||
{
|
||||
return isset($this->modelData[$key]);
|
||||
}
|
||||
|
||||
public function __unset($key)
|
||||
{
|
||||
unset($this->modelData[$key]);
|
||||
}
|
||||
}
|
56
app/api/Google/Service.php
Executable file
56
app/api/Google/Service.php
Executable file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Google_Service
|
||||
{
|
||||
public $batchPath;
|
||||
public $rootUrl;
|
||||
public $version;
|
||||
public $servicePath;
|
||||
public $availableScopes;
|
||||
public $resource;
|
||||
private $client;
|
||||
|
||||
public function __construct(Google_Client $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated Google_Client class.
|
||||
* @return Google_Client
|
||||
*/
|
||||
public function getClient()
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new HTTP Batch handler for this service
|
||||
*
|
||||
* @return Google_Http_Batch
|
||||
*/
|
||||
public function createBatch()
|
||||
{
|
||||
return new Google_Http_Batch(
|
||||
$this->client,
|
||||
false,
|
||||
$this->rootUrl,
|
||||
$this->batchPath
|
||||
);
|
||||
}
|
||||
}
|
3856
app/api/Google/Service/Calendar.php
Executable file
3856
app/api/Google/Service/Calendar.php
Executable file
File diff suppressed because it is too large
Load diff
105
app/api/Google/Service/Exception.php
Executable file
105
app/api/Google/Service/Exception.php
Executable file
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
class Google_Service_Exception extends Google_Exception implements Google_Task_Retryable
|
||||
{
|
||||
/**
|
||||
* Optional list of errors returned in a JSON body of an HTTP error response.
|
||||
*/
|
||||
protected $errors = [];
|
||||
|
||||
/**
|
||||
* @var array $retryMap Map of errors with retry counts.
|
||||
*/
|
||||
private $retryMap = [];
|
||||
|
||||
/**
|
||||
* Override default constructor to add the ability to set $errors and a retry
|
||||
* map.
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Exception|null $previous
|
||||
* @param [{string, string}] errors List of errors returned in an HTTP
|
||||
* response. Defaults to [].
|
||||
* @param array|null $retryMap Map of errors with retry counts.
|
||||
*/
|
||||
public function __construct(
|
||||
$message,
|
||||
$code = 0,
|
||||
Exception $previous = null,
|
||||
$errors = array(),
|
||||
array $retryMap = null
|
||||
) {
|
||||
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
} else {
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
$this->errors = $errors;
|
||||
|
||||
if (is_array($retryMap)) {
|
||||
$this->retryMap = $retryMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An example of the possible errors returned.
|
||||
*
|
||||
* {
|
||||
* "domain": "global",
|
||||
* "reason": "authError",
|
||||
* "message": "Invalid Credentials",
|
||||
* "locationType": "header",
|
||||
* "location": "Authorization",
|
||||
* }
|
||||
*
|
||||
* @return [{string, string}] List of errors return in an HTTP response or [].
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of times the associated task can be retried.
|
||||
*
|
||||
* NOTE: -1 is returned if the task can be retried indefinitely
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function allowedRetries()
|
||||
{
|
||||
if (isset($this->retryMap[$this->code])) {
|
||||
return $this->retryMap[$this->code];
|
||||
}
|
||||
|
||||
$errors = $this->getErrors();
|
||||
|
||||
if (!empty($errors) && isset($errors[0]['reason']) &&
|
||||
isset($this->retryMap[$errors[0]['reason']])) {
|
||||
return $this->retryMap[$errors[0]['reason']];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
255
app/api/Google/Service/Resource.php
Executable file
255
app/api/Google/Service/Resource.php
Executable file
|
@ -0,0 +1,255 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the actual methods/resources of the discovered Google API using magic function
|
||||
* calling overloading (__call()), which on call will see if the method name (plus.activities.list)
|
||||
* is available in this service, and if so construct an apiHttpRequest representing it.
|
||||
*
|
||||
*/
|
||||
class Google_Service_Resource
|
||||
{
|
||||
// Valid query parameters that work, but don't appear in discovery.
|
||||
private $stackParameters = array(
|
||||
'alt' => array('type' => 'string', 'location' => 'query'),
|
||||
'fields' => array('type' => 'string', 'location' => 'query'),
|
||||
'trace' => array('type' => 'string', 'location' => 'query'),
|
||||
'userIp' => array('type' => 'string', 'location' => 'query'),
|
||||
'quotaUser' => array('type' => 'string', 'location' => 'query'),
|
||||
'data' => array('type' => 'string', 'location' => 'body'),
|
||||
'mimeType' => array('type' => 'string', 'location' => 'header'),
|
||||
'uploadType' => array('type' => 'string', 'location' => 'query'),
|
||||
'mediaUpload' => array('type' => 'complex', 'location' => 'query'),
|
||||
'prettyPrint' => array('type' => 'string', 'location' => 'query'),
|
||||
);
|
||||
|
||||
/** @var string $rootUrl */
|
||||
private $rootUrl;
|
||||
|
||||
/** @var Google_Client $client */
|
||||
private $client;
|
||||
|
||||
/** @var string $serviceName */
|
||||
private $serviceName;
|
||||
|
||||
/** @var string $servicePath */
|
||||
private $servicePath;
|
||||
|
||||
/** @var string $resourceName */
|
||||
private $resourceName;
|
||||
|
||||
/** @var array $methods */
|
||||
private $methods;
|
||||
|
||||
public function __construct($service, $serviceName, $resourceName, $resource)
|
||||
{
|
||||
$this->rootUrl = $service->rootUrl;
|
||||
$this->client = $service->getClient();
|
||||
$this->servicePath = $service->servicePath;
|
||||
$this->serviceName = $serviceName;
|
||||
$this->resourceName = $resourceName;
|
||||
$this->methods = is_array($resource) && isset($resource['methods']) ?
|
||||
$resource['methods'] :
|
||||
array($resourceName => $resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: This function needs simplifying.
|
||||
* @param $name
|
||||
* @param $arguments
|
||||
* @param $expected_class - optional, the expected class name
|
||||
* @return Google_Http_Request|expected_class
|
||||
* @throws Google_Exception
|
||||
*/
|
||||
public function call($name, $arguments, $expected_class = null)
|
||||
{
|
||||
if (! isset($this->methods[$name])) {
|
||||
$this->client->getLogger()->error(
|
||||
'Service method unknown',
|
||||
array(
|
||||
'service' => $this->serviceName,
|
||||
'resource' => $this->resourceName,
|
||||
'method' => $name
|
||||
)
|
||||
);
|
||||
|
||||
throw new Google_Exception(
|
||||
"Unknown function: " .
|
||||
"{$this->serviceName}->{$this->resourceName}->{$name}()"
|
||||
);
|
||||
}
|
||||
$method = $this->methods[$name];
|
||||
$parameters = $arguments[0];
|
||||
|
||||
// postBody is a special case since it's not defined in the discovery
|
||||
// document as parameter, but we abuse the param entry for storing it.
|
||||
$postBody = null;
|
||||
if (isset($parameters['postBody'])) {
|
||||
if ($parameters['postBody'] instanceof Google_Model) {
|
||||
// In the cases the post body is an existing object, we want
|
||||
// to use the smart method to create a simple object for
|
||||
// for JSONification.
|
||||
$parameters['postBody'] = $parameters['postBody']->toSimpleObject();
|
||||
} else if (is_object($parameters['postBody'])) {
|
||||
// If the post body is another kind of object, we will try and
|
||||
// wrangle it into a sensible format.
|
||||
$parameters['postBody'] =
|
||||
$this->convertToArrayAndStripNulls($parameters['postBody']);
|
||||
}
|
||||
$postBody = json_encode($parameters['postBody']);
|
||||
if ($postBody === false && $parameters['postBody'] !== false) {
|
||||
throw new Google_Exception("JSON encoding failed. Ensure all strings in the request are UTF-8 encoded.");
|
||||
}
|
||||
unset($parameters['postBody']);
|
||||
}
|
||||
|
||||
// TODO: optParams here probably should have been
|
||||
// handled already - this may well be redundant code.
|
||||
if (isset($parameters['optParams'])) {
|
||||
$optParams = $parameters['optParams'];
|
||||
unset($parameters['optParams']);
|
||||
$parameters = array_merge($parameters, $optParams);
|
||||
}
|
||||
|
||||
if (!isset($method['parameters'])) {
|
||||
$method['parameters'] = [];
|
||||
}
|
||||
|
||||
$method['parameters'] = array_merge(
|
||||
$this->stackParameters,
|
||||
$method['parameters']
|
||||
);
|
||||
foreach ($parameters as $key => $val) {
|
||||
if ($key != 'postBody' && ! isset($method['parameters'][$key])) {
|
||||
$this->client->getLogger()->error(
|
||||
'Service parameter unknown',
|
||||
array(
|
||||
'service' => $this->serviceName,
|
||||
'resource' => $this->resourceName,
|
||||
'method' => $name,
|
||||
'parameter' => $key
|
||||
)
|
||||
);
|
||||
throw new Google_Exception("($name) unknown parameter: '$key'");
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($method['parameters'] as $paramName => $paramSpec) {
|
||||
if (isset($paramSpec['required']) &&
|
||||
$paramSpec['required'] &&
|
||||
! isset($parameters[$paramName])
|
||||
) {
|
||||
$this->client->getLogger()->error(
|
||||
'Service parameter missing',
|
||||
array(
|
||||
'service' => $this->serviceName,
|
||||
'resource' => $this->resourceName,
|
||||
'method' => $name,
|
||||
'parameter' => $paramName
|
||||
)
|
||||
);
|
||||
throw new Google_Exception("($name) missing required param: '$paramName'");
|
||||
}
|
||||
if (isset($parameters[$paramName])) {
|
||||
$value = $parameters[$paramName];
|
||||
$parameters[$paramName] = $paramSpec;
|
||||
$parameters[$paramName]['value'] = $value;
|
||||
unset($parameters[$paramName]['required']);
|
||||
} else {
|
||||
// Ensure we don't pass nulls.
|
||||
unset($parameters[$paramName]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->client->getLogger()->info(
|
||||
'Service Call',
|
||||
array(
|
||||
'service' => $this->serviceName,
|
||||
'resource' => $this->resourceName,
|
||||
'method' => $name,
|
||||
'arguments' => $parameters,
|
||||
)
|
||||
);
|
||||
|
||||
$url = Google_Http_REST::createRequestUri(
|
||||
$this->servicePath,
|
||||
$method['path'],
|
||||
$parameters
|
||||
);
|
||||
$httpRequest = new Google_Http_Request(
|
||||
$url,
|
||||
$method['httpMethod'],
|
||||
null,
|
||||
$postBody
|
||||
);
|
||||
|
||||
if ($this->rootUrl) {
|
||||
$httpRequest->setBaseComponent($this->rootUrl);
|
||||
} else {
|
||||
$httpRequest->setBaseComponent($this->client->getBasePath());
|
||||
}
|
||||
|
||||
if ($postBody) {
|
||||
$contentTypeHeader = [];
|
||||
$contentTypeHeader['content-type'] = 'application/json; charset=UTF-8';
|
||||
$httpRequest->setRequestHeaders($contentTypeHeader);
|
||||
$httpRequest->setPostBody($postBody);
|
||||
}
|
||||
|
||||
$httpRequest = $this->client->getAuth()->sign($httpRequest);
|
||||
$httpRequest->setExpectedClass($expected_class);
|
||||
|
||||
if (isset($parameters['data']) &&
|
||||
($parameters['uploadType']['value'] == 'media' || $parameters['uploadType']['value'] == 'multipart')) {
|
||||
// If we are doing a simple media upload, trigger that as a convenience.
|
||||
$mfu = new Google_Http_MediaFileUpload(
|
||||
$this->client,
|
||||
$httpRequest,
|
||||
isset($parameters['mimeType']) ? $parameters['mimeType']['value'] : 'application/octet-stream',
|
||||
$parameters['data']['value']
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($parameters['alt']) && $parameters['alt']['value'] == 'media') {
|
||||
$httpRequest->enableExpectedRaw();
|
||||
}
|
||||
|
||||
if ($this->client->shouldDefer()) {
|
||||
// If we are in batch or upload mode, return the raw request.
|
||||
return $httpRequest;
|
||||
}
|
||||
|
||||
return $this->client->execute($httpRequest);
|
||||
}
|
||||
|
||||
protected function convertToArrayAndStripNulls($o)
|
||||
{
|
||||
$o = (array) $o;
|
||||
foreach ($o as $k => $v) {
|
||||
if ($v === null) {
|
||||
unset($o[$k]);
|
||||
} elseif (is_object($v) || is_array($v)) {
|
||||
$o[$k] = $this->convertToArrayAndStripNulls($o[$k]);
|
||||
}
|
||||
}
|
||||
return $o;
|
||||
}
|
||||
}
|
29
app/api/Google/Signer/Abstract.php
Executable file
29
app/api/Google/Signer/Abstract.php
Executable file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Signs data.
|
||||
*
|
||||
* @author Brian Eaton <beaton@google.com>
|
||||
*/
|
||||
abstract class Google_Signer_Abstract
|
||||
{
|
||||
/**
|
||||
* Signs data, returns the signature as binary data.
|
||||
*/
|
||||
abstract public function sign($data);
|
||||
}
|
94
app/api/Google/Signer/P12.php
Executable file
94
app/api/Google/Signer/P12.php
Executable file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs data.
|
||||
*
|
||||
* Only used for testing.
|
||||
*
|
||||
* @author Brian Eaton <beaton@google.com>
|
||||
*/
|
||||
class Google_Signer_P12 extends Google_Signer_Abstract
|
||||
{
|
||||
// OpenSSL private key resource
|
||||
private $privateKey;
|
||||
|
||||
// Creates a new signer from a .p12 file.
|
||||
public function __construct($p12, $password)
|
||||
{
|
||||
if (!function_exists('openssl_x509_read')) {
|
||||
throw new Google_Exception(
|
||||
'The Google PHP API library needs the openssl PHP extension'
|
||||
);
|
||||
}
|
||||
|
||||
// If the private key is provided directly, then this isn't in the p12
|
||||
// format. Different versions of openssl support different p12 formats
|
||||
// and the key from google wasn't being accepted by the version available
|
||||
// at the time.
|
||||
if (!$password && strpos($p12, "-----BEGIN RSA PRIVATE KEY-----") !== false) {
|
||||
$this->privateKey = openssl_pkey_get_private($p12);
|
||||
} elseif ($password === 'notasecret' && strpos($p12, "-----BEGIN PRIVATE KEY-----") !== false) {
|
||||
$this->privateKey = openssl_pkey_get_private($p12);
|
||||
} else {
|
||||
// This throws on error
|
||||
$certs = [];
|
||||
if (!openssl_pkcs12_read($p12, $certs, $password)) {
|
||||
throw new Google_Auth_Exception(
|
||||
"Unable to parse the p12 file. " .
|
||||
"Is this a .p12 file? Is the password correct? OpenSSL error: " .
|
||||
openssl_error_string()
|
||||
);
|
||||
}
|
||||
// TODO(beaton): is this part of the contract for the openssl_pkcs12_read
|
||||
// method? What happens if there are multiple private keys? Do we care?
|
||||
if (!array_key_exists("pkey", $certs) || !$certs["pkey"]) {
|
||||
throw new Google_Auth_Exception("No private key found in p12 file.");
|
||||
}
|
||||
$this->privateKey = openssl_pkey_get_private($certs['pkey']);
|
||||
}
|
||||
|
||||
if (!$this->privateKey) {
|
||||
throw new Google_Auth_Exception("Unable to load private key");
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->privateKey) {
|
||||
openssl_pkey_free($this->privateKey);
|
||||
}
|
||||
}
|
||||
|
||||
public function sign($data)
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.3.0') < 0) {
|
||||
throw new Google_Auth_Exception(
|
||||
"PHP 5.3.0 or higher is required to use service accounts."
|
||||
);
|
||||
}
|
||||
$hash = defined("OPENSSL_ALGO_SHA256") ? OPENSSL_ALGO_SHA256 : "sha256";
|
||||
if (!openssl_sign($data, $signature, $this->privateKey, $hash)) {
|
||||
throw new Google_Auth_Exception("Unable to sign data");
|
||||
}
|
||||
return $signature;
|
||||
}
|
||||
}
|
24
app/api/Google/Task/Exception.php
Executable file
24
app/api/Google/Task/Exception.php
Executable file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
class Google_Task_Exception extends Google_Exception
|
||||
{
|
||||
}
|
36
app/api/Google/Task/Retryable.php
Executable file
36
app/api/Google/Task/Retryable.php
Executable file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for checking how many times a given task can be retried following
|
||||
* a failure.
|
||||
*/
|
||||
interface Google_Task_Retryable
|
||||
{
|
||||
/**
|
||||
* Gets the number of times the associated task can be retried.
|
||||
*
|
||||
* NOTE: -1 is returned if the task can be retried indefinitely
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function allowedRetries();
|
||||
}
|
257
app/api/Google/Task/Runner.php
Executable file
257
app/api/Google/Task/Runner.php
Executable file
|
@ -0,0 +1,257 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* A task runner with exponential backoff support.
|
||||
*
|
||||
* @see https://developers.google.com/drive/web/handle-errors#implementing_exponential_backoff
|
||||
*/
|
||||
class Google_Task_Runner
|
||||
{
|
||||
/**
|
||||
* @var integer $maxDelay The max time (in seconds) to wait before a retry.
|
||||
*/
|
||||
private $maxDelay = 60;
|
||||
/**
|
||||
* @var integer $delay The previous delay from which the next is calculated.
|
||||
*/
|
||||
private $delay = 1;
|
||||
|
||||
/**
|
||||
* @var integer $factor The base number for the exponential back off.
|
||||
*/
|
||||
private $factor = 2;
|
||||
/**
|
||||
* @var float $jitter A random number between -$jitter and $jitter will be
|
||||
* added to $factor on each iteration to allow for a better distribution of
|
||||
* retries.
|
||||
*/
|
||||
private $jitter = 0.5;
|
||||
|
||||
/**
|
||||
* @var integer $attempts The number of attempts that have been tried so far.
|
||||
*/
|
||||
private $attempts = 0;
|
||||
/**
|
||||
* @var integer $maxAttempts The max number of attempts allowed.
|
||||
*/
|
||||
private $maxAttempts = 1;
|
||||
|
||||
/**
|
||||
* @var Google_Client $client The current API client.
|
||||
*/
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @var string $name The name of the current task (used for logging).
|
||||
*/
|
||||
private $name;
|
||||
/**
|
||||
* @var callable $action The task to run and possibly retry.
|
||||
*/
|
||||
private $action;
|
||||
/**
|
||||
* @var array $arguments The task arguments.
|
||||
*/
|
||||
private $arguments;
|
||||
|
||||
/**
|
||||
* Creates a new task runner with exponential backoff support.
|
||||
*
|
||||
* @param Google_Client $client The current API client
|
||||
* @param string $name The name of the current task (used for logging)
|
||||
* @param callable $action The task to run and possibly retry
|
||||
* @param array $arguments The task arguments
|
||||
* @throws Google_Task_Exception when misconfigured
|
||||
*/
|
||||
public function __construct(
|
||||
Google_Client $client,
|
||||
$name,
|
||||
$action,
|
||||
array $arguments = array()
|
||||
) {
|
||||
$config = (array) $client->getClassConfig('Google_Task_Runner');
|
||||
|
||||
if (isset($config['initial_delay'])) {
|
||||
if ($config['initial_delay'] < 0) {
|
||||
throw new Google_Task_Exception(
|
||||
'Task configuration `initial_delay` must not be negative.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->delay = $config['initial_delay'];
|
||||
}
|
||||
|
||||
if (isset($config['max_delay'])) {
|
||||
if ($config['max_delay'] <= 0) {
|
||||
throw new Google_Task_Exception(
|
||||
'Task configuration `max_delay` must be greater than 0.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->maxDelay = $config['max_delay'];
|
||||
}
|
||||
|
||||
if (isset($config['factor'])) {
|
||||
if ($config['factor'] <= 0) {
|
||||
throw new Google_Task_Exception(
|
||||
'Task configuration `factor` must be greater than 0.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->factor = $config['factor'];
|
||||
}
|
||||
|
||||
if (isset($config['jitter'])) {
|
||||
if ($config['jitter'] <= 0) {
|
||||
throw new Google_Task_Exception(
|
||||
'Task configuration `jitter` must be greater than 0.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->jitter = $config['jitter'];
|
||||
}
|
||||
|
||||
if (isset($config['retries'])) {
|
||||
if ($config['retries'] < 0) {
|
||||
throw new Google_Task_Exception(
|
||||
'Task configuration `retries` must not be negative.'
|
||||
);
|
||||
}
|
||||
$this->maxAttempts += $config['retries'];
|
||||
}
|
||||
|
||||
if (!is_callable($action)) {
|
||||
throw new Google_Task_Exception(
|
||||
'Task argument `$action` must be a valid callable.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->client = $client;
|
||||
$this->action = $action;
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a retry can be attempted.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canAttmpt()
|
||||
{
|
||||
return $this->attempts < $this->maxAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the task and (if applicable) automatically retries when errors occur.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Google_Task_Retryable on failure when no retries are available.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
while ($this->attempt()) {
|
||||
try {
|
||||
return call_user_func_array($this->action, $this->arguments);
|
||||
} catch (Google_Task_Retryable $exception) {
|
||||
$allowedRetries = $exception->allowedRetries();
|
||||
|
||||
if (!$this->canAttmpt() || !$allowedRetries) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if ($allowedRetries > 0) {
|
||||
$this->maxAttempts = min(
|
||||
$this->maxAttempts,
|
||||
$this->attempts + $allowedRetries
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a task once, if possible. This is useful for bypassing the `run()`
|
||||
* loop.
|
||||
*
|
||||
* NOTE: If this is not the first attempt, this function will sleep in
|
||||
* accordance to the backoff configurations before running the task.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function attempt()
|
||||
{
|
||||
if (!$this->canAttmpt()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->attempts > 0) {
|
||||
$this->backOff();
|
||||
}
|
||||
|
||||
$this->attempts++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleeps in accordance to the backoff configurations.
|
||||
*/
|
||||
private function backOff()
|
||||
{
|
||||
$delay = $this->getDelay();
|
||||
|
||||
$this->client->getLogger()->debug(
|
||||
'Retrying task with backoff',
|
||||
array(
|
||||
'request' => $this->name,
|
||||
'retry' => $this->attempts,
|
||||
'backoff_seconds' => $delay
|
||||
)
|
||||
);
|
||||
|
||||
usleep($delay * 1000000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the delay (in seconds) for the current backoff period.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
private function getDelay()
|
||||
{
|
||||
$jitter = $this->getJitter();
|
||||
$factor = $this->attempts > 1 ? $this->factor + $jitter : 1 + abs($jitter);
|
||||
|
||||
return $this->delay = min($this->maxDelay, $this->delay * $factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current jitter (random number between -$this->jitter and
|
||||
* $this->jitter).
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
private function getJitter()
|
||||
{
|
||||
return $this->jitter * 2 * mt_rand() / mt_getrandmax() - $this->jitter;
|
||||
}
|
||||
}
|
133
app/api/Google/Utils.php
Executable file
133
app/api/Google/Utils.php
Executable file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Collection of static utility methods used for convenience across
|
||||
* the client library.
|
||||
*/
|
||||
class Google_Utils
|
||||
{
|
||||
public static function urlSafeB64Encode($data)
|
||||
{
|
||||
$b64 = base64_encode($data);
|
||||
$b64 = str_replace(
|
||||
array('+', '/', '\r', '\n', '='),
|
||||
array('-', '_'),
|
||||
$b64
|
||||
);
|
||||
return $b64;
|
||||
}
|
||||
|
||||
public static function urlSafeB64Decode($b64)
|
||||
{
|
||||
$b64 = str_replace(
|
||||
array('-', '_'),
|
||||
array('+', '/'),
|
||||
$b64
|
||||
);
|
||||
return base64_decode($b64);
|
||||
}
|
||||
|
||||
/**
|
||||
* Misc function used to count the number of bytes in a post body, in the
|
||||
* world of multi-byte chars and the unpredictability of
|
||||
* strlen/mb_strlen/sizeof, this is the only way to do that in a sane
|
||||
* manner at the moment.
|
||||
*
|
||||
* This algorithm was originally developed for the
|
||||
* Solar Framework by Paul M. Jones
|
||||
*
|
||||
* @link http://solarphp.com/
|
||||
* @link http://svn.solarphp.com/core/trunk/Solar/Json.php
|
||||
* @link http://framework.zend.com/svn/framework/standard/trunk/library/Zend/Json/Decoder.php
|
||||
* @param string $str
|
||||
* @return int The number of bytes in a string.
|
||||
*/
|
||||
public static function getStrLen($str)
|
||||
{
|
||||
$strlenVar = strlen($str);
|
||||
$d = $ret = 0;
|
||||
for ($count = 0; $count < $strlenVar; ++ $count) {
|
||||
$ordinalValue = ord($str[$ret]);
|
||||
switch (true) {
|
||||
case (($ordinalValue >= 0x20) && ($ordinalValue <= 0x7F)):
|
||||
// characters U-00000000 - U-0000007F (same as ASCII)
|
||||
$ret ++;
|
||||
break;
|
||||
case (($ordinalValue & 0xE0) == 0xC0):
|
||||
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$ret += 2;
|
||||
break;
|
||||
case (($ordinalValue & 0xF0) == 0xE0):
|
||||
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$ret += 3;
|
||||
break;
|
||||
case (($ordinalValue & 0xF8) == 0xF0):
|
||||
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$ret += 4;
|
||||
break;
|
||||
case (($ordinalValue & 0xFC) == 0xF8):
|
||||
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$ret += 5;
|
||||
break;
|
||||
case (($ordinalValue & 0xFE) == 0xFC):
|
||||
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$ret += 6;
|
||||
break;
|
||||
default:
|
||||
$ret ++;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize all keys in an array to lower-case.
|
||||
* @param array $arr
|
||||
* @return array Normalized array.
|
||||
*/
|
||||
public static function normalize($arr)
|
||||
{
|
||||
if (!is_array($arr)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$normalized = [];
|
||||
foreach ($arr as $key => $val) {
|
||||
$normalized[strtolower($key)] = $val;
|
||||
}
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to camelCase
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function camelCase($value)
|
||||
{
|
||||
$value = ucwords(str_replace(array('-', '_'), ' ', $value));
|
||||
$value = str_replace(' ', '', $value);
|
||||
$value[0] = strtolower($value[0]);
|
||||
return $value;
|
||||
}
|
||||
}
|
333
app/api/Google/Utils/URITemplate.php
Executable file
333
app/api/Google/Utils/URITemplate.php
Executable file
|
@ -0,0 +1,333 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of levels 1-3 of the URI Template spec.
|
||||
* @see http://tools.ietf.org/html/rfc6570
|
||||
*/
|
||||
class Google_Utils_URITemplate
|
||||
{
|
||||
const TYPE_MAP = "1";
|
||||
const TYPE_LIST = "2";
|
||||
const TYPE_SCALAR = "4";
|
||||
|
||||
/**
|
||||
* @var $operators array
|
||||
* These are valid at the start of a template block to
|
||||
* modify the way in which the variables inside are
|
||||
* processed.
|
||||
*/
|
||||
private $operators = array(
|
||||
"+" => "reserved",
|
||||
"/" => "segments",
|
||||
"." => "dotprefix",
|
||||
"#" => "fragment",
|
||||
";" => "semicolon",
|
||||
"?" => "form",
|
||||
"&" => "continuation"
|
||||
);
|
||||
|
||||
/**
|
||||
* @var reserved array
|
||||
* These are the characters which should not be URL encoded in reserved
|
||||
* strings.
|
||||
*/
|
||||
private $reserved = array(
|
||||
"=", ",", "!", "@", "|", ":", "/", "?", "#",
|
||||
"[", "]",'$', "&", "'", "(", ")", "*", "+", ";"
|
||||
);
|
||||
private $reservedEncoded = array(
|
||||
"%3D", "%2C", "%21", "%40", "%7C", "%3A", "%2F", "%3F",
|
||||
"%23", "%5B", "%5D", "%24", "%26", "%27", "%28", "%29",
|
||||
"%2A", "%2B", "%3B"
|
||||
);
|
||||
|
||||
public function parse($string, array $parameters)
|
||||
{
|
||||
return $this->resolveNextSection($string, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function finds the first matching {...} block and
|
||||
* executes the replacement. It then calls itself to find
|
||||
* subsequent blocks, if any.
|
||||
*/
|
||||
private function resolveNextSection($string, $parameters)
|
||||
{
|
||||
$start = strpos($string, "{");
|
||||
if ($start === false) {
|
||||
return $string;
|
||||
}
|
||||
$end = strpos($string, "}");
|
||||
if ($end === false) {
|
||||
return $string;
|
||||
}
|
||||
$string = $this->replace($string, $start, $end, $parameters);
|
||||
return $this->resolveNextSection($string, $parameters);
|
||||
}
|
||||
|
||||
private function replace($string, $start, $end, $parameters)
|
||||
{
|
||||
// We know a data block will have {} round it, so we can strip that.
|
||||
$data = substr($string, $start + 1, $end - $start - 1);
|
||||
|
||||
// If the first character is one of the reserved operators, it effects
|
||||
// the processing of the stream.
|
||||
if (isset($this->operators[$data[0]])) {
|
||||
$op = $this->operators[$data[0]];
|
||||
$data = substr($data, 1);
|
||||
$prefix = "";
|
||||
$prefix_on_missing = false;
|
||||
|
||||
switch ($op) {
|
||||
case "reserved":
|
||||
// Reserved means certain characters should not be URL encoded
|
||||
$data = $this->replaceVars($data, $parameters, ",", null, true);
|
||||
break;
|
||||
case "fragment":
|
||||
// Comma separated with fragment prefix. Bare values only.
|
||||
$prefix = "#";
|
||||
$prefix_on_missing = true;
|
||||
$data = $this->replaceVars($data, $parameters, ",", null, true);
|
||||
break;
|
||||
case "segments":
|
||||
// Slash separated data. Bare values only.
|
||||
$prefix = "/";
|
||||
$data =$this->replaceVars($data, $parameters, "/");
|
||||
break;
|
||||
case "dotprefix":
|
||||
// Dot separated data. Bare values only.
|
||||
$prefix = ".";
|
||||
$prefix_on_missing = true;
|
||||
$data = $this->replaceVars($data, $parameters, ".");
|
||||
break;
|
||||
case "semicolon":
|
||||
// Semicolon prefixed and separated. Uses the key name
|
||||
$prefix = ";";
|
||||
$data = $this->replaceVars($data, $parameters, ";", "=", false, true, false);
|
||||
break;
|
||||
case "form":
|
||||
// Standard URL format. Uses the key name
|
||||
$prefix = "?";
|
||||
$data = $this->replaceVars($data, $parameters, "&", "=");
|
||||
break;
|
||||
case "continuation":
|
||||
// Standard URL, but with leading ampersand. Uses key name.
|
||||
$prefix = "&";
|
||||
$data = $this->replaceVars($data, $parameters, "&", "=");
|
||||
break;
|
||||
}
|
||||
|
||||
// Add the initial prefix character if data is valid.
|
||||
if ($data || ($data !== false && $prefix_on_missing)) {
|
||||
$data = $prefix . $data;
|
||||
}
|
||||
|
||||
} else {
|
||||
// If no operator we replace with the defaults.
|
||||
$data = $this->replaceVars($data, $parameters);
|
||||
}
|
||||
// This is chops out the {...} and replaces with the new section.
|
||||
return substr($string, 0, $start) . $data . substr($string, $end + 1);
|
||||
}
|
||||
|
||||
private function replaceVars(
|
||||
$section,
|
||||
$parameters,
|
||||
$sep = ",",
|
||||
$combine = null,
|
||||
$reserved = false,
|
||||
$tag_empty = false,
|
||||
$combine_on_empty = true
|
||||
) {
|
||||
if (strpos($section, ",") === false) {
|
||||
// If we only have a single value, we can immediately process.
|
||||
return $this->combine(
|
||||
$section,
|
||||
$parameters,
|
||||
$sep,
|
||||
$combine,
|
||||
$reserved,
|
||||
$tag_empty,
|
||||
$combine_on_empty
|
||||
);
|
||||
} else {
|
||||
// If we have multiple values, we need to split and loop over them.
|
||||
// Each is treated individually, then glued together with the
|
||||
// separator character.
|
||||
$vars = explode(",", $section);
|
||||
return $this->combineList(
|
||||
$vars,
|
||||
$sep,
|
||||
$parameters,
|
||||
$combine,
|
||||
$reserved,
|
||||
false, // Never emit empty strings in multi-param replacements
|
||||
$combine_on_empty
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function combine(
|
||||
$key,
|
||||
$parameters,
|
||||
$sep,
|
||||
$combine,
|
||||
$reserved,
|
||||
$tag_empty,
|
||||
$combine_on_empty
|
||||
) {
|
||||
$length = false;
|
||||
$explode = false;
|
||||
$skip_final_combine = false;
|
||||
$value = false;
|
||||
|
||||
// Check for length restriction.
|
||||
if (strpos($key, ":") !== false) {
|
||||
list($key, $length) = explode(":", $key);
|
||||
}
|
||||
|
||||
// Check for explode parameter.
|
||||
if ($key[strlen($key) - 1] == "*") {
|
||||
$explode = true;
|
||||
$key = substr($key, 0, -1);
|
||||
$skip_final_combine = true;
|
||||
}
|
||||
|
||||
// Define the list separator.
|
||||
$list_sep = $explode ? $sep : ",";
|
||||
|
||||
if (isset($parameters[$key])) {
|
||||
$data_type = $this->getDataType($parameters[$key]);
|
||||
switch ($data_type) {
|
||||
case self::TYPE_SCALAR:
|
||||
$value = $this->getValue($parameters[$key], $length);
|
||||
break;
|
||||
case self::TYPE_LIST:
|
||||
$values = [];
|
||||
foreach ($parameters[$key] as $pkey => $pvalue) {
|
||||
$pvalue = $this->getValue($pvalue, $length);
|
||||
if ($combine && $explode) {
|
||||
$values[$pkey] = $key . $combine . $pvalue;
|
||||
} else {
|
||||
$values[$pkey] = $pvalue;
|
||||
}
|
||||
}
|
||||
$value = implode($list_sep, $values);
|
||||
if ($value == '') {
|
||||
return '';
|
||||
}
|
||||
break;
|
||||
case self::TYPE_MAP:
|
||||
$values = [];
|
||||
foreach ($parameters[$key] as $pkey => $pvalue) {
|
||||
$pvalue = $this->getValue($pvalue, $length);
|
||||
if ($explode) {
|
||||
$pkey = $this->getValue($pkey, $length);
|
||||
$values[] = $pkey . "=" . $pvalue; // Explode triggers = combine.
|
||||
} else {
|
||||
$values[] = $pkey;
|
||||
$values[] = $pvalue;
|
||||
}
|
||||
}
|
||||
$value = implode($list_sep, $values);
|
||||
if ($value == '') {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if ($tag_empty) {
|
||||
// If we are just indicating empty values with their key name, return that.
|
||||
return $key;
|
||||
} else {
|
||||
// Otherwise we can skip this variable due to not being defined.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($reserved) {
|
||||
$value = str_replace($this->reservedEncoded, $this->reserved, $value);
|
||||
}
|
||||
|
||||
// If we do not need to include the key name, we just return the raw
|
||||
// value.
|
||||
if (!$combine || $skip_final_combine) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Else we combine the key name: foo=bar, if value is not the empty string.
|
||||
return $key . ($value != '' || $combine_on_empty ? $combine . $value : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of a passed in value
|
||||
*/
|
||||
private function getDataType($data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
reset($data);
|
||||
if (key($data) !== 0) {
|
||||
return self::TYPE_MAP;
|
||||
}
|
||||
return self::TYPE_LIST;
|
||||
}
|
||||
return self::TYPE_SCALAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that merges multiple combine calls
|
||||
* for multi-key templates.
|
||||
*/
|
||||
private function combineList(
|
||||
$vars,
|
||||
$sep,
|
||||
$parameters,
|
||||
$combine,
|
||||
$reserved,
|
||||
$tag_empty,
|
||||
$combine_on_empty
|
||||
) {
|
||||
$ret = [];
|
||||
foreach ($vars as $var) {
|
||||
$response = $this->combine(
|
||||
$var,
|
||||
$parameters,
|
||||
$sep,
|
||||
$combine,
|
||||
$reserved,
|
||||
$tag_empty,
|
||||
$combine_on_empty
|
||||
);
|
||||
if ($response === false) {
|
||||
continue;
|
||||
}
|
||||
$ret[] = $response;
|
||||
}
|
||||
return implode($sep, $ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to encode and trim values
|
||||
*/
|
||||
private function getValue($value, $length)
|
||||
{
|
||||
if ($length) {
|
||||
$value = substr($value, 0, $length);
|
||||
}
|
||||
$value = rawurlencode($value);
|
||||
return $value;
|
||||
}
|
||||
}
|
30
app/api/Google/Verifier/Abstract.php
Executable file
30
app/api/Google/Verifier/Abstract.php
Executable file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Verifies signatures.
|
||||
*
|
||||
* @author Brian Eaton <beaton@google.com>
|
||||
*/
|
||||
abstract class Google_Verifier_Abstract
|
||||
{
|
||||
/**
|
||||
* Checks a signature, returns true if the signature is correct,
|
||||
* false otherwise.
|
||||
*/
|
||||
abstract public function verify($data, $signature);
|
||||
}
|
75
app/api/Google/Verifier/Pem.php
Executable file
75
app/api/Google/Verifier/Pem.php
Executable file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (!class_exists('Google_Client')) {
|
||||
require_once dirname(__FILE__) . '/../autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies signatures using PEM encoded certificates.
|
||||
*
|
||||
* @author Brian Eaton <beaton@google.com>
|
||||
*/
|
||||
class Google_Verifier_Pem extends Google_Verifier_Abstract
|
||||
{
|
||||
private $publicKey;
|
||||
|
||||
/**
|
||||
* Constructs a verifier from the supplied PEM-encoded certificate.
|
||||
*
|
||||
* $pem: a PEM encoded certificate (not a file).
|
||||
* @param $pem
|
||||
* @throws Google_Auth_Exception
|
||||
* @throws Google_Exception
|
||||
*/
|
||||
public function __construct($pem)
|
||||
{
|
||||
if (!function_exists('openssl_x509_read')) {
|
||||
throw new Google_Exception('Google API PHP client needs the openssl PHP extension');
|
||||
}
|
||||
$this->publicKey = openssl_x509_read($pem);
|
||||
if (!$this->publicKey) {
|
||||
throw new Google_Auth_Exception("Unable to parse PEM: $pem");
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->publicKey) {
|
||||
openssl_x509_free($this->publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the signature on data.
|
||||
*
|
||||
* Returns true if the signature is valid, false otherwise.
|
||||
* @param $data
|
||||
* @param $signature
|
||||
* @throws Google_Auth_Exception
|
||||
* @return bool
|
||||
*/
|
||||
public function verify($data, $signature)
|
||||
{
|
||||
$hash = defined("OPENSSL_ALGO_SHA256") ? OPENSSL_ALGO_SHA256 : "sha256";
|
||||
$status = openssl_verify($data, $signature, $this->publicKey, $hash);
|
||||
if ($status === -1) {
|
||||
throw new Google_Auth_Exception('Signature verification error: ' . openssl_error_string());
|
||||
}
|
||||
return $status === 1;
|
||||
}
|
||||
}
|
37
app/api/Google/autoload.php
Executable file
37
app/api/Google/autoload.php
Executable file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function mec_google_api_php_client_autoload($className)
|
||||
{
|
||||
$classPath = explode('_', $className);
|
||||
|
||||
if($classPath[0] != 'Google')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Drop 'Google', and maximum class file path depth in this project is 3.
|
||||
$classPath = array_slice($classPath, 1, 2);
|
||||
$filePath = dirname(__FILE__) . DS . implode(DS, $classPath) . '.php';
|
||||
|
||||
if(file_exists($filePath))
|
||||
{
|
||||
require_once($filePath);
|
||||
}
|
||||
}
|
||||
|
||||
spl_autoload_register('mec_google_api_php_client_autoload');
|
0
app/api/Google/index.html
Executable file
0
app/api/Google/index.html
Executable file
132
app/api/Meetup/meetup.php
Executable file
132
app/api/Meetup/meetup.php
Executable file
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
class Meetup {
|
||||
const BASE = 'https://api.meetup.com';
|
||||
|
||||
protected $_parameters = array(
|
||||
'sign' => 'true',
|
||||
);
|
||||
|
||||
public function __construct(array $parameters = array()) {
|
||||
$this->_parameters = array_merge($this->_parameters, $parameters);
|
||||
}
|
||||
|
||||
public function getEvents(array $parameters = array()) {
|
||||
return $this->get('/:urlname/events', $parameters);
|
||||
}
|
||||
|
||||
public function getEvent(array $parameters = array()) {
|
||||
return $this->get('/:urlname/events/:id', $parameters);
|
||||
}
|
||||
|
||||
public function getPhotos(array $parameters = array()) {
|
||||
return $this->get('/2/photos', $parameters)->results;
|
||||
}
|
||||
|
||||
public function getDiscussionBoards(array $parameters = array()) {
|
||||
return $this->get('/:urlname/boards', $parameters);
|
||||
}
|
||||
|
||||
public function getDiscussions(array $parameters = array()) {
|
||||
return $this->get('/:urlname/boards/:bid/discussions', $parameters);
|
||||
}
|
||||
|
||||
public function getMembers(array $parameters = array()) {
|
||||
return $this->get('/2/members', $parameters);
|
||||
}
|
||||
|
||||
public function getNext($response) {
|
||||
if (!isset($response) || !isset($response->meta->next))
|
||||
{
|
||||
throw new Exception("Invalid response object.");
|
||||
}
|
||||
return $this->get_url($response->meta->next);
|
||||
}
|
||||
|
||||
public function get($path, array $parameters = array()) {
|
||||
$parameters = array_merge($this->_parameters, $parameters);
|
||||
|
||||
if (preg_match_all('/:([a-z]+)/', $path, $matches)) {
|
||||
|
||||
foreach ($matches[0] as $i => $match) {
|
||||
|
||||
if (isset($parameters[$matches[1][$i]])) {
|
||||
$path = str_replace($match, $parameters[$matches[1][$i]], $path);
|
||||
unset($parameters[$matches[1][$i]]);
|
||||
} else {
|
||||
throw new Exception("Missing parameter '" . $matches[1][$i] . "' for path '" . $path . "'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$url = self::BASE . $path . '?' . http_build_query($parameters);
|
||||
|
||||
return $this->get_url($url);
|
||||
}
|
||||
|
||||
protected function get_url($url) {
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept-Charset: utf-8"));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
||||
$content = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
throw new Exception("Failed retrieving '" . $url . "' because of ' " . $error . "'.");
|
||||
}
|
||||
|
||||
$response = json_decode($content);
|
||||
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($status != 200) {
|
||||
|
||||
if (isset($response->errors[0]->message)) {
|
||||
$error = $response->errors[0]->message;
|
||||
} else {
|
||||
$error = 'Status ' . $status;
|
||||
}
|
||||
|
||||
throw new Exception("Failed retrieving '" . $url . "' because of ' " . $error . "'.");
|
||||
}
|
||||
|
||||
if (isset($response) == false) {
|
||||
|
||||
switch (json_last_error()) {
|
||||
case JSON_ERROR_NONE:
|
||||
$error = 'No errors';
|
||||
break;
|
||||
case JSON_ERROR_DEPTH:
|
||||
$error = 'Maximum stack depth exceeded';
|
||||
break;
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
$error = ' Underflow or the modes mismatch';
|
||||
break;
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
$error = 'Unexpected control character found';
|
||||
break;
|
||||
case JSON_ERROR_SYNTAX:
|
||||
$error = 'Syntax error, malformed JSON';
|
||||
break;
|
||||
case JSON_ERROR_UTF8:
|
||||
$error = 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||
break;
|
||||
default:
|
||||
$error = 'Unknown error';
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Exception("Cannot read response by '" . $url . "' because of: '" . $error . "'.");
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
431
app/api/Stripe/Account.php
Executable file
431
app/api/Stripe/Account.php
Executable file
|
@ -0,0 +1,431 @@
|
|||
<?php
|
||||
|
||||
// File generated from our OpenAPI spec
|
||||
|
||||
namespace Stripe;
|
||||
|
||||
/**
|
||||
* This is an object representing a Stripe account. You can retrieve it to see
|
||||
* properties on the account like its current e-mail address or if the account is
|
||||
* enabled yet to make live charges.
|
||||
*
|
||||
* Some properties, marked below, are available only to platforms that want to <a
|
||||
* href="https://stripe.com/docs/connect/accounts">create and manage Express or
|
||||
* Custom accounts</a>.
|
||||
*
|
||||
* @property string $id Unique identifier for the object.
|
||||
* @property string $object String representing the object's type. Objects of the same type share the same value.
|
||||
* @property null|\Stripe\StripeObject $business_profile Business information about the account.
|
||||
* @property null|string $business_type The business type.
|
||||
* @property \Stripe\StripeObject $capabilities
|
||||
* @property bool $charges_enabled Whether the account can create live charges.
|
||||
* @property \Stripe\StripeObject $company
|
||||
* @property string $country The account's country.
|
||||
* @property int $created Time at which the object was created. Measured in seconds since the Unix epoch.
|
||||
* @property string $default_currency Three-letter ISO currency code representing the default currency for the account. This must be a currency that <a href="https://stripe.com/docs/payouts">Stripe supports in the account's country</a>.
|
||||
* @property bool $details_submitted Whether account details have been submitted. Standard accounts cannot receive payouts before this is true.
|
||||
* @property null|string $email The primary user's email address.
|
||||
* @property \Stripe\Collection $external_accounts External accounts (bank accounts and debit cards) currently attached to this account
|
||||
* @property \Stripe\Person $individual <p>This is an object representing a person associated with a Stripe account.</p><p>A platform cannot access a Standard or Express account's persons after the account starts onboarding, such as after generating an account link for the account. See the <a href="https://stripe.com/docs/connect/standard-accounts">Standard onboarding</a> or <a href="https://stripe.com/docs/connect/express-accounts">Express onboarding documentation</a> for information about platform pre-filling and account onboarding steps.</p><p>Related guide: <a href="https://stripe.com/docs/connect/identity-verification-api#person-information">Handling Identity Verification with the API</a>.</p>
|
||||
* @property \Stripe\StripeObject $metadata Set of <a href="https://stripe.com/docs/api/metadata">key-value pairs</a> that you can attach to an object. This can be useful for storing additional information about the object in a structured format.
|
||||
* @property bool $payouts_enabled Whether Stripe can send payouts to this account.
|
||||
* @property \Stripe\StripeObject $requirements
|
||||
* @property null|\Stripe\StripeObject $settings Options for customizing how the account functions within Stripe.
|
||||
* @property \Stripe\StripeObject $tos_acceptance
|
||||
* @property string $type The Stripe account type. Can be <code>standard</code>, <code>express</code>, or <code>custom</code>.
|
||||
*/
|
||||
class Account extends ApiResource
|
||||
{
|
||||
const OBJECT_NAME = 'account';
|
||||
|
||||
use ApiOperations\All;
|
||||
use ApiOperations\Create;
|
||||
use ApiOperations\Delete;
|
||||
use ApiOperations\NestedResource;
|
||||
use ApiOperations\Update;
|
||||
|
||||
const BUSINESS_TYPE_COMPANY = 'company';
|
||||
const BUSINESS_TYPE_GOVERNMENT_ENTITY = 'government_entity';
|
||||
const BUSINESS_TYPE_INDIVIDUAL = 'individual';
|
||||
const BUSINESS_TYPE_NON_PROFIT = 'non_profit';
|
||||
|
||||
const CAPABILITY_CARD_PAYMENTS = 'card_payments';
|
||||
const CAPABILITY_LEGACY_PAYMENTS = 'legacy_payments';
|
||||
const CAPABILITY_PLATFORM_PAYMENTS = 'platform_payments';
|
||||
const CAPABILITY_TRANSFERS = 'transfers';
|
||||
|
||||
const CAPABILITY_STATUS_ACTIVE = 'active';
|
||||
const CAPABILITY_STATUS_INACTIVE = 'inactive';
|
||||
const CAPABILITY_STATUS_PENDING = 'pending';
|
||||
|
||||
const TYPE_CUSTOM = 'custom';
|
||||
const TYPE_EXPRESS = 'express';
|
||||
const TYPE_STANDARD = 'standard';
|
||||
|
||||
use ApiOperations\Retrieve {
|
||||
retrieve as protected _retrieve;
|
||||
}
|
||||
|
||||
public static function getSavedNestedResources()
|
||||
{
|
||||
static $savedNestedResources = null;
|
||||
if (null === $savedNestedResources) {
|
||||
$savedNestedResources = new Util\Set([
|
||||
'external_account',
|
||||
'bank_account',
|
||||
]);
|
||||
}
|
||||
|
||||
return $savedNestedResources;
|
||||
}
|
||||
|
||||
public function instanceUrl()
|
||||
{
|
||||
if (null === $this['id']) {
|
||||
return '/v1/account';
|
||||
}
|
||||
|
||||
return parent::instanceUrl();
|
||||
}
|
||||
|
||||
public function serializeParameters($force = false)
|
||||
{
|
||||
$update = parent::serializeParameters($force);
|
||||
if (isset($this->_values['legal_entity'])) {
|
||||
$entity = $this['legal_entity'];
|
||||
if (isset($entity->_values['additional_owners'])) {
|
||||
$owners = $entity['additional_owners'];
|
||||
$entityUpdate = isset($update['legal_entity']) ? $update['legal_entity'] : [];
|
||||
$entityUpdate['additional_owners'] = $this->serializeAdditionalOwners($entity, $owners);
|
||||
$update['legal_entity'] = $entityUpdate;
|
||||
}
|
||||
}
|
||||
if (isset($this->_values['individual'])) {
|
||||
$individual = $this['individual'];
|
||||
if (($individual instanceof Person) && !isset($update['individual'])) {
|
||||
$update['individual'] = $individual->serializeParameters($force);
|
||||
}
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
private function serializeAdditionalOwners($legalEntity, $additionalOwners)
|
||||
{
|
||||
if (isset($legalEntity->_originalValues['additional_owners'])) {
|
||||
$originalValue = $legalEntity->_originalValues['additional_owners'];
|
||||
} else {
|
||||
$originalValue = [];
|
||||
}
|
||||
if (($originalValue) && (\count($originalValue) > \count($additionalOwners))) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
'You cannot delete an item from an array, you must instead set a new array'
|
||||
);
|
||||
}
|
||||
|
||||
$updateArr = [];
|
||||
foreach ($additionalOwners as $i => $v) {
|
||||
$update = ($v instanceof StripeObject) ? $v->serializeParameters() : $v;
|
||||
|
||||
if ([] !== $update) {
|
||||
if (!$originalValue
|
||||
|| !\array_key_exists($i, $originalValue)
|
||||
|| ($update !== $legalEntity->serializeParamsValue($originalValue[$i], null, false, true))) {
|
||||
$updateArr[$i] = $update;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $updateArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|array|string $id the ID of the account to retrieve, or an
|
||||
* options array containing an `id` key
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Account
|
||||
*/
|
||||
public static function retrieve($id = null, $opts = null)
|
||||
{
|
||||
if (!$opts && \is_string($id) && 'sk_' === \substr($id, 0, 3)) {
|
||||
$opts = $id;
|
||||
$id = null;
|
||||
}
|
||||
|
||||
return self::_retrieve($id, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|array $clientId
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\StripeObject object containing the response from the API
|
||||
*/
|
||||
public function deauthorize($clientId = null, $opts = null)
|
||||
{
|
||||
$params = [
|
||||
'client_id' => $clientId,
|
||||
'stripe_user_id' => $this->id,
|
||||
];
|
||||
|
||||
return OAuth::deauthorize($params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Collection the list of persons
|
||||
*/
|
||||
public function persons($params = null, $opts = null)
|
||||
{
|
||||
$url = $this->instanceUrl() . '/persons';
|
||||
list($response, $opts) = $this->_request('get', $url, $params, $opts);
|
||||
$obj = Util\Util::convertToStripeObject($response, $opts);
|
||||
$obj->setLastResponse($response);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return Account the rejected account
|
||||
*/
|
||||
public function reject($params = null, $opts = null)
|
||||
{
|
||||
$url = $this->instanceUrl() . '/reject';
|
||||
list($response, $opts) = $this->_request('post', $url, $params, $opts);
|
||||
$this->refreshFrom($response, $opts);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Capabilities methods
|
||||
* We can not add the capabilities() method today as the Account object already has a
|
||||
* capabilities property which is a hash and not the sub-list of capabilities.
|
||||
*/
|
||||
|
||||
const PATH_CAPABILITIES = '/capabilities';
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account on which to retrieve the capabilities
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Collection the list of capabilities
|
||||
*/
|
||||
public static function allCapabilities($id, $params = null, $opts = null)
|
||||
{
|
||||
return self::_allNestedResources($id, static::PATH_CAPABILITIES, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account to which the capability belongs
|
||||
* @param string $capabilityId the ID of the capability to retrieve
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Capability
|
||||
*/
|
||||
public static function retrieveCapability($id, $capabilityId, $params = null, $opts = null)
|
||||
{
|
||||
return self::_retrieveNestedResource($id, static::PATH_CAPABILITIES, $capabilityId, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account to which the capability belongs
|
||||
* @param string $capabilityId the ID of the capability to update
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Capability
|
||||
*/
|
||||
public static function updateCapability($id, $capabilityId, $params = null, $opts = null)
|
||||
{
|
||||
return self::_updateNestedResource($id, static::PATH_CAPABILITIES, $capabilityId, $params, $opts);
|
||||
}
|
||||
|
||||
const PATH_EXTERNAL_ACCOUNTS = '/external_accounts';
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account on which to retrieve the external accounts
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Collection the list of external accounts (BankAccount or Card)
|
||||
*/
|
||||
public static function allExternalAccounts($id, $params = null, $opts = null)
|
||||
{
|
||||
return self::_allNestedResources($id, static::PATH_EXTERNAL_ACCOUNTS, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account on which to create the external account
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\BankAccount|\Stripe\Card
|
||||
*/
|
||||
public static function createExternalAccount($id, $params = null, $opts = null)
|
||||
{
|
||||
return self::_createNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account to which the external account belongs
|
||||
* @param string $externalAccountId the ID of the external account to delete
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\BankAccount|\Stripe\Card
|
||||
*/
|
||||
public static function deleteExternalAccount($id, $externalAccountId, $params = null, $opts = null)
|
||||
{
|
||||
return self::_deleteNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account to which the external account belongs
|
||||
* @param string $externalAccountId the ID of the external account to retrieve
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\BankAccount|\Stripe\Card
|
||||
*/
|
||||
public static function retrieveExternalAccount($id, $externalAccountId, $params = null, $opts = null)
|
||||
{
|
||||
return self::_retrieveNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account to which the external account belongs
|
||||
* @param string $externalAccountId the ID of the external account to update
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\BankAccount|\Stripe\Card
|
||||
*/
|
||||
public static function updateExternalAccount($id, $externalAccountId, $params = null, $opts = null)
|
||||
{
|
||||
return self::_updateNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
|
||||
}
|
||||
|
||||
const PATH_LOGIN_LINKS = '/login_links';
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account on which to create the login link
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\LoginLink
|
||||
*/
|
||||
public static function createLoginLink($id, $params = null, $opts = null)
|
||||
{
|
||||
return self::_createNestedResource($id, static::PATH_LOGIN_LINKS, $params, $opts);
|
||||
}
|
||||
|
||||
const PATH_PERSONS = '/persons';
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account on which to retrieve the persons
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Collection the list of persons
|
||||
*/
|
||||
public static function allPersons($id, $params = null, $opts = null)
|
||||
{
|
||||
return self::_allNestedResources($id, static::PATH_PERSONS, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account on which to create the person
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Person
|
||||
*/
|
||||
public static function createPerson($id, $params = null, $opts = null)
|
||||
{
|
||||
return self::_createNestedResource($id, static::PATH_PERSONS, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account to which the person belongs
|
||||
* @param string $personId the ID of the person to delete
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Person
|
||||
*/
|
||||
public static function deletePerson($id, $personId, $params = null, $opts = null)
|
||||
{
|
||||
return self::_deleteNestedResource($id, static::PATH_PERSONS, $personId, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account to which the person belongs
|
||||
* @param string $personId the ID of the person to retrieve
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Person
|
||||
*/
|
||||
public static function retrievePerson($id, $personId, $params = null, $opts = null)
|
||||
{
|
||||
return self::_retrieveNestedResource($id, static::PATH_PERSONS, $personId, $params, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id the ID of the account to which the person belongs
|
||||
* @param string $personId the ID of the person to update
|
||||
* @param null|array $params
|
||||
* @param null|array|string $opts
|
||||
*
|
||||
* @throws \Stripe\Exception\ApiErrorException if the request fails
|
||||
*
|
||||
* @return \Stripe\Person
|
||||
*/
|
||||
public static function updatePerson($id, $personId, $params = null, $opts = null)
|
||||
{
|
||||
return self::_updateNestedResource($id, static::PATH_PERSONS, $personId, $params, $opts);
|
||||
}
|
||||
}
|
26
app/api/Stripe/AccountLink.php
Executable file
26
app/api/Stripe/AccountLink.php
Executable file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
// File generated from our OpenAPI spec
|
||||
|
||||
namespace Stripe;
|
||||
|
||||
/**
|
||||
* Account Links are the means by which a Connect platform grants a connected
|
||||
* account permission to access Stripe-hosted applications, such as Connect
|
||||
* Onboarding.
|
||||
*
|
||||
* Related guide: <a
|
||||
* href="https://stripe.com/docs/connect/connect-onboarding">Connect
|
||||
* Onboarding</a>.
|
||||
*
|
||||
* @property string $object String representing the object's type. Objects of the same type share the same value.
|
||||
* @property int $created Time at which the object was created. Measured in seconds since the Unix epoch.
|
||||
* @property int $expires_at The timestamp at which this account link will expire.
|
||||
* @property string $url The URL for the account link.
|
||||
*/
|
||||
class AccountLink extends ApiResource
|
||||
{
|
||||
const OBJECT_NAME = 'account_link';
|
||||
|
||||
use ApiOperations\Create;
|
||||
}
|
75
app/api/Stripe/AlipayAccount.php
Executable file
75
app/api/Stripe/AlipayAccount.php
Executable file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Stripe;
|
||||
|
||||
/**
|
||||
* Class AlipayAccount.
|
||||
*
|
||||
* @deprecated Alipay accounts are deprecated. Please use the sources API instead.
|
||||
* @see https://stripe.com/docs/sources/alipay
|
||||
*/
|
||||
class AlipayAccount extends ApiResource
|
||||
{
|
||||
const OBJECT_NAME = 'alipay_account';
|
||||
|
||||
use ApiOperations\Delete;
|
||||
use ApiOperations\Update;
|
||||
|
||||
/**
|
||||
* @return string The instance URL for this resource. It needs to be special
|
||||
* cased because it doesn't fit into the standard resource pattern.
|
||||
*/
|
||||
public function instanceUrl()
|
||||
{
|
||||
if ($this['customer']) {
|
||||
$base = Customer::classUrl();
|
||||
$parent = $this['customer'];
|
||||
$path = 'sources';
|
||||
} else {
|
||||
$msg = 'Alipay accounts cannot be accessed without a customer ID.';
|
||||
|
||||
throw new Exception\UnexpectedValueException($msg);
|
||||
}
|
||||
$parentExtn = \urlencode(Util\Util::utf8($parent));
|
||||
$extn = \urlencode(Util\Util::utf8($this['id']));
|
||||
|
||||
return "{$base}/{$parentExtn}/{$path}/{$extn}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $_id
|
||||
* @param null|array|string $_opts
|
||||
*
|
||||
* @throws \Stripe\Exception\BadMethodCallException
|
||||
*
|
||||
* @deprecated Alipay accounts are deprecated. Please use the sources API instead.
|
||||
* @see https://stripe.com/docs/sources/alipay
|
||||
*/
|
||||
public static function retrieve($_id, $_opts = null)
|
||||
{
|
||||
$msg = 'Alipay accounts cannot be retrieved without a customer ID. ' .
|
||||
'Retrieve an Alipay account using `Customer::retrieveSource(' .
|
||||
"'customer_id', 'alipay_account_id')`.";
|
||||
|
||||
throw new Exception\BadMethodCallException($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $_id
|
||||
* @param null|array $_params
|
||||
* @param null|array|string $_options
|
||||
*
|
||||
* @throws \Stripe\Exception\BadMethodCallException
|
||||
*
|
||||
* @deprecated Alipay accounts are deprecated. Please use the sources API instead.
|
||||
* @see https://stripe.com/docs/sources/alipay
|
||||
*/
|
||||
public static function update($_id, $_params = null, $_options = null)
|
||||
{
|
||||
$msg = 'Alipay accounts cannot be updated without a customer ID. ' .
|
||||
'Update an Alipay account using `Customer::updateSource(' .
|
||||
"'customer_id', 'alipay_account_id', \$updateParams)`.";
|
||||
|
||||
throw new Exception\BadMethodCallException($msg);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue