From b5869e7961dd284c542f32769391f69d0a78c3de Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Thu, 5 Jun 2014 17:45:13 +0200 Subject: [PATCH 1/4] Facebook: First changes to make the connector maybe work with the second version of the Facebook API Twitter/Facebook: New functions for expiring of imported posts from twitter and facebook. --- fbpost/fbpost.php | 15 ++++++++------- fbsync/fbsync.php | 27 ++++++++++++++++++++++++++- twitter/twitter.php | 32 +++++++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/fbpost/fbpost.php b/fbpost/fbpost.php index b5e320fc5..60c8e9392 100644 --- a/fbpost/fbpost.php +++ b/fbpost/fbpost.php @@ -248,7 +248,7 @@ function fbpost_content(&$a) { //read_stream,publish_stream,manage_pages,photo_upload,user_groups,offline_access $o .= '' . t('Install Facebook Post connector for this account.') . ''; + . $a->get_baseurl() . '/fbpost/' . $a->user['nickname'] . '&scope=export_stream,read_stream,publish_stream,manage_pages,photo_upload,user_groups,publish_actions,user_friends,share_item,video_upload,status_update">' . t('Install Facebook Post connector for this account.') . ''; $o .= ''; } @@ -260,7 +260,7 @@ function fbpost_content(&$a) { $o .= '
'; $o .= '' . t('Re-authenticate [This is necessary whenever your Facebook password is changed.]') . ''; + . $a->get_baseurl() . '/fbpost/' . $a->user['nickname'] . '&scope=export_stream,read_stream,publish_stream,manage_pages,photo_upload,user_groups,publish_actions,user_friends,share_item,video_upload,status_update">' . t('Re-authenticate [This is necessary whenever your Facebook password is changed.]') . ''; $o .= '
'; $o .= '
'; @@ -666,11 +666,13 @@ function fbpost_post_hook(&$a,&$b) { $postvars['message'] = $msg; $url = 'https://graph.facebook.com/'.$target.'/photos'; - } else if (($link != "") or ($image != "") or ($b['title'] == '') or (strlen($msg) < 500)) { + //} else if (($link != "") or ($image != "") or ($b['title'] == '') or (strlen($msg) < 500)) { + } else { $url = 'https://graph.facebook.com/'.$target.'/feed'; if (!get_pconfig($b['uid'],'facebook','suppress_view_on_friendica') and $b['plink']) $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' . $b['plink'] . '"}'; - } else { + } +/* } else { // if its only a message and a subject and the message is larger than 500 characters then post it as note $postvars = array( 'access_token' => $fb_token, @@ -678,7 +680,7 @@ function fbpost_post_hook(&$a,&$b) { 'subject' => $b['title'], ); $url = 'https://graph.facebook.com/'.$target.'/notes'; - } + } */ // Post to page? if (!$reply and ($target != "me") and $page_access_token) @@ -946,6 +948,7 @@ function fbpost_cron($a,$b) { function fbpost_fetchwall($a, $uid) { require_once("include/oembed.php"); + require_once('mod/item.php'); $access_token = get_pconfig($uid,'facebook','access_token'); $post_to_page = get_pconfig($uid,'facebook','post_to_page'); @@ -1081,8 +1084,6 @@ function fbpost_fetchwall($a, $uid) { //print_r($_REQUEST); logger('facebook: posting for user '.$uid); - - require_once('mod/item.php'); item_post($a); } diff --git a/fbsync/fbsync.php b/fbsync/fbsync.php index 21779769e..858591519 100644 --- a/fbsync/fbsync.php +++ b/fbsync/fbsync.php @@ -26,6 +26,7 @@ function fbsync_install() { register_hook('connector_settings_post', 'addon/fbsync/fbsync.php', 'fbsync_settings_post'); register_hook('cron', 'addon/fbsync/fbsync.php', 'fbsync_cron'); register_hook('follow', 'addon/fbsync/fbsync.php', 'fbsync_follow'); + register_hook('expire', 'addon/fbsync/fbsync.php', 'fbsync_expire'); } function fbsync_uninstall() { @@ -33,6 +34,7 @@ function fbsync_uninstall() { unregister_hook('connector_settings_post', 'addon/fbsync/fbsync.php', 'fbsync_settings_post'); unregister_hook('cron', 'addon/fbsync/fbsync.php', 'fbsync_cron'); unregister_hook('follow', 'addon/fbsync/fbsync.php', 'fbsync_follow'); + unregister_hook('expire', 'addon/fbsync/fbsync.php', 'fbsync_expire'); } function fbsync_follow($a, &$contact) { @@ -185,6 +187,30 @@ function fbsync_cron($a,$b) { set_config('fbsync','last_poll', time()); } +function fbsync_expire($a,$b) { + + $days = get_config('fbsync', 'expire'); + + if ($days == 0) + return; + + $r = q("DELETE FROM `item` WHERE `deleted` AND `network` = '%s'", dbesc(NETWORK_FACEBOOK)); + + require_once("include/items.php"); + + logger('fbsync_expire: expire_start'); + + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'fbsync' AND `k` = 'sync' AND `v` = '1' ORDER BY RAND()"); + if(count($r)) { + foreach($r as $rr) { + logger('fbsync_expire: user '.$rr['uid']); + item_expire($rr['uid'], $days, NETWORK_FACEBOOK, true); + } + } + + logger('fbsync_expire: expire_end'); +} + function fbsync_createpost($a, $uid, $self, $contacts, $applications, $post, $create_user) { require_once("include/oembed.php"); @@ -1018,7 +1044,6 @@ function fbsync_fetchfeed($a, $uid) { foreach ($post_data AS $post) { if ($post->updated_time > $last_updated) $last_updated = $post->updated_time; - fbsync_createpost($a, $uid, $self, $contacts, $application_data, $post, $create_user); } diff --git a/twitter/twitter.php b/twitter/twitter.php index 8a80f471b..16debcb7c 100755 --- a/twitter/twitter.php +++ b/twitter/twitter.php @@ -64,7 +64,7 @@ define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes function twitter_install() { // we need some hooks, for the configuration and for sending tweets - register_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); + register_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); register_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post'); register_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local'); register_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook'); @@ -72,12 +72,13 @@ function twitter_install() { register_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron'); register_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook'); register_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow'); + register_hook('expire', 'addon/twitter/twitter.php', 'twitter_expire'); logger("installed twitter"); } function twitter_uninstall() { - unregister_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); + unregister_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); unregister_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post'); unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local'); unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook'); @@ -85,10 +86,11 @@ function twitter_uninstall() { unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron'); unregister_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook'); unregister_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow'); + unregister_hook('expire', 'addon/twitter/twitter.php', 'twitter_expire'); // old setting - remove only unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook'); - unregister_hook('plugin_settings', 'addon/twitter/twitter.php', 'twitter_settings'); + unregister_hook('plugin_settings', 'addon/twitter/twitter.php', 'twitter_settings'); unregister_hook('plugin_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post'); } @@ -624,6 +626,30 @@ function twitter_cron($a,$b) { set_config('twitter','last_poll', time()); } +function twitter_expire($a,$b) { + + $days = get_config('twitter', 'expire'); + + if ($days == 0) + return; + + $r = q("DELETE FROM `item` WHERE `deleted` AND `network` = '%s'", dbesc(NETWORK_TWITTER)); + + require_once("include/items.php"); + + logger('twitter_expire: expire_start'); + + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()"); + if(count($r)) { + foreach($r as $rr) { + logger('twitter_expire: user '.$rr['uid']); + item_expire($rr['uid'], $days, NETWORK_TWITTER, true); + } + } + + logger('twitter_expire: expire_end'); +} + function twitter_fetchtimeline($a, $uid) { $ckey = get_config('twitter', 'consumerkey'); $csecret = get_config('twitter', 'consumersecret'); From 8b0b38ea0c7d114846ba9f10073f4978b9d9b1fc Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sat, 7 Jun 2014 10:27:48 +0200 Subject: [PATCH 2/4] App.net: New connector that will be expanded to a bidirectional connector in the future. --- appnet/AppDotNet.php | 1647 ++++++++++++++++++++++ appnet/DigiCertHighAssuranceEVRootCA.pem | 23 + appnet/appnet.css | 29 + appnet/appnet.php | 331 +++++ appnetpost/appnetpost.css | 0 5 files changed, 2030 insertions(+) create mode 100644 appnet/AppDotNet.php create mode 100644 appnet/DigiCertHighAssuranceEVRootCA.pem create mode 100755 appnet/appnet.css create mode 100644 appnet/appnet.php mode change 100755 => 100644 appnetpost/appnetpost.css diff --git a/appnet/AppDotNet.php b/appnet/AppDotNet.php new file mode 100644 index 000000000..323613145 --- /dev/null +++ b/appnet/AppDotNet.php @@ -0,0 +1,1647 @@ +_clientId = $client_id; + $this->_clientSecret = $client_secret; + + // if the digicert certificate exists in the same folder as this file, + // remember that fact for later + if (file_exists(dirname(__FILE__).'/DigiCertHighAssuranceEVRootCA.pem')) { + $this->_sslCA = dirname(__FILE__).'/DigiCertHighAssuranceEVRootCA.pem'; + } + } + + /** + * Set whether or not to strip Envelope Response (meta) information + * This option will be deprecated in the future. Is it to allow + * a stepped migration path between code expecting the old behavior + * and new behavior. When not stripped, you still can use the proper + * method to pull the meta information. Please start converting your code ASAP + */ + public function includeResponseEnvelope() { + $this->_stripResponseEnvelope=false; + } + + /** + * Construct the proper Auth URL for the user to visit and either grant + * or not access to your app. Usually you would place this as a link for + * the user to client, or a redirect to send them to the auth URL. + * Also can be called after authentication for additional scopes + * @param string $callbackUri Where you want the user to be directed + * after authenticating with App.net. This must be one of the URIs + * allowed by your App.net application settings. + * @param array $scope An array of scopes (permissions) you wish to obtain + * from the user. Currently options are stream, email, write_post, follow, + * messages, and export. If you don't specify anything, you'll only receive + * access to the user's basic profile (the default). + */ + public function getAuthUrl($callback_uri,$scope=null) { + + // construct an authorization url based on our client id and other data + $data = array( + 'client_id'=>$this->_clientId, + 'response_type'=>'code', + 'redirect_uri'=>$callback_uri, + ); + + $url = $this->_authUrl; + if ($this->_accessToken) { + $url .= 'authorize?'; + } else { + $url .= 'authenticate?'; + } + $url .= $this->buildQueryString($data); + + if ($scope) { + $url .= '&scope='.implode('+',$scope); + } + + // return the constructed url + return $url; + } + + /** + * Call this after they return from the auth page, or anytime you need the + * token. For example, you could store it in a database and use + * setAccessToken() later on to return on behalf of the user. + */ + public function getAccessToken($callback_uri) { + // if there's no access token set, and they're returning from + // the auth page with a code, use the code to get a token + if (!$this->_accessToken && isset($_GET['code']) && $_GET['code']) { + + // construct the necessary elements to get a token + $data = array( + 'client_id'=>$this->_clientId, + 'client_secret'=>$this->_clientSecret, + 'grant_type'=>'authorization_code', + 'redirect_uri'=>$callback_uri, + 'code'=>$_GET['code'] + ); + + // try and fetch the token with the above data + $res = $this->httpReq('post',$this->_authUrl.'access_token', $data); + + // store it for later + $this->_accessToken = $res['access_token']; + $this->_username = $res['username']; + $this->_user_id = $res['user_id']; + } + + // return what we have (this may be a token, or it may be nothing) + return $this->_accessToken; + } + + /** + * Check the scope of current token to see if it has required scopes + * has to be done after a check + */ + public function checkScopes($app_scopes) { + if (!count($this->_scopes)) { + return -1; // _scope is empty + } + $missing=array(); + foreach($app_scopes as $scope) { + if (!in_array($scope,$this->_scopes)) { + if ($scope=='public_messages') { + // messages works for public_messages + if (in_array('messages',$this->_scopes)) { + // if we have messages in our scopes + continue; + } + } + $missing[]=$scope; + } + } + // identify the ones missing + if (count($missing)) { + // do something + return $missing; + } + return 0; // 0 missing + } + + /** + * Set the access token (eg: after retrieving it from offline storage) + * @param string $token A valid access token you're previously received + * from calling getAccessToken(). + */ + public function setAccessToken($token) { + $this->_accessToken = $token; + } + + /** + * Deauthorize the current token (delete your authorization from the API) + * Generally this is useful for logging users out from a web app, so they + * don't get automatically logged back in the next time you redirect them + * to the authorization URL. + */ + public function deauthorizeToken() { + return $this->httpReq('delete',$this->_baseUrl.'token'); + } + + /** + * Retrieve an app access token from the app.net API. This allows you + * to access the API without going through the user access flow if you + * just want to (eg) consume global. App access tokens are required for + * some actions (like streaming global). DO NOT share the return value + * of this function with any user (or save it in a cookie, etc). This + * is considered secret info for your app only. + * @return string The app access token + */ + public function getAppAccessToken() { + + // construct the necessary elements to get a token + $data = array( + 'client_id'=>$this->_clientId, + 'client_secret'=>$this->_clientSecret, + 'grant_type'=>'client_credentials', + ); + + // try and fetch the token with the above data + $res = $this->httpReq('post',$this->_authUrl.'access_token', $data); + + // store it for later + $this->_appAccessToken = $res['access_token']; + $this->_accessToken = $res['access_token']; + $this->_username = null; + $this->_user_id = null; + + return $this->_accessToken; + } + + /** + * Returns the total number of requests you're allowed within the + * alloted time period. + * @see getRateLimitReset() + */ + public function getRateLimit() { + return $this->_rateLimit; + } + + /** + * The number of requests you have remaining within the alloted time period + * @see getRateLimitReset() + */ + public function getRateLimitRemaining() { + return $this->_rateLimitRemaining; + } + + /** + * The number of seconds remaining in the alloted time period. + * When this time is up you'll have getRateLimit() available again. + */ + public function getRateLimitReset() { + return $this->_rateLimitReset; + } + + /** + * The scope the user has + */ + public function getScope() { + return $this->_scope; + } + + /** + * Internal function, parses out important information App.net adds + * to the headers. + */ + protected function parseHeaders($response) { + // take out the headers + // set internal variables + // return the body/content + $this->_rateLimit = null; + $this->_rateLimitRemaining = null; + $this->_rateLimitReset = null; + $this->_scope = null; + + $response = explode("\r\n\r\n",$response,2); + $headers = $response[0]; + + if($headers == 'HTTP/1.1 100 Continue') { + $response = explode("\r\n\r\n",$response[1],2); + $headers = $response[0]; + } + + if (isset($response[1])) { + $content = $response[1]; + } + else { + $content = null; + } + + // this is not a good way to parse http headers + // it will not (for example) take into account multiline headers + // but what we're looking for is pretty basic, so we can ignore those shortcomings + $headers = explode("\r\n",$headers); + foreach ($headers as $header) { + $header = explode(': ',$header,2); + if (count($header)<2) { + continue; + } + list($k,$v) = $header; + switch ($k) { + case 'X-RateLimit-Remaining': + $this->_rateLimitRemaining = $v; + break; + case 'X-RateLimit-Limit': + $this->_rateLimit = $v; + break; + case 'X-RateLimit-Reset': + $this->_rateLimitReset = $v; + break; + case 'X-OAuth-Scopes': + $this->_scope = $v; + $this->_scopes=explode(',',$v); + break; + } + } + return $content; + } + + /** + * Internal function. Used to turn things like TRUE into 1, and then + * calls http_build_query. + */ + protected function buildQueryString($array) { + foreach ($array as $k=>&$v) { + if ($v===true) { + $v = '1'; + } + elseif ($v===false) { + $v = '0'; + } + unset($v); + } + return http_build_query($array); + } + + + /** + * Internal function to handle all + * HTTP requests (POST,PUT,GET,DELETE) + */ + protected function httpReq($act, $req, $params=array(),$contentType='application/x-www-form-urlencoded') { + $ch = curl_init($req); + $headers = array(); + if($act != 'get') { + curl_setopt($ch, CURLOPT_POST, true); + // if they passed an array, build a list of parameters from it + if (is_array($params) && $act != 'post-raw') { + $params = $this->buildQueryString($params); + } + curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + $headers[] = "Content-Type: ".$contentType; + } + if($act != 'post' && $act != 'post-raw') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($act)); + } + if($act == 'get' && isset($params['access_token'])) { + $headers[] = 'Authorization: Bearer '.$params['access_token']; + } + else if ($this->_accessToken) { + $headers[] = 'Authorization: Bearer '.$this->_accessToken; + } + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLINFO_HEADER_OUT, true); + curl_setopt($ch, CURLOPT_HEADER, true); + if ($this->_sslCA) { + curl_setopt($ch, CURLOPT_CAINFO, $this->_sslCA); + } + $this->_last_response = curl_exec($ch); + $this->_last_request = curl_getinfo($ch,CURLINFO_HEADER_OUT); + $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + if ($http_status==0) { + throw new AppDotNetException('Unable to connect to '.$req); + } + if ($http_status<200 || $http_status>=300) { + throw new AppDotNetException('HTTP error '.$this->_last_response); + } + if ($this->_last_request===false) { + if (!curl_getinfo($ch,CURLINFO_SSL_VERIFYRESULT)) { + throw new AppDotNetException('SSL verification failed, connection terminated.'); + } + } + $response = $this->parseHeaders($this->_last_response); + $response = json_decode($response,true); + + if (isset($response['meta'])) { + if (isset($response['meta']['max_id'])) { + $this->_maxid=$response['meta']['max_id']; + $this->_minid=$response['meta']['min_id']; + } + if (isset($response['meta']['more'])) { + $this->_more=$response['meta']['more']; + } + if (isset($response['meta']['marker'])) { + $this->_last_marker=$response['meta']['marker']; + } + } + + // look for errors + if (isset($response['error'])) { + if (is_array($response['error'])) { + throw new AppDotNetException($response['error']['message'], + $response['error']['code']); + } + else { + throw new AppDotNetException($response['error']); + } + } + + // look for response migration errors + elseif (isset($response['meta']) && isset($response['meta']['error_message'])) { + throw new AppDotNetException($response['meta']['error_message'],$response['meta']['code']); + } + + // if we've received a migration response, handle it and return data only + elseif ($this->_stripResponseEnvelope && isset($response['meta']) && isset($response['data'])) { + return $response['data']; + } + + // else non response migration response, just return it + else { + return $response; + } + } + + + /** + * Get max_id from last meta response data envelope + */ + public function getResponseMaxID() { + return $this->_maxid; + } + + /** + * Get min_id from last meta response data envelope + */ + public function getResponseMinID() { + return $this->_minid; + } + + /** + * Get more from last meta response data envelope + */ + public function getResponseMore() { + return $this->_more; + } + + /** + * Get marker from last meta response data envelope + */ + public function getResponseMarker() { + return $this->_last_marker; + } + + /** + * Fetch API configuration object + */ + public function getConfig() { + return $this->httpReq('get',$this->_baseUrl.'config'); + } + + /** + * Return the Filters for the current user. + */ + public function getAllFilters() { + return $this->httpReq('get',$this->_baseUrl.'filters'); + } + + /** + * Create a Filter for the current user. + * @param string $name The name of the new filter + * @param array $filters An associative array of filters to be applied. + * This may change as the API evolves, as of this writing possible + * values are: user_ids, hashtags, link_domains, and mention_user_ids. + * You will need to provide at least one filter name=>value pair. + */ + public function createFilter($name='New filter', $filters=array()) { + $filters['name'] = $name; + return $this->httpReq('post',$this->_baseUrl.'filters',$filters); + } + + /** + * Returns a specific Filter object. + * @param integer $filter_id The ID of the filter you wish to retrieve. + */ + public function getFilter($filter_id=null) { + return $this->httpReq('get',$this->_baseUrl.'filters/'.urlencode($filter_id)); + } + + /** + * Delete a Filter. The Filter must belong to the current User. + * @return object Returns the deleted Filter on success. + */ + public function deleteFilter($filter_id=null) { + return $this->httpReq('delete',$this->_baseUrl.'filters/'.urlencode($filter_id)); + } + + /** + * Process user description, message or post text. + * Mentions and hashtags will be parsed out of the + * text, as will bare URLs. To create a link in the text without using a + * bare URL, include the anchor text in the object text and include a link + * entity in the function call. + * @param string $text The text of the description/message/post + * @param array $data An associative array of optional post data. This + * will likely change as the API evolves, as of this writing allowed keys are: + * reply_to, and annotations. "annotations" may be a complex object represented + * by an associative array. + * @param array $params An associative array of optional data to be included + * in the URL (such as 'include_annotations' and 'include_machine') + * @return array An associative array representing the post. + */ + public function processText($text=null, $data = array(), $params = array()) { + $data['text'] = $text; + $json = json_encode($data); + $qs = ''; + if (!empty($params)) { + $qs = '?'.$this->buildQueryString($params); + } + return $this->httpReq('post',$this->_baseUrl.'text/process'.$qs, $json, 'application/json'); + } + + /** + * Create a new Post object. Mentions and hashtags will be parsed out of the + * post text, as will bare URLs. To create a link in a post without using a + * bare URL, include the anchor text in the post's text and include a link + * entity in the post creation call. + * @param string $text The text of the post + * @param array $data An associative array of optional post data. This + * will likely change as the API evolves, as of this writing allowed keys are: + * reply_to, and annotations. "annotations" may be a complex object represented + * by an associative array. + * @param array $params An associative array of optional data to be included + * in the URL (such as 'include_annotations' and 'include_machine') + * @return array An associative array representing the post. + */ + public function createPost($text=null, $data = array(), $params = array()) { + $data['text'] = $text; + + $json = json_encode($data); + $qs = ''; + if (!empty($params)) { + $qs = '?'.$this->buildQueryString($params); + } + return $this->httpReq('post',$this->_baseUrl.'posts'.$qs, $json, 'application/json'); + } + + /** + * Returns a specific Post. + * @param integer $post_id The ID of the post to retrieve + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: include_annotations. + * @return array An associative array representing the post + */ + public function getPost($post_id=null,$params = array()) { + return $this->httpReq('get',$this->_baseUrl.'posts/'.urlencode($post_id) + .'?'.$this->buildQueryString($params)); + } + + /** + * Delete a Post. The current user must be the same user who created the Post. + * It returns the deleted Post on success. + * @param integer $post_id The ID of the post to delete + * @param array An associative array representing the post that was deleted + */ + public function deletePost($post_id=null) { + return $this->httpReq('delete',$this->_baseUrl.'posts/'.urlencode($post_id)); + } + + /** + * Retrieve the Posts that are 'in reply to' a specific Post. + * @param integer $post_id The ID of the post you want to retrieve replies for. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: count, before_id, since_id, include_muted, include_deleted, + * include_directed_posts, and include_annotations. + * @return An array of associative arrays, each representing a single post. + */ + public function getPostReplies($post_id=null,$params = array()) { + return $this->httpReq('get',$this->_baseUrl.'posts/'.urlencode($post_id) + .'/replies?'.$this->buildQueryString($params)); + } + + /** + * Get the most recent Posts created by a specific User in reverse + * chronological order (most recent first). + * @param mixed $user_id Either the ID of the user you wish to retrieve posts by, + * or the string "me", which will retrieve posts for the user you're authenticated + * as. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: count, before_id, since_id, include_muted, include_deleted, + * include_directed_posts, and include_annotations. + * @return An array of associative arrays, each representing a single post. + */ + public function getUserPosts($user_id='me', $params = array()) { + return $this->httpReq('get',$this->_baseUrl.'users/'.urlencode($user_id) + .'/posts?'.$this->buildQueryString($params)); + } + + /** + * Get the most recent Posts mentioning by a specific User in reverse + * chronological order (newest first). + * @param mixed $user_id Either the ID of the user who is being mentioned, or + * the string "me", which will retrieve posts for the user you're authenticated + * as. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: count, before_id, since_id, include_muted, include_deleted, + * include_directed_posts, and include_annotations. + * @return An array of associative arrays, each representing a single post. + */ + public function getUserMentions($user_id='me',$params = array()) { + return $this->httpReq('get',$this->_baseUrl.'users/' + .urlencode($user_id).'/mentions?'.$this->buildQueryString($params)); + } + + /** + * Return the 20 most recent posts from the current User and + * the Users they follow. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: count, before_id, since_id, include_muted, include_deleted, + * include_directed_posts, and include_annotations. + * @return An array of associative arrays, each representing a single post. + */ + public function getUserStream($params = array()) { + return $this->httpReq('get',$this->_baseUrl.'posts/stream?'.$this->buildQueryString($params)); + } + + /** + * Returns a specific user object. + * @param mixed $user_id The ID of the user you want to retrieve, or the string + * "me" to retrieve data for the users you're currently authenticated as. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: include_annotations|include_user_annotations. + * @return array An associative array representing the user data. + */ + public function getUser($user_id='me', $params = array()) { + return $this->httpReq('get',$this->_baseUrl.'users/'.urlencode($user_id) + .'?'.$this->buildQueryString($params)); + } + + /** + * Returns multiple users request by an array of user ids + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: include_annotations|include_user_annotations. + * @return array An associative array representing the users data. + */ + public function getUsers($user_arr, $params = array()) { + return $this->httpReq('get',$this->_baseUrl.'users?ids='.join(',',$user_arr) + .'&'.$this->buildQueryString($params)); + } + + /** + * Add the specified user ID to the list of users followed. + * Returns the User object of the user being followed. + * @param integer $user_id The user ID of the user to follow. + * @return array An associative array representing the user you just followed. + */ + public function followUser($user_id=null) { + return $this->httpReq('post',$this->_baseUrl.'users/'.urlencode($user_id).'/follow'); + } + + /** + * Removes the specified user ID to the list of users followed. + * Returns the User object of the user being unfollowed. + * @param integer $user_id The user ID of the user to unfollow. + * @return array An associative array representing the user you just unfollowed. + */ + public function unfollowUser($user_id=null) { + return $this->httpReq('delete',$this->_baseUrl.'users/'.urlencode($user_id).'/follow'); + } + + /** + * Returns an array of User objects the specified user is following. + * @param mixed $user_id Either the ID of the user being followed, or + * the string "me", which will retrieve posts for the user you're authenticated + * as. + * @return array An array of associative arrays, each representing a single + * user following $user_id + */ + public function getFollowing($user_id='me') { + return $this->httpReq('get',$this->_baseUrl.'users/'.$user_id.'/following'); + } + + /** + * Returns an array of User objects for users following the specified user. + * @param mixed $user_id Either the ID of the user being followed, or + * the string "me", which will retrieve posts for the user you're authenticated + * as. + * @return array An array of associative arrays, each representing a single + * user following $user_id + */ + public function getFollowers($user_id='me') { + return $this->httpReq('get',$this->_baseUrl.'users/'.$user_id.'/followers'); + } + + /** + * Return Posts matching a specific #hashtag. + * @param string $hashtag The hashtag you're looking for. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: count, before_id, since_id, include_muted, include_deleted, + * include_directed_posts, and include_annotations. + * @return An array of associative arrays, each representing a single post. + */ + public function searchHashtags($hashtag=null, $params = array()) { + return $this->httpReq('get',$this->_baseUrl.'posts/tag/' + .urlencode($hashtag).'?'.$this->buildQueryString($params)); + } + + /** + * Retrieve a list of all public Posts on App.net, often referred to as the + * global stream. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: count, before_id, since_id, include_muted, include_deleted, + * include_directed_posts, and include_annotations. + * @return An array of associative arrays, each representing a single post. + */ + public function getPublicPosts($params = array()) { + return $this->httpReq('get',$this->_baseUrl.'posts/stream/global?'.$this->buildQueryString($params)); + } + + /** + * List User interactions + */ + public function getMyInteractions($params = array()) { + return $this->httpReq('get',$this->_baseUrl.'users/me/interactions?'.$this->buildQueryString($params)); + } + + /** + * Retrieve a user's user ID by specifying their username. + * Now supported by the API. We use the API if we have a token + * Otherwise we scrape the alpha.app.net site for the info. + * @param string $username The username of the user you want the ID of, without + * an @ symbol at the beginning. + * @return integer The user's user ID + */ + public function getIdByUsername($username=null) { + if ($this->_accessToken) { + $res=$this->httpReq('get',$this->_baseUrl.'users/@'.$username); + $user_id=$res['data']['id']; + } else { + $ch = curl_init('https://alpha.app.net/'.urlencode(strtolower($username))); + curl_setopt($ch, CURLOPT_POST, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch,CURLOPT_USERAGENT, + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:7.0.1) Gecko/20100101 Firefox/7.0.1'); + $response = curl_exec($ch); + curl_close($ch); + $temp = explode('title="User Id ',$response); + $temp2 = explode('"',$temp[1]); + $user_id = $temp2[0]; + } + return $user_id; + } + + /** + * Mute a user + * @param integer $user_id The user ID to mute + */ + public function muteUser($user_id=null) { + return $this->httpReq('post',$this->_baseUrl.'users/'.urlencode($user_id).'/mute'); + } + + /** + * Unmute a user + * @param integer $user_id The user ID to unmute + */ + public function unmuteUser($user_id=null) { + return $this->httpReq('delete',$this->_baseUrl.'users/'.urlencode($user_id).'/mute'); + } + + /** + * List the users muted by the current user + * @return array An array of associative arrays, each representing one muted user. + */ + public function getMuted() { + return $this->httpReq('get',$this->_baseUrl.'users/me/muted'); + } + + /** + * Star a post + * @param integer $post_id The post ID to star + */ + public function starPost($post_id=null) { + return $this->httpReq('post',$this->_baseUrl.'posts/'.urlencode($post_id).'/star'); + } + + /** + * Unstar a post + * @param integer $post_id The post ID to unstar + */ + public function unstarPost($post_id=null) { + return $this->httpReq('delete',$this->_baseUrl.'posts/'.urlencode($post_id).'/star'); + } + + /** + * List the posts starred by the current user + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: count, before_id, since_id, include_muted, include_deleted, + * include_directed_posts, and include_annotations. + * See https://github.com/appdotnet/api-spec/blob/master/resources/posts.md#general-parameters + * @return array An array of associative arrays, each representing a single + * user who has starred a post + */ + public function getStarred($user_id='me', $params = array()) { + return $this->httpReq('get',$this->_baseUrl.'users/'.urlencode($user_id).'/stars' + .'?'.$this->buildQueryString($params)); + } + + /** + * List the users who have starred a post + * @param integer $post_id the post ID to get stars from + * @return array An array of associative arrays, each representing one user. + */ + public function getStars($post_id=null) { + return $this->httpReq('get',$this->_baseUrl.'posts/'.urlencode($post_id).'/stars'); + } + + /** + * Returns an array of User objects of users who reposted the specified post. + * @param integer $post_id the post ID to + * @return array An array of associative arrays, each representing a single + * user who reposted $post_id + */ + public function getReposters($post_id){ + return $this->httpReq('get',$this->_baseUrl.'posts/'.urlencode($post_id).'/reposters'); + } + + /** + * Repost an existing Post object. + * @param integer $post_id The id of the post + * @return not a clue + */ + public function repost($post_id){ + return $this->httpReq('post',$this->_baseUrl.'posts/'.urlencode($post_id).'/repost'); + } + + /** + * Delete a post that the user has reposted. + * @param integer $post_id The id of the post + * @return not a clue + */ + public function deleteRepost($post_id){ + return $this->httpReq('delete',$this->_baseUrl.'posts/'.urlencode($post_id).'/repost'); + } + + /** + * List the posts who match a specific search term + * @param array $params a list of filter, search query, and general Post parameters + * see: https://developers.app.net/reference/resources/post/search/ + * @param string $query The search query. Supports + * normal search terms. Searches post text. + * @return array An array of associative arrays, each representing one post. + * or false on error + */ + public function searchPosts($params = array(), $query='', $order='default') { + if (!is_array($params)) { + return false; + } + if (!empty($query)) { + $params['query']=$query; + } + if ($order=='default') { + if (!empty($query)) { + $params['order']='score'; + } else { + $params['order']='id'; + } + } + return $this->httpReq('get',$this->_baseUrl.'posts/search?'.$this->buildQueryString($params)); + } + + + /** + * List the users who match a specific search term + * @param string $search The search query. Supports @username or #tag searches as + * well as normal search terms. Searches username, display name, bio information. + * Does not search posts. + * @return array An array of associative arrays, each representing one user. + */ + public function searchUsers($search="") { + return $this->httpReq('get',$this->_baseUrl.'users/search?q='.urlencode($search)); + } + + /** + * Return the 20 most recent posts for a stream using a valid Token + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: count, before_id, since_id, include_muted, include_deleted, + * include_directed_posts, and include_annotations. + * @return An array of associative arrays, each representing a single post. + */ + public function getTokenStream($params = array()) { + if ($params['access_token']) { + return $this->httpReq('get',$this->_baseUrl.'posts/stream?'.$this->buildQueryString($params),$params); + } else { + return $this->httpReq('get',$this->_baseUrl.'posts/stream?'.$this->buildQueryString($params)); + } + } + + /** + * Get a user object by username + * @param string $name the @name to get + * @return array representing one user + */ + public function getUserByName($name=null) { + return $this->httpReq('get',$this->_baseUrl.'users/@'.$name); + } + + /** + * Return the 20 most recent Posts from the current User's personalized stream + * and mentions stream merged into one stream. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: count, before_id, since_id, include_muted, include_deleted, + * include_directed_posts, and include_annotations. + * @return An array of associative arrays, each representing a single post. + */ + public function getUserUnifiedStream($params = array()) { + return $this->httpReq('get',$this->_baseUrl.'posts/stream/unified?'.$this->buildQueryString($params)); + } + + /** + * Update Profile Data via JSON + * @data array containing user descriptors + */ + public function updateUserData($data = array(), $params = array()) { + $json = json_encode($data); + return $this->httpReq('put',$this->_baseUrl.'users/me'.'?'. + $this->buildQueryString($params), $json, 'application/json'); + } + + /** + * Update a user image + * @which avatar|cover + * @image path reference to image + */ + protected function updateUserImage($which = 'avatar', $image = null) { + $data = array($which=>"@$image"); + return $this->httpReq('post-raw',$this->_baseUrl.'users/me/'.$which, $data, 'multipart/form-data'); + } + + public function updateUserAvatar($avatar = null) { + if($avatar != null) + return $this->updateUserImage('avatar', $avatar); + } + + public function updateUserCover($cover = null) { + if($cover != null) + return $this->updateUserImage('cover', $cover); + } + + /** + * update stream marker + */ + public function updateStreamMarker($data = array()) { + $json = json_encode($data); + return $this->httpReq('post',$this->_baseUrl.'posts/marker', $json, 'application/json'); + } + + /** + * get a page of current user subscribed channels + */ + public function getUserSubscriptions($params = array()) { + return $this->httpReq('get',$this->_baseUrl.'channels?'.$this->buildQueryString($params)); + } + + /** + * get user channels + */ + public function getMyChannels($params = array()) { + return $this->httpReq('get',$this->_baseUrl.'channels/me?'.$this->buildQueryString($params)); + } + + /** + * create a channel + * note: you cannot create a channel with type=net.app.core.pm (see createMessage) + */ + public function createChannel($data = array()) { + $json = json_encode($data); + return $this->httpReq('post',$this->_baseUrl.'channels'.($pm?'/pm/messsages':''), $json, 'application/json'); + } + + /** + * get channelid info + */ + public function getChannel($channelid, $params = array()) { + return $this->httpReq('get',$this->_baseUrl.'channels/'.$channelid.'?'.$this->buildQueryString($params)); + } + + /** + * get multiple channels' info by an array of channelids + */ + public function getChannels($channels, $params = array()) { + return $this->httpReq('get',$this->_baseUrl.'channels?ids='.join(',',$channels).'&'.$this->buildQueryString($params)); + } + + /** + * update channelid + */ + public function updateChannel($channelid, $data = array()) { + $json = json_encode($data); + return $this->httpReq('put',$this->_baseUrl.'channels/'.$channelid, $json, 'application/json'); + } + + /** + * subscribe from channelid + */ + public function channelSubscribe($channelid) { + return $this->httpReq('post',$this->_baseUrl.'channels/'.$channelid.'/subscribe'); + } + + /** + * unsubscribe from channelid + */ + public function channelUnsubscribe($channelid) { + return $this->httpReq('delete',$this->_baseUrl.'channels/'.$channelid.'/subscribe'); + } + + /** + * get all user objects subscribed to channelid + */ + public function getChannelSubscriptions($channelid, $params = array()) { + return $this->httpReq('get',$this->_baseUrl.'channel/'.$channelid.'/subscribers?'.$this->buildQueryString($params)); + } + + /** + * get all user IDs subscribed to channelid + */ + public function getChannelSubscriptionsById($channelid) { + return $this->httpReq('get',$this->_baseUrl.'channel/'.$channelid.'/subscribers/ids'); + } + + + /** + * get a page of messages in channelid + */ + public function getMessages($channelid, $params = array()) { + return $this->httpReq('get',$this->_baseUrl.'channels/'.$channelid.'/messages?'.$this->buildQueryString($params)); + } + + /** + * create message + * @param $channelid numeric or "pm" for auto-chanenl (type=net.app.core.pm) + * @param $data array('text'=>'YOUR_MESSAGE') If a type=net.app.core.pm, then "destinations" key can be set to address as an array of people to send this PM too + */ + public function createMessage($channelid,$data) { + $json = json_encode($data); + return $this->httpReq('post',$this->_baseUrl.'channels/'.$channelid.'/messages', $json, 'application/json'); + } + + /** + * get message + */ + public function getMessage($channelid,$messageid) { + return $this->httpReq('get',$this->_baseUrl.'channels/'.$channelid.'/messages/'.$messageid); + } + + /** + * delete messsage + */ + public function deleteMessage($channelid,$messageid) { + return $this->httpReq('delete',$this->_baseUrl.'channels/'.$channelid.'/messages/'.$messageid); + } + + + /** + * Get Application Information + */ + public function getAppTokenInfo() { + // requires appAccessToken + if (!$this->_appAccessToken) { + $this->getAppAccessToken(); + } + // ensure request is made with our appAccessToken + $params['access_token']=$this->_appAccessToken; + return $this->httpReq('get',$this->_baseUrl.'token',$params); + } + + /** + * Get User Information + */ + public function getUserTokenInfo() { + return $this->httpReq('get',$this->_baseUrl.'token'); + } + + /** + * Get Application Authorized User IDs + */ + public function getAppUserIDs() { + // requires appAccessToken + if (!$this->_appAccessToken) { + $this->getAppAccessToken(); + } + // ensure request is made with our appAccessToken + $params['access_token']=$this->_appAccessToken; + return $this->httpReq('get',$this->_baseUrl.'apps/me/tokens/user_ids',$params); + } + + /** + * Get Application Authorized User Tokens + */ + public function getAppUserTokens() { + // requires appAccessToken + if (!$this->_appAccessToken) { + $this->getAppAccessToken(); + } + // ensure request is made with our appAccessToken + $params['access_token']=$this->_appAccessToken; + return $this->httpReq('get',$this->_baseUrl.'apps/me/tokens',$params); + } + + public function getLastRequest() { + return $this->_last_request; + } + public function getLastResponse() { + return $this->_last_response; + } + + /** + * Registers your function (or an array of object and method) to be called + * whenever an event is received via an open app.net stream. Your function + * will receive a single parameter, which is the object wrapper containing + * the meta and data. + * @param mixed A PHP callback (either a string containing the function name, + * or an array where the first element is the class/object and the second + * is the method). + */ + public function registerStreamFunction($function) { + $this->_streamCallback = $function; + } + + /** + * Opens a stream that's been created for this user/app and starts sending + * events/objects to your defined callback functions. You must define at + * least one callback function before opening a stream. + * @param mixed $stream Either a stream ID or the endpoint of a stream + * you've already created. This stream must exist and must be valid for + * your current access token. If you pass a stream ID, the library will + * make an API call to get the endpoint. + * + * This function will return immediately, but your callback functions + * will continue to receive events until you call closeStream() or until + * App.net terminates the stream from their end with an error. + * + * If you're disconnected due to a network error, the library will + * automatically attempt to reconnect you to the same stream, no action + * on your part is necessary for this. However if the app.net API returns + * an error, a reconnection attempt will not be made. + * + * Note there is no closeStream, because once you open a stream you + * can't stop it (unless you exit() or die() or throw an uncaught + * exception, or something else that terminates the script). + * @return boolean True + * @see createStream() + */ + public function openStream($stream) { + // if there's already a stream running, don't allow another + if ($this->_currentStream) { + throw new AppDotNetException('There is already a stream being consumed, only one stream can be consumed per AppDotNetStream instance'); + } + // must register a callback (or the exercise is pointless) + if (!$this->_streamCallback) { + throw new AppDotNetException('You must define your callback function using registerStreamFunction() before calling openStream'); + } + // if the stream is a numeric value, get the stream info from the api + if (is_numeric($stream)) { + $stream = $this->getStream($stream); + $this->_streamUrl = $stream['endpoint']; + } + else { + $this->_streamUrl = $stream; + } + // continue doing this until we get an error back or something...? + $this->httpStream('get',$this->_streamUrl); + + return true; + } + + /** + * Close the currently open stream. + * @return true; + */ + public function closeStream() { + if (!$this->_lastStreamActivity) { + // never opened + return; + } + if (!$this->_multiStream) { + throw new AppDotNetException('You must open a stream before calling closeStream()'); + } + curl_close($this->_currentStream); + curl_multi_remove_handle($this->_multiStream,$this->_currentStream); + curl_multi_close($this->_multiStream); + $this->_currentStream = null; + $this->_multiStream = null; + } + + /** + * Retrieve all streams for the current access token. + * @return array An array of stream definitions. + */ + public function getAllStreams() { + return $this->httpReq('get',$this->_baseUrl.'streams'); + } + + /** + * Returns a single stream specified by a stream ID. The stream must have been + * created with the current access token. + * @return array A stream definition + */ + public function getStream($streamId) { + return $this->httpReq('get',$this->_baseUrl.'streams/'.urlencode($streamId)); + } + + /** + * Creates a stream for the current app access token. + * + * @param array $objectTypes The objects you want to retrieve data for from the + * stream. At time of writing these can be 'post', 'star', and/or 'user_follow'. + * If you don't specify, all events will be retrieved. + */ + public function createStream($objectTypes=null) { + // default object types to everything + if (is_null($objectTypes)) { + $objectTypes = array('post','star','user_follow'); + } + $data = array( + 'object_types'=>$objectTypes, + 'type'=>'long_poll', + ); + $data = json_encode($data); + $response = $this->httpReq('post',$this->_baseUrl.'streams',$data,'application/json'); + return $response; + } + + /** + * Update stream for the current app access token + * + * @param integer $streamId The stream ID to update. This stream must have been + * created by the current access token. + * @param array $data allows object_types, type, filter_id and key to be updated. filter_id/key can be omitted + */ + public function updateStream($streamId,$data) { + // objectTypes is likely required + if (is_null($data['object_types'])) { + $data['object_types'] = array('post','star','user_follow'); + } + // type can still only be long_poll + if (is_null($data['type'])) { + $data['type']='long_poll'; + } + $data = json_encode($data); + $response = $this->httpReq('put',$this->_baseUrl.'streams/'.urlencode($streamId),$data,'application/json'); + return $response; + } + + /** + * Deletes a stream if you no longer need it. + * + * @param integer $streamId The stream ID to delete. This stream must have been + * created by the current access token. + */ + public function deleteStream($streamId) { + return $this->httpReq('delete',$this->_baseUrl.'streams/'.urlencode($streamId)); + } + + /** + * Deletes all streams created by the current access token. + */ + public function deleteAllStreams() { + return $this->httpReq('delete',$this->_baseUrl.'streams'); + } + + /** + * Internal function used to process incoming chunks from the stream. This is only + * public because it needs to be accessed by CURL. Do not call or use this function + * in your own code. + * @ignore + */ + public function httpStreamReceive($ch,$data) { + $this->_lastStreamActivity = time(); + $this->_streamBuffer .= $data; + if (!$this->_streamHeaders) { + $pos = strpos($this->_streamBuffer,"\r\n\r\n"); + if ($pos!==false) { + $this->_streamHeaders = substr($this->_streamBuffer,0,$pos); + $this->_streamBuffer = substr($this->_streamBuffer,$pos+4); + } + } + else { + $pos = strpos($this->_streamBuffer,"\r\n"); + while ($pos!==false) { + $command = substr($this->_streamBuffer,0,$pos); + $this->_streamBuffer = substr($this->_streamBuffer,$pos+2); + $command = json_decode($command,true); + if ($command) { + call_user_func($this->_streamCallback,$command); + } + $pos = strpos($this->_streamBuffer,"\r\n"); + } + } + return strlen($data); + } + + /** + * Opens a long lived HTTP connection to the app.net servers, and sends data + * received to the httpStreamReceive function. As a general rule you should not + * directly call this method, it's used by openStream(). + */ + protected function httpStream($act, $req, $params=array(),$contentType='application/x-www-form-urlencoded') { + if ($this->_currentStream) { + throw new AppDotNetException('There is already an open stream, you must close the existing one before opening a new one'); + } + $headers = array(); + $this->_streamBuffer = ''; + if ($this->_accessToken) { + $headers[] = 'Authorization: Bearer '.$this->_accessToken; + } + $this->_currentStream = curl_init($req); + curl_setopt($this->_currentStream, CURLOPT_HTTPHEADER, $headers); + curl_setopt($this->_currentStream, CURLOPT_RETURNTRANSFER, true); + curl_setopt($this->_currentStream, CURLINFO_HEADER_OUT, true); + curl_setopt($this->_currentStream, CURLOPT_HEADER, true); + if ($this->_sslCA) { + curl_setopt($this->_currentStream, CURLOPT_CAINFO, $this->_sslCA); + } + // every time we receive a chunk of data, forward it to httpStreamReceive + curl_setopt($this->_currentStream, CURLOPT_WRITEFUNCTION, array($this, "httpStreamReceive")); + + // curl_exec($ch); + // return; + + $this->_multiStream = curl_multi_init(); + $this->_lastStreamActivity = time(); + curl_multi_add_handle($this->_multiStream,$this->_currentStream); + } + + public function reconnectStream() { + $this->closeStream(); + $this->_connectFailCounter++; + // if we've failed a few times, back off + if ($this->_connectFailCounter>1) { + $sleepTime = pow(2,$this->_connectFailCounter); + // don't sleep more than 60 seconds + if ($sleepTime>60) { + $sleepTime = 60; + } + sleep($sleepTime); + } + $this->httpStream('get',$this->_streamUrl); + } + + /** + * Process an open stream for x microseconds, then return. This is useful if you want + * to be doing other things while processing the stream. If you just want to + * consume the stream without other actions, you can call processForever() instead. + * @param float @microseconds The number of microseconds to process for before + * returning. There are 1,000,000 microseconds in a second. + * + * @return void + */ + public function processStream($microseconds=null) { + if (!$this->_multiStream) { + throw new AppDotNetException('You must open a stream before calling processStream()'); + } + $start = microtime(true); + $active = null; + $inQueue = null; + $sleepFor = 0; + do { + // if we haven't received anything within 5.5 minutes, reconnect + // keepalives are sent every 5 minutes (measured on 2013-3-12 by @ryantharp) + if (time()-$this->_lastStreamActivity>=330) { + $this->reconnectStream(); + } + curl_multi_exec($this->_multiStream, $active); + if (!$active) { + $httpCode = curl_getinfo($this->_currentStream,CURLINFO_HTTP_CODE); + // don't reconnect on 400 errors + if ($httpCode>=400 && $httpCode<=499) { + throw new AppDotNetException('Received HTTP error '.$httpCode.' check your URL and credentials before reconnecting'); + } + $this->reconnectStream(); + } + // sleep for a max of 2/10 of a second + $timeSoFar = (microtime(true)-$start)*1000000; + $sleepFor = $this->streamingSleepFor; + if ($timeSoFar+$sleepFor>$microseconds) { + $sleepFor = $microseconds - $timeSoFar; + } + + if ($sleepFor>0) { + usleep($sleepFor); + } + } while ($timeSoFar+$sleepFor<$microseconds); + } + + /** + * Process an open stream forever. This function will never return, if you + * want to perform other actions while consuming the stream, you should use + * processFor() instead. + * @return void This function will never return + * @see processFor(); + */ + public function processStreamForever() { + while (true) { + $this->processStream(600); + } + } + + + /** + * Upload a file to a user's file store + * @param string $file A string containing the path of the file to upload. + * @param array $data Additional data about the file you're uploading. At the + * moment accepted keys are: mime-type, kind, type, name, public and annotations. + * - If you don't specify mime-type, ADNPHP will attempt to guess the mime type + * based on the file, however this isn't always reliable. + * - If you don't specify kind ADNPHP will attempt to determine if the file is + * an image or not. + * - If you don't specify name, ADNPHP will use the filename of the first + * parameter. + * - If you don't specify public, your file will be uploaded as a private file. + * - Type is REQUIRED. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: include_annotations|include_file_annotations. + * @return array An associative array representing the file + */ + public function createFile($file, $data, $params=array()) { + if (!$file) { + throw new AppDotNetException('You must specify a path to a file'); + } + if (!file_exists($file)) { + throw new AppDotNetException('File path specified does not exist'); + } + if (!is_readable($file)) { + throw new AppDotNetException('File path specified is not readable'); + } + + if (!$data) { + $data = array(); + } + + if (!array_key_exists('type',$data) || !$data['type']) { + throw new AppDotNetException('Type is required when creating a file'); + } + + if (!array_key_exists('name',$data)) { + $data['name'] = basename($file); + } + + if (array_key_exists('mime-type',$data)) { + $mimeType = $data['mime-type']; + unset($data['mime-type']); + } + else { + $mimeType = null; + } + if (!array_key_exists('kind',$data)) { + $test = @getimagesize($path); + if ($test && array_key_exists('mime',$test)) { + $data['kind'] = 'image'; + if (!$mimeType) { + $mimeType = $test['mime']; + } + } + else { + $data['kind'] = 'other'; + } + } + if (!$mimeType) { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mimeType = finfo_file($finfo, $file); + finfo_close($finfo); + } + if (!$mimeType) { + throw new AppDotNetException('Unable to determine mime type of file, try specifying it explicitly'); + } + if (!array_key_exists('public',$data) || !$data['public']) { + $public = false; + } + else { + $public = true; + } + + $data['content'] = "@$file;type=$mimeType"; + return $this->httpReq('post-raw',$this->_baseUrl.'files', $data, 'multipart/form-data'); + } + + + public function createFilePlaceholder($file = null, $params=array()) { + $name = basename($file); + $data = array('annotations' => $params['annotations'], 'kind' => $params['kind'], + 'name' => $name, 'type' => $params['metadata']); + $json = json_encode($data); + return $this->httpReq('post',$this->_baseUrl.'files', $json, 'application/json'); + } + + public function updateFileContent($fileid, $file) { + + $data = file_get_contents($file); + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_file($finfo, $file); + finfo_close($finfo); + + return $this->httpReq('put',$this->_baseUrl.'files/' . $fileid + .'/content', $data, $mime); + } + + /** + * Allows for file rename and annotation changes. + * @param integer $file_id The ID of the file to update + * @param array $params An associative array of file parameters. + * @return array An associative array representing the updated file + */ + public function updateFile($file_id=null, $params=array()) { + $data = array('annotations' => $params['annotations'] , 'name' => $params['name']); + $json = json_encode($data); + return $this->httpReq('put',$this->_baseUrl.'files/'.urlencode($file_id), $json, 'application/json'); + } + + /** + * Returns a specific File. + * @param integer $file_id The ID of the file to retrieve + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: include_annotations|include_file_annotations. + * @return array An associative array representing the file + */ + public function getFile($file_id=null,$params = array()) { + return $this->httpReq('get',$this->_baseUrl.'files/'.urlencode($file_id) + .'?'.$this->buildQueryString($params)); + } + + public function getFileContent($file_id=null,$params = array()) { + return $this->httpReq('get',$this->_baseUrl.'files/'.urlencode($file_id) + .'/content?'.$this->buildQueryString($params)); + } + + /** $file_key : derived_file_key */ + public function getDerivedFileContent($file_id=null,$file_key=null,$params = array()) { + return $this->httpReq('get',$this->_baseUrl.'files/'.urlencode($file_id) + .'/content/'.urlencode($file_key) + .'?'.$this->buildQueryString($params)); + } + + /** + * Returns file objects. + * @param array $file_ids The IDs of the files to retrieve + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: include_annotations|include_file_annotations. + * @return array An associative array representing the file data. + */ + public function getFiles($file_ids=array(), $params = array()) { + $ids = ''; + foreach($file_ids as $id) { + $ids .= $id . ','; + } + $params['ids'] = substr($ids, 0, -1); + return $this->httpReq('get',$this->_baseUrl.'files' + .'?'.$this->buildQueryString($params)); + } + + /** + * Returns a user's file objects. + * @param array $params An associative array of optional general parameters. + * This will likely change as the API evolves, as of this writing allowed keys + * are: include_annotations|include_file_annotations|include_user_annotations. + * @return array An associative array representing the file data. + */ + public function getUserFiles($params = array()) { + return $this->httpReq('get',$this->_baseUrl.'users/me/files' + .'?'.$this->buildQueryString($params)); + } + + /** + * Delete a File. The current user must be the same user who created the File. + * It returns the deleted File on success. + * @param integer $file_id The ID of the file to delete + * @return array An associative array representing the file that was deleted + */ + public function deleteFile($file_id=null) { + return $this->httpReq('delete',$this->_baseUrl.'files/'.urlencode($file_id)); + } + +} + +class AppDotNetException extends Exception {} diff --git a/appnet/DigiCertHighAssuranceEVRootCA.pem b/appnet/DigiCertHighAssuranceEVRootCA.pem new file mode 100644 index 000000000..9e6810ab7 --- /dev/null +++ b/appnet/DigiCertHighAssuranceEVRootCA.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- diff --git a/appnet/appnet.css b/appnet/appnet.css new file mode 100755 index 000000000..231f4306a --- /dev/null +++ b/appnet/appnet.css @@ -0,0 +1,29 @@ +#appnet-disconnect-label, #appnet-token-label, +#appnet-enable-label, #appnet-bydefault-label, +#appnet-clientid-label, #appnet-clientsecret-label { + float: left; + width: 200px; + margin-top: 10px; +} + +#appnet-disconnect, #appnet-token, +#appnet-checkbox, #appnet-bydefault, +#appnet-clientid, #appnet-clientsecret { + float: left; + margin-top: 10px; +} + +#appnet-submit { + margin-top: 15px; +} + +#appnet-avatar { + float: left; + width: 48px; + height: 48px; + padding: 2px; +} +#appnet-info-block { + height: 52px; + vertical-align: middle; +} diff --git a/appnet/appnet.php b/appnet/appnet.php new file mode 100644 index 000000000..e30ecdca4 --- /dev/null +++ b/appnet/appnet.php @@ -0,0 +1,331 @@ + + */ + +function appnet_install() { + register_hook('post_local', 'addon/appnet/appnet.php', 'appnet_post_local'); + register_hook('notifier_normal', 'addon/appnet/appnet.php', 'appnet_send'); + register_hook('jot_networks', 'addon/appnet/appnet.php', 'appnet_jot_nets'); + register_hook('connector_settings', 'addon/appnet/appnet.php', 'appnet_settings'); + register_hook('connector_settings_post', 'addon/appnet/appnet.php', 'appnet_settings_post'); +} + + +function appnet_uninstall() { + unregister_hook('post_local', 'addon/appnet/appnet.php', 'appnet_post_local'); + unregister_hook('notifier_normal', 'addon/appnet/appnet.php', 'appnet_send'); + unregister_hook('jot_networks', 'addon/appnet/appnet.php', 'appnet_jot_nets'); + unregister_hook('connector_settings', 'addon/appnet/appnet.php', 'appnet_settings'); + unregister_hook('connector_settings_post', 'addon/appnet/appnet.php', 'appnet_settings_post'); +} + +function appnet_module() {} + +function appnet_content(&$a) { + if(! local_user()) { + notice( t('Permission denied.') . EOL); + return ''; + } + + require_once("mod/settings.php"); + settings_init($a); + + if (isset($a->argv[1])) + switch ($a->argv[1]) { + case "connect": + $o = appnet_connect($a); + break; + default: + $o = print_r($a->argv, true); + break; + } + else + $o = appnet_connect($a); + + return $o; +} + +function appnet_connect(&$a) { + require_once 'addon/appnet/AppDotNet.php'; + + $clientId = get_pconfig(local_user(),'appnet','clientid'); + $clientSecret = get_pconfig(local_user(),'appnet','clientsecret'); + + $app = new AppDotNet($clientId, $clientSecret); + + try { + $token = $app->getAccessToken($a->get_baseurl().'/appnet/connect'); + + logger("appnet_connect: authenticated"); + $o .= t("You are now authenticated to app.net. "); + set_pconfig(local_user(),'appnet','token', $token); + } + catch (AppDotNetException $e) { + $o .= t("

Error fetching token. Please try again.

"); + } + + $o .= '
'.t("return to the connector page").''; + + return($o); +} + +function appnet_jot_nets(&$a,&$b) { + if(! local_user()) + return; + + $post = get_pconfig(local_user(),'appnet','post'); + if(intval($post) == 1) { + $defpost = get_pconfig(local_user(),'appnet','post_by_default'); + $selected = ((intval($defpost) == 1) ? ' checked="checked" ' : ''); + $b .= '
' + . t('Post to app.net') . '
'; + } +} + +function appnet_settings(&$a,&$s) { + require_once 'addon/appnet/AppDotNet.php'; + + if(! local_user()) + return; + + $token = get_pconfig(local_user(),'appnet','token'); + $app_clientId = get_pconfig(local_user(),'appnet','clientid'); + $app_clientSecret = get_pconfig(local_user(),'appnet','clientsecret'); + + /* Add our stylesheet to the page so we can make our settings look nice */ + $a->page['htmlhead'] .= '' . "\r\n"; + + $enabled = get_pconfig(local_user(),'appnet','post'); + $checked = (($enabled) ? ' checked="checked" ' : ''); + + $css = (($enabled) ? '' : '-disabled'); + + $def_enabled = get_pconfig(local_user(),'appnet','post_by_default'); + $def_checked = (($def_enabled) ? ' checked="checked" ' : ''); + + $s .= ''; + $s .= '

'. t('App.net Export').'

'; + $s .= '
'; + $s .= ''; +} + +function appnet_settings_post(&$a,&$b) { + + if(x($_POST,'appnet-submit')) { + + if (isset($_POST['appnet-disconnect'])) { + del_pconfig(local_user(), 'appnet', 'clientsecret'); + del_pconfig(local_user(), 'appnet', 'clientid'); + del_pconfig(local_user(), 'appnet', 'token'); + } + + if (isset($_POST["clientsecret"])) + set_pconfig(local_user(),'appnet','clientsecret', $_POST['clientsecret']); + + if (isset($_POST["clientid"])) + set_pconfig(local_user(),'appnet','clientid', $_POST['clientid']); + + if (isset($_POST["token"]) AND ($_POST["token"] != "")) + set_pconfig(local_user(),'appnet','token', $_POST['token']); + + set_pconfig(local_user(),'appnet','post',intval($_POST['appnet'])); + set_pconfig(local_user(),'appnet','post_by_default',intval($_POST['appnet_bydefault'])); + } +} + +function appnet_post_local(&$a,&$b) { + + if($b['edit']) + return; + + if((! local_user()) || (local_user() != $b['uid'])) + return; + + if($b['private'] || $b['parent']) + return; + + $post = intval(get_pconfig(local_user(),'appnet','post')); + + $enable = (($post && x($_REQUEST,'appnet_enable')) ? intval($_REQUEST['appnet_enable']) : 0); + + if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'appnet','post_by_default'))) + $enable = 1; + + if(!$enable) + return; + + if(strlen($b['postopts'])) + $b['postopts'] .= ','; + + $b['postopts'] .= 'appnet'; +} + +function appnet_send(&$a,&$b) { + + logger('appnet_send: invoked for post '.$b['id']." ".$b['app']); + + if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited'])) + return; + + if(! strstr($b['postopts'],'appnet')) + return; + + if($b['parent'] != $b['id']) + return; + + $token = get_pconfig($b['uid'],'appnet','token'); + + if($token) { + require_once 'addon/appnet/AppDotNet.php'; + + $clientId = get_pconfig(local_user(),'appnet','clientid'); + $clientSecret = get_pconfig(local_user(),'appnet','clientsecret'); + + $app = new AppDotNet($clientId, $clientSecret); + $app->setAccessToken($token); + + $data = array(); + + require_once("include/plaintext.php"); + require_once("include/network.php"); + + $post = plaintext($a, $b, 256, false); + logger("appnet_send: converted message ".$b["id"]." result: ".print_r($post, true), LOGGER_DEBUG); + + if (isset($post["image"])) { + $img_str = fetch_url($post['image'],true, $redirects, 10); + $tempfile = tempnam(get_config("system","temppath"), "cache"); + file_put_contents($tempfile, $img_str); + + try { + $photoFile = $app->createFile($tempfile, array(type => "com.github.jdolitsky.appdotnetphp.photo")); + + $data["annotations"][] = array( + "type" => "net.app.core.oembed", + "value" => array( + "+net.app.core.file" => array( + "file_id" => $photoFile["id"], + "file_token" => $photoFile["file_token"], + "format" => "oembed") + ) + ); + } + catch (AppDotNetException $e) { + logger("appnet_send: Error creating file"); + } + + unlink($tempfile); + } + + // To-Do + // Alle Links verkürzen + + if (isset($post["url"]) AND !isset($post["title"])) { + $display_url = str_replace(array("http://www.", "https://www."), array("", ""), $post["url"]); + $display_url = str_replace(array("http://", "https://"), array("", ""), $display_url); + + if (strlen($display_url) > 26) + $display_url = substr($display_url, 0, 25)."…"; + + $post["title"] = $display_url; + } + + if (isset($post["url"]) AND isset($post["title"])) { + $post["title"] = shortenmsg($post["title"], 90); + $post["text"] = shortenmsg($post["text"], 256 - strlen($post["title"])); + $post["text"] .= "\n[".$post["title"]."](".$post["url"].")"; + } elseif (isset($post["url"])) { + $post["url"] = short_link($post["url"]); + $post["text"] = shortenmsg($post["text"], 240); + $post["text"] .= " ".$post["url"]; + } + + //print_r($post); + $data["entities"]["parse_links"] = true; + $data["entities"]["parse_markdown_links"] = true; + + try { + $ret = $app->createPost($post["text"], $data); + logger("appnet_send: send message ".$b["id"]." result: ".print_r($ret, true), LOGGER_DEBUG); + } + catch (AppDotNetException $e) { + logger("appnet_send: Error sending message ".$b["id"]); + } + } +} diff --git a/appnetpost/appnetpost.css b/appnetpost/appnetpost.css old mode 100755 new mode 100644 From ca9bd8b877d8ed2306bef76c93bcc6eb77ed1fd1 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Tue, 10 Jun 2014 20:13:11 +0200 Subject: [PATCH 3/4] Removing blanks with tabs --- fbpost/fbpost.php | 8 +- pumpio/pumpio.php | 355 ++++++++++++++++++++-------------------- statusnet/statusnet.php | 80 ++++----- tumblr/tumblr.php | 200 +++++++++++----------- twitter/twitter.php | 247 ++++++++++++++-------------- 5 files changed, 445 insertions(+), 445 deletions(-) mode change 100755 => 100644 pumpio/pumpio.php mode change 100755 => 100644 tumblr/tumblr.php mode change 100755 => 100644 twitter/twitter.php diff --git a/fbpost/fbpost.php b/fbpost/fbpost.php index 60c8e9392..b554453fa 100644 --- a/fbpost/fbpost.php +++ b/fbpost/fbpost.php @@ -69,7 +69,7 @@ function fbpost_init(&$a) { if(strlen($nick)) $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1", - dbesc($nick) + dbesc($nick) ); if(!(isset($r) && count($r))) return; @@ -154,7 +154,7 @@ function fbpost_post(&$a) { $result = q("SELECT `installed` FROM `addon` WHERE `name` = 'fbsync' AND `installed`"); if (count($result) > 0) { set_pconfig(local_user(),'fbsync','sync',intval($_POST['fbsync'])); - set_pconfig(local_user(),'fbsync','create_user',intval($_POST['create_user'])); + set_pconfig(local_user(),'fbsync','create_user',intval($_POST['create_user'])); } info( t('Settings updated.') . EOL); @@ -316,11 +316,11 @@ function fbpost_content(&$a) { $o .= '
'; - $sync_enabled = get_pconfig(local_user(),'fbsync','sync'); + $sync_enabled = get_pconfig(local_user(),'fbsync','sync'); $checked = (($sync_enabled) ? ' checked="checked" ' : ''); $o .= '' . ' ' . t('Import Facebook newsfeed.') . EOL; - $create_user = get_pconfig(local_user(),'fbsync','create_user'); + $create_user = get_pconfig(local_user(),'fbsync','create_user'); $checked = (($create_user) ? ' checked="checked" ' : ''); $o .= '' . ' ' . t('Automatically create contacts.') . EOL; diff --git a/pumpio/pumpio.php b/pumpio/pumpio.php old mode 100755 new mode 100644 index 78b738fab..624674144 --- a/pumpio/pumpio.php +++ b/pumpio/pumpio.php @@ -11,23 +11,23 @@ require('addon/pumpio/oauth/oauth_client.php'); define('PUMPIO_DEFAULT_POLL_INTERVAL', 5); // given in minutes function pumpio_install() { - register_hook('post_local', 'addon/pumpio/pumpio.php', 'pumpio_post_local'); - register_hook('notifier_normal', 'addon/pumpio/pumpio.php', 'pumpio_send'); - register_hook('jot_networks', 'addon/pumpio/pumpio.php', 'pumpio_jot_nets'); - register_hook('connector_settings', 'addon/pumpio/pumpio.php', 'pumpio_settings'); - register_hook('connector_settings_post', 'addon/pumpio/pumpio.php', 'pumpio_settings_post'); - register_hook('cron', 'addon/pumpio/pumpio.php', 'pumpio_cron'); - register_hook('queue_predeliver', 'addon/pumpio/pumpio.php', 'pumpio_queue_hook'); + register_hook('post_local', 'addon/pumpio/pumpio.php', 'pumpio_post_local'); + register_hook('notifier_normal', 'addon/pumpio/pumpio.php', 'pumpio_send'); + register_hook('jot_networks', 'addon/pumpio/pumpio.php', 'pumpio_jot_nets'); + register_hook('connector_settings', 'addon/pumpio/pumpio.php', 'pumpio_settings'); + register_hook('connector_settings_post', 'addon/pumpio/pumpio.php', 'pumpio_settings_post'); + register_hook('cron', 'addon/pumpio/pumpio.php', 'pumpio_cron'); + register_hook('queue_predeliver', 'addon/pumpio/pumpio.php', 'pumpio_queue_hook'); } function pumpio_uninstall() { - unregister_hook('post_local', 'addon/pumpio/pumpio.php', 'pumpio_post_local'); - unregister_hook('notifier_normal', 'addon/pumpio/pumpio.php', 'pumpio_send'); - unregister_hook('jot_networks', 'addon/pumpio/pumpio.php', 'pumpio_jot_nets'); - unregister_hook('connector_settings', 'addon/pumpio/pumpio.php', 'pumpio_settings'); - unregister_hook('connector_settings_post', 'addon/pumpio/pumpio.php', 'pumpio_settings_post'); - unregister_hook('cron', 'addon/pumpio/pumpio.php', 'pumpio_cron'); - unregister_hook('queue_predeliver', 'addon/pumpio/pumpio.php', 'pumpio_queue_hook'); + unregister_hook('post_local', 'addon/pumpio/pumpio.php', 'pumpio_post_local'); + unregister_hook('notifier_normal', 'addon/pumpio/pumpio.php', 'pumpio_send'); + unregister_hook('jot_networks', 'addon/pumpio/pumpio.php', 'pumpio_jot_nets'); + unregister_hook('connector_settings', 'addon/pumpio/pumpio.php', 'pumpio_settings'); + unregister_hook('connector_settings_post', 'addon/pumpio/pumpio.php', 'pumpio_settings_post'); + unregister_hook('cron', 'addon/pumpio/pumpio.php', 'pumpio_cron'); + unregister_hook('queue_predeliver', 'addon/pumpio/pumpio.php', 'pumpio_queue_hook'); } function pumpio_module() {} @@ -61,37 +61,37 @@ function pumpio_registerclient(&$a, $host) { $url = "https://".$host."/api/client/register"; - $params = array(); + $params = array(); $application_name = get_config('pumpio', 'application_name'); if ($application_name == "") $application_name = $a->get_hostname(); - $params["type"] = "client_associate"; - $params["contacts"] = $a->config['admin_email']; - $params["application_type"] = "native"; - $params["application_name"] = $application_name; - $params["logo_url"] = $a->get_baseurl()."/images/friendica-256.png"; - $params["redirect_uris"] = $a->get_baseurl()."/pumpio/connect"; + $params["type"] = "client_associate"; + $params["contacts"] = $a->config['admin_email']; + $params["application_type"] = "native"; + $params["application_name"] = $application_name; + $params["logo_url"] = $a->get_baseurl()."/images/friendica-256.png"; + $params["redirect_uris"] = $a->get_baseurl()."/pumpio/connect"; logger("pumpio_registerclient: ".$url." parameters ".print_r($params, true), LOGGER_DEBUG); - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - curl_setopt($ch, CURLOPT_POST,1); - curl_setopt($ch, CURLOPT_POSTFIELDS,$params); - curl_setopt($ch, CURLOPT_USERAGENT, "Friendica"); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); + curl_setopt($ch, CURLOPT_POST,1); + curl_setopt($ch, CURLOPT_POSTFIELDS,$params); + curl_setopt($ch, CURLOPT_USERAGENT, "Friendica"); - $s = curl_exec($ch); - $curl_info = curl_getinfo($ch); + $s = curl_exec($ch); + $curl_info = curl_getinfo($ch); - if ($curl_info["http_code"] == "200") { - $values = json_decode($s); + if ($curl_info["http_code"] == "200") { + $values = json_decode($s); logger("pumpio_registerclient: success ".print_r($values, true), LOGGER_DEBUG); return($values); - } + } logger("pumpio_registerclient: failed: ".print_r($curl_info, true), LOGGER_DEBUG); return(false); @@ -156,10 +156,10 @@ function pumpio_connect(&$a) { } $success = $client->Finalize($success); } - if($client->exit) - $o = 'Could not connect to pumpio. Refresh the page or try again later.'; + if($client->exit) + $o = 'Could not connect to pumpio. Refresh the page or try again later.'; - if($success) { + if($success) { logger("pumpio_connect: authenticated"); $o .= t("You are now authenticated to pumpio."); $o .= '
'.t("return to the connector page").''; @@ -172,118 +172,117 @@ function pumpio_connect(&$a) { } function pumpio_jot_nets(&$a,&$b) { - if(! local_user()) - return; + if(! local_user()) + return; - $pumpio_post = get_pconfig(local_user(),'pumpio','post'); - if(intval($pumpio_post) == 1) { - $pumpio_defpost = get_pconfig(local_user(),'pumpio','post_by_default'); - $selected = ((intval($pumpio_defpost) == 1) ? ' checked="checked" ' : ''); - $b .= '
' - . t('Post to pumpio') . '
'; - } + $pumpio_post = get_pconfig(local_user(),'pumpio','post'); + if(intval($pumpio_post) == 1) { + $pumpio_defpost = get_pconfig(local_user(),'pumpio','post_by_default'); + $selected = ((intval($pumpio_defpost) == 1) ? ' checked="checked" ' : ''); + $b .= '
' + . t('Post to pumpio') . '
'; + } } function pumpio_settings(&$a,&$s) { - if(! local_user()) - return; + if(! local_user()) + return; - /* Add our stylesheet to the page so we can make our settings look nice */ + /* Add our stylesheet to the page so we can make our settings look nice */ - $a->page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; - /* Get the current state of our config variables */ + /* Get the current state of our config variables */ - $import_enabled = get_pconfig(local_user(),'pumpio','import'); - $import_checked = (($import_enabled) ? ' checked="checked" ' : ''); + $import_enabled = get_pconfig(local_user(),'pumpio','import'); + $import_checked = (($import_enabled) ? ' checked="checked" ' : ''); - $enabled = get_pconfig(local_user(),'pumpio','post'); - $checked = (($enabled) ? ' checked="checked" ' : ''); - $css = (($enabled) ? '' : '-disabled'); + $enabled = get_pconfig(local_user(),'pumpio','post'); + $checked = (($enabled) ? ' checked="checked" ' : ''); + $css = (($enabled) ? '' : '-disabled'); - $def_enabled = get_pconfig(local_user(),'pumpio','post_by_default'); - $def_checked = (($def_enabled) ? ' checked="checked" ' : ''); + $def_enabled = get_pconfig(local_user(),'pumpio','post_by_default'); + $def_checked = (($def_enabled) ? ' checked="checked" ' : ''); - $public_enabled = get_pconfig(local_user(),'pumpio','public'); - $public_checked = (($public_enabled) ? ' checked="checked" ' : ''); + $public_enabled = get_pconfig(local_user(),'pumpio','public'); + $public_checked = (($public_enabled) ? ' checked="checked" ' : ''); - $mirror_enabled = get_pconfig(local_user(),'pumpio','mirror'); - $mirror_checked = (($mirror_enabled) ? ' checked="checked" ' : ''); + $mirror_enabled = get_pconfig(local_user(),'pumpio','mirror'); + $mirror_checked = (($mirror_enabled) ? ' checked="checked" ' : ''); - $servername = get_pconfig(local_user(), "pumpio", "host"); - $username = get_pconfig(local_user(), "pumpio", "user"); + $servername = get_pconfig(local_user(), "pumpio", "host"); + $username = get_pconfig(local_user(), "pumpio", "user"); - /* Add some HTML to the existing form */ + /* Add some HTML to the existing form */ - $s .= ''; - $s .= '

'. t('Pump.io Import/Export/Mirror').'

'; - $s .= '
'; - $s .= ''; $s .= ''; - $s .= ''; - $s .= '
'; + $s .= ''; + $s .= '
'; /* - $s .= ''; - $s .= ''; - $s .= '
'; + $s .= ''; + $s .= ''; + $s .= '
'; */ $s .= '
'; $s .= ''; @@ -439,20 +439,20 @@ function statusnet_action($a, $uid, $pid, $action) { $connection = new StatusNetOAuth($api,$ckey,$csecret,$otoken,$osecret); - logger("statusnet_action '".$action."' ID: ".$pid, LOGGER_DATA); + logger("statusnet_action '".$action."' ID: ".$pid, LOGGER_DATA); - switch ($action) { - case "delete": - $result = $connection->post("statuses/destroy/".$pid); - break; - case "like": - $result = $connection->post("favorites/create/".$pid); - break; - case "unlike": - $result = $connection->post("favorites/destroy/".$pid); - break; - } - logger("statusnet_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG); + switch ($action) { + case "delete": + $result = $connection->post("statuses/destroy/".$pid); + break; + case "like": + $result = $connection->post("favorites/create/".$pid); + break; + case "unlike": + $result = $connection->post("favorites/destroy/".$pid); + break; + } + logger("statusnet_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG); } function statusnet_post_hook(&$a,&$b) { @@ -510,19 +510,19 @@ function statusnet_post_hook(&$a,&$b) { } if (($b['verb'] == ACTIVITY_POST) AND $b['deleted']) - statusnet_action($a, $b["uid"], substr($orig_post["uri"], $hostlength), "delete"); + statusnet_action($a, $b["uid"], substr($orig_post["uri"], $hostlength), "delete"); - if($b['verb'] == ACTIVITY_LIKE) { - logger("statusnet_post_hook: parameter 2 ".substr($b["thr-parent"], $hostlength), LOGGER_DEBUG); - if ($b['deleted']) - statusnet_action($a, $b["uid"], substr($b["thr-parent"], $hostlength), "unlike"); - else - statusnet_action($a, $b["uid"], substr($b["thr-parent"], $hostlength), "like"); - return; + if($b['verb'] == ACTIVITY_LIKE) { + logger("statusnet_post_hook: parameter 2 ".substr($b["thr-parent"], $hostlength), LOGGER_DEBUG); + if ($b['deleted']) + statusnet_action($a, $b["uid"], substr($b["thr-parent"], $hostlength), "unlike"); + else + statusnet_action($a, $b["uid"], substr($b["thr-parent"], $hostlength), "like"); + return; } - if($b['deleted'] || ($b['created'] !== $b['edited'])) - return; + if($b['deleted'] || ($b['created'] !== $b['edited'])) + return; // if posts comes from statusnet don't send it back if($b['app'] == "StatusNet") @@ -700,12 +700,12 @@ function statusnet_cron($a,$b) { } $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'statusnet' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()"); - if(count($r)) { - foreach($r as $rr) { - logger('statusnet: importing timeline from user '.$rr['uid']); - statusnet_fetchhometimeline($a, $rr["uid"]); - } - } + if(count($r)) { + foreach($r as $rr) { + logger('statusnet: importing timeline from user '.$rr['uid']); + statusnet_fetchhometimeline($a, $rr["uid"]); + } + } logger('statusnet: cron_end'); @@ -1429,7 +1429,7 @@ function statusnet_convertmsg($a, $body, $no_tags = false) { $expanded_url = original_url($match[1]); $oembed_data = oembed_fetch_url($expanded_url, true); -print_r($oembed_data); + if ($type == "") $type = $oembed_data->type; if ($oembed_data->type == "video") { diff --git a/tumblr/tumblr.php b/tumblr/tumblr.php old mode 100755 new mode 100644 index 054b53f86..cd9713cf8 --- a/tumblr/tumblr.php +++ b/tumblr/tumblr.php @@ -12,19 +12,19 @@ require_once('library/OAuth1.php'); require_once('addon/tumblr/tumblroauth/tumblroauth.php'); function tumblr_install() { - register_hook('post_local', 'addon/tumblr/tumblr.php', 'tumblr_post_local'); - register_hook('notifier_normal', 'addon/tumblr/tumblr.php', 'tumblr_send'); - register_hook('jot_networks', 'addon/tumblr/tumblr.php', 'tumblr_jot_nets'); - register_hook('connector_settings', 'addon/tumblr/tumblr.php', 'tumblr_settings'); - register_hook('connector_settings_post', 'addon/tumblr/tumblr.php', 'tumblr_settings_post'); + register_hook('post_local', 'addon/tumblr/tumblr.php', 'tumblr_post_local'); + register_hook('notifier_normal', 'addon/tumblr/tumblr.php', 'tumblr_send'); + register_hook('jot_networks', 'addon/tumblr/tumblr.php', 'tumblr_jot_nets'); + register_hook('connector_settings', 'addon/tumblr/tumblr.php', 'tumblr_settings'); + register_hook('connector_settings_post', 'addon/tumblr/tumblr.php', 'tumblr_settings_post'); } function tumblr_uninstall() { - unregister_hook('post_local', 'addon/tumblr/tumblr.php', 'tumblr_post_local'); - unregister_hook('notifier_normal', 'addon/tumblr/tumblr.php', 'tumblr_send'); - unregister_hook('jot_networks', 'addon/tumblr/tumblr.php', 'tumblr_jot_nets'); - unregister_hook('connector_settings', 'addon/tumblr/tumblr.php', 'tumblr_settings'); - unregister_hook('connector_settings_post', 'addon/tumblr/tumblr.php', 'tumblr_settings_post'); + unregister_hook('post_local', 'addon/tumblr/tumblr.php', 'tumblr_post_local'); + unregister_hook('notifier_normal', 'addon/tumblr/tumblr.php', 'tumblr_send'); + unregister_hook('jot_networks', 'addon/tumblr/tumblr.php', 'tumblr_jot_nets'); + unregister_hook('connector_settings', 'addon/tumblr/tumblr.php', 'tumblr_settings'); + unregister_hook('connector_settings_post', 'addon/tumblr/tumblr.php', 'tumblr_settings_post'); } function tumblr_module() {} @@ -85,22 +85,22 @@ function tumblr_connect($a) { // Check the HTTP Code. It should be a 200 (OK), if it's anything else then something didn't work. switch ($tum_oauth->http_code) { - case 200: - // Ask Tumblr to give us a special address to their login page - $url = $tum_oauth->getAuthorizeURL($token); + case 200: + // Ask Tumblr to give us a special address to their login page + $url = $tum_oauth->getAuthorizeURL($token); - // Redirect the user to the login URL given to us by Tumblr - header('Location: ' . $url); + // Redirect the user to the login URL given to us by Tumblr + header('Location: ' . $url); - // That's it for our side. The user is sent to a Tumblr Login page and - // asked to authroize our app. After that, Tumblr sends the user back to - // our Callback URL (callback.php) along with some information we need to get - // an access token. + // That's it for our side. The user is sent to a Tumblr Login page and + // asked to authroize our app. After that, Tumblr sends the user back to + // our Callback URL (callback.php) along with some information we need to get + // an access token. - break; - default: - // Give an error message - $o = 'Could not connect to Tumblr. Refresh the page or try again later.'; + break; + default: + // Give an error message + $o = 'Could not connect to Tumblr. Refresh the page or try again later.'; } return($o); } @@ -133,9 +133,9 @@ function tumblr_callback($a) { // Make sure nothing went wrong. if (200 == $tum_oauth->http_code) { - // good to go + // good to go } else { - return('Unable to authenticate'); + return('Unable to authenticate'); } // What's next? Now that we have an Access Token and Secret, we can make an API call. @@ -148,96 +148,96 @@ function tumblr_callback($a) { } function tumblr_jot_nets(&$a,&$b) { - if(! local_user()) - return; + if(! local_user()) + return; - $tmbl_post = get_pconfig(local_user(),'tumblr','post'); - if(intval($tmbl_post) == 1) { - $tmbl_defpost = get_pconfig(local_user(),'tumblr','post_by_default'); - $selected = ((intval($tmbl_defpost) == 1) ? ' checked="checked" ' : ''); - $b .= '
' - . t('Post to Tumblr') . '
'; - } + $tmbl_post = get_pconfig(local_user(),'tumblr','post'); + if(intval($tmbl_post) == 1) { + $tmbl_defpost = get_pconfig(local_user(),'tumblr','post_by_default'); + $selected = ((intval($tmbl_defpost) == 1) ? ' checked="checked" ' : ''); + $b .= '
' + . t('Post to Tumblr') . '
'; + } } function tumblr_settings(&$a,&$s) { - if(! local_user()) - return; + if(! local_user()) + return; - /* Add our stylesheet to the page so we can make our settings look nice */ + /* Add our stylesheet to the page so we can make our settings look nice */ - $a->page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; - /* Get the current state of our config variables */ + /* Get the current state of our config variables */ - $enabled = get_pconfig(local_user(),'tumblr','post'); - $checked = (($enabled) ? ' checked="checked" ' : ''); - $css = (($enabled) ? '' : '-disabled'); + $enabled = get_pconfig(local_user(),'tumblr','post'); + $checked = (($enabled) ? ' checked="checked" ' : ''); + $css = (($enabled) ? '' : '-disabled'); - $def_enabled = get_pconfig(local_user(),'tumblr','post_by_default'); + $def_enabled = get_pconfig(local_user(),'tumblr','post_by_default'); - $def_checked = (($def_enabled) ? ' checked="checked" ' : ''); + $def_checked = (($def_enabled) ? ' checked="checked" ' : ''); - /* Add some HTML to the existing form */ + /* Add some HTML to the existing form */ - $s .= ''; - $s .= '

'. t('Tumblr Export').'

'; - $s .= '
'; - $s .= ''; } @@ -267,19 +267,19 @@ function tumblr_post_local(&$a,&$b) { if($b['private'] || $b['parent']) return; - $tmbl_post = intval(get_pconfig(local_user(),'tumblr','post')); + $tmbl_post = intval(get_pconfig(local_user(),'tumblr','post')); $tmbl_enable = (($tmbl_post && x($_REQUEST,'tumblr_enable')) ? intval($_REQUEST['tumblr_enable']) : 0); if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'tumblr','post_by_default'))) $tmbl_enable = 1; - if(! $tmbl_enable) - return; + if(! $tmbl_enable) + return; - if(strlen($b['postopts'])) - $b['postopts'] .= ','; - $b['postopts'] .= 'tumblr'; + if(strlen($b['postopts'])) + $b['postopts'] .= ','; + $b['postopts'] .= 'tumblr'; } @@ -287,14 +287,14 @@ function tumblr_post_local(&$a,&$b) { function tumblr_send(&$a,&$b) { - if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited'])) - return; + if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited'])) + return; - if(! strstr($b['postopts'],'tumblr')) - return; + if(! strstr($b['postopts'],'tumblr')) + return; - if($b['parent'] != $b['id']) - return; + if($b['parent'] != $b['id']) + return; $oauth_token = get_pconfig($b['uid'], "tumblr", "oauth_token"); $oauth_token_secret = get_pconfig($b['uid'], "tumblr", "oauth_token_secret"); diff --git a/twitter/twitter.php b/twitter/twitter.php old mode 100755 new mode 100644 index 16debcb7c..7c1674590 --- a/twitter/twitter.php +++ b/twitter/twitter.php @@ -161,10 +161,10 @@ function twitter_settings_post ($a,$post) { */ del_pconfig(local_user(), 'twitter', 'consumerkey'); del_pconfig(local_user(), 'twitter', 'consumersecret'); - del_pconfig(local_user(), 'twitter', 'oauthtoken'); - del_pconfig(local_user(), 'twitter', 'oauthsecret'); - del_pconfig(local_user(), 'twitter', 'post'); - del_pconfig(local_user(), 'twitter', 'post_by_default'); + del_pconfig(local_user(), 'twitter', 'oauthtoken'); + del_pconfig(local_user(), 'twitter', 'oauthsecret'); + del_pconfig(local_user(), 'twitter', 'post'); + del_pconfig(local_user(), 'twitter', 'post_by_default'); del_pconfig(local_user(), 'twitter', 'lastid'); del_pconfig(local_user(), 'twitter', 'mirror_posts'); del_pconfig(local_user(), 'twitter', 'import'); @@ -183,30 +183,30 @@ function twitter_settings_post ($a,$post) { $connection = new TwitterOAuth($ckey, $csecret, $_POST['twitter-token'], $_POST['twitter-token2']); $token = $connection->getAccessToken( $_POST['twitter-pin'] ); // ok, now that we have the Access Token, save them in the user config - set_pconfig(local_user(),'twitter', 'oauthtoken', $token['oauth_token']); + set_pconfig(local_user(),'twitter', 'oauthtoken', $token['oauth_token']); set_pconfig(local_user(),'twitter', 'oauthsecret', $token['oauth_token_secret']); - set_pconfig(local_user(),'twitter', 'post', 1); - // reload the Addon Settings page, if we don't do it see Bug #42 - goaway($a->get_baseurl().'/settings/connectors'); + set_pconfig(local_user(),'twitter', 'post', 1); + // reload the Addon Settings page, if we don't do it see Bug #42 + goaway($a->get_baseurl().'/settings/connectors'); } 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(),'twitter','post',intval($_POST['twitter-enable'])); - set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default'])); + set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default'])); set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror'])); set_pconfig(local_user(), 'twitter', 'import', intval($_POST['twitter-import'])); set_pconfig(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user'])); - if (!intval($_POST['twitter-mirror'])) - del_pconfig(local_user(),'twitter','lastid'); + if (!intval($_POST['twitter-mirror'])) + del_pconfig(local_user(),'twitter','lastid'); - info(t('Twitter settings updated.') . EOL); + info(t('Twitter settings updated.') . EOL); }} } function twitter_settings(&$a,&$s) { - if(! local_user()) - return; - $a->page['htmlhead'] .= '' . "\r\n"; + if(! local_user()) + return; + $a->page['htmlhead'] .= '' . "\r\n"; /*** * 1) Check that we have global consumer key & secret * 2) If no OAuthtoken & stuff is present, generate button to get some @@ -216,16 +216,16 @@ function twitter_settings(&$a,&$s) { $csecret = get_config('twitter', 'consumersecret' ); $otoken = get_pconfig(local_user(), 'twitter', 'oauthtoken' ); $osecret = get_pconfig(local_user(), 'twitter', 'oauthsecret' ); - $enabled = get_pconfig(local_user(), 'twitter', 'post'); + $enabled = get_pconfig(local_user(), 'twitter', 'post'); $checked = (($enabled) ? ' checked="checked" ' : ''); - $defenabled = get_pconfig(local_user(),'twitter','post_by_default'); + $defenabled = get_pconfig(local_user(),'twitter','post_by_default'); $defchecked = (($defenabled) ? ' checked="checked" ' : ''); - $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts'); - $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : ''); - $importenabled = get_pconfig(local_user(),'twitter','import'); - $importchecked = (($importenabled) ? ' checked="checked" ' : ''); - $create_userenabled = get_pconfig(local_user(),'twitter','create_user'); - $create_userchecked = (($create_userenabled) ? ' checked="checked" ' : ''); + $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts'); + $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : ''); + $importenabled = get_pconfig(local_user(),'twitter','import'); + $importchecked = (($importenabled) ? ' checked="checked" ' : ''); + $create_userenabled = get_pconfig(local_user(),'twitter','create_user'); + $create_userchecked = (($create_userenabled) ? ' checked="checked" ' : ''); $css = (($enabled) ? '' : '-disabled'); @@ -254,7 +254,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']; @@ -268,50 +268,50 @@ function twitter_settings(&$a,&$s) { $s .= ''; $s .= ''; $s .= ''; - $s .= '
'; - $s .= '
'; + $s .= '
'; + $s .= '
'; } else { /*** * we have an OAuth key / secret pair for the user * so let's give a chance to disable the postings to Twitter */ - require_once('library/twitteroauth.php'); + require_once('library/twitteroauth.php'); $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret); $details = $connection->get('account/verify_credentials'); $s .= '

'. t('Currently connected to: ') .''.$details->screen_name.'
'.$details->description.'

'; $s .= '

'. t('If enabled all your public postings can be posted to the associated Twitter account. You can choose to do so by default (here) or for every posting separately in the posting options when writing the entry.') .'

'; - if ($a->user['hidewall']) { - $s .= '

'. t('Note: Due your privacy settings (Hide your profile details from unknown viewers?) the link potentially included in public postings relayed to Twitter will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted.') .'

'; - } + if ($a->user['hidewall']) { + $s .= '

'. t('Note: Due your privacy settings (Hide your profile details from unknown viewers?) the link potentially included in public postings relayed to Twitter will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted.') .'

'; + } $s .= '
'; $s .= ''; $s .= ''; - $s .= '
'; - $s .= ''; - $s .= ''; + $s .= '
'; + $s .= ''; + $s .= ''; $s .= '
'; - $s .= ''; - $s .= ''; + $s .= ''; + $s .= ''; $s .= '
'; $s .= '
'; - $s .= ''; - $s .= ''; + $s .= ''; + $s .= ''; $s .= '
'; - $s .= ''; - $s .= ''; + $s .= ''; + $s .= ''; $s .= '
'; $s .= '
'; - $s .= ''; - $s .= ''; + $s .= ''; + $s .= ''; $s .= '
'; $s .= '
'; } } - $s .= '
'; + $s .= '
'; } @@ -329,12 +329,12 @@ function twitter_post_local(&$a,&$b) { if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'twitter','post_by_default'))) $twitter_enable = 1; - if(! $twitter_enable) - return; + if(! $twitter_enable) + return; - if(strlen($b['postopts'])) - $b['postopts'] .= ','; - $b['postopts'] .= 'twitter'; + if(strlen($b['postopts'])) + $b['postopts'] .= ','; + $b['postopts'] .= 'twitter'; } } @@ -385,29 +385,31 @@ function twitter_post_hook(&$a,&$b) { if($b['parent'] != $b['id']) { logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA); - // Looking if its a reply to a twitter post + // Looking if its a reply to a twitter post if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) { - logger("twitter_post_hook: no twitter post ".$b["parent"]); - return; + logger("twitter_post_hook: no twitter post ".$b["parent"]); + return; } - $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1", - dbesc($b["thr-parent"]), - intval($b["uid"])); + $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1", + dbesc($b["thr-parent"]), + intval($b["uid"])); - if(!count($r)) { - logger("twitter_post_hook: no parent found ".$b["thr-parent"]); - return; - } else { - $iscomment = true; - $orig_post = $r[0]; - } + if(!count($r)) { + logger("twitter_post_hook: no parent found ".$b["thr-parent"]); + return; + } else { + $iscomment = true; + $orig_post = $r[0]; + } - $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]); - $nickname = "@[url=".$orig_post["author-link"]."]".$nickname."[/url]"; - logger("twitter_post_hook: comparing ".$nickname." with ".$b["body"], LOGGER_DEBUG); - if (strpos($b["body"], $nickname) === false) + $nicknameplain = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]); + $nickname = "@[url=".$orig_post["author-link"]."]".$nicknameplain."[/url]"; + $nicknameplain = "@".$nicknameplain; + + logger("twitter_post_hook: comparing ".$nickname." and ".$nicknameplain." with ".$b["body"], LOGGER_DEBUG); + if ((strpos($b["body"], $nickname) === false) AND (strpos($b["body"], $nicknameplain) === false)) $b["body"] = $nickname." ".$b["body"]; logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA); @@ -428,10 +430,10 @@ function twitter_post_hook(&$a,&$b) { else twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like"); return; - } + } if($b['deleted'] || ($b['created'] !== $b['edited'])) - return; + return; // if post comes from twitter don't send it back if($b['app'] == "Twitter") @@ -484,7 +486,7 @@ function twitter_post_hook(&$a,&$b) { // so we are using a new library for twitter // To-Do: // Switching completely to this library with all functions - require_once("addon/twitter/codebird.php"); + require_once("addon/twitter/codebird.php"); $cb = \Codebird\Codebird::getInstance(); $cb->setConsumerKey($ckey, $csecret); @@ -554,7 +556,7 @@ function twitter_post_hook(&$a,&$b) { function twitter_plugin_admin_post(&$a){ $consumerkey = ((x($_POST,'consumerkey')) ? notags(trim($_POST['consumerkey'])) : ''); $consumersecret = ((x($_POST,'consumersecret')) ? notags(trim($_POST['consumersecret'])): ''); - $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):''); + $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):''); set_config('twitter','consumerkey',$consumerkey); set_config('twitter','consumersecret',$consumersecret); set_config('twitter','application_name',$applicationname); @@ -567,8 +569,8 @@ function twitter_plugin_admin(&$a, &$o){ '$submit' => t('Save Settings'), // name, label, value, help, [extra values] '$consumerkey' => array('consumerkey', t('Consumer key'), get_config('twitter', 'consumerkey' ), ''), - '$consumersecret' => array('consumersecret', t('Consumer secret'), get_config('twitter', 'consumersecret' ), ''), - '$applicationname' => array('applicationname', t('Name of the Twitter Application'), get_config('twitter','application_name'),t('set this to avoid mirroring postings from ~friendica back to ~friendica')) + '$consumersecret' => array('consumersecret', t('Consumer secret'), get_config('twitter', 'consumersecret' ), ''), + '$applicationname' => array('applicationname', t('Name of the Twitter Application'), get_config('twitter','application_name'),t('set this to avoid mirroring postings from ~friendica back to ~friendica')) )); } @@ -605,21 +607,21 @@ function twitter_cron($a,$b) { /* // To-Do - // check for new contacts once a day - $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check'); - if($last_contact_check) - $next_contact_check = $last_contact_check + 86400; - else - $next_contact_check = 0; + // check for new contacts once a day + $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check'); + if($last_contact_check) + $next_contact_check = $last_contact_check + 86400; + else + $next_contact_check = 0; - if($next_contact_check <= time()) { - pumpio_getallusers($a, $rr["uid"]); - set_pconfig($rr['uid'],'pumpio','contact_check',time()); - } + if($next_contact_check <= time()) { + pumpio_getallusers($a, $rr["uid"]); + set_pconfig($rr['uid'],'pumpio','contact_check',time()); + } */ - } - } + } + } logger('twitter: cron_end'); @@ -628,26 +630,26 @@ function twitter_cron($a,$b) { function twitter_expire($a,$b) { - $days = get_config('twitter', 'expire'); + $days = get_config('twitter', 'expire'); - if ($days == 0) - return; + if ($days == 0) + return; $r = q("DELETE FROM `item` WHERE `deleted` AND `network` = '%s'", dbesc(NETWORK_TWITTER)); - require_once("include/items.php"); + require_once("include/items.php"); - logger('twitter_expire: expire_start'); + logger('twitter_expire: expire_start'); - $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()"); - if(count($r)) { - foreach($r as $rr) { - logger('twitter_expire: user '.$rr['uid']); - item_expire($rr['uid'], $days, NETWORK_TWITTER, true); - } - } + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()"); + if(count($r)) { + foreach($r as $rr) { + logger('twitter_expire: user '.$rr['uid']); + item_expire($rr['uid'], $days, NETWORK_TWITTER, true); + } + } - logger('twitter_expire: expire_end'); + logger('twitter_expire: expire_end'); } function twitter_fetchtimeline($a, $uid) { @@ -684,7 +686,7 @@ function twitter_fetchtimeline($a, $uid) { $posts = array_reverse($items); - if (count($posts)) { + if (count($posts)) { foreach ($posts as $post) { if ($post->id_str > $lastid) $lastid = $post->id_str; @@ -767,8 +769,8 @@ function twitter_fetchtimeline($a, $uid) { // require_once('mod/item.php'); item_post($a); - } - } + } + } } set_pconfig($uid, 'twitter', 'lastid', $lastid); } @@ -842,7 +844,7 @@ function twitter_fetch_contact($uid, $contact, $create_user) { // Check if the unique contact is existing // To-Do: only update once a while - $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1", + $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1", dbesc(normalise_link("https://twitter.com/".$contact->screen_name))); if (count($r) == 0) @@ -1114,16 +1116,16 @@ function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontinclud } // it seems as if the entities aren't always covering all mentions. So the rest will be checked here - $tags = get_tags($body); + $tags = get_tags($body); - if(count($tags)) { + if(count($tags)) { foreach($tags as $tag) { if (strstr(trim($tag), " ")) continue; - if(strpos($tag,'#') === 0) { - if(strpos($tag,'[url=')) - continue; + if(strpos($tag,'#') === 0) { + if(strpos($tag,'[url=')) + continue; // don't link tags that are already embedded in links @@ -1138,8 +1140,8 @@ function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontinclud $tags_arr["#".$basetag] = $url; continue; } elseif(strpos($tag,'@') === 0) { - if(strpos($tag,'[url=')) - continue; + if(strpos($tag,'[url=')) + continue; $basetag = substr($tag,1); $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]'; @@ -1458,7 +1460,7 @@ function twitter_fetchhometimeline($a, $uid) { return; } - $posts = array_reverse($items); + $posts = array_reverse($items); logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG); @@ -1501,7 +1503,7 @@ function twitter_fetchhometimeline($a, $uid) { return; } - $posts = array_reverse($items); + $posts = array_reverse($items); logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG); @@ -1535,19 +1537,19 @@ function twitter_fetchhometimeline($a, $uid) { require_once('include/enotify.php'); notification(array( 'type' => NOTIFY_TAGSELF, - 'notify_flags' => $u[0]['notify-flags'], - 'language' => $u[0]['language'], - 'to_name' => $u[0]['username'], - 'to_email' => $u[0]['email'], - 'uid' => $u[0]['uid'], - 'item' => $postarray, - 'link' => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item, - 'source_name' => $postarray['author-name'], - 'source_link' => $postarray['author-link'], + 'notify_flags' => $u[0]['notify-flags'], + 'language' => $u[0]['language'], + 'to_name' => $u[0]['username'], + 'to_email' => $u[0]['email'], + 'uid' => $u[0]['uid'], + 'item' => $postarray, + 'link' => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item, + 'source_name' => $postarray['author-name'], + 'source_link' => $postarray['author-link'], 'source_photo' => $postarray['author-avatar'], - 'verb' => ACTIVITY_TAG, - 'otype' => 'item' - )); + 'verb' => ACTIVITY_TAG, + 'otype' => 'item' + )); } } } @@ -1578,8 +1580,8 @@ function twitter_fetch_own_contact($a, $uid) { $contact_id = twitter_fetch_contact($uid, $user, true); } else { - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", - intval($uid), dbesc("twitter::".$own_id)); + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", + intval($uid), dbesc("twitter::".$own_id)); if(count($r)) $contact_id = $r[0]["id"]; else @@ -1636,5 +1638,4 @@ function twitter_is_retweet($a, $uid, $body) { return(!isset($result->errors)); } - ?> From d7c129fb1330d04e7a568fd70e494e83cb9b0e09 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Tue, 10 Jun 2014 20:14:51 +0200 Subject: [PATCH 4/4] Appnet: Bidirectional sync with app.net --- appnet/appnet.css | 4 +- appnet/appnet.php | 820 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 770 insertions(+), 54 deletions(-) diff --git a/appnet/appnet.css b/appnet/appnet.css index 231f4306a..b1d8d27e3 100755 --- a/appnet/appnet.css +++ b/appnet/appnet.css @@ -1,4 +1,4 @@ -#appnet-disconnect-label, #appnet-token-label, +#appnet-import-label, #appnet-disconnect-label, #appnet-token-label, #appnet-enable-label, #appnet-bydefault-label, #appnet-clientid-label, #appnet-clientsecret-label { float: left; @@ -6,7 +6,7 @@ margin-top: 10px; } -#appnet-disconnect, #appnet-token, +#appnet-import, #appnet-disconnect, #appnet-token, #appnet-checkbox, #appnet-bydefault, #appnet-clientid, #appnet-clientsecret { float: left; diff --git a/appnet/appnet.php b/appnet/appnet.php index e30ecdca4..d175ad589 100644 --- a/appnet/appnet.php +++ b/appnet/appnet.php @@ -2,17 +2,24 @@ /** * Name: App.net Connector - * Description: Post to app.net - * Version: 0.1 + * Description: app.net postings import and export + * Version: 0.2 * Author: Michael Vogel */ +/* + Some marker in the post so that reimported posts can be treated better. (BBCode over app.net?) +*/ + +define('APPNET_DEFAULT_POLL_INTERVAL', 5); // given in minutes + function appnet_install() { - register_hook('post_local', 'addon/appnet/appnet.php', 'appnet_post_local'); - register_hook('notifier_normal', 'addon/appnet/appnet.php', 'appnet_send'); - register_hook('jot_networks', 'addon/appnet/appnet.php', 'appnet_jot_nets'); - register_hook('connector_settings', 'addon/appnet/appnet.php', 'appnet_settings'); - register_hook('connector_settings_post', 'addon/appnet/appnet.php', 'appnet_settings_post'); + register_hook('post_local', 'addon/appnet/appnet.php', 'appnet_post_local'); + register_hook('notifier_normal', 'addon/appnet/appnet.php', 'appnet_send'); + register_hook('jot_networks', 'addon/appnet/appnet.php', 'appnet_jot_nets'); + register_hook('cron', 'addon/appnet/appnet.php', 'appnet_cron'); + register_hook('connector_settings', 'addon/appnet/appnet.php', 'appnet_settings'); + register_hook('connector_settings_post','addon/appnet/appnet.php', 'appnet_settings_post'); } @@ -20,6 +27,7 @@ function appnet_uninstall() { unregister_hook('post_local', 'addon/appnet/appnet.php', 'appnet_post_local'); unregister_hook('notifier_normal', 'addon/appnet/appnet.php', 'appnet_send'); unregister_hook('jot_networks', 'addon/appnet/appnet.php', 'appnet_jot_nets'); + unregister_hook('cron', 'addon/appnet/appnet.php', 'appnet_cron'); unregister_hook('connector_settings', 'addon/appnet/appnet.php', 'appnet_settings'); unregister_hook('connector_settings_post', 'addon/appnet/appnet.php', 'appnet_settings_post'); } @@ -27,27 +35,27 @@ function appnet_uninstall() { function appnet_module() {} function appnet_content(&$a) { - if(! local_user()) { - notice( t('Permission denied.') . EOL); - return ''; - } + if(! local_user()) { + notice( t('Permission denied.') . EOL); + return ''; + } - require_once("mod/settings.php"); - settings_init($a); + require_once("mod/settings.php"); + settings_init($a); - if (isset($a->argv[1])) - switch ($a->argv[1]) { - case "connect": - $o = appnet_connect($a); - break; - default: - $o = print_r($a->argv, true); - break; - } - else + if (isset($a->argv[1])) + switch ($a->argv[1]) { + case "connect": + $o = appnet_connect($a); + break; + default: + $o = print_r($a->argv, true); + break; + } + else $o = appnet_connect($a); - return $o; + return $o; } function appnet_connect(&$a) { @@ -108,6 +116,11 @@ function appnet_settings(&$a,&$s) { $def_enabled = get_pconfig(local_user(),'appnet','post_by_default'); $def_checked = (($def_enabled) ? ' checked="checked" ' : ''); + $importenabled = get_pconfig(local_user(),'appnet','import'); + $importchecked = (($importenabled) ? ' checked="checked" ' : ''); + + $ownid = get_pconfig(local_user(),'appnet','ownid'); + $s .= ''; $s .= '

'. t('App.net Export').'

'; $s .= '
'; @@ -123,6 +136,9 @@ function appnet_settings(&$a,&$s) { try { $userdata = $app->getUser(); + if ($ownid != $userdata["id"]) + set_pconfig(local_user(),'appnet','ownid', $userdata["id"]); + $s .= '

'. t('Currently connected to: ') .''.$userdata["username"].'
'.$userdata["description"]["text"].'

'; $s .= '
'; $s .= ''; @@ -133,8 +149,13 @@ function appnet_settings(&$a,&$s) { $s .= ''; $s .= ''; $s .= '
'; + + $s .= ''; + $s .= ''; + $s .= '
'; + } - catch (AppDotNetException $e) { + catch (AppDotNetException $e) { $s .= t("

Error fetching user profile. Please clear the configuration and try again.

"); } //$s .= print_r($userdata, true); @@ -193,6 +214,9 @@ function appnet_settings_post(&$a,&$b) { del_pconfig(local_user(), 'appnet', 'clientsecret'); del_pconfig(local_user(), 'appnet', 'clientid'); del_pconfig(local_user(), 'appnet', 'token'); + del_pconfig(local_user(), 'appnet', 'post'); + del_pconfig(local_user(), 'appnet', 'post_by_default'); + del_pconfig(local_user(), 'appnet', 'import'); } if (isset($_POST["clientsecret"])) @@ -204,58 +228,109 @@ function appnet_settings_post(&$a,&$b) { if (isset($_POST["token"]) AND ($_POST["token"] != "")) set_pconfig(local_user(),'appnet','token', $_POST['token']); - set_pconfig(local_user(),'appnet','post',intval($_POST['appnet'])); - set_pconfig(local_user(),'appnet','post_by_default',intval($_POST['appnet_bydefault'])); + set_pconfig(local_user(), 'appnet', 'post', intval($_POST['appnet'])); + set_pconfig(local_user(), 'appnet', 'post_by_default', intval($_POST['appnet_bydefault'])); + set_pconfig(local_user(), 'appnet', 'import', intval($_POST['appnet_import'])); } } function appnet_post_local(&$a,&$b) { - if($b['edit']) return; - if((! local_user()) || (local_user() != $b['uid'])) - return; + if((local_user()) && (local_user() == $b['uid']) && (!$b['private']) && (!$b['parent'])) { + $appnet_post = intval(get_pconfig(local_user(),'appnet','post')); + $appnet_enable = (($appnet_post && x($_REQUEST,'appnet_enable')) ? intval($_REQUEST['appnet_enable']) : 0); - if($b['private'] || $b['parent']) - return; + // if API is used, default to the chosen settings + if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'appnet','post_by_default'))) + $appnet_enable = 1; - $post = intval(get_pconfig(local_user(),'appnet','post')); + if(! $appnet_enable) + return; - $enable = (($post && x($_REQUEST,'appnet_enable')) ? intval($_REQUEST['appnet_enable']) : 0); + if(strlen($b['postopts'])) + $b['postopts'] .= ','; - if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'appnet','post_by_default'))) - $enable = 1; - - if(!$enable) - return; - - if(strlen($b['postopts'])) - $b['postopts'] .= ','; - - $b['postopts'] .= 'appnet'; + $b['postopts'] .= 'appnet'; + } } function appnet_send(&$a,&$b) { logger('appnet_send: invoked for post '.$b['id']." ".$b['app']); - if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited'])) - return; + if (!get_pconfig($b["uid"],'appnet','import')) { + if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited'])) + return; + } - if(! strstr($b['postopts'],'appnet')) - return; + if($b['parent'] != $b['id']) { + logger("appnet_send: parameter ".print_r($b, true), LOGGER_DATA); - if($b['parent'] != $b['id']) + // Looking if its a reply to an app.net post + if ((substr($b["parent-uri"], 0, 5) != "adn::") AND (substr($b["extid"], 0, 5) != "adn::") AND (substr($b["thr-parent"], 0, 5) != "adn::")) { + logger("appnet_send: no app.net post ".$b["parent"]); + return; + } + + $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1", + dbesc($b["thr-parent"]), + intval($b["uid"])); + + if(!count($r)) { + logger("appnet_send: no parent found ".$b["thr-parent"]); + return; + } else { + $iscomment = true; + $orig_post = $r[0]; + } + + $nicknameplain = preg_replace("=https?://alpha.app.net/(.*)=ism", "$1", $orig_post["author-link"]); + $nickname = "@[url=".$orig_post["author-link"]."]".$nicknameplain."[/url]"; + $nicknameplain = "@".$nicknameplain; + + logger("appnet_send: comparing ".$nickname." and ".$nicknameplain." with ".$b["body"], LOGGER_DEBUG); + if ((strpos($b["body"], $nickname) === false) AND (strpos($b["body"], $nicknameplain) === false)) + $b["body"] = $nickname." ".$b["body"]; + + logger("appnet_send: parent found ".print_r($orig_post, true), LOGGER_DATA); + } else { + $iscomment = false; + + if($b['private'] OR !strstr($b['postopts'],'appnet')) + return; + } + + if (($b['verb'] == ACTIVITY_POST) AND $b['deleted']) + appnet_action($a, $b["uid"], substr($orig_post["uri"], 5), "delete"); + + if($b['verb'] == ACTIVITY_LIKE) { + logger("appnet_send: ".print_r($b, true), LOGGER_DEBUG); + logger("appnet_send: parameter 2 ".substr($b["thr-parent"], 5), LOGGER_DEBUG); + if ($b['deleted']) + appnet_action($a, $b["uid"], substr($b["thr-parent"], 5), "unlike"); + else + appnet_action($a, $b["uid"], substr($b["thr-parent"], 5), "like"); + return; + } + + if($b['deleted'] || ($b['created'] !== $b['edited'])) return; $token = get_pconfig($b['uid'],'appnet','token'); if($token) { + + // If it's a repeated message from app.net then do a native repost and exit + if (appnet_is_repost($a, $b['uid'], $b['body'])) + return; + + require_once 'addon/appnet/AppDotNet.php'; - $clientId = get_pconfig(local_user(),'appnet','clientid'); - $clientSecret = get_pconfig(local_user(),'appnet','clientsecret'); + $clientId = get_pconfig($b["uid"],'appnet','clientid'); + $clientSecret = get_pconfig($b["uid"],'appnet','clientsecret'); $app = new AppDotNet($clientId, $clientSecret); $app->setAccessToken($token); @@ -293,6 +368,28 @@ function appnet_send(&$a,&$b) { unlink($tempfile); } + // Adding a link to the original post, if it is a root post + if($b['parent'] == $b['id']) + $data["annotations"][] = array( + "type" => "net.app.core.crosspost", + "value" => array("canonical_url" => $b["plink"]) + ); + + // Adding the original post + $data["annotations"][] = array( + "type" => "com.friendica.post", + "value" => array( + "uri" => $b["uri"], + "title" => $b["title"], + "body" => substr($b["body"], 0, 4000), // To-Do: Better shortening + "tag" => $b["tag"], + "author-name" => $b["author-name"], + "author-link" => $b["author-link"], + "author-avatar" => $b["author-avatar"], + ) + ); + + // To-Do // Alle Links verkürzen @@ -318,14 +415,633 @@ function appnet_send(&$a,&$b) { //print_r($post); $data["entities"]["parse_links"] = true; - $data["entities"]["parse_markdown_links"] = true; + $data["entities"]["parse_markdown_links"] = true; + + if ($iscomment) + $data["reply_to"] = substr($orig_post["uri"], 5); try { $ret = $app->createPost($post["text"], $data); logger("appnet_send: send message ".$b["id"]." result: ".print_r($ret, true), LOGGER_DEBUG); + + if ($iscomment) { + logger('appnet_send: Update extid '.$ret["id"]." for post id ".$b['id']); + q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d", + dbesc("adn::".$ret["id"]), + intval($b['id']) + ); + } } catch (AppDotNetException $e) { logger("appnet_send: Error sending message ".$b["id"]); } } } + +function appnet_action($a, $uid, $pid, $action) { + require_once 'addon/appnet/AppDotNet.php'; + + $token = get_pconfig($uid,'appnet','token'); + $clientId = get_pconfig($uid,'appnet','clientid'); + $clientSecret = get_pconfig($uid,'appnet','clientsecret'); + + $app = new AppDotNet($clientId, $clientSecret); + $app->setAccessToken($token); + + logger("appnet_action '".$action."' ID: ".$pid, LOGGER_DATA); + + try { + switch ($action) { + case "delete": + $result = $app->deletePost($pid); + break; + case "like": + $result = $app->starPost($pid); + break; + case "unlike": + $result = $app->unstarPost($pid); + break; + } + logger("appnet_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG); + } + catch (AppDotNetException $e) { + logger("appnet_action: Error sending action ".$action." pid ".$pid, LOGGER_DEBUG); + } +} + +function appnet_is_repost($a, $uid, $body) { + $body = trim($body); + + // Skip if it isn't a pure repeated messages + // Does it start with a share? + if (strpos($body, "[share") > 0) + return(false); + + // Does it end with a share? + if (strlen($body) > (strrpos($body, "[/share]") + 8)) + return(false); + + $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body); + // Skip if there is no shared message in there + if ($body == $attributes) + return(false); + + $link = ""; + preg_match("/link='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $link = $matches[1]; + + preg_match('/link="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $link = $matches[1]; + + $id = preg_replace("=https?://alpha.app.net/(.*)/post/(.*)=ism", "$2", $link); + if ($id == $link) + return(false); + + logger('appnet_is_repost: Reposting id '.$id.' for user '.$uid, LOGGER_DEBUG); + + require_once 'addon/appnet/AppDotNet.php'; + + $token = get_pconfig($uid,'appnet','token'); + $clientId = get_pconfig($uid,'appnet','clientid'); + $clientSecret = get_pconfig($uid,'appnet','clientsecret'); + + $app = new AppDotNet($clientId, $clientSecret); + $app->setAccessToken($token); + + try { + $result = $app->repost($id); + logger('appnet_is_repost: result '.print_r($result, true), LOGGER_DEBUG); + return true; + } + catch (AppDotNetException $e) { + logger('appnet_is_repost: error doing repost', LOGGER_DEBUG); + return false; + } +} + +function appnet_fetchstream($a, $uid) { + require_once("addon/appnet/AppDotNet.php"); + require_once('include/items.php'); + + $token = get_pconfig($uid,'appnet','token'); + $clientId = get_pconfig($uid,'appnet','clientid'); + $clientSecret = get_pconfig($uid,'appnet','clientsecret'); + + $app = new AppDotNet($clientId, $clientSecret); + $app->setAccessToken($token); + + $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1", + intval($uid)); + + if(count($r)) + $me = $r[0]; + else { + logger("appnet_fetchstream: Own contact not found for user ".$uid, LOGGER_DEBUG); + return; + } + + $user = q("SELECT * FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1", + intval($uid) + ); + + if(count($user)) + $user = $user[0]; + else { + logger("appnet_fetchstream: Own user not found for user ".$uid, LOGGER_DEBUG); + return; + } + + $ownid = get_pconfig($uid,'appnet','ownid'); + + // Fetch stream + $param = array("count" => 200, "include_deleted" => false, "include_directed_posts" => true, + "include_html" => false, "include_post_annotations" => true); + + $lastid = get_pconfig($uid, 'appnet', 'laststreamid'); + + if ($lastid <> "") + $param["since_id"] = $lastid; + + try { + $stream = $app->getUserStream($param); + } + catch (AppDotNetException $e) { + logger("appnet_fetchstream: Error fetching stream for user ".$uid); + } + + $stream = array_reverse($stream); + foreach ($stream AS $post) { + $postarray = appnet_createpost($a, $uid, $post, $me, $user, $ownid, true); + + $item = item_store($postarray); + logger('appnet_fetchstream: User '.$uid.' posted stream item '.$item); + + $lastid = $post["id"]; + + if (($item != 0) AND ($postarray['contact-id'] != $me["id"])) { + $r = q("SELECT `thread`.`iid` AS `parent` FROM `thread` + INNER JOIN `item` ON `thread`.`iid` = `item`.`parent` AND `thread`.`uid` = `item`.`uid` + WHERE `item`.`id` = %d AND `thread`.`mention` LIMIT 1", dbesc($item)); + + if (count($r)) { + require_once('include/enotify.php'); + notification(array( + 'type' => NOTIFY_COMMENT, + 'notify_flags' => $user['notify-flags'], + 'language' => $user['language'], + 'to_name' => $user['username'], + 'to_email' => $user['email'], + 'uid' => $user['uid'], + 'item' => $postarray, + 'link' => $a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $item, + 'source_name' => $postarray['author-name'], + 'source_link' => $postarray['author-link'], + 'source_photo' => $postarray['author-avatar'], + 'verb' => ACTIVITY_POST, + 'otype' => 'item', + 'parent' => $r[0]["parent"], + )); + } + } + } + + set_pconfig($uid, 'appnet', 'laststreamid', $lastid); + + // Fetch mentions + $param = array("count" => 200, "include_deleted" => false, "include_directed_posts" => true, + "include_html" => false, "include_post_annotations" => true); + + $lastid = get_pconfig($uid, 'appnet', 'lastmentionid'); + + if ($lastid <> "") + $param["since_id"] = $lastid; + + try { + $mentions = $app->getUserMentions("me", $param); + } + catch (AppDotNetException $e) { + logger("appnet_fetchstream: Error fetching mentions for user ".$uid); + } + + $mentions = array_reverse($mentions); + foreach ($mentions AS $post) { + $postarray = appnet_createpost($a, $uid, $post, $me, $user, $ownid, false); + + if (isset($postarray["id"])) + $item = $postarray["id"]; + elseif (isset($postarray["body"])) { + $item = item_store($postarray); + logger('appnet_fetchstream: User '.$uid.' posted mention item '.$item); + } else + $item = 0; + + $lastid = $post["id"]; + + if (($item != 0) AND ($postarray['contact-id'] != $me["id"])) { + require_once('include/enotify.php'); + notification(array( + 'type' => NOTIFY_TAGSELF, + 'notify_flags' => $user['notify-flags'], + 'language' => $user['language'], + 'to_name' => $user['username'], + 'to_email' => $user['email'], + 'uid' => $user['uid'], + 'item' => $postarray, + 'link' => $a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $item, + 'source_name' => $postarray['author-name'], + 'source_link' => $postarray['author-link'], + 'source_photo' => $postarray['author-avatar'], + 'verb' => ACTIVITY_TAG, + 'otype' => 'item' + )); + } + } + + set_pconfig($uid, 'appnet', 'lastmentionid', $lastid); + + +/* To-Do + $param = array("interaction_actions" => "star"); + $interactions = $app->getMyInteractions($param); + foreach ($interactions AS $interaction) + appnet_dolike($a, $uid, $interaction); +*/ +} + +function appnet_createpost($a, $uid, $post, $me, $user, $ownid, $createuser, $threadcompletion = true) { + require_once('include/items.php'); + + if ($post["machine_only"]) + return; + + if ($post["is_deleted"]) + return; + + $postarray = array(); + $postarray['gravity'] = 0; + $postarray['uid'] = $uid; + $postarray['wall'] = 0; + $postarray['verb'] = ACTIVITY_POST; + $postarray['network'] = dbesc(NETWORK_APPNET); + $postarray['uri'] = "adn::".$post["id"]; + + $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($postarray['uri']), + intval($uid) + ); + + if (count($r)) + return($r[0]); + + $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1", + dbesc($postarray['uri']), + intval($uid) + ); + + if (count($r)) + return($r[0]); + + $postarray['parent-uri'] = "adn::".$post["thread_id"]; + if (isset($post["reply_to"]) AND ($post["reply_to"] != "")) { + $postarray['thr-parent'] = "adn::".$post["reply_to"]; + + // Complete the thread if the parent doesn't exists + if ($threadcompletion) { + $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($postarray['thr-parent']), + intval($uid) + ); + if (!count($r)) { + require_once("addon/appnet/AppDotNet.php"); + + $token = get_pconfig($uid,'appnet','token'); + $clientId = get_pconfig($uid,'appnet','clientid'); + $clientSecret = get_pconfig($uid,'appnet','clientsecret'); + + $app = new AppDotNet($clientId, $clientSecret); + $app->setAccessToken($token); + + $param = array("count" => 200, "include_deleted" => false, "include_directed_posts" => true, + "include_html" => false, "include_post_annotations" => true); + try { + $thread = $app->getPostReplies($post["thread_id"], $param); + } + catch (AppDotNetException $e) { + logger("appnet_createpost: Error fetching thread for user ".$uid); + } + $thread = array_reverse($thread); + foreach ($thread AS $tpost) { + $threadpost = appnet_createpost($a, $uid, $tpost, $me, $user, $ownid, $createuser, false); + $item = item_store($threadpost); + } + } + } + } else + $postarray['thr-parent'] = $postarray['uri']; + + $postarray['plink'] = $post["canonical_url"]; + + if (($post["user"]["id"] != $ownid) OR ($postarray['thr-parent'] == $postarray['uri'])) { + $postarray['owner-name'] = $post["user"]["name"]; + $postarray['owner-link'] = $post["user"]["canonical_url"]; + $postarray['owner-avatar'] = $post["user"]["avatar_image"]["url"]; + $postarray['contact-id'] = appnet_fetchcontact($a, $uid, $post["user"], $me, $createuser); + } else { + $postarray['owner-name'] = $me["name"]; + $postarray['owner-link'] = $me["url"]; + $postarray['owner-avatar'] = $me["thumb"]; + $postarray['contact-id'] = $me["id"]; + } + + $links = array(); + + if (is_array($post["repost_of"])) { + $postarray['author-name'] = $post["repost_of"]["user"]["name"]; + $postarray['author-link'] = $post["repost_of"]["user"]["canonical_url"]; + $postarray['author-avatar'] = $post["repost_of"]["user"]["avatar_image"]["url"]; + + $content = $post["repost_of"]; + } else { + $postarray['author-name'] = $postarray['owner-name']; + $postarray['author-link'] = $postarray['owner-link']; + $postarray['author-avatar'] = $postarray['owner-avatar']; + + $content = $post; + } + + if (is_array($content["entities"])) { + $converted = appnet_expand_entities($a, $content["text"], $content["entities"]); + $postarray['body'] = $converted["body"]; + $postarray['tag'] = $converted["tags"]; + } else + $postarray['body'] = $content["text"]; + + if (is_array($content["annotations"])) + $postarray['body'] = appnet_expand_annotations($a, $postarray['body'], $content["annotations"]); + + if (sizeof($content["entities"]["links"])) + foreach($content["entities"]["links"] AS $link) { + $url = normalise_link($link["url"]); + $links[$url] = $link["url"]; + } + + if (sizeof($content["annotations"])) + foreach($content["annotations"] AS $annotation) { + if ($annotation[type] == "net.app.core.oembed") { + if (isset($annotation["value"]["embeddable_url"])) { + $url = normalise_link($annotation["value"]["embeddable_url"]); + if (isset($links[$url])) + unset($links[$url]); + } + } elseif ($annotation[type] == "com.friendica.post") { + $links = array(); + if (isset($annotation["value"]["embeddable_url"])) + $postarray['title'] = $annotation["value"]["title"]; + + if (isset($annotation["value"]["body"])) + $postarray['body'] = $annotation["value"]["body"]; + + if (isset($annotation["value"]["tag"])) + $postarray['tag'] = $annotation["value"]["tag"]; + + if (isset($annotation["value"]["author-name"])) + $postarray['author-name'] = $annotation["value"]["author-name"]; + + if (isset($annotation["value"]["author-link"])) + $postarray['author-link'] = $annotation["value"]["author-link"]; + + if (isset($annotation["value"]["author-avatar"])) + $postarray['author-avatar'] = $annotation["value"]["author-avatar"]; + } + + } + + if (sizeof($links)) { + $link = array_pop($links); + $url = "[url=".$link."]".$link."[/url]"; + + $removedlink = trim(str_replace($url, "", $postarray['body'])); + + if (($removedlink == "") OR strstr($postarray['body'], $removedlink)) + $postarray['body'] = $removedlink; + + $postarray['body'] .= add_page_info($link); + } + + $postarray['created'] = datetime_convert('UTC','UTC',$post["created_at"]); + $postarray['edited'] = datetime_convert('UTC','UTC',$post["created_at"]); + + $postarray['app'] = $post["source"]["name"]; + + return($postarray); + //print_r($postarray); + //print_r($post); +} + +function appnet_expand_entities($a, $body, $entities) { + + if (!function_exists('substr_unicode')) { + function substr_unicode($str, $s, $l = null) { + return join("", array_slice( + preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY), $s, $l)); + } + } + + $tags_arr = array(); + $replace = array(); + + foreach ($entities["mentions"] AS $mention) { + $url = "@[url=https://alpha.app.net/".rawurlencode($mention["name"])."]".$mention["name"]."[/url]"; + $tags_arr["@".$mention["name"]] = $url; + $replace[$mention["pos"]] = array("pos"=> $mention["pos"], "len"=> $mention["len"], "replace"=> $url); + } + + foreach ($entities["hashtags"] AS $hashtag) { + $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag["name"])."]".$hashtag["name"]."[/url]"; + $tags_arr["#".$hashtag["name"]] = $url; + $replace[$hashtag["pos"]] = array("pos"=> $hashtag["pos"], "len"=> $hashtag["len"], "replace"=> $url); + } + + foreach ($entities["links"] AS $links) { + $url = "[url=".$links["url"]."]".$links["text"]."[/url]"; + $replace[$links["pos"]] = array("pos"=> $links["pos"], "len"=> $links["len"], "replace"=> $url); + } + + + if (sizeof($replace)) { + krsort($replace); + foreach ($replace AS $entity) { + $pre = substr_unicode($body, 0, $entity["pos"]); + $post = substr_unicode($body, $entity["pos"] + $entity["len"]); + + $body = $pre.$entity["replace"].$post; + } + } + + return(array("body" => $body, "tags" => implode($tags_arr, ","))); +} + +function appnet_expand_annotations($a, $body, $annotations) { + foreach ($annotations AS $annotation) { + if ($annotation["value"]["type"] == "photo") { + if (($annotation["value"]["thumbnail_large_url"] != "") AND ($annotation["value"]["url"] != "")) + $body .= "\n[url=".$annotation["value"]["url"]."][img]".$annotation["value"]["thumbnail_large_url"]."[/img][/url]"; + elseif ($annotation["value"]["url"] != "") + $body .= "\n[img]".$annotation["value"]["url"]."[/img]"; + } + } + return $body; +} + +function appnet_fetchcontact($a, $uid, $contact, $me, $create_user) { + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", + intval($uid), dbesc("adn::".$contact["id"])); + + if(!count($r) AND !$create_user) + return($me); + + + if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) { + logger("appnet_fetchcontact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG); + return(-1); + } + + if(!count($r)) { + // create contact record + q("INSERT INTO `contact` (`uid`, `created`, `url`, `nurl`, `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', '%s', %d, %d, %d, 0, 0, 0 ) ", + intval($uid), + dbesc(datetime_convert()), + dbesc($contact["canonical_url"]), + dbesc(normalise_link($contact["canonical_url"])), + dbesc($contact["username"]."@app.net"), + dbesc("adn::".$contact["id"]), + dbesc(''), + dbesc("adn::".$contact["id"]), + dbesc($contact["name"]), + dbesc($contact["username"]), + dbesc($contact["avatar_image"]["url"]), + dbesc(NETWORK_APPNET), + intval(CONTACT_IS_FRIEND), + intval(1), + intval(1) + ); + + $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1", + dbesc("adn::".$contact["id"]), + intval($uid) + ); + + if(! count($r)) + return(false); + + $contact_id = $r[0]['id']; + + $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1", + intval($uid) + ); + + if($g && intval($g[0]['def_gid'])) { + require_once('include/group.php'); + group_add_member($uid,'',$contact_id,$g[0]['def_gid']); + } + + require_once("Photo.php"); + + $photos = import_profile_photo($contact["avatar_image"]["url"],$uid,$contact_id); + + q("UPDATE `contact` SET `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `name-date` = '%s', + `uri-date` = '%s', + `avatar-date` = '%s' + WHERE `id` = %d", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($contact_id) + ); + + } else { + // update profile photos once every two weeks as we have no notification of when they change. + + //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false); + $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours')); +$update_photo = true; + // 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']) || ($update_photo)) { + + logger("appnet_fetchcontact: Updating contact ".$contact["username"], LOGGER_DEBUG); + + require_once("Photo.php"); + + $photos = import_profile_photo($contact["avatar_image"]["url"], $uid, $r[0]['id']); + + q("UPDATE `contact` SET `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `name-date` = '%s', + `uri-date` = '%s', + `avatar-date` = '%s', + `url` = '%s', + `nurl` = '%s', + `addr` = '%s', + `name` = '%s', + `nick` = '%s' + WHERE `id` = %d", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($contact["canonical_url"]), + dbesc(normalise_link($contact["canonical_url"])), + dbesc($contact["username"]."@app.net"), + dbesc($contact["name"]), + dbesc($contact["username"]), + intval($r[0]['id']) + ); + } + } + + return($r[0]["id"]); +} + +function appnet_cron($a,$b) { + $last = get_config('appnet','last_poll'); + + $poll_interval = intval(get_config('appnet','poll_interval')); + if(! $poll_interval) + $poll_interval = APPNET_DEFAULT_POLL_INTERVAL; + + if($last) { + $next = $last + ($poll_interval * 60); + if($next > time()) { + logger('appnet_cron: poll intervall not reached'); + return; + } + } + logger('appnet_cron: cron_start'); + + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'appnet' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()"); + if(count($r)) { + foreach($r as $rr) { + logger('appnet_cron: importing timeline from user '.$rr['uid']); + appnet_fetchstream($a, $rr["uid"]); + } + } + + logger('appnet_cron: cron_end'); + + set_config('appnet','last_poll', time()); +}