]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/twittersettings.php
Merge branch '0.7.x' into 0.8.x
[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         $this->elementStart('li');
162         $this->checkbox('noticerecv',
163                         _('Import my Friends Timeline.'),
164                         ($flink) ?
165                         ($flink->noticesync & FOREIGN_NOTICE_RECV) :
166                         false);
167         $this->elementEnd('li');
168         $this->elementEnd('ul');
169
170         if ($flink) {
171             $this->submit('save', _('Save'));
172         } else {
173             $this->submit('add', _('Add'));
174         }
175         $this->elementEnd('fieldset');
176
177         $this->showTwitterSubscriptions();
178
179         $this->elementEnd('form');
180     }
181
182     /**
183      * Gets some of the user's Twitter friends
184      *
185      * Gets the number of Twitter friends that are on this
186      * instance of Laconica.
187      *
188      * @return array array of User objects
189      */
190
191     function subscribedTwitterUsers()
192     {
193
194         $current_user = common_current_user();
195
196         $qry = 'SELECT "user".* ' .
197           'FROM subscription ' .
198           'JOIN "user" ON subscription.subscribed = "user".id ' .
199           'JOIN foreign_link ON foreign_link.user_id = "user".id ' .
200           'WHERE subscriber = %d ' .
201           'ORDER BY "user".nickname';
202
203         $user = new User();
204
205         $user->query(sprintf($qry, $current_user->id));
206
207         $users = array();
208
209         while ($user->fetch()) {
210
211             // Don't include the user's own self-subscription
212             if ($user->id != $current_user->id) {
213                 $users[] = clone($user);
214             }
215         }
216
217         return $users;
218     }
219
220     /**
221      * Show user's Twitter friends
222      *
223      * Gets the number of Twitter friends that are on this
224      * instance of Laconica, and shows their mini-avatars.
225      *
226      * @return void
227      */
228
229     function showTwitterSubscriptions()
230     {
231
232         $friends = $this->subscribedTwitterUsers();
233
234         $friends_count = count($friends);
235
236         if ($friends_count > 0) {
237             $this->elementStart('div', array('id' => 'entity_subscriptions',
238                                              'class' => 'section'));
239             $this->element('h2', null, _('Twitter Friends'));
240             $this->elementStart('ul', 'entities users xoxo');
241
242             for ($i = 0; $i < min($friends_count, SUBSCRIPTIONS); $i++) {
243
244                 $other = Profile::staticGet($friends[$i]->id);
245
246                 if (!$other) {
247                     common_log_db_error($subs, 'SELECT', __FILE__);
248                     continue;
249                 }
250
251                 $this->elementStart('li', 'vcard');
252                 $this->elementStart('a', array('title' => ($other->fullname) ?
253                                                $other->fullname :
254                                                $other->nickname,
255                                                'href' => $other->profileurl,
256                                                'class' => 'url'));
257
258                 $avatar = $other->getAvatar(AVATAR_MINI_SIZE);
259
260                 $avatar_url = ($avatar) ?
261                   $avatar->displayUrl() :
262                   Avatar::defaultImage(AVATAR_MINI_SIZE);
263
264                 $this->element('img', array('src' => $avatar_url,
265                                             'width' => AVATAR_MINI_SIZE,
266                                             'height' => AVATAR_MINI_SIZE,
267                                             'class' => 'avatar photo',
268                                             'alt' =>  ($other->fullname) ?
269                                             $other->fullname :
270                                             $other->nickname));
271
272                 $this->element('span', 'fn nickname', $other->nickname);
273                 $this->elementEnd('a');
274                 $this->elementEnd('li');
275
276             }
277
278             $this->elementEnd('ul');
279             $this->elementEnd('div');
280
281         }
282     }
283
284     /**
285      * Handle posts to this form
286      *
287      * Based on the button that was pressed, muxes out to other functions
288      * to do the actual task requested.
289      *
290      * All sub-functions reload the form with a message -- success or failure.
291      *
292      * @return void
293      */
294
295     function handlePost()
296     {
297
298         // CSRF protection
299         $token = $this->trimmed('token');
300         if (!$token || $token != common_session_token()) {
301             $this->showForm(_('There was a problem with your session token. '.
302                               'Try again, please.'));
303             return;
304         }
305
306         if ($this->arg('save')) {
307             $this->savePreferences();
308         } else if ($this->arg('add')) {
309             $this->addTwitterAccount();
310         } else if ($this->arg('remove')) {
311             $this->removeTwitterAccount();
312         } else {
313             $this->showForm(_('Unexpected form submission.'));
314         }
315     }
316
317     /**
318      * Associate a Twitter account with the user's account
319      *
320      * Validates post input; verifies it against Twitter; and if
321      * successful stores in the database.
322      *
323      * @return void
324      */
325
326     function addTwitterAccount()
327     {
328         $screen_name = $this->trimmed('twitter_username');
329         $password    = $this->trimmed('twitter_password');
330         $noticesend  = $this->boolean('noticesend');
331         $noticerecv  = $this->boolean('noticerecv');
332         $replysync   = $this->boolean('replysync');
333         $friendsync  = $this->boolean('friendsync');
334
335         if (!Validate::string($screen_name,
336                               array('min_length' => 1,
337                                     'max_length' => 15,
338                                     'format' => VALIDATE_NUM.VALIDATE_ALPHA.'_'))) {
339             $this->showForm(_('Username must have only numbers, '.
340                               'upper- and lowercase letters, '.
341                               'and underscore (_). 15 chars max.'));
342             return;
343         }
344
345         if (!$this->verifyCredentials($screen_name, $password)) {
346             $this->showForm(_('Could not verify your Twitter credentials!'));
347             return;
348         }
349
350         $twit_user = twitter_user_info($screen_name, $password);
351
352         if (!$twit_user) {
353             $this->showForm(sprintf(_('Unable to retrieve account information '.
354                                       'For "%s" from Twitter.'),
355                                     $screen_name));
356             return;
357         }
358
359         if (!save_twitter_user($twit_user->id, $screen_name)) {
360             $this->showForm(_('Unable to save your Twitter settings!'));
361             return;
362         }
363
364         $user = common_current_user();
365
366         $flink = new Foreign_link();
367
368         $flink->user_id     = $user->id;
369         $flink->foreign_id  = $twit_user->id;
370         $flink->service     = TWITTER_SERVICE;
371         $flink->credentials = $password;
372         $flink->created     = common_sql_now();
373
374         $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
375
376         $flink_id = $flink->insert();
377
378         if (!$flink_id) {
379             common_log_db_error($flink, 'INSERT', __FILE__);
380             $this->showForm(_('Unable to save your Twitter settings!'));
381             return;
382         }
383
384         if ($friendsync) {
385             save_twitter_friends($user, $twit_user->id, $screen_name, $password);
386         }
387
388         $this->showForm(_('Twitter settings saved.'), true);
389     }
390
391     /**
392      * Disassociate an existing Twitter account from this account
393      *
394      * @return void
395      */
396
397     function removeTwitterAccount()
398     {
399         $user = common_current_user();
400
401         $flink = Foreign_link::getByUserID($user->id, 1);
402
403         $flink_foreign_id = $this->arg('flink_foreign_id');
404
405         // Maybe an old tab open...?
406         if ($flink->foreign_id != $flink_foreign_id) {
407             $this->showForm(_('That is not your Twitter account.'));
408             return;
409         }
410
411         $result = $flink->delete();
412
413         if (!$result) {
414             common_log_db_error($flink, 'DELETE', __FILE__);
415             $this->serverError(_('Couldn\'t remove Twitter user.'));
416             return;
417         }
418
419         $this->showForm(_('Twitter account removed.'), true);
420     }
421
422     /**
423      * Save user's Twitter-bridging preferences
424      *
425      * @return void
426      */
427
428     function savePreferences()
429     {
430         $noticesend = $this->boolean('noticesend');
431         $noticerecv = $this->boolean('noticerecv');
432         $friendsync = $this->boolean('friendsync');
433         $replysync  = $this->boolean('replysync');
434
435         $user = common_current_user();
436
437         $flink = Foreign_link::getByUserID($user->id, 1);
438
439         if (!$flink) {
440             common_log_db_error($flink, 'SELECT', __FILE__);
441             $this->showForm(_('Couldn\'t save Twitter preferences.'));
442             return;
443         }
444
445         $twitter_id = $flink->foreign_id;
446         $password   = $flink->credentials;
447
448         $fuser = $flink->getForeignUser();
449
450         if (!$fuser) {
451             common_log_db_error($fuser, 'SELECT', __FILE__);
452             $this->showForm(_('Couldn\'t save Twitter preferences.'));
453             return;
454         }
455
456         $screen_name = $fuser->nickname;
457
458         $original = clone($flink);
459
460         $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
461
462         $result = $flink->update($original);
463
464         if ($result === false) {
465             common_log_db_error($flink, 'UPDATE', __FILE__);
466             $this->showForm(_('Couldn\'t save Twitter preferences.'));
467             return;
468         }
469
470         if ($friendsync) {
471             save_twitter_friends($user, $flink->foreign_id, $screen_name, $password);
472         }
473
474         $this->showForm(_('Twitter preferences saved.'), true);
475     }
476
477     /**
478      * Verifies a username and password against Twitter's API
479      *
480      * @param string $screen_name Twitter user name
481      * @param string $password    Twitter password
482      *
483      * @return boolean success flag
484      */
485
486     function verifyCredentials($screen_name, $password)
487     {
488         $uri = 'http://twitter.com/account/verify_credentials.json';
489
490         $data = get_twitter_data($uri, $screen_name, $password);
491
492         if (!$data) {
493             return false;
494         }
495
496         $user = json_decode($data);
497
498         if (!$user) {
499             return false;
500         }
501
502         $twitter_id = $user->id;
503
504         if ($twitter_id) {
505             return $twitter_id;
506         }
507
508         return false;
509     }
510
511 }