diff options
Diffstat (limited to 'addon')
-rw-r--r-- | addon/facebook/facebook.php | 597 | ||||
-rw-r--r-- | addon/oembed/oembed.php | 8 | ||||
-rw-r--r-- | addon/statusnet/statusnet.css | 19 | ||||
-rw-r--r-- | addon/statusnet/statusnet.php | 62 | ||||
-rw-r--r-- | addon/twitter/twitter.php | 2 | ||||
-rw-r--r-- | addon/widgets/widget_friends.php | 32 | ||||
-rw-r--r-- | addon/widgets/widget_like.php | 22 | ||||
-rw-r--r-- | addon/widgets/widgets.js | 64 | ||||
-rw-r--r-- | addon/widgets/widgets.php | 168 |
9 files changed, 930 insertions, 44 deletions
diff --git a/addon/facebook/facebook.php b/addon/facebook/facebook.php index edfc5a374..667b2ce6f 100644 --- a/addon/facebook/facebook.php +++ b/addon/facebook/facebook.php @@ -1,17 +1,13 @@ <?php /** - * This module still needs a lot of work, but is functional today. - * Please review this section if you upgrade because things will change. - * If you have issues upgrading, remove facebook from the addon list, - * view a page on your site, then add it back to the list. This will reset - * all of the plugin 'hooks'. + * Installing the Friendika/Facebook connector * * 1. register an API key for your site from developer.facebook.com * a. We'd be very happy if you include "Friendika" in the application name * to increase name recognition. The Friendika icons are also present * in the images directory and may be uploaded as a Facebook app icon. - * Use images/ff-16.jpg for the Icon and images/ff-128.jpg for the Logo. + * Use images/friendika-16.jpg for the Icon and images/friendika-128.jpg for the Logo. * b. The url should be your site URL with a trailing slash. * You may use http://portal.friendika.com/privacy as the privacy policy * URL unless your site has different requirements, and @@ -24,20 +20,20 @@ * Replace with the settings Facebook gives you. * 2. Enable the facebook plugin by including it in .htconfig.php - e.g. * $a->config['system']['addon'] = 'plugin1,plugin2,facebook'; - * 3. Visit your site url + '/facebook' (e.g. http://example.com/facebook) - * and click 'Install Facebook posting'. + * 3. Visit the Facebook Settings section of the "Settings->Plugin Settings" page. + * and click 'Install Facebook Connector'. * 4. This will ask you to login to Facebook and grant permission to the * plugin to do its stuff. Allow it to do so. - * 5. You're done. To turn it off visit your site's /facebook page again and + * 5. You're done. To turn it off visit the Plugin Settings page again and * 'Remove Facebook posting'. * - * Turn logging on (see the github Friendika wiki page 'Settings') and - * repeat these steps if you have trouble. * Vidoes and embeds will not be posted if there is no other content. Links - * and images will be converted to text and long posts truncated - with a link - * to view the full post. Posts with permission settings and comments will - * not be posted to Facebook. + * and images will be converted to a format suitable for the Facebook API and + * long posts truncated - with a link to view the full post. * + * Facebook contacts will not be able to view private photos, as they are not able to + * authenticate to your site to establish identity. We will address this + * in a future release. */ define('FACEBOOK_MAXPOSTLEN', 420); @@ -88,6 +84,10 @@ function facebook_init(&$a) { $token = substr($token,0,strpos($token,'&')); set_pconfig($uid,'facebook','access_token',$token); set_pconfig($uid,'facebook','post','1'); + fb_get_self($uid); + fb_get_friends($uid); + fb_consume_all($uid); + } // todo: is this a browser session or a server session? where do we go? @@ -95,6 +95,138 @@ function facebook_init(&$a) { } + +function fb_get_self($uid) { + $access_token = get_pconfig($uid,'facebook','access_token'); + if(! $access_token) + return; + $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token); + if($s) { + $j = json_decode($s); + set_pconfig($uid,'facebook','self_id',(string) $j->id); + } +} + + + +function fb_get_friends($uid) { + + $access_token = get_pconfig($uid,'facebook','access_token'); + if(! $access_token) + return; + $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token); + if($s) { + logger('facebook: fb_get_friends: ' . $s, LOGGER_DATA); + $j = json_decode($s); + logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA); + foreach($j->data as $person) { + $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token); + if($s) { + $jp = json_decode($s); + logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA); + + // always use numeric link for consistency + + $jp->link = 'http://facebook.com/profile.php?id=' . $person->id; + + // check if we already have a contact + + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", + intval($uid), + dbesc($jp->link) + ); + + if(count($r)) { + + // check that we have all the photos, this has been known to fail on occasion + + if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) { + require_once("Photo.php"); + + $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']); + + $r = q("UPDATE `contact` SET `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `name-date` = '%s', + `uri-date` = '%s', + `avatar-date` = '%s' + WHERE `id` = %d LIMIT 1 + ", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($r[0]['id']) + ); + } + continue; + } + else { + + // create contact record + $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `addr`, `alias`, `notify`, `poll`, + `name`, `nick`, `photo`, `network`, `rel`, `priority`, + `writable`, `blocked`, `readonly`, `pending` ) + VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ", + intval($uid), + dbesc(datetime_convert()), + dbesc($jp->link), + dbesc(''), + dbesc(''), + dbesc($jp->id), + dbesc('facebook ' . $jp->id), + dbesc($jp->name), + dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)), + dbesc('https://graph.facebook.com/' . $jp->id . '/picture'), + dbesc(NETWORK_FACEBOOK), + intval(REL_BUD), + intval(1), + intval(1) + ); + } + + $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", + dbesc($jp->link), + intval($uid) + ); + + if(! count($r)) { + continue; + } + + $contact = $r[0]; + $contact_id = $r[0]['id']; + + require_once("Photo.php"); + + $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id); + + $r = q("UPDATE `contact` SET `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `name-date` = '%s', + `uri-date` = '%s', + `avatar-date` = '%s' + WHERE `id` = %d LIMIT 1 + ", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($contact_id) + ); + + } + } + } +} + + function facebook_post(&$a) { if(local_user()){ @@ -116,6 +248,12 @@ function facebook_content(&$a) { notice( t('Facebook disabled') . EOL); } + if($a->argc > 1 && $a->argv[1] === 'friends') { + fb_get_friends(local_user()); + notice( t('Updating contacts') . EOL); + } + + $fb_installed = get_pconfig(local_user(),'facebook','post'); $appid = get_config('facebook','appid'); @@ -134,14 +272,14 @@ function facebook_content(&$a) { $o .= '<div id="facebook-enable-wrapper">'; $o .= '<a href="https://www.facebook.com/dialog/oauth?client_id=' . $appid . '&redirect_uri=' - . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook post connector') . '</a>'; + . $a->get_baseurl() . '/facebook/' . $a->user['nickname'] . '&scope=publish_stream,read_stream,offline_access">' . t('Install Facebook connector for this account.') . '</a>'; $o .= '</div>'; } if($fb_installed) { $o .= '<div id="facebook-disable-wrapper">'; - $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook post connector') . '</a></div>'; + $o .= '<a href="' . $a->get_baseurl() . '/facebook/remove' . '">' . t('Remove Facebook connector') . '</a></div>'; $o .= '<div id="facebook-post-default-form">'; $o .= '<form action="facebook" method="post" >'; @@ -158,6 +296,7 @@ function facebook_install() { register_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook'); register_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets'); register_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings'); + register_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron'); } @@ -165,9 +304,46 @@ function facebook_uninstall() { unregister_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook'); unregister_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets'); unregister_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings'); + unregister_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron'); } +function facebook_cron($a,$b) { + + $last = get_config('facebook','last_poll'); + + $poll_interval = intval(get_config('facebook','poll_interval')); + if(! $poll_interval) + $poll_interval = 3600; + + if($last) { + $next = $last + $poll_interval; + if($next > time()) + return; + } + + logger('facebook_cron'); + + set_config('facebook','last_poll', time()); + + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' "); + if(count($r)) { + foreach($r as $rr) { + // check for new friends once a day + $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check'); + if($last_friend_check) + $next_friend_check = $last_friend_check + 86400; + if($next_friend_check <= time()) { + fb_get_friends($rr['uid']); + set_pconfig($rr['uid'],'facebook','friend_check',time()); + } + fb_consume_all($rr['uid']); + } + } +} + + + function facebook_plugin_settings(&$a,&$b) { $b .= '<div class="settings-block">'; @@ -197,9 +373,77 @@ function facebook_post_hook(&$a,&$b) { * Post to Facebook stream */ + require_once('include/group.php'); + logger('Facebook post'); - if((local_user()) && (local_user() == $b['uid']) && (! $b['private']) && (! $b['parent'])) { + $reply = false; + $likes = false; + + if((local_user()) && (local_user() == $b['uid'])) { + + if($b['parent']) { + $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", + intval($b['parent']), + intval(local_user()) + ); + if(count($r) && substr($r[0]['uri'],0,4) === 'fb::') + $reply = substr($r[0]['uri'],4); + elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::') + $reply = substr($r[0]['extid'],4); + else + return; + logger('facebook reply id=' . $reply); + } + + if($b['private'] && $reply == false) { + $allow_people = expand_acl($b['allow_cid']); + $allow_groups = expand_groups(expand_acl($b['allow_gid'])); + $deny_people = expand_acl($b['deny_cid']); + $deny_groups = expand_groups(expand_acl($b['deny_gid'])); + + $recipients = array_unique(array_merge($allow_people,$allow_groups)); + $deny = array_unique(array_merge($deny_people,$deny_groups)); + + $allow_str = dbesc(implode(', ',$recipients)); + if($allow_str) { + $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'"); + $allow_arr = array(); + if(count($r)) + foreach($r as $rr) + $allow_arr[] = $rr['notify']; + } + + $deny_str = dbesc(implode(', ',$deny)); + if($deny_str) { + $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'"); + $deny_arr = array(); + if(count($r)) + foreach($r as $rr) + $deny_arr[] = $rr['notify']; + } + + if(count($deny_arr) && (! count($allow_arr))) { + + // One or more FB folks were denied access but nobody on FB was specifically allowed access. + // This might cause the post to be open to public on Facebook, but only to selected members + // on another network. Since this could potentially leak a post to somebody who was denied, + // we will skip posting it to Facebook with a slightly vague but relevant message that will + // hopefully lead somebody to this code comment for a better explanation of what went wrong. + + notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL); + return; + } + + + // if it's a private message but no Facebook members are allowed or denied, skip Facebook post + + if((! count($allow_arr)) && (! count($deny_arr))) + return; + } + + if($b['verb'] == ACTIVITY_LIKE) + $likes = true; $appid = get_config('facebook', 'appid' ); @@ -214,7 +458,12 @@ function facebook_post_hook(&$a,&$b) { $fb_token = get_pconfig(local_user(),'facebook','access_token'); logger('facebook: $fb_post: ' . $fb_post . ' $fb_enable: ' . $fb_enable . ' $fb_token: ' . $fb_token,LOGGER_DEBUG); - if($fb_post && $fb_token && $fb_enable) { + + // post to facebook if it's a public post and we've ticked the 'post to Facebook' box, + // or it's a private message with facebook participants + // or it's a reply or likes action to an existing facebook post + + if($fb_post && $fb_token && ($fb_enable || $b['private'] || $reply)) { logger('facebook: able to post'); require_once('library/facebook.php'); require_once('include/bbcode.php'); @@ -225,9 +474,32 @@ function facebook_post_hook(&$a,&$b) { // make links readable before we strip the code - $msg = preg_replace("/\[url=(.+?)\](.+?)\[\/url\]/is",'$2 [$1]',$msg); + // unless it's a dislike - just send the text as a comment + + if($b['verb'] == ACTIVITY_DISLIKE) + $msg = trim(strip_tags(bbcode($msg))); + + $search_str = $a->get_baseurl() . '/search'; + + if(preg_match("/\[url=(.+?)\](.+?)\[\/url\]/is",$msg,$matches)) { + + // don't use hashtags for message link + + if(strpos($matches[2],$search_str) === false) { + $link = $matches[1]; + if(substr($matches[2],0,5) != '[img]') + $linkname = $matches[2]; + } + } + + $msg = preg_replace("/\[url=(.+?)\](.+?)\[\/url\]/is",'$2 $1',$msg); + + if(preg_match("/\[img\](.+?)\[\/img\]/is",$msg,$matches)) + $image = $matches[1]; + + $msg = preg_replace("/\[img\](.+?)\[\/img\]/is", t('Image: ') . '$1', $msg); + - $msg = preg_replace("/\[img\](.+?)\[\/img\]/is", t('Image: ') . '$1',$msg); $msg = trim(strip_tags(bbcode($msg))); $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8'); @@ -237,7 +509,7 @@ function facebook_post_hook(&$a,&$b) { require_once('library/slinky.php'); $display_url = $a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $b['id']; - $slinky = new Slinky( $posturl ); + $slinky = new Slinky( $display_url ); // setup a cascade of shortening services // try to get a short link from these services // in the order ur1.ca, trim, id.gd, tinyurl @@ -253,9 +525,57 @@ function facebook_post_hook(&$a,&$b) { logger('Facebook post: msg=' . $msg, LOGGER_DATA); - $postvars = array('access_token' => $fb_token, 'message' => $msg); + if($likes) { + $postvars = array('access_token' => $fb_token); + } + else { + $postvars = array( + 'access_token' => $fb_token, + 'message' => $msg + ); + if(isset($image)) + $postvars['picture'] = $image; + if(isset($link)) + $postvars['link'] = $link; + if(isset($linkname)) + $postvars['name'] = $linkname; + } + + if(($b['private']) && (! $b['parent'])) { + $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"'; + if(count($allow_arr)) + $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"'; + if(count($deny_arr)) + $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"'; + $postvars['privacy'] .= '}'; + + } + + if($reply) { + $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments'); + } + else { + $url = 'https://graph.facebook.com/me/feed'; + if($b['plink']) + $postvars['actions'] = '{"name": "' . t('View on Friendika') . '", "link": "' . $b['plink'] . '"}'; + } + + logger('facebook: post to ' . $url); + logger('facebook: postvars: ' . print_r($postvars,true)); - $x = post_url('https://graph.facebook.com/me/feed', $postvars); + // "test_mode" prevents anything from actually being posted. + // Otherwise, let's do it. + + if(! get_config('facebook','test_mode')) + $x = post_url($url, $postvars); + + $retj = json_decode($x); + if($retj->id) { + q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1", + dbesc('fb::' . $retj->id), + intval($b['id']) + ); + } logger('Facebook post returns: ' . $x, LOGGER_DEBUG); @@ -264,3 +584,234 @@ function facebook_post_hook(&$a,&$b) { } } + +function fb_consume_all($uid) { + + require_once('include/items.php'); + + $access_token = get_pconfig($uid,'facebook','access_token'); + if(! $access_token) + return; + $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token); + if($s) { + $j = json_decode($s); + logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA); + fb_consume_stream($uid,$j,true); + } + $s = fetch_url('https://graph.facebook.com/me/home?access_token=' . $access_token); + if($s) { + $j = json_decode($s); + logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA); + fb_consume_stream($uid,$j,false); + } + +} + +function fb_consume_stream($uid,$j,$wall = false) { + $a = get_app(); + + $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1", + intval($uid) + ); + + $user = q("SELECT `nickname` FROM `user` WHERE `uid` = %d LIMIT 1", + intval($uid) + ); + if(count($user)) + $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname']; + + + $self_id = get_pconfig($uid,'facebook','self_id'); + if(! count($j->data) || (! strlen($self_id))) + return; + + foreach($j->data as $entry) { + logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA); + $datarray = array(); + $we_posted = false; + $app = $entry->application; + if($app->id == get_config('facebook','appid') && $wall) + $we_posted = true; + + $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1", + dbesc('fb::' . $entry->id), + dbesc('fb::' . $entry->id), + intval($uid) + ); + if(count($r)) { + $post_exists = true; + $orig_post = $r[0]; + $top_item = $r[0]['id']; + } + else { + $post_exists = false; + $orig_post = null; + } + + if(! $orig_post) { + $datarray['gravity'] = 0; + $datarray['uid'] = $uid; + $datarray['wall'] = (($wall) ? 1 : 0); + $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id; + $from = $entry->from; + if($from->id == $self_id) + $datarray['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($from->id), + intval($uid) + ); + if(count($r)) + $datarray['contact-id'] = $r[0]['id']; + } + + // don't store post if we don't have a contact + + if(! x($datarray,'contact-id')) + continue; + + $datarray['verb'] = ACTIVITY_POST; + if($wall) { + $datarray['owner-name'] = $self[0]['name']; + $datarray['owner-link'] = $self[0]['url']; + $datarray['owner-avatar'] = $self[0]['thumb']; + } + $datarray['author-name'] = $from->name; + $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id; + $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture'; + $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1); + + $datarray['body'] = $entry->message; + if($entry->picture) + $datarray['body'] .= "\n\n" . '[img]' . $entry->picture . '[/img]'; + if($entry->link) + $datarray['body'] .= "\n" . linkify($entry->link); + if($entry->name) + $datarray['body'] .= "\n" . $entry->name; + if($entry->caption) + $datarray['body'] .= "\n" . $entry->caption; + if($entry->description) + $datarray['body'] .= "\n" . $entry->description; + $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time); + $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time); + if($entry->privacy && $entry->privacy->value !== 'EVERYONE') + $datarray['private'] = 1; + $top_item = item_store($datarray); + $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", + intval($top_item), + intval($uid) + ); + if(count($r)) + $orig_post = $r[0]; + + } + $likers = $entry->likes->data; + $comments = $entry->comments->data; + + if(is_array($likers)) { + foreach($likers as $likes) { + + if(! $orig_post) + continue; + + $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s' AND `author-link` = '%s' LIMIT 1", + dbesc($orig_post['uri']), + intval($uid), + dbesc(ACTIVITY_LIKE), + dbesc('http://facebook.com/profile.php?id=' . $likes->id) + ); + + 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['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="' . $orig_post['plink'] . '">') . '</link><title>' . $orig_post['title'] . '</title><content>' . $orig_post['body'] . '</content></object>'; + + $item = item_store($likedata); + } + } + if(is_array($comments)) { + foreach($comments as $cmnt) { + + if(! $orig_post) + continue; + + $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)) + 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['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); + } + } + } +} + diff --git a/addon/oembed/oembed.php b/addon/oembed/oembed.php index 4bbd75387..d9b205a3a 100644 --- a/addon/oembed/oembed.php +++ b/addon/oembed/oembed.php @@ -18,6 +18,10 @@ function oembed_uninstall() { } function oembed_hook_page_header($a, &$b){ + + if(($a->module !== 'network') && ($a->module !== 'profile')) + return; + $b .= '<script src="addon/oembed/oembed.js"></script> <style>#oembed.hide { display: none } #oembed { @@ -33,11 +37,11 @@ function oembed_hook_page_header($a, &$b){ <div id="oembed" class="hide"><input id="oembed_url"> <input type="button" value="Embed" onclick="oembed_do()" style="float:left;"> <a onclick="oembed(); return false;" style="float:right;"><img onmouseout="imgdull(this);" onmouseover="imgbright(this);" class="wall-item-delete-icon" src="images/b_drophide.gif" style="width: 16px; height: 16px;"></a> - <p style="clear:both">Paste a link from 5min.com, Amazon Product Image, blip.tv, Clikthrough, CollegeHumor Video, + <div style="clear:both">Paste a link from 5min.com, Amazon Product Image, blip.tv, Clikthrough, CollegeHumor Video, Daily Show with Jon Stewart, Dailymotion, dotSUB.com, Flickr Photos, Funny or Die Video, Google Video, Hulu, Kinomap, LiveJournal UserPic, Metacafe, National Film Board of Canada, Phodroid Photos, Photobucket, Qik Video, Revision3, Scribd, SlideShare, TwitPic, Twitter Status, - Viddler Video, Vimeo, Wikipedia, Wordpress.com, XKCD Comic, YFrog, YouTube</p> + Viddler Video, Vimeo, Wikipedia, Wordpress.com, XKCD Comic, YFrog, YouTube</div> </div> '; } diff --git a/addon/statusnet/statusnet.css b/addon/statusnet/statusnet.css index 4e27b0938..6c1347fc7 100644 --- a/addon/statusnet/statusnet.css +++ b/addon/statusnet/statusnet.css @@ -12,25 +12,32 @@ } #statusnet-disconnect-label { float: left; - width: 200px; + width: 250px; + margin-bottom: 25px; +} +#statusnet-default-label { + float: left; + width: 250px; margin-bottom: 25px; } #statusnet-disconnect { float: left; } + #statusnet-enable-label { float: left; - width: 200px; + width: 250px; margin-bottom: 5px; } #statusnet-checkbox { float: left; } + #statusnet-pin-label { float: left; - width: 200px; + width: 250px; margin-bottom: 25px; } #statusnet-pin { @@ -40,7 +47,7 @@ #statusnet-consumerkey-label { float: left; - width: 200px; + width: 250px; margin-bottom: 8px; } #statusnet-consumerkey { @@ -49,7 +56,7 @@ } #statusnet-consumersecret-label { float: left; - width: 200px; + width: 250px; margin-bottom: 8px; } #statusnet-consumersecret { @@ -58,7 +65,7 @@ } #statusnet-baseapi-label { float: left; - width: 200px; + width: 250px; margin-bottom: 25px; } #statusnet-baseapi { diff --git a/addon/statusnet/statusnet.php b/addon/statusnet/statusnet.php index f763cd0c3..1d12292c8 100644 --- a/addon/statusnet/statusnet.php +++ b/addon/statusnet/statusnet.php @@ -90,18 +90,40 @@ function statusnet_settings_post ($a,$post) { * if the statusnet-disconnect checkbox is set, clear the statusnet configuration * TODO can we revoke the access tokens at Twitter and do we need to do so? */ - del_pconfig( local_user(), 'statusnet', 'consumerkey' ); - del_pconfig( local_user(), 'statusnet', 'consumersecret' ); - del_pconfig( local_user(), 'statusnet', 'post' ); - del_pconfig( local_user(), 'statusnet', 'oauthtoken' ); + del_pconfig( local_user(), 'statusnet', 'consumerkey' ); + del_pconfig( local_user(), 'statusnet', 'consumersecret' ); + del_pconfig( local_user(), 'statusnet', 'post' ); + del_pconfig( local_user(), 'statusnet', 'post_by_default' ); + del_pconfig( local_user(), 'statusnet', 'oauthtoken' ); del_pconfig( local_user(), 'statusnet', 'oauthsecret' ); del_pconfig( local_user(), 'statusnet', 'baseapi' ); } else { if (isset($_POST['statusnet-consumersecret'])) { - set_pconfig(local_user(), 'statusnet', 'consumerkey', $_POST['statusnet-consumerkey']); - set_pconfig(local_user(), 'statusnet', 'consumersecret', $_POST['statusnet-consumersecret']); - set_pconfig(local_user(), 'statusnet', 'baseapi', $_POST['statusnet-baseapi']); - header('Location: '.$a->get_baseurl().'/settings/addon'); + // check if we can reach the API of the StatusNet server + // we'll check the API Version for that, if we don't get one we'll try to fix the path but will + // resign quickly after this one try to fix the path ;-) + $apibase = $_POST['statusnet-baseapi']; + $c = fetch_url( $apibase . 'statusnet/version.xml' ); + if (strlen($c) > 0) { + // ok the API path is correct, let's save the settings + set_pconfig(local_user(), 'statusnet', 'consumerkey', $_POST['statusnet-consumerkey']); + set_pconfig(local_user(), 'statusnet', 'consumersecret', $_POST['statusnet-consumersecret']); + set_pconfig(local_user(), 'statusnet', 'baseapi', $apibase ); + } else { + // the API path is not correct, maybe missing trailing / ? + $apibase = $apibase . '/'; + $c = fetch_url( $apibase . 'statusnet/version.xml' ); + if (strlen($c) > 0) { + // ok the API path is now correct, let's save the settings + set_pconfig(local_user(), 'statusnet', 'consumerkey', $_POST['statusnet-consumerkey']); + set_pconfig(local_user(), 'statusnet', 'consumersecret', $_POST['statusnet-consumersecret']); + set_pconfig(local_user(), 'statusnet', 'baseapi', $apibase ); + } else { + // still not the correct API base, let's do noting + notice( t('We could not contact the StatusNet API with the Path you entered.').EOL ); + } + } + goaway($a->get_baseurl().'/settings/addon'); } else { if (isset($_POST['statusnet-pin'])) { // if the user supplied us with a PIN from Twitter, let the magic of OAuth happen @@ -119,11 +141,13 @@ function statusnet_settings_post ($a,$post) { set_pconfig(local_user(),'statusnet', 'oauthsecret', $token['oauth_token_secret']); set_pconfig(local_user(),'statusnet', 'post', 1); // reload the Addon Settings page, if we don't do it see Bug #42 - header('Location: '.$a->get_baseurl().'/settings/addon'); + goaway($a->get_baseurl().'/settings/addon'); } else { // if no PIN is supplied in the POST variables, the user has changed the setting // to post a tweet for every new __public__ posting to the wall set_pconfig(local_user(),'statusnet','post',intval($_POST['statusnet-enable'])); + set_pconfig(local_user(),'statusnet','post_by_default',intval($_POST['statusnet-default'])); + notice( t('StatusNet settings updated.') . EOL); }}} } function statusnet_settings(&$a,&$s) { @@ -133,6 +157,7 @@ function statusnet_settings(&$a,&$s) { /*** * 1) Check that we have a base api url and a consumer key & secret * 2) If no OAuthtoken & stuff is present, generate button to get some + * allow the user to cancel the connection process at this step * 3) Checkbox for "Send public notices (respect size limitation) */ $api = get_pconfig(local_user(), 'statusnet', 'baseapi'); @@ -140,8 +165,10 @@ function statusnet_settings(&$a,&$s) { $csecret = get_pconfig(local_user(), 'statusnet', 'consumersecret' ); $otoken = get_pconfig(local_user(), 'statusnet', 'oauthtoken' ); $osecret = get_pconfig(local_user(), 'statusnet', 'oauthsecret' ); - $enabled = get_pconfig(local_user(), 'statusnet', 'post'); + $enabled = get_pconfig(local_user(), 'statusnet', 'post'); $checked = (($enabled) ? ' checked="checked" ' : ''); + $defenabled = get_pconfig(local_user(),'statusnet','post_by_default'); + $defchecked = (($defenabled) ? ' checked="checked" ' : ''); $s .= '<div class="settings-block">'; $s .= '<h3>'. t('StatusNet Posting Settings').'</h3>'; @@ -187,6 +214,13 @@ function statusnet_settings(&$a,&$s) { $s .= '<input id="statusnet-token2" type="hidden" name="statusnet-token2" value="'.$request_token['oauth_token_secret'].'" />'; $s .= '</div><div class="clear"></div>'; $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="submit" class="settings-submit" value="' . t('Submit') . '" /></div>'; + $s .= '<h4>'.t('Cancel Connection Process').'</h4>'; + $s .= '<div id="statusnet-cancel-wrapper">'; + $s .= '<p>'.t('Current StatusNet API is').': '.$api.'</p>'; + $s .= '<label id="statusnet-cancel-label" for="statusnet-cancel">'. t('Cancel StatusNet Connection') . '</label>'; + $s .= '<input id="statusnet-cancel" type="checkbox" name="statusnet-disconnect" value="1" />'; + $s .= '</div><div class="clear"></div>'; + $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="submit" class="settings-submit" value="' . t('Submit') . '" /></div>'; } else { /*** * we have an OAuth key / secret pair for the user @@ -195,11 +229,15 @@ function statusnet_settings(&$a,&$s) { $connection = new StatusNetOAuth($api,$ckey,$csecret,$otoken,$osecret); $details = $connection->get('account/verify_credentials'); $s .= '<div id="statusnet-info" ><img id="statusnet-avatar" src="'.$details->profile_image_url.'" /><p id="statusnet-info-block">'. t('Currently connected to: ') .'<a href="'.$details->statusnet_profile_url.'" target="_statusnet">'.$details->screen_name.'</a><br /><em>'.$details->description.'</em></p></div>'; - $s .= '<p>'. t('If enabled all your <strong>public</strong> postings will be posted to the associated StatusNet account as well.') .'</p>'; + $s .= '<p>'. t('If enabled all your <strong>public</strong> postings will be posted to the associated StatusNet account.') .'</p>'; $s .= '<div id="statusnet-enable-wrapper">'; - $s .= '<label id="statusnet-enable-label" for="statusnet-checkbox">'. t('Send public postings to StatusNet') .'</label>'; + $s .= '<label id="statusnet-enable-label" for="statusnet-checkbox">'. t('Allow posting to StatusNet') .'</label>'; $s .= '<input id="statusnet-checkbox" type="checkbox" name="statusnet-enable" value="1" ' . $checked . '/>'; + $s .= '<div class="clear"></div>'; + $s .= '<label id="statusnet-default-label" for="statusnet-default">'. t('Send public postings to StatusNet by default') .'</label>'; + $s .= '<input id="statusnet-default" type="checkbox" name="statusnet-default" value="1" ' . $defchecked . '/>'; $s .= '</div><div class="clear"></div>'; + $s .= '<div id="statusnet-disconnect-wrapper">'; $s .= '<label id="statusnet-disconnect-label" for="statusnet-disconnect">'. t('Clear OAuth configuration') .'</label>'; $s .= '<input id="statusnet-disconnect" type="checkbox" name="statusnet-disconnect" value="1" />'; diff --git a/addon/twitter/twitter.php b/addon/twitter/twitter.php index bb424fb65..c59d1b9e5 100644 --- a/addon/twitter/twitter.php +++ b/addon/twitter/twitter.php @@ -138,7 +138,7 @@ function twitter_settings(&$a,&$s) { * which the user can request a PIN to connect the account to a * account at Twitter. */ - require_once('library/twitteroauth.php'); + require_once('library/twitteroauth.php'); $connection = new TwitterOAuth($ckey, $csecret); $request_token = $connection->getRequestToken(); $token = $request_token['oauth_token']; diff --git a/addon/widgets/widget_friends.php b/addon/widgets/widget_friends.php new file mode 100644 index 000000000..2286f68ca --- /dev/null +++ b/addon/widgets/widget_friends.php @@ -0,0 +1,32 @@ +<?php + +function friends_widget_name() { + return "Shows profile contacts"; +} +function friends_widget_help() { + return ""; +} + +function friends_widget_args(){ + return Array(); +} + +function friends_widget_content(&$a, $conf){ + + $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `user`.* FROM `profile` + LEFT JOIN `user` ON `profile`.`uid` = `user`.`uid` + WHERE `user`.`uid` = %s AND `profile`.`is-default` = 1 LIMIT 1", + intval($conf['uid']) + ); + if(!count($r)) return; + $a->profile = $r[0]; + + $o = ""; + $o .= "<style> + .f9k_widget .contact-block-div { display: block !important; float: left!important; width: 50px!important; height: 50px!important; margin: 2px!important;} + .f9k_widget #contact-block-end { clear: left; } + </style>"; + $o .= _abs_url(contact_block()); + $o .= "<a href='".$a->get_baseurl().'/profile/'.$a->profile['nickname']."'>". t('Connect on Friendika!') ."</a>"; + return $o; +} diff --git a/addon/widgets/widget_like.php b/addon/widgets/widget_like.php new file mode 100644 index 000000000..9b54212a8 --- /dev/null +++ b/addon/widgets/widget_like.php @@ -0,0 +1,22 @@ +<?php + +function like_widget_name() { + return "Shows likes"; +} +function like_widget_help() { + return "Search first item wich contains <em>KEY</em> and print like/dislike count"; +} + +function like_widget_args(){ + return Array("KEY"); +} + +function like_widget_content(&$a, $conf){ + $args = explode(",",$_GET['a']); + + if ($args[0]!=""){ + return " #TODO like/dislike count for item with <em>" .$args[0]. "</em> # "; + } else { + return " #TODO# "; + } +} diff --git a/addon/widgets/widgets.js b/addon/widgets/widgets.js new file mode 100644 index 000000000..45d36c4d7 --- /dev/null +++ b/addon/widgets/widgets.js @@ -0,0 +1,64 @@ +/** + * @author Fabio Comuni + */ + +var f9a_widget_$widget_id = { + entrypoint : "$entrypoint", + key : "$key", + widgetid: "$widget_id", + argstr: "$args", + xmlhttp : null, + + getXHRObj : function(){ + if (window.XMLHttpRequest) { + // code for IE7+, Firefox, Chrome, Opera, Safari + this.xmlhttp = new XMLHttpRequest(); + } else { + // code for IE6, IE5 + this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } + }, + + dorequest : function(args, cb) { + if (args===null) args = new Array(); + args['k']=this.key; + args['s']=window.location; + args['a']=this.argstr; + var urlencodedargs = new Array(); + for(k in args){ urlencodedargs.push( encodeURIComponent(k)+"="+encodeURIComponent(args[k]) ); } + + var url = this.entrypoint + "?"+ urlencodedargs.join("&"); + + this.xmlhttp.open("GET", url ,true); + this.xmlhttp.send(); + this.xmlhttp.obj = this; + this.xmlhttp.onreadystatechange=function(){ + if (this.readyState==4){ + if (this.status==200) { + cb(this.obj, this.responseText); + } else { + document.getElementById(this.obj.widgetid).innerHTML="Error loading widget."; + } + } + } + + }, + + requestcb: function(obj, responseText) { + document.getElementById(obj.widgetid).innerHTML=responseText; + }, + + load : function (){ + this.getXHRObj(); + this.dorequest(null, this.requestcb); + } + +}; + +(function() { + f9a_widget_$widget_id.load(); +})(); + +document.writeln("<div id='$widget_id' class='f9k_widget'>"); +document.writeln("<img id='$widget_id_ld' src='$loader'>"); +document.writeln("</div>"); diff --git a/addon/widgets/widgets.php b/addon/widgets/widgets.php new file mode 100644 index 000000000..6bd7a73d1 --- /dev/null +++ b/addon/widgets/widgets.php @@ -0,0 +1,168 @@ +<?php + /** + * widgets from friendika + * + * allow to embed info from friendika into another site + */ + + +function widgets_install() { + // we need some hooks, for the configuration and for sending tweets + register_hook('plugin_settings', 'addon/widgets/widgets.php', 'widgets_settings'); + register_hook('plugin_settings_post', 'addon/widgets/widgets.php', 'widgets_settings_post'); + + logger("installed widgets"); +} + +function widgets_settings_post(){ + + if (isset($_POST['widgets-submit'])){ + del_pconfig(local_user(), 'widgets', 'key'); + + } +} + +function widgets_settings(&$a,&$o) { + if(! local_user()) + return; + + + $key = get_pconfig(local_user(), 'widgets', 'key' ); + if ($key=='') { $key = mt_rand(); set_pconfig(local_user(), 'widgets', 'key', $key); } + + $o .='<h3 class="settings-heading">Widgets</h3>'; + + + $o.=' + <div id="settings-username-wrapper"> + '. t('Widgets key: ') .'<strong>'.$key.'</strong> + </div> + <div id="settings-username-end"></div> + <div class="settings-submit-wrapper"> + <input type="submit" value="'.t('Generate new key').'" class="settings-submit" name="widgets-submit"> + </div>'; + + + $o.='<h4>Widgets:</h4>'; + $o .= '<ul>'; + $d = dir(dirname(__file__)); + while(false !== ($f = $d->read())) { + if(substr($f,0,7)=="widget_") { + preg_match("|widget_([^.]+).php|", $f, $m); + $w=$m[1]; + require_once($f); + $o.='<li><a href="'.$a->get_baseurl().'/widgets/'.$w.'/?k='.$key.'&p=1">'. call_user_func($w."_widget_name") .'</a></li>'; + } + } + + $o .= '</ul>'; + +} + +function widgets_module() { + return; +} + +function _abs_url($s){ + $a = get_app(); + return preg_replace("|href=(['\"])([^h][^t][^t][^p])|", "href=\$1".$a->get_baseurl()."/\$2", $s); +} + + +function widgets_content(&$a) { + + if (!isset($_GET['k'])) { + if($a->argv[2]=="cb"){header('HTTP/1.0 400 Bad Request'); killme();} + return; + } + + $r = q("SELECT * FROM pconfig WHERE uid IN (SELECT uid FROM pconfig WHERE v='%s')AND cat='widgets'", + dbesc($_GET['k']) + ); + if (!count($r)){ + if($a->argv[2]=="cb"){header('HTTP/1.0 400 Bad Request'); killme();} + return; + } + $conf = array(); + $conf['uid'] = $r[0]['uid']; + foreach($r as $e) { $conf[$e['k']]=$e['v']; } + + $o = ""; + + $widgetfile =dirname(__file__)."/widget_".$a->argv[1].".php"; + if (file_exists($widgetfile)){ + require_once($widgetfile); + } else { + if($a->argv[2]=="cb"){header('HTTP/1.0 400 Bad Request'); killme();} + return; + } + + + + + //echo "<pre>"; var_dump($a->argv); die(); + if ($a->argv[2]=="cb"){ + /*if (!local_user()){ + if (!isset($_GET['s'])) + {header('HTTP/1.0 400 Bad Request'); killme();} + + if (substr($_GET['s'],0,strlen($conf['site'])) !== $conf['site']) + {header('HTTP/1.0 400 Bad Request'); killme();} + } */ + $o .= call_user_func($a->argv[1].'_widget_content',$a, $conf); + + } else { + + + if (isset($_GET['p']) && local_user()==$conf['uid'] ) { + $o .= "<style>.f9k_widget { float: left;border:1px solid black; }</style>"; + $o .= "<h1>Preview Widget</h1>"; + $o .= '<a href="'.$a->get_baseurl().'/settings/addon">'. t("Plugin Settings") .'</a>'; + + $o .= "<h4>".call_user_func($a->argv[1].'_widget_name')."</h4>"; + $o .= call_user_func($a->argv[1].'_widget_help'); + $o .= "<br style='clear:left'/><br/>"; + $o .= "<script>"; + } else { + header("content-type: application/x-javascript"); + } + + + + + $script = file_get_contents(dirname(__file__)."/widgets.js"); + $o .= replace_macros($script, array( + '$entrypoint' => $a->get_baseurl()."/widgets/".$a->argv[1]."/cb/", + '$key' => $conf['key'], + '$widget_id' => 'f9k_'.$a->argv[1]."_".time(), + '$loader' => $a->get_baseurl()."/images/rotator.gif", + '$args' => (isset($_GET['a'])?$_GET['a']:''), + )); + + + if (isset($_GET['p'])) { + $jsargs = implode("</em>,<em>", call_user_func($a->argv[1].'_widget_args')); + if ($jsargs!='') $jsargs = "&a=<em>".$jsargs."</em>"; + + $o .= "</script> + <br style='clear:left'/><br/> + <h4>Copy and paste this code</h4> + <code>" + + .htmlspecialchars('<script src="'.$a->get_baseurl().'/widgets/'.$a->argv[1].'?k='.$conf['key']) + .$jsargs + .htmlspecialchars('"></script>') + ."</code>"; + return $o; + } + + } + + echo $o; + killme(); +} + + + + +?> |