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