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