]> git.mxchange.org Git - friendica-addons.git/blob - twitter/twitter.php
Reduce the opacity of the network settings icon when disabled.
[friendica-addons.git] / twitter / twitter.php
1 <?php
2 /**
3  * Name: Twitter Connector
4  * Description: Relay public postings to a connected Twitter account
5  * Version: 1.0.4
6  * Author: Tobias Diekershoff <https://f.diekershoff.de/profile/tobias>
7  * Author: Michael Vogel <https://pirati.ca/profile/heluecht>
8  *
9  * Copyright (c) 2011-2013 Tobias Diekershoff, Michael Vogel
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *    * Redistributions of source code must retain the above copyright notice,
15  *     this list of conditions and the following disclaimer.
16  *    * Redistributions in binary form must reproduce the above
17  *    * copyright notice, this list of conditions and the following disclaimer in
18  *      the documentation and/or other materials provided with the distribution.
19  *    * Neither the name of the <organization> nor the names of its contributors
20  *      may be used to endorse or promote products derived from this software
21  *      without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
31  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35  
36 /*   Twitter Plugin for Friendica
37  *
38  *   Author: Tobias Diekershoff
39  *           tobias.diekershoff@gmx.net
40  *
41  *   License:3-clause BSD license
42  *
43  *   Configuration:
44  *     To use this plugin you need a OAuth Consumer key pair (key & secret)
45  *     you can get it from Twitter at https://twitter.com/apps
46  *
47  *     Register your Friendica site as "Client" application with "Read & Write" access
48  *     we do not need "Twitter as login". When you've registered the app you get the
49  *     OAuth Consumer key and secret pair for your application/site.
50  *
51  *     Add this key pair to your global .htconfig.php or use the admin panel.
52  *
53  *     $a->config['twitter']['consumerkey'] = 'your consumer_key here';
54  *     $a->config['twitter']['consumersecret'] = 'your consumer_secret here';
55  *
56  *     To activate the plugin itself add it to the $a->config['system']['addon']
57  *     setting. After this, your user can configure their Twitter account settings
58  *     from "Settings -> Plugin Settings".
59  *
60  *     Requirements: PHP5, curl [Slinky library]
61  */
62
63 define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes
64
65 function twitter_install() {
66         //  we need some hooks, for the configuration and for sending tweets
67         register_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); 
68         register_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
69         register_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
70         register_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
71         register_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
72         register_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
73         register_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
74         register_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
75         logger("installed twitter");
76 }
77
78
79 function twitter_uninstall() {
80         unregister_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); 
81         unregister_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
82         unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
83         unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
84         unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
85         unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
86         unregister_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
87         unregister_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
88
89         // old setting - remove only
90         unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook');
91         unregister_hook('plugin_settings', 'addon/twitter/twitter.php', 'twitter_settings'); 
92         unregister_hook('plugin_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
93
94 }
95
96 function twitter_follow($a, &$contact) {
97
98         logger("twitter_follow: Check if contact is twitter contact. ".$contact["url"], LOGGER_DEBUG);
99
100         if (!strstr($contact["url"], "://twitter.com") AND !strstr($contact["url"], "@twitter.com"))
101                 return;
102
103         // contact seems to be a twitter contact, so continue
104         $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $contact["url"]);
105         $nickname = str_replace("@twitter.com", "", $nickname);
106
107         $uid = $a->user["uid"];
108
109         $ckey    = get_config('twitter', 'consumerkey');
110         $csecret = get_config('twitter', 'consumersecret');
111         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
112         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
113
114         require_once("addon/twitter/codebird.php");
115
116         $cb = \Codebird\Codebird::getInstance();
117         $cb->setConsumerKey($ckey, $csecret);
118         $cb->setToken($otoken, $osecret);
119
120         $parameters = array();
121         $parameters["screen_name"] = $nickname;
122
123         $user = $cb->friendships_create($parameters);
124
125         twitter_fetchuser($a, $uid, $nickname);
126
127         $r = q("SELECT name,nick,url,addr,batch,notify,poll,request,confirm,poco,photo,priority,network,alias,pubkey
128                 FROM `contact` WHERE `uid` = %d AND `nick` = '%s'",
129                                 intval($uid),
130                                 dbesc($nickname));
131         if (count($r))
132                 $contact["contact"] = $r[0];
133 }
134
135 function twitter_jot_nets(&$a,&$b) {
136         if(! local_user())
137                 return;
138
139         $tw_post = get_pconfig(local_user(),'twitter','post');
140         if(intval($tw_post) == 1) {
141                 $tw_defpost = get_pconfig(local_user(),'twitter','post_by_default');
142                 $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : '');
143                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="twitter_enable"' . $selected . ' value="1" /> ' 
144                         . t('Post to Twitter') . '</div>';
145         }
146 }
147
148 function twitter_settings_post ($a,$post) {
149         if(! local_user())
150                 return;
151         // don't check twitter settings if twitter submit button is not clicked
152         if (!x($_POST,'twitter-submit'))
153                 return;
154
155         if (isset($_POST['twitter-disconnect'])) {
156                 /***
157                  * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
158                  * from the user configuration
159                  */
160                 del_pconfig(local_user(), 'twitter', 'consumerkey');
161                 del_pconfig(local_user(), 'twitter', 'consumersecret');
162                 del_pconfig(local_user(), 'twitter', 'oauthtoken');
163                 del_pconfig(local_user(), 'twitter', 'oauthsecret');
164                 del_pconfig(local_user(), 'twitter', 'post');
165                 del_pconfig(local_user(), 'twitter', 'post_by_default');
166                 del_pconfig(local_user(), 'twitter', 'post_taglinks');
167                 del_pconfig(local_user(), 'twitter', 'lastid');
168                 del_pconfig(local_user(), 'twitter', 'mirror_posts');
169                 del_pconfig(local_user(), 'twitter', 'intelligent_shortening');
170                 del_pconfig(local_user(), 'twitter', 'import');
171                 del_pconfig(local_user(), 'twitter', 'create_user');
172                 del_pconfig(local_user(), 'twitter', 'own_id');
173         } else {
174         if (isset($_POST['twitter-pin'])) {
175                 //  if the user supplied us with a PIN from Twitter, let the magic of OAuth happen
176                 logger('got a Twitter PIN');
177                 require_once('library/twitteroauth.php');
178                 $ckey    = get_config('twitter', 'consumerkey');
179                 $csecret = get_config('twitter', 'consumersecret');
180                 //  the token and secret for which the PIN was generated were hidden in the settings
181                 //  form as token and token2, we need a new connection to Twitter using these token
182                 //  and secret to request a Access Token with the PIN
183                 $connection = new TwitterOAuth($ckey, $csecret, $_POST['twitter-token'], $_POST['twitter-token2']);
184                 $token   = $connection->getAccessToken( $_POST['twitter-pin'] );
185                 //  ok, now that we have the Access Token, save them in the user config
186                 set_pconfig(local_user(),'twitter', 'oauthtoken',  $token['oauth_token']);
187                 set_pconfig(local_user(),'twitter', 'oauthsecret', $token['oauth_token_secret']);
188                 set_pconfig(local_user(),'twitter', 'post', 1);
189                 set_pconfig(local_user(),'twitter', 'post_taglinks', 1);
190                 //  reload the Addon Settings page, if we don't do it see Bug #42
191                 goaway($a->get_baseurl().'/settings/connectors');
192         } else {
193                 //  if no PIN is supplied in the POST variables, the user has changed the setting
194                 //  to post a tweet for every new __public__ posting to the wall
195                 set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable']));
196                 set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default']));
197                 set_pconfig(local_user(),'twitter','post_taglinks',intval($_POST['twitter-sendtaglinks']));
198                 set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
199                 set_pconfig(local_user(), 'twitter', 'intelligent_shortening', intval($_POST['twitter-shortening']));
200                 set_pconfig(local_user(), 'twitter', 'import', intval($_POST['twitter-import']));
201                 set_pconfig(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user']));
202                 info( t('Twitter settings updated.') . EOL);
203         }}
204 }
205 function twitter_settings(&$a,&$s) {
206         if(! local_user())
207                 return;
208         $a->page['htmlhead'] .= '<link rel="stylesheet"  type="text/css" href="' . $a->get_baseurl() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
209         /***
210          * 1) Check that we have global consumer key & secret
211          * 2) If no OAuthtoken & stuff is present, generate button to get some
212          * 3) Checkbox for "Send public notices (140 chars only)
213          */
214         $ckey    = get_config('twitter', 'consumerkey' );
215         $csecret = get_config('twitter', 'consumersecret' );
216         $otoken  = get_pconfig(local_user(), 'twitter', 'oauthtoken'  );
217         $osecret = get_pconfig(local_user(), 'twitter', 'oauthsecret' );
218         $enabled = get_pconfig(local_user(), 'twitter', 'post');
219         $checked = (($enabled) ? ' checked="checked" ' : '');
220         $defenabled = get_pconfig(local_user(),'twitter','post_by_default');
221         $defchecked = (($defenabled) ? ' checked="checked" ' : '');
222         $linksenabled = get_pconfig(local_user(),'twitter','post_taglinks');
223         $linkschecked = (($linksenabled) ? ' checked="checked" ' : '');
224         $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts');
225         $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : '');
226         $shorteningenabled = get_pconfig(local_user(),'twitter','intelligent_shortening');
227         $shorteningchecked = (($shorteningenabled) ? ' checked="checked" ' : '');
228         $importenabled = get_pconfig(local_user(),'twitter','import');
229         $importchecked = (($importenabled) ? ' checked="checked" ' : '');
230         $create_userenabled = get_pconfig(local_user(),'twitter','create_user');
231         $create_userchecked = (($create_userenabled) ? ' checked="checked" ' : '');
232
233         $css = (($enabled) ? '' : '-disabled');
234
235         $globalshortening = get_config('twitter','intelligent_shortening');
236
237         $s .= '<span id="settings_twitter_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
238         $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
239         $s .= '</span>';
240         $s .= '<div id="settings_twitter_expanded" class="settings-block" style="display: none;">';
241         $s .= '<span class="fakelink" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
242         $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
243         $s .= '</span>';
244
245         if ( (!$ckey) && (!$csecret) ) {
246                 /***
247                  * no global consumer keys
248                  * display warning and skip personal config
249                  */
250                 $s .= '<p>'. t('No consumer key pair for Twitter found. Please contact your site administrator.') .'</p>';
251         } else {
252                 /***
253                  * ok we have a consumer key pair now look into the OAuth stuff
254                  */
255                 if ( (!$otoken) && (!$osecret) ) {
256                         /***
257                          * the user has not yet connected the account to twitter...
258                          * get a temporary OAuth key/secret pair and display a button with
259                          * which the user can request a PIN to connect the account to a
260                          * account at Twitter.
261                          */
262                         require_once('library/twitteroauth.php');
263                         $connection = new TwitterOAuth($ckey, $csecret);
264                         $request_token = $connection->getRequestToken();
265                         $token = $request_token['oauth_token'];
266                         /***
267                          *  make some nice form
268                          */
269                         $s .= '<p>'. t('At this Friendica instance the Twitter plugin was enabled but you have not yet connected your account to your Twitter account. To do so click the button below to get a PIN from Twitter which you have to copy into the input box below and submit the form. Only your <strong>public</strong> posts will be posted to Twitter.') .'</p>';
270                         $s .= '<a href="'.$connection->getAuthorizeURL($token).'" target="_twitter"><img src="addon/twitter/lighter.png" alt="'.t('Log in with Twitter').'"></a>';
271                         $s .= '<div id="twitter-pin-wrapper">';
272                         $s .= '<label id="twitter-pin-label" for="twitter-pin">'. t('Copy the PIN from Twitter here') .'</label>';
273                         $s .= '<input id="twitter-pin" type="text" name="twitter-pin" />';
274                         $s .= '<input id="twitter-token" type="hidden" name="twitter-token" value="'.$token.'" />';
275                         $s .= '<input id="twitter-token2" type="hidden" name="twitter-token2" value="'.$request_token['oauth_token_secret'].'" />';
276             $s .= '</div><div class="clear"></div>';
277             $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
278                 } else {
279                         /***
280                          *  we have an OAuth key / secret pair for the user
281                          *  so let's give a chance to disable the postings to Twitter
282                          */
283                         require_once('library/twitteroauth.php');
284                         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
285                         $details = $connection->get('account/verify_credentials');
286                         $s .= '<div id="twitter-info" ><img id="twitter-avatar" src="'.$details->profile_image_url.'" /><p id="twitter-info-block">'. t('Currently connected to: ') .'<a href="https://twitter.com/'.$details->screen_name.'" target="_twitter">'.$details->screen_name.'</a><br /><em>'.$details->description.'</em></p></div>';
287                         $s .= '<p>'. t('If enabled all your <strong>public</strong> 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.') .'</p>';
288                         if ($a->user['hidewall']) {
289                             $s .= '<p>'. t('<strong>Note</strong>: Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) 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.') .'</p>';
290                         }
291                         $s .= '<div id="twitter-enable-wrapper">';
292                         $s .= '<label id="twitter-enable-label" for="twitter-checkbox">'. t('Allow posting to Twitter'). '</label>';
293                         $s .= '<input id="twitter-checkbox" type="checkbox" name="twitter-enable" value="1" ' . $checked . '/>';
294                         $s .= '<div class="clear"></div>';
295                         $s .= '<label id="twitter-default-label" for="twitter-default">'. t('Send public postings to Twitter by default') .'</label>';
296                         $s .= '<input id="twitter-default" type="checkbox" name="twitter-default" value="1" ' . $defchecked . '/>';
297                         $s .= '<div class="clear"></div>';
298
299                         $s .= '<label id="twitter-mirror-label" for="twitter-mirror">'.t('Mirror all posts from twitter that are no replies').'</label>';
300                         $s .= '<input id="twitter-mirror" type="checkbox" name="twitter-mirror" value="1" '. $mirrorchecked . '/>';
301                         $s .= '<div class="clear"></div>';
302
303                         if (!$globalshortening) {
304                                 $s .= '<label id="twitter-shortening-label" for="twitter-shortening">'.t('Shortening method that optimizes the tweet').'</label>';
305                                 $s .= '<input id="twitter-shortening" type="checkbox" name="twitter-shortening" value="1" '. $shorteningchecked . '/>';
306                                 $s .= '<div class="clear"></div>';
307
308                                 $s .= '<label id="twitter-sendtaglinks-label" for="twitter-sendtaglinks">'.t('Send linked #-tags and @-names to Twitter').'</label>';
309                                 $s .= '<input id="twitter-sendtaglinks" type="checkbox" name="twitter-sendtaglinks" value="1" '. $linkschecked . '/>';
310                                 $s .= '<div class="clear"></div>';
311                         }
312                         $s .= '</div>';
313
314                         $s .= '<label id="twitter-import-label" for="twitter-import">'.t('Import the remote timeline').'</label>';
315                         $s .= '<input id="twitter-import" type="checkbox" name="twitter-import" value="1" '. $importchecked . '/>';
316                         $s .= '<div class="clear"></div>';
317
318                         $s .= '<label id="twitter-create_user-label" for="twitter-create_user">'.t('Automatically create contacts').'</label>';
319                         $s .= '<input id="twitter-create_user" type="checkbox" name="twitter-create_user" value="1" '. $create_userchecked . '/>';
320                         $s .= '<div class="clear"></div>';
321
322                         $s .= '<div id="twitter-disconnect-wrapper">';
323                         $s .= '<label id="twitter-disconnect-label" for="twitter-disconnect">'. t('Clear OAuth configuration') .'</label>';
324                         $s .= '<input id="twitter-disconnect" type="checkbox" name="twitter-disconnect" value="1" />';
325                         $s .= '</div><div class="clear"></div>';
326                         $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>'; 
327                 }
328         }
329         $s .= '</div><div class="clear"></div>';
330 }
331
332
333 function twitter_post_local(&$a,&$b) {
334
335         if($b['edit'])
336                 return;
337
338         if((local_user()) && (local_user() == $b['uid']) && (! $b['private']) && (! $b['parent']) ) {
339
340                 $twitter_post = intval(get_pconfig(local_user(),'twitter','post'));
341                 $twitter_enable = (($twitter_post && x($_REQUEST,'twitter_enable')) ? intval($_REQUEST['twitter_enable']) : 0);
342
343                 // if API is used, default to the chosen settings
344                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'twitter','post_by_default')))
345                         $twitter_enable = 1;
346
347         if(! $twitter_enable)
348             return;
349
350         if(strlen($b['postopts']))
351             $b['postopts'] .= ',';
352         $b['postopts'] .= 'twitter';
353         }
354 }
355
356 if (! function_exists('short_link')) {
357 function short_link ($url) {
358     require_once('library/slinky.php');
359     $slinky = new Slinky( $url );
360     $yourls_url = get_config('yourls','url1');
361     if ($yourls_url) {
362             $yourls_username = get_config('yourls','username1');
363             $yourls_password = get_config('yourls', 'password1');
364             $yourls_ssl = get_config('yourls', 'ssl1');
365             $yourls = new Slinky_YourLS();
366             $yourls->set( 'username', $yourls_username );
367             $yourls->set( 'password', $yourls_password );
368             $yourls->set( 'ssl', $yourls_ssl );
369             $yourls->set( 'yourls-url', $yourls_url );
370             $slinky->set_cascade( array( $yourls, new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
371     }
372     else {
373             // setup a cascade of shortening services
374             // try to get a short link from these services
375             // in the order ur1.ca, trim, id.gd, tinyurl
376             $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
377     }
378     return $slinky->short();
379 } };
380
381 function twitter_shortenmsg($b, $shortlink = false) {
382         require_once("include/api.php");
383         require_once("include/bbcode.php");
384         require_once("include/html2plain.php");
385
386         $max_char = 140;
387
388         $b['body'] = bb_CleanPictureLinks($b['body']);
389
390         // Looking for the first image
391         $cleaned_body = api_clean_plain_items($b['body']);
392         $image = '';
393         if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$cleaned_body,$matches))
394                 $image = $matches[3];
395
396         if ($image == '')
397                 if(preg_match("/\[img\](.*?)\[\/img\]/is",$cleaned_body,$matches))
398                         $image = $matches[1];
399
400         $multipleimages = (strpos($cleaned_body, "[img") != strrpos($cleaned_body, "[img"));
401
402         // When saved into the database the content is sent through htmlspecialchars
403         // That means that we have to decode all image-urls
404         $image = htmlspecialchars_decode($image);
405
406         $body = $b["body"];
407         if ($b["title"] != "")
408                 $body = $b["title"]."\n\n".$body;
409
410         if (strpos($body, "[bookmark") !== false) {
411                 // splitting the text in two parts:
412                 // before and after the bookmark
413                 $pos = strpos($body, "[bookmark");
414                 $body1 = substr($body, 0, $pos);
415                 $body2 = substr($body, $pos);
416
417                 // Removing all quotes after the bookmark
418                 // they are mostly only the content after the bookmark.
419                 $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2);
420                 $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2);
421                 $body = $body1.$body2;
422         }
423
424         // Add some newlines so that the message could be cut better
425         $body = str_replace(array("[quote", "[bookmark", "[/bookmark]", "[/quote]"),
426                         array("\n[quote", "\n[bookmark", "[/bookmark]\n", "[/quote]\n"), $body);
427
428         // remove the recycle signs and the names since they aren't helpful on twitter
429         // recycle 1
430         $recycle = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8');
431         $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
432         // recycle 2 (Test)
433         $recycle = html_entity_decode("&#x25CC; ", ENT_QUOTES, 'UTF-8');
434         $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
435
436         // remove the share element
437         //$body = preg_replace("/\[share(.*?)\](.*?)\[\/share\]/ism","\n\n$2\n\n",$body);
438
439         // At first convert the text to html
440         $html = bbcode(api_clean_plain_items($body), false, false, 2);
441
442         // Then convert it to plain text
443         $msg = trim(html2plain($html, 0, true));
444         $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
445
446         // Removing multiple newlines
447         while (strpos($msg, "\n\n\n") !== false)
448                 $msg = str_replace("\n\n\n", "\n\n", $msg);
449
450         // Removing multiple spaces
451         while (strpos($msg, "  ") !== false)
452                 $msg = str_replace("  ", " ", $msg);
453
454         $origmsg = trim($msg);
455
456         // Removing URLs
457         $msg = preg_replace('/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', "", $msg);
458
459         $msg = trim($msg);
460
461         $link = '';
462         // look for bookmark-bbcode and handle it with priority
463         if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches))
464                 $link = $matches[1];
465
466         $multiplelinks = (strpos($b['body'], "[bookmark") != strrpos($b['body'], "[bookmark"));
467
468         // If there is no bookmark element then take the first link
469         if ($link == '') {
470                 $links = collecturls($html);
471
472                 foreach($links AS $singlelink) {
473                         $img_str = fetch_url($singlelink);
474
475                         $tempfile = tempnam(get_config("system","temppath"), "cache");
476                         file_put_contents($tempfile, $img_str);
477                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
478                         unlink($tempfile);
479
480                         if (substr($mime, 0, 6) == "image/") {
481                                 $image = $singlelink;
482                                 unset($links[$singlelink]);
483                         }
484                 }
485
486                 if (sizeof($links) > 0) {
487                         reset($links);
488                         $link = current($links);
489                 }
490                 $multiplelinks = (sizeof($links) > 1);
491         }
492
493         $msglink = "";
494         if ($multiplelinks)
495                 $msglink = $b["plink"];
496         else if ($link != "")
497                 $msglink = $link;
498         else if ($multipleimages)
499                 $msglink = $b["plink"];
500         else if ($image != "")
501                 $msglink = $image;
502
503         if (($msglink == "") and strlen($msg) > $max_char)
504                 $msglink = $b["plink"];
505
506         // If the message is short enough then don't modify it.
507         if ((strlen($origmsg) <= $max_char) AND ($msglink == ""))
508                 return(array("msg"=>$origmsg, "image"=>""));
509
510         // If the message is short enough and contains a picture then post the picture as well
511         if ((strlen($origmsg) <= ($max_char - 23)) AND strpos($origmsg, $msglink))
512                 return(array("msg"=>$origmsg, "image"=>$image));
513
514         // If the message is short enough and the link exists in the original message don't modify it as well
515         // -3 because of the bad shortener of twitter
516         if ((strlen($origmsg) <= ($max_char - 3)) AND strpos($origmsg, $msglink))
517                 return(array("msg"=>$origmsg, "image"=>""));
518
519         // Preserve the unshortened link
520         $orig_link = $msglink;
521
522         // Just replace the message link with a 22 character long string
523         // Twitter calculates with this length
524         if (trim($msglink) <> '')
525                 $msglink = "1234567890123456789012";
526
527         if (strlen(trim($msg." ".$msglink)) > ($max_char)) {
528                 $msg = substr($msg, 0, ($max_char) - (strlen($msglink)));
529                 $lastchar = substr($msg, -1);
530                 $msg = substr($msg, 0, -1);
531                 $pos = strrpos($msg, "\n");
532                 if ($pos > 0)
533                         $msg = substr($msg, 0, $pos);
534                 else if ($lastchar != "\n")
535                         $msg = substr($msg, 0, -3)."...";
536
537                 // if the post contains a picture and a link then the system tries to cut the post earlier.
538                 // So the link and the picture can be posted.
539                 if (($image != "") AND ($orig_link != $image)) {
540                         $msg2 = substr($msg, 0, ($max_char - 20) - (strlen($msglink)));
541                         $lastchar = substr($msg2, -1);
542                         $msg2 = substr($msg2, 0, -1);
543                         $pos = strrpos($msg2, "\n");
544                         if ($pos > 0)
545                                 $msg = substr($msg2, 0, $pos);
546                         else if ($lastchar == "\n")
547                                 $msg = trim($msg2);
548                 }
549
550         }
551         // Removing multiple spaces - again
552         while (strpos($msg, "  ") !== false)
553                 $msg = str_replace("  ", " ", $msg);
554
555         $msg = trim($msg);
556
557         // Removing multiple newlines
558         //while (strpos($msg, "\n\n") !== false)
559         //      $msg = str_replace("\n\n", "\n", $msg);
560
561         // Looking if the link points to an image
562         $img_str = fetch_url($orig_link);
563
564         $tempfile = tempnam(get_config("system","temppath"), "cache");
565         file_put_contents($tempfile, $img_str);
566         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
567         unlink($tempfile);
568
569         if (($image == $orig_link) OR (substr($mime, 0, 6) == "image/"))
570                 return(array("msg"=>$msg, "image"=>$orig_link));
571         else if (($image != $orig_link) AND ($image != "") AND (strlen($msg." ".$msglink) <= ($max_char - 23))) {
572                 if ($shortlink)
573                         $orig_link = short_link($orig_link);
574
575                 return(array("msg"=>$msg." ".$orig_link, "image"=>$image));
576         } else {
577                 if ($shortlink)
578                         $orig_link = short_link($orig_link);
579
580                 return(array("msg"=>$msg." ".$orig_link, "image"=>""));
581         }
582 }
583
584 function twitter_action($a, $uid, $pid, $action) {
585
586         $ckey    = get_config('twitter', 'consumerkey');
587         $csecret = get_config('twitter', 'consumersecret');
588         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
589         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
590
591         require_once("addon/twitter/codebird.php");
592
593         $cb = \Codebird\Codebird::getInstance();
594         $cb->setConsumerKey($ckey, $csecret);
595         $cb->setToken($otoken, $osecret);
596
597         $post = array('id' => $pid);
598
599         logger("twitter_action '".$action."' ID: ".$pid." data: " . print_r($post, true), LOGGER_DATA);
600
601         switch ($action) {
602                 case "delete":
603                         $result = $cb->statuses_destroy($post);
604                         break;
605                 case "like":
606                         $result = $cb->favorites_create($post);
607                         break;
608                 case "unlike":
609                         $result = $cb->favorites_destroy($post);
610                         break;
611         }
612         logger("twitter_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
613 }
614
615 function twitter_post_hook(&$a,&$b) {
616
617         /**
618          * Post to Twitter
619          */
620
621         if (!get_pconfig($b["uid"],'twitter','import')) {
622                 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
623                         return;
624         }
625
626         if($b['parent'] != $b['id']) {
627                 logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
628
629                 // Looking if its a reply to a twitter post
630                 if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) {
631                         logger("twitter_post_hook: no twitter post ".$b["parent"]);
632                         return;
633                 }
634
635                 $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
636                         dbesc($b["thr-parent"]),
637                         intval($b["uid"]));
638
639                 if(!count($r)) {
640                         logger("twitter_post_hook: no parent found ".$b["thr-parent"]);
641                         return;
642                 } else {
643                         $iscomment = true;
644                         $orig_post = $r[0];
645                 }
646
647                 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
648                 $nickname = "@[url=".$orig_post["author-link"]."]".$nickname."[/url]";
649
650                 logger("twitter_post_hook: comparing ".$nickname." with ".$b["body"], LOGGER_DEBUG);
651                 if (strpos($b["body"], $nickname) === false)
652                         $b["body"] = $nickname." ".$b["body"];
653
654                 logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA);
655         } else {
656                 $iscomment = false;
657
658                 if($b['private'] OR !strstr($b['postopts'],'twitter'))
659                         return;
660         }
661
662         if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
663                 twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
664
665         if($b['verb'] == ACTIVITY_LIKE) {
666                 logger("twitter_post_hook: parameter 2 ".substr($b["thr-parent"], 9), LOGGER_DEBUG);
667                 if ($b['deleted'])
668                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
669                 else
670                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like");
671                 return;
672         }
673
674         if($b['deleted'] || ($b['created'] !== $b['edited']))
675                 return;
676
677         // if post comes from twitter don't send it back
678         if($b['app'] == "Twitter")
679                 return;
680
681         logger('twitter post invoked');
682
683
684         load_pconfig($b['uid'], 'twitter');
685
686         $ckey    = get_config('twitter', 'consumerkey');
687         $csecret = get_config('twitter', 'consumersecret');
688         $otoken  = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
689         $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
690         $intelligent_shortening = get_pconfig($b['uid'], 'twitter', 'intelligent_shortening');
691
692         // Global setting overrides this
693         if (get_config('twitter','intelligent_shortening'))
694                 $intelligent_shortening = get_config('twitter','intelligent_shortening');
695
696         if($ckey && $csecret && $otoken && $osecret) {
697                 logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
698
699                 // If it's a repeated message from twitter then do a native retweet and exit
700                 if (twitter_is_retweet($a, $b['uid'], $b['body']))
701                         return;
702
703                 require_once('library/twitteroauth.php');
704                 require_once('include/bbcode.php');
705                 $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
706
707                 // in theory max char is 140 but T. uses t.co to make links 
708                 // longer so we give them 10 characters extra
709                 if (!$intelligent_shortening) {
710                         $max_char = 130; // max. length for a tweet
711                         // we will only work with up to two times the length of the dent 
712                         // we can later send to Twitter. This way we can "gain" some 
713                         // information during shortening of potential links but do not 
714                         // shorten all the links in a 200000 character long essay.
715                         if (! $b['title']=='') {
716                             $tmp = $b['title'] . ' : '. $b['body'];
717         //                    $tmp = substr($tmp, 0, 4*$max_char);
718                         } else {
719                             $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char);
720                         }
721                         // if [url=bla][img]blub.png[/img][/url] get blub.png
722                         $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp);
723                         // preserve links to images, videos and audios
724                         $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp);
725                         $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp);
726                         $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp);
727                         $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp);
728                         $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp);
729                         $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp);
730                         $linksenabled = get_pconfig($b['uid'],'twitter','post_taglinks');
731                         // if a #tag is linked, don't send the [url] over to SN
732                         // that is, don't send if the option is not set in the
733                         // connector settings
734                         if ($linksenabled=='0') {
735                                 // #-tags
736                                 $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp);
737                                 // @-mentions
738                                 $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp);
739                                 // recycle 1
740                                 $recycle = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8');
741                                 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
742                                 // recycle 2 (Test)
743                                 $recycle = html_entity_decode("&#x25CC; ", ENT_QUOTES, 'UTF-8');
744                                 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
745                         }
746                         $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp);
747                         $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp);
748                         // find all http or https links in the body of the entry and
749                         // apply the shortener if the link is longer then 20 characters
750                         if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) {
751                             preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls  );
752                             foreach ($allurls as $url) {
753                                 foreach ($url as $u) {
754                                     if (strlen($u)>20) {
755                                         $sl = short_link($u);
756                                         $tmp = str_replace( $u, $sl, $tmp );
757                                     }
758                                 }
759                             }
760                         }
761                         // ok, all the links we want to send out are save, now strip 
762                         // away the remaining bbcode
763                         //$msg = strip_tags(bbcode($tmp, false, false));
764                         $msg = bbcode($tmp, false, false, true);
765                         $msg = str_replace(array('<br>','<br />'),"\n",$msg);
766                         $msg = strip_tags($msg);
767
768                         // quotes not working - let's try this
769                         $msg = html_entity_decode($msg);
770                         if (( strlen($msg) > $max_char) && $max_char > 0) {
771                                 $shortlink = short_link( $b['plink'] );
772                                 // the new message will be shortened such that "... $shortlink"
773                                 // will fit into the character limit
774                                 $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4));
775                                 $msg = str_replace(array('<br>','<br />'),' ',$msg);
776                                 $e = explode(' ', $msg);
777                                 //  remove the last word from the cut down message to 
778                                 //  avoid sending cut words to the MicroBlog
779                                 array_pop($e);
780                                 $msg = implode(' ', $e);
781                                 $msg .= '... ' . $shortlink;
782                         }
783
784                         $msg = trim($msg);
785                         $image = "";
786                 } else {
787                         $msgarr = twitter_shortenmsg($b);
788                         $msg = $msgarr["msg"];
789                         $image = $msgarr["image"];
790                 }
791
792                 // and now tweet it :-)
793                 if(strlen($msg) and ($image != "")) {
794                         $img_str = fetch_url($image);
795
796                         $tempfile = tempnam(get_config("system","temppath"), "cache");
797                         file_put_contents($tempfile, $img_str);
798
799                         // Twitter had changed something so that the old library doesn't work anymore
800                         // so we are using a new library for twitter
801                         // To-Do:
802                         // Switching completely to this library with all functions
803                         require_once("addon/twitter/codebird.php");
804
805                         $cb = \Codebird\Codebird::getInstance();
806                         $cb->setConsumerKey($ckey, $csecret);
807                         $cb->setToken($otoken, $osecret);
808
809                         $post = array('status' => $msg, 'media[]' => $tempfile);
810
811                         if ($iscomment)
812                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
813
814                         $result = $cb->statuses_updateWithMedia($post);
815                         unlink($tempfile);
816
817                         logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG);
818                         if ($result->errors OR $result->error) {
819                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
820
821                                 // Workaround: Remove the picture link so that the post can be reposted without it
822                                 $msg .= " ".$image;
823                                 $image = "";
824                         } elseif ($iscomment) {
825                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
826                                 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
827                                         dbesc("twitter::".$result->id_str),
828                                         dbesc($result->text),
829                                         intval($b['id'])
830                                 );
831                         }
832                 }
833
834                 if(strlen($msg) and ($image == "")) {
835                         $url = 'statuses/update';
836                         $post = array('status' => $msg);
837
838                         if ($iscomment)
839                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
840
841                         $result = $tweet->post($url, $post);
842                         logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
843                         if ($result->errors) {
844                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
845
846                                 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
847                                 if (count($r))
848                                         $a->contact = $r[0]["id"];
849
850                                 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
851                                 require_once('include/queue_fn.php');
852                                 add_to_queue($a->contact,NETWORK_TWITTER,$s);
853                                 notice(t('Twitter post failed. Queued for retry.').EOL);
854                         } elseif ($iscomment) {
855                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
856                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
857                                         dbesc("twitter::".$result->id_str),
858                                         intval($b['id'])
859                                 );
860                                 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
861                                 //      dbesc("twitter::".$result->id_str),
862                                 //      dbesc($result->text),
863                                 //      intval($b['id'])
864                                 //);
865                         }
866                 }
867         }
868 }
869
870 function twitter_plugin_admin_post(&$a){
871         $consumerkey    =       ((x($_POST,'consumerkey'))              ? notags(trim($_POST['consumerkey']))   : '');
872         $consumersecret =       ((x($_POST,'consumersecret'))   ? notags(trim($_POST['consumersecret'])): '');
873         $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
874         set_config('twitter','consumerkey',$consumerkey);
875         set_config('twitter','consumersecret',$consumersecret);
876         set_config('twitter','application_name',$applicationname);
877         info( t('Settings updated.'). EOL );
878 }
879 function twitter_plugin_admin(&$a, &$o){
880         $t = get_markup_template( "admin.tpl", "addon/twitter/" );
881
882         $o = replace_macros($t, array(
883                 '$submit' => t('Save Settings'),
884                                                                 // name, label, value, help, [extra values]
885                 '$consumerkey' => array('consumerkey', t('Consumer key'),  get_config('twitter', 'consumerkey' ), ''),
886                 '$consumersecret' => array('consumersecret', t('Consumer secret'),  get_config('twitter', 'consumersecret' ), ''),
887                 '$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'))
888         ));
889 }
890
891 function twitter_cron($a,$b) {
892         $last = get_config('twitter','last_poll');
893
894         $poll_interval = intval(get_config('twitter','poll_interval'));
895         if(! $poll_interval)
896                 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
897
898         if($last) {
899                 $next = $last + ($poll_interval * 60);
900                 if($next > time()) {
901                         logger('twitter: poll intervall not reached');
902                         return;
903                 }
904         }
905         logger('twitter: cron_start');
906
907         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND()");
908         if(count($r)) {
909                 foreach($r as $rr) {
910                         logger('twitter: fetching for user '.$rr['uid']);
911                         twitter_fetchtimeline($a, $rr['uid']);
912                 }
913         }
914
915
916         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
917         if(count($r)) {
918                 foreach($r as $rr) {
919                         logger('twitter: importing timeline from user '.$rr['uid']);
920                         twitter_fetchhometimeline($a, $rr["uid"]);
921
922 /*
923                         // To-Do
924                         // check for new contacts once a day
925                         $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
926                         if($last_contact_check)
927                                 $next_contact_check = $last_contact_check + 86400;
928                         else
929                                 $next_contact_check = 0;
930
931                         if($next_contact_check <= time()) {
932                                 pumpio_getallusers($a, $rr["uid"]);
933                                 set_pconfig($rr['uid'],'pumpio','contact_check',time());
934                         }
935 */
936
937                 }
938         }
939
940         logger('twitter: cron_end');
941
942         set_config('twitter','last_poll', time());
943 }
944
945 function twitter_fetchtimeline($a, $uid) {
946         $ckey    = get_config('twitter', 'consumerkey');
947         $csecret = get_config('twitter', 'consumersecret');
948         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
949         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
950         $lastid  = get_pconfig($uid, 'twitter', 'lastid');
951
952         $application_name  = get_config('twitter', 'application_name');
953
954         if ($application_name == "")
955                 $application_name = $a->get_hostname();
956
957         $has_picture = false;
958
959         require_once('mod/item.php');
960         require_once('include/items.php');
961
962         require_once('library/twitteroauth.php');
963         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
964
965         $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
966
967         $first_time = ($lastid == "");
968
969         if ($lastid <> "")
970                 $parameters["since_id"] = $lastid;
971
972         $items = $connection->get('statuses/user_timeline', $parameters);
973
974         if (!is_array($items))
975                 return;
976
977         $posts = array_reverse($items);
978
979         if (count($posts)) {
980             foreach ($posts as $post) {
981                 if ($post->id_str > $lastid)
982                         $lastid = $post->id_str;
983
984                 if ($first_time)
985                         continue;
986
987                 if (!strpos($post->source, $application_name)) {
988                         $_SESSION["authenticated"] = true;
989                         $_SESSION["uid"] = $uid;
990
991                         unset($_REQUEST);
992                         $_REQUEST["type"] = "wall";
993                         $_REQUEST["api_source"] = true;
994                         $_REQUEST["profile_uid"] = $uid;
995                         $_REQUEST["source"] = "Twitter";
996
997                         //$_REQUEST["date"] = $post->created_at;
998
999                         $_REQUEST["title"] = "";
1000
1001                         if (is_object($post->retweeted_status)) {
1002
1003                                 $_REQUEST['body'] = $post->retweeted_status->text;
1004
1005                                 // media
1006                                 if (is_array($post->retweeted_status->entities->media)) {
1007                                         foreach($post->retweeted_status->entities->media AS $media) {
1008                                                 switch($media->type) {
1009                                                         case 'photo':
1010                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1011                                                                 $has_picture = true;
1012                                                                 break;
1013                                                 }
1014                                         }
1015                                 }
1016
1017                                 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $has_picture);
1018                                 $_REQUEST['body'] = $converted["body"];
1019
1020                                 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
1021                                         "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1022                                         "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1023                                         "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1024                                         $_REQUEST['body'];
1025                                 $_REQUEST['body'] .= "[/share]";
1026                         } else {
1027                                 $_REQUEST["body"] = $post->text;
1028
1029                                 if (is_array($post->entities->media)) {
1030                                         foreach($post->entities->media AS $media) {
1031                                                 switch($media->type) {
1032                                                         case 'photo':
1033                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1034                                                                 $has_picture = true;
1035                                                                 break;
1036                                                 }
1037                                         }
1038                                 }
1039
1040                                 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $has_picture);
1041                                 $_REQUEST['body'] = $converted["body"];
1042                         }
1043
1044                         if (is_string($post->place->name))
1045                                 $_REQUEST["location"] = $post->place->name;
1046
1047                         if (is_string($post->place->full_name))
1048                                 $_REQUEST["location"] = $post->place->full_name;
1049
1050                         if (is_array($post->geo->coordinates))
1051                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1052
1053                         if (is_array($post->coordinates->coordinates))
1054                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1055
1056                         //print_r($_REQUEST);
1057                         logger('twitter: posting for user '.$uid);
1058
1059 //                      require_once('mod/item.php');
1060
1061                         item_post($a);
1062                 }
1063             }
1064         }
1065         set_pconfig($uid, 'twitter', 'lastid', $lastid);
1066 }
1067
1068 function twitter_queue_hook(&$a,&$b) {
1069
1070         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
1071                 dbesc(NETWORK_TWITTER)
1072                 );
1073         if(! count($qi))
1074                 return;
1075
1076         require_once('include/queue_fn.php');
1077
1078         foreach($qi as $x) {
1079                 if($x['network'] !== NETWORK_TWITTER)
1080                         continue;
1081
1082                 logger('twitter_queue: run');
1083
1084                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
1085                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1086                         intval($x['cid'])
1087                 );
1088                 if(! count($r))
1089                         continue;
1090
1091                 $user = $r[0];
1092
1093                 $ckey    = get_config('twitter', 'consumerkey');
1094                 $csecret = get_config('twitter', 'consumersecret');
1095                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
1096                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
1097
1098                 $success = false;
1099
1100                 if ($ckey AND $csecret AND $otoken AND $osecret) {
1101
1102                         logger('twitter_queue: able to post');
1103
1104                         $z = unserialize($x['content']);
1105
1106                         require_once("addon/twitter/codebird.php");
1107
1108                         $cb = \Codebird\Codebird::getInstance();
1109                         $cb->setConsumerKey($ckey, $csecret);
1110                         $cb->setToken($otoken, $osecret);
1111
1112                         if ($z['url'] == "statuses/update")
1113                                 $result = $cb->statuses_update($z['post']);
1114
1115                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
1116
1117                         if ($result->errors)
1118                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
1119                         else {
1120                                 $success = true;
1121                                 remove_queue_item($x['id']);
1122                         }
1123                 } else
1124                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
1125
1126                 if (!$success) {
1127                         logger('twitter_queue: delayed');
1128                         update_queue_time($x['id']);
1129                 }
1130         }
1131 }
1132
1133 function twitter_fetch_contact($uid, $contact, $create_user) {
1134
1135         // Check if the unique contact is existing
1136         // To-Do: only update once a while
1137          $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
1138                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1139
1140         if (count($r) == 0)
1141                 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
1142                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1143                         dbesc($contact->name),
1144                         dbesc($contact->screen_name),
1145                         dbesc($contact->profile_image_url_https));
1146         else
1147                 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
1148                         dbesc($contact->name),
1149                         dbesc($contact->screen_name),
1150                         dbesc($contact->profile_image_url_https),
1151                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1152
1153         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1154                 intval($uid), dbesc("twitter::".$contact->id_str));
1155
1156         if(!count($r) AND !$create_user)
1157                 return(0);
1158
1159         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
1160                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
1161                 return(-1);
1162         }
1163
1164         if(!count($r)) {
1165                 // create contact record
1166                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
1167                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
1168                                         `writable`, `blocked`, `readonly`, `pending` )
1169                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
1170                         intval($uid),
1171                         dbesc(datetime_convert()),
1172                         dbesc("https://twitter.com/".$contact->screen_name),
1173                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1174                         dbesc($contact->screen_name."@twitter.com"),
1175                         dbesc("twitter::".$contact->id_str),
1176                         dbesc(''),
1177                         dbesc("twitter::".$contact->id_str),
1178                         dbesc($contact->name),
1179                         dbesc($contact->screen_name),
1180                         dbesc($contact->profile_image_url_https),
1181                         dbesc(NETWORK_TWITTER),
1182                         intval(CONTACT_IS_FRIEND),
1183                         intval(1),
1184                         intval(1)
1185                 );
1186
1187                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
1188                         dbesc("twitter::".$contact->id_str),
1189                         intval($uid)
1190                         );
1191
1192                 if(! count($r))
1193                         return(false);
1194
1195                 $contact_id  = $r[0]['id'];
1196
1197                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
1198                         intval($uid)
1199                 );
1200
1201                 if($g && intval($g[0]['def_gid'])) {
1202                         require_once('include/group.php');
1203                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
1204                 }
1205
1206                 require_once("Photo.php");
1207
1208                 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
1209
1210                 q("UPDATE `contact` SET `photo` = '%s',
1211                                         `thumb` = '%s',
1212                                         `micro` = '%s',
1213                                         `name-date` = '%s',
1214                                         `uri-date` = '%s',
1215                                         `avatar-date` = '%s'
1216                                 WHERE `id` = %d",
1217                         dbesc($photos[0]),
1218                         dbesc($photos[1]),
1219                         dbesc($photos[2]),
1220                         dbesc(datetime_convert()),
1221                         dbesc(datetime_convert()),
1222                         dbesc(datetime_convert()),
1223                         intval($contact_id)
1224                 );
1225
1226         } else {
1227                 // update profile photos once every two weeks as we have no notification of when they change.
1228
1229                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
1230                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
1231
1232                 // check that we have all the photos, this has been known to fail on occasion
1233
1234                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
1235
1236                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
1237
1238                         require_once("Photo.php");
1239
1240                         $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
1241
1242                         q("UPDATE `contact` SET `photo` = '%s',
1243                                                 `thumb` = '%s',
1244                                                 `micro` = '%s',
1245                                                 `name-date` = '%s',
1246                                                 `uri-date` = '%s',
1247                                                 `avatar-date` = '%s',
1248                                                 `url` = '%s',
1249                                                 `nurl` = '%s',
1250                                                 `addr` = '%s',
1251                                                 `name` = '%s',
1252                                                 `nick` = '%s'
1253                                         WHERE `id` = %d",
1254                                 dbesc($photos[0]),
1255                                 dbesc($photos[1]),
1256                                 dbesc($photos[2]),
1257                                 dbesc(datetime_convert()),
1258                                 dbesc(datetime_convert()),
1259                                 dbesc(datetime_convert()),
1260                                 dbesc("https://twitter.com/".$contact->screen_name),
1261                                 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1262                                 dbesc($contact->screen_name."@twitter.com"),
1263                                 dbesc($contact->name),
1264                                 dbesc($contact->screen_name),
1265                                 intval($r[0]['id'])
1266                         );
1267                 }
1268         }
1269
1270         return($r[0]["id"]);
1271 }
1272
1273 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
1274         $ckey    = get_config('twitter', 'consumerkey');
1275         $csecret = get_config('twitter', 'consumersecret');
1276         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1277         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1278
1279         require_once("addon/twitter/codebird.php");
1280
1281         $cb = \Codebird\Codebird::getInstance();
1282         $cb->setConsumerKey($ckey, $csecret);
1283         $cb->setToken($otoken, $osecret);
1284
1285         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1286                 intval($uid));
1287
1288         if(count($r)) {
1289                 $self = $r[0];
1290         } else
1291                 return;
1292
1293         $parameters = array();
1294
1295         if ($screen_name != "")
1296                 $parameters["screen_name"] = $screen_name;
1297
1298         if ($user_id != "")
1299                 $parameters["user_id"] = $user_id;
1300
1301         // Fetching user data
1302         $user = $cb->users_show($parameters);
1303
1304         if (!is_object($user))
1305                 return;
1306
1307         $contact_id = twitter_fetch_contact($uid, $user, true);
1308
1309         return $contact_id;
1310 }
1311
1312 function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontincludemedia) {
1313         require_once("include/oembed.php");
1314         require_once("include/network.php");
1315
1316         $tags = "";
1317
1318         if (isset($item->entities->urls)) {
1319                 $type = "";
1320                 $footerurl = "";
1321                 $footerlink = "";
1322                 $footer = "";
1323
1324                 foreach ($item->entities->urls AS $url) {
1325                         if ($url->url AND $url->expanded_url AND $url->display_url) {
1326
1327                                 $expanded_url = original_url($url->expanded_url);
1328
1329                                 $oembed_data = oembed_fetch_url($expanded_url);
1330
1331                                 // Quickfix: Workaround for URL with "[" and "]" in it
1332                                 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1333                                         $expanded_url = $url->url;
1334
1335                                 if ($type == "")
1336                                         $type = $oembed_data->type;
1337
1338                                 if ($oembed_data->type == "video") {
1339                                         $body = str_replace($url->url,
1340                                                         "[video]".$expanded_url."[/video]", $body);
1341                                         $dontincludemedia = true;
1342                                 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1343                                         $body = str_replace($url->url,
1344                                                         "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1345                                                         $body);
1346                                         $dontincludemedia = true;
1347                                 } elseif ($oembed_data->type != "link")
1348                                         $body = str_replace($url->url,
1349                                                         "[url=".$expanded_url."]".$expanded_url."[/url]",
1350                                                         $body);
1351                                 else {
1352                                         $img_str = fetch_url($expanded_url, true, $redirects, 4);
1353
1354                                         $tempfile = tempnam(get_config("system","temppath"), "cache");
1355                                         file_put_contents($tempfile, $img_str);
1356                                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1357                                         unlink($tempfile);
1358
1359                                         if (substr($mime, 0, 6) == "image/") {
1360                                                 $type = "photo";
1361                                                 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1362                                                 $dontincludemedia = true;
1363                                         } else {
1364                                                 $type = $oembed_data->type;
1365                                                 $footerurl = $expanded_url;
1366                                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1367
1368                                                 $body = str_replace($url->url, $footerlink, $body);
1369                                         }
1370                                 }
1371                         }
1372                 }
1373
1374                 if ($footerurl != "")
1375                         $footer = add_page_info($footerurl);
1376
1377                 if (($footerlink != "") AND (trim($footer) != "")) {
1378                         $removedlink = trim(str_replace($footerlink, "", $body));
1379
1380                         if (strstr($body, $removedlink))
1381                                 $body = $removedlink;
1382
1383                         $body .= $footer;
1384                 }
1385
1386                 if ($no_tags)
1387                         return(array("body" => $body, "tags" => ""));
1388
1389                 $tags_arr = array();
1390
1391                 foreach ($item->entities->hashtags AS $hashtag) {
1392                         $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1393                         $tags_arr["#".$hashtag->text] = $url;
1394                         $body = str_replace("#".$hashtag->text, $url, $body);
1395                 }
1396
1397                 foreach ($item->entities->user_mentions AS $mention) {
1398                         $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1399                         $tags_arr["@".$mention->screen_name] = $url;
1400                         $body = str_replace("@".$mention->screen_name, $url, $body);
1401                 }
1402
1403                 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1404                 $tags = get_tags($body);
1405
1406                 if(count($tags)) {
1407                         foreach($tags as $tag) {
1408                                 if (strstr(trim($tag), " "))
1409                                         continue;
1410
1411                                 if(strpos($tag,'#') === 0) {
1412                                         if(strpos($tag,'[url='))
1413                                                 continue;
1414
1415                                         // don't link tags that are already embedded in links
1416
1417                                         if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1418                                                 continue;
1419                                         if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1420                                                 continue;
1421
1422                                         $basetag = str_replace('_',' ',substr($tag,1));
1423                                         $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1424                                         $body = str_replace($tag,$url,$body);
1425                                         $tags_arr["#".$basetag] = $url;
1426                                         continue;
1427                                 } elseif(strpos($tag,'@') === 0) {
1428                                         if(strpos($tag,'[url='))
1429                                                 continue;
1430
1431                                         $basetag = substr($tag,1);
1432                                         $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1433                                         $body = str_replace($tag,$url,$body);
1434                                         $tags_arr["@".$basetag] = $url;
1435                                 }
1436                         }
1437                 }
1438
1439
1440                 $tags = implode($tags_arr, ",");
1441
1442         }
1443         return(array("body" => $body, "tags" => $tags));
1444 }
1445
1446 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1447
1448         $has_picture = false;
1449
1450         $postarray = array();
1451         $postarray['network'] = NETWORK_TWITTER;
1452         $postarray['gravity'] = 0;
1453         $postarray['uid'] = $uid;
1454         $postarray['wall'] = 0;
1455         $postarray['uri'] = "twitter::".$post->id_str;
1456
1457         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1458                         dbesc($postarray['uri']),
1459                         intval($uid)
1460                 );
1461
1462         if (count($r))
1463                 return(array());
1464
1465         $contactid = 0;
1466
1467         if ($post->in_reply_to_status_id_str != "") {
1468
1469                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1470
1471                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1472                                 dbesc($parent),
1473                                 intval($uid)
1474                         );
1475                 if (count($r)) {
1476                         $postarray['thr-parent'] = $r[0]["uri"];
1477                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1478                 } else {
1479                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1480                                         dbesc($parent),
1481                                         intval($uid)
1482                                 );
1483                         if (count($r)) {
1484                                 $postarray['thr-parent'] = $r[0]['uri'];
1485                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1486                         } else {
1487                                 $postarray['thr-parent'] = $postarray['uri'];
1488                                 $postarray['parent-uri'] = $postarray['uri'];
1489                         }
1490                 }
1491
1492                 // Is it me?
1493                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1494
1495                 if ($post->user->id_str == $own_id) {
1496                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1497                                 intval($uid));
1498
1499                         if(count($r)) {
1500                                 $contactid = $r[0]["id"];
1501
1502                                 $postarray['owner-name'] =  $r[0]["name"];
1503                                 $postarray['owner-link'] = $r[0]["url"];
1504                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1505                         } else
1506                                 return(array());
1507                 }
1508         } else
1509                 $postarray['parent-uri'] = $postarray['uri'];
1510
1511         if ($contactid == 0) {
1512                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1513
1514                 $postarray['owner-name'] = $post->user->name;
1515                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1516                 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1517         }
1518
1519         if(($contactid == 0) AND !$only_existing_contact)
1520                 $contactid = $self['id'];
1521         elseif ($contactid <= 0)
1522                 return(array());
1523
1524         $postarray['contact-id'] = $contactid;
1525
1526         $postarray['verb'] = ACTIVITY_POST;
1527         $postarray['author-name'] = $postarray['owner-name'];
1528         $postarray['author-link'] = $postarray['owner-link'];
1529         $postarray['author-avatar'] = $postarray['owner-avatar'];
1530         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1531         $postarray['app'] = strip_tags($post->source);
1532
1533         if ($post->user->protected) {
1534                 $postarray['private'] = 1;
1535                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1536         }
1537
1538         $postarray['body'] = $post->text;
1539
1540         // media
1541         if (is_array($post->entities->media)) {
1542                 foreach($post->entities->media AS $media) {
1543                         switch($media->type) {
1544                                 case 'photo':
1545                                         $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1546                                         $has_picture = true;
1547                                         break;
1548                                 default:
1549                                         $postarray['body'] .= print_r($media, true);
1550                         }
1551                 }
1552         }
1553
1554         $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $has_picture);
1555         $postarray['body'] = $converted["body"];
1556         $postarray['tag'] = $converted["tags"];
1557
1558         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1559         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1560
1561         if (is_string($post->place->name))
1562                 $postarray["location"] = $post->place->name;
1563
1564         if (is_string($post->place->full_name))
1565                 $postarray["location"] = $post->place->full_name;
1566
1567         if (is_array($post->geo->coordinates))
1568                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1569
1570         if (is_array($post->coordinates->coordinates))
1571                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1572
1573         if (is_object($post->retweeted_status)) {
1574
1575                 $postarray['body'] = $post->retweeted_status->text;
1576
1577                 // media
1578                 if (is_array($post->retweeted_status->entities->media)) {
1579                         foreach($post->retweeted_status->entities->media AS $media) {
1580                                 switch($media->type) {
1581                                         case 'photo':
1582                                                 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1583                                                 $has_picture = true;
1584                                                 break;
1585                                         default:
1586                                                 $postarray['body'] .= print_r($media, true);
1587                                 }
1588                         }
1589                 }
1590
1591                 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $has_picture);
1592                 $postarray['body'] = $converted["body"];
1593                 $postarray['tag'] = $converted["tags"];
1594
1595                 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1596
1597                 // Deactivated at the moment, since there are problems with answers to retweets
1598                 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1599                         $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1600                                 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1601                                 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1602                                 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1603                                 $postarray['body'];
1604                         $postarray['body'] .= "[/share]";
1605                 } else {
1606                         // Let retweets look like wall-to-wall posts
1607                         $postarray['author-name'] = $post->retweeted_status->user->name;
1608                         $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1609                         $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1610                         //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1611                         //      $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1612                         //      $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1613                         //}
1614                 }
1615
1616         }
1617         return($postarray);
1618 }
1619
1620 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1621
1622         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1623                         intval($uid)
1624                 );
1625
1626         if(!count($user))
1627                 return;
1628
1629         // Is it me?
1630         if (link_compare($user[0]["url"], $postarray['author-link']))
1631                 return;
1632
1633         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1634                         intval($uid),
1635                         dbesc("twitter::".$own_id)
1636                 );
1637
1638         if(!count($own_user))
1639                 return;
1640
1641         // Is it me from twitter?
1642         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1643                 return;
1644
1645         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1646                         dbesc($postarray['parent-uri']),
1647                         intval($uid)
1648                         );
1649
1650         if(count($myconv)) {
1651
1652                 foreach($myconv as $conv) {
1653                         // now if we find a match, it means we're in this conversation
1654
1655                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1656                                 continue;
1657
1658                         require_once('include/enotify.php');
1659
1660                         $conv_parent = $conv['parent'];
1661
1662                         notification(array(
1663                                 'type'         => NOTIFY_COMMENT,
1664                                 'notify_flags' => $user[0]['notify-flags'],
1665                                 'language'     => $user[0]['language'],
1666                                 'to_name'      => $user[0]['username'],
1667                                 'to_email'     => $user[0]['email'],
1668                                 'uid'          => $user[0]['uid'],
1669                                 'item'         => $postarray,
1670                                 'link'             => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1671                                 'source_name'  => $postarray['author-name'],
1672                                 'source_link'  => $postarray['author-link'],
1673                                 'source_photo' => $postarray['author-avatar'],
1674                                 'verb'         => ACTIVITY_POST,
1675                                 'otype'        => 'item',
1676                                 'parent'       => $conv_parent,
1677                         ));
1678
1679                         // only send one notification
1680                         break;
1681                 }
1682         }
1683 }
1684
1685 function twitter_fetchhometimeline($a, $uid) {
1686         $ckey    = get_config('twitter', 'consumerkey');
1687         $csecret = get_config('twitter', 'consumersecret');
1688         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1689         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1690         $create_user = get_pconfig($uid, 'twitter', 'create_user');
1691
1692         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1693
1694         require_once('library/twitteroauth.php');
1695         require_once('include/items.php');
1696
1697         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1698
1699         $own_contact = twitter_fetch_own_contact($a, $uid);
1700
1701         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1702                 intval($own_contact),
1703                 intval($uid));
1704
1705         if(count($r)) {
1706                 $own_id = $r[0]["nick"];
1707         } else {
1708                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1709                 return;
1710         }
1711
1712         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1713                 intval($uid));
1714
1715         if(count($r)) {
1716                 $self = $r[0];
1717         } else {
1718                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1719                 return;
1720         }
1721
1722         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1723                 intval($uid));
1724         if(!count($u)) {
1725                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1726                 return;
1727         }
1728
1729         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1730         //$parameters["count"] = 200;
1731
1732
1733         // Fetching timeline
1734         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1735
1736         $first_time = ($lastid == "");
1737
1738         if ($lastid <> "")
1739                 $parameters["since_id"] = $lastid;
1740
1741         $items = $connection->get('statuses/home_timeline', $parameters);
1742
1743         if (!is_array($items)) {
1744                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1745                 return;
1746         }
1747
1748         $posts = array_reverse($items);
1749
1750         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1751
1752         if (count($posts)) {
1753                 foreach ($posts as $post) {
1754                         if ($post->id_str > $lastid)
1755                                 $lastid = $post->id_str;
1756
1757                         if ($first_time)
1758                                 continue;
1759
1760                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1761
1762                         if (trim($postarray['body']) == "")
1763                                 continue;
1764
1765                         $item = item_store($postarray);
1766
1767                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1768
1769                         if ($item != 0)
1770                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1771
1772                 }
1773         }
1774         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1775
1776         // Fetching mentions
1777         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1778
1779         $first_time = ($lastid == "");
1780
1781         if ($lastid <> "")
1782                 $parameters["since_id"] = $lastid;
1783
1784         $items = $connection->get('statuses/mentions_timeline', $parameters);
1785
1786         if (!is_array($items)) {
1787                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1788                 return;
1789         }
1790
1791         $posts = array_reverse($items);
1792
1793         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1794
1795         if (count($posts)) {
1796                 foreach ($posts as $post) {
1797                         if ($post->id_str > $lastid)
1798                                 $lastid = $post->id_str;
1799
1800                         if ($first_time)
1801                                 continue;
1802
1803                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1804
1805                         if (trim($postarray['body']) == "")
1806                                 continue;
1807
1808                         $item = item_store($postarray);
1809
1810                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1811
1812                         if ($item == 0) {
1813                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1814                                         dbesc($postarray['uri']),
1815                                         intval($uid)
1816                                 );
1817                                 if (count($r))
1818                                         $item = $r[0]['id'];
1819                         }
1820
1821                         if ($item != 0) {
1822                                 require_once('include/enotify.php');
1823                                 notification(array(
1824                                         'type'         => NOTIFY_TAGSELF,
1825                                         'notify_flags' => $u[0]['notify-flags'],
1826                                         'language'     => $u[0]['language'],
1827                                         'to_name'      => $u[0]['username'],
1828                                         'to_email'     => $u[0]['email'],
1829                                         'uid'          => $u[0]['uid'],
1830                                         'item'         => $postarray,
1831                                         'link'         => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1832                                         'source_name'  => $postarray['author-name'],
1833                                         'source_link'  => $postarray['author-link'],
1834                                         'source_photo' => $postarray['author-avatar'],
1835                                         'verb'         => ACTIVITY_TAG,
1836                                         'otype'        => 'item'
1837                                 ));
1838                         }
1839                 }
1840         }
1841
1842         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1843 }
1844
1845 function twitter_fetch_own_contact($a, $uid) {
1846         $ckey    = get_config('twitter', 'consumerkey');
1847         $csecret = get_config('twitter', 'consumersecret');
1848         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1849         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1850
1851         $own_id = get_pconfig($uid, 'twitter', 'own_id');
1852
1853         $contact_id = 0;
1854
1855         if ($own_id == "") {
1856                 require_once('library/twitteroauth.php');
1857
1858                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1859
1860                 // Fetching user data
1861                 $user = $connection->get('account/verify_credentials');
1862
1863                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1864
1865                 $contact_id = twitter_fetch_contact($uid, $user, true);
1866
1867         } else {
1868                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1869                         intval($uid), dbesc("twitter::".$own_id));
1870                 if(count($r))
1871                         $contact_id = $r[0]["id"];
1872                 else
1873                         del_pconfig($uid, 'twitter', 'own_id');
1874
1875         }
1876
1877         return($contact_id);
1878 }
1879
1880 function twitter_is_retweet($a, $uid, $body) {
1881         $body = trim($body);
1882
1883         // Skip if it isn't a pure repeated messages
1884         // Does it start with a share?
1885         if (strpos($body, "[share") > 0)
1886                 return(false);
1887
1888         // Does it end with a share?
1889         if (strlen($body) > (strrpos($body, "[/share]") + 8))
1890                 return(false);
1891
1892         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1893         // Skip if there is no shared message in there
1894         if ($body == $attributes)
1895                 return(false);
1896
1897         $link = "";
1898         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1899         if ($matches[1] != "")
1900                 $link = $matches[1];
1901
1902         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1903         if ($matches[1] != "")
1904                 $link = $matches[1];
1905
1906         $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1907         if ($id == $link)
1908                 return(false);
1909
1910         logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1911
1912         $ckey    = get_config('twitter', 'consumerkey');
1913         $csecret = get_config('twitter', 'consumersecret');
1914         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1915         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1916
1917         require_once('library/twitteroauth.php');
1918         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1919
1920         $result = $connection->post('statuses/retweet/'.$id);
1921
1922         logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1923
1924         return(!isset($result->errors));
1925 }
1926
1927 ?>