3 * Laconica - a distributed open-source microblogging tool
4 * Copyright (C) 2008, Controlez-Vous, Inc.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 if (!defined('LACONICA')) { exit(1); }
22 require_once(INSTALLDIR.'/lib/settingsaction.php');
24 define('SUBSCRIPTIONS', 80);
26 class TwittersettingsAction extends SettingsAction {
31 var $friends_count = 0;
36 function get_instructions() {
37 return _('Add your Twitter account to automatically send your notices to Twitter, ' .
38 'and subscribe to Twitter friends already here.');
41 function show_form($msg=NULL, $success=false) {
42 $user = common_current_user();
43 $profile = $user->getProfile();
45 $flink = Foreign_link::getByUserID($user->id, 1); // 1 == Twitter
48 $fuser = $flink->getForeignUser();
51 $this->form_header(_('Twitter settings'), $msg, $success);
52 common_element_start('form', array('method' => 'post',
53 'id' => 'twittersettings',
55 common_local_url('twittersettings')));
56 common_hidden('token', common_session_token());
58 common_element('h2', NULL, _('Twitter Account'));
61 common_element_start('p');
63 common_element('span', 'twitter_user', $fuser->nickname);
64 common_element('a', array('href' => $fuser->uri), $fuser->uri);
65 common_element('span', 'input_instructions',
66 _('Current verified Twitter account.'));
67 common_hidden('flink_foreign_id', $flink->foreign_id);
68 common_element_end('p');
69 common_submit('remove', _('Remove'));
71 common_input('twitter_username', _('Twitter Username'),
72 ($this->arg('twitter_username')) ? $this->arg('twitter_username') : $profile->nickname,
73 _('No spaces, please.')); // hey, it's what Twitter says
75 common_password('twitter_password', _('Twitter Password'));
78 common_element('h2', NULL, _('Preferences'));
80 common_checkbox('noticesync', _('Automatically send my notices to Twitter.'),
81 ($flink) ? ($flink->noticesync & FOREIGN_NOTICE_SEND) : true);
83 common_checkbox('replysync', _('Send local "@" replies to Twitter.'),
84 ($flink) ? ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) : true);
86 common_checkbox('friendsync', _('Subscribe to my Twitter friends here.'),
87 ($flink) ? ($flink->friendsync & FOREIGN_FRIEND_RECV) : true);
90 common_submit('save', _('Save'));
92 common_submit('add', _('Add'));
96 $this->show_twitter_subscriptions();
98 common_element_end('form');
100 common_show_footer();
103 function subscribed_twitter_users() {
105 $current_user = common_current_user();
107 $qry = 'SELECT user.* ' .
108 'FROM subscription ' .
109 'JOIN user ON subscription.subscribed = user.id ' .
110 'JOIN foreign_link ON foreign_link.user_id = user.id ' .
111 'WHERE subscriber = %d ' .
112 'ORDER BY user.nickname';
116 $user->query(sprintf($qry, $current_user->id));
120 while ($user->fetch()) {
121 $users[] = clone($user);
128 function show_twitter_subscriptions() {
130 common_debug('show twitter subs');
131 $friends = $this->subscribed_twitter_users();
133 $friends_count = count($friends);
135 common_debug("friends count = $friends_count");
137 if ($friends_count > 0) {
139 common_element('h3', NULL, _('Twitter Friends'));
140 common_element_start('div', array('id' => 'subscriptions'));
141 common_element_start('ul', array('id' => 'subscriptions_avatars'));
143 for ($i = 0; $i < min($friends_count, SUBSCRIPTIONS); $i++) {
145 $other = Profile::staticGet($friends[$i]->id);
148 common_log_db_error($subs, 'SELECT', __FILE__);
152 common_element_start('li');
153 common_element_start('a', array('title' => ($other->fullname) ?
156 'href' => $other->profileurl,
158 'class' => 'subscription'));
159 $avatar = $other->getAvatar(AVATAR_MINI_SIZE);
160 common_element('img', array('src' => (($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_MINI_SIZE)),
161 'width' => AVATAR_MINI_SIZE,
162 'height' => AVATAR_MINI_SIZE,
163 'class' => 'avatar mini',
164 'alt' => ($other->fullname) ?
167 common_element_end('a');
168 common_element_end('li');
172 common_element_end('ul');
173 common_element_end('div');
177 // XXX Figure out a way to show all Twitter friends...
180 if ($subs_count > SUBSCRIPTIONS) {
181 common_element_start('p', array('id' => 'subscriptions_viewall'));
183 common_element('a', array('href' => common_local_url('subscriptions',
184 array('nickname' => $profile->nickname)),
185 'class' => 'moresubscriptions'),
186 _('All subscriptions'));
187 common_element_end('p');
195 function handle_post() {
198 $token = $this->trimmed('token');
199 if (!$token || $token != common_session_token()) {
200 $this->show_form(_('There was a problem with your session token. Try again, please.'));
204 if ($this->arg('save')) {
205 $this->save_preferences();
206 } else if ($this->arg('add')) {
207 $this->add_twitter_acct();
208 } else if ($this->arg('remove')) {
209 $this->remove_twitter_acct();
211 $this->show_form(_('Unexpected form submission.'));
215 function add_twitter_acct() {
217 $this->twit_username = $this->trimmed('twitter_username');
218 $this->twit_password = $this->trimmed('twitter_password');
219 $this->noticesync = $this->boolean('noticesync');
220 $this->replysync = $this->boolean('replysync');
221 $this->friendsync = $this->boolean('friendsync');
223 if (!Validate::string($this->twit_username, array('min_length' => 1,
225 'format' => VALIDATE_NUM . VALIDATE_ALPHA . '_'))) {
226 $this->show_form(_('Username must have only numbers, upper- and lowercase letters, and underscore (_). 15 chars max.'));
230 // Verify this is a real Twitter user.
231 if (!$this->verify_credentials()) {
232 $this->show_form(_('Could not verify your Twitter credentials!'));
236 if (!$this->twitter_user_info()) {
237 $this->show_form(sprintf(_('Unable to retrieve account information for "%s" from Twitter.'),
242 $fuser_id = $this->update_twitter_user($this->twit_id, $this->twit_username);
245 $this->show_form(_('Unable to save your Twitter settings!'));
249 $user = common_current_user();
251 $flink = DB_DataObject::factory('foreign_link');
252 $flink->user_id = $user->id;
253 $flink->foreign_id = $fuser_id;
254 $flink->service = 1; // Twitter
255 $flink->credentials = $this->twit_password;
256 $flink->created = common_sql_now();
258 $this->set_flags($flink, $this->noticesync, $this->replysync, $this->friendsync);
260 $flink_id = $flink->insert();
263 common_log_db_error($flink, 'INSERT', __FILE__);
264 $this->show_form(_('Unable to save your Twitter settings!'));
268 if ($this->friendsync) {
269 $this->save_friends();
272 $this->show_form(_('Twitter settings saved.'), true);
275 function remove_twitter_acct() {
276 $user = common_current_user();
278 // For now we assume one Twitter acct per Laconica acct
279 $flink = Foreign_link::getByUserID($user->id, 1);
280 $flink_foreign_id = $this->arg('flink_foreign_id');
283 common_debug("couldn't get flink");
286 # Maybe an old tab open...?
287 if ($flink->foreign_id != $flink_foreign_id) {
288 common_debug("flink user_id = " . $flink->user_id);
289 $this->show_form(_('That is not your Twitter account.'));
293 $result = $flink->delete();
296 common_log_db_error($flink, 'DELETE', __FILE__);
297 common_server_error(_('Couldn\'t remove Twitter user.'));
301 $this->show_form(_('Twitter account removed.'), TRUE);
304 function save_preferences() {
305 $this->noticesync = $this->boolean('noticesync');
306 $this->friendsync = $this->boolean('friendsync');
307 $this->replysync = $this->boolean('replysync');
309 $user = common_current_user();
310 $flink = Foreign_link::getByUserID($user->id, 1);
313 common_log_db_error($flink, 'SELECT', __FILE__);
314 $this->show_form(_('Couldn\'t save Twitter preferences.'));
318 $this->twit_id = $flink->foreign_id;
319 $this->twit_password = $flink->credentials;
321 $fuser = $flink->getForeignUser();
324 common_log_db_error($fuser, 'SELECT', __FILE__);
325 $this->show_form(_('Couldn\'t save Twitter preferences.'));
329 $this->twit_username = $fuser->nickname;
331 $original = clone($flink);
332 $this->set_flags($flink, $this->noticesync, $this->replysync, $this->friendsync);
333 $result = $flink->update($original);
335 if ($result === FALSE) {
336 common_log_db_error($flink, 'UPDATE', __FILE__);
337 $this->show_form(_('Couldn\'t save Twitter preferences.'));
341 if ($this->friendsync) {
342 $this->save_friends();
345 $this->show_form(_('Twitter preferences saved.'));
348 function twitter_user_info() {
349 $uri = "http://twitter.com/users/show/$this->twit_username.json";
350 $data = $this->get_twitter_data($uri);
356 $twit_user = json_decode($data);
362 $this->friends_count = $twit_user->friends_count;
363 $this->twit_id = $twit_user->id;
365 common_debug("Twitter_id = $this->twit_id");
366 common_debug("Friends_count = $this->friends_count");
371 function verify_credentials() {
372 $uri = 'http://twitter.com/account/verify_credentials.json';
373 $data = $this->get_twitter_data($uri);
379 $creds = json_decode($data);
385 if ($creds->authorized == 1) {
392 function get_twitter_data($uri) {
395 CURLOPT_USERPWD => sprintf("%s:%s", $this->twit_username, $this->twit_password),
396 CURLOPT_RETURNTRANSFER => true,
397 CURLOPT_FAILONERROR => true,
398 CURLOPT_HEADER => false,
399 CURLOPT_FOLLOWLOCATION => true,
400 // CURLOPT_USERAGENT => "identi.ca",
401 CURLOPT_CONNECTTIMEOUT => 120,
402 CURLOPT_TIMEOUT => 120
406 $ch = curl_init($uri);
407 curl_setopt_array($ch, $options);
408 $data = curl_exec($ch);
409 $errmsg = curl_error($ch);
412 common_debug("cURL error: $errmsg - trying to load: $uri with user $this->twit_user.",
421 function set_flags(&$flink, $noticesync, $replysync, $friendsync) {
423 $flink->noticesync |= FOREIGN_NOTICE_SEND;
425 $flink->noticesync &= ~FOREIGN_NOTICE_SEND;
429 $flink->noticesync |= FOREIGN_NOTICE_SEND_REPLY;
431 $flink->noticesync &= ~FOREIGN_NOTICE_SEND_REPLY;
435 $flink->friendsync |= FOREIGN_FRIEND_RECV;
437 $flink->friendsync &= ~FOREIGN_FRIEND_RECV;
440 $flink->profilesync = 0; // XXX: leave as default?
443 function save_friends() {
445 $uri = 'http://twitter.com/statuses/friends.json?page=';
447 $this->twitter_user_info();
449 // Calculate how many pages to get...
450 $pages = ceil($this->friends_count / 100);
452 common_debug("number of pages to get: $pages");
456 for ($i = 1; $i <= $pages; $i++) {
458 $data = $this->get_twitter_data($uri . $i);
460 common_debug("fetching " . $uri . $i);
466 common_debug("got data");
468 $more_friends = json_decode($data);
470 if (!$more_friends) {
474 $friends = array_merge($friends, $more_friends);
478 common_debug("number of friends =" + count($friends));
480 $user = common_current_user();
482 foreach ($friends as $friend) {
484 $friend_name = $friend->screen_name;
485 $friend_id = $friend->id;
487 // Update or create the Foreign_user record
488 $this->update_twitter_user($friend_id, $friend_name);
490 // Check to see if there's a related local user
491 $flink = Foreign_link::getByForeignID($friend_id, 1);
495 // Get associated user
496 $friend_user = User::staticGet('id', $flink->user_id);
497 subs_subscribe_to($user, $friend_user);
504 // Creates or Updates a Twitter user
505 function update_twitter_user($twitter_id, $screen_name) {
509 $uri = "http://twitter.com/$screen_name";
511 // Check to see whether the Twitter user is already in the system,
512 // and update its screen name and uri if so.
513 $fuser = Foreign_User::getForeignUser($twitter_id, 1);
517 // Only update if Twitter screen name has changed
518 if ($fuser->nickname != $screen_name) {
520 $original = clone($fuser);
521 $fuser->nickname = $screen_name;
523 $result = $fuser->updateKeys($original);
526 common_log_db_error($fuser, 'UPDATE', __FILE__);
531 sprintf('Updated Twitter user %, screen name was: %, now: %s.',
532 $twitter_id, $original->nickname, $screen_name));
535 common_debug("No update for $screen_name needed.");
539 // Otherwise, create a new Twitter user
540 $fuser = DB_DataObject::factory('foreign_user');
542 $fuser->nickname = $screen_name;
544 $fuser->id = $twitter_id;
545 $fuser->service = 1; // Twitter
546 $fuser->created = common_sql_now();
547 $result = $fuser->insert();
550 common_debug("Failed to add new Twitter user: $twitter_id - $screen_name.");
551 common_log_db_error($fuser, 'INSERT', __FILE__);
555 common_debug("Added new Twitter user: $twitter_id - $screen_name.");
557 // common_debug(print_r($friend, true));