Google CalDAV receiving 405 while trying to alter an existing event via 'PUT' - caldav

I'm working with Google's CalDAV functionality and am having issues when trying to update an event on a calendar.
I'm receiving a "405 - Method Not Allowed" response on my PUT requests.
$calendar = 'BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
X-WR-CALNAME:Primary Calendar
X-WR-TIMEZONE:America/New_York
BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:DAYLIGHT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:EDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:EST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;VALUE=DATE:20181010
DTEND;VALUE=DATE:20181011
DTSTAMP:20181016T192214Z
ORGANIZER;CN=cest#tave.wtf:mailto:cest#tave.wtf
UID:076e0pqgifk0vli961dojq886p#google.com
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS- ACTION;CN=kody#tave.com;X-NUM-GUESTS=0:mailto:kody#tave.com
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ- PARTICIPANT;PARTSTAT=ACCEPTED;CN=cest457#tave.wtf;X-NUM-GUESTS=0:mailto:cest457#tave.wtf
CREATED:20181016T132132Z
DESCRIPTION:this is a description from caldav too
LAST-MODIFIED:20181016T192214Z
LOCATION:
SEQUENCE:1
STATUS:CONFIRMED
SUMMARY:test google event 2, updated in caldav stuff
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR';
$url = 'https://apidata.googleusercontent.com/caldav/v2/' . $GoogleCalendarSync->calendarID . '/events';
$opts = [
'allow_redirects' => true,
'timeout' => 40,
'connect_timeout' => 10,
'headers' => [
'Authorization' => 'Bearer ' . $this->config['token']['access_token'],
'If-None-Match' => '*',
'Content-Type' => 'text/calendar',
],
'body' => $calendar,
];
$Client = new GuzzleHttp\Client();
try {
$Response = $Client->request('PUT', $url, $opts);
} catch (Exception $e) {
App::recoverableError(__METHOD__, 'Failed to update calendar', [
'exception' => $e,
'GoogleCalendarSync' => (array) $GoogleCalendarSync,
]);
return false;
}
return (string) $Response->getBody();
I'm not sure if the wrong URL is being used, although I believe it is the correct one. (If not, where could I find it?) Google's documentation is extremely limited here.
I've also verified that the access token (generated via the OAuth flow) is indeed correct since I'm also using it to successfully retrieve the calendar in another separate request. It's just the "PUT" requests that are causing the issues.
Thanks for your time!

Sure enough, after hours of searching and tweaking the code, I found the answer less than 30 minutes after I made this question.
The issue was that the URL being used was missing the calendar object at the end.
url = 'https://apidata.googleusercontent.com/caldav/v2/' . $GoogleCalendarSync->calendarID . '/events';
vs
url = 'https://apidata.googleusercontent.com/caldav/v2/' . $GoogleCalendarSync->calendarID . '/events/basic.ics';

Related

Facebook Error #100 when uploading Offline Conversions using Marketing API

I am using the the Facebook Marketing API to upload offline conversions.
The attached code was working until about 2 weeks ago and now reports the following error.
{"error":{"message":"(#100) The parameter data is required","type":"OAuthException","code":100,"fbtrace_id":"HkpzkWB1I5g"}}
I don't understand why it should simply stop working after working as expected for so long. I don't know how to fix it. Any ideas?
$data=array();
$data["match_keys"]["email"]=$emails;
$data["match_keys"]["phone"]=$mobiles;
$data["match_keys"]["fn"]=hash("sha256",$first_name);
$data["match_keys"]["ln"]=hash("sha256",$last_name);
$data["match_keys"]["ln"]=hash("sha256",$last_name);
$data["match_keys"]["ct"]=hash("sha256",$suburb);
$data["match_keys"]["zip"]=hash("sha256",$postcode);
$data["match_keys"]["country"]=hash("sha256","Australia");
$data["event_time"] = strtotime($order_date);
$data["event_name"] = "Purchase";
$data["currency"] = "AUD";
$data["value"] = $order_total;
$data['order_id']=$order_id;
$access_token = '<access_token_0>';
// PURCHASE DATA
$data_json = json_encode(array($data));
$fields = array();
$fields['access_token'] = $access_token;
$fields['upload_tag'] = uniqid() // You should set a tag here (feel free to adjust)
$fields['data'] = $data_json;
$ch = curl_init();
curl_setopt_array($ch, array(
// Replace with your offline_event_set_id
CURLOPT_URL => "https://graph.facebook.com/v2.12/1696501357346693/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => http_build_query($fields),
CURLOPT_HTTPHEADER => array(
"cache-control: no-cache",
"content-type: multipart/form-data",
"Accept: application/json" ),
));
$result = curl_exec($ch);
echo "\nResult encode";
echo ($result);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close ($ch);
Same thing just started happening here. Exact same problem. I had to play around for hours... But to get it working, I commented out
//"content-type: multipart/form-data",
and it started to work for me. Please let know if that also solves your problem.
It simply means that data is not sent as per documentation. I was facing this issue as in my case "contents" parameter was passing empty string, instead of documentation says that "contents" parameter should contain atleast 2 parameters, that is, quantity and id.
Go through documentation and you can compare your value format with documentation.
Here is link for documentation :- https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/custom-data#content-ids

PHPUnit - REST API testing

I have REST API written in php, i want to test it with phpunit.
I wrote test like this, it works but response body was empty. I tested it with fiddler, it send response body.
Sorry for my english.
class ProgrammerControllerTest extends PHPUnit_Framework_TestCase
{
public function testPOST()
{
// create our http client (Guzzle)
$client = new Guzzle\Http\Client();
$response = $client->post("http://api.loc/v2/", array(
'headers' => "User-Agent: Fiddler\r\n" .
"Host: api.loc\r\n".
"Content-Type: application/x-www-form-urlencoded\r\n".
"Content-Length: 34\r\n",
'body' => array('kle' =>'sino','lat' => '41', 'long' => '69'),
));
var_dump($response);
}
}
Can you try with
var_dump($response->getBody()->getContents());
this is my snippet ( it's in Get but it's the same )
$client = new GuzzleHttp\Client();
$response = $client->get('http://192.168.99.100/v1/hello');
var_dump($response->getBody()->getContents());
result:
string(13) "{"bar":"foo"}"
You can use built in method call of Laravel to test your controllers.
$response = $this->call('POST', '/api/v1.0/pages', $parameters);
$data = $response->getData();
Your ProgrammerControllerTest should extend from TestCase
I know Amazon uses Guzzle to test AWS SDK, you can read more info at http://guzzle3.readthedocs.org/testing/unit-testing.html
And also, don't hesitate to dig what others are using (such as Amazon, Facebook etc...), thats why open-source is so great!

Transmitting POST-event to Google Calendar using Perl and Net::OAuth2

I'm developing a webapp in Perl and try to create events in user's Google Calendar with OAuth2. Authentication and requesting calendar-data is working fine, I'm just totally stuck when it comes to sending a POST-request and attaching JSON- or hash-data to it. Are there methods coming with the module for this? The documentation does not point me anywhere here.
I guess LWP would provide ways, but this seems like a lot of overhead.
Here is how i accomplished getting calendar-events so far (as a simple console-app for now):
use Net::OAuth2::Profile::WebServer;
my $auth = Net::OAuth2::Profile::WebServer->new
( name => 'Google Calendar'
, client_id => $id
, client_secret => $secret
, site => 'https://accounts.google.com'
, scope => 'https://www.googleapis.com/auth/calendar'
, authorize_path => '/o/oauth2/auth'
, access_token_path => '/o/oauth2/token'
, redirect_uri => $redirect
);
print $auth->authorize_response->as_string;
my $code=<STDIN>;
my $access_token = $auth->get_access_token($code);
my $response = $access_token->get('https://www.googleapis.com/calendar/v3/calendars/2j6r4iegh2u8o2409jk8k2g838#group.calendar.google.com/events');
$response->is_success
or die "error: " . $response->status_line;
print $response->decoded_content;
Thanks a lot for your time!
Markus
I guess it's time to answer my own question.
To transmit JSON in a POST-request (creating a calendar-event) I ended up using LWP::UserAgent and HTTP::Request. For setting the content-type I first had to create an HTTP::Request-object and set header and data:
my $req = HTTP::Request->new( 'POST', 'https://www.googleapis.com/calendar/v3/calendars/<calendarID>/events' );
$req->header( 'Content-Type' => 'application/json' );
$req->content( "{ 'summary': 'EventName', 'start': { 'dateTime': '2015-02-11T20:48:00+01:00' }, 'end': { 'dateTime': '2015-02-11T22:30:00+01:00' } }" );
Then I created an LWP::UserAgent-object, appended the OAuth2-token to it and let it fire the request:
my $apiUA = LWP::UserAgent->new();
$apiUA->default_header(Authorization => 'Bearer ' . $access_token->access_token() );
my $apiResponse = $apiUA->request( $req );
It's that simple. Would've been a lot nicer to do this all-in-one with Net::OAuth2, though.

Zend_Feed: white screen of death on Production, works perfectly on Dev Server

A few weeks ago I noticed that the RSS feed on my live site was broken - I get a white screen of death. It had worked fine up until then. The rest of my site continues to work fine. Additionally the identical code continues to work perfectly on my dev box.
No code changes have occurred, so I'm guessing my web host have changed a server setting - but I've no idea what the setting may be (so I don't know if there's a workaround or if I need to ask my web host to change something). Both Prod & Dev are running PHP 5.3.8.
Could anyone give me a clue as to what that setting might be?
The only major difference I could see in the response headers was that my (non-working) Production RSS feed has this Response Header: "Accept-Ranges: none".
I've double-checked the DB call that populates the feed, and even replaced it with some static data within the class (just in case there was a DB problem), but it makes no difference.
Code for the relevant Controller method below:
public function articlesAction(){
$format = $this->_request->getParam('format');
//default format to rss if unspecified
$format = in_array($format, array('rss','atom')) ? $format : 'rss';
$articles = new Application_Model_DbTable_Articles();
$rows = $articles->getLatestArticlesForFeed();
$channel = array(
'title' => 'Feed of articles',
'link' => 'http://www.mysite.co.uk',
'description' => 'The latest articles and reviews from my site',
'author' => 'My name',
'language' => 'en',
'ttl' => '60',
'copyright' => '© the writers of the articles',
'charset' => 'utf-8',
'entries' => array()
);
foreach ($rows as $item) {
$articlelink = 'http://www.mysite.co.uk/articles/' . $item['stub'];
$formattedlink = '<p><strong>Source: '.$articlelink.'</strong></p>';
$channel['entries'][] = array(
'title' => $item['title'],
'link' => $articlelink,
'guid' => $articlelink,
'description' => $formattedlink . $item['content'] . '<p>© ' . $item['byline'] . ', ' . $item['copyright'] . '</p>' ,
'lastUpdate' => strtotime($item['date_published'])
);
}
$feed = Zend_Feed::importArray($channel, $format);
$feed->__wakeup();
}
$feed->send();
$this->_helper->viewRenderer->setNoRender();
$this->_helper->layout()->disableLayout();
}
I wasted an hour once figuring out why I have a WSOD just because I initiated a class with one lowercase letter...
$table = new Model_DbTable_EshopSubcategories(); instead of
$table = new Model_DbTable_EshopSubCategories();
The dev server does not have to be case sensitive and the production server can.

How to format Serialized Zend_Service_Twitter response

When I use OAuth to connect an application to a twitter account I put the access token and the user data in my database as follow:
$this->consumer = new Zend_Oauth_Consumer($this->configTwitter);
if(!empty($_GET) && isset($_SESSION['TWITTER_REQUEST_TOKEN']))
{
$token = $this->consumer->getAccessToken($_GET, unserialize($_SESSION['TWITTER_REQUEST_TOKEN']));
// Get user info
$twitter = new Zend_Service_Twitter(array(
'username' => $token->screen_name,
'accessToken' => $token,
'callbackUrl' => $this->_getParam('SettingsWebsiteUrl').'/news/index/connecttwitter',
'consumerKey' => 'XXXXXX',
'consumerSecret' => 'XXXXXXX'
));
$response = $twitter->account->verifyCredentials();
// place in database
$this->NewsService->TokenSocialMedia(
$token,
'twitter',
serialize($response)
);
$_SESSION['TWITTER_REQUEST_TOKEN'] = null;
$this->_helper->flashMessenger(array('message' => $this->view->translate('The CMS is successfully connected to your twitter account'), 'status' => 'success'));
$this->_helper->redirector('settings', 'index');
}
else
{
$token = $this->consumer->getRequestToken();
$_SESSION['TWITTER_REQUEST_TOKEN'] = serialize($token);
$this->consumer->redirect();
}
This works fine. The response looks like this below and I have no clue of how to format this and display it to the user. Only thing I want is to store the twitter id, twitter name (not screen_name) and the image url.
O:23:"Zend_Rest_Client_Result":2:{s:8:"�*�_sxml";O:16:"SimpleXMLElement":34:{s:2:"id";s:9:"150638034";s:4:"name";s:20:"De Graaf & Partners ";s:11:"screen_name";s:15:"GraafenPartners";s:8:"location";s:9:"Culemborg";s:11:"description";s:34:"Full service communications agency";s:17:"profile_image_url";s:78:"http://a2.twimg.com/profile_images/1191111069/Logo_DG_P-CMYK_Square_normal.jpg";s:3:"url";s:31:"http://www.degraafenpartners.nl";s:9:"protected";s:5:"false";s:15:"followers_count";s:1:"0";s:24:"profile_background_color";s:6:"C0DEED";s:18:"profile_text_color";s:6:"333333";s:18:"profile_link_color";s:6:"0084B4";s:26:"profile_sidebar_fill_color";s:6:"DDEEF6";s:28:"profile_sidebar_border_color";s:6:"C0DEED";s:13:"friends_count";s:1:"0";s:10:"created_at";s:30:"Tue Jun 01 12:47:51 +0000 2010";s:16:"favourites_count";s:1:"0";s:10:"utc_offset";s:4:"3600";s:9:"time_zone";s:9:"Amsterdam";s:28:"profile_background_image_url";s:60:"http://a3.twimg.com/a/1296525272/images/themes/theme1/bg.png";s:23:"profile_background_tile";s:5:"false";s:28:"profile_use_background_image";s:4:"true";s:13:"notifications";s:5:"false";s:11:"geo_enabled";s:4:"true";s:8:"verified";s:5:"false";s:9:"following";s:5:"false";s:14:"statuses_count";s:1:"6";s:4:"lang";s:2:"en";s:20:"contributors_enabled";s:5:"false";s:19:"follow_request_sent";s:5:"false";s:12:"listed_count";s:1:"0";s:21:"show_all_inline_media";s:5:"false";s:13:"is_translator";s:5:"false";s:6:"status";O:16:"SimpleXMLElement":15:{s:10:"created_at";s:30:"Mon Jan 03 08:41:11 +0000 2011";s:2:"id";s:17:"21848534684794880";s:4:"text";s:53:"De Graaf & Partners wenst iedereen een creatief 2011!";s:6:"source";s:3:"web";s:9:"truncated";s:5:"false";s:9:"favorited";s:5:"false";s:21:"in_reply_to_status_id";O:16:"SimpleXMLElement":0:{}s:19:"in_reply_to_user_id";O:16:"SimpleXMLElement":0:{}s:23:"in_reply_to_screen_name";O:16:"SimpleXMLElement":0:{}s:13:"retweet_count";s:1:"0";s:9:"retweeted";s:5:"false";s:3:"geo";O:16:"SimpleXMLElement":0:{}s:11:"coordinates";O:16:"SimpleXMLElement":0:{}s:5:"place";O:16:"SimpleXMLElement":0:{}s:12:"contributors";O:16:"SimpleXMLElement":0:{}}}s:10:"�*�_errstr";N;}
How do I need to format the data, or how can I store the data better?
If all you want to store is their image url, then you just need their username, then use this:
http://img.tweetimag.es/i/oprah_o
where oprah_o is the username.
You don't need to use OAuth and Zend_Service_Twitter to do that. If you want to be able to get data from a non public feed, or publish tweets to their accounts, that's where you need Zend_Service_Twitter.
If you want to get data from that resultset, just do:
$result_obj = unserialize($token);