]> git.mxchange.org Git - friendica-addons.git/blob - twitter/twitter.php
d2dc4fd69c92551c78b79277d67915b07668faa3
[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         register_hook('expire', 'addon/twitter/twitter.php', 'twitter_expire');
76         logger("installed twitter");
77 }
78
79
80 function twitter_uninstall() {
81         unregister_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings');
82         unregister_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
83         unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
84         unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
85         unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
86         unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
87         unregister_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
88         unregister_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
89         unregister_hook('expire', 'addon/twitter/twitter.php', 'twitter_expire');
90
91         // old setting - remove only
92         unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook');
93         unregister_hook('plugin_settings', 'addon/twitter/twitter.php', 'twitter_settings');
94         unregister_hook('plugin_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
95
96 }
97
98 function twitter_follow($a, &$contact) {
99
100         logger("twitter_follow: Check if contact is twitter contact. ".$contact["url"], LOGGER_DEBUG);
101
102         if (!strstr($contact["url"], "://twitter.com") AND !strstr($contact["url"], "@twitter.com"))
103                 return;
104
105         // contact seems to be a twitter contact, so continue
106         $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $contact["url"]);
107         $nickname = str_replace("@twitter.com", "", $nickname);
108
109         $uid = $a->user["uid"];
110
111         $ckey    = get_config('twitter', 'consumerkey');
112         $csecret = get_config('twitter', 'consumersecret');
113         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
114         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
115
116         require_once("addon/twitter/codebird.php");
117
118         $cb = \Codebird\Codebird::getInstance();
119         $cb->setConsumerKey($ckey, $csecret);
120         $cb->setToken($otoken, $osecret);
121
122         $parameters = array();
123         $parameters["screen_name"] = $nickname;
124
125         $user = $cb->friendships_create($parameters);
126
127         twitter_fetchuser($a, $uid, $nickname);
128
129         $r = q("SELECT name,nick,url,addr,batch,notify,poll,request,confirm,poco,photo,priority,network,alias,pubkey
130                 FROM `contact` WHERE `uid` = %d AND `nick` = '%s'",
131                                 intval($uid),
132                                 dbesc($nickname));
133         if (count($r))
134                 $contact["contact"] = $r[0];
135 }
136
137 function twitter_jot_nets(&$a,&$b) {
138         if(! local_user())
139                 return;
140
141         $tw_post = get_pconfig(local_user(),'twitter','post');
142         if(intval($tw_post) == 1) {
143                 $tw_defpost = get_pconfig(local_user(),'twitter','post_by_default');
144                 $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : '');
145                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="twitter_enable"' . $selected . ' value="1" /> ' 
146                         . t('Post to Twitter') . '</div>';
147         }
148 }
149
150 function twitter_settings_post ($a,$post) {
151         if(! local_user())
152                 return;
153         // don't check twitter settings if twitter submit button is not clicked
154         if (!x($_POST,'twitter-submit'))
155                 return;
156
157         if (isset($_POST['twitter-disconnect'])) {
158                 /***
159                  * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
160                  * from the user configuration
161                  */
162                 del_pconfig(local_user(), 'twitter', 'consumerkey');
163                 del_pconfig(local_user(), 'twitter', 'consumersecret');
164                 del_pconfig(local_user(), 'twitter', 'oauthtoken');
165                 del_pconfig(local_user(), 'twitter', 'oauthsecret');
166                 del_pconfig(local_user(), 'twitter', 'post');
167                 del_pconfig(local_user(), 'twitter', 'post_by_default');
168                 del_pconfig(local_user(), 'twitter', 'lastid');
169                 del_pconfig(local_user(), 'twitter', 'mirror_posts');
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                 //  reload the Addon Settings page, if we don't do it see Bug #42
190                 goaway($a->get_baseurl().'/settings/connectors');
191         } else {
192                 //  if no PIN is supplied in the POST variables, the user has changed the setting
193                 //  to post a tweet for every new __public__ posting to the wall
194                 set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable']));
195                 set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default']));
196                 set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
197                 set_pconfig(local_user(), 'twitter', 'import', intval($_POST['twitter-import']));
198                 set_pconfig(local_user(), 'twitter', 'create_user', intval($_POST['twitter-create_user']));
199
200                 if (!intval($_POST['twitter-mirror']))
201                         del_pconfig(local_user(),'twitter','lastid');
202
203                 info(t('Twitter settings updated.') . EOL);
204         }}
205 }
206 function twitter_settings(&$a,&$s) {
207         if(! local_user())
208                 return;
209         $a->page['htmlhead'] .= '<link rel="stylesheet"  type="text/css" href="' . $a->get_baseurl() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
210         /***
211          * 1) Check that we have global consumer key & secret
212          * 2) If no OAuthtoken & stuff is present, generate button to get some
213          * 3) Checkbox for "Send public notices (140 chars only)
214          */
215         $ckey    = get_config('twitter', 'consumerkey' );
216         $csecret = get_config('twitter', 'consumersecret' );
217         $otoken  = get_pconfig(local_user(), 'twitter', 'oauthtoken'  );
218         $osecret = get_pconfig(local_user(), 'twitter', 'oauthsecret' );
219         $enabled = get_pconfig(local_user(), 'twitter', 'post');
220         $checked = (($enabled) ? ' checked="checked" ' : '');
221         $defenabled = get_pconfig(local_user(),'twitter','post_by_default');
222         $defchecked = (($defenabled) ? ' checked="checked" ' : '');
223         $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts');
224         $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : '');
225         $importenabled = get_pconfig(local_user(),'twitter','import');
226         $importchecked = (($importenabled) ? ' checked="checked" ' : '');
227         $create_userenabled = get_pconfig(local_user(),'twitter','create_user');
228         $create_userchecked = (($create_userenabled) ? ' checked="checked" ' : '');
229
230         $css = (($enabled) ? '' : '-disabled');
231
232         $s .= '<span id="settings_twitter_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
233         $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
234         $s .= '</span>';
235         $s .= '<div id="settings_twitter_expanded" class="settings-block" style="display: none;">';
236         $s .= '<span class="fakelink" onclick="openClose(\'settings_twitter_expanded\'); openClose(\'settings_twitter_inflated\');">';
237         $s .= '<img class="connector'.$css.'" src="images/twitter.png" /><h3 class="connector">'. t('Twitter Import/Export/Mirror').'</h3>';
238         $s .= '</span>';
239
240         if ( (!$ckey) && (!$csecret) ) {
241                 /***
242                  * no global consumer keys
243                  * display warning and skip personal config
244                  */
245                 $s .= '<p>'. t('No consumer key pair for Twitter found. Please contact your site administrator.') .'</p>';
246         } else {
247                 /***
248                  * ok we have a consumer key pair now look into the OAuth stuff
249                  */
250                 if ( (!$otoken) && (!$osecret) ) {
251                         /***
252                          * the user has not yet connected the account to twitter...
253                          * get a temporary OAuth key/secret pair and display a button with
254                          * which the user can request a PIN to connect the account to a
255                          * account at Twitter.
256                          */
257                         require_once('library/twitteroauth.php');
258                         $connection = new TwitterOAuth($ckey, $csecret);
259                         $request_token = $connection->getRequestToken();
260                         $token = $request_token['oauth_token'];
261                         /***
262                          *  make some nice form
263                          */
264                         $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>';
265                         $s .= '<a href="'.$connection->getAuthorizeURL($token).'" target="_twitter"><img src="addon/twitter/lighter.png" alt="'.t('Log in with Twitter').'"></a>';
266                         $s .= '<div id="twitter-pin-wrapper">';
267                         $s .= '<label id="twitter-pin-label" for="twitter-pin">'. t('Copy the PIN from Twitter here') .'</label>';
268                         $s .= '<input id="twitter-pin" type="text" name="twitter-pin" />';
269                         $s .= '<input id="twitter-token" type="hidden" name="twitter-token" value="'.$token.'" />';
270                         $s .= '<input id="twitter-token2" type="hidden" name="twitter-token2" value="'.$request_token['oauth_token_secret'].'" />';
271             $s .= '</div><div class="clear"></div>';
272             $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>';
273                 } else {
274                         /***
275                          *  we have an OAuth key / secret pair for the user
276                          *  so let's give a chance to disable the postings to Twitter
277                          */
278                         require_once('library/twitteroauth.php');
279                         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
280                         $details = $connection->get('account/verify_credentials');
281                         $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>';
282                         $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>';
283                         if ($a->user['hidewall']) {
284                             $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>';
285                         }
286                         $s .= '<div id="twitter-enable-wrapper">';
287                         $s .= '<label id="twitter-enable-label" for="twitter-checkbox">'. t('Allow posting to Twitter'). '</label>';
288                         $s .= '<input id="twitter-checkbox" type="checkbox" name="twitter-enable" value="1" ' . $checked . '/>';
289                         $s .= '<div class="clear"></div>';
290                         $s .= '<label id="twitter-default-label" for="twitter-default">'. t('Send public postings to Twitter by default') .'</label>';
291                         $s .= '<input id="twitter-default" type="checkbox" name="twitter-default" value="1" ' . $defchecked . '/>';
292                         $s .= '<div class="clear"></div>';
293
294                         $s .= '<label id="twitter-mirror-label" for="twitter-mirror">'.t('Mirror all posts from twitter that are no replies').'</label>';
295                         $s .= '<input id="twitter-mirror" type="checkbox" name="twitter-mirror" value="1" '. $mirrorchecked . '/>';
296                         $s .= '<div class="clear"></div>';
297                         $s .= '</div>';
298
299                         $s .= '<label id="twitter-import-label" for="twitter-import">'.t('Import the remote timeline').'</label>';
300                         $s .= '<input id="twitter-import" type="checkbox" name="twitter-import" value="1" '. $importchecked . '/>';
301                         $s .= '<div class="clear"></div>';
302
303                         $s .= '<label id="twitter-create_user-label" for="twitter-create_user">'.t('Automatically create contacts').'</label>';
304                         $s .= '<input id="twitter-create_user" type="checkbox" name="twitter-create_user" value="1" '. $create_userchecked . '/>';
305                         $s .= '<div class="clear"></div>';
306
307                         $s .= '<div id="twitter-disconnect-wrapper">';
308                         $s .= '<label id="twitter-disconnect-label" for="twitter-disconnect">'. t('Clear OAuth configuration') .'</label>';
309                         $s .= '<input id="twitter-disconnect" type="checkbox" name="twitter-disconnect" value="1" />';
310                         $s .= '</div><div class="clear"></div>';
311                         $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Save Settings') . '" /></div>'; 
312                 }
313         }
314         $s .= '</div><div class="clear"></div>';
315 }
316
317
318 function twitter_post_local(&$a,&$b) {
319
320         if($b['edit'])
321                 return;
322
323         if((local_user()) && (local_user() == $b['uid']) && (! $b['private']) && (! $b['parent']) ) {
324
325                 $twitter_post = intval(get_pconfig(local_user(),'twitter','post'));
326                 $twitter_enable = (($twitter_post && x($_REQUEST,'twitter_enable')) ? intval($_REQUEST['twitter_enable']) : 0);
327
328                 // if API is used, default to the chosen settings
329                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'twitter','post_by_default')))
330                         $twitter_enable = 1;
331
332         if(! $twitter_enable)
333                 return;
334
335         if(strlen($b['postopts']))
336                 $b['postopts'] .= ',';
337                 $b['postopts'] .= 'twitter';
338         }
339 }
340
341 function twitter_action($a, $uid, $pid, $action) {
342
343         $ckey    = get_config('twitter', 'consumerkey');
344         $csecret = get_config('twitter', 'consumersecret');
345         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
346         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
347
348         require_once("addon/twitter/codebird.php");
349
350         $cb = \Codebird\Codebird::getInstance();
351         $cb->setConsumerKey($ckey, $csecret);
352         $cb->setToken($otoken, $osecret);
353
354         $post = array('id' => $pid);
355
356         logger("twitter_action '".$action."' ID: ".$pid." data: " . print_r($post, true), LOGGER_DATA);
357
358         switch ($action) {
359                 case "delete":
360                         $result = $cb->statuses_destroy($post);
361                         break;
362                 case "like":
363                         $result = $cb->favorites_create($post);
364                         break;
365                 case "unlike":
366                         $result = $cb->favorites_destroy($post);
367                         break;
368         }
369         logger("twitter_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
370 }
371
372 function twitter_post_hook(&$a,&$b) {
373
374         /**
375          * Post to Twitter
376          */
377
378         require_once("include/network.php");
379
380         if (!get_pconfig($b["uid"],'twitter','import')) {
381                 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
382                         return;
383         }
384
385         if($b['parent'] != $b['id']) {
386                 logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
387
388                 // Looking if its a reply to a twitter post
389                 if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) {
390                         logger("twitter_post_hook: no twitter post ".$b["parent"]);
391                         return;
392                 }
393
394                 $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
395                         dbesc($b["thr-parent"]),
396                         intval($b["uid"]));
397
398                 if(!count($r)) {
399                         logger("twitter_post_hook: no parent found ".$b["thr-parent"]);
400                         return;
401                 } else {
402                         $iscomment = true;
403                         $orig_post = $r[0];
404                 }
405
406
407                 $nicknameplain = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
408                 $nickname = "@[url=".$orig_post["author-link"]."]".$nicknameplain."[/url]";
409                 $nicknameplain = "@".$nicknameplain;
410
411                 logger("twitter_post_hook: comparing ".$nickname." and ".$nicknameplain." with ".$b["body"], LOGGER_DEBUG);
412                 if ((strpos($b["body"], $nickname) === false) AND (strpos($b["body"], $nicknameplain) === false))
413                         $b["body"] = $nickname." ".$b["body"];
414
415                 logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA);
416         } else {
417                 $iscomment = false;
418
419                 if($b['private'] OR !strstr($b['postopts'],'twitter'))
420                         return;
421         }
422
423         if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
424                 twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
425
426         if($b['verb'] == ACTIVITY_LIKE) {
427                 logger("twitter_post_hook: parameter 2 ".substr($b["thr-parent"], 9), LOGGER_DEBUG);
428                 if ($b['deleted'])
429                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
430                 else
431                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like");
432                 return;
433         }
434
435         if($b['deleted'] || ($b['created'] !== $b['edited']))
436                 return;
437
438         // if post comes from twitter don't send it back
439         if($b['app'] == "Twitter")
440                 return;
441
442         logger('twitter post invoked');
443
444
445         load_pconfig($b['uid'], 'twitter');
446
447         $ckey    = get_config('twitter', 'consumerkey');
448         $csecret = get_config('twitter', 'consumersecret');
449         $otoken  = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
450         $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
451
452         if($ckey && $csecret && $otoken && $osecret) {
453                 logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
454
455                 // If it's a repeated message from twitter then do a native retweet and exit
456                 if (twitter_is_retweet($a, $b['uid'], $b['body']))
457                         return;
458
459                 require_once('library/twitteroauth.php');
460                 require_once('include/bbcode.php');
461                 $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
462
463                 $max_char = 140;
464                 require_once("include/plaintext.php");
465                 $msgarr = plaintext($a, $b, $max_char, true);
466                 $msg = $msgarr["text"];
467
468                 if (($msg == "") AND isset($msgarr["title"]))
469                         $msg = shortenmsg($msgarr["title"], $max_char - 50);
470
471                 $image = "";
472
473                 if (isset($msgarr["url"]))
474                         $msg .= "\n".$msgarr["url"];
475                 elseif (isset($msgarr["image"]))
476                         $image = $msgarr["image"];
477
478                 // and now tweet it :-)
479                 if(strlen($msg) and ($image != "")) {
480                         $img_str = fetch_url($image);
481
482                         $tempfile = tempnam(get_temppath(), "cache");
483                         file_put_contents($tempfile, $img_str);
484
485                         // Twitter had changed something so that the old library doesn't work anymore
486                         // so we are using a new library for twitter
487                         // To-Do:
488                         // Switching completely to this library with all functions
489                         require_once("addon/twitter/codebird.php");
490
491                         $cb = \Codebird\Codebird::getInstance();
492                         $cb->setConsumerKey($ckey, $csecret);
493                         $cb->setToken($otoken, $osecret);
494
495                         $post = array('status' => $msg, 'media[]' => $tempfile);
496
497                         if ($iscomment)
498                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
499
500                         $result = $cb->statuses_updateWithMedia($post);
501                         unlink($tempfile);
502
503                         logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG);
504                         if ($result->errors OR $result->error) {
505                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
506
507                                 // Workaround: Remove the picture link so that the post can be reposted without it
508                                 $msg .= " ".$image;
509                                 $image = "";
510                         } elseif ($iscomment) {
511                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
512                                 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
513                                         dbesc("twitter::".$result->id_str),
514                                         dbesc($result->text),
515                                         intval($b['id'])
516                                 );
517                         }
518                 }
519
520                 if(strlen($msg) and ($image == "")) {
521                         $url = 'statuses/update';
522                         $post = array('status' => $msg);
523
524                         if ($iscomment)
525                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
526
527                         $result = $tweet->post($url, $post);
528                         logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
529                         if ($result->errors) {
530                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
531
532                                 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
533                                 if (count($r))
534                                         $a->contact = $r[0]["id"];
535
536                                 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
537                                 require_once('include/queue_fn.php');
538                                 add_to_queue($a->contact,NETWORK_TWITTER,$s);
539                                 notice(t('Twitter post failed. Queued for retry.').EOL);
540                         } elseif ($iscomment) {
541                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
542                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
543                                         dbesc("twitter::".$result->id_str),
544                                         intval($b['id'])
545                                 );
546                                 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
547                                 //      dbesc("twitter::".$result->id_str),
548                                 //      dbesc($result->text),
549                                 //      intval($b['id'])
550                                 //);
551                         }
552                 }
553         }
554 }
555
556 function twitter_plugin_admin_post(&$a){
557         $consumerkey    =       ((x($_POST,'consumerkey'))              ? notags(trim($_POST['consumerkey']))   : '');
558         $consumersecret =       ((x($_POST,'consumersecret'))   ? notags(trim($_POST['consumersecret'])): '');
559         $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
560         set_config('twitter','consumerkey',$consumerkey);
561         set_config('twitter','consumersecret',$consumersecret);
562         set_config('twitter','application_name',$applicationname);
563         info( t('Settings updated.'). EOL );
564 }
565 function twitter_plugin_admin(&$a, &$o){
566         $t = get_markup_template( "admin.tpl", "addon/twitter/" );
567
568         $o = replace_macros($t, array(
569                 '$submit' => t('Save Settings'),
570                                                                 // name, label, value, help, [extra values]
571                 '$consumerkey' => array('consumerkey', t('Consumer key'),  get_config('twitter', 'consumerkey' ), ''),
572                 '$consumersecret' => array('consumersecret', t('Consumer secret'),  get_config('twitter', 'consumersecret' ), ''),
573                 '$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'))
574         ));
575 }
576
577 function twitter_cron($a,$b) {
578         $last = get_config('twitter','last_poll');
579
580         $poll_interval = intval(get_config('twitter','poll_interval'));
581         if(! $poll_interval)
582                 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
583
584         if($last) {
585                 $next = $last + ($poll_interval * 60);
586                 if($next > time()) {
587                         logger('twitter: poll intervall not reached');
588                         return;
589                 }
590         }
591         logger('twitter: cron_start');
592
593         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND()");
594         if(count($r)) {
595                 foreach($r as $rr) {
596                         logger('twitter: fetching for user '.$rr['uid']);
597                         twitter_fetchtimeline($a, $rr['uid']);
598                 }
599         }
600
601
602         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
603         if(count($r)) {
604                 foreach($r as $rr) {
605                         logger('twitter: importing timeline from user '.$rr['uid']);
606                         twitter_fetchhometimeline($a, $rr["uid"]);
607
608 /*
609                         // To-Do
610                         // check for new contacts once a day
611                         $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
612                         if($last_contact_check)
613                                 $next_contact_check = $last_contact_check + 86400;
614                         else
615                                 $next_contact_check = 0;
616
617                         if($next_contact_check <= time()) {
618                                 pumpio_getallusers($a, $rr["uid"]);
619                                 set_pconfig($rr['uid'],'pumpio','contact_check',time());
620                         }
621 */
622
623                 }
624         }
625
626         logger('twitter: cron_end');
627
628         set_config('twitter','last_poll', time());
629 }
630
631 function twitter_expire($a,$b) {
632
633         $days = get_config('twitter', 'expire');
634
635         if ($days == 0)
636                 return;
637
638         $r = q("DELETE FROM `item` WHERE `deleted` AND `network` = '%s'", dbesc(NETWORK_TWITTER));
639
640         require_once("include/items.php");
641
642         logger('twitter_expire: expire_start');
643
644         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
645         if(count($r)) {
646                 foreach($r as $rr) {
647                         logger('twitter_expire: user '.$rr['uid']);
648                         item_expire($rr['uid'], $days, NETWORK_TWITTER, true);
649                 }
650         }
651
652         logger('twitter_expire: expire_end');
653 }
654
655 function twitter_fetchtimeline($a, $uid) {
656         $ckey    = get_config('twitter', 'consumerkey');
657         $csecret = get_config('twitter', 'consumersecret');
658         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
659         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
660         $lastid  = get_pconfig($uid, 'twitter', 'lastid');
661
662         $application_name  = get_config('twitter', 'application_name');
663
664         if ($application_name == "")
665                 $application_name = $a->get_hostname();
666
667         $has_picture = false;
668
669         require_once('mod/item.php');
670         require_once('include/items.php');
671
672         require_once('library/twitteroauth.php');
673         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
674
675         $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
676
677         $first_time = ($lastid == "");
678
679         if ($lastid <> "")
680                 $parameters["since_id"] = $lastid;
681
682         $items = $connection->get('statuses/user_timeline', $parameters);
683
684         if (!is_array($items))
685                 return;
686
687         $posts = array_reverse($items);
688
689         if (count($posts)) {
690             foreach ($posts as $post) {
691                 if ($post->id_str > $lastid)
692                         $lastid = $post->id_str;
693
694                 if ($first_time)
695                         continue;
696
697                 if (!strpos($post->source, $application_name)) {
698                         $_SESSION["authenticated"] = true;
699                         $_SESSION["uid"] = $uid;
700
701                         unset($_REQUEST);
702                         $_REQUEST["type"] = "wall";
703                         $_REQUEST["api_source"] = true;
704                         $_REQUEST["profile_uid"] = $uid;
705                         $_REQUEST["source"] = "Twitter";
706
707                         //$_REQUEST["date"] = $post->created_at;
708
709                         $_REQUEST["title"] = "";
710
711                         if (is_object($post->retweeted_status)) {
712
713                                 $_REQUEST['body'] = $post->retweeted_status->text;
714
715                                 $picture = "";
716
717                                 // media
718                                 if (is_array($post->retweeted_status->entities->media)) {
719                                         foreach($post->retweeted_status->entities->media AS $media) {
720                                                 switch($media->type) {
721                                                         case 'photo':
722                                                                 //$_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
723                                                                 //$has_picture = true;
724                                                                 $_REQUEST['body'] = str_replace($media->url, "", $_REQUEST['body']);
725                                                                 $picture = $media->media_url_https;
726                                                                 break;
727                                                 }
728                                         }
729                                 }
730
731                                 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $picture);
732                                 $_REQUEST['body'] = $converted["body"];
733
734                                 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
735                                         "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
736                                         "' avatar='".$post->retweeted_status->user->profile_image_url_https.
737                                         "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
738                                         $_REQUEST['body'];
739                                 $_REQUEST['body'] .= "[/share]";
740                         } else {
741                                 $_REQUEST["body"] = $post->text;
742
743                                 $picture = "";
744
745                                 if (is_array($post->entities->media)) {
746                                         foreach($post->entities->media AS $media) {
747                                                 switch($media->type) {
748                                                         case 'photo':
749                                                                 //$_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
750                                                                 //$has_picture = true;
751                                                                 $_REQUEST['body'] = str_replace($media->url, "", $_REQUEST['body']);
752                                                                 $picture = $media->media_url_https;
753                                                                 break;
754                                                 }
755                                         }
756                                 }
757
758                                 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $picture);
759                                 $_REQUEST['body'] = $converted["body"];
760                         }
761
762                         if (is_string($post->place->name))
763                                 $_REQUEST["location"] = $post->place->name;
764
765                         if (is_string($post->place->full_name))
766                                 $_REQUEST["location"] = $post->place->full_name;
767
768                         if (is_array($post->geo->coordinates))
769                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
770
771                         if (is_array($post->coordinates->coordinates))
772                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
773
774                         //print_r($_REQUEST);
775                         logger('twitter: posting for user '.$uid);
776
777 //                      require_once('mod/item.php');
778
779                         item_post($a);
780                 }
781             }
782         }
783         set_pconfig($uid, 'twitter', 'lastid', $lastid);
784 }
785
786 function twitter_queue_hook(&$a,&$b) {
787
788         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
789                 dbesc(NETWORK_TWITTER)
790                 );
791         if(! count($qi))
792                 return;
793
794         require_once('include/queue_fn.php');
795
796         foreach($qi as $x) {
797                 if($x['network'] !== NETWORK_TWITTER)
798                         continue;
799
800                 logger('twitter_queue: run');
801
802                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
803                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
804                         intval($x['cid'])
805                 );
806                 if(! count($r))
807                         continue;
808
809                 $user = $r[0];
810
811                 $ckey    = get_config('twitter', 'consumerkey');
812                 $csecret = get_config('twitter', 'consumersecret');
813                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
814                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
815
816                 $success = false;
817
818                 if ($ckey AND $csecret AND $otoken AND $osecret) {
819
820                         logger('twitter_queue: able to post');
821
822                         $z = unserialize($x['content']);
823
824                         require_once("addon/twitter/codebird.php");
825
826                         $cb = \Codebird\Codebird::getInstance();
827                         $cb->setConsumerKey($ckey, $csecret);
828                         $cb->setToken($otoken, $osecret);
829
830                         if ($z['url'] == "statuses/update")
831                                 $result = $cb->statuses_update($z['post']);
832
833                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
834
835                         if ($result->errors)
836                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
837                         else {
838                                 $success = true;
839                                 remove_queue_item($x['id']);
840                         }
841                 } else
842                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
843
844                 if (!$success) {
845                         logger('twitter_queue: delayed');
846                         update_queue_time($x['id']);
847                 }
848         }
849 }
850
851 function twitter_fetch_contact($uid, $contact, $create_user) {
852
853         // Check if the unique contact is existing
854         // To-Do: only update once a while
855         $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
856                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
857
858         if (count($r) == 0)
859                 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
860                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
861                         dbesc($contact->name),
862                         dbesc($contact->screen_name),
863                         dbesc($contact->profile_image_url_https));
864         else
865                 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
866                         dbesc($contact->name),
867                         dbesc($contact->screen_name),
868                         dbesc($contact->profile_image_url_https),
869                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
870
871         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
872                 intval($uid), dbesc("twitter::".$contact->id_str));
873
874         if(!count($r) AND !$create_user)
875                 return(0);
876
877         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
878                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
879                 return(-1);
880         }
881
882         if(!count($r)) {
883                 // create contact record
884                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
885                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
886                                         `writable`, `blocked`, `readonly`, `pending` )
887                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
888                         intval($uid),
889                         dbesc(datetime_convert()),
890                         dbesc("https://twitter.com/".$contact->screen_name),
891                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
892                         dbesc($contact->screen_name."@twitter.com"),
893                         dbesc("twitter::".$contact->id_str),
894                         dbesc(''),
895                         dbesc("twitter::".$contact->id_str),
896                         dbesc($contact->name),
897                         dbesc($contact->screen_name),
898                         dbesc($contact->profile_image_url_https),
899                         dbesc(NETWORK_TWITTER),
900                         intval(CONTACT_IS_FRIEND),
901                         intval(1),
902                         intval(1)
903                 );
904
905                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
906                         dbesc("twitter::".$contact->id_str),
907                         intval($uid)
908                         );
909
910                 if(! count($r))
911                         return(false);
912
913                 $contact_id  = $r[0]['id'];
914
915                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
916                         intval($uid)
917                 );
918
919                 if($g && intval($g[0]['def_gid'])) {
920                         require_once('include/group.php');
921                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
922                 }
923
924                 require_once("Photo.php");
925
926                 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
927
928                 q("UPDATE `contact` SET `photo` = '%s',
929                                         `thumb` = '%s',
930                                         `micro` = '%s',
931                                         `name-date` = '%s',
932                                         `uri-date` = '%s',
933                                         `avatar-date` = '%s'
934                                 WHERE `id` = %d",
935                         dbesc($photos[0]),
936                         dbesc($photos[1]),
937                         dbesc($photos[2]),
938                         dbesc(datetime_convert()),
939                         dbesc(datetime_convert()),
940                         dbesc(datetime_convert()),
941                         intval($contact_id)
942                 );
943
944         } else {
945                 // update profile photos once every two weeks as we have no notification of when they change.
946
947                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
948                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
949
950                 // check that we have all the photos, this has been known to fail on occasion
951
952                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
953
954                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
955
956                         require_once("Photo.php");
957
958                         $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
959
960                         q("UPDATE `contact` SET `photo` = '%s',
961                                                 `thumb` = '%s',
962                                                 `micro` = '%s',
963                                                 `name-date` = '%s',
964                                                 `uri-date` = '%s',
965                                                 `avatar-date` = '%s',
966                                                 `url` = '%s',
967                                                 `nurl` = '%s',
968                                                 `addr` = '%s',
969                                                 `name` = '%s',
970                                                 `nick` = '%s'
971                                         WHERE `id` = %d",
972                                 dbesc($photos[0]),
973                                 dbesc($photos[1]),
974                                 dbesc($photos[2]),
975                                 dbesc(datetime_convert()),
976                                 dbesc(datetime_convert()),
977                                 dbesc(datetime_convert()),
978                                 dbesc("https://twitter.com/".$contact->screen_name),
979                                 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
980                                 dbesc($contact->screen_name."@twitter.com"),
981                                 dbesc($contact->name),
982                                 dbesc($contact->screen_name),
983                                 intval($r[0]['id'])
984                         );
985                 }
986         }
987
988         return($r[0]["id"]);
989 }
990
991 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
992         $ckey    = get_config('twitter', 'consumerkey');
993         $csecret = get_config('twitter', 'consumersecret');
994         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
995         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
996
997         require_once("addon/twitter/codebird.php");
998
999         $cb = \Codebird\Codebird::getInstance();
1000         $cb->setConsumerKey($ckey, $csecret);
1001         $cb->setToken($otoken, $osecret);
1002
1003         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1004                 intval($uid));
1005
1006         if(count($r)) {
1007                 $self = $r[0];
1008         } else
1009                 return;
1010
1011         $parameters = array();
1012
1013         if ($screen_name != "")
1014                 $parameters["screen_name"] = $screen_name;
1015
1016         if ($user_id != "")
1017                 $parameters["user_id"] = $user_id;
1018
1019         // Fetching user data
1020         $user = $cb->users_show($parameters);
1021
1022         if (!is_object($user))
1023                 return;
1024
1025         $contact_id = twitter_fetch_contact($uid, $user, true);
1026
1027         return $contact_id;
1028 }
1029
1030 function twitter_expand_entities($a, $body, $item, $no_tags = false, $picture) {
1031         require_once("include/oembed.php");
1032         require_once("include/network.php");
1033
1034         $tags = "";
1035
1036         if (isset($item->entities->urls)) {
1037                 $type = "";
1038                 $footerurl = "";
1039                 $footerlink = "";
1040                 $footer = "";
1041
1042                 foreach ($item->entities->urls AS $url) {
1043                         if ($url->url AND $url->expanded_url AND $url->display_url) {
1044
1045                                 $expanded_url = original_url($url->expanded_url);
1046
1047                                 $oembed_data = oembed_fetch_url($expanded_url);
1048
1049                                 // Quickfix: Workaround for URL with "[" and "]" in it
1050                                 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1051                                         $expanded_url = $url->url;
1052
1053                                 if ($type == "")
1054                                         $type = $oembed_data->type;
1055
1056                                 if ($oembed_data->type == "video") {
1057                                         //$body = str_replace($url->url,
1058                                         //              "[video]".$expanded_url."[/video]", $body);
1059                                         //$dontincludemedia = true;
1060                                         $type = $oembed_data->type;
1061                                         $footerurl = $expanded_url;
1062                                         $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1063
1064                                         $body = str_replace($url->url, $footerlink, $body);
1065                                 //} elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1066                                 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url)) {
1067                                         $body = str_replace($url->url,
1068                                                         "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1069                                                         $body);
1070                                         //$dontincludemedia = true;
1071                                 } elseif ($oembed_data->type != "link")
1072                                         $body = str_replace($url->url,
1073                                                         "[url=".$expanded_url."]".$expanded_url."[/url]",
1074                                                         $body);
1075                                 else {
1076                                         $img_str = fetch_url($expanded_url, true, $redirects, 4);
1077
1078                                         $tempfile = tempnam(get_temppath(), "cache");
1079                                         file_put_contents($tempfile, $img_str);
1080                                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1081                                         unlink($tempfile);
1082
1083                                         if (substr($mime, 0, 6) == "image/") {
1084                                                 $type = "photo";
1085                                                 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1086                                                 //$dontincludemedia = true;
1087                                         } else {
1088                                                 $type = $oembed_data->type;
1089                                                 $footerurl = $expanded_url;
1090                                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1091
1092                                                 $body = str_replace($url->url, $footerlink, $body);
1093                                         }
1094                                 }
1095                         }
1096                 }
1097
1098                 if ($footerurl != "")
1099                         $footer = add_page_info($footerurl, false, $picture);
1100
1101                 if (($footerlink != "") AND (trim($footer) != "")) {
1102                         $removedlink = trim(str_replace($footerlink, "", $body));
1103
1104                         if (strstr($body, $removedlink))
1105                                 $body = $removedlink;
1106
1107                         $body .= $footer;
1108                 }
1109
1110                 if (($footer == "") AND ($picture != ""))
1111                         $body .= "\n\n[img]".$picture."[/img]\n";
1112
1113                 if ($no_tags)
1114                         return(array("body" => $body, "tags" => ""));
1115
1116                 $tags_arr = array();
1117
1118                 foreach ($item->entities->hashtags AS $hashtag) {
1119                         $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1120                         $tags_arr["#".$hashtag->text] = $url;
1121                         $body = str_replace("#".$hashtag->text, $url, $body);
1122                 }
1123
1124                 foreach ($item->entities->user_mentions AS $mention) {
1125                         $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1126                         $tags_arr["@".$mention->screen_name] = $url;
1127                         $body = str_replace("@".$mention->screen_name, $url, $body);
1128                 }
1129
1130                 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1131                 $tags = get_tags($body);
1132
1133                 if(count($tags)) {
1134                         foreach($tags as $tag) {
1135                                 if (strstr(trim($tag), " "))
1136                                         continue;
1137
1138                                 if(strpos($tag,'#') === 0) {
1139                                         if(strpos($tag,'[url='))
1140                                                 continue;
1141
1142                                         // don't link tags that are already embedded in links
1143
1144                                         if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1145                                                 continue;
1146                                         if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1147                                                 continue;
1148
1149                                         $basetag = str_replace('_',' ',substr($tag,1));
1150                                         $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1151                                         $body = str_replace($tag,$url,$body);
1152                                         $tags_arr["#".$basetag] = $url;
1153                                         continue;
1154                                 } elseif(strpos($tag,'@') === 0) {
1155                                         if(strpos($tag,'[url='))
1156                                                 continue;
1157
1158                                         $basetag = substr($tag,1);
1159                                         $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1160                                         $body = str_replace($tag,$url,$body);
1161                                         $tags_arr["@".$basetag] = $url;
1162                                 }
1163                         }
1164                 }
1165
1166
1167                 $tags = implode($tags_arr, ",");
1168
1169         }
1170         return(array("body" => $body, "tags" => $tags));
1171 }
1172
1173 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1174
1175         $has_picture = false;
1176
1177         $postarray = array();
1178         $postarray['network'] = NETWORK_TWITTER;
1179         $postarray['gravity'] = 0;
1180         $postarray['uid'] = $uid;
1181         $postarray['wall'] = 0;
1182         $postarray['uri'] = "twitter::".$post->id_str;
1183
1184         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1185                         dbesc($postarray['uri']),
1186                         intval($uid)
1187                 );
1188
1189         if (count($r))
1190                 return(array());
1191
1192         $contactid = 0;
1193
1194         if ($post->in_reply_to_status_id_str != "") {
1195
1196                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1197
1198                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1199                                 dbesc($parent),
1200                                 intval($uid)
1201                         );
1202                 if (count($r)) {
1203                         $postarray['thr-parent'] = $r[0]["uri"];
1204                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1205                 } else {
1206                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1207                                         dbesc($parent),
1208                                         intval($uid)
1209                                 );
1210                         if (count($r)) {
1211                                 $postarray['thr-parent'] = $r[0]['uri'];
1212                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1213                         } else {
1214                                 $postarray['thr-parent'] = $postarray['uri'];
1215                                 $postarray['parent-uri'] = $postarray['uri'];
1216                         }
1217                 }
1218
1219                 // Is it me?
1220                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1221
1222                 if ($post->user->id_str == $own_id) {
1223                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1224                                 intval($uid));
1225
1226                         if(count($r)) {
1227                                 $contactid = $r[0]["id"];
1228
1229                                 $postarray['owner-name'] =  $r[0]["name"];
1230                                 $postarray['owner-link'] = $r[0]["url"];
1231                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1232                         } else
1233                                 return(array());
1234                 }
1235                 // Don't create accounts of people who just comment something
1236                 $create_user = false;
1237         } else
1238                 $postarray['parent-uri'] = $postarray['uri'];
1239
1240         if ($contactid == 0) {
1241                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1242
1243                 $postarray['owner-name'] = $post->user->name;
1244                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1245                 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1246         }
1247
1248         if(($contactid == 0) AND !$only_existing_contact)
1249                 $contactid = $self['id'];
1250         elseif ($contactid <= 0)
1251                 return(array());
1252
1253         $postarray['contact-id'] = $contactid;
1254
1255         $postarray['verb'] = ACTIVITY_POST;
1256         $postarray['author-name'] = $postarray['owner-name'];
1257         $postarray['author-link'] = $postarray['owner-link'];
1258         $postarray['author-avatar'] = $postarray['owner-avatar'];
1259         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1260         $postarray['app'] = strip_tags($post->source);
1261
1262         if ($post->user->protected) {
1263                 $postarray['private'] = 1;
1264                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1265         }
1266
1267         $postarray['body'] = $post->text;
1268
1269         $picture = "";
1270
1271         // media
1272         if (is_array($post->entities->media)) {
1273                 foreach($post->entities->media AS $media) {
1274                         switch($media->type) {
1275                                 case 'photo':
1276                                         //$postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1277                                         //$has_picture = true;
1278                                         $postarray['body'] = str_replace($media->url, "", $postarray['body']);
1279                                         $picture = $media->media_url_https;
1280                                         break;
1281                                 default:
1282                                         $postarray['body'] .= print_r($media, true);
1283                         }
1284                 }
1285         }
1286
1287         $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $picture);
1288         $postarray['body'] = $converted["body"];
1289         $postarray['tag'] = $converted["tags"];
1290
1291         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1292         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1293
1294         if (is_string($post->place->name))
1295                 $postarray["location"] = $post->place->name;
1296
1297         if (is_string($post->place->full_name))
1298                 $postarray["location"] = $post->place->full_name;
1299
1300         if (is_array($post->geo->coordinates))
1301                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1302
1303         if (is_array($post->coordinates->coordinates))
1304                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1305
1306         if (is_object($post->retweeted_status)) {
1307
1308                 $postarray['body'] = $post->retweeted_status->text;
1309
1310                 $picture = "";
1311
1312                 // media
1313                 if (is_array($post->retweeted_status->entities->media)) {
1314                         foreach($post->retweeted_status->entities->media AS $media) {
1315                                 switch($media->type) {
1316                                         case 'photo':
1317                                                 //$postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1318                                                 //$has_picture = true;
1319                                                 $postarray['body'] = str_replace($media->url, "", $postarray['body']);
1320                                                 $picture = $media->media_url_https;
1321                                                 break;
1322                                         default:
1323                                                 $postarray['body'] .= print_r($media, true);
1324                                 }
1325                         }
1326                 }
1327
1328                 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $picture);
1329                 $postarray['body'] = $converted["body"];
1330                 $postarray['tag'] = $converted["tags"];
1331
1332                 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1333
1334                 // Deactivated at the moment, since there are problems with answers to retweets
1335                 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1336                         $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1337                                 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1338                                 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1339                                 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1340                                 $postarray['body'];
1341                         $postarray['body'] .= "[/share]";
1342                 } else {
1343                         // Let retweets look like wall-to-wall posts
1344                         $postarray['author-name'] = $post->retweeted_status->user->name;
1345                         $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1346                         $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1347                         //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1348                         //      $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1349                         //      $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1350                         //}
1351                 }
1352
1353         }
1354         return($postarray);
1355 }
1356
1357 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1358
1359         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1360                         intval($uid)
1361                 );
1362
1363         if(!count($user))
1364                 return;
1365
1366         // Is it me?
1367         if (link_compare($user[0]["url"], $postarray['author-link']))
1368                 return;
1369
1370         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1371                         intval($uid),
1372                         dbesc("twitter::".$own_id)
1373                 );
1374
1375         if(!count($own_user))
1376                 return;
1377
1378         // Is it me from twitter?
1379         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1380                 return;
1381
1382         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1383                         dbesc($postarray['parent-uri']),
1384                         intval($uid)
1385                         );
1386
1387         if(count($myconv)) {
1388
1389                 foreach($myconv as $conv) {
1390                         // now if we find a match, it means we're in this conversation
1391
1392                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1393                                 continue;
1394
1395                         require_once('include/enotify.php');
1396
1397                         $conv_parent = $conv['parent'];
1398
1399                         notification(array(
1400                                 'type'         => NOTIFY_COMMENT,
1401                                 'notify_flags' => $user[0]['notify-flags'],
1402                                 'language'     => $user[0]['language'],
1403                                 'to_name'      => $user[0]['username'],
1404                                 'to_email'     => $user[0]['email'],
1405                                 'uid'          => $user[0]['uid'],
1406                                 'item'         => $postarray,
1407                                 'link'             => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1408                                 'source_name'  => $postarray['author-name'],
1409                                 'source_link'  => $postarray['author-link'],
1410                                 'source_photo' => $postarray['author-avatar'],
1411                                 'verb'         => ACTIVITY_POST,
1412                                 'otype'        => 'item',
1413                                 'parent'       => $conv_parent,
1414                         ));
1415
1416                         // only send one notification
1417                         break;
1418                 }
1419         }
1420 }
1421
1422 function twitter_fetchhometimeline($a, $uid) {
1423         $ckey    = get_config('twitter', 'consumerkey');
1424         $csecret = get_config('twitter', 'consumersecret');
1425         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1426         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1427         $create_user = get_pconfig($uid, 'twitter', 'create_user');
1428
1429         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1430
1431         require_once('library/twitteroauth.php');
1432         require_once('include/items.php');
1433
1434         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1435
1436         $own_contact = twitter_fetch_own_contact($a, $uid);
1437
1438         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1439                 intval($own_contact),
1440                 intval($uid));
1441
1442         if(count($r)) {
1443                 $own_id = $r[0]["nick"];
1444         } else {
1445                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1446                 return;
1447         }
1448
1449         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1450                 intval($uid));
1451
1452         if(count($r)) {
1453                 $self = $r[0];
1454         } else {
1455                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1456                 return;
1457         }
1458
1459         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1460                 intval($uid));
1461         if(!count($u)) {
1462                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1463                 return;
1464         }
1465
1466         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1467         //$parameters["count"] = 200;
1468
1469
1470         // Fetching timeline
1471         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1472
1473         $first_time = ($lastid == "");
1474
1475         if ($lastid <> "")
1476                 $parameters["since_id"] = $lastid;
1477
1478         $items = $connection->get('statuses/home_timeline', $parameters);
1479
1480         if (!is_array($items)) {
1481                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1482                 return;
1483         }
1484
1485         $posts = array_reverse($items);
1486
1487         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1488
1489         if (count($posts)) {
1490                 foreach ($posts as $post) {
1491                         if ($post->id_str > $lastid)
1492                                 $lastid = $post->id_str;
1493
1494                         if ($first_time)
1495                                 continue;
1496
1497                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1498
1499                         if (trim($postarray['body']) == "")
1500                                 continue;
1501
1502                         $item = item_store($postarray);
1503
1504                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1505
1506                         if ($item != 0)
1507                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1508
1509                 }
1510         }
1511         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1512
1513         // Fetching mentions
1514         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1515
1516         $first_time = ($lastid == "");
1517
1518         if ($lastid <> "")
1519                 $parameters["since_id"] = $lastid;
1520
1521         $items = $connection->get('statuses/mentions_timeline', $parameters);
1522
1523         if (!is_array($items)) {
1524                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1525                 return;
1526         }
1527
1528         $posts = array_reverse($items);
1529
1530         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1531
1532         if (count($posts)) {
1533                 foreach ($posts as $post) {
1534                         if ($post->id_str > $lastid)
1535                                 $lastid = $post->id_str;
1536
1537                         if ($first_time)
1538                                 continue;
1539
1540                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1541
1542                         if (trim($postarray['body']) == "")
1543                                 continue;
1544
1545                         $item = item_store($postarray);
1546
1547                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1548
1549                         if ($item == 0) {
1550                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1551                                         dbesc($postarray['uri']),
1552                                         intval($uid)
1553                                 );
1554                                 if (count($r))
1555                                         $item = $r[0]['id'];
1556                         }
1557
1558                         if ($item != 0) {
1559                                 require_once('include/enotify.php');
1560                                 notification(array(
1561                                         'type'         => NOTIFY_TAGSELF,
1562                                         'notify_flags' => $u[0]['notify-flags'],
1563                                         'language'     => $u[0]['language'],
1564                                         'to_name'      => $u[0]['username'],
1565                                         'to_email'     => $u[0]['email'],
1566                                         'uid'          => $u[0]['uid'],
1567                                         'item'         => $postarray,
1568                                         'link'         => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1569                                         'source_name'  => $postarray['author-name'],
1570                                         'source_link'  => $postarray['author-link'],
1571                                         'source_photo' => $postarray['author-avatar'],
1572                                         'verb'         => ACTIVITY_TAG,
1573                                         'otype'        => 'item'
1574                                 ));
1575                         }
1576                 }
1577         }
1578
1579         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1580 }
1581
1582 function twitter_fetch_own_contact($a, $uid) {
1583         $ckey    = get_config('twitter', 'consumerkey');
1584         $csecret = get_config('twitter', 'consumersecret');
1585         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1586         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1587
1588         $own_id = get_pconfig($uid, 'twitter', 'own_id');
1589
1590         $contact_id = 0;
1591
1592         if ($own_id == "") {
1593                 require_once('library/twitteroauth.php');
1594
1595                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1596
1597                 // Fetching user data
1598                 $user = $connection->get('account/verify_credentials');
1599
1600                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1601
1602                 $contact_id = twitter_fetch_contact($uid, $user, true);
1603
1604         } else {
1605                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1606                         intval($uid), dbesc("twitter::".$own_id));
1607                 if(count($r))
1608                         $contact_id = $r[0]["id"];
1609                 else
1610                         del_pconfig($uid, 'twitter', 'own_id');
1611
1612         }
1613
1614         return($contact_id);
1615 }
1616
1617 function twitter_is_retweet($a, $uid, $body) {
1618         $body = trim($body);
1619
1620         // Skip if it isn't a pure repeated messages
1621         // Does it start with a share?
1622         if (strpos($body, "[share") > 0)
1623                 return(false);
1624
1625         // Does it end with a share?
1626         if (strlen($body) > (strrpos($body, "[/share]") + 8))
1627                 return(false);
1628
1629         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1630         // Skip if there is no shared message in there
1631         if ($body == $attributes)
1632                 return(false);
1633
1634         $link = "";
1635         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1636         if ($matches[1] != "")
1637                 $link = $matches[1];
1638
1639         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1640         if ($matches[1] != "")
1641                 $link = $matches[1];
1642
1643         $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
1644         if ($id == $link)
1645                 return(false);
1646
1647         logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1648
1649         $ckey    = get_config('twitter', 'consumerkey');
1650         $csecret = get_config('twitter', 'consumersecret');
1651         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1652         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1653
1654         require_once('library/twitteroauth.php');
1655         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1656
1657         $result = $connection->post('statuses/retweet/'.$id);
1658
1659         logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1660
1661         return(!isset($result->errors));
1662 }
1663 ?>