]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/twittersettings.php
Merge branch 'master' of git@gitorious.org:laconica/mainline
[quix0rs-gnu-social.git] / actions / twittersettings.php
1 <?php
2 /**
3  * Laconica, the distributed open-source microblogging tool
4  *
5  * Settings for Twitter integration
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  Settings
23  * @package   Laconica
24  * @author    Evan Prodromou <evan@controlyourself.ca>
25  * @copyright 2008-2009 Control Yourself, Inc.
26  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
27  * @link      http://laconi.ca/
28  */
29
30 if (!defined('LACONICA')) {
31     exit(1);
32 }
33
34 require_once INSTALLDIR.'/lib/connectsettingsaction.php';
35 require_once INSTALLDIR.'/lib/twitter.php';
36
37 define('SUBSCRIPTIONS', 80);
38
39 /**
40  * Settings for Twitter integration
41  *
42  * @category Settings
43  * @package  Laconica
44  * @author   Evan Prodromou <evan@controlyourself.ca>
45  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
46  * @link     http://laconi.ca/
47  *
48  * @see      SettingsAction
49  */
50
51 class TwittersettingsAction extends ConnectSettingsAction
52 {
53     /**
54      * Title of the page
55      *
56      * @return string Title of the page
57      */
58
59     function title()
60     {
61         return _('Twitter settings');
62     }
63
64     /**
65      * Instructions for use
66      *
67      * @return instructions for use
68      */
69
70     function getInstructions()
71     {
72         return _('Add your Twitter account to automatically send '.
73                  ' your notices to Twitter, ' .
74                  'and subscribe to Twitter friends already here.');
75     }
76
77     /**
78      * Content area of the page
79      *
80      * Shows a form for associating a Twitter account with this
81      * Laconica account. Also lets the user set preferences.
82      *
83      * @return void
84      */
85
86     function showContent()
87     {
88         $user = common_current_user();
89
90         $profile = $user->getProfile();
91
92         $fuser = null;
93
94         $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
95
96         if ($flink) {
97             $fuser = $flink->getForeignUser();
98         }
99
100         $this->elementStart('form', array('method' => 'post',
101                                           'id' => 'form_settings_twitter',
102                                           'class' => 'form_settings',
103                                           'action' =>
104                                           common_local_url('twittersettings')));
105         $this->elementStart('fieldset', array('id' => 'settings_twitter_account'));
106         $this->element('legend', null, _('Twitter Account'));
107         $this->hidden('token', common_session_token());
108         if ($fuser) {
109             $this->elementStart('ul', 'form_data');
110             $this->elementStart('li', array('id' => 'settings_twitter_remove'));
111             $this->element('span', 'twitter_user', $fuser->nickname);
112             $this->element('a', array('href' => $fuser->uri), $fuser->uri);
113             $this->element('p', 'form_note',
114                            _('Current verified Twitter account.'));
115             $this->hidden('flink_foreign_id', $flink->foreign_id);
116             $this->elementEnd('li');
117             $this->elementEnd('ul');
118             $this->submit('remove', _('Remove'));
119         } else {
120             $this->elementStart('ul', 'form_data');
121             $this->elementStart('li', array('id' => 'settings_twitter_login'));
122             $this->input('twitter_username', _('Twitter user name'),
123                          ($this->arg('twitter_username')) ?
124                          $this->arg('twitter_username') :
125                          $profile->nickname,
126                          _('No spaces, please.')); // hey, it's what Twitter says
127             $this->elementEnd('li');
128             $this->elementStart('li');
129             $this->password('twitter_password', _('Twitter password'));
130             $this->elementend('li');
131             $this->elementEnd('ul');
132         }
133         $this->elementEnd('fieldset');
134
135         $this->elementStart('fieldset',
136                             array('id' => 'settings_twitter_preferences'));
137         $this->element('legend', null, _('Preferences'));
138
139         $this->elementStart('ul', 'form_data');
140         $this->elementStart('li');
141         $this->checkbox('noticesend',
142                         _('Automatically send my notices to Twitter.'),
143                         ($flink) ?
144                         ($flink->noticesync & FOREIGN_NOTICE_SEND) :
145                         true);
146         $this->elementEnd('li');
147         $this->elementStart('li');
148         $this->checkbox('replysync',
149                         _('Send local "@" replies to Twitter.'),
150                         ($flink) ?
151                         ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) :
152                         true);
153         $this->elementEnd('li');
154         $this->elementStart('li');
155         $this->checkbox('friendsync',
156                         _('Subscribe to my Twitter friends here.'),
157                         ($flink) ?
158                         ($flink->friendsync & FOREIGN_FRIEND_RECV) :
159                         false);
160         $this->elementEnd('li');
161
162         if (common_config('twitterbridge','enabled')) {
163             $this->elementStart('li');
164             $this->checkbox('noticerecv',
165                             _('Import my Friends Timeline.'),
166                             ($flink) ?
167                             ($flink->noticesync & FOREIGN_NOTICE_RECV) :
168                             false);
169             $this->elementEnd('li');
170         } else {
171             // preserve setting even if bidrection bridge toggled off
172             if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) {
173                 $this->hidden('noticerecv', true, 'noticerecv');
174             }
175         }
176
177         $this->elementEnd('ul');
178
179         if ($flink) {
180             $this->submit('save', _('Save'));
181         } else {
182             $this->submit('add', _('Add'));
183         }
184         $this->elementEnd('fieldset');
185
186         $this->showTwitterSubscriptions();
187
188         $this->elementEnd('form');
189     }
190
191     /**
192      * Gets some of the user's Twitter friends
193      *
194      * Gets the number of Twitter friends that are on this
195      * instance of Laconica.
196      *
197      * @return array array of User objects
198      */
199
200     function subscribedTwitterUsers()
201     {
202
203         $current_user = common_current_user();
204
205         $qry = 'SELECT "user".* ' .
206           'FROM subscription ' .
207           'JOIN "user" ON subscription.subscribed = "user".id ' .
208           'JOIN foreign_link ON foreign_link.user_id = "user".id ' .
209           'WHERE subscriber = %d ' .
210           'ORDER BY "user".nickname';
211
212         $user = new User();
213
214         $user->query(sprintf($qry, $current_user->id));
215
216         $users = array();
217
218         while ($user->fetch()) {
219
220             // Don't include the user's own self-subscription
221             if ($user->id != $current_user->id) {
222                 $users[] = clone($user);
223             }
224         }
225
226         return $users;
227     }
228
229     /**
230      * Show user's Twitter friends
231      *
232      * Gets the number of Twitter friends that are on this
233      * instance of Laconica, and shows their mini-avatars.
234      *
235      * @return void
236      */
237
238     function showTwitterSubscriptions()
239     {
240
241         $friends = $this->subscribedTwitterUsers();
242
243         $friends_count = count($friends);
244
245         if ($friends_count > 0) {
246             $this->elementStart('div', array('id' => 'entity_subscriptions',
247                                              'class' => 'section'));
248             $this->element('h2', null, _('Twitter Friends'));
249             $this->elementStart('ul', 'entities users xoxo');
250
251             for ($i = 0; $i < min($friends_count, SUBSCRIPTIONS); $i++) {
252
253                 $other = Profile::staticGet($friends[$i]->id);
254
255                 if (!$other) {
256                     common_log_db_error($subs, 'SELECT', __FILE__);
257                     continue;
258                 }
259
260                 $this->elementStart('li', 'vcard');
261                 $this->elementStart('a', array('title' => ($other->fullname) ?
262                                                $other->fullname :
263                                                $other->nickname,
264                                                'href' => $other->profileurl,
265                                                'class' => 'url'));
266
267                 $avatar = $other->getAvatar(AVATAR_MINI_SIZE);
268
269                 $avatar_url = ($avatar) ?
270                   $avatar->displayUrl() :
271                   Avatar::defaultImage(AVATAR_MINI_SIZE);
272
273                 $this->element('img', array('src' => $avatar_url,
274                                             'width' => AVATAR_MINI_SIZE,
275                                             'height' => AVATAR_MINI_SIZE,
276                                             'class' => 'avatar photo',
277                                             'alt' =>  ($other->fullname) ?
278                                             $other->fullname :
279                                             $other->nickname));
280
281                 $this->element('span', 'fn nickname', $other->nickname);
282                 $this->elementEnd('a');
283                 $this->elementEnd('li');
284
285             }
286
287             $this->elementEnd('ul');
288             $this->elementEnd('div');
289
290         }
291     }
292
293     /**
294      * Handle posts to this form
295      *
296      * Based on the button that was pressed, muxes out to other functions
297      * to do the actual task requested.
298      *
299      * All sub-functions reload the form with a message -- success or failure.
300      *
301      * @return void
302      */
303
304     function handlePost()
305     {
306
307         // CSRF protection
308         $token = $this->trimmed('token');
309         if (!$token || $token != common_session_token()) {
310             $this->showForm(_('There was a problem with your session token. '.
311                               'Try again, please.'));
312             return;
313         }
314
315         if ($this->arg('save')) {
316             $this->savePreferences();
317         } else if ($this->arg('add')) {
318             $this->addTwitterAccount();
319         } else if ($this->arg('remove')) {
320             $this->removeTwitterAccount();
321         } else {
322             $this->showForm(_('Unexpected form submission.'));
323         }
324     }
325
326     /**
327      * Associate a Twitter account with the user's account
328      *
329      * Validates post input; verifies it against Twitter; and if
330      * successful stores in the database.
331      *
332      * @return void
333      */
334
335     function addTwitterAccount()
336     {
337         $screen_name = $this->trimmed('twitter_username');
338         $password    = $this->trimmed('twitter_password');
339         $noticesend  = $this->boolean('noticesend');
340         $noticerecv  = $this->boolean('noticerecv');
341         $replysync   = $this->boolean('replysync');
342         $friendsync  = $this->boolean('friendsync');
343
344         if (!Validate::string($screen_name,
345                               array('min_length' => 1,
346                                     'max_length' => 15,
347                                     'format' => VALIDATE_NUM.VALIDATE_ALPHA.'_'))) {
348             $this->showForm(_('Username must have only numbers, '.
349                               'upper- and lowercase letters, '.
350                               'and underscore (_). 15 chars max.'));
351             return;
352         }
353
354         if (!$this->verifyCredentials($screen_name, $password)) {
355             $this->showForm(_('Could not verify your Twitter credentials!'));
356             return;
357         }
358
359         $twit_user = twitter_user_info($screen_name, $password);
360
361         if (!$twit_user) {
362             $this->showForm(sprintf(_('Unable to retrieve account information '.
363                                       'For "%s" from Twitter.'),
364                                     $screen_name));
365             return;
366         }
367
368         if (!save_twitter_user($twit_user->id, $screen_name)) {
369             $this->showForm(_('Unable to save your Twitter settings!'));
370             return;
371         }
372
373         $user = common_current_user();
374
375         $flink = new Foreign_link();
376
377         $flink->user_id     = $user->id;
378         $flink->foreign_id  = $twit_user->id;
379         $flink->service     = TWITTER_SERVICE;
380         $flink->credentials = $password;
381         $flink->created     = common_sql_now();
382
383         $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
384
385         $flink_id = $flink->insert();
386
387         if (!$flink_id) {
388             common_log_db_error($flink, 'INSERT', __FILE__);
389             $this->showForm(_('Unable to save your Twitter settings!'));
390             return;
391         }
392
393         if ($friendsync) {
394             save_twitter_friends($user, $twit_user->id, $screen_name, $password);
395             $flink->last_friendsync = common_sql_now();
396             $flink->update();
397         }
398
399         $this->showForm(_('Twitter settings saved.'), true);
400     }
401
402     /**
403      * Disassociate an existing Twitter account from this account
404      *
405      * @return void
406      */
407
408     function removeTwitterAccount()
409     {
410         $user = common_current_user();
411
412         $flink = Foreign_link::getByUserID($user->id, 1);
413
414         $flink_foreign_id = $this->arg('flink_foreign_id');
415
416         // Maybe an old tab open...?
417         if ($flink->foreign_id != $flink_foreign_id) {
418             $this->showForm(_('That is not your Twitter account.'));
419             return;
420         }
421
422         $result = $flink->delete();
423
424         if (!$result) {
425             common_log_db_error($flink, 'DELETE', __FILE__);
426             $this->serverError(_('Couldn\'t remove Twitter user.'));
427             return;
428         }
429
430         $this->showForm(_('Twitter account removed.'), true);
431     }
432
433     /**
434      * Save user's Twitter-bridging preferences
435      *
436      * @return void
437      */
438
439     function savePreferences()
440     {
441         $noticesend = $this->boolean('noticesend');
442         $noticerecv = $this->boolean('noticerecv');
443         $friendsync = $this->boolean('friendsync');
444         $replysync  = $this->boolean('replysync');
445
446         $user = common_current_user();
447
448         $flink = Foreign_link::getByUserID($user->id, 1);
449
450         if (!$flink) {
451             common_log_db_error($flink, 'SELECT', __FILE__);
452             $this->showForm(_('Couldn\'t save Twitter preferences.'));
453             return;
454         }
455
456         $twitter_id = $flink->foreign_id;
457         $password   = $flink->credentials;
458
459         $fuser = $flink->getForeignUser();
460
461         if (!$fuser) {
462             common_log_db_error($fuser, 'SELECT', __FILE__);
463             $this->showForm(_('Couldn\'t save Twitter preferences.'));
464             return;
465         }
466
467         $screen_name = $fuser->nickname;
468
469         $original = clone($flink);
470
471         $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
472
473         $result = $flink->update($original);
474
475         if ($result === false) {
476             common_log_db_error($flink, 'UPDATE', __FILE__);
477             $this->showForm(_('Couldn\'t save Twitter preferences.'));
478             return;
479         }
480
481         if ($friendsync) {
482             save_twitter_friends($user, $flink->foreign_id, $screen_name, $password);
483         }
484
485         $this->showForm(_('Twitter preferences saved.'), true);
486     }
487
488     /**
489      * Verifies a username and password against Twitter's API
490      *
491      * @param string $screen_name Twitter user name
492      * @param string $password    Twitter password
493      *
494      * @return boolean success flag
495      */
496
497     function verifyCredentials($screen_name, $password)
498     {
499         $uri = 'http://twitter.com/account/verify_credentials.json';
500
501         $data = get_twitter_data($uri, $screen_name, $password);
502
503         if (!$data) {
504             return false;
505         }
506
507         $user = json_decode($data);
508
509         if (!$user) {
510             return false;
511         }
512
513         $twitter_id = $user->id;
514
515         if ($twitter_id) {
516             return $twitter_id;
517         }
518
519         return false;
520     }
521
522 }