Browse Source

Merge branch 'sebsel-master'

pull/49/head
Aaron Parecki 4 years ago
parent
commit
796defb389
No known key found for this signature in database GPG Key ID: 276C2817346D6056
10 changed files with 387 additions and 77 deletions
  1. +23
    -6
      README.md
  2. +2
    -1
      composer.json
  3. +128
    -70
      composer.lock
  4. +1
    -0
      controllers/Parse.php
  5. +25
    -0
      lib/XRay/Fetcher.php
  6. +135
    -0
      lib/XRay/Formats/Facebook.php
  7. +4
    -0
      lib/XRay/Parser.php
  8. +67
    -0
      tests/FacebookTest.php
  9. +1
    -0
      tests/data/graph.facebook.com/1596554663924436.json
  10. +1
    -0
      tests/data/graph.facebook.com/446197069049722.json

+ 23
- 6
README.md View File

@ -13,6 +13,7 @@ XRay will parse content in the following formats. First the URL is checked again
* GitHub
* XKCD
* Hackernews
* Facebook (public events)
If the contents of the URL is XML or JSON, then XRay will parse the Atom, RSS or JSONFeed formats.
@ -165,7 +166,7 @@ url=https://aaronparecki.com/2016/01/16/11/
&body=<html>....</html>
```
or for Twitter/GitHub where you might have JSON,
or for Twitter/GitHub/Facebook where you might have JSON,
```
POST /parse
@ -199,11 +200,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.
### API Authentication
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.
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.
#### 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
@ -211,13 +218,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
@ -273,7 +290,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.

+ 2
- 1
composer.json View File

@ -12,7 +12,8 @@
"p3k/timezone": "*",
"p3k/http": "0.1.*",
"cebe/markdown": "1.1.*",
"miniflux/picofeed": "^0.1.37"
"miniflux/picofeed": "^0.1.37",
"facebook/graph-sdk": "^5.5"
},
"autoload": {
"psr-4": {

+ 128
- 70
composer.lock View File

@ -4,20 +4,20 @@
"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": "4cacb7d73963baec7b3fbc3248e43833",
"content-hash": "a1330f39bf5204a5cc6bdd1222915639",
"packages": [
{
"name": "cebe/markdown",
"version": "1.1.1",
"version": "1.1.2",
"source": {
"type": "git",
"url": "https://github.com/cebe/markdown.git",
"reference": "c30eb5e01fe021cc5bba2f9ee0eeef96d4931166"
"reference": "25b28bae8a6f185b5030673af77b32e1163d5c6e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cebe/markdown/zipball/c30eb5e01fe021cc5bba2f9ee0eeef96d4931166",
"reference": "c30eb5e01fe021cc5bba2f9ee0eeef96d4931166",
"url": "https://api.github.com/repos/cebe/markdown/zipball/25b28bae8a6f185b5030673af77b32e1163d5c6e",
"reference": "25b28bae8a6f185b5030673af77b32e1163d5c6e",
"shasum": ""
},
"require": {
@ -64,7 +64,7 @@
"markdown",
"markdown-extra"
],
"time": "2016-09-14T20:40:20+00:00"
"time": "2017-07-16T21:13:23+00:00"
},
{
"name": "dg/twitter-php",
@ -110,16 +110,16 @@
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.9.2",
"version": "v4.9.3",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "6d50e5282afdfdfc3e0ff6d192aff56c5629b3d4"
"reference": "95e1bae3182efc0f3422896a3236e991049dac69"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/6d50e5282afdfdfc3e0ff6d192aff56c5629b3d4",
"reference": "6d50e5282afdfdfc3e0ff6d192aff56c5629b3d4",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/95e1bae3182efc0f3422896a3236e991049dac69",
"reference": "95e1bae3182efc0f3422896a3236e991049dac69",
"shasum": ""
},
"require": {
@ -153,7 +153,65 @@
"keywords": [
"html"
],
"time": "2017-03-13T06:30:53+00:00"
"time": "2017-06-03T02:28:16+00:00"
},
{
"name": "facebook/graph-sdk",
"version": "5.6.1",
"source": {
"type": "git",
"url": "https://github.com/facebook/php-graph-sdk.git",
"reference": "2f9639c15ae043911f40ffe44080b32bac2c5280"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/facebook/php-graph-sdk/zipball/2f9639c15ae043911f40ffe44080b32bac2c5280",
"reference": "2f9639c15ae043911f40ffe44080b32bac2c5280",
"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-08-16T17:28:07+00:00"
},
{
"name": "indieweb/link-rel-parser",
@ -741,16 +799,16 @@
},
{
"name": "phpdocumentor/reflection-common",
"version": "1.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c"
"reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
"reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
"reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
"shasum": ""
},
"require": {
@ -791,26 +849,26 @@
"reflection",
"static analysis"
],
"time": "2015-12-27T11:43:31+00:00"
"time": "2017-09-11T18:02:19+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "3.1.1",
"version": "3.2.2",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e"
"reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e",
"reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/4aada1f93c72c35e22fb1383b47fee43b8f1d157",
"reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157",
"shasum": ""
},
"require": {
"php": ">=5.5",
"phpdocumentor/reflection-common": "^1.0@dev",
"phpdocumentor/type-resolver": "^0.2.0",
"phpdocumentor/type-resolver": "^0.3.0",
"webmozart/assert": "^1.0"
},
"require-dev": {
@ -836,24 +894,24 @@
}
],
"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": "2017-08-08T06:39:58+00:00"
},
{
"name": "phpdocumentor/type-resolver",
"version": "0.2.1",
"version": "0.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb"
"reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
"reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/fb3933512008d8162b3cdf9e18dba9309b7c3773",
"reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773",
"shasum": ""
},
"require": {
"php": ">=5.5",
"php": "^5.5 || ^7.0",
"phpdocumentor/reflection-common": "^1.0"
},
"require-dev": {
@ -883,26 +941,26 @@
"email": "me@mikevanriel.com"
}
],
"time": "2016-11-25T06:54:22+00:00"
"time": "2017-06-03T08:32:36+00:00"
},
{
"name": "phpspec/prophecy",
"version": "v1.7.0",
"version": "v1.7.2",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "93d39f1f7f9326d746203c7c056f300f7f126073"
"reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073",
"reference": "93d39f1f7f9326d746203c7c056f300f7f126073",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6",
"reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
"sebastian/comparator": "^1.1|^2.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
},
@ -913,7 +971,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6.x-dev"
"dev-master": "1.7.x-dev"
}
},
"autoload": {
@ -946,7 +1004,7 @@
"spy",
"stub"
],
"time": "2017-03-02T20:05:34+00:00"
"time": "2017-09-04T11:05:03+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -1198,16 +1256,16 @@
},
{
"name": "phpunit/phpunit",
"version": "4.8.35",
"version": "4.8.36",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87"
"reference": "46023de9a91eec7dfb06cc56cb4e260017298517"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/791b1a67c25af50e230f841ee7a9c6eba507dc87",
"reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517",
"reference": "46023de9a91eec7dfb06cc56cb4e260017298517",
"shasum": ""
},
"require": {
@ -1266,7 +1324,7 @@
"testing",
"xunit"
],
"time": "2017-02-06T05:18:07+00:00"
"time": "2017-06-21T08:07:12+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -1698,16 +1756,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v2.8.20",
"version": "v2.8.29",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "cfa9013809ad18514855144d14bb321cf4673561"
"reference": "3aae75693835239d3a640efe3b5572dcd2d2abf3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/cfa9013809ad18514855144d14bb321cf4673561",
"reference": "cfa9013809ad18514855144d14bb321cf4673561",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/3aae75693835239d3a640efe3b5572dcd2d2abf3",
"reference": "3aae75693835239d3a640efe3b5572dcd2d2abf3",
"shasum": ""
},
"require": {
@ -1749,20 +1807,20 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
"time": "2017-05-01T14:31:55+00:00"
"time": "2017-11-05T19:06:07+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.3.0",
"version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "e79d363049d1c2128f133a2667e4f4190904f7f4"
"reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4",
"reference": "e79d363049d1c2128f133a2667e4f4190904f7f4",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
"reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
"shasum": ""
},
"require": {
@ -1774,7 +1832,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
"dev-master": "1.6-dev"
}
},
"autoload": {
@ -1808,20 +1866,20 @@
"portable",
"shim"
],
"time": "2016-11-14T01:06:16+00:00"
"time": "2017-10-11T12:05:26+00:00"
},
{
"name": "symfony/polyfill-php54",
"version": "v1.3.0",
"version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php54.git",
"reference": "90e085822963fdcc9d1c5b73deb3d2e5783b16a0"
"reference": "d7810a14b2c6c1aff415e1bb755f611b3d5327bc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/90e085822963fdcc9d1c5b73deb3d2e5783b16a0",
"reference": "90e085822963fdcc9d1c5b73deb3d2e5783b16a0",
"url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/d7810a14b2c6c1aff415e1bb755f611b3d5327bc",
"reference": "d7810a14b2c6c1aff415e1bb755f611b3d5327bc",
"shasum": ""
},
"require": {
@ -1830,7 +1888,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
"dev-master": "1.6-dev"
}
},
"autoload": {
@ -1866,20 +1924,20 @@
"portable",
"shim"
],
"time": "2016-11-14T01:06:16+00:00"
"time": "2017-10-11T12:05:26+00:00"
},
{
"name": "symfony/polyfill-php55",
"version": "v1.3.0",
"version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php55.git",
"reference": "03e3f0350bca2220e3623a0e340eef194405fc67"
"reference": "b64e7f0c37ecf144ecc16668936eef94e628fbfd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/03e3f0350bca2220e3623a0e340eef194405fc67",
"reference": "03e3f0350bca2220e3623a0e340eef194405fc67",
"url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/b64e7f0c37ecf144ecc16668936eef94e628fbfd",
"reference": "b64e7f0c37ecf144ecc16668936eef94e628fbfd",
"shasum": ""
},
"require": {
@ -1889,7 +1947,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
"dev-master": "1.6-dev"
}
},
"autoload": {
@ -1922,24 +1980,24 @@
"portable",
"shim"
],
"time": "2016-11-14T01:06:16+00:00"
"time": "2017-10-11T12:05:26+00:00"
},
{
"name": "symfony/yaml",
"version": "v3.2.8",
"version": "v3.3.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6"
"reference": "0938408c4faa518d95230deabb5f595bf0de31b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/acec26fcf7f3031e094e910b94b002fa53d4e4d6",
"reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6",
"url": "https://api.github.com/repos/symfony/yaml/zipball/0938408c4faa518d95230deabb5f595bf0de31b9",
"reference": "0938408c4faa518d95230deabb5f595bf0de31b9",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
"php": "^5.5.9|>=7.0.8"
},
"require-dev": {
"symfony/console": "~2.8|~3.0"
@ -1950,7 +2008,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
"dev-master": "3.3-dev"
}
},
"autoload": {
@ -1977,7 +2035,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2017-05-01T14:55:58+00:00"
"time": "2017-11-10T18:26:04+00:00"
},
{
"name": "webmozart/assert",

+ 1
- 0
controllers/Parse.php View File

@ -87,6 +87,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'
];

+ 25
- 0
lib/XRay/Fetcher.php View File

@ -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);
@ -148,6 +153,26 @@ class Fetcher {
return Formats\Twitter::fetch($url, $creds);
}
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 = [];

+ 135
- 0
lib/XRay/Formats/Facebook.php View File

@ -0,0 +1,135 @@
<?php
namespace p3k\XRay\Formats;
use DOMDocument, DOMXPath;
use DateTime, DateTimeZone;
class Facebook extends Format {
public static function matches_host($url) {
$host = parse_url($url, PHP_URL_HOST);
// TODO: match hosts like 'm.facebook.com' and 'mbasic.facebook.com'
return in_array($host, ['www.facebook.com','facebook.com']);
}
public static function matches($url) {
return self::matches_host($url);
}
public static function parse($fbObject, $url) {
if(is_string($fbObject)) $fbObject = json_decode($fbObject, true);
$parts = self::extract_url_parts($url);
if($parts['type'] == 'event') {
$event = array(
'type' => 'event',
'url' => $url,
'name' => $fbObject['name'],
'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?
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['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'];
}
$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
];
}
}
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(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();
$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;
}
}

+ 4
- 0
lib/XRay/Parser.php View File

@ -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);
}

+ 67
- 0
tests/FacebookTest.php View File

@ -0,0 +1,67 @@
<?php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class FacebookTest extends PHPUnit_Framework_TestCase {
private $http;
public function setUp() {
$this->client = new Parse();
$this->client->mc = null;
}
private function parse($params) {
$request = new Request($params);
$response = new 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/';
$json = $this->loadFbObject('446197069049722');
$data = $this->parse(['url' => $url, 'body' => $json]);
$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']);
$card = $data['data']['refs'][0];
$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/';
$json = $this->loadFbObject('1596554663924436');
$data = $this->parse(['url' => $url, 'body' => $json]);
$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']);
}
}

+ 1
- 0
tests/data/graph.facebook.com/1596554663924436.json View File

@ -0,0 +1 @@
{"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"}

+ 1
- 0
tests/data/graph.facebook.com/446197069049722.json View File

@ -0,0 +1 @@
{"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"}

Loading…
Cancel
Save