From 12f0bebc8abea7bf5ee9cfa52f7dd2bd090224e2 Mon Sep 17 00:00:00 2001 From: sebsel Date: Thu, 1 Jun 2017 14:48:45 +0200 Subject: [PATCH 1/7] add Facebook events --- composer.json | 3 +- composer.lock | 135 ++++++++++++++++++++++++---------- controllers/Parse.php | 1 + lib/XRay/Fetcher.php | 25 +++++++ lib/XRay/Formats/Facebook.php | 105 ++++++++++++++++++++++++++ lib/XRay/Parser.php | 4 + 6 files changed, 234 insertions(+), 39 deletions(-) create mode 100644 lib/XRay/Formats/Facebook.php diff --git a/composer.json b/composer.json index 94d6f6a..67247d2 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "dg/twitter-php": "3.6.*", "p3k/timezone": "*", "p3k/http": "0.1.*", - "cebe/markdown": "1.1.*" + "cebe/markdown": "1.1.*", + "facebook/graph-sdk": "^5.5" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 79cf6c0..bb40af9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "082e2019c20a5d611f58716d3f75a131", + "hash": "280d14580c259d2219f07c649ef3c613", + "content-hash": "c4305989e1c70ad69a6073d15652f52e", "packages": [ { "name": "cebe/markdown", @@ -64,7 +65,7 @@ "markdown", "markdown-extra" ], - "time": "2016-09-14T20:40:20+00:00" + "time": "2016-09-14 20:40:20" }, { "name": "dg/twitter-php", @@ -106,7 +107,7 @@ "oauth", "twitter" ], - "time": "2016-08-15T16:46:22+00:00" + "time": "2016-08-15 16:46:22" }, { "name": "ezyang/htmlpurifier", @@ -153,7 +154,65 @@ "keywords": [ "html" ], - "time": "2017-03-13T06:30:53+00:00" + "time": "2017-03-13 06:30:53" + }, + { + "name": "facebook/graph-sdk", + "version": "5.5.0", + "source": { + "type": "git", + "url": "https://github.com/facebook/php-graph-sdk.git", + "reference": "93d7dc87e55a541d2e27d38f3aed40abbffdf6e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facebook/php-graph-sdk/zipball/93d7dc87e55a541d2e27d38f3aed40abbffdf6e1", + "reference": "93d7dc87e55a541d2e27d38f3aed40abbffdf6e1", + "shasum": "" + }, + "require": { + "php": "^5.4|^7.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "~5.0", + "mockery/mockery": "~0.8", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "guzzlehttp/guzzle": "Allows for implementation of the Guzzle HTTP client", + "paragonie/random_compat": "Provides a better CSPRNG option in PHP 5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Facebook\\": "src/Facebook/" + }, + "files": [ + "src/Facebook/polyfills.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Facebook Platform" + ], + "authors": [ + { + "name": "Facebook", + "homepage": "https://github.com/facebook/php-graph-sdk/contributors" + } + ], + "description": "Facebook SDK for PHP", + "homepage": "https://github.com/facebook/php-graph-sdk", + "keywords": [ + "facebook", + "sdk" + ], + "time": "2017-04-20 14:15:02" }, { "name": "indieweb/link-rel-parser", @@ -199,7 +258,7 @@ "indieweb", "microformats2" ], - "time": "2017-01-11T17:14:49+00:00" + "time": "2017-01-11 17:14:49" }, { "name": "mf2/mf2", @@ -254,7 +313,7 @@ "parser", "semantic" ], - "time": "2017-05-27T15:27:47+00:00" + "time": "2017-05-27 15:27:47" }, { "name": "p3k/http", @@ -292,7 +351,7 @@ ], "description": "A simple wrapper API around the PHP curl functions", "homepage": "https://github.com/aaronpk/p3k-http", - "time": "2017-04-29T17:43:29+00:00" + "time": "2017-04-29 17:43:29" }, { "name": "p3k/timezone", @@ -327,7 +386,7 @@ "p3k", "timezone" ], - "time": "2017-01-12T17:30:08+00:00" + "time": "2017-01-12 17:30:08" } ], "packages-dev": [ @@ -383,7 +442,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2015-06-14 21:17:01" }, { "name": "ircmaxell/password-compat", @@ -425,7 +484,7 @@ "hashing", "password" ], - "time": "2014-11-20T16:49:30+00:00" + "time": "2014-11-20 16:49:30" }, { "name": "league/container", @@ -483,7 +542,7 @@ "injection", "league" ], - "time": "2015-04-05T17:14:48+00:00" + "time": "2015-04-05 17:14:48" }, { "name": "league/plates", @@ -538,7 +597,7 @@ "templating", "views" ], - "time": "2016-12-28T00:14:17+00:00" + "time": "2016-12-28 00:14:17" }, { "name": "league/route", @@ -596,7 +655,7 @@ "league", "route" ], - "time": "2015-09-11T07:40:31+00:00" + "time": "2015-09-11 07:40:31" }, { "name": "nikic/fast-route", @@ -639,7 +698,7 @@ "router", "routing" ], - "time": "2016-03-25T23:46:52+00:00" + "time": "2016-03-25 23:46:52" }, { "name": "phpdocumentor/reflection-common", @@ -693,7 +752,7 @@ "reflection", "static analysis" ], - "time": "2015-12-27T11:43:31+00:00" + "time": "2015-12-27 11:43:31" }, { "name": "phpdocumentor/reflection-docblock", @@ -738,7 +797,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-09-30T07:12:33+00:00" + "time": "2016-09-30 07:12:33" }, { "name": "phpdocumentor/type-resolver", @@ -785,7 +844,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2016-11-25T06:54:22+00:00" + "time": "2016-11-25 06:54:22" }, { "name": "phpspec/prophecy", @@ -848,7 +907,7 @@ "spy", "stub" ], - "time": "2017-03-02T20:05:34+00:00" + "time": "2017-03-02 20:05:34" }, { "name": "phpunit/php-code-coverage", @@ -910,7 +969,7 @@ "testing", "xunit" ], - "time": "2015-10-06T15:47:00+00:00" + "time": "2015-10-06 15:47:00" }, { "name": "phpunit/php-file-iterator", @@ -957,7 +1016,7 @@ "filesystem", "iterator" ], - "time": "2016-10-03T07:40:28+00:00" + "time": "2016-10-03 07:40:28" }, { "name": "phpunit/php-text-template", @@ -998,7 +1057,7 @@ "keywords": [ "template" ], - "time": "2015-06-21T13:50:34+00:00" + "time": "2015-06-21 13:50:34" }, { "name": "phpunit/php-timer", @@ -1047,7 +1106,7 @@ "keywords": [ "timer" ], - "time": "2017-02-26T11:10:40+00:00" + "time": "2017-02-26 11:10:40" }, { "name": "phpunit/php-token-stream", @@ -1096,7 +1155,7 @@ "keywords": [ "tokenizer" ], - "time": "2017-02-27T10:12:30+00:00" + "time": "2017-02-27 10:12:30" }, { "name": "phpunit/phpunit", @@ -1168,7 +1227,7 @@ "testing", "xunit" ], - "time": "2017-02-06T05:18:07+00:00" + "time": "2017-02-06 05:18:07" }, { "name": "phpunit/phpunit-mock-objects", @@ -1224,7 +1283,7 @@ "mock", "xunit" ], - "time": "2015-10-02T06:51:40+00:00" + "time": "2015-10-02 06:51:40" }, { "name": "sebastian/comparator", @@ -1288,7 +1347,7 @@ "compare", "equality" ], - "time": "2017-01-29T09:50:25+00:00" + "time": "2017-01-29 09:50:25" }, { "name": "sebastian/diff", @@ -1340,7 +1399,7 @@ "keywords": [ "diff" ], - "time": "2017-05-22T07:24:03+00:00" + "time": "2017-05-22 07:24:03" }, { "name": "sebastian/environment", @@ -1390,7 +1449,7 @@ "environment", "hhvm" ], - "time": "2016-08-18T05:49:44+00:00" + "time": "2016-08-18 05:49:44" }, { "name": "sebastian/exporter", @@ -1457,7 +1516,7 @@ "export", "exporter" ], - "time": "2016-06-17T09:04:28+00:00" + "time": "2016-06-17 09:04:28" }, { "name": "sebastian/global-state", @@ -1508,7 +1567,7 @@ "keywords": [ "global state" ], - "time": "2015-10-12T03:26:01+00:00" + "time": "2015-10-12 03:26:01" }, { "name": "sebastian/recursion-context", @@ -1561,7 +1620,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-10-03T07:41:43+00:00" + "time": "2016-10-03 07:41:43" }, { "name": "sebastian/version", @@ -1596,7 +1655,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21T13:59:46+00:00" + "time": "2015-06-21 13:59:46" }, { "name": "symfony/http-foundation", @@ -1651,7 +1710,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2017-05-01T14:31:55+00:00" + "time": "2017-05-01 14:31:55" }, { "name": "symfony/polyfill-mbstring", @@ -1710,7 +1769,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-php54", @@ -1768,7 +1827,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-php55", @@ -1824,7 +1883,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/yaml", @@ -1879,7 +1938,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-05-01T14:55:58+00:00" + "time": "2017-05-01 14:55:58" }, { "name": "webmozart/assert", @@ -1929,7 +1988,7 @@ "check", "validate" ], - "time": "2016-11-23T20:04:58+00:00" + "time": "2016-11-23 20:04:58" } ], "aliases": [], diff --git a/controllers/Parse.php b/controllers/Parse.php index ed6a753..46392f8 100644 --- a/controllers/Parse.php +++ b/controllers/Parse.php @@ -83,6 +83,7 @@ class Parse { $fields = [ 'twitter_api_key','twitter_api_secret','twitter_access_token','twitter_access_token_secret', + 'facebook_app_id', 'facebook_app_secret', 'github_access_token', 'token' ]; diff --git a/lib/XRay/Fetcher.php b/lib/XRay/Fetcher.php index 8139cf8..ec8e175 100644 --- a/lib/XRay/Fetcher.php +++ b/lib/XRay/Fetcher.php @@ -43,6 +43,11 @@ class Fetcher { return $this->_fetch_tweet($url, $opts); } + // Check if this is a Facebook URL and use the API + if(Formats\Facebook::matches_host($url)) { + return $this->_fetch_facebook($url, $opts); + } + // Transform the HTML GitHub URL into an GitHub API request and fetch the API response if(Formats\GitHub::matches_host($url)) { return $this->_fetch_github($url, $opts); @@ -155,6 +160,26 @@ class Fetcher { ]; } + private function _fetch_facebook($url, $opts) { + $fields = ['facebook_app_id','facebook_app_secret']; + $creds = []; + foreach($fields as $f) { + if(isset($opts[$f])) + $creds[$f] = $opts[$f]; + } + + if(count($creds) < 2) { + return [ + 'error_code' => 400, + 'error' => 'missing_parameters', + 'error_description' => 'Both Facebook credentials must be included in the request' + ]; + } + + // TODO: Question, should I do this like Twitter or like Github? + return Formats\Facebook::fetch($url, $creds); + } + private function _fetch_github($url, $opts) { $fields = ['github_access_token']; $creds = []; diff --git a/lib/XRay/Formats/Facebook.php b/lib/XRay/Formats/Facebook.php new file mode 100644 index 0000000..af1ca68 --- /dev/null +++ b/lib/XRay/Formats/Facebook.php @@ -0,0 +1,105 @@ + 'event', + 'url' => $url, + 'name' => $fbObject['name'], + 'start' => $fbObject['start_time'], + 'end' => $fbObject['end_time'], + 'summary' => $fbObject['description'], + 'location' => [ + $fbObject['place']['name'] + ] + ); + + return [ + 'data' => $event, + 'original' => $fbObject + ]; + } + } + + public static function fetch($url, $creds) { + + $parts = self::extract_url_parts($url); + + if(!$parts or $parts['api_uri'] == false) { + return [ + 'error' => 'unsupported_url', + 'error_description' => 'This Facebook URL is not supported', + 'error_code' => 400, + ]; + } + + $fb = new \Facebook\Facebook([ + 'app_id' => $creds['facebook_app_id'], + 'app_secret' => $creds['facebook_app_secret'], + 'default_graph_version' => 'v2.9', + ]); + + $fbApp = new \Facebook\FacebookApp($creds['facebook_app_id'], $creds['facebook_app_secret']); + $token = $fbApp->getAccessToken(); + + $request = new \Facebook\FacebookRequest($fbApp, $token, 'GET', $parts['api_uri']); + + try { + $response = $fb->getClient()->sendRequest($request); + } catch(\Facebook\Exceptions\FacebookResponseException $e) { + return [ + 'error' => 'facebook_graph_error', + 'error_description' => 'Graph returned an error: ' . $e->getMessage(), + 'error_code' => 400, + ]; + } catch(\Facebook\Exceptions\FacebookSDKException $e) { + return [ + 'error' => 'facebook_sdk_error', + 'error_description' => 'Facebook SDK returned an error: ' . $e->getMessage(), + 'error_code' => 400, + ]; + } + + return [ + 'code' => 200, + 'body' => $response->getDecodedBody(), + 'url' => $url + ]; + } + + private static function extract_url_parts($url) { + $response = false; + + if(preg_match('~https://(.*?).?facebook.com/([^/]+)/posts/(\d+)/?$~', $url, $match)) { + // TODO: how do we get these? + // $response['type'] = 'entry'; + // $response['api_uri'] = false; + + } elseif(preg_match('~https://(.*?).?facebook.com/events/(\d+)/?$~', $url, $match)) { + $response['type'] = 'event'; + $response['api_uri'] = '/'.$match[2]; + } + + return $response; + } +} diff --git a/lib/XRay/Parser.php b/lib/XRay/Parser.php index 639aba7..bf9f13a 100644 --- a/lib/XRay/Parser.php +++ b/lib/XRay/Parser.php @@ -30,6 +30,10 @@ class Parser { return Formats\Twitter::parse($body, $url); } + if(Formats\Facebook::matches($url)) { + return Formats\Facebook::parse($body, $url); + } + if(Formats\XKCD::matches($url)) { return Formats\XKCD::parse($body, $url); } From b3c081361030c336cf98aa7e5cac9aa3503427f5 Mon Sep 17 00:00:00 2001 From: sebsel Date: Sat, 24 Jun 2017 00:23:01 +0200 Subject: [PATCH 2/7] store location as h-card in refs --- lib/XRay/Formats/Facebook.php | 39 ++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/XRay/Formats/Facebook.php b/lib/XRay/Formats/Facebook.php index af1ca68..e6f7ee8 100644 --- a/lib/XRay/Formats/Facebook.php +++ b/lib/XRay/Formats/Facebook.php @@ -27,13 +27,42 @@ class Facebook extends Format { 'url' => $url, 'name' => $fbObject['name'], 'start' => $fbObject['start_time'], - 'end' => $fbObject['end_time'], - 'summary' => $fbObject['description'], - 'location' => [ - $fbObject['place']['name'] - ] + 'end' => $fbObject['end_time'] ); + if(isset($fbObject['description'])) $event['summary'] = $fbObject['description']; + + // Is the event linked to a Page? + if(isset($fbObject['place']['id'])) { + + $card = array( + 'type' => 'card', + 'url' => 'https://facebook.com/'.$fbObject['place']['id'], + 'name' => $fbObject['place']['name'] + ); + + if(isset($fbObject['place']['location'])) { + + $location = $fbObject['place']['location']; + + if(isset($location['zip'])) $card['postal-code'] = $location['zip']; + if(isset($location['city'])) $card['locality'] = $location['city']; + if(isset($location['state'])) $card['region'] = $location['state']; + if(isset($location['street'])) $card['adr'] = $location['street']; + if(isset($location['country'])) $card['country-name'] = $location['country']; + if(isset($location['latitude'])) $card['latitude'] = $location['latitude']; + if(isset($location['longitude'])) $card['longitude'] = $location['longitude']; + + } + + $event['location'] = $card['url']; + $event['refs'] = array($card); + + // If we only have a name, use that + } elseif(isset($fbObject['place']['name'])) { + $event['location'] = $fbObject['place']['name']; + } + return [ 'data' => $event, 'original' => $fbObject From 8c1ffe3f05a47935461b6fef52302e23ddb37b88 Mon Sep 17 00:00:00 2001 From: sebsel Date: Sat, 24 Jun 2017 01:10:39 +0200 Subject: [PATCH 3/7] fix proper mf2 names, cast lon/lat as string, optional end, array notation --- lib/XRay/Formats/Facebook.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/XRay/Formats/Facebook.php b/lib/XRay/Formats/Facebook.php index e6f7ee8..fb7daae 100644 --- a/lib/XRay/Formats/Facebook.php +++ b/lib/XRay/Formats/Facebook.php @@ -26,10 +26,10 @@ class Facebook extends Format { 'type' => 'event', 'url' => $url, 'name' => $fbObject['name'], - 'start' => $fbObject['start_time'], - 'end' => $fbObject['end_time'] + 'start' => $fbObject['start_time'] ); + if(isset($fbObject['end_time'])) $event['end'] = $fbObject['end_time']; if(isset($fbObject['description'])) $event['summary'] = $fbObject['description']; // Is the event linked to a Page? @@ -48,10 +48,10 @@ class Facebook extends Format { if(isset($location['zip'])) $card['postal-code'] = $location['zip']; if(isset($location['city'])) $card['locality'] = $location['city']; if(isset($location['state'])) $card['region'] = $location['state']; - if(isset($location['street'])) $card['adr'] = $location['street']; - if(isset($location['country'])) $card['country-name'] = $location['country']; - if(isset($location['latitude'])) $card['latitude'] = $location['latitude']; - if(isset($location['longitude'])) $card['longitude'] = $location['longitude']; + if(isset($location['street'])) $card['street-address'] = $location['street']; + if(isset($location['country'])) $card['country'] = $location['country']; + if(isset($location['latitude'])) $card['latitude'] = (string)$location['latitude']; + if(isset($location['longitude'])) $card['longitude'] = (string)$location['longitude']; } @@ -82,11 +82,11 @@ class Facebook extends Format { ]; } - $fb = new \Facebook\Facebook([ + $fb = new \Facebook\Facebook(array( 'app_id' => $creds['facebook_app_id'], 'app_secret' => $creds['facebook_app_secret'], 'default_graph_version' => 'v2.9', - ]); + )); $fbApp = new \Facebook\FacebookApp($creds['facebook_app_id'], $creds['facebook_app_secret']); $token = $fbApp->getAccessToken(); From 67c159ec290d7cc95541add4affdc34c911c5565 Mon Sep 17 00:00:00 2001 From: sebsel Date: Sat, 24 Jun 2017 01:10:46 +0200 Subject: [PATCH 4/7] added tests --- tests/FacebookTest.php | 65 +++++++++++++++++++ .../graph.facebook.com/v2.9_1596554663924436 | 17 +++++ .../graph.facebook.com/v2.9_446197069049722 | 17 +++++ 3 files changed, 99 insertions(+) create mode 100644 tests/FacebookTest.php create mode 100644 tests/data/graph.facebook.com/v2.9_1596554663924436 create mode 100644 tests/data/graph.facebook.com/v2.9_446197069049722 diff --git a/tests/FacebookTest.php b/tests/FacebookTest.php new file mode 100644 index 0000000..4f07530 --- /dev/null +++ b/tests/FacebookTest.php @@ -0,0 +1,65 @@ +client = new Parse(); + $this->client->http = new p3k\HTTP\Test(dirname(__FILE__).'/data/'); + $this->client->mc = null; + } + + private function parse($params) { + $request = new Request($params); + $response = new Response(); + return $this->client->parse($request, $response); + } + + public function testFacebookEventWithHCard() { + $url = 'https://www.facebook.com/events/446197069049722/'; + $response = $this->parse(['url' => $url]); + + $body = $response->getContent(); + $this->assertEquals(200, $response->getStatusCode()); + $data = json_decode($body, true); + $card = $data['data']['refs'][0]; + + $this->assertEquals('event', $data['data']['type']); + $this->assertEquals('IndieWeb Summit', $data['data']['name']); + $this->assertEquals('2017-06-24T09:00:00-0700', $data['data']['start']); + $this->assertEquals('2017-06-25T18:00:00-0700', $data['data']['end']); + $this->assertContains('The seventh annual gathering for independent web creators of all kinds,', $data['data']['summary']); + $this->assertEquals('https://facebook.com/332204056925945', $data['data']['location']); + + $this->assertEquals('card', $card['type']); + $this->assertEquals('https://facebook.com/332204056925945', $card['url']); + $this->assertEquals('Mozilla PDX', $card['name']); + $this->assertEquals('97209', $card['postal-code']); + $this->assertEquals('Portland', $card['locality']); + $this->assertEquals('OR', $card['region']); + $this->assertEquals('1120 NW Couch St, Ste 320', $card['street-address']); + $this->assertEquals('United States', $card['country']); + $this->assertEquals('45.5233192', $card['latitude']); + $this->assertEquals('-122.6824722', $card['longitude']); + } + + public function testFacebookEvent() { + $url = 'https://www.facebook.com/events/1596554663924436/'; + $response = $this->parse(['url' => $url]); + + $body = $response->getContent(); + $this->assertEquals(200, $response->getStatusCode()); + $data = json_decode($body, true); + $card = $data['data']['refs'][0]; + + $this->assertEquals('event', $data['data']['type']); + $this->assertEquals('Homebrew Website Club', $data['data']['name']); + $this->assertEquals('2015-04-22T19:00:00-0400', $data['data']['start']); + $this->assertContains('Are you building your own website? Indie reader?', $data['data']['summary']); + $this->assertEquals('Charging Bull - WeeWork - 25 Broadway, New York, NY 10004', $data['data']['location']); + } + +} diff --git a/tests/data/graph.facebook.com/v2.9_1596554663924436 b/tests/data/graph.facebook.com/v2.9_1596554663924436 new file mode 100644 index 0000000..f42dd20 --- /dev/null +++ b/tests/data/graph.facebook.com/v2.9_1596554663924436 @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +Access-Control-Allow-Origin: * +ETag: "dd72a86bf1349e65bf77eb6ea08dd8df8ead9100" +Pragma: no-cache +Cache-Control: private, no-cache, no-store, must-revalidate +x-fb-rev: 3113126 +Content-Type: application/json; charset=UTF-8 +x-fb-trace-id: GBOFw9pYkZY +facebook-api-version: v2.9 +Expires: Sat, 01 Jan 2000 00:00:00 GMT +Vary: Accept-Encoding +X-FB-Debug: J6wZdSjkKDtf5Tyc3RO0VzVdsjAOdRwB1q7NxbzSmkUGPheeqeYYQ4nSHCjHxFTGuqsXhLzdAt4UVnK56yvR8Q== +Date: Fri, 23 Jun 2017 22:59:59 GMT +Transfer-Encoding: chunked +Connection: keep-alive + +{"description":"Are you building your own website? Indie reader? Personal publishing web app? Or some other digital magic-cloud proxy? If so, come on by and join a gathering of people with like-minded interests. Bring your friends that want to start a personal web site. Exchange information, swap ideas, talk shop, help work on a project. \n\n Contact (914) 414-1775 when you arrive to be admitted. \n\n\nMore information: \u0040[NjQyMTgzOTU5MjA4MTA3Omh0dHBzXGEvL2luZGlld2ViY2FtcC5jb20vZXZlbnRzLzIwMTUtMDQtMjItaG9tZWJyZXctd2Vic2l0ZS1jbHViOjo=:\u0040[NjQyMTgzOTU5MjA4MTA3Omh0dHBzXGEvL2luZGlld2ViY2FtcC5jb20vZXZlbnRzLzIwMTUtMDQtMjItaG9tZWJyZXctd2Vic2l0ZS1jbHViOjo=:https:\/\/indiewebcamp.com\/events\/2015-04-22-homebrew-website-club]]","name":"Homebrew Website Club","place":{"name":"Charging Bull - WeeWork - 25 Broadway, New York, NY 10004"},"start_time":"2015-04-22T19:00:00-0400","id":"1596554663924436"} \ No newline at end of file diff --git a/tests/data/graph.facebook.com/v2.9_446197069049722 b/tests/data/graph.facebook.com/v2.9_446197069049722 new file mode 100644 index 0000000..2e0d904 --- /dev/null +++ b/tests/data/graph.facebook.com/v2.9_446197069049722 @@ -0,0 +1,17 @@ +HTTP/1.1 200 OK +Access-Control-Allow-Origin: * +ETag: "cf33fe9966e2a664781acc2043f226d67946b048" +Pragma: no-cache +Cache-Control: private, no-cache, no-store, must-revalidate +x-fb-rev: 3113126 +Content-Type: application/json; charset=UTF-8 +x-fb-trace-id: Ad/GSn3EwKN +facebook-api-version: v2.9 +Expires: Sat, 01 Jan 2000 00:00:00 GMT +Vary: Accept-Encoding +X-FB-Debug: 4p8L2dOeNMJ5Ay6meL3zW4mh9TZgCNUEOsNRDUS8twNTtlvnkHiHfqSuQBIyvTlppBpOG8sWF+2MNQT4LaENtg== +Date: Fri, 23 Jun 2017 22:57:08 GMT +Transfer-Encoding: chunked +Connection: keep-alive + +{"description":"The seventh annual gathering for independent web creators of all kinds, from graphic artists, to designers, UX engineers, coders, hackers, to share ideas, actively work on creating for their own personal websites, and build upon each others creations.\n\nIMPORTANT: Please register for a ticket from https:\/\/2017.indieweb.org\/ to attend! RSVPing on Facebook is not enough. Your registration will ensure that we get enough food for everyone!\n\nOriginally posted at https:\/\/aaronparecki.com\/2017\/06\/24\/1\/indieweb-summit","end_time":"2017-06-25T18:00:00-0700","name":"IndieWeb Summit","place":{"name":"Mozilla PDX","location":{"city":"Portland","country":"United States","latitude":45.5233192,"longitude":-122.6824722,"state":"OR","street":"1120 NW Couch St, Ste 320","zip":"97209"},"id":"332204056925945"},"start_time":"2017-06-24T09:00:00-0700","id":"446197069049722"} \ No newline at end of file From 6b286157e3e4126245ee3fe183382404a7387f45 Mon Sep 17 00:00:00 2001 From: sebsel Date: Sat, 24 Jun 2017 10:17:45 +0200 Subject: [PATCH 5/7] based tests on TwitterTest.php --- lib/XRay/Formats/Facebook.php | 1 + tests/FacebookTest.php | 26 ++++++++++--------- ...1596554663924436 => 1596554663924436.json} | 16 ------------ ...9_446197069049722 => 446197069049722.json} | 16 ------------ 4 files changed, 15 insertions(+), 44 deletions(-) rename tests/data/graph.facebook.com/{v2.9_1596554663924436 => 1596554663924436.json} (61%) rename tests/data/graph.facebook.com/{v2.9_446197069049722 => 446197069049722.json} (57%) diff --git a/lib/XRay/Formats/Facebook.php b/lib/XRay/Formats/Facebook.php index fb7daae..330b20e 100644 --- a/lib/XRay/Formats/Facebook.php +++ b/lib/XRay/Formats/Facebook.php @@ -17,6 +17,7 @@ class Facebook extends Format { } public static function parse($fbObject, $url) { + if(is_string($fbObject)) $fbObject = json_decode($fbObject, true); $parts = self::extract_url_parts($url); diff --git a/tests/FacebookTest.php b/tests/FacebookTest.php index 4f07530..002a5b8 100644 --- a/tests/FacebookTest.php +++ b/tests/FacebookTest.php @@ -8,24 +8,28 @@ class FacebookTest extends PHPUnit_Framework_TestCase { public function setUp() { $this->client = new Parse(); - $this->client->http = new p3k\HTTP\Test(dirname(__FILE__).'/data/'); $this->client->mc = null; } private function parse($params) { $request = new Request($params); $response = new Response(); - return $this->client->parse($request, $response); + + $result = $this->client->parse($request, $response); + $body = $result->getContent(); + $this->assertEquals(200, $result->getStatusCode()); + return json_decode($body, true); + } + + private function loadFbObject($id) { + return file_get_contents(dirname(__FILE__).'/data/graph.facebook.com/'.$id.'.json'); } public function testFacebookEventWithHCard() { $url = 'https://www.facebook.com/events/446197069049722/'; - $response = $this->parse(['url' => $url]); + $json = $this->loadFbObject('446197069049722'); - $body = $response->getContent(); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode($body, true); - $card = $data['data']['refs'][0]; + $data = $this->parse(['url' => $url, 'body' => $json]); $this->assertEquals('event', $data['data']['type']); $this->assertEquals('IndieWeb Summit', $data['data']['name']); @@ -34,6 +38,7 @@ class FacebookTest extends PHPUnit_Framework_TestCase { $this->assertContains('The seventh annual gathering for independent web creators of all kinds,', $data['data']['summary']); $this->assertEquals('https://facebook.com/332204056925945', $data['data']['location']); + $card = $data['data']['refs'][0]; $this->assertEquals('card', $card['type']); $this->assertEquals('https://facebook.com/332204056925945', $card['url']); $this->assertEquals('Mozilla PDX', $card['name']); @@ -48,12 +53,9 @@ class FacebookTest extends PHPUnit_Framework_TestCase { public function testFacebookEvent() { $url = 'https://www.facebook.com/events/1596554663924436/'; - $response = $this->parse(['url' => $url]); + $json = $this->loadFbObject('1596554663924436'); - $body = $response->getContent(); - $this->assertEquals(200, $response->getStatusCode()); - $data = json_decode($body, true); - $card = $data['data']['refs'][0]; + $data = $this->parse(['url' => $url, 'body' => $json]); $this->assertEquals('event', $data['data']['type']); $this->assertEquals('Homebrew Website Club', $data['data']['name']); diff --git a/tests/data/graph.facebook.com/v2.9_1596554663924436 b/tests/data/graph.facebook.com/1596554663924436.json similarity index 61% rename from tests/data/graph.facebook.com/v2.9_1596554663924436 rename to tests/data/graph.facebook.com/1596554663924436.json index f42dd20..aeb5f61 100644 --- a/tests/data/graph.facebook.com/v2.9_1596554663924436 +++ b/tests/data/graph.facebook.com/1596554663924436.json @@ -1,17 +1 @@ -HTTP/1.1 200 OK -Access-Control-Allow-Origin: * -ETag: "dd72a86bf1349e65bf77eb6ea08dd8df8ead9100" -Pragma: no-cache -Cache-Control: private, no-cache, no-store, must-revalidate -x-fb-rev: 3113126 -Content-Type: application/json; charset=UTF-8 -x-fb-trace-id: GBOFw9pYkZY -facebook-api-version: v2.9 -Expires: Sat, 01 Jan 2000 00:00:00 GMT -Vary: Accept-Encoding -X-FB-Debug: J6wZdSjkKDtf5Tyc3RO0VzVdsjAOdRwB1q7NxbzSmkUGPheeqeYYQ4nSHCjHxFTGuqsXhLzdAt4UVnK56yvR8Q== -Date: Fri, 23 Jun 2017 22:59:59 GMT -Transfer-Encoding: chunked -Connection: keep-alive - {"description":"Are you building your own website? Indie reader? Personal publishing web app? Or some other digital magic-cloud proxy? If so, come on by and join a gathering of people with like-minded interests. Bring your friends that want to start a personal web site. Exchange information, swap ideas, talk shop, help work on a project. \n\n Contact (914) 414-1775 when you arrive to be admitted. \n\n\nMore information: \u0040[NjQyMTgzOTU5MjA4MTA3Omh0dHBzXGEvL2luZGlld2ViY2FtcC5jb20vZXZlbnRzLzIwMTUtMDQtMjItaG9tZWJyZXctd2Vic2l0ZS1jbHViOjo=:\u0040[NjQyMTgzOTU5MjA4MTA3Omh0dHBzXGEvL2luZGlld2ViY2FtcC5jb20vZXZlbnRzLzIwMTUtMDQtMjItaG9tZWJyZXctd2Vic2l0ZS1jbHViOjo=:https:\/\/indiewebcamp.com\/events\/2015-04-22-homebrew-website-club]]","name":"Homebrew Website Club","place":{"name":"Charging Bull - WeeWork - 25 Broadway, New York, NY 10004"},"start_time":"2015-04-22T19:00:00-0400","id":"1596554663924436"} \ No newline at end of file diff --git a/tests/data/graph.facebook.com/v2.9_446197069049722 b/tests/data/graph.facebook.com/446197069049722.json similarity index 57% rename from tests/data/graph.facebook.com/v2.9_446197069049722 rename to tests/data/graph.facebook.com/446197069049722.json index 2e0d904..7818a4d 100644 --- a/tests/data/graph.facebook.com/v2.9_446197069049722 +++ b/tests/data/graph.facebook.com/446197069049722.json @@ -1,17 +1 @@ -HTTP/1.1 200 OK -Access-Control-Allow-Origin: * -ETag: "cf33fe9966e2a664781acc2043f226d67946b048" -Pragma: no-cache -Cache-Control: private, no-cache, no-store, must-revalidate -x-fb-rev: 3113126 -Content-Type: application/json; charset=UTF-8 -x-fb-trace-id: Ad/GSn3EwKN -facebook-api-version: v2.9 -Expires: Sat, 01 Jan 2000 00:00:00 GMT -Vary: Accept-Encoding -X-FB-Debug: 4p8L2dOeNMJ5Ay6meL3zW4mh9TZgCNUEOsNRDUS8twNTtlvnkHiHfqSuQBIyvTlppBpOG8sWF+2MNQT4LaENtg== -Date: Fri, 23 Jun 2017 22:57:08 GMT -Transfer-Encoding: chunked -Connection: keep-alive - {"description":"The seventh annual gathering for independent web creators of all kinds, from graphic artists, to designers, UX engineers, coders, hackers, to share ideas, actively work on creating for their own personal websites, and build upon each others creations.\n\nIMPORTANT: Please register for a ticket from https:\/\/2017.indieweb.org\/ to attend! RSVPing on Facebook is not enough. Your registration will ensure that we get enough food for everyone!\n\nOriginally posted at https:\/\/aaronparecki.com\/2017\/06\/24\/1\/indieweb-summit","end_time":"2017-06-25T18:00:00-0700","name":"IndieWeb Summit","place":{"name":"Mozilla PDX","location":{"city":"Portland","country":"United States","latitude":45.5233192,"longitude":-122.6824722,"state":"OR","street":"1120 NW Couch St, Ste 320","zip":"97209"},"id":"332204056925945"},"start_time":"2017-06-24T09:00:00-0700","id":"446197069049722"} \ No newline at end of file From 19f0fd3efe8bbfbb709d7f65a4120f3053ab13c7 Mon Sep 17 00:00:00 2001 From: sebsel Date: Sat, 24 Jun 2017 10:42:10 +0200 Subject: [PATCH 6/7] added Facebook to the docs --- README.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b5d778d..49b6662 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ The contents of the URL is checked in the following order: * Twitter * GitHub * XKCD + * Facebook (public events) * (more coming soon) * Microformats * h-card @@ -158,7 +159,7 @@ url=https://aaronparecki.com/2016/01/16/11/ &body=.... ``` -or for Twitter/GitHub where you might have JSON, +or for Twitter/GitHub/Facebook where you might have JSON, ``` POST /parse @@ -180,11 +181,17 @@ url=https://aaronparecki.com/2016/01/16/11/ &token=12341234123412341234 ``` + ### Twitter Authentication -XRay uses the Twitter API to fetch posts, and the Twitter API requires authentication. In order to keep XRay stateless, it is required that you pass in Twitter credentials to the parse call. You can register an application on the Twitter developer website, and generate an access token for your account without writing any code, and then use those credentials when making an API request to XRay. +XRay uses the Twitter, Github and Facebook APIs to fetch posts, and those API require authentication. In order to keep XRay stateless, it is required that you pass in the credentials to the parse call. + +You should only send the credentials when the URL you are trying to parse is a Twitter URL, a GitHub URL or a Facebook URL, so you'll want to check for whether the hostname is `twitter.com`, `github.com`, etc. before you include credentials in this call. + -You should only send Twitter credentials when the URL you are trying to parse is a Twitter URL, so you'll want to check for whether the hostname is `twitter.com` before you include credentials in this call. +#### Twitter Authentication + +XRay uses the Twitter API to fetch Twitter URLs. You can register an application on the Twitter developer website, and generate an access token for your account without writing any code, and then use those credentials when making an API request to XRay. * twitter_api_key - Your application's API key * twitter_api_secret - Your application's API secret @@ -192,13 +199,23 @@ You should only send Twitter credentials when the URL you are trying to parse is * twitter_access_token_secret - Your Twitter secret access token -### GitHub Authentication +#### GitHub Authentication XRay uses the GitHub API to fetch GitHub URLs, which provides higher rate limits when used with authentication. You can pass a GitHub access token along with the request and XRay will use it when making requests to the API. * github_access_token - A GitHub access token +#### Facebook Authentication + +XRay uses the Facebook API to fetch Facebook URLs. You can create a Facebook App on Facebooks developer website. + +* facebook_app_id - Your application's App ID +* facebook_app_secret - Your application's App Secret + +At this moment, XRay is able to get it's own access token from those credentials. + + ### Error Response ```json @@ -254,7 +271,7 @@ Possible errors are listed below: } ``` -#### Primary Data +#### Primary Data The primary object on the page is returned in the `data` property. This will indicate the type of object (e.g. `entry`), and will contain the vocabulary's properties that it was able to parse from the page. From f688d79ef720df0395efd175156fc44b1032cdf0 Mon Sep 17 00:00:00 2001 From: sebsel Date: Sat, 24 Jun 2017 10:43:39 +0200 Subject: [PATCH 7/7] oops --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 49b6662..e959215 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ url=https://aaronparecki.com/2016/01/16/11/ ``` -### Twitter Authentication +### API Authentication XRay uses the Twitter, Github and Facebook APIs to fetch posts, and those API require authentication. In order to keep XRay stateless, it is required that you pass in the credentials to the parse call.