Merge pull request #50 from CatoTH/master

Upgrade to FB-Connector + Privacy Image Cache
pull/51/merge
friendica 2012-04-22 15:44:55 -07:00
commit e94b0868df
4 changed files with 437 additions and 157 deletions

View File

@ -1,7 +1,7 @@
Installing the Friendica/Facebook connector Installing the Friendica/Facebook connector
Detailed instructions how to use this plugin can be found at Detailed instructions how to use this plugin can be found at
https://github.com/friendica/friendica/wiki/How-to:-Friendica%E2%80%99s-Facebook-connector the [How to: Friendica's Facebook Connector](https://github.com/friendica/friendica/wiki/How-to:-Friendica%E2%80%99s-Facebook-connector) page.
Vidoes and embeds will not be posted if there is no other content. Links Vidoes and embeds will not be posted if there is no other content. Links
and images will be converted to a format suitable for the Facebook API and and images will be converted to a format suitable for the Facebook API and
@ -12,5 +12,4 @@ authenticate to your site to establish identity. We will address this
in a future release. in a future release.
Info: please make sure that you understand all aspects due to Friendica's Info: please make sure that you understand all aspects due to Friendica's
default licence which is: MIT License (further info: default licence which is: [MIT License](https://github.com/friendica/friendica/blob/master/LICENSE)
https://github.com/friendica/friendica/blob/master/LICENSE)

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Name: Facebook Connector * Name: Facebook Connector
* Version: 1.2 * Version: 1.3
* Author: Mike Macgirvin <http://macgirvin.com/profile/mike> * Author: Mike Macgirvin <http://macgirvin.com/profile/mike>
* Tobias Hößl <https://github.com/CatoTH/> * Tobias Hößl <https://github.com/CatoTH/>
*/ */
@ -516,7 +516,7 @@ function facebook_content(&$a) {
return ''; return '';
} }
$a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="' $a->page['htmlhead'] .= '<link rel="stylesheet" type="text/css" href="'
. $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n"; . $a->get_baseurl() . '/addon/facebook/facebook.css' . '" media="all" />' . "\r\n";
$o .= '<h3>' . t('Facebook Connect') . '</h3>'; $o .= '<h3>' . t('Facebook Connect') . '</h3>';
@ -693,6 +693,7 @@ function facebook_plugin_admin(&$a, &$o){
$appid = get_config('facebook', 'appid' ); $appid = get_config('facebook', 'appid' );
$appsecret = get_config('facebook', 'appsecret' ); $appsecret = get_config('facebook', 'appsecret' );
$poll_interval = get_config('facebook', 'poll_interval' ); $poll_interval = get_config('facebook', 'poll_interval' );
$sync_comments = get_config('facebook', 'sync_comments' );
if (!$poll_interval) $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL; if (!$poll_interval) $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL;
$ret1 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appid' LIMIT 1"); $ret1 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appid' LIMIT 1");
@ -709,9 +710,10 @@ function facebook_plugin_admin(&$a, &$o){
} else $o .= t('The correctness of the API Key could not be detected. Somthing strange\'s going on.') . '<br>'; } else $o .= t('The correctness of the API Key could not be detected. Somthing strange\'s going on.') . '<br>';
} }
$o .= '<label for="fb_appid">' . t('App-ID / API-Key') . '</label><input name="appid" type="text" value="' . escape_tags($appid ? $appid : "") . '"><br style="clear: both;">'; $o .= '<label for="fb_appid">' . t('App-ID / API-Key') . '</label><input id="fb_appid" name="appid" type="text" value="' . escape_tags($appid ? $appid : "") . '"><br style="clear: both;">';
$o .= '<label for="fb_appsecret">' . t('Application secret') . '</label><input name="appsecret" type="text" value="' . escape_tags($appsecret ? $appsecret : "") . '"><br style="clear: both;">'; $o .= '<label for="fb_appsecret">' . t('Application secret') . '</label><input id="fb_appsecret" name="appsecret" type="text" value="' . escape_tags($appsecret ? $appsecret : "") . '"><br style="clear: both;">';
$o .= '<label for="fb_poll_interval">' . sprintf(t('Polling Interval (min. %1$s minutes)'), FACEBOOK_MIN_POLL_INTERVAL) . '</label><input name="poll_interval" type="number" min="' . FACEBOOK_MIN_POLL_INTERVAL . '" value="' . $poll_interval . '"><br style="clear: both;">'; $o .= '<label for="fb_poll_interval">' . sprintf(t('Polling Interval (min. %1$s minutes)'), FACEBOOK_MIN_POLL_INTERVAL) . '</label><input name="poll_interval" id="fb_poll_interval" type="number" min="' . FACEBOOK_MIN_POLL_INTERVAL . '" value="' . $poll_interval . '"><br style="clear: both;">';
$o .= '<label for="fb_sync_comments">' . t('Synchronize comments (no comments on Facebook are missed, at the cost of increased system load)') . '</label><input name="sync_comments" id="fb_sync_comments" type="checkbox" ' . ($sync_comments ? 'checked' : '') . '><br style="clear: both;">';
$o .= '<input type="submit" name="fb_save_keys" value="' . t('Save') . '">'; $o .= '<input type="submit" name="fb_save_keys" value="' . t('Save') . '">';
if ($working_connection) { if ($working_connection) {
@ -739,6 +741,7 @@ function facebook_plugin_admin_post(&$a, &$o){
set_config('facebook', 'appsecret', $_REQUEST['appsecret']); set_config('facebook', 'appsecret', $_REQUEST['appsecret']);
$poll_interval = IntVal($_REQUEST['poll_interval']); $poll_interval = IntVal($_REQUEST['poll_interval']);
if ($poll_interval >= FACEBOOK_MIN_POLL_INTERVAL) set_config('facebook', 'poll_interval', $poll_interval); if ($poll_interval >= FACEBOOK_MIN_POLL_INTERVAL) set_config('facebook', 'poll_interval', $poll_interval);
set_config('facebook', 'sync_comments', (x($_REQUEST, 'sync_comments') ? 1 : 0));
del_config('facebook', 'app_access_token'); del_config('facebook', 'app_access_token');
info(t('The new values have been saved.')); info(t('The new values have been saved.'));
} }
@ -1361,6 +1364,219 @@ function fb_get_photo($uid,$link) {
return ""; return "";
} }
/**
* @param App $a
* @param array $user
* @param array $self
* @param string $fb_id
* @param bool $wall
* @param array $orig_post
* @param object $cmnt
*/
function fb_consume_comment(&$a, &$user, &$self, $fb_id, $wall, &$orig_post, &$cmnt) {
if(! $orig_post)
return;
$top_item = $orig_post['id'];
$uid = IntVal($user[0]['uid']);
$r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
intval($uid),
dbesc('fb::' . $cmnt->id),
dbesc('fb::' . $cmnt->id)
);
if(count($r))
return;
$cmntdata = array();
$cmntdata['parent'] = $top_item;
$cmntdata['verb'] = ACTIVITY_POST;
$cmntdata['gravity'] = 6;
$cmntdata['uid'] = $uid;
$cmntdata['wall'] = (($wall) ? 1 : 0);
$cmntdata['uri'] = 'fb::' . $cmnt->id;
$cmntdata['parent-uri'] = $orig_post['uri'];
if($cmnt->from->id == $fb_id) {
$cmntdata['contact-id'] = $self[0]['id'];
}
else {
$r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
dbesc($cmnt->from->id),
intval($uid)
);
if(count($r)) {
$cmntdata['contact-id'] = $r[0]['id'];
if($r[0]['blocked'] || $r[0]['readonly'])
return;
}
}
if(! x($cmntdata,'contact-id'))
$cmntdata['contact-id'] = $orig_post['contact-id'];
$cmntdata['app'] = 'facebook';
$cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
$cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
$cmntdata['verb'] = ACTIVITY_POST;
$cmntdata['author-name'] = $cmnt->from->name;
$cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
$cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
$cmntdata['body'] = $cmnt->message;
$item = item_store($cmntdata);
$myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 ",
dbesc($orig_post['uri']),
intval($uid)
);
if(count($myconv)) {
$importer_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
foreach($myconv as $conv) {
// now if we find a match, it means we're in this conversation
if(! link_compare($conv['author-link'],$importer_url))
continue;
require_once('include/enotify.php');
$conv_parent = $conv['parent'];
notification(array(
'type' => NOTIFY_COMMENT,
'notify_flags' => $user[0]['notify-flags'],
'language' => $user[0]['language'],
'to_name' => $user[0]['username'],
'to_email' => $user[0]['email'],
'uid' => $user[0]['uid'],
'item' => $cmntdata,
'link' => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $item,
'source_name' => $cmntdata['author-name'],
'source_link' => $cmntdata['author-link'],
'source_photo' => $cmntdata['author-avatar'],
'verb' => ACTIVITY_POST,
'otype' => 'item',
'parent' => $conv_parent,
));
// only send one notification
break;
}
}
}
/**
* @param App $a
* @param array $user
* @param array $self
* @param string $fb_id
* @param bool $wall
* @param array $orig_post
* @param object $likes
*/
function fb_consume_like(&$a, &$user, &$self, $fb_id, $wall, &$orig_post, &$likes) {
$top_item = $orig_post['id'];
$uid = IntVal($user[0]['uid']);
if(! $orig_post)
return;
// If we posted the like locally, it will be found with our url, not the FB url.
$second_url = (($likes->id == $fb_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id);
$r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s'
AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
dbesc($orig_post['uri']),
intval($uid),
dbesc(ACTIVITY_LIKE),
dbesc('http://facebook.com/profile.php?id=' . $likes->id),
dbesc($second_url)
);
if(count($r))
return;
$likedata = array();
$likedata['parent'] = $top_item;
$likedata['verb'] = ACTIVITY_LIKE;
$likedata['gravity'] = 3;
$likedata['uid'] = $uid;
$likedata['wall'] = (($wall) ? 1 : 0);
$likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
$likedata['parent-uri'] = $orig_post['uri'];
if($likes->id == $fb_id)
$likedata['contact-id'] = $self[0]['id'];
else {
$r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
dbesc($likes->id),
intval($uid)
);
if(count($r))
$likedata['contact-id'] = $r[0]['id'];
}
if(! x($likedata,'contact-id'))
$likedata['contact-id'] = $orig_post['contact-id'];
$likedata['app'] = 'facebook';
$likedata['verb'] = ACTIVITY_LIKE;
$likedata['author-name'] = $likes->name;
$likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
$likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
$author = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
$objauthor = '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
$post_type = t('status');
$plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
$likedata['object-type'] = ACTIVITY_OBJ_NOTE;
$likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
$likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' .
'<id>' . $orig_post['uri'] . '</id><link>' . xmlify('<link rel="alternate" type="text/html" href="' . xmlify($orig_post['plink']) . '" />') . '</link><title>' . $orig_post['title'] . '</title><content>' . $orig_post['body'] . '</content></object>';
item_store($likedata);
}
/**
* @param App $a
* @param array $user
* @param object $entry
* @param array $self
* @param string $fb_id
* @param bool $wall
* @param array $orig_post
*/
function fb_consume_status(&$a, &$user, &$entry, &$self, $fb_id, $wall, &$orig_post) {
$uid = IntVal($user[0]['uid']);
$access_token = get_pconfig($uid, 'facebook', 'access_token');
$s = fetch_url('https://graph.facebook.com/' . $entry->id . '?access_token=' . $access_token);
if($s) {
$j = json_decode($s);
if (isset($j->comments) && isset($j->comments->data))
foreach ($j->comments->data as $cmnt)
fb_consume_comment($a, $user, $self, $fb_id, $wall, $orig_post, $cmnt);
if (isset($j->likes) && isset($j->likes->data) && isset($j->likes->count)) {
if (count($j->likes->data) == $j->likes->count) {
foreach ($j->likes->data as $likers) fb_consume_like($a, $user, $self, $fb_id, $wall, $orig_post, $likers);
} else {
$t = fetch_url('https://graph.facebook.com/' . $entry->id . '/likes?access_token=' . $access_token);
if ($t) {
$k = json_decode($t);
if (isset($k->data))
foreach ($k->data as $likers)
fb_consume_like($a, $user, $self, $fb_id, $wall, $orig_post, $likers);
}
}
}
}
}
/** /**
* @param int $uid * @param int $uid
* @param object $j * @param object $j
@ -1370,7 +1586,6 @@ function fb_consume_stream($uid,$j,$wall = false) {
$a = get_app(); $a = get_app();
$user = q("SELECT * FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1", $user = q("SELECT * FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
intval($uid) intval($uid)
); );
@ -1390,6 +1605,9 @@ function fb_consume_stream($uid,$j,$wall = false) {
$blocked_apps = get_pconfig($uid,'facebook','blocked_apps'); $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
$blocked_apps_arr = explode(',',$blocked_apps); $blocked_apps_arr = explode(',',$blocked_apps);
$sync_comments = get_config('facebook', 'sync_comments');
/** @var string $self_id */
$self_id = get_pconfig($uid,'facebook','self_id'); $self_id = get_pconfig($uid,'facebook','self_id');
if(! count($j->data) || (! strlen($self_id))) if(! count($j->data) || (! strlen($self_id)))
return; return;
@ -1591,169 +1809,53 @@ function fb_consume_stream($uid,$j,$wall = false) {
} }
} }
/** @var array $orig_post */
$likers_num = (isset($entry->likes) && isset($entry->likes->count) ? IntVal($entry->likes->count) : 0 );
if(isset($entry->likes) && isset($entry->likes->data)) if(isset($entry->likes) && isset($entry->likes->data))
$likers = $entry->likes->data; $likers = $entry->likes->data;
else else
$likers = null; $likers = null;
$comments_num = (isset($entry->comments) && isset($entry->comments->count) ? IntVal($entry->comments->count) : 0 );
if(isset($entry->comments) && isset($entry->comments->data)) if(isset($entry->comments) && isset($entry->comments->data))
$comments = $entry->comments->data; $comments = $entry->comments->data;
else else
$comments = null; $comments = null;
if(is_array($likers)) { $needs_sync = false;
foreach($likers as $likes) {
if(! $orig_post) if(is_array($likers)) {
continue; foreach($likers as $likes) fb_consume_like($a, $user, $self, $self_id, $wall, $orig_post, $likes);
if ($sync_comments) {
// If we posted the like locally, it will be found with our url, not the FB url. $r = q("SELECT COUNT(*) likes FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' AND `parent-uri` != `uri`",
dbesc($orig_post['uri']),
$second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id); intval($uid),
dbesc(ACTIVITY_LIKE)
$r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' );
AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1", if ($r[0]['likes'] < $likers_num) {
dbesc($orig_post['uri']), logger('fb_consume_stream: missing likes found for ' . $orig_post['uri'] . ' (we have ' . $r[0]['likes'] . ' of ' . $likers_num . '). Synchronizing...', LOGGER_DEBUG);
intval($uid), $needs_sync = true;
dbesc(ACTIVITY_LIKE), }
dbesc('http://facebook.com/profile.php?id=' . $likes->id), }
dbesc($second_url)
);
if(count($r))
continue;
$likedata = array();
$likedata['parent'] = $top_item;
$likedata['verb'] = ACTIVITY_LIKE;
$likedata['gravity'] = 3;
$likedata['uid'] = $uid;
$likedata['wall'] = (($wall) ? 1 : 0);
$likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
$likedata['parent-uri'] = $orig_post['uri'];
if($likes->id == $self_id)
$likedata['contact-id'] = $self[0]['id'];
else {
$r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
dbesc($likes->id),
intval($uid)
);
if(count($r))
$likedata['contact-id'] = $r[0]['id'];
}
if(! x($likedata,'contact-id'))
$likedata['contact-id'] = $orig_post['contact-id'];
$likedata['app'] = 'facebook';
$likedata['verb'] = ACTIVITY_LIKE;
$likedata['author-name'] = $likes->name;
$likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
$likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
$author = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
$objauthor = '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
$post_type = t('status');
$plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
$likedata['object-type'] = ACTIVITY_OBJ_NOTE;
$likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
$likedata['object'] = '<object><type>' . ACTIVITY_OBJ_NOTE . '</type><local>1</local>' .
'<id>' . $orig_post['uri'] . '</id><link>' . xmlify('<link rel="alternate" type="text/html" href="' . xmlify($orig_post['plink']) . '" />') . '</link><title>' . $orig_post['title'] . '</title><content>' . $orig_post['body'] . '</content></object>';
item_store($likedata);
}
} }
if(is_array($comments)) { if(is_array($comments)) {
foreach($comments as $cmnt) { foreach($comments as $cmnt) fb_consume_comment($a, $user, $self, $self_id, $wall, $orig_post, $cmnt);
if ($sync_comments) {
if(! $orig_post) $r = q("SELECT COUNT(*) comments FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' AND `parent-uri` != `uri`",
continue; dbesc($orig_post['uri']),
intval($uid),
$r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1", ACTIVITY_POST
intval($uid), );
dbesc('fb::' . $cmnt->id), if ($r[0]['comments'] < $comments_num) {
dbesc('fb::' . $cmnt->id) logger('fb_consume_stream: missing comments found for ' . $orig_post['uri'] . ' (we have ' . $r[0]['comments'] . ' of ' . $comments_num . '). Synchronizing...', LOGGER_DEBUG);
); $needs_sync = true;
if(count($r)) }
continue;
$cmntdata = array();
$cmntdata['parent'] = $top_item;
$cmntdata['verb'] = ACTIVITY_POST;
$cmntdata['gravity'] = 6;
$cmntdata['uid'] = $uid;
$cmntdata['wall'] = (($wall) ? 1 : 0);
$cmntdata['uri'] = 'fb::' . $cmnt->id;
$cmntdata['parent-uri'] = $orig_post['uri'];
if($cmnt->from->id == $self_id) {
$cmntdata['contact-id'] = $self[0]['id'];
}
else {
$r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
dbesc($cmnt->from->id),
intval($uid)
);
if(count($r)) {
$cmntdata['contact-id'] = $r[0]['id'];
if($r[0]['blocked'] || $r[0]['readonly'])
continue;
}
}
if(! x($cmntdata,'contact-id'))
$cmntdata['contact-id'] = $orig_post['contact-id'];
$cmntdata['app'] = 'facebook';
$cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
$cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
$cmntdata['verb'] = ACTIVITY_POST;
$cmntdata['author-name'] = $cmnt->from->name;
$cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
$cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
$cmntdata['body'] = $cmnt->message;
$item = item_store($cmntdata);
$myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 ",
dbesc($orig_post['uri']),
intval($uid)
);
if(count($myconv)) {
$importer_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
foreach($myconv as $conv) {
// now if we find a match, it means we're in this conversation
if(! link_compare($conv['author-link'],$importer_url))
continue;
require_once('include/enotify.php');
$conv_parent = $conv['parent'];
notification(array(
'type' => NOTIFY_COMMENT,
'notify_flags' => $user[0]['notify-flags'],
'language' => $user[0]['language'],
'to_name' => $user[0]['username'],
'to_email' => $user[0]['email'],
'uid' => $user[0]['uid'],
'item' => $cmntdata,
'link' => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $item,
'source_name' => $cmntdata['author-name'],
'source_link' => $cmntdata['author-link'],
'source_photo' => $cmntdata['author-avatar'],
'verb' => ACTIVITY_POST,
'otype' => 'item',
'parent' => $conv_parent,
));
// only send one notification
break;
}
}
} }
} }
if ($needs_sync) fb_consume_status($a, $user, $entry, $self, $self_id, $wall, $orig_post);
} }
} }

View File

@ -0,0 +1,4 @@
Once activated, this addon acs as a proxy between the user and external image resources.
If external images are to be shown, like avatars or embedded images, the server retrieves the image and the local link is sent to the user.
By using this proxy, the server hosting the original image cannot retrieve personal information of the user like the IP address, User Agent or Cookies.
However, the remote server will be able to track the IP address of the server hosting the friendica installation.

View File

@ -0,0 +1,175 @@
<?php
/**
* Name: Privacy Image Cache
* Version: 0.1
* Author: Tobias Hößl <https://github.com/CatoTH/>
*/
define("PRIVACY_IMAGE_CACHE_DEFAULT_TIME", 86400); // 1 Day
require_once('include/security.php');
function privacy_image_cache_install() {
register_hook('bbcode', 'addon/privacy_image_cache/privacy_image_cache.php', 'privacy_image_cache_bbcode_hook');
register_hook('display_item', 'addon/privacy_image_cache/privacy_image_cache.php', 'privacy_image_cache_display_item_hook');
register_hook('ping_xmlize', 'addon/privacy_image_cache/privacy_image_cache.php', 'privacy_image_cache_ping_xmlize_hook');
register_hook('cron', 'addon/privacy_image_cache/privacy_image_cache.php', 'privacy_image_cache_cron');
}
function privacy_image_cache_uninstall() {
unregister_hook('bbcode', 'addon/privacy_image_cache/privacy_image_cache.php', 'privacy_image_cache_bbcode_hook');
unregister_hook('display_item', 'addon/privacy_image_cache/privacy_image_cache.php', 'privacy_image_cache_display_item_hook');
unregister_hook('ping_xmlize', 'addon/privacy_image_cache/privacy_image_cache.php', 'privacy_image_cache_ping_xmlize_hook');
unregister_hook('cron', 'addon/privacy_image_cache/privacy_image_cache.php', 'privacy_image_cache_cron');
}
function privacy_image_cache_module() {}
function privacy_image_cache_init() {
$urlhash = 'pic:' . sha1($_REQUEST['url']);
$r = q("SELECT * FROM `photo` WHERE `resource-id` = '%s' LIMIT 1", $urlhash );
if (count($r)) {
$img_str = $r[0]['data'];
}
else {
require_once("Photo.php");
$img_str = fetch_url($_REQUEST['url'],true);
$img = new Photo($img_str);
if($img->is_valid()) {
$img->store(0, 0, $urlhash, $_REQUEST['url'], '', 100);
$img_str = $img->imageString();
}
}
header("Content-type: image/jpeg");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + (3600*24)) . " GMT");
header("Cache-Control: max-age=" . (3600*24));
echo $img_str;
killme();
}
/**
* @param $url string
* @return boolean
*/
function privacy_image_cache_is_local_image($url) {
if ($url[0] == '/') return true;
$baseurl = get_app()->get_baseurl();
return (substr($url, 0, strlen($baseurl)) == $baseurl);
}
/**
* @param array $matches
* @return string
*/
function privacy_image_cache_img_cb($matches) {
if (privacy_image_cache_is_local_image($matches[2])) return $matches[2];
return $matches[1] . "/privacy_image_cache/?url=" . escape_tags(addslashes($matches[2])) . $matches[3];
}
/**
* @param App $a
* @param string $o
*/
function privacy_image_cache_bbcode_hook(&$a, &$o) {
$o = preg_replace_callback("/(<img [^>]*src *= *[\"'])([^\"']+)([\"'][^>]*>)/siU", "privacy_image_cache_img_cb", $o);
}
/**
* @param App $a
* @param string $o
*/
function privacy_image_cache_display_item_hook(&$a, &$o) {
if (isset($o["output"])) {
if (isset($o["output"]["thumb"]) && !privacy_image_cache_is_local_image($o["output"]["thumb"]))
$o["output"]["thumb"] = "/privacy_image_cache/?url=" . escape_tags(addslashes($o["output"]["thumb"]));
if (isset($o["output"]["author-avatar"]) && !privacy_image_cache_is_local_image($o["output"]["author-avatar"]))
$o["output"]["author-avatar"] = "/privacy_image_cache/?url=" . escape_tags(addslashes($o["output"]["author-avatar"]));
}
}
/**
* @param App $a
* @param string $o
*/
function privacy_image_cache_ping_xmlize_hook(&$a, &$o) {
if ($o["photo"] != "" && !privacy_image_cache_is_local_image($o["photo"]))
$o["photo"] = "/privacy_image_cache/?url=" . escape_tags(addslashes($o["photo"]));
}
/**
* @param App $a
* @param null|object $b
*/
function privacy_image_cache_cron(&$a, &$b) {
$cachetime = get_config('privacy_image_cache','cache_time');
if (!$cachetime) $cachetime = PRIVACY_IMAGE_CACHE_DEFAULT_TIME;
$last = get_config('pi_cache','last_delete');
$time = time();
if ($time < ($last + 3600)) return;
logger("Purging old Cache of the Privacy Image Cache", LOGGER_DEBUG);
q('DELETE FROM `photo` WHERE `uid` = 0 AND `resource-id` LIKE "pic:%%" AND `created` < NOW() - INTERVAL %d SECOND', $cachetime);
set_config('pi_cache', 'last_delete', $time);
}
/**
* @param App $a
* @param null|object $o
*/
function privacy_image_cache_plugin_admin(&$a, &$o){
$o = '<input type="hidden" name="form_security_token" value="' . get_form_security_token("picsave") . '">';
$cachetime = get_config('privacy_image_cache','cache_time');
if (!$cachetime) $cachetime = PRIVACY_IMAGE_CACHE_DEFAULT_TIME;
$cachetime_h = Ceil($cachetime / 3600);
$o .= '<label for="pic_cachetime">' . t('Lifetime of the cache (in hours)') . '</label>
<input id="pic_cachetime" name="cachetime" type="text" value="' . escape_tags($cachetime_h) . '"><br style="clear: both;">';
$o .= '<input type="submit" name="save" value="' . t('Save') . '">';
$o .= '<h4>' . t('Cache Statistics') . '</h4>';
$num = q('SELECT COUNT(*) num, SUM(LENGTH(data)) size FROM `photo` WHERE `uid`=0 AND `contact-id`=0 AND `resource-id` LIKE "pic:%%"');
$o .= '<label for="statictics_num">' . t('Number of items') . '</label><input style="color: gray;" id="statistics_num" disabled value="' . escape_tags($num[0]['num']) . '"><br style="clear: both;">';
$size = Ceil($num[0]['size'] / (1024 * 1024));
$o .= '<label for="statictics_size">' . t('Size of the cache') . '</label><input style="color: gray;" id="statistics_size" disabled value="' . $size . ' MB"><br style="clear: both;">';
$o .= '<input type="submit" name="delete_all" value="' . t('Delete the whole cache') . '">';
}
/**
* @param App $a
* @param null|object $o
*/
function privacy_image_cache_plugin_admin_post(&$a, &$o){
check_form_security_token_redirectOnErr('/admin/plugins/privacy_image_cache', 'picsave');
if (isset($_REQUEST['save'])) {
$cachetime_h = IntVal($_REQUEST['cachetime']);
if ($cachetime_h < 1) $cachetime_h = 1;
set_config('privacy_image_cache','cache_time', $cachetime_h * 3600);
}
if (isset($_REQUEST['delete_all'])) {
q('DELETE FROM `photo` WHERE `uid` = 0 AND `resource-id` LIKE "pic:%%"');
}
}