diff --git a/XeroOauth2.php b/XeroOauth2.php new file mode 100644 index 0000000..15e2b66 --- /dev/null +++ b/XeroOauth2.php @@ -0,0 +1,712 @@ + ['openid email profile offline_access assets projects accounting.settings accounting.transactions accounting.contacts accounting.journals.read accounting.reports.read accounting.attachments payroll.employees payroll.payruns payroll.payslip payroll.timesheets payroll.settings files'], + 'scope1' => [ + 'openid', + 'email', + 'profile', + 'offline_access', + 'assets', + 'projects', + 'accounting.settings', + 'accounting.transactions', + 'accounting.contacts', + 'accounting.journals.read', + 'accounting.reports.read', + 'accounting.attachments', + 'payroll.employees', + 'payroll.payruns', + 'payroll.payslip', + 'payroll.timesheets', + 'payroll.settings' + ] + ]; + public $storage; + public $config; + public $apiAccountingInstance; //accounting instance + public $apiPayrollInstance; // payroll au instance + public $xeroTenantId; + + + public function __construct($office) + { + $this->office = $office; + add_action('init', array($this, 'init_wp')); + add_action('after_setup_theme', array($this, 'boot_carbon')); + // add_action('init', array('Carbon_Fields\\Carbon_Fields', 'boot')); + add_action( 'plugins_loaded', array( '\\Carbon_Fields\\Carbon_Fields', 'boot' ) ); + add_action('carbon_fields_register_fields', array($this, 'build_settings_page')); + add_action('carbon_fields_container_activated', array($this, 'field_activated')); + add_action('parse_request', array($this, 'xero_callback')); + + add_shortcode('xero_org_name', array($this, 'xero_org_name')); + add_shortcode('xero_org_contacts', array($this, 'xero_org_contacts')); + add_shortcode('xero_org_invoices', array($this, 'xero_org_invoices')); + add_shortcode('xero_org_pay_items', array($this, 'xero_org_pay_items')); + add_shortcode('xero_org_employees', array($this, 'xero_org_employees')); + add_shortcode('xero_org_clients', array($this, 'xero_org_clients')); + add_shortcode('xero_org_payroll_calendar', array($this, 'xero_org_payroll_calendar')); + + + + add_action('init', array($this, "xero_init")); + add_action('plugins_loaded', array($this, "load_storage")); + // $this->xero_init(); + $this->storage = new StorageClass(); + } + + public function __call($method, $args) { + if ( method_exists($this->XeroOauth1, $method) ) { + syslog(LOG_INFO,"Calling $method"); + $this->XeroOauth1->$method($args); + } else { + error_log("$method is not defined" ); + } + } + + public function xero_init() + { + $this->provider = $this->create_provider(); + + } + + private function create_provider() { + $provider = new \League\OAuth2\Client\Provider\GenericProvider([ + 'clientId' => $this->clientID, + 'clientSecret' => $this->clientSecret, + 'redirectUri' => $this->getRedirectURL(), + 'urlAuthorize' => 'https://login.xero.com/identity/connect/authorize', + 'urlAccessToken' => 'https://identity.xero.com/connect/token', + 'urlResourceOwnerDetails' => 'https://api.xero.com/api.xro/2.0/Organisation' + ]); + return $provider; + } + + public function load_storage() + { + //$this->storage->read_value(); + } + + public function boot_carbon() + { + \Carbon_Fields\Carbon_Fields::boot(); + } + + + function build_settings_page() + { + Container::make('theme_options', __('Xero Integration')) + ->set_page_parent('options-general.php') + ->add_fields(array( + Field::make('text', 'xero_oauth2state', 'Xero Oauth2 State') + ->set_attribute('maxLength', 2048) + ->set_attribute('readOnly', true) + ->set_default_value($this->storage->getOauth2State()), + Field::make('text', 'xero_token', 'Xero Token') + ->set_attribute('maxLength', 2048) + ->set_attribute('readOnly', true) + ->set_default_value($this->storage->getSession()['token']), + Field::make('text', 'xero_refresh_token', 'RefreshToken') + ->set_attribute('readOnly', true) + ->set_default_value($this->storage->getSession()['refresh_token']), + Field::make('text', 'xero_id_token', 'ID Token') + ->set_attribute('readOnly', true) + ->set_default_value($this->storage->getSession()['id_token']), + Field::make('text', 'xero_tenant_id', 'Tenant ID') + ->set_attribute('readOnly', true) + ->set_default_value($this->storage->getSession()['tenant_id']), + Field::make('text', 'xero_expires', 'Expires') + ->set_attribute('readOnly', true) + ->set_default_value($this->storage->getSession()['expires']), + Field::make('text', 'xero_expires_human', 'Expires') + ->set_attribute('readOnly', true) + ->set_default_value($this->storage->getSession()['expires_human']), + + Field::make('html', 'crb_information_text') + ->set_html('

Connect/Reconnect

if the above field is empty, + or the expire date looks suspicous, please reconnect to XeroOauth1

+
+ + +
') + )); + + $this->storage->read_value(); + } + + + function field_activated() + { + $this->storage->read_value(); + $xeroTenantId = (string)$this->storage->getSession()['tenant_id']; + if ($xeroTenantId == "") { + $this->startAuthorization(); + } else { + $this->refresh_token(); + } + } + + public function startAuthorization() + { + // This returns the authorizeUrl with necessary parameters applied (e.g. state). + $authorizationUrl = $this->provider->getAuthorizationUrl($this->options); + + // Save the state generated for you and store it to the session. + // For security, on callback we compare the saved state with the one returned to ensure they match. + $this->storage->setOauth2State($this->provider->getState()); + + // Redirect the user to the authorization URL. + header('Location: ' . $authorizationUrl); + exit(); + } + + + function xero_callback() + { + if (isset($_GET['xero_reauth'])) { + $this->startAuthorization(); + return; + } + if (!isset($_GET['xero_callback'])) { + return; + } + // If we don't have an authorization code then get one + if (!isset($_GET['code'])) { + echo "Something went wrong, no authorization code found"; + exit("Something went wrong, no authorization code found"); + + // Check given state against previously stored one to mitigate CSRF attack + } elseif (empty($_GET['state']) || ($_GET['state'] !== $this->storage->getOauth2State())) { + echo "Invalid State"; + $this->storage->setOauth2State(""); + exit('Invalid state'); + } else { + + try { + // Try to get an access token using the authorization code grant. + $accessToken = $this->provider->getAccessToken('authorization_code', [ + 'code' => $_GET['code'] + ]); + + $config = \XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken((string)$accessToken->getToken()); + $identityInstance = new \XeroAPI\XeroPHP\Api\IdentityApi( + new \GuzzleHttp\Client(), + $config + ); + + $result = $identityInstance->getConnections(); + + // Save my tokens, expiration tenant_id + $this->storage->setToken( + $accessToken->getToken(), + $accessToken->getExpires(), + $result[0]->getTenantId(), + $accessToken->getRefreshToken(), + $accessToken->getValues()["id_token"] + ); + + // related to $this->build_settings_page + $url = "/wp-admin/options-general.php?page=crb_carbon_fields_container_xero_integration.php"; + header('Location: ' . $url); + exit(); + + } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) { + echo "Xero Callback failed"; + exit(); + } + } + } + + private function getRedirectURL() { + return get_site_url() . "/?xero_callback"; + } + + function refresh_token($enforced=false) + { + $this->storage->read_value(); + $this->xeroTenantId = (string)$this->storage->getSession()['tenant_id']; + + if ($this->storage->getHasExpired() || $enforced) { + $this->provider = $this->create_provider(); + + try { + $newAccessToken = $this->provider->getAccessToken('refresh_token', [ + 'refresh_token' => $this->storage->getRefreshToken() + ]); + + } catch (\Exception $e) { + $this->startAuthorization(); + return; + } + + // Save my token, expiration and refresh token + $this->storage->setToken( + $newAccessToken->getToken(), + $newAccessToken->getExpires(), + $this->xeroTenantId, + $newAccessToken->getRefreshToken(), + $newAccessToken->getValues()["id_token"]); + } + } + + private function get_accounting_instance() { + $this->refresh_token(); + if ($this->apiAccountingInstance == null) { + $this->config = \XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( + (string)$this->storage->getSession()['token']); + $this->apiAccountingInstance = new \XeroAPI\XeroPHP\Api\AccountingApi( + new \GuzzleHttp\Client(), + $this->config + ); + } + return $this->apiAccountingInstance; + } + + private function get_payroll_au_instance() { + $this->refresh_token(); + if ($this->apiPayrollInstance == null) { + $this->config = \XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( + (string)$this->storage->getSession()['token']); + $this->config->setDebug(true); + $this->config->setDebugFile("/home/acaresydneycom/public_html/wp-content/plugins/ts/debug.log"); + $this->apiPayrollInstance = new \XeroAPI\XeroPHP\Api\PayrollAuApi( + new \GuzzleHttp\Client(), + $this->config + ); + } + return $this->apiPayrollInstance; + } + + public function xero_org_name() + { + ini_set('display_errors', 'On'); + $apiInstance = $this->get_accounting_instance(); + $apiResponse = $apiInstance->getOrganisations($this->xeroTenantId); + return 'Organisation Name: ' . $apiResponse->getOrganisations()[0]->getName() . " "; + } + + public function xero_org_contacts() + { + ini_set('display_errors', 'On'); + $apiInstance = $this->get_accounting_instance(); + + $apiResponse = $apiInstance->getContacts($this->xeroTenantId); + $contacts = $apiResponse->getContacts(); + $message = " "; + $message .= " + + + + + + "; + $count = 1; + foreach ($contacts as $c) { + + $group = ""; + foreach ($c->getContactGroups() as $g) { + $group .= $g->getName() . " - " . $g->getStatus() . "
"; + } + + $message .= " " . + " " . + " " . + " " . + " " . + " " . + " " . + ""; + } + $message .= "
# unique-id name Supplier Customer Group Status
" . $count++ . " " . $c->getContactId() . " " . $c->getName() . " " . ($c->getIsSupplier() ? "yes" : " - ") . " " . ($c->getIsCustomer() ? "yes" : " - ") . " " . $group . " " . $c->getContactStatus() . "
"; + return $message; + + } + + public function xero_org_invoices() { + ini_set('display_errors', 'On'); + $apiInstance = $this->get_accounting_instance(); + + $apiResponse = $apiInstance->getInvoices($this->xeroTenantId); + $invoices = $apiResponse->getInvoices(); + + $message = " "; + $message .= " + + + + + + "; + $count = 1; + + foreach ( $invoices as $c) { + $strDate = ""; + $d = $c->getDate(); + if ( $d != null) { + $da = $c->getDateAsDate(); + $strDate = $da->format("Y-m-d"); + } else { + $strDate = $d; + } + + $message .= " " . + " " . + " " . + " " . + " " . + " " . + " " . + ""; + } + $message .= "
# invoice num date contact amount Type Status
" . $count ++ . " " . $c->getInvoiceNumber() . " " . $strDate . " " . $c->getContact()->getName() . " " . $c->getTotal() . " " . $c->getType() . " " . $c->getStatus() . "
"; + return $message; + + } + + public function xero_org_pay_items(){ + ini_set('display_errors', 'On'); + $api = $this->get_payroll_au_instance(); + + $xeroTenantId = $this->xeroTenantId; + //$xeroTenantId = "e23fd416-3b66-43e9-b908-97fbefa24eb8"; // demo company; + //$xeroTenantId = "4e2521ae-83e6-4895-aa90-b20aa0825ce1"; // Acaresydney ; + + $ifModifiedSince = null; + $where = null; // "Status==\"ACTIVE\""; + $order = null; // "EmailAddress%20DESC"; + $page = 1; + // $result = null; + try { + $result = $api->getPayItems ($xeroTenantId, $ifModifiedSince, $where, $order, $page); + $rates = $result->getPayItems()->getEarningsRates(); + $message = " "; + $message .= " + + + + + + + + + + + "; + $count = 1; + + foreach ( $rates as $r) { + $message .= " " . + " " . + " " . + " " . + " " . + " " . + " " . + " " . + " " . + " " . + " " . + " " . + ""; + } + $message .= "
# Name EarningsType RateType AccountCode Multiplier IsExemptFromTax IsExemptFromSuper AccrueLeave IsReportableAsW1 UpdatedDateUTC CurrentRecord
" . $r->getEarningsRateId() . " " . $r->getName() . " " . $r->getEarningsType() . " " . $r->getRateType() . " " . $r->getAccountCode() . " " . $r->getTypeOfUnits() . " " . $r->getRatePerUnit() . " " . $r->getIsExemptFromTax() . " " . $r->getIsExemptFromSuper() . " " . $r->getIsReportableAsW1() . " " . $r->getUpdatedDateUtc() . " " . $r->getCurrentRecord() . "
"; + return $message; + } catch (\Exception $e) { + echo 'Exception when calling PayrollAuApi->getPayItems: ', $e->getMessage(), PHP_EOL; + return; + } + } + + public function xero_org_employees(){ + ini_set('display_errors', 'On'); + $api = $this->get_payroll_au_instance(); + + $xeroTenantId = $this->xeroTenantId; + //$xeroTenantId = "e23fd416-3b66-43e9-b908-97fbefa24eb8"; // demo company; + //$xeroTenantId = "4e2521ae-83e6-4895-aa90-b20aa0825ce1"; // Acaresydney ; + + $ifModifiedSince = date("M d Y H:i:s", strtotime("-30 days")); + $where = "Status==\"ACTIVE\""; + $order = null; // "EmailAddress%20DESC"; + $page = 1; + // $result = null; + try { + $result = $api->getEmployees($xeroTenantId,$ifModifiedSince,$where,$order,$page); + $employees = $result->getEmployees(); + $message = " "; + $message .= " + + + + + + + + + + + "; + $count = 1; + foreach ($employees as $r){ + + $message .= " " . + " " . + " " . + " " . + " " . + " " . + " " . + " " . + " " . + " " . + " " . + ""; + $count ++; + } + $message .= "
# First Last Status Email DOB Gender Phone Mobile Start Group
" . $count . " " . $r->getFirstName() . " " . $r->getLastName() . " " . $r->getStatus() . " " . $r->getEmail() . " " . $r->getDateOfBirthAsDate()->format("M d Y") . " " . $r->getGender() . " " . $r->getPhone() . " " . $r->getMobile() . " " . $r->getStartDateAsDate()->format("M d Y") . " " . $r->getEmployeeGroupName() . "
"; + return $message; + } catch (\Exception $e) { + echo 'Exception when calling PayrollAuApi->getPayItems: ', $e->getMessage(), PHP_EOL; + return; + } + } + + public function xero_org_clients(){ + ini_set('display_errors', 'On'); + try { + $contacts = $this->getClients(); + $message = " "; + $message .= " + + + + + + + "; + $count = 1; + foreach ($contacts as $r){ + + $message .= " " . + " " . + " " . + " " . + " " . + " " . + " " . + ""; + $count ++; + } + $message .= "
# Name Last Status Email AccountNumber Addresses
" . $count . " - ". $r->getContactID() . " " . $r->getFirstName() . " " . $r->getLastName() . " " . $r->getContactStatus() . " " . $r->getEmailAddress() . " " . $r->getAccountNumber() . " " . $r->getAddresses() . "
"; + return $message; + } catch (\Exception $e) { + echo 'Exception when calling PayrollAuApi->getPayContacts: ', $e->getMessage(), PHP_EOL; + return; + } + } + + + public function xero_org_payroll_calendar() + { + // update_option('bts_pay_roll_calendar_last_sync', time()); + try { + $result = $this->get_payroll_calendar(); + $pc = $result->getPayrollCalendars()[0]; + $start = $pc->getStartDateAsDate()->format('Y-m-d'); + $finish = new \DateTime($start); + $finish = $finish->modify("+13 days")->format('Y-m-d'); + $paydate = $pc->getPaymentDateAsDate()->format('Y-m-d'); + $calendar["start"] = $start; + $calendar["finish"] = $finish; + $calendar["paydate"] = $paydate; + + return print_r ($calendar); + }catch (\Exception $e) { + echo 'Exception when calling PayrollAuApi->getPayrollCalendar: ', $e->getMessage(), PHP_EOL; + } + //update_option('bts_pay_roll_calendar', $calendar); + } + + // + //TS implementation + // + + /* sync xero to wp options */ + public function init_wp(){ + try{ + error_log("init_wp is empty"); + $this->sync_pay_item(); +// $this->add_new_client(); +// $this->add_new_employee(); +// $this->sync_payroll_calendar(); + }catch(\Exception $e){ + + } + } + + private function sync_pay_item() { + if ($this->too_close_to_sync_payitem()){ + //return; + } + + $api = $this->get_payroll_au_instance(); + $xeroTenantId = $this->xeroTenantId; + $page = 1; + try { + $result = $api->getPayItems($xeroTenantId, $ifModifiedSince, $where, $order, $page); + + + + foreach ($result->getPayItems()->getEarningsRates() as $e){ +// "EarningsRateID": "34e17d08-237a-4ae2-8115-375d1ff8a9ed", +// "Name": "Overtime Hours (exempt from super)", +// "EarningsType": "OVERTIMEEARNINGS", +// "RateType": "MULTIPLE", +// "AccountCode": "477", +// "Multiplier": 1.5, +// "IsExemptFromTax": true, +// "IsExemptFromSuper": true, +// "AccrueLeave": false, +// "IsReportableAsW1": true, +// "UpdatedDateUTC": "2019-03-16T13:18:19+00:00", +// "CurrentRecord": false + if ($e->getCurrentRecord() == "true"){ + $payitem_options[]= array( + 'EarningsRateID' => $e->getEarningsRateID(), + 'Name'=> $e->getName(), + 'EarningsType'=> $e->getEarningstype(), + 'RatePerUnit' => $e->getRatePerUnit(), + 'RateType' => $e->getRateType(), + 'AccountCode' => $e->getAccountCode(), + "Multiplier"=> $e->getMultiplier(), + "IsExemptFromTax" => $e->getIsExemptFromTax(), + "IsExemptFromSuper"=> $e->getIsExemptFromSuper(), + "AccrueLeave" => $e->getAccrueLeave(), + "TypeOfUnits" => $e->getTypeOfUnits(), + "CurrentRecord"=> $e->getCurrentRecord(), + ); + } + } + update_option('bts_payitem_earnings_rate', $payitem_options); + update_option('bts_payitem_last_sync', time()); + + + } catch (Exception $e) { + echo 'Exception when calling PayrollAuApi->getPayItems: ', $e->getMessage(), PHP_EOL; + } + } + + public function sync_users($mininterval, $employeeonly, $clientsonly){ + echo "not implemented "; //TODO; + } + + public function sync_payroll_calendar() { + + } + + private function too_close_to_sync_payitem(){ + $lastsync = get_option('bts_payitem_last_sync', 0); + $now = time(); + $diff = $now - (int) $lastsync; + return $diff < $this->minimum_sync_interval_in_seconds; //default 10 minutes + } + + private function sync_payitem(){ + + + } + + + private function too_close_to_add_employee(){ + $lastsync = get_option('bts_add_employee_last_sync', 0); + $now = time(); + $diff = $now - (int) $lastsync; + return $diff < 1.5 * $this->minimum_sync_interval_in_seconds; //default 1.1 * 10 minutes + } + private function too_close_to_add_client(){ + $lastsync = get_option('bts_add_client_last_sync', 0); + $now = time(); + $diff = $now - (int) $lastsync; + return $diff < 2.0 * $this->minimum_sync_interval_in_seconds; //default 1.2 * 10 minutes + } + private function too_close_to_sync_payroll_calendar(){ + $lastsync = get_option('bts_pay_roll_calendar_last_sync', 0); + $now = time(); + $diff = $now - (int) $lastsync; + return $diff < 3.0 * $this->minimum_sync_interval_in_seconds; //default 1.3 * 10 minutes + } + + + + + + + public function getClients($contact_group_id = null) { + if ( $contact_group_id == null ){ + $contact_group_id = $this->clientContactGroupID; + } + $apiAcc = $this->get_accounting_instance(); + $result = $apiAcc->getContactGroup($this->xeroTenantId, $contact_group_id); + $cg = $result->getContactGroups(); + $allClients = $cg[0]->getContacts(); + + $ifModifiedSince = new \DateTime(); + $recent = new \DateInterval("P30D"); + $ifModifiedSince->sub($recent); + + + $allContacts = $apiAcc->getContacts($this->xeroTenantId, $ifModifiedSince); + $ret = []; + foreach ( $allContacts as $ac ) { + //search from within the group + $found = false; + $id = $ac->getContactID(); + foreach ($allClients as $client) { + $clientID = $client->getContactID(); + if ( $clientID == $id ) { + $found = true; + break; + } + } + + if ( $found ) { + $ret[] = $ac; + } + + } + return $ret; + } + + public function get_payroll_calendar() + { + $id = "33dc7df5-3060-4d76-b4da-57c20685d77d"; //fortnightly + $api = $this->get_payroll_au_instance(); + + $pc = $api->getPayrollCalendar($this->xeroTenantId, $id); + return $pc; + } + +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0f9d205 --- /dev/null +++ b/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "xeroapi/xero-php-oauth2": "2.8.1", + "htmlburger/carbon-fields": "^3.3", + "guzzlehttp/psr7": "1.8.2" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..976c9fa --- /dev/null +++ b/composer.lock @@ -0,0 +1,778 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "fd8f5e90c9c2ce1e1f6b11421a29aa4a", + "packages": [ + { + "name": "firebase/php-jwt", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d2113d9b2e0e349796e72d2a63cf9319100382d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d2113d9b2e0e349796e72d2a63cf9319100382d2", + "reference": "d2113d9b2e0e349796e72d2a63cf9319100382d2", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": ">=4.8 <=9" + }, + "suggest": { + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v5.4.0" + }, + "time": "2021-06-23T19:00:23+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "7008573787b430c1c1f650e3722d9bba59967628" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628", + "reference": "7008573787b430c1c1f650e3722d9bba59967628", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.4", + "guzzlehttp/psr7": "^1.7 || ^2.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.3-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://github.com/alexeyshockov", + "type": "github" + }, + { + "url": "https://github.com/gmponos", + "type": "github" + } + ], + "time": "2021-03-23T11:33:13+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.4.1" + }, + "time": "2021-03-07T09:25:29+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "dc960a912984efb74d0a90222870c72c87f10c91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", + "reference": "dc960a912984efb74d0a90222870c72c87f10c91", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/1.8.2" + }, + "time": "2021-04-26T09:17:50+00:00" + }, + { + "name": "htmlburger/carbon-fields", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/htmlburger/carbon-fields.git", + "reference": "dd5663e14c6db365323b688dbae1cfbeaf14bee7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/htmlburger/carbon-fields/zipball/dd5663e14c6db365323b688dbae1cfbeaf14bee7", + "reference": "dd5663e14c6db365323b688dbae1cfbeaf14bee7", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "mockery/mockery": "^0.9.7", + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon_Fields\\": "core/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-only" + ], + "authors": [ + { + "name": "htmlBurger", + "email": "wordpress@htmlburger.com", + "homepage": "https://htmlburger.com/", + "role": "Developer" + }, + { + "name": "Miroslav Mitev", + "email": "mmitev.2create@gmail.com", + "role": "Developer" + }, + { + "name": "Atanas Angelov", + "email": "atanas.angelov.dev@gmail.com", + "role": "Developer" + }, + { + "name": "Georgi Stoyanov", + "email": "stoyanov.gs@gmail.com", + "role": "Developer" + }, + { + "name": "Plamen Kostadinov", + "email": "pkostadinov.2create@gmail.com", + "homepage": "http://plasmen.info/", + "role": "Developer" + }, + { + "name": "Stanimir Panchev", + "email": "Stan4omir@gmail.com", + "role": "Developer" + }, + { + "name": "Marin Atanasov", + "email": "contact@marinatanasov.com", + "homepage": "http://marinatanasov.com/", + "role": "Developer" + }, + { + "name": "Siyan Panayotov", + "homepage": "http://siyanpanayotov.com/", + "role": "Developer" + }, + { + "name": "Peter Petrov", + "email": "peter.petrov89@gmail.com", + "role": "Developer" + }, + { + "name": "Stanimir Stoyanov", + "email": "stanimir.k.stoyanov@gmail.com", + "role": "Developer" + }, + { + "name": "Kaloyan Ivanov", + "email": "kaloyanxivanov@gmail.com", + "homepage": "http://vilepixels.com/", + "role": "Developer" + }, + { + "name": "Georgi Popov", + "homepage": "http://magadanski.com/", + "role": "Developer" + }, + { + "name": "German Velchev", + "email": "germozy@gmail.com", + "role": "Developer" + }, + { + "name": "Rashko Petrov", + "email": "brutalenemy666@gmail.com", + "homepage": "http://errorfactory.com/", + "role": "Developer" + }, + { + "name": "Alexander Panayotov", + "email": "alexander.panayotov@gmail.com", + "homepage": "http://alexanderpanayotov.com/", + "role": "Developer" + }, + { + "name": "Viktor Vasilev", + "email": "liberalcho@gmail.com", + "role": "Developer" + }, + { + "name": "Georgi Georgiev", + "email": "george.georgiev96@gmail.com", + "role": "Developer" + }, + { + "name": "Atanas Vasilev", + "email": "atanasvasilev91@gmail.com", + "role": "Developer" + } + ], + "description": "WordPress developer-friendly custom fields for post types, taxonomy terms, users, comments, widgets, options and more.", + "homepage": "http://carbonfields.net/", + "support": { + "docs": "http://carbonfields.net/docs/", + "email": "wordpress@htmlburger.com", + "issues": "https://github.com/htmlburger/carbon-fields/issues", + "source": "https://github.com/htmlburger/carbon-fields" + }, + "time": "2021-04-22T13:24:34+00:00" + }, + { + "name": "league/oauth2-client", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-client.git", + "reference": "badb01e62383430706433191b82506b6df24ad98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/badb01e62383430706433191b82506b6df24ad98", + "reference": "badb01e62383430706433191b82506b6df24ad98", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0 || ^7.0", + "paragonie/random_compat": "^1 || ^2 || ^9.99", + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpunit/phpunit": "^5.7 || ^6.0 || ^9.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + }, + { + "name": "Woody Gilk", + "homepage": "https://github.com/shadowhand", + "role": "Contributor" + } + ], + "description": "OAuth 2.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "identity", + "idp", + "oauth", + "oauth2", + "single sign on" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-client/issues", + "source": "https://github.com/thephpleague/oauth2-client/tree/2.6.0" + }, + "time": "2020-10-28T02:03:40+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "xeroapi/xero-php-oauth2", + "version": "2.8.1", + "source": { + "type": "git", + "url": "https://github.com/XeroAPI/xero-php-oauth2.git", + "reference": "cdf5dbeddff46d25212be596ff474c8bcf7351da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/XeroAPI/xero-php-oauth2/zipball/cdf5dbeddff46d25212be596ff474c8bcf7351da", + "reference": "cdf5dbeddff46d25212be596ff474c8bcf7351da", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "firebase/php-jwt": "^5.0", + "guzzlehttp/guzzle": "^6.2 | ^7.0", + "guzzlehttp/psr7": "^1.8", + "league/oauth2-client": "^2.5.0", + "php": ">=5.6" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.12", + "phpunit/phpunit": "^4.8", + "squizlabs/php_codesniffer": "~2.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "XeroAPI\\XeroPHP\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Xero API Team", + "homepage": "https://developer.xero.com" + } + ], + "description": "Xero official PHP SDK for oAuth2 generated with OpenAPI spec 3", + "homepage": "https://github.com/xeroapi/xero-php-oauth2", + "keywords": [ + "api", + "openapispec", + "php", + "rest", + "sdk", + "xero" + ], + "support": { + "issues": "https://github.com/XeroAPI/xero-php-oauth2/issues", + "source": "https://github.com/XeroAPI/xero-php-oauth2/tree/2.8.1" + }, + "time": "2021-08-09T22:52:00+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.1.0" +}