Browse Source

Support for "likes" and adding "settings" page

* Supports a bookmarklet to create "like" posts.
* Beginning a "settings" page to connect silo profiles for POSSEing likes to twitter, facebook and instagram
pull/5/head
Aaron Parecki 10 years ago
parent
commit
2cd148c792
  1. 3
      composer.json
  2. 67
      composer.lock
  3. 160
      controllers/controllers.php
  4. 7
      lib/config.template.php
  5. 20
      lib/helpers.php
  6. 17
      public/css/favorite.css
  7. BIN
      public/images/quill-logo-1024.png
  8. 8
      public/images/red-x.svg
  9. 8
      public/images/star.svg
  10. 23
      public/js/fav.js
  11. 8
      views/layout.php
  12. 22
      views/liked-js.php
  13. 20
      views/partials/fb-script.php
  14. 88
      views/settings.php

3
composer.json

@ -8,7 +8,8 @@
"indieweb/date-formatter": "0.1.*", "indieweb/date-formatter": "0.1.*",
"indieauth/client": "0.1.3", "indieauth/client": "0.1.3",
"mpratt/relativetime": ">=1.0", "mpratt/relativetime": ">=1.0",
"firebase/php-jwt": "dev-master"
"firebase/php-jwt": "dev-master",
"ruudk/twitter-oauth": "dev-master"
}, },
"autoload": { "autoload": {
"files": [ "files": [

67
composer.lock

@ -3,7 +3,7 @@
"This file locks the dependencies of your project to a known state", "This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
], ],
"hash": "3e034e0a6a692d5bbfecfdc95ee69db2",
"hash": "502847c033f5a54c69a6a1a51d26e894",
"packages": [ "packages": [
{ {
"name": "firebase/php-jwt", "name": "firebase/php-jwt",
@ -12,12 +12,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/firebase/php-jwt.git", "url": "https://github.com/firebase/php-jwt.git",
"reference": "53669d621149e49c2a428722a62acfef3342c260"
"reference": "83b8899cb73d85d648af93f37ec0ac89f4a5bbae"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/53669d621149e49c2a428722a62acfef3342c260",
"reference": "53669d621149e49c2a428722a62acfef3342c260",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/83b8899cb73d85d648af93f37ec0ac89f4a5bbae",
"reference": "83b8899cb73d85d648af93f37ec0ac89f4a5bbae",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -26,7 +26,8 @@
"type": "library", "type": "library",
"autoload": { "autoload": {
"classmap": [ "classmap": [
"Authentication/"
"Authentication/",
"Exceptions/"
] ]
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
@ -47,7 +48,7 @@
], ],
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", "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", "homepage": "https://github.com/firebase/php-jwt",
"time": "2013-09-03 20:55:18"
"time": "2014-11-18 17:58:25"
}, },
{ {
"name": "indieauth/client", "name": "indieauth/client",
@ -367,6 +368,41 @@
], ],
"time": "2013-09-23 22:51:48" "time": "2013-09-23 22:51:48"
}, },
{
"name": "ruudk/twitter-oauth",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/ruudk/twitteroauth.git",
"reference": "7f5a94eaa1572ddbc7f0a32ba3b865b8ac23712a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ruudk/twitteroauth/zipball/7f5a94eaa1572ddbc7f0a32ba3b865b8ac23712a",
"reference": "7f5a94eaa1572ddbc7f0a32ba3b865b8ac23712a",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"classmap": [
"src"
]
},
"notification-url": "https://packagist.org/downloads/",
"authors": [
{
"name": "Ruud Kamphuis",
"email": "ruud@1plus1media.nl",
"role": "Developer"
}
],
"description": "PHP 5.3 version of abraham/twitteroauth",
"homepage": "http://github.com/ruudk/twitteroauth",
"time": "2014-06-10 18:17:38"
},
{ {
"name": "saltybeagle/savant3", "name": "saltybeagle/savant3",
"version": "dev-master", "version": "dev-master",
@ -446,21 +482,14 @@
"time": "2012-12-13 02:15:50" "time": "2012-12-13 02:15:50"
} }
], ],
"packages-dev": [
],
"aliases": [
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": { "stability-flags": {
"saltybeagle/savant3": 20, "saltybeagle/savant3": 20,
"firebase/php-jwt": 20
"firebase/php-jwt": 20,
"ruudk/twitter-oauth": 20
}, },
"platform": [
],
"platform-dev": [
]
"platform": [],
"platform-dev": []
} }

160
controllers/controllers.php

@ -1,6 +1,6 @@
<?php <?php
function require_login(&$app) {
function require_login(&$app, $redirect=true) {
$params = $app->request()->params(); $params = $app->request()->params();
if(array_key_exists('token', $params)) { if(array_key_exists('token', $params)) {
try { try {
@ -8,15 +8,24 @@ function require_login(&$app) {
$_SESSION['user_id'] = $data->user_id; $_SESSION['user_id'] = $data->user_id;
$_SESSION['me'] = $data->me; $_SESSION['me'] = $data->me;
} catch(DomainException $e) { } catch(DomainException $e) {
if($redirect) {
header('X-Error: DomainException'); header('X-Error: DomainException');
$app->redirect('/', 301); $app->redirect('/', 301);
} else {
return false;
}
} catch(UnexpectedValueException $e) { } catch(UnexpectedValueException $e) {
if($redirect) {
header('X-Error: UnexpectedValueException'); header('X-Error: UnexpectedValueException');
$app->redirect('/', 301); $app->redirect('/', 301);
} else {
return false;
}
} }
} }
if(!array_key_exists('user_id', $_SESSION)) { if(!array_key_exists('user_id', $_SESSION)) {
if($redirect)
$app->redirect('/'); $app->redirect('/');
return false; return false;
} else { } else {
@ -160,6 +169,42 @@ $app->get('/add-to-home', function() use($app) {
} }
}); });
$app->get('/settings', function() use($app) {
if($user=require_login($app)) {
$html = render('settings', array('title' => 'Settings', 'include_facebook' => true));
$app->response()->body($html);
}
});
$app->get('/favorite.js', function() use($app) {
$app->response()->header("Content-type", "text/javascript");
if($user=require_login($app, false)) {
$params = $app->request()->params();
if(array_key_exists('url', $params)) {
$micropub_request = array(
'like-of' => $params['url']
);
$r = micropub_post_for_user($user, $micropub_request);
}
if(preg_match('/https?:\/\/(?:www\.)?facebook\.com\/(?:[^\/]+)\/posts\/(\d+)/', $params['url'], $match)) {
$facebook_id = $match[1];
} else {
$facebook_id = false;
}
$app->response()->body($app->render('liked-js.php', array(
'url' => $params['url'],
'like_url' => $r['location'],
'error' => $r['error'],
'facebook_id' => $facebook_id
)));
} else {
$app->response()->body('alert("invalid token");');
}
});
$app->get('/micropub/syndications', function() use($app) { $app->get('/micropub/syndications', function() use($app) {
if($user=require_login($app)) { if($user=require_login($app)) {
$data = get_syndication_targets($user); $data = get_syndication_targets($user);
@ -179,31 +224,112 @@ $app->post('/micropub/post', function() use($app) {
return $v !== ''; return $v !== '';
}); });
// Now send to the micropub endpoint
$r = micropub_post($user->micropub_endpoint, $params, $user->micropub_access_token);
$request = $r['request'];
$response = $r['response'];
$r = micropub_post_for_user($user, $params);
$user->last_micropub_response = json_encode($r);
$user->last_micropub_response_date = date('Y-m-d H:i:s');
$app->response()->body(json_encode(array(
'request' => htmlspecialchars($r['request']),
'response' => htmlspecialchars($r['response']),
'location' => $r['location'],
'error' => $r['error'],
'curlinfo' => $r['curlinfo']
)));
}
});
// Check the response and look for a "Location" header containing the URL
if($response && preg_match('/Location: (.+)/', $response, $match)) {
$location = $match[1];
$user->micropub_success = 1;
$app->post('/auth/facebook', function() use($app) {
if($user=require_login($app, false)) {
$params = $app->request()->params();
// User just auth'd with facebook, store the access token
$user->facebook_access_token = $params['fb_token'];
$user->save();
$app->response()->body(json_encode(array(
'result' => 'ok'
)));
} else { } else {
$location = false;
$app->response()->body(json_encode(array(
'result' => 'error'
)));
} }
});
$app->post('/auth/twitter', function() use($app) {
if($user=require_login($app, false)) {
$params = $app->request()->params();
// User just auth'd with facebook, store the access token
$user->twitter_access_token = $params['twitter_token'];
$user->twitter_token_secret = $params['twitter_secret'];
$user->save(); $user->save();
$app->response()->body(json_encode(array( $app->response()->body(json_encode(array(
'request' => htmlspecialchars($request),
'response' => htmlspecialchars($response),
'location' => $location,
'error' => $r['error'],
'curlinfo' => $r['curlinfo']
'result' => 'ok'
)));
} else {
$app->response()->body(json_encode(array(
'result' => 'error'
))); )));
} }
}); });
function getTwitterLoginURL(&$twitter) {
$request_token = $twitter->getRequestToken(Config::$base_url . 'auth/twitter/callback');
$_SESSION['twitter_auth'] = $request_token;
return $twitter->getAuthorizeURL($request_token['oauth_token']);
}
$app->get('/auth/twitter', function() use($app) {
$params = $app->request()->params();
if($user=require_login($app, false)) {
// If there is an existing Twitter token, check if it is valid
// Otherwise, generate a Twitter login link
$twitter_login_url = false;
$twitter = new \TwitterOAuth\Api(Config::$twitterClientID, Config::$twitterClientSecret,
$user->twitter_access_token, $user->twitter_token_secret);
if(array_key_exists('login', $params)) {
$twitter = new \TwitterOAuth\Api(Config::$twitterClientID, Config::$twitterClientSecret);
$twitter_login_url = getTwitterLoginURL($twitter);
} else {
if($user->twitter_access_token) {
if ($twitter->get('account/verify_credentials')) {
$app->response()->body(json_encode(array(
'result' => 'ok'
)));
return;
} else {
// If the existing twitter token is not valid, generate a login link
$twitter_login_url = getTwitterLoginURL($twitter);
}
} else {
$twitter_login_url = getTwitterLoginURL($twitter);
}
}
$app->response()->body(json_encode(array(
'url' => $twitter_login_url
)));
} else {
$app->response()->body(json_encode(array(
'result' => 'error'
)));
}
});
$app->get('/auth/twitter/callback', function() use($app) {
if($user=require_login($app)) {
$params = $app->request()->params();
$twitter = new \TwitterOAuth\Api(Config::$twitterClientID, Config::$twitterClientSecret,
$_SESSION['twitter_auth']['oauth_token'], $_SESSION['twitter_auth']['oauth_token_secret']);
$credentials = $twitter->getAccessToken($params['oauth_verifier']);
$user->twitter_access_token = $credentials['oauth_token'];
$user->twitter_token_secret = $credentials['oauth_token_secret'];
$user->twitter_username = $credentials['screen_name'];
$user->save();
$app->redirect('/settings');
}
});

7
lib/config.template.php

@ -10,5 +10,12 @@ class Config {
public static $dbPassword = ''; public static $dbPassword = '';
public static $jwtSecret = 'xxx'; public static $jwtSecret = 'xxx';
public static $fbClientID = '';
public static $fbClientSecret = '';
public static $twitterClientID = '';
public static $twitterClientSecret = '';
public static $instagramClientID = '';
public static $instagramClientSecret = '';
} }

20
lib/helpers.php

@ -70,6 +70,26 @@ function get_timezone($lat, $lng) {
return null; return null;
} }
function micropub_post_for_user(&$user, $params) {
// Now send to the micropub endpoint
$r = micropub_post($user->micropub_endpoint, $params, $user->micropub_access_token);
$user->last_micropub_response = json_encode($r);
$user->last_micropub_response_date = date('Y-m-d H:i:s');
// Check the response and look for a "Location" header containing the URL
if($r['response'] && preg_match('/Location: (.+)/', $r['response'], $match)) {
$r['location'] = $match[1];
$user->micropub_success = 1;
} else {
$r['location'] = false;
}
$user->save();
return $r;
}
function micropub_post($endpoint, $params, $access_token) { function micropub_post($endpoint, $params, $access_token) {
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint); curl_setopt($ch, CURLOPT_URL, $endpoint);

17
public/css/favorite.css

@ -0,0 +1,17 @@
#quill-star {
position: absolute;
top: 50%;
left: 50%;
margin-top: -100px;
margin-left: -100px;
width: 200px;
height: 200px;
}
#quill-star.hidden {
display: none;
}

BIN
public/images/quill-logo-1024.png

After

Width: 1024  |  Height: 1024  |  Size: 38 KiB

8
public/images/red-x.svg

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="108px" height="108px" viewBox="0 0 108 108" enable-background="new 0 0 108 108" xml:space="preserve">
<line fill="none" stroke="#972D2C" stroke-width="20" stroke-miterlimit="10" x1="8.788" y1="8.308" x2="99.81" y2="99.329"/>
<line fill="none" stroke="#972D2C" stroke-width="20" stroke-miterlimit="10" x1="99.81" y1="8.308" x2="8.788" y2="99.329"/>
</svg>

8
public/images/star.svg

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="108px" height="108px" viewBox="0 0 108 108" enable-background="new 0 0 108 108" xml:space="preserve">
<polygon fill="#FCB117" points="54.306,4.549 70.476,37.312 106.633,42.566 80.47,68.069 86.646,104.079 54.306,87.077
21.967,104.079 28.143,68.069 1.98,42.566 38.136,37.312 "/>
</svg>

23
public/js/fav.js

@ -0,0 +1,23 @@
console.log("Favoriting with token: " + quill_token);
var http = new XMLHttpRequest();
var params = "like-of=" + encodeURIComponent(window.location) + "&token=" + quill_token;
http.open("POST", "http://quill.dev/favorite", true);
http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
http.onreadystatechange = function() {//Call a function when the state changes.
console.log(http);
if(http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(params);
/*
(function(){var el=document.createElement('input'); el.type="hidden"; el.id="quill_token"; el.value="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMjciLCJtZSI6Imh0dHA6XC9cL2Fhcm9ucGFyZWNraS5jb20iLCJjcmVhdGVkX2F0IjoxNDEwMTE3NTM5fQ.ifp1VIgCTz9NPtMTlTLPBXAGSxHwpGS5tLPhXGxrjNk"; document.body.appendChild(el); document.body.appendChild(document.createElement('script')).src='http://quill.dev/js/fav.js';})();
(function(){document.body.appendChild(document.createElement('script')).src='http://quill.dev/favorite.js?url='+encodeURIComponent(window.location)+'&token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMSIsIm1lIjoiaHR0cDpcL1wvcGsuZGV2XC8iLCJjcmVhdGVkX2F0IjoxNDE5MDM2NzAzfQ.AgJ5xyviiBzWOvQO0je0Bdi3BUpKJ4CLJnx8GIm-0OI';})();
*/

8
views/layout.php

@ -31,6 +31,12 @@
</head> </head>
<body role="document"> <body role="document">
<?php
if(property_exists($this, 'include_facebook')) {
echo partial('partials/fb-script');
}
?>
<script type="text/javascript"> <script type="text/javascript">
var _gaq = _gaq || []; var _gaq = _gaq || [];
@ -65,7 +71,7 @@
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<? if(session('me')) { ?> <? if(session('me')) { ?>
<li><a href="/add-to-home?start">Add to Home Screen</a></li> <li><a href="/add-to-home?start">Add to Home Screen</a></li>
<li><span class="navbar-text"><?= preg_replace('/https?:\/\//','',session('me')) ?></span></li>
<li><a href="/settings"><?= preg_replace(array('/https?:\/\//','/\/$/'),'',session('me')) ?></a></li>
<li><a href="/signout">Sign Out</a></li> <li><a href="/signout">Sign Out</a></li>
<? } else if(property_exists($this, 'authorizing')) { ?> <? } else if(property_exists($this, 'authorizing')) { ?>
<li class="navbar-text"><?= $this->authorizing ?></li> <li class="navbar-text"><?= $this->authorizing ?></li>

22
views/liked-js.php

@ -0,0 +1,22 @@
<?= $this->facebook_id ? partial('partials/fb-script') : '' ?>
console.log("Favoriting URL: <?= $this->url ?>");
var star = document.createElement('img');
star.id="quill-star";
star.src="http://quill.dev/images/<?= $this->like_url ? 'star' : 'red-x' ?>.svg";
document.body.appendChild(star);
var css = document.createElement('link');
css.rel="stylesheet";
css.type="text/css";
css.href="http://quill.dev/css/favorite.css";
document.body.appendChild(css);
setTimeout(function(){
document.getElementById('quill-star').classList.add('hidden');
var el = document.getElementById('quill-star');
el.parentNode.removeChild(el);
}, 1200);

20
views/partials/fb-script.php

@ -0,0 +1,20 @@
<script>
window.fbAsyncInit = function() {
FB.init({
appId : '<?= Config::$fbClientID ?>',
xfbml : true,
version : 'v2.2'
});
if(window.quillFbInit) {
window.quillFbInit();
}
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>

88
views/settings.php

@ -0,0 +1,88 @@
<div class="narrow">
<?= partial('partials/header') ?>
<h2>Signed In As</h2>
<code><?= session('me') ?></code>
<h3>Facebook</h3>
<input type="button" id="facebook-button" value="Checking" class="btn">
<h3>Twitter</h3>
<input type="button" id="twitter-button" value="Checking" class="btn">
<!--
<h3>Instagram</h3>
-->
</div>
<script>
window.quillFbInit = function() {
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
// the user is logged in and has authenticated your
// app, and response.authResponse supplies
// the user's ID, a valid access token, a signed
// request, and the time the access token
// and signed request each expire
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
save_facebook_token(response.authResponse.accessToken);
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
// but has not authenticated your app
console.log("Logged in but not authorized");
$("#facebook-button").val("Sign In").addClass("btn-warning");
} else {
// the user isn't logged in to Facebook.
console.log("User isn't logged in");
$("#facebook-button").val("Sign In").addClass("btn-warning");
}
});
};
window.quillHandleFbLogin = function(response) {
save_facebook_token(response.authResponse.accessToken);
};
function save_facebook_token(token) {
console.log("Authed with token: " + token);
$.post('/auth/facebook', {
fb_token: token
}, function(data){
$("#facebook-button").val("Connected").addClass("btn-success");
});
}
$(function(){
$.getJSON("/auth/twitter", function(data){
// Check if we're already authorized with twitter
if(data && data.result == 'ok') {
$("#twitter-button").val("Connected").addClass("btn-success");
} else if(data && data.url) {
$("#twitter-button").val("Sign In").data("url", data.url).addClass("btn-warning");
} else {
$("#twitter-button").val("Error").addClass("btn-danger");
}
});
$("#twitter-button").click(function(){
if($(this).data('url')) {
window.location = $(this).data('url');
} else {
$.getJSON("/auth/twitter", {login: 1}, function(data){
window.location = data.url;
});
}
});
$("#facebook-button").click(function(){
FB.login(window.quillHandleFbLogin, {scope:'publish_actions'});
});
});
</script>
Loading…
Cancel
Save