From 9b9d80cd97704426e54434d8777c5c15154014ad Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 25 Aug 2009 14:52:25 -0700 Subject: Pluginized Twitter settings stuff --- lib/connectsettingsaction.php | 49 +++++----- lib/router.php | 6 +- lib/twitteroauthclient.php | 220 ------------------------------------------ 3 files changed, 27 insertions(+), 248 deletions(-) delete mode 100644 lib/twitteroauthclient.php (limited to 'lib') diff --git a/lib/connectsettingsaction.php b/lib/connectsettingsaction.php index 02c468a35..7902931e0 100644 --- a/lib/connectsettingsaction.php +++ b/lib/connectsettingsaction.php @@ -98,34 +98,37 @@ class ConnectSettingsNav extends Widget function show() { - # action => array('prompt', 'title') - $menu = array(); - if (common_config('xmpp', 'enabled')) { - $menu['imsettings'] = - array(_('IM'), - _('Updates by instant messenger (IM)')); - } - if (common_config('sms', 'enabled')) { - $menu['smssettings'] = - array(_('SMS'), - _('Updates by SMS')); - } - if (common_config('twitter', 'enabled')) { - $menu['twittersettings'] = - array(_('Twitter'), - _('Twitter integration options')); - } - $action_name = $this->action->trimmed('action'); $this->action->elementStart('ul', array('class' => 'nav')); - foreach ($menu as $menuaction => $menudesc) { - $this->action->menuItem(common_local_url($menuaction), - $menudesc[0], - $menudesc[1], - $action_name === $menuaction); + if (Event::handle('StartConnectSettingsNav', array(&$this->action))) { + + # action => array('prompt', 'title') + $menu = array(); + if (common_config('xmpp', 'enabled')) { + $menu['imsettings'] = + array(_('IM'), + _('Updates by instant messenger (IM)')); + } + if (common_config('sms', 'enabled')) { + $menu['smssettings'] = + array(_('SMS'), + _('Updates by SMS')); + } + + foreach ($menu as $menuaction => $menudesc) { + $this->action->menuItem(common_local_url($menuaction), + $menudesc[0], + $menudesc[1], + $action_name === $menuaction); + } + + Event::handle('EndConnectSettingsNav', array(&$this->action)); } $this->action->elementEnd('ul'); } + } + + diff --git a/lib/router.php b/lib/router.php index 08bc0566d..5bffdb16b 100644 --- a/lib/router.php +++ b/lib/router.php @@ -86,10 +86,6 @@ class Router $m->connect('doc/:title', array('action' => 'doc')); - // Twitter - - $m->connect('twitter/authorization', array('action' => 'twitterauthorization')); - // facebook $m->connect('facebook', array('action' => 'facebookhome')); @@ -136,7 +132,7 @@ class Router // settings foreach (array('profile', 'avatar', 'password', 'im', - 'email', 'sms', 'twitter', 'userdesign', 'other') as $s) { + 'email', 'sms', 'userdesign', 'other') as $s) { $m->connect('settings/'.$s, array('action' => $s.'settings')); } diff --git a/lib/twitteroauthclient.php b/lib/twitteroauthclient.php deleted file mode 100644 index b7dc4a80c..000000000 --- a/lib/twitteroauthclient.php +++ /dev/null @@ -1,220 +0,0 @@ -. - * - * @category Integration - * @package Laconica - * @author Zach Copley - * @copyright 2008 Control Yourself, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://laconi.ca/ - */ - -if (!defined('LACONICA')) { - exit(1); -} - -/** - * Class for talking to the Twitter API with OAuth. - * - * @category Integration - * @package Laconica - * @author Zach Copley - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://laconi.ca/ - * - */ -class TwitterOAuthClient extends OAuthClient -{ - public static $requestTokenURL = 'https://twitter.com/oauth/request_token'; - public static $authorizeURL = 'https://twitter.com/oauth/authorize'; - public static $accessTokenURL = 'https://twitter.com/oauth/access_token'; - - /** - * Constructor - * - * @param string $oauth_token the user's token - * @param string $oauth_token_secret the user's token secret - * - * @return nothing - */ - function __construct($oauth_token = null, $oauth_token_secret = null) - { - $consumer_key = common_config('twitter', 'consumer_key'); - $consumer_secret = common_config('twitter', 'consumer_secret'); - - parent::__construct($consumer_key, $consumer_secret, - $oauth_token, $oauth_token_secret); - } - - // XXX: the following two functions are to support the horrible hack - // of using the credentils field in Foreign_link to store both - // the access token and token secret. This hack should go away with - // 0.9, in which we can make DB changes and add a new column for the - // token itself. - - static function packToken($token) - { - return implode(chr(0), array($token->key, $token->secret)); - } - - static function unpackToken($str) - { - $vals = explode(chr(0), $str); - return new OAuthToken($vals[0], $vals[1]); - } - - /** - * Builds a link to Twitter's endpoint for authorizing a request token - * - * @param OAuthToken $request_token token to authorize - * - * @return the link - */ - function getAuthorizeLink($request_token) - { - return parent::getAuthorizeLink(self::$authorizeURL, - $request_token, - common_local_url('twitterauthorization')); - } - - /** - * Calls Twitter's /account/verify_credentials API method - * - * @return mixed the Twitter user - */ - function verifyCredentials() - { - $url = 'https://twitter.com/account/verify_credentials.json'; - $response = $this->oAuthGet($url); - $twitter_user = json_decode($response); - return $twitter_user; - } - - /** - * Calls Twitter's /stutuses/update API method - * - * @param string $status text of the status - * @param int $in_reply_to_status_id optional id of the status it's - * a reply to - * - * @return mixed the status - */ - function statusesUpdate($status, $in_reply_to_status_id = null) - { - $url = 'https://twitter.com/statuses/update.json'; - $params = array('status' => $status, - 'in_reply_to_status_id' => $in_reply_to_status_id); - $response = $this->oAuthPost($url, $params); - $status = json_decode($response); - return $status; - } - - /** - * Calls Twitter's /stutuses/friends_timeline API method - * - * @param int $since_id show statuses after this id - * @param int $max_id show statuses before this id - * @param int $cnt number of statuses to show - * @param int $page page number - * - * @return mixed an array of statuses - */ - function statusesFriendsTimeline($since_id = null, $max_id = null, - $cnt = null, $page = null) - { - - $url = 'https://twitter.com/statuses/friends_timeline.json'; - $params = array('since_id' => $since_id, - 'max_id' => $max_id, - 'count' => $cnt, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->oAuthGet($url); - $statuses = json_decode($response); - return $statuses; - } - - /** - * Calls Twitter's /stutuses/friends API method - * - * @param int $id id of the user whom you wish to see friends of - * @param int $user_id numerical user id - * @param int $screen_name screen name - * @param int $page page number - * - * @return mixed an array of twitter users and their latest status - */ - function statusesFriends($id = null, $user_id = null, $screen_name = null, - $page = null) - { - $url = "https://twitter.com/statuses/friends.json"; - - $params = array('id' => $id, - 'user_id' => $user_id, - 'screen_name' => $screen_name, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->oAuthGet($url); - $friends = json_decode($response); - return $friends; - } - - /** - * Calls Twitter's /stutuses/friends/ids API method - * - * @param int $id id of the user whom you wish to see friends of - * @param int $user_id numerical user id - * @param int $screen_name screen name - * @param int $page page number - * - * @return mixed a list of ids, 100 per page - */ - function friendsIds($id = null, $user_id = null, $screen_name = null, - $page = null) - { - $url = "https://twitter.com/friends/ids.json"; - - $params = array('id' => $id, - 'user_id' => $user_id, - 'screen_name' => $screen_name, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->oAuthGet($url); - $ids = json_decode($response); - return $ids; - } - -} -- cgit v1.2.3-54-g00ecf From 5efe588174c71979fc1970353c9a556ea441f138 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 26 Aug 2009 00:59:06 +0000 Subject: Moved the rest of the Twitter stuff into the TwitterBridge plugin --- lib/common.php | 1 - lib/twitter.php | 258 ---------- plugins/TwitterBridge/TwitterBridgePlugin.php | 2 +- .../TwitterBridge/daemons/synctwitterfriends.php | 280 +++++++++++ .../TwitterBridge/daemons/twitterqueuehandler.php | 73 +++ .../TwitterBridge/daemons/twitterstatusfetcher.php | 559 +++++++++++++++++++++ plugins/TwitterBridge/twitter.php | 258 ++++++++++ plugins/TwitterBridge/twitterauthorization.php | 2 + plugins/TwitterBridge/twittersettings.php | 4 +- scripts/synctwitterfriends.php | 279 ---------- scripts/twitterqueuehandler.php | 74 --- scripts/twitterstatusfetcher.php | 558 -------------------- 12 files changed, 1175 insertions(+), 1173 deletions(-) delete mode 100644 lib/twitter.php create mode 100755 plugins/TwitterBridge/daemons/synctwitterfriends.php create mode 100755 plugins/TwitterBridge/daemons/twitterqueuehandler.php create mode 100755 plugins/TwitterBridge/daemons/twitterstatusfetcher.php create mode 100644 plugins/TwitterBridge/twitter.php delete mode 100755 scripts/synctwitterfriends.php delete mode 100755 scripts/twitterqueuehandler.php delete mode 100755 scripts/twitterstatusfetcher.php (limited to 'lib') diff --git a/lib/common.php b/lib/common.php index 62cf5640d..d23d9da7d 100644 --- a/lib/common.php +++ b/lib/common.php @@ -409,7 +409,6 @@ require_once INSTALLDIR.'/lib/theme.php'; require_once INSTALLDIR.'/lib/mail.php'; require_once INSTALLDIR.'/lib/subs.php'; require_once INSTALLDIR.'/lib/Shorturl_api.php'; -require_once INSTALLDIR.'/lib/twitter.php'; require_once INSTALLDIR.'/lib/clientexception.php'; require_once INSTALLDIR.'/lib/serverexception.php'; diff --git a/lib/twitter.php b/lib/twitter.php deleted file mode 100644 index 280cdb0a3..000000000 --- a/lib/twitter.php +++ /dev/null @@ -1,258 +0,0 @@ -. - */ - -if (!defined('LACONICA')) { - exit(1); -} - -define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1 - -function update_twitter_user($twitter_id, $screen_name) -{ - $uri = 'http://twitter.com/' . $screen_name; - $fuser = new Foreign_user(); - - $fuser->query('BEGIN'); - - // Dropping down to SQL because regular DB_DataObject udpate stuff doesn't seem - // to work so good with tables that have multiple column primary keys - - // Any time we update the uri for a forein user we have to make sure there - // are no dupe entries first -- unique constraint on the uri column - - $qry = 'UPDATE foreign_user set uri = \'\' WHERE uri = '; - $qry .= '\'' . $uri . '\'' . ' AND service = ' . TWITTER_SERVICE; - - $fuser->query($qry); - - // Update the user - - $qry = 'UPDATE foreign_user SET nickname = '; - $qry .= '\'' . $screen_name . '\'' . ', uri = \'' . $uri . '\' '; - $qry .= 'WHERE id = ' . $twitter_id . ' AND service = ' . TWITTER_SERVICE; - - $fuser->query('COMMIT'); - - $fuser->free(); - unset($fuser); - - return true; -} - -function add_twitter_user($twitter_id, $screen_name) -{ - - $new_uri = 'http://twitter.com/' . $screen_name; - - // Clear out any bad old foreign_users with the new user's legit URL - // This can happen when users move around or fakester accounts get - // repoed, and things like that. - - $luser = new Foreign_user(); - $luser->uri = $new_uri; - $luser->service = TWITTER_SERVICE; - $result = $luser->delete(); - - if (empty($result)) { - common_log(LOG_WARNING, - "Twitter bridge - removed invalid Twitter user squatting on uri: $new_uri"); - } - - $luser->free(); - unset($luser); - - // Otherwise, create a new Twitter user - - $fuser = new Foreign_user(); - - $fuser->nickname = $screen_name; - $fuser->uri = 'http://twitter.com/' . $screen_name; - $fuser->id = $twitter_id; - $fuser->service = TWITTER_SERVICE; - $fuser->created = common_sql_now(); - $result = $fuser->insert(); - - if (empty($result)) { - common_log(LOG_WARNING, - "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name."); - common_log_db_error($fuser, 'INSERT', __FILE__); - } else { - common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id)."); - } - - return $result; -} - -// Creates or Updates a Twitter user -function save_twitter_user($twitter_id, $screen_name) -{ - - // Check to see whether the Twitter user is already in the system, - // and update its screen name and uri if so. - - $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE); - - if (!empty($fuser)) { - - $result = true; - - // Only update if Twitter screen name has changed - - if ($fuser->nickname != $screen_name) { - $result = update_twitter_user($twitter_id, $screen_name); - - common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' . - "$fuser->id to $screen_name, was $fuser->nickname"); - } - - return $result; - - } else { - return add_twitter_user($twitter_id, $screen_name); - } - - $fuser->free(); - unset($fuser); - - return true; -} - -function is_twitter_bound($notice, $flink) { - - // Check to see if notice should go to Twitter - if (!empty($flink) && ($flink->noticesync & FOREIGN_NOTICE_SEND)) { - - // If it's not a Twitter-style reply, or if the user WANTS to send replies. - if (!preg_match('/^@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) || - ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY)) { - return true; - } - } - - return false; -} - -function broadcast_twitter($notice) -{ - $flink = Foreign_link::getByUserID($notice->profile_id, - TWITTER_SERVICE); - - if (is_twitter_bound($notice, $flink)) { - - $user = $flink->getUser(); - - // XXX: Hack to get around PHP cURL's use of @ being a a meta character - $statustxt = preg_replace('/^@/', ' @', $notice->content); - - $token = TwitterOAuthClient::unpackToken($flink->credentials); - - $client = new TwitterOAuthClient($token->key, $token->secret); - - $status = null; - - try { - $status = $client->statusesUpdate($statustxt); - } catch (OAuthClientCurlException $e) { - - if ($e->getMessage() == 'The requested URL returned error: 401') { - - $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . - 'Twitter OAuth access token.', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - - // Bad auth token! We need to delete the foreign_link - // to Twitter and inform the user. - - remove_twitter_link($flink); - return true; - - } else { - - // Some other error happened, so we should probably - // try to send again later. - - $errmsg = sprintf('cURL error trying to send notice to Twitter ' . - 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: $4$s.', - $user->nickname, $user->id, - $e->getCode(), $e->getMessage()); - common_log(LOG_WARNING, $errmsg); - - return false; - } - } - - if (empty($status)) { - - // This could represent a failure posting, - // or the Twitter API might just be behaving flakey. - - $errmsg = sprint('No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s).', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - - return false; - } - - // Notice crossed the great divide - - $msg = sprintf('Twitter bridge posted notice %s to Twitter.', - $notice->id); - common_log(LOG_INFO, $msg); - } - - return true; -} - -function remove_twitter_link($flink) -{ - $user = $flink->getUser(); - - common_log(LOG_INFO, 'Removing Twitter bridge Foreign link for ' . - "user $user->nickname (user id: $user->id)."); - - $result = $flink->delete(); - - if (empty($result)) { - common_log(LOG_ERR, 'Could not remove Twitter bridge ' . - "Foreign_link for $user->nickname (user id: $user->id)!"); - common_log_db_error($flink, 'DELETE', __FILE__); - } - - // Notify the user that her Twitter bridge is down - - if (isset($user->email)) { - - $result = mail_twitter_bridge_removed($user); - - if (!$result) { - - $msg = 'Unable to send email to notify ' . - "$user->nickname (user id: $user->id) " . - 'that their Twitter bridge link was ' . - 'removed!'; - - common_log(LOG_WARNING, $msg); - } - } - -} - diff --git a/plugins/TwitterBridge/TwitterBridgePlugin.php b/plugins/TwitterBridge/TwitterBridgePlugin.php index f7daa9a22..a8de1c664 100644 --- a/plugins/TwitterBridge/TwitterBridgePlugin.php +++ b/plugins/TwitterBridge/TwitterBridgePlugin.php @@ -90,7 +90,7 @@ class TwitterBridgePlugin extends Plugin require_once(INSTALLDIR.'/plugins/TwitterBridge/' . strtolower(mb_substr($cls, 0, -6)) . '.php'); return false; case 'TwitterOAuthClient': - require_once(INSTALLDIR.'/plugins/TwitterBridge/twitteroAuthclient.php'); + require_once(INSTALLDIR.'/plugins/TwitterBridge/twitteroauthclient.php'); return false; default: return true; diff --git a/plugins/TwitterBridge/daemons/synctwitterfriends.php b/plugins/TwitterBridge/daemons/synctwitterfriends.php new file mode 100755 index 000000000..b7be5d043 --- /dev/null +++ b/plugins/TwitterBridge/daemons/synctwitterfriends.php @@ -0,0 +1,280 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); + +$shortoptions = 'di::'; +$longoptions = array('id::', 'debug'); + +$helptext = << + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +$helptext = <<_id); + } + + /** + * Find all the Twitter foreign links for users who have requested + * automatically subscribing to their Twitter friends locally. + * + * @return array flinks an array of Foreign_link objects + */ + function getObjects() + { + $flinks = array(); + $flink = new Foreign_link(); + + $conn = &$flink->getDatabaseConnection(); + + $flink->service = TWITTER_SERVICE; + $flink->orderBy('last_friendsync'); + $flink->limit(25); // sync this many users during this run + $flink->find(); + + while ($flink->fetch()) { + if (($flink->friendsync & FOREIGN_FRIEND_RECV) == FOREIGN_FRIEND_RECV) { + $flinks[] = clone($flink); + } + } + + $conn->disconnect(); + + global $_DB_DATAOBJECT; + unset($_DB_DATAOBJECT['CONNECTIONS']); + + return $flinks; + } + + function childTask($flink) { + + // Each child ps needs its own DB connection + + // Note: DataObject::getDatabaseConnection() creates + // a new connection if there isn't one already + + $conn = &$flink->getDatabaseConnection(); + + $this->subscribeTwitterFriends($flink); + + $flink->last_friendsync = common_sql_now(); + $flink->update(); + + $conn->disconnect(); + + // XXX: Couldn't find a less brutal way to blow + // away a cached connection + + global $_DB_DATAOBJECT; + unset($_DB_DATAOBJECT['CONNECTIONS']); + } + + function fetchTwitterFriends($flink) + { + $friends = array(); + + $token = TwitterOAuthClient::unpackToken($flink->credentials); + + $client = new TwitterOAuthClient($token->key, $token->secret); + + try { + $friends_ids = $client->friendsIds(); + } catch (OAuthCurlException $e) { + common_log(LOG_WARNING, $this->name() . + ' - cURL error getting friend ids ' . + $e->getCode() . ' - ' . $e->getMessage()); + return $friends; + } + + if (empty($friends_ids)) { + common_debug($this->name() . + " - Twitter user $flink->foreign_id " . + 'doesn\'t have any friends!'); + return $friends; + } + + common_debug($this->name() . ' - Twitter\'s API says Twitter user id ' . + "$flink->foreign_id has " . + count($friends_ids) . ' friends.'); + + // Calculate how many pages to get... + $pages = ceil(count($friends_ids) / 100); + + if ($pages == 0) { + common_debug($this->name() . " - $user seems to have no friends."); + } + + for ($i = 1; $i <= $pages; $i++) { + + try { + $more_friends = $client->statusesFriends(null, null, null, $i); + } catch (OAuthCurlException $e) { + common_log(LOG_WARNING, $this->name() . + ' - cURL error getting Twitter statuses/friends ' . + "page $i - " . $e->getCode() . ' - ' . + $e->getMessage()); + } + + if (empty($more_friends)) { + common_log(LOG_WARNING, $this->name() . + " - Couldn't retrieve page $i " . + "of Twitter user $flink->foreign_id friends."); + continue; + } else { + $friends = array_merge($friends, $more_friends); + } + } + + return $friends; + } + + function subscribeTwitterFriends($flink) + { + $friends = $this->fetchTwitterFriends($flink); + + if (empty($friends)) { + common_debug($this->name() . + ' - Couldn\'t get friends from Twitter for ' . + "Twitter user $flink->foreign_id."); + return false; + } + + $user = $flink->getUser(); + + foreach ($friends as $friend) { + + $friend_name = $friend->screen_name; + $friend_id = (int) $friend->id; + + // Update or create the Foreign_user record for each + // Twitter friend + + if (!save_twitter_user($friend_id, $friend_name)) { + common_log(LOG_WARNING, $this-name() . + " - Couldn't save $screen_name's friend, $friend_name."); + continue; + } + + // Check to see if there's a related local user + + $friend_flink = Foreign_link::getByForeignID($friend_id, + TWITTER_SERVICE); + + if (!empty($friend_flink)) { + + // Get associated user and subscribe her + + $friend_user = User::staticGet('id', $friend_flink->user_id); + + if (!empty($friend_user)) { + $result = subs_subscribe_to($user, $friend_user); + + if ($result === true) { + common_log(LOG_INFO, + $this->name() . ' - Subscribed ' . + "$friend_user->nickname to $user->nickname."); + } else { + common_debug($this->name() . + ' - Tried subscribing ' . + "$friend_user->nickname to $user->nickname - " . + $result); + } + } + } + } + + return true; + } + +} + +$id = null; +$debug = null; + +if (have_option('i')) { + $id = get_option_value('i'); +} else if (have_option('--id')) { + $id = get_option_value('--id'); +} else if (count($args) > 0) { + $id = $args[0]; +} else { + $id = null; +} + +if (have_option('d') || have_option('debug')) { + $debug = true; +} + +$syncer = new SyncTwitterFriendsDaemon($id, 60, 2, $debug); +$syncer->runOnce(); + diff --git a/plugins/TwitterBridge/daemons/twitterqueuehandler.php b/plugins/TwitterBridge/daemons/twitterqueuehandler.php new file mode 100755 index 000000000..9aa9d1ed0 --- /dev/null +++ b/plugins/TwitterBridge/daemons/twitterqueuehandler.php @@ -0,0 +1,73 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); + +$shortoptions = 'i::'; +$longoptions = array('id::'); + +$helptext = <<log(LOG_INFO, "INITIALIZE"); + return true; + } + + function handle_notice($notice) + { + return broadcast_twitter($notice); + } + + function finish() + { + } + +} + +if (have_option('i')) { + $id = get_option_value('i'); +} else if (have_option('--id')) { + $id = get_option_value('--id'); +} else if (count($args) > 0) { + $id = $args[0]; +} else { + $id = null; +} + +$handler = new TwitterQueueHandler($id); + +$handler->runOnce(); diff --git a/plugins/TwitterBridge/daemons/twitterstatusfetcher.php b/plugins/TwitterBridge/daemons/twitterstatusfetcher.php new file mode 100755 index 000000000..3023bf423 --- /dev/null +++ b/plugins/TwitterBridge/daemons/twitterstatusfetcher.php @@ -0,0 +1,559 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); + +// Tune number of processes and how often to poll Twitter +// XXX: Should these things be in config.php? +define('MAXCHILDREN', 2); +define('POLL_INTERVAL', 60); // in seconds + +$shortoptions = 'di::'; +$longoptions = array('id::', 'debug'); + +$helptext = << + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +// NOTE: an Avatar path MUST be set in config.php for this +// script to work: e.g.: $config['avatar']['path'] = '/laconica/avatar'; + +class TwitterStatusFetcher extends ParallelizingDaemon +{ + /** + * Constructor + * + * @param string $id the name/id of this daemon + * @param int $interval sleep this long before doing everything again + * @param int $max_children maximum number of child processes at a time + * @param boolean $debug debug output flag + * + * @return void + * + **/ + function __construct($id = null, $interval = 60, + $max_children = 2, $debug = null) + { + parent::__construct($id, $interval, $max_children, $debug); + } + + /** + * Name of this daemon + * + * @return string Name of the daemon. + */ + + function name() + { + return ('twitterstatusfetcher.'.$this->_id); + } + + /** + * Find all the Twitter foreign links for users who have requested + * importing of their friends' timelines + * + * @return array flinks an array of Foreign_link objects + */ + + function getObjects() + { + global $_DB_DATAOBJECT; + + $flink = new Foreign_link(); + $conn = &$flink->getDatabaseConnection(); + + $flink->service = TWITTER_SERVICE; + $flink->orderBy('last_noticesync'); + $flink->find(); + + $flinks = array(); + + while ($flink->fetch()) { + + if (($flink->noticesync & FOREIGN_NOTICE_RECV) == + FOREIGN_NOTICE_RECV) { + $flinks[] = clone($flink); + } + } + + $flink->free(); + unset($flink); + + $conn->disconnect(); + unset($_DB_DATAOBJECT['CONNECTIONS']); + + return $flinks; + } + + function childTask($flink) { + + // Each child ps needs its own DB connection + + // Note: DataObject::getDatabaseConnection() creates + // a new connection if there isn't one already + + $conn = &$flink->getDatabaseConnection(); + + $this->getTimeline($flink); + + $flink->last_friendsync = common_sql_now(); + $flink->update(); + + $conn->disconnect(); + + // XXX: Couldn't find a less brutal way to blow + // away a cached connection + + global $_DB_DATAOBJECT; + unset($_DB_DATAOBJECT['CONNECTIONS']); + } + + function getTimeline($flink) + { + if (empty($flink)) { + common_log(LOG_WARNING, $this->name() . + " - Can't retrieve Foreign_link for foreign ID $fid"); + return; + } + + common_debug($this->name() . ' - Trying to get timeline for Twitter user ' . + $flink->foreign_id); + + // XXX: Biggest remaining issue - How do we know at which status + // to start importing? How many statuses? Right now I'm going + // with the default last 20. + + $token = TwitterOAuthClient::unpackToken($flink->credentials); + + $client = new TwitterOAuthClient($token->key, $token->secret); + + $timeline = null; + + try { + $timeline = $client->statusesFriendsTimeline(); + } catch (OAuthClientCurlException $e) { + common_log(LOG_WARNING, $this->name() . + ' - OAuth client unable to get friends timeline for user ' . + $flink->user_id . ' - code: ' . + $e->getCode() . 'msg: ' . $e->getMessage()); + } + + if (empty($timeline)) { + common_log(LOG_WARNING, $this->name() . " - Empty timeline."); + return; + } + + // Reverse to preserve order + + foreach (array_reverse($timeline) as $status) { + + // Hacktastic: filter out stuff coming from this Laconica + + $source = mb_strtolower(common_config('integration', 'source')); + + if (preg_match("/$source/", mb_strtolower($status->source))) { + common_debug($this->name() . ' - Skipping import of status ' . + $status->id . ' with source ' . $source); + continue; + } + + $this->saveStatus($status, $flink); + } + + // Okay, record the time we synced with Twitter for posterity + + $flink->last_noticesync = common_sql_now(); + $flink->update(); + } + + function saveStatus($status, $flink) + { + $id = $this->ensureProfile($status->user); + + $profile = Profile::staticGet($id); + + if (empty($profile)) { + common_log(LOG_ERR, $this->name() . + ' - Problem saving notice. No associated Profile.'); + return null; + } + + // XXX: change of screen name? + + $uri = 'http://twitter.com/' . $status->user->screen_name . + '/status/' . $status->id; + + $notice = Notice::staticGet('uri', $uri); + + // check to see if we've already imported the status + + if (empty($notice)) { + + $notice = new Notice(); + + $notice->profile_id = $id; + $notice->uri = $uri; + $notice->created = strftime('%Y-%m-%d %H:%M:%S', + strtotime($status->created_at)); + $notice->content = common_shorten_links($status->text); // XXX + $notice->rendered = common_render_content($notice->content, $notice); + $notice->source = 'twitter'; + $notice->reply_to = null; // XXX: lookup reply + $notice->is_local = Notice::GATEWAY; + + if (Event::handle('StartNoticeSave', array(&$notice))) { + $id = $notice->insert(); + Event::handle('EndNoticeSave', array($notice)); + } + } + + if (!Notice_inbox::pkeyGet(array('notice_id' => $notice->id, + 'user_id' => $flink->user_id))) { + // Add to inbox + $inbox = new Notice_inbox(); + + $inbox->user_id = $flink->user_id; + $inbox->notice_id = $notice->id; + $inbox->created = $notice->created; + $inbox->source = NOTICE_INBOX_SOURCE_GATEWAY; // From a private source + + $inbox->insert(); + } + } + + function ensureProfile($user) + { + // check to see if there's already a profile for this user + + $profileurl = 'http://twitter.com/' . $user->screen_name; + $profile = Profile::staticGet('profileurl', $profileurl); + + if (!empty($profile)) { + common_debug($this->name() . + " - Profile for $profile->nickname found."); + + // Check to see if the user's Avatar has changed + + $this->checkAvatar($user, $profile); + return $profile->id; + + } else { + common_debug($this->name() . ' - Adding profile and remote profile ' . + "for Twitter user: $profileurl."); + + $profile = new Profile(); + $profile->query("BEGIN"); + + $profile->nickname = $user->screen_name; + $profile->fullname = $user->name; + $profile->homepage = $user->url; + $profile->bio = $user->description; + $profile->location = $user->location; + $profile->profileurl = $profileurl; + $profile->created = common_sql_now(); + + $id = $profile->insert(); + + if (empty($id)) { + common_log_db_error($profile, 'INSERT', __FILE__); + $profile->query("ROLLBACK"); + return false; + } + + // check for remote profile + + $remote_pro = Remote_profile::staticGet('uri', $profileurl); + + if (empty($remote_pro)) { + + $remote_pro = new Remote_profile(); + + $remote_pro->id = $id; + $remote_pro->uri = $profileurl; + $remote_pro->created = common_sql_now(); + + $rid = $remote_pro->insert(); + + if (empty($rid)) { + common_log_db_error($profile, 'INSERT', __FILE__); + $profile->query("ROLLBACK"); + return false; + } + } + + $profile->query("COMMIT"); + + $this->saveAvatars($user, $id); + + return $id; + } + } + + function checkAvatar($twitter_user, $profile) + { + global $config; + + $path_parts = pathinfo($twitter_user->profile_image_url); + + $newname = 'Twitter_' . $twitter_user->id . '_' . + $path_parts['basename']; + + $oldname = $profile->getAvatar(48)->filename; + + if ($newname != $oldname) { + common_debug($this->name() . ' - Avatar for Twitter user ' . + "$profile->nickname has changed."); + common_debug($this->name() . " - old: $oldname new: $newname"); + + $this->updateAvatars($twitter_user, $profile); + } + + if ($this->missingAvatarFile($profile)) { + common_debug($this->name() . ' - Twitter user ' . + $profile->nickname . + ' is missing one or more local avatars.'); + common_debug($this->name() ." - old: $oldname new: $newname"); + + $this->updateAvatars($twitter_user, $profile); + } + + } + + function updateAvatars($twitter_user, $profile) { + + global $config; + + $path_parts = pathinfo($twitter_user->profile_image_url); + + $img_root = substr($path_parts['basename'], 0, -11); + $ext = $path_parts['extension']; + $mediatype = $this->getMediatype($ext); + + foreach (array('mini', 'normal', 'bigger') as $size) { + $url = $path_parts['dirname'] . '/' . + $img_root . '_' . $size . ".$ext"; + $filename = 'Twitter_' . $twitter_user->id . '_' . + $img_root . "_$size.$ext"; + + $this->updateAvatar($profile->id, $size, $mediatype, $filename); + $this->fetchAvatar($url, $filename); + } + } + + function missingAvatarFile($profile) { + + foreach (array(24, 48, 73) as $size) { + + $filename = $profile->getAvatar($size)->filename; + $avatarpath = Avatar::path($filename); + + if (file_exists($avatarpath) == FALSE) { + return true; + } + } + + return false; + } + + function getMediatype($ext) + { + $mediatype = null; + + switch (strtolower($ext)) { + case 'jpg': + $mediatype = 'image/jpg'; + break; + case 'gif': + $mediatype = 'image/gif'; + break; + default: + $mediatype = 'image/png'; + } + + return $mediatype; + } + + function saveAvatars($user, $id) + { + global $config; + + $path_parts = pathinfo($user->profile_image_url); + $ext = $path_parts['extension']; + $end = strlen('_normal' . $ext); + $img_root = substr($path_parts['basename'], 0, -($end+1)); + $mediatype = $this->getMediatype($ext); + + foreach (array('mini', 'normal', 'bigger') as $size) { + $url = $path_parts['dirname'] . '/' . + $img_root . '_' . $size . ".$ext"; + $filename = 'Twitter_' . $user->id . '_' . + $img_root . "_$size.$ext"; + + if ($this->fetchAvatar($url, $filename)) { + $this->newAvatar($id, $size, $mediatype, $filename); + } else { + common_log(LOG_WARNING, $this->id() . + " - Problem fetching Avatar: $url"); + } + } + } + + function updateAvatar($profile_id, $size, $mediatype, $filename) { + + common_debug($this->name() . " - Updating avatar: $size"); + + $profile = Profile::staticGet($profile_id); + + if (empty($profile)) { + common_debug($this->name() . " - Couldn't get profile: $profile_id!"); + return; + } + + $sizes = array('mini' => 24, 'normal' => 48, 'bigger' => 73); + $avatar = $profile->getAvatar($sizes[$size]); + + // Delete the avatar, if present + + if ($avatar) { + $avatar->delete(); + } + + $this->newAvatar($profile->id, $size, $mediatype, $filename); + } + + function newAvatar($profile_id, $size, $mediatype, $filename) + { + global $config; + + $avatar = new Avatar(); + $avatar->profile_id = $profile_id; + + switch($size) { + case 'mini': + $avatar->width = 24; + $avatar->height = 24; + break; + case 'normal': + $avatar->width = 48; + $avatar->height = 48; + break; + default: + + // Note: Twitter's big avatars are a different size than + // Laconica's (Laconica's = 96) + + $avatar->width = 73; + $avatar->height = 73; + } + + $avatar->original = 0; // we don't have the original + $avatar->mediatype = $mediatype; + $avatar->filename = $filename; + $avatar->url = Avatar::url($filename); + + common_debug($this->name() . " - New filename: $avatar->url"); + + $avatar->created = common_sql_now(); + + $id = $avatar->insert(); + + if (empty($id)) { + common_log_db_error($avatar, 'INSERT', __FILE__); + return null; + } + + common_debug($this->name() . + " - Saved new $size avatar for $profile_id."); + + return $id; + } + + function fetchAvatar($url, $filename) + { + $avatar_dir = INSTALLDIR . '/avatar/'; + + $avatarfile = $avatar_dir . $filename; + + $out = fopen($avatarfile, 'wb'); + if (!$out) { + common_log(LOG_WARNING, $this->name() . + " - Couldn't open file $filename"); + return false; + } + + common_debug($this->name() . " - Fetching Twitter avatar: $url"); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_FILE, $out); + curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); + $result = curl_exec($ch); + curl_close($ch); + + fclose($out); + + return $result; + } +} + +$id = null; +$debug = null; + +if (have_option('i')) { + $id = get_option_value('i'); +} else if (have_option('--id')) { + $id = get_option_value('--id'); +} else if (count($args) > 0) { + $id = $args[0]; +} else { + $id = null; +} + +if (have_option('d') || have_option('debug')) { + $debug = true; +} + +$fetcher = new TwitterStatusFetcher($id, 60, 2, $debug); +$fetcher->runOnce(); + diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php new file mode 100644 index 000000000..280cdb0a3 --- /dev/null +++ b/plugins/TwitterBridge/twitter.php @@ -0,0 +1,258 @@ +. + */ + +if (!defined('LACONICA')) { + exit(1); +} + +define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1 + +function update_twitter_user($twitter_id, $screen_name) +{ + $uri = 'http://twitter.com/' . $screen_name; + $fuser = new Foreign_user(); + + $fuser->query('BEGIN'); + + // Dropping down to SQL because regular DB_DataObject udpate stuff doesn't seem + // to work so good with tables that have multiple column primary keys + + // Any time we update the uri for a forein user we have to make sure there + // are no dupe entries first -- unique constraint on the uri column + + $qry = 'UPDATE foreign_user set uri = \'\' WHERE uri = '; + $qry .= '\'' . $uri . '\'' . ' AND service = ' . TWITTER_SERVICE; + + $fuser->query($qry); + + // Update the user + + $qry = 'UPDATE foreign_user SET nickname = '; + $qry .= '\'' . $screen_name . '\'' . ', uri = \'' . $uri . '\' '; + $qry .= 'WHERE id = ' . $twitter_id . ' AND service = ' . TWITTER_SERVICE; + + $fuser->query('COMMIT'); + + $fuser->free(); + unset($fuser); + + return true; +} + +function add_twitter_user($twitter_id, $screen_name) +{ + + $new_uri = 'http://twitter.com/' . $screen_name; + + // Clear out any bad old foreign_users with the new user's legit URL + // This can happen when users move around or fakester accounts get + // repoed, and things like that. + + $luser = new Foreign_user(); + $luser->uri = $new_uri; + $luser->service = TWITTER_SERVICE; + $result = $luser->delete(); + + if (empty($result)) { + common_log(LOG_WARNING, + "Twitter bridge - removed invalid Twitter user squatting on uri: $new_uri"); + } + + $luser->free(); + unset($luser); + + // Otherwise, create a new Twitter user + + $fuser = new Foreign_user(); + + $fuser->nickname = $screen_name; + $fuser->uri = 'http://twitter.com/' . $screen_name; + $fuser->id = $twitter_id; + $fuser->service = TWITTER_SERVICE; + $fuser->created = common_sql_now(); + $result = $fuser->insert(); + + if (empty($result)) { + common_log(LOG_WARNING, + "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name."); + common_log_db_error($fuser, 'INSERT', __FILE__); + } else { + common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id)."); + } + + return $result; +} + +// Creates or Updates a Twitter user +function save_twitter_user($twitter_id, $screen_name) +{ + + // Check to see whether the Twitter user is already in the system, + // and update its screen name and uri if so. + + $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE); + + if (!empty($fuser)) { + + $result = true; + + // Only update if Twitter screen name has changed + + if ($fuser->nickname != $screen_name) { + $result = update_twitter_user($twitter_id, $screen_name); + + common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' . + "$fuser->id to $screen_name, was $fuser->nickname"); + } + + return $result; + + } else { + return add_twitter_user($twitter_id, $screen_name); + } + + $fuser->free(); + unset($fuser); + + return true; +} + +function is_twitter_bound($notice, $flink) { + + // Check to see if notice should go to Twitter + if (!empty($flink) && ($flink->noticesync & FOREIGN_NOTICE_SEND)) { + + // If it's not a Twitter-style reply, or if the user WANTS to send replies. + if (!preg_match('/^@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) || + ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY)) { + return true; + } + } + + return false; +} + +function broadcast_twitter($notice) +{ + $flink = Foreign_link::getByUserID($notice->profile_id, + TWITTER_SERVICE); + + if (is_twitter_bound($notice, $flink)) { + + $user = $flink->getUser(); + + // XXX: Hack to get around PHP cURL's use of @ being a a meta character + $statustxt = preg_replace('/^@/', ' @', $notice->content); + + $token = TwitterOAuthClient::unpackToken($flink->credentials); + + $client = new TwitterOAuthClient($token->key, $token->secret); + + $status = null; + + try { + $status = $client->statusesUpdate($statustxt); + } catch (OAuthClientCurlException $e) { + + if ($e->getMessage() == 'The requested URL returned error: 401') { + + $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . + 'Twitter OAuth access token.', + $user->nickname, $user->id); + common_log(LOG_WARNING, $errmsg); + + // Bad auth token! We need to delete the foreign_link + // to Twitter and inform the user. + + remove_twitter_link($flink); + return true; + + } else { + + // Some other error happened, so we should probably + // try to send again later. + + $errmsg = sprintf('cURL error trying to send notice to Twitter ' . + 'for user %1$s (user id: %2$s) - ' . + 'code: %3$s message: $4$s.', + $user->nickname, $user->id, + $e->getCode(), $e->getMessage()); + common_log(LOG_WARNING, $errmsg); + + return false; + } + } + + if (empty($status)) { + + // This could represent a failure posting, + // or the Twitter API might just be behaving flakey. + + $errmsg = sprint('No data returned by Twitter API when ' . + 'trying to send update for %1$s (user id %2$s).', + $user->nickname, $user->id); + common_log(LOG_WARNING, $errmsg); + + return false; + } + + // Notice crossed the great divide + + $msg = sprintf('Twitter bridge posted notice %s to Twitter.', + $notice->id); + common_log(LOG_INFO, $msg); + } + + return true; +} + +function remove_twitter_link($flink) +{ + $user = $flink->getUser(); + + common_log(LOG_INFO, 'Removing Twitter bridge Foreign link for ' . + "user $user->nickname (user id: $user->id)."); + + $result = $flink->delete(); + + if (empty($result)) { + common_log(LOG_ERR, 'Could not remove Twitter bridge ' . + "Foreign_link for $user->nickname (user id: $user->id)!"); + common_log_db_error($flink, 'DELETE', __FILE__); + } + + // Notify the user that her Twitter bridge is down + + if (isset($user->email)) { + + $result = mail_twitter_bridge_removed($user); + + if (!$result) { + + $msg = 'Unable to send email to notify ' . + "$user->nickname (user id: $user->id) " . + 'that their Twitter bridge link was ' . + 'removed!'; + + common_log(LOG_WARNING, $msg); + } + } + +} + diff --git a/plugins/TwitterBridge/twitterauthorization.php b/plugins/TwitterBridge/twitterauthorization.php index b04f35327..a54528434 100644 --- a/plugins/TwitterBridge/twitterauthorization.php +++ b/plugins/TwitterBridge/twitterauthorization.php @@ -31,6 +31,8 @@ if (!defined('LACONICA')) { exit(1); } +require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php'; + /** * Class for doing OAuth authentication against Twitter * diff --git a/plugins/TwitterBridge/twittersettings.php b/plugins/TwitterBridge/twittersettings.php index a3e02e125..b3d4a971f 100644 --- a/plugins/TwitterBridge/twittersettings.php +++ b/plugins/TwitterBridge/twittersettings.php @@ -31,8 +31,8 @@ if (!defined('LACONICA')) { exit(1); } -require_once INSTALLDIR.'/lib/connectsettingsaction.php'; -require_once INSTALLDIR.'/lib/twitter.php'; +require_once INSTALLDIR . '/lib/connectsettingsaction.php'; +require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php'; /** * Settings for Twitter integration diff --git a/scripts/synctwitterfriends.php b/scripts/synctwitterfriends.php deleted file mode 100755 index 2de464bcc..000000000 --- a/scripts/synctwitterfriends.php +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/env php -. - */ - -define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); - -$shortoptions = 'di::'; -$longoptions = array('id::', 'debug'); - -$helptext = << - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://laconi.ca/ - */ - -$helptext = <<_id); - } - - /** - * Find all the Twitter foreign links for users who have requested - * automatically subscribing to their Twitter friends locally. - * - * @return array flinks an array of Foreign_link objects - */ - function getObjects() - { - $flinks = array(); - $flink = new Foreign_link(); - - $conn = &$flink->getDatabaseConnection(); - - $flink->service = TWITTER_SERVICE; - $flink->orderBy('last_friendsync'); - $flink->limit(25); // sync this many users during this run - $flink->find(); - - while ($flink->fetch()) { - if (($flink->friendsync & FOREIGN_FRIEND_RECV) == FOREIGN_FRIEND_RECV) { - $flinks[] = clone($flink); - } - } - - $conn->disconnect(); - - global $_DB_DATAOBJECT; - unset($_DB_DATAOBJECT['CONNECTIONS']); - - return $flinks; - } - - function childTask($flink) { - - // Each child ps needs its own DB connection - - // Note: DataObject::getDatabaseConnection() creates - // a new connection if there isn't one already - - $conn = &$flink->getDatabaseConnection(); - - $this->subscribeTwitterFriends($flink); - - $flink->last_friendsync = common_sql_now(); - $flink->update(); - - $conn->disconnect(); - - // XXX: Couldn't find a less brutal way to blow - // away a cached connection - - global $_DB_DATAOBJECT; - unset($_DB_DATAOBJECT['CONNECTIONS']); - } - - function fetchTwitterFriends($flink) - { - $friends = array(); - - $token = TwitterOAuthClient::unpackToken($flink->credentials); - - $client = new TwitterOAuthClient($token->key, $token->secret); - - try { - $friends_ids = $client->friendsIds(); - } catch (OAuthCurlException $e) { - common_log(LOG_WARNING, $this->name() . - ' - cURL error getting friend ids ' . - $e->getCode() . ' - ' . $e->getMessage()); - return $friends; - } - - if (empty($friends_ids)) { - common_debug($this->name() . - " - Twitter user $flink->foreign_id " . - 'doesn\'t have any friends!'); - return $friends; - } - - common_debug($this->name() . ' - Twitter\'s API says Twitter user id ' . - "$flink->foreign_id has " . - count($friends_ids) . ' friends.'); - - // Calculate how many pages to get... - $pages = ceil(count($friends_ids) / 100); - - if ($pages == 0) { - common_debug($this->name() . " - $user seems to have no friends."); - } - - for ($i = 1; $i <= $pages; $i++) { - - try { - $more_friends = $client->statusesFriends(null, null, null, $i); - } catch (OAuthCurlException $e) { - common_log(LOG_WARNING, $this->name() . - ' - cURL error getting Twitter statuses/friends ' . - "page $i - " . $e->getCode() . ' - ' . - $e->getMessage()); - } - - if (empty($more_friends)) { - common_log(LOG_WARNING, $this->name() . - " - Couldn't retrieve page $i " . - "of Twitter user $flink->foreign_id friends."); - continue; - } else { - $friends = array_merge($friends, $more_friends); - } - } - - return $friends; - } - - function subscribeTwitterFriends($flink) - { - $friends = $this->fetchTwitterFriends($flink); - - if (empty($friends)) { - common_debug($this->name() . - ' - Couldn\'t get friends from Twitter for ' . - "Twitter user $flink->foreign_id."); - return false; - } - - $user = $flink->getUser(); - - foreach ($friends as $friend) { - - $friend_name = $friend->screen_name; - $friend_id = (int) $friend->id; - - // Update or create the Foreign_user record for each - // Twitter friend - - if (!save_twitter_user($friend_id, $friend_name)) { - common_log(LOG_WARNING, $this-name() . - " - Couldn't save $screen_name's friend, $friend_name."); - continue; - } - - // Check to see if there's a related local user - - $friend_flink = Foreign_link::getByForeignID($friend_id, - TWITTER_SERVICE); - - if (!empty($friend_flink)) { - - // Get associated user and subscribe her - - $friend_user = User::staticGet('id', $friend_flink->user_id); - - if (!empty($friend_user)) { - $result = subs_subscribe_to($user, $friend_user); - - if ($result === true) { - common_log(LOG_INFO, - $this->name() . ' - Subscribed ' . - "$friend_user->nickname to $user->nickname."); - } else { - common_debug($this->name() . - ' - Tried subscribing ' . - "$friend_user->nickname to $user->nickname - " . - $result); - } - } - } - } - - return true; - } - -} - -$id = null; -$debug = null; - -if (have_option('i')) { - $id = get_option_value('i'); -} else if (have_option('--id')) { - $id = get_option_value('--id'); -} else if (count($args) > 0) { - $id = $args[0]; -} else { - $id = null; -} - -if (have_option('d') || have_option('debug')) { - $debug = true; -} - -$syncer = new SyncTwitterFriendsDaemon($id, 60, 2, $debug); -$syncer->runOnce(); - diff --git a/scripts/twitterqueuehandler.php b/scripts/twitterqueuehandler.php deleted file mode 100755 index 00e735d98..000000000 --- a/scripts/twitterqueuehandler.php +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env php -. - */ - -define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); - -$shortoptions = 'i::'; -$longoptions = array('id::'); - -$helptext = <<log(LOG_INFO, "INITIALIZE"); - return true; - } - - function handle_notice($notice) - { - return broadcast_twitter($notice); - } - - function finish() - { - } - -} - -if (have_option('i')) { - $id = get_option_value('i'); -} else if (have_option('--id')) { - $id = get_option_value('--id'); -} else if (count($args) > 0) { - $id = $args[0]; -} else { - $id = null; -} - -$handler = new TwitterQueueHandler($id); - -$handler->runOnce(); diff --git a/scripts/twitterstatusfetcher.php b/scripts/twitterstatusfetcher.php deleted file mode 100755 index f5289c5f4..000000000 --- a/scripts/twitterstatusfetcher.php +++ /dev/null @@ -1,558 +0,0 @@ -#!/usr/bin/env php -. - */ - -define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); - -// Tune number of processes and how often to poll Twitter -// XXX: Should these things be in config.php? -define('MAXCHILDREN', 2); -define('POLL_INTERVAL', 60); // in seconds - -$shortoptions = 'di::'; -$longoptions = array('id::', 'debug'); - -$helptext = << - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://laconi.ca/ - */ - -// NOTE: an Avatar path MUST be set in config.php for this -// script to work: e.g.: $config['avatar']['path'] = '/laconica/avatar'; - -class TwitterStatusFetcher extends ParallelizingDaemon -{ - /** - * Constructor - * - * @param string $id the name/id of this daemon - * @param int $interval sleep this long before doing everything again - * @param int $max_children maximum number of child processes at a time - * @param boolean $debug debug output flag - * - * @return void - * - **/ - function __construct($id = null, $interval = 60, - $max_children = 2, $debug = null) - { - parent::__construct($id, $interval, $max_children, $debug); - } - - /** - * Name of this daemon - * - * @return string Name of the daemon. - */ - - function name() - { - return ('twitterstatusfetcher.'.$this->_id); - } - - /** - * Find all the Twitter foreign links for users who have requested - * importing of their friends' timelines - * - * @return array flinks an array of Foreign_link objects - */ - - function getObjects() - { - global $_DB_DATAOBJECT; - - $flink = new Foreign_link(); - $conn = &$flink->getDatabaseConnection(); - - $flink->service = TWITTER_SERVICE; - $flink->orderBy('last_noticesync'); - $flink->find(); - - $flinks = array(); - - while ($flink->fetch()) { - - if (($flink->noticesync & FOREIGN_NOTICE_RECV) == - FOREIGN_NOTICE_RECV) { - $flinks[] = clone($flink); - } - } - - $flink->free(); - unset($flink); - - $conn->disconnect(); - unset($_DB_DATAOBJECT['CONNECTIONS']); - - return $flinks; - } - - function childTask($flink) { - - // Each child ps needs its own DB connection - - // Note: DataObject::getDatabaseConnection() creates - // a new connection if there isn't one already - - $conn = &$flink->getDatabaseConnection(); - - $this->getTimeline($flink); - - $flink->last_friendsync = common_sql_now(); - $flink->update(); - - $conn->disconnect(); - - // XXX: Couldn't find a less brutal way to blow - // away a cached connection - - global $_DB_DATAOBJECT; - unset($_DB_DATAOBJECT['CONNECTIONS']); - } - - function getTimeline($flink) - { - if (empty($flink)) { - common_log(LOG_WARNING, $this->name() . - " - Can't retrieve Foreign_link for foreign ID $fid"); - return; - } - - common_debug($this->name() . ' - Trying to get timeline for Twitter user ' . - $flink->foreign_id); - - // XXX: Biggest remaining issue - How do we know at which status - // to start importing? How many statuses? Right now I'm going - // with the default last 20. - - $token = TwitterOAuthClient::unpackToken($flink->credentials); - - $client = new TwitterOAuthClient($token->key, $token->secret); - - $timeline = null; - - try { - $timeline = $client->statusesFriendsTimeline(); - } catch (OAuthClientCurlException $e) { - common_log(LOG_WARNING, $this->name() . - ' - OAuth client unable to get friends timeline for user ' . - $flink->user_id . ' - code: ' . - $e->getCode() . 'msg: ' . $e->getMessage()); - } - - if (empty($timeline)) { - common_log(LOG_WARNING, $this->name() . " - Empty timeline."); - return; - } - - // Reverse to preserve order - - foreach (array_reverse($timeline) as $status) { - - // Hacktastic: filter out stuff coming from this Laconica - - $source = mb_strtolower(common_config('integration', 'source')); - - if (preg_match("/$source/", mb_strtolower($status->source))) { - common_debug($this->name() . ' - Skipping import of status ' . - $status->id . ' with source ' . $source); - continue; - } - - $this->saveStatus($status, $flink); - } - - // Okay, record the time we synced with Twitter for posterity - - $flink->last_noticesync = common_sql_now(); - $flink->update(); - } - - function saveStatus($status, $flink) - { - $id = $this->ensureProfile($status->user); - - $profile = Profile::staticGet($id); - - if (empty($profile)) { - common_log(LOG_ERR, $this->name() . - ' - Problem saving notice. No associated Profile.'); - return null; - } - - // XXX: change of screen name? - - $uri = 'http://twitter.com/' . $status->user->screen_name . - '/status/' . $status->id; - - $notice = Notice::staticGet('uri', $uri); - - // check to see if we've already imported the status - - if (empty($notice)) { - - $notice = new Notice(); - - $notice->profile_id = $id; - $notice->uri = $uri; - $notice->created = strftime('%Y-%m-%d %H:%M:%S', - strtotime($status->created_at)); - $notice->content = common_shorten_links($status->text); // XXX - $notice->rendered = common_render_content($notice->content, $notice); - $notice->source = 'twitter'; - $notice->reply_to = null; // XXX: lookup reply - $notice->is_local = Notice::GATEWAY; - - if (Event::handle('StartNoticeSave', array(&$notice))) { - $id = $notice->insert(); - Event::handle('EndNoticeSave', array($notice)); - } - } - - if (!Notice_inbox::pkeyGet(array('notice_id' => $notice->id, - 'user_id' => $flink->user_id))) { - // Add to inbox - $inbox = new Notice_inbox(); - - $inbox->user_id = $flink->user_id; - $inbox->notice_id = $notice->id; - $inbox->created = $notice->created; - $inbox->source = NOTICE_INBOX_SOURCE_GATEWAY; // From a private source - - $inbox->insert(); - } - } - - function ensureProfile($user) - { - // check to see if there's already a profile for this user - - $profileurl = 'http://twitter.com/' . $user->screen_name; - $profile = Profile::staticGet('profileurl', $profileurl); - - if (!empty($profile)) { - common_debug($this->name() . - " - Profile for $profile->nickname found."); - - // Check to see if the user's Avatar has changed - - $this->checkAvatar($user, $profile); - return $profile->id; - - } else { - common_debug($this->name() . ' - Adding profile and remote profile ' . - "for Twitter user: $profileurl."); - - $profile = new Profile(); - $profile->query("BEGIN"); - - $profile->nickname = $user->screen_name; - $profile->fullname = $user->name; - $profile->homepage = $user->url; - $profile->bio = $user->description; - $profile->location = $user->location; - $profile->profileurl = $profileurl; - $profile->created = common_sql_now(); - - $id = $profile->insert(); - - if (empty($id)) { - common_log_db_error($profile, 'INSERT', __FILE__); - $profile->query("ROLLBACK"); - return false; - } - - // check for remote profile - - $remote_pro = Remote_profile::staticGet('uri', $profileurl); - - if (empty($remote_pro)) { - - $remote_pro = new Remote_profile(); - - $remote_pro->id = $id; - $remote_pro->uri = $profileurl; - $remote_pro->created = common_sql_now(); - - $rid = $remote_pro->insert(); - - if (empty($rid)) { - common_log_db_error($profile, 'INSERT', __FILE__); - $profile->query("ROLLBACK"); - return false; - } - } - - $profile->query("COMMIT"); - - $this->saveAvatars($user, $id); - - return $id; - } - } - - function checkAvatar($twitter_user, $profile) - { - global $config; - - $path_parts = pathinfo($twitter_user->profile_image_url); - - $newname = 'Twitter_' . $twitter_user->id . '_' . - $path_parts['basename']; - - $oldname = $profile->getAvatar(48)->filename; - - if ($newname != $oldname) { - common_debug($this->name() . ' - Avatar for Twitter user ' . - "$profile->nickname has changed."); - common_debug($this->name() . " - old: $oldname new: $newname"); - - $this->updateAvatars($twitter_user, $profile); - } - - if ($this->missingAvatarFile($profile)) { - common_debug($this->name() . ' - Twitter user ' . - $profile->nickname . - ' is missing one or more local avatars.'); - common_debug($this->name() ." - old: $oldname new: $newname"); - - $this->updateAvatars($twitter_user, $profile); - } - - } - - function updateAvatars($twitter_user, $profile) { - - global $config; - - $path_parts = pathinfo($twitter_user->profile_image_url); - - $img_root = substr($path_parts['basename'], 0, -11); - $ext = $path_parts['extension']; - $mediatype = $this->getMediatype($ext); - - foreach (array('mini', 'normal', 'bigger') as $size) { - $url = $path_parts['dirname'] . '/' . - $img_root . '_' . $size . ".$ext"; - $filename = 'Twitter_' . $twitter_user->id . '_' . - $img_root . "_$size.$ext"; - - $this->updateAvatar($profile->id, $size, $mediatype, $filename); - $this->fetchAvatar($url, $filename); - } - } - - function missingAvatarFile($profile) { - - foreach (array(24, 48, 73) as $size) { - - $filename = $profile->getAvatar($size)->filename; - $avatarpath = Avatar::path($filename); - - if (file_exists($avatarpath) == FALSE) { - return true; - } - } - - return false; - } - - function getMediatype($ext) - { - $mediatype = null; - - switch (strtolower($ext)) { - case 'jpg': - $mediatype = 'image/jpg'; - break; - case 'gif': - $mediatype = 'image/gif'; - break; - default: - $mediatype = 'image/png'; - } - - return $mediatype; - } - - function saveAvatars($user, $id) - { - global $config; - - $path_parts = pathinfo($user->profile_image_url); - $ext = $path_parts['extension']; - $end = strlen('_normal' . $ext); - $img_root = substr($path_parts['basename'], 0, -($end+1)); - $mediatype = $this->getMediatype($ext); - - foreach (array('mini', 'normal', 'bigger') as $size) { - $url = $path_parts['dirname'] . '/' . - $img_root . '_' . $size . ".$ext"; - $filename = 'Twitter_' . $user->id . '_' . - $img_root . "_$size.$ext"; - - if ($this->fetchAvatar($url, $filename)) { - $this->newAvatar($id, $size, $mediatype, $filename); - } else { - common_log(LOG_WARNING, $this->id() . - " - Problem fetching Avatar: $url"); - } - } - } - - function updateAvatar($profile_id, $size, $mediatype, $filename) { - - common_debug($this->name() . " - Updating avatar: $size"); - - $profile = Profile::staticGet($profile_id); - - if (empty($profile)) { - common_debug($this->name() . " - Couldn't get profile: $profile_id!"); - return; - } - - $sizes = array('mini' => 24, 'normal' => 48, 'bigger' => 73); - $avatar = $profile->getAvatar($sizes[$size]); - - // Delete the avatar, if present - - if ($avatar) { - $avatar->delete(); - } - - $this->newAvatar($profile->id, $size, $mediatype, $filename); - } - - function newAvatar($profile_id, $size, $mediatype, $filename) - { - global $config; - - $avatar = new Avatar(); - $avatar->profile_id = $profile_id; - - switch($size) { - case 'mini': - $avatar->width = 24; - $avatar->height = 24; - break; - case 'normal': - $avatar->width = 48; - $avatar->height = 48; - break; - default: - - // Note: Twitter's big avatars are a different size than - // Laconica's (Laconica's = 96) - - $avatar->width = 73; - $avatar->height = 73; - } - - $avatar->original = 0; // we don't have the original - $avatar->mediatype = $mediatype; - $avatar->filename = $filename; - $avatar->url = Avatar::url($filename); - - common_debug($this->name() . " - New filename: $avatar->url"); - - $avatar->created = common_sql_now(); - - $id = $avatar->insert(); - - if (empty($id)) { - common_log_db_error($avatar, 'INSERT', __FILE__); - return null; - } - - common_debug($this->name() . - " - Saved new $size avatar for $profile_id."); - - return $id; - } - - function fetchAvatar($url, $filename) - { - $avatar_dir = INSTALLDIR . '/avatar/'; - - $avatarfile = $avatar_dir . $filename; - - $out = fopen($avatarfile, 'wb'); - if (!$out) { - common_log(LOG_WARNING, $this->name() . - " - Couldn't open file $filename"); - return false; - } - - common_debug($this->name() . " - Fetching Twitter avatar: $url"); - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_FILE, $out); - curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); - $result = curl_exec($ch); - curl_close($ch); - - fclose($out); - - return $result; - } -} - -$id = null; -$debug = null; - -if (have_option('i')) { - $id = get_option_value('i'); -} else if (have_option('--id')) { - $id = get_option_value('--id'); -} else if (count($args) > 0) { - $id = $args[0]; -} else { - $id = null; -} - -if (have_option('d') || have_option('debug')) { - $debug = true; -} - -$fetcher = new TwitterStatusFetcher($id, 60, 2, $debug); -$fetcher->runOnce(); - -- cgit v1.2.3-54-g00ecf From c8c87fc6030cacd6591098b8f5ab69e3a824babe Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 31 Aug 2009 10:59:50 +1200 Subject: some typoes in comments that annoyed me, fixed now --- lib/twitteroauthclient.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/twitteroauthclient.php b/lib/twitteroauthclient.php index e37fa05f0..bad2b74ca 100644 --- a/lib/twitteroauthclient.php +++ b/lib/twitteroauthclient.php @@ -118,7 +118,7 @@ class TwitterOAuthClient extends OAuthClient } /** - * Calls Twitter's /stutuses/update API method + * Calls Twitter's /statuses/update API method * * @param string $status text of the status * @param int $in_reply_to_status_id optional id of the status it's @@ -137,7 +137,7 @@ class TwitterOAuthClient extends OAuthClient } /** - * Calls Twitter's /stutuses/friends_timeline API method + * Calls Twitter's /statuses/friends_timeline API method * * @param int $since_id show statuses after this id * @param int $max_id show statuses before this id @@ -167,7 +167,7 @@ class TwitterOAuthClient extends OAuthClient } /** - * Calls Twitter's /stutuses/friends API method + * Calls Twitter's /statuses/friends API method * * @param int $id id of the user whom you wish to see friends of * @param int $user_id numerical user id @@ -197,7 +197,7 @@ class TwitterOAuthClient extends OAuthClient } /** - * Calls Twitter's /stutuses/friends/ids API method + * Calls Twitter's /statuses/friends/ids API method * * @param int $id id of the user whom you wish to see friends of * @param int $user_id numerical user id -- cgit v1.2.3-54-g00ecf From ddc95559215142e806428716772cb0edff206ecb Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 1 Sep 2009 19:00:18 +0000 Subject: Stop requeuing notices not bound for Twitter. --- lib/twitter.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/twitter.php b/lib/twitter.php index b734d22d8..e049dc8df 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -160,6 +160,8 @@ function broadcast_twitter($notice) return broadcast_basicauth($notice, $flink); } } + + return true; } function broadcast_oauth($notice, $flink) { -- cgit v1.2.3-54-g00ecf From 6adc50b97fb6d7b5dfd70321281f041f2f579461 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sat, 29 Aug 2009 06:20:19 +0000 Subject: Fix error in log msg format specifier --- lib/twitter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/twitter.php b/lib/twitter.php index e049dc8df..455f7e7ef 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -196,7 +196,7 @@ function broadcast_oauth($notice, $flink) { $errmsg = sprintf('cURL error trying to send notice to Twitter ' . 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: $4$s.', + 'code: %3$s message: %4$s.', $user->nickname, $user->id, $e->getCode(), $e->getMessage()); common_log(LOG_WARNING, $errmsg); @@ -254,7 +254,7 @@ function broadcast_basicauth($notice, $flink) $errmsg = sprintf('cURL error trying to send notice to Twitter ' . 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: $4$s.', + 'code: %3$s message: %4$s.', $user->nickname, $user->id, $e->getCode(), $e->getMessage()); common_log(LOG_WARNING, $errmsg); -- cgit v1.2.3-54-g00ecf From f049d669d95128741f9cb7b1604b758df0550360 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 2 Sep 2009 00:50:41 +0000 Subject: Better error handling --- lib/twitter.php | 100 ++++++++++++++++++++++++-------------------------------- 1 file changed, 42 insertions(+), 58 deletions(-) (limited to 'lib') diff --git a/lib/twitter.php b/lib/twitter.php index 455f7e7ef..676c9b20a 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -175,34 +175,7 @@ function broadcast_oauth($notice, $flink) { try { $status = $client->statusesUpdate($statustxt); } catch (OAuthClientCurlException $e) { - - if ($e->getMessage() == 'The requested URL returned error: 401') { - - $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . - 'Twitter OAuth access token.', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - - // Bad auth token! We need to delete the foreign_link - // to Twitter and inform the user. - - remove_twitter_link($flink); - return true; - - } else { - - // Some other error happened, so we should probably - // try to send again later. - - $errmsg = sprintf('cURL error trying to send notice to Twitter ' . - 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: %4$s.', - $user->nickname, $user->id, - $e->getCode(), $e->getMessage()); - common_log(LOG_WARNING, $errmsg); - - return false; - } + return process_error($e, $flink); } if (empty($status)) { @@ -210,9 +183,9 @@ function broadcast_oauth($notice, $flink) { // This could represent a failure posting, // or the Twitter API might just be behaving flakey. - $errmsg = sprintf('No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s).', - $user->nickname, $user->id); + $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' . + 'trying to send update for %1$s (user id %2$s).', + $user->nickname, $user->id); common_log(LOG_WARNING, $errmsg); return false; @@ -220,7 +193,7 @@ function broadcast_oauth($notice, $flink) { // Notice crossed the great divide - $msg = sprintf('Twitter bridge posted notice %s to Twitter using OAuth.', + $msg = sprintf('Twitter bridge - posted notice %s to Twitter using OAuth.', $notice->id); common_log(LOG_INFO, $msg); @@ -239,46 +212,57 @@ function broadcast_basicauth($notice, $flink) try { $status = $client->statusesUpdate($statustxt); } catch (BasicAuthCurlException $e) { - - if ($e->getMessage() == 'The requested URL returned error: 401') { - - $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . - 'Twitter screen_name/password combo.', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - - remove_twitter_link($flink); - return true; - - } else { - - $errmsg = sprintf('cURL error trying to send notice to Twitter ' . - 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: %4$s.', - $user->nickname, $user->id, - $e->getCode(), $e->getMessage()); - common_log(LOG_WARNING, $errmsg); - - return false; - } + return process_error($e, $flink); } if (empty($status)) { - $errmsg = sprintf('No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s).', - $user->nickname, $user->id); + $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' . + 'trying to send update for %1$s (user id %2$s).', + $user->nickname, $user->id); common_log(LOG_WARNING, $errmsg); return false; } - $msg = sprintf('Twitter bridge posted notice %s to Twitter using basic auth.', + $msg = sprintf('Twitter bridge - posted notice %s to Twitter using basic auth.', $notice->id); common_log(LOG_INFO, $msg); return true; +} + +function process_error($e, $flink) +{ + $user = $flink->getUser(); + $errmsg = $e->getMessage(); + $delivered = false; + + switch($errmsg) { + case 'The requested URL returned error: 401': + $logmsg = sprintf('Twiter bridge - User %1$s (user id: %2$s) has an invalid ' . + 'Twitter screen_name/password combo or an invalid acesss token.', + $user->nickname, $user->id); + $delivered = true; + remove_twitter_link($flink); + break; + case 'The requested URL returned error: 403': + $logmsg = sprintf('Twitter bridge - User %1$s (user id: %2$s) has exceeded ' . + 'his/her Twitter request limit.', + $user->nickname, $user->id); + break; + default: + $logmsg = sprintf('Twitter bridge - cURL error trying to send notice to Twitter ' . + 'for user %1$s (user id: %2$s) - ' . + 'code: %3$s message: %4$s.', + $user->nickname, $user->id, + $e->getCode(), $e->getMessage()); + break; + } + + common_log(LOG_WARNING, $logmsg); + return $delivered; } function format_status($notice) -- cgit v1.2.3-54-g00ecf From 876f56254d3ccdcc7ba3e9c7693345cef8a6e22b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 8 Sep 2009 16:07:01 -0700 Subject: Moved basic auth client into plugin dir --- lib/twitterbasicauthclient.php | 236 ----------------------- plugins/TwitterBridge/twitterbasicauthclient.php | 236 +++++++++++++++++++++++ 2 files changed, 236 insertions(+), 236 deletions(-) delete mode 100644 lib/twitterbasicauthclient.php create mode 100644 plugins/TwitterBridge/twitterbasicauthclient.php (limited to 'lib') diff --git a/lib/twitterbasicauthclient.php b/lib/twitterbasicauthclient.php deleted file mode 100644 index fd331fbdc..000000000 --- a/lib/twitterbasicauthclient.php +++ /dev/null @@ -1,236 +0,0 @@ -. - * - * @category Integration - * @package StatusNet - * @author Zach Copley - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -/** - * Exception wrapper for cURL errors - * - * @category Integration - * @package StatusNet - * @author Zach Copley - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - */ -class BasicAuthCurlException extends Exception -{ -} - -/** - * Class for talking to the Twitter API with HTTP Basic Auth. - * - * @category Integration - * @package StatusNet - * @author Zach Copley - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - */ -class TwitterBasicAuthClient -{ - var $screen_name = null; - var $password = null; - - /** - * constructor - * - * @param Foreign_link $flink a Foreign_link storing the - * Twitter user's password, etc. - */ - function __construct($flink) - { - $fuser = $flink->getForeignUser(); - $this->screen_name = $fuser->nickname; - $this->password = $flink->credentials; - } - - /** - * Calls Twitter's /statuses/update API method - * - * @param string $status text of the status - * @param int $in_reply_to_status_id optional id of the status it's - * a reply to - * - * @return mixed the status - */ - function statusesUpdate($status, $in_reply_to_status_id = null) - { - $url = 'https://twitter.com/statuses/update.json'; - $params = array('status' => $status, - 'source' => common_config('integration', 'source'), - 'in_reply_to_status_id' => $in_reply_to_status_id); - $response = $this->httpRequest($url, $params); - $status = json_decode($response); - return $status; - } - - /** - * Calls Twitter's /statuses/friends_timeline API method - * - * @param int $since_id show statuses after this id - * @param int $max_id show statuses before this id - * @param int $cnt number of statuses to show - * @param int $page page number - * - * @return mixed an array of statuses - */ - function statusesFriendsTimeline($since_id = null, $max_id = null, - $cnt = null, $page = null) - { - $url = 'https://twitter.com/statuses/friends_timeline.json'; - $params = array('since_id' => $since_id, - 'max_id' => $max_id, - 'count' => $cnt, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->httpRequest($url); - $statuses = json_decode($response); - return $statuses; - } - - /** - * Calls Twitter's /statuses/friends API method - * - * @param int $id id of the user whom you wish to see friends of - * @param int $user_id numerical user id - * @param int $screen_name screen name - * @param int $page page number - * - * @return mixed an array of twitter users and their latest status - */ - function statusesFriends($id = null, $user_id = null, $screen_name = null, - $page = null) - { - $url = "https://twitter.com/statuses/friends.json"; - - $params = array('id' => $id, - 'user_id' => $user_id, - 'screen_name' => $screen_name, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->httpRequest($url); - $friends = json_decode($response); - return $friends; - } - - /** - * Calls Twitter's /statuses/friends/ids API method - * - * @param int $id id of the user whom you wish to see friends of - * @param int $user_id numerical user id - * @param int $screen_name screen name - * @param int $page page number - * - * @return mixed a list of ids, 100 per page - */ - function friendsIds($id = null, $user_id = null, $screen_name = null, - $page = null) - { - $url = "https://twitter.com/friends/ids.json"; - - $params = array('id' => $id, - 'user_id' => $user_id, - 'screen_name' => $screen_name, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->httpRequest($url); - $ids = json_decode($response); - return $ids; - } - - /** - * Make a HTTP request using cURL. - * - * @param string $url Where to make the request - * @param array $params post parameters - * - * @return mixed the request - */ - function httpRequest($url, $params = null, $auth = true) - { - $options = array( - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FAILONERROR => true, - CURLOPT_HEADER => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_USERAGENT => 'StatusNet', - CURLOPT_CONNECTTIMEOUT => 120, - CURLOPT_TIMEOUT => 120, - CURLOPT_HTTPAUTH => CURLAUTH_ANY, - CURLOPT_SSL_VERIFYPEER => false, - - // Twitter is strict about accepting invalid "Expect" headers - - CURLOPT_HTTPHEADER => array('Expect:') - ); - - if (isset($params)) { - $options[CURLOPT_POST] = true; - $options[CURLOPT_POSTFIELDS] = $params; - } - - if ($auth) { - $options[CURLOPT_USERPWD] = $this->screen_name . - ':' . $this->password; - } - - $ch = curl_init($url); - curl_setopt_array($ch, $options); - $response = curl_exec($ch); - - if ($response === false) { - $msg = curl_error($ch); - $code = curl_errno($ch); - throw new BasicAuthCurlException($msg, $code); - } - - curl_close($ch); - - return $response; - } - -} diff --git a/plugins/TwitterBridge/twitterbasicauthclient.php b/plugins/TwitterBridge/twitterbasicauthclient.php new file mode 100644 index 000000000..fd331fbdc --- /dev/null +++ b/plugins/TwitterBridge/twitterbasicauthclient.php @@ -0,0 +1,236 @@ +. + * + * @category Integration + * @package StatusNet + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Exception wrapper for cURL errors + * + * @category Integration + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + */ +class BasicAuthCurlException extends Exception +{ +} + +/** + * Class for talking to the Twitter API with HTTP Basic Auth. + * + * @category Integration + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + */ +class TwitterBasicAuthClient +{ + var $screen_name = null; + var $password = null; + + /** + * constructor + * + * @param Foreign_link $flink a Foreign_link storing the + * Twitter user's password, etc. + */ + function __construct($flink) + { + $fuser = $flink->getForeignUser(); + $this->screen_name = $fuser->nickname; + $this->password = $flink->credentials; + } + + /** + * Calls Twitter's /statuses/update API method + * + * @param string $status text of the status + * @param int $in_reply_to_status_id optional id of the status it's + * a reply to + * + * @return mixed the status + */ + function statusesUpdate($status, $in_reply_to_status_id = null) + { + $url = 'https://twitter.com/statuses/update.json'; + $params = array('status' => $status, + 'source' => common_config('integration', 'source'), + 'in_reply_to_status_id' => $in_reply_to_status_id); + $response = $this->httpRequest($url, $params); + $status = json_decode($response); + return $status; + } + + /** + * Calls Twitter's /statuses/friends_timeline API method + * + * @param int $since_id show statuses after this id + * @param int $max_id show statuses before this id + * @param int $cnt number of statuses to show + * @param int $page page number + * + * @return mixed an array of statuses + */ + function statusesFriendsTimeline($since_id = null, $max_id = null, + $cnt = null, $page = null) + { + $url = 'https://twitter.com/statuses/friends_timeline.json'; + $params = array('since_id' => $since_id, + 'max_id' => $max_id, + 'count' => $cnt, + 'page' => $page); + $qry = http_build_query($params); + + if (!empty($qry)) { + $url .= "?$qry"; + } + + $response = $this->httpRequest($url); + $statuses = json_decode($response); + return $statuses; + } + + /** + * Calls Twitter's /statuses/friends API method + * + * @param int $id id of the user whom you wish to see friends of + * @param int $user_id numerical user id + * @param int $screen_name screen name + * @param int $page page number + * + * @return mixed an array of twitter users and their latest status + */ + function statusesFriends($id = null, $user_id = null, $screen_name = null, + $page = null) + { + $url = "https://twitter.com/statuses/friends.json"; + + $params = array('id' => $id, + 'user_id' => $user_id, + 'screen_name' => $screen_name, + 'page' => $page); + $qry = http_build_query($params); + + if (!empty($qry)) { + $url .= "?$qry"; + } + + $response = $this->httpRequest($url); + $friends = json_decode($response); + return $friends; + } + + /** + * Calls Twitter's /statuses/friends/ids API method + * + * @param int $id id of the user whom you wish to see friends of + * @param int $user_id numerical user id + * @param int $screen_name screen name + * @param int $page page number + * + * @return mixed a list of ids, 100 per page + */ + function friendsIds($id = null, $user_id = null, $screen_name = null, + $page = null) + { + $url = "https://twitter.com/friends/ids.json"; + + $params = array('id' => $id, + 'user_id' => $user_id, + 'screen_name' => $screen_name, + 'page' => $page); + $qry = http_build_query($params); + + if (!empty($qry)) { + $url .= "?$qry"; + } + + $response = $this->httpRequest($url); + $ids = json_decode($response); + return $ids; + } + + /** + * Make a HTTP request using cURL. + * + * @param string $url Where to make the request + * @param array $params post parameters + * + * @return mixed the request + */ + function httpRequest($url, $params = null, $auth = true) + { + $options = array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FAILONERROR => true, + CURLOPT_HEADER => false, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_USERAGENT => 'StatusNet', + CURLOPT_CONNECTTIMEOUT => 120, + CURLOPT_TIMEOUT => 120, + CURLOPT_HTTPAUTH => CURLAUTH_ANY, + CURLOPT_SSL_VERIFYPEER => false, + + // Twitter is strict about accepting invalid "Expect" headers + + CURLOPT_HTTPHEADER => array('Expect:') + ); + + if (isset($params)) { + $options[CURLOPT_POST] = true; + $options[CURLOPT_POSTFIELDS] = $params; + } + + if ($auth) { + $options[CURLOPT_USERPWD] = $this->screen_name . + ':' . $this->password; + } + + $ch = curl_init($url); + curl_setopt_array($ch, $options); + $response = curl_exec($ch); + + if ($response === false) { + $msg = curl_error($ch); + $code = curl_errno($ch); + throw new BasicAuthCurlException($msg, $code); + } + + curl_close($ch); + + return $response; + } + +} -- cgit v1.2.3-54-g00ecf From 1241e651ae54adfc429aceb92a2bc873f1c82f8d Mon Sep 17 00:00:00 2001 From: Trever Fischer Date: Thu, 15 Oct 2009 05:16:37 -0400 Subject: Added support for profile designs to the twitter API --- classes/Profile.php | 5 +++++ lib/api.php | 20 +++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/classes/Profile.php b/classes/Profile.php index 4a069ee84..a78a27f4a 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -46,6 +46,11 @@ class Profile extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + function getUser() + { + return User::staticGet('id', $this->id); + } + function getAvatar($width, $height=null) { if (is_null($height)) { diff --git a/lib/api.php b/lib/api.php index 7a63a4a78..9bd2083de 100644 --- a/lib/api.php +++ b/lib/api.php @@ -134,11 +134,19 @@ class ApiAction extends Action $twitter_user['protected'] = false; # not supported by StatusNet yet $twitter_user['followers_count'] = $profile->subscriberCount(); - // To be supported soon... - $twitter_user['profile_background_color'] = ''; - $twitter_user['profile_text_color'] = ''; - $twitter_user['profile_link_color'] = ''; - $twitter_user['profile_sidebar_fill_color'] = ''; + // Need to pull up the user for some of this + $user = $profile->getUser(); + $design = $user->getDesign(); + $defaultDesign = Design::siteDesign(); + if (!$design) $design = $defaultDesign; + $color = Design::toWebColor(empty($design->backgroundcolor) ? $defaultDesign->backgroundcolor : $design->backgroundcolor); + $twitter_user['profile_background_color'] = ($color == null) ? '' : '#'.$color->hexValue(); + $color = Design::toWebColor(empty($design->textcolor) ? $defaultDesign->textcolor : $design->textcolor); + $twitter_user['profile_text_color'] = ($color == null) ? '' : '#'.$color->hexValue(); + $color = Design::toWebColor(empty($design->linkcolor) ? $defaultDesign->linkcolor : $design->linkcolor); + $twitter_user['profile_link_color'] = ($color == null) ? '' : '#'.$color->hexValue(); + $color = Design::toWebColor(empty($design->sidebarcolor) ? $defaultDesign->sidebarcolor : $design->sidebarcolor); + $twitter_user['profile_sidebar_fill_color'] = ($color == null) ? '' : '#'.$color->hexValue(); $twitter_user['profile_sidebar_border_color'] = ''; $twitter_user['friends_count'] = $profile->subscriptionCount(); @@ -147,8 +155,6 @@ class ApiAction extends Action $twitter_user['favourites_count'] = $profile->faveCount(); // British spelling! - // Need to pull up the user for some of this - $user = User::staticGet($profile->id); $timezone = 'UTC'; -- cgit v1.2.3-54-g00ecf From 90de6eae5a6195e4455a726d7183dbefc8eeb617 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 15 Oct 2009 06:01:26 -0400 Subject: add more events to profile list --- EVENTS.txt | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ actions/groupmembers.php | 9 ++++--- lib/profilelist.php | 62 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 120 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/EVENTS.txt b/EVENTS.txt index 9de2f8bc6..5d34a9e13 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -390,3 +390,68 @@ EndProfilePageProfileTags: after showing the tags on the profile page - $action: the current action - &$profile: the profile being shown +StartProfileList: when starting a list of profiles (before
    ) +- $profilelist: ProfileList widget, with $profile, $action, and $out + +EndProfileList: when ending a list of profiles (after
) +- $profilelist: ProfileList widget + +StartProfileListItem: when starting to show a profile list item +- $item: ProfileListItem widget + +EndProfileListItem: after showing a profile list item +- $item: ProfileListItem widget + +StartProfileListItemProfile: the profile data part of the item +- $item: ProfileListItem widget + +EndProfileListItemProfile: the profile data part of the item +- $item: ProfileListItem widget + +StartProfileListItemActions: the actions (buttons) for an item +- $item: ProfileListItem widget + +EndProfileListItemActions: the actions (buttons) for an item +- $item: ProfileListItem widget + +StartProfileListItemProfileElements: inside the
+- $item: ProfileListItem widget + +EndProfileListItemProfileElements: inside the
+- $item: ProfileListItem widget + +StartProfileListItemAvatar: Showing a profile list avatar +- $item: ProfileListItem widget + +EndProfileListItemAvatar: Showing a profile list avatar +- $item: ProfileListItem widget + +StartProfileListItemFullName: Showing the profile list full name +- $item: ProfileListItem widget + +EndProfileListItemFullName: Showing the profile list full name +- $item: ProfileListItem widget + +StartProfileListItemLocation: Showing the profile list location +- $item: ProfileListItem widget + +EndProfileListItemLocation: Showing the profile list location +- $item: ProfileListItem widget + +StartProfileListItemHomepage: Showing the profile list homepage +- $item: ProfileListItem widget + +EndProfileListItemHomepage: Showing the profile list homepage +- $item: ProfileListItem widget + +StartProfileListItemBio: Showing the profile list bio +- $item: ProfileListItem widget + +EndProfileListItemBio: Showing the profile list bio +- $item: ProfileListItem widget + +StartProfileListItemActionElements: Showing the profile list actions (prepend a button here, or replace all buttons) +- $item: ProfileListItem widget + +EndProfileListItemActionElements: Showing profile list actions (append a button here) +- $item: ProfileListItem widget diff --git a/actions/groupmembers.php b/actions/groupmembers.php index dcbdd3759..b326a0df7 100644 --- a/actions/groupmembers.php +++ b/actions/groupmembers.php @@ -179,9 +179,12 @@ class GroupMemberListItem extends ProfileListItem function showActions() { $this->startActions(); - $this->showSubscribeButton(); - $this->showMakeAdminForm(); - $this->showGroupBlockForm(); + if (Event::handle('StartProfileListItemActionElements', array($this))) { + $this->showSubscribeButton(); + $this->showMakeAdminForm(); + $this->showGroupBlockForm(); + Event::handle('EndProfileListItemActionElements', array($this)); + } $this->endActions(); } diff --git a/lib/profilelist.php b/lib/profilelist.php index 331430b3e..5cc211e36 100644 --- a/lib/profilelist.php +++ b/lib/profilelist.php @@ -62,9 +62,15 @@ class ProfileList extends Widget function show() { - $this->startList(); - $cnt = $this->showProfiles(); - $this->endList(); + $cnt = 0; + + if (Event::handle('StartProfileList', array($this))) { + $this->startList(); + $cnt = $this->showProfiles(); + $this->endList(); + Event::handle('EndProfileList', array($this)); + } + return $cnt; } @@ -117,10 +123,19 @@ class ProfileListItem extends Widget function show() { - $this->startItem(); - $this->showProfile(); - $this->showActions(); - $this->endItem(); + if (Event::handle('StartProfileListItem', array($this))) { + $this->startItem(); + if (Event::handle('StartProfileListItemProfile', array($this))) { + $this->showProfile(); + Event::handle('EndProfileListItemProfile', array($this)); + } + if (Event::handle('StartProfileListItemActions', array($this))) { + $this->showActions(); + Event::handle('EndProfileListItemActions', array($this)); + } + $this->endItem(); + Event::handle('EndProfileListItem', array($this)); + } } function startItem() @@ -132,11 +147,29 @@ class ProfileListItem extends Widget function showProfile() { $this->startProfile(); - $this->showAvatar(); - $this->showFullName(); - $this->showLocation(); - $this->showHomepage(); - $this->showBio(); + if (Event::handle('StartProfileListItemProfileElements', array($this))) { + if (Event::handle('StartProfileListItemAvatar', array($this))) { + $this->showAvatar(); + Event::handle('EndProfileListItemAvatar', array($this)); + } + if (Event::handle('StartProfileListItemFullName', array($this))) { + $this->showFullName(); + Event::handle('EndProfileListItemFullName', array($this)); + } + if (Event::handle('StartProfileListItemLocation', array($this))) { + $this->showLocation(); + Event::handle('EndProfileListItemLocation', array($this)); + } + if (Event::handle('StartProfileListItemHomepage', array($this))) { + $this->showHomepage(); + Event::handle('EndProfileListItemHomepage', array($this)); + } + if (Event::handle('StartProfileListItemBio', array($this))) { + $this->showBio(); + Event::handle('EndProfileListItemBio', array($this)); + } + Event::handle('EndProfileListItemProfileElements', array($this)); + } $this->endProfile(); } @@ -225,7 +258,10 @@ class ProfileListItem extends Widget function showActions() { $this->startActions(); - $this->showSubscribeButton(); + if (Event::handle('StartProfileListItemActionElements', array($this))) { + $this->showSubscribeButton(); + Event::handle('EndProfileListItemActionElements', array($this)); + } $this->endActions(); } -- cgit v1.2.3-54-g00ecf From 4ef0665408aee19d9ae69758e02fc98003e202ff Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 16 Oct 2009 16:29:50 +0000 Subject: Galician translation (out of date but functional... unless you have locale issues of course) .po file from http://status.net/trac/ticket/481 last updated by mvazquez --- lib/language.php | 5 +- locale/ga/LC_MESSAGES/statusnet.mo | Bin 0 -> 73858 bytes locale/ga/LC_MESSAGES/statusnet.po | 4713 ++++++++++++++++++++++++++++++++++++ 3 files changed, 4716 insertions(+), 2 deletions(-) create mode 100644 locale/ga/LC_MESSAGES/statusnet.mo create mode 100644 locale/ga/LC_MESSAGES/statusnet.po (limited to 'lib') diff --git a/lib/language.php b/lib/language.php index 561a4ddb8..666f97b52 100644 --- a/lib/language.php +++ b/lib/language.php @@ -109,9 +109,10 @@ function get_all_languages() { 'en-us' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'), 'en-gb' => array('q' => 1, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'), 'en' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'), - 'es' => array('q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'), - 'fi' => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'), + 'es' => array('q' => 1, 'lang' => 'es_ES', 'name' => 'Spanish', 'direction' => 'ltr'), + 'fi' => array('q' => 1, 'lang' => 'fi_FI', 'name' => 'Finnish', 'direction' => 'ltr'), 'fr-fr' => array('q' => 1, 'lang' => 'fr_FR', 'name' => 'French', 'direction' => 'ltr'), + 'ga' => array('q' => 0.5, 'lang' => 'ga_ES', 'name' => 'Galician', 'direction' => 'ltr'), 'he' => array('q' => 0.5, 'lang' => 'he_IL', 'name' => 'Hebrew', 'direction' => 'rtl'), 'it' => array('q' => 1, 'lang' => 'it_IT', 'name' => 'Italian', 'direction' => 'ltr'), 'jp' => array('q' => 0.5, 'lang' => 'ja_JP', 'name' => 'Japanese', 'direction' => 'ltr'), diff --git a/locale/ga/LC_MESSAGES/statusnet.mo b/locale/ga/LC_MESSAGES/statusnet.mo new file mode 100644 index 000000000..c13acd1a8 Binary files /dev/null and b/locale/ga/LC_MESSAGES/statusnet.mo differ diff --git a/locale/ga/LC_MESSAGES/statusnet.po b/locale/ga/LC_MESSAGES/statusnet.po new file mode 100644 index 000000000..3815e4853 --- /dev/null +++ b/locale/ga/LC_MESSAGES/statusnet.po @@ -0,0 +1,4713 @@ +# translation of statusnet.po to Galician +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Francisco Diéguez , 2008. +msgid "" +msgstr "" +"Project-Id-Version: laconica-new\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-12-17 20:08+0100\n" +"PO-Revision-Date: 2008-12-17 20:10+0100\n" +"Last-Translator: Martín Vázquez Cabanas \n" +"Language-Team: Galician \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.11.4\n" + +#: ../actions/noticesearchrss.php:64 +#: actions/noticesearchrss.php:68 +#: actions/noticesearchrss.php:61 +#, php-format +msgid " Search Stream for \"%s\"" +msgstr "Buscar \"%s\" na Liña de tempo" + +#: ../actions/finishopenidlogin.php:82 +#: ../actions/register.php:191 +#: actions/finishopenidlogin.php:88 +#: actions/register.php:205 +#: actions/register.php:232 +msgid " except this private data: password, email address, IM address, phone number." +msgstr " agás esta informción privada: contrasinal, dirección de correo electrónico, dirección IM, número de teléfono." + +#: ../actions/showstream.php:400 +#: ../lib/stream.php:109 +#: actions/showstream.php:418 +#: lib/mailbox.php:164 +#: lib/stream.php:76 +#: lib/facebookaction.php:195 +#: lib/noticelist.php:160 +msgid " from " +msgstr " dende " + +#: ../actions/twitapistatuses.php:478 +#: actions/twitapistatuses.php:412 +#: actions/twitapistatuses.php:341 +#, php-format +msgid "%1$s / Updates replying to %2$s" +msgstr "%1$s / Chíos que respostan a %2$s" + +#: ../actions/invite.php:168 +#: actions/invite.php:176 +#, php-format +msgid "%1$s has invited you to join them on %2$s" +msgstr "%1$s invitoute a unirse a él en %2$s." + +#: ../actions/invite.php:170 +#: actions/invite.php:178 +#, php-format +msgid "" +"%1$s has invited you to join them on %2$s (%3$s).\n" +"\n" +"%2$s is a micro-blogging service that lets you keep up-to-date with people you know and people who interest you.\n" +"\n" +"You can also share news about yourself, your thoughts, or your life online with people who know about you. It's also great for meeting new people who share your interests.\n" +"\n" +"%1$s said:\n" +"\n" +"%4$s\n" +"\n" +"You can see %1$s's profile page on %2$s here:\n" +"\n" +"%5$s\n" +"\n" +"If you'd like to try the service, click on the link below to accept the invitation.\n" +"\n" +"%6$s\n" +"\n" +"If not, you can ignore this message. Thanks for your patience and your time.\n" +"\n" +"Sincerely, %2$s\n" +msgstr "" +"%1$s invitoute a unirte a el en %2$s (%3$s).\n" +"\n" +"%2$s é un servizo de microbloguexo que che permite manterte actualizado coa xente que coñeces e xente na que tes interese.\n" +"\n" +"Tamén podes compartir novas contigo mesmo, ou pensamentos, ou vivir en liña con xente que te coñece. Tamén está moi ben para coñecer xente que comparte os mesmos intereses..\n" +"\n" +"%1$s dixo:\n" +"\n" +"%4$s\n" +"\n" +"Podes ollar a súa páxina de perfil %1$s's en %2$s here:\n" +"\n" +"%5$s\n" +"\n" +"Se queres probar, fai clic na ligazón de abaixo para aceptar a invitación..\n" +"\n" +"%6$s\n" +"\n" +"Se non, pois ignora esta mensaxe. Pero aló ti, aquí se pasa moi ben.\n" +"\n" +"Saudiños, %2$s\n" + +#: ../lib/mail.php:124 +#: lib/mail.php:124 +#: lib/mail.php:126 +#: lib/mail.php:115 +#, php-format +msgid "%1$s is now listening to your notices on %2$s." +msgstr "%1$s está a escoitar os teus chíos %2$s." + +#: ../lib/mail.php:126 +#: lib/mail.php:117 +#, php-format +msgid "" +"%1$s is now listening to your notices on %2$s.\n" +"\n" +"\t%3$s\n" +"\n" +"Faithfully yours,\n" +"%4$s.\n" +msgstr "" +"%1$s está a escoitar os teus chíos en %2$s.\n" +"\n" +"\t%3$s\n" +"\n" +"Atentamente todo seu,\n" +"%4$s.\n" + +#: ../actions/twitapistatuses.php:482 +#: actions/twitapistatuses.php:415 +#: actions/twitapistatuses.php:344 +#, php-format +msgid "%1$s updates that reply to updates from %2$s / %3$s." +msgstr "Hai %1$s chíos en resposta a chíos dende %2$s / %3$s." + +#: ../actions/shownotice.php:45 +#: actions/shownotice.php:45 +#: actions/shownotice.php:73 +#, php-format +msgid "%1$s's status on %2$s" +msgstr "Estado de %1$s en %2$s" + +#: ../actions/invite.php:84 +#: ../actions/invite.php:92 +#: actions/invite.php:91 +#: actions/invite.php:99 +#, php-format +msgid "%s (%s)" +msgstr "%s (%s)" + +#: ../actions/publicrss.php:62 +#: actions/publicrss.php:48 +#, php-format +msgid "%s Public Stream" +msgstr "%s Fio Público" + +#: ../actions/all.php:47 +#: ../actions/allrss.php:60 +#: ../actions/twitapistatuses.php:238 +#: ../lib/stream.php:51 +#: actions/all.php:47 +#: actions/allrss.php:60 +#: actions/twitapistatuses.php:155 +#: lib/personal.php:51 +#: actions/twitapistatuses.php:123 +#, php-format +msgid "%s and friends" +msgstr "%s e amigos" + +#: ../actions/twitapistatuses.php:49 +#: actions/twitapistatuses.php:49 +#: actions/twitapistatuses.php:31 +#, php-format +msgid "%s public timeline" +msgstr "Liña de tempo pública de %s" + +#: ../lib/mail.php:206 +#: lib/mail.php:212 +#: lib/mail.php:207 +#, php-format +msgid "%s status" +msgstr "Estado de %s" + +#: ../actions/twitapistatuses.php:338 +#: actions/twitapistatuses.php:265 +#: actions/twitapistatuses.php:195 +#, php-format +msgid "%s timeline" +msgstr "Liña de tempo de %s" + +#: ../actions/twitapistatuses.php:52 +#: actions/twitapistatuses.php:52 +#: actions/twitapistatuses.php:34 +#, php-format +msgid "%s updates from everyone!" +msgstr "%s chíos de calquera!" + +#: ../actions/register.php:213 +#: actions/register.php:254 +msgid "(You should receive a message by email momentarily, with instructions on how to confirm your email address.)" +msgstr "(Deberías recibir unha mensaxe no teu email nun intre, coas instrucións de como confirmar a túa dirección de correo.)" + +#: ../lib/util.php:257 +#: lib/util.php:273 +#: lib/util.php:290 +#, php-format +msgid "**%%site.name%%** is a microblogging service brought to you by [%%site.broughtby%%](%%site.broughtbyurl%%). " +msgstr "**%%site.name%%** é un servizo de microbloguexo que che proporciona [%%site.broughtby%%](%%site.broughtbyurl%%). " + +#: ../lib/util.php:259 +#: lib/util.php:275 +#: lib/util.php:292 +#, php-format +msgid "**%%site.name%%** is a microblogging service. " +msgstr "**%%site.name%%** é un servizo de microbloguexo." + +#: ../lib/util.php:274 +#: lib/util.php:290 +#: lib/util.php:307 +msgid ". Contributors should be attributed by full name or nickname." +msgstr ". Os contribuíntes deben atribuiselles polo nome completo ou apodo." + +#: ../actions/finishopenidlogin.php:73 +#: ../actions/profilesettings.php:43 +#: actions/finishopenidlogin.php:79 +#: actions/profilesettings.php:76 +#: actions/profilesettings.php:78 +msgid "1-64 lowercase letters or numbers, no punctuation or spaces" +msgstr "De 1 a 64 letras minúsculas ou númeors, nin espazos nin signos de puntuación" + +#: ../actions/register.php:152 +#: actions/register.php:166 +#: actions/register.php:193 +msgid "1-64 lowercase letters or numbers, no punctuation or spaces. Required." +msgstr "De 1 a 64 letras minúsculas ou números, nin espazos nin signos de puntuación. Requerido." + +#: ../actions/password.php:42 +#: actions/profilesettings.php:181 +#: actions/profilesettings.php:198 +msgid "6 or more characters" +msgstr "6 ou máis caracteres" + +#: ../actions/recoverpassword.php:180 +#: actions/recoverpassword.php:186 +msgid "6 or more characters, and don't forget it!" +msgstr "6 ou máis caracteres, non o esquenzas!" + +#: ../actions/register.php:154 +#: actions/register.php:168 +#: actions/register.php:195 +msgid "6 or more characters. Required." +msgstr "6 ou máis caracteres. Requerido." + +#: ../actions/imsettings.php:197 +#: actions/imsettings.php:205 +#: actions/imsettings.php:210 +#, php-format +msgid "A confirmation code was sent to the IM address you added. You must approve %s for sending messages to you." +msgstr "O código de confirmación foi embiado á dirección IM que engadiches. Deberías engadir a %s como contacto para que che poida enviar mensaxes." + +#: ../actions/emailsettings.php:213 +#: actions/emailsettings.php:231 +#: actions/emailsettings.php:235 +msgid "A confirmation code was sent to the email address you added. Check your inbox (and spam box!) for the code and instructions on how to use it." +msgstr "Enviouseche un código de confirmación á dirección de correo que engadiches. Comproba a túa bandexa de entrada (ou spam!) polo código e instrucións que debes seguir." + +#: ../actions/smssettings.php:216 +#: actions/smssettings.php:224 +#: actions/smssettings.php:226 +msgid "A confirmation code was sent to the phone number you added. Check your inbox (and spam box!) for the code and instructions on how to use it." +msgstr "Enviouseche o código de confirmación ó número de teléfono que engadiches. Comproba a túa bandexa de entrada (ou spam!) polo código e instrucións que debes seguir." + +#: ../actions/twitapiaccount.php:49 +#: ../actions/twitapihelp.php:45 +#: ../actions/twitapistatuses.php:88 +#: ../actions/twitapistatuses.php:259 +#: ../actions/twitapistatuses.php:370 +#: ../actions/twitapistatuses.php:532 +#: ../actions/twitapiusers.php:122 +#: actions/twitapiaccount.php:49 +#: actions/twitapidirect_messages.php:104 +#: actions/twitapifavorites.php:111 +#: actions/twitapifavorites.php:120 +#: actions/twitapifriendships.php:156 +#: actions/twitapihelp.php:46 +#: actions/twitapistatuses.php:93 +#: actions/twitapistatuses.php:176 +#: actions/twitapistatuses.php:288 +#: actions/twitapistatuses.php:298 +#: actions/twitapistatuses.php:454 +#: actions/twitapistatuses.php:463 +#: actions/twitapistatuses.php:504 +#: actions/twitapiusers.php:55 +#: actions/twitapiaccount.php:35 +#: actions/twitapidirect_messages.php:107 +#: actions/twitapifavorites.php:83 +#: actions/twitapifavorites.php:99 +#: actions/twitapifriendships.php:117 +#: actions/twitapihelp.php:42 +#: actions/twitapistatuses.php:77 +#: actions/twitapistatuses.php:144 +#: actions/twitapistatuses.php:224 +#: actions/twitapistatuses.php:234 +#: actions/twitapistatuses.php:386 +#: actions/twitapistatuses.php:395 +#: actions/twitapistatuses.php:421 +#: actions/twitapiusers.php:30 +msgid "API method not found!" +msgstr "Método da API non atopado" + +#: ../actions/twitapiaccount.php:57 +#: ../actions/twitapiaccount.php:113 +#: ../actions/twitapiaccount.php:119 +#: ../actions/twitapiblocks.php:28 +#: ../actions/twitapiblocks.php:34 +#: ../actions/twitapidirect_messages.php:43 +#: ../actions/twitapidirect_messages.php:49 +#: ../actions/twitapidirect_messages.php:56 +#: ../actions/twitapidirect_messages.php:62 +#: ../actions/twitapifavorites.php:41 +#: ../actions/twitapifavorites.php:47 +#: ../actions/twitapifavorites.php:53 +#: ../actions/twitapihelp.php:52 +#: ../actions/twitapinotifications.php:29 +#: ../actions/twitapinotifications.php:35 +#: ../actions/twitapistatuses.php:768 +#: actions/twitapiaccount.php:56 +#: actions/twitapiaccount.php:109 +#: actions/twitapiaccount.php:114 +#: actions/twitapiblocks.php:28 +#: actions/twitapiblocks.php:33 +#: actions/twitapidirect_messages.php:170 +#: actions/twitapifavorites.php:168 +#: actions/twitapihelp.php:53 +#: actions/twitapinotifications.php:29 +#: actions/twitapinotifications.php:34 +#: actions/twitapistatuses.php:690 +#: actions/twitapiaccount.php:42 +#: actions/twitapiaccount.php:92 +#: actions/twitapiaccount.php:97 +#: actions/twitapidirect_messages.php:178 +#: actions/twitapifavorites.php:139 +#: actions/twitapihelp.php:49 +#: actions/twitapistatuses.php:549 +msgid "API method under construction." +msgstr "Método da API en contrución." + +#: ../lib/util.php:324 +#: lib/util.php:340 +#: lib/util.php:369 +msgid "About" +msgstr "Sobre" + +#: ../actions/userauthorization.php:119 +#: actions/userauthorization.php:126 +msgid "Accept" +msgstr "Aceptar" + +#: ../actions/emailsettings.php:62 +#: ../actions/imsettings.php:63 +#: ../actions/openidsettings.php:57 +#: ../actions/smssettings.php:71 +#: actions/emailsettings.php:63 +#: actions/imsettings.php:64 +#: actions/openidsettings.php:58 +#: actions/smssettings.php:71 +#: actions/twittersettings.php:85 +#: actions/smspostsettings.php:50 +#: actions/twittersettings.php:84 +#: actions/smspostsettings.php:51 +msgid "Add" +msgstr "Engadir" + +#: ../actions/openidsettings.php:43 +#: actions/openidsettings.php:44 +msgid "Add OpenID" +msgstr "Engadir dirección OpenID" + +#: ../lib/settingsaction.php:97 +#: lib/settingsaction.php:91 +#: actions/deleteprofile.php:248 +msgid "Add or remove OpenIDs" +msgstr "Engadir ou eliminar OpenID" + +#: ../actions/emailsettings.php:38 +#: ../actions/imsettings.php:39 +#: ../actions/smssettings.php:39 +#: actions/emailsettings.php:39 +#: actions/imsettings.php:40 +#: actions/smssettings.php:39 +msgid "Address" +msgstr "Enderezo" + +#: ../actions/invite.php:131 +#: actions/invite.php:139 +msgid "Addresses of friends to invite (one per line)" +msgstr "Direccións dos amigos que queres invitar (unha por liña)" + +#: ../actions/showstream.php:273 +#: actions/showstream.php:288 +#: actions/showstream.php:336 +msgid "All subscriptions" +msgstr "Tódalas subscricións" + +#: ../actions/publicrss.php:64 +#: actions/publicrss.php:50 +#, php-format +msgid "All updates for %s" +msgstr "Tódalas actualizacións de %s" + +#: ../actions/noticesearchrss.php:66 +#: actions/noticesearchrss.php:70 +#: actions/noticesearchrss.php:63 +#, php-format +msgid "All updates matching search term \"%s\"" +msgstr "Tódalas actualizacións que coinciden co termo de procura \"%s\"" + +#: ../actions/finishopenidlogin.php:29 +#: ../actions/login.php:31 +#: ../actions/openidlogin.php:29 +#: ../actions/register.php:30 +#: actions/finishopenidlogin.php:29 +#: actions/login.php:31 +#: actions/openidlogin.php:29 +#: actions/register.php:30 +msgid "Already logged in." +msgstr "Sesión xa iniciada" + +#: ../lib/subs.php:42 +#: lib/subs.php:42 +#: lib/subs.php:47 +msgid "Already subscribed!." +msgstr "¡Xa está subscrito!." + +#: ../actions/deletenotice.php:54 +#: actions/deletenotice.php:55 +msgid "Are you sure you want to delete this notice?" +msgstr "Estas seguro que queres eliminar este chío?" + +#: ../actions/userauthorization.php:77 +#: actions/userauthorization.php:83 +msgid "Authorize subscription" +msgstr "Subscrición de autorización." + +#: ../actions/login.php:104 +#: ../actions/register.php:178 +#: actions/register.php:192 +#: actions/login.php:111 +#: actions/openidlogin.php:86 +#: actions/register.php:219 +msgid "Automatically login in the future; not for shared computers!" +msgstr "Endiante acceder automáticamente, coidado en equipos compartidos!" + +#: ../actions/profilesettings.php:65 +#: actions/profilesettings.php:98 +#: actions/profilesettings.php:112 +msgid "Automatically subscribe to whoever subscribes to me (best for non-humans)" +msgstr "Suscribirse automáticamente a calquera que se suscriba a min (o mellor para non humáns)" + +#: ../actions/avatar.php:32 +#: ../lib/settingsaction.php:90 +#: actions/profilesettings.php:34 +msgid "Avatar" +msgstr "Avatar" + +#: ../actions/avatar.php:113 +#: actions/profilesettings.php:350 +#: actions/profilesettings.php:396 +msgid "Avatar updated." +msgstr "Avatar actualizado." + +#: ../actions/imsettings.php:55 +#: actions/imsettings.php:56 +#, php-format +msgid "Awaiting confirmation on this address. Check your Jabber/GTalk account for a message with further instructions. (Did you add %s to your buddy list?)" +msgstr "Awaiting confirmation on this address. Check your Jabber/GTalk account for a message with further instructions. (Did you add %s to your buddy list?)" + +#: ../actions/emailsettings.php:54 +#: actions/emailsettings.php:55 +msgid "Awaiting confirmation on this address. Check your inbox (and spam box!) for a message with further instructions." +msgstr "Agardando confirmación para esta dirección. Comproba a túa conta de Jabber/GTalk que ten que haber unha mensaxe coas seguintes instrucións. (Engadiches a %s á túa lista de contactos?)" + +#: ../actions/smssettings.php:58 +#: actions/smssettings.php:58 +msgid "Awaiting confirmation on this phone number." +msgstr "Agardando a confirmación neste número de teléfono." + +#: ../lib/util.php:1318 +#: lib/util.php:1452 +#: lib/facebookaction.php:259 +#: lib/util.php:1851 +msgid "Before »" +msgstr "Antes »" + +#: ../actions/profilesettings.php:49 +#: ../actions/register.php:170 +#: actions/profilesettings.php:82 +#: actions/register.php:184 +#: actions/profilesettings.php:84 +#: actions/register.php:211 +msgid "Bio" +msgstr "Bio" + +#: ../actions/profilesettings.php:101 +#: ../actions/register.php:82 +#: ../actions/updateprofile.php:103 +#: actions/profilesettings.php:216 +#: actions/register.php:89 +#: actions/updateprofile.php:104 +#: actions/profilesettings.php:235 +#: actions/register.php:98 +msgid "Bio is too long (max 140 chars)." +msgstr "O teu Bio é demasiado longo (max 140 car.)." + +#: ../lib/deleteaction.php:41 +#: lib/deleteaction.php:41 +msgid "Can't delete this notice." +msgstr "Non se pode eliminar este chíos." + +#: ../actions/updateprofile.php:119 +#: actions/updateprofile.php:120 +#, php-format +msgid "Can't read avatar URL '%s'" +msgstr "Non se pode ler a URL do avatar de '%s'" + +#: ../actions/password.php:85 +#: ../actions/recoverpassword.php:300 +#: actions/profilesettings.php:404 +#: actions/recoverpassword.php:313 +#: actions/profilesettings.php:450 +msgid "Can't save new password." +msgstr "Non se pode gardar a contrasinal." + +#: ../actions/emailsettings.php:57 +#: ../actions/imsettings.php:58 +#: ../actions/smssettings.php:62 +#: actions/emailsettings.php:58 +#: actions/imsettings.php:59 +#: actions/smssettings.php:62 +msgid "Cancel" +msgstr "Cancelar" + +#: ../lib/openid.php:121 +#: lib/openid.php:121 +msgid "Cannot instantiate OpenID consumer object." +msgstr "Non se pode instanciar o obxecto consumidor de OpenID." + +#: ../actions/imsettings.php:163 +#: actions/imsettings.php:171 +#: actions/imsettings.php:176 +msgid "Cannot normalize that Jabber ID" +msgstr "Non se pode normalizar ese identificador de Jabber" + +#: ../actions/emailsettings.php:181 +#: actions/emailsettings.php:199 +#: actions/emailsettings.php:205 +msgid "Cannot normalize that email address" +msgstr "Esa dirección de correo non se pode normalizar " + +#: ../actions/password.php:45 +#: actions/profilesettings.php:184 +#: actions/profilesettings.php:201 +msgid "Change" +msgstr "Modificado" + +#: ../lib/settingsaction.php:88 +#: lib/settingsaction.php:88 +#: actions/deleteprofile.php:245 +msgid "Change email handling" +msgstr "Cambiar a xestión de email" + +#: ../actions/password.php:32 +#: actions/profilesettings.php:36 +msgid "Change password" +msgstr "Cambiar contrasinal" + +#: ../lib/settingsaction.php:94 +msgid "Change your password" +msgstr "Cambiar contrasinal" + +#: ../lib/settingsaction.php:85 +#: lib/settingsaction.php:85 +#: actions/deleteprofile.php:242 +msgid "Change your profile settings" +msgstr "Configuración de perfil" + +#: ../actions/password.php:43 +#: ../actions/recoverpassword.php:181 +#: ../actions/register.php:155 +#: ../actions/smssettings.php:65 +#: actions/profilesettings.php:182 +#: actions/recoverpassword.php:187 +#: actions/register.php:169 +#: actions/smssettings.php:65 +#: actions/profilesettings.php:199 +#: actions/register.php:196 +msgid "Confirm" +msgstr "Confirmar" + +#: ../actions/confirmaddress.php:90 +#: actions/confirmaddress.php:90 +msgid "Confirm Address" +msgstr "Confirmar enderezo" + +#: ../actions/emailsettings.php:238 +#: ../actions/imsettings.php:222 +#: ../actions/smssettings.php:245 +#: actions/emailsettings.php:256 +#: actions/imsettings.php:230 +#: actions/smssettings.php:253 +#: actions/emailsettings.php:260 +#: actions/imsettings.php:235 +#: actions/smssettings.php:255 +msgid "Confirmation cancelled." +msgstr "Confirmación cancealada." + +#: ../actions/smssettings.php:63 +#: actions/smssettings.php:63 +msgid "Confirmation code" +msgstr "Código de confirmación." + +#: ../actions/confirmaddress.php:38 +#: actions/confirmaddress.php:38 +msgid "Confirmation code not found." +msgstr "Confirmation code not found." + +#: ../actions/register.php:202 +#: actions/register.php:243 +#, php-format +msgid "" +"Congratulations, %s! And welcome to %%%%site.name%%%%. From here, you may want to...\n" +"\n" +"* Go to [your profile](%s) and post your first message.\n" +"* Add a [Jabber/GTalk address](%%%%action.imsettings%%%%) so you can send notices through instant messages.\n" +"* [Search for people](%%%%action.peoplesearch%%%%) that you may know or that share your interests. \n" +"* Update your [profile settings](%%%%action.profilesettings%%%%) to tell others more about you. \n" +"* Read over the [online docs](%%%%doc.help%%%%) for features you may have missed. \n" +"\n" +"Thanks for signing up and we hope you enjoy using this service." +msgstr "" +"Noraboa, %s! e benvido a %%%%site.name%%%%. Dende aquí, podes...\n" +"\n" +"* Ír ó [teu perfil](%s) e enviar o teu primeiro chío.\n" +"* Engadir unha [conta de Jabber/Gtalk](%%%%action.imsettings%%%%) para enviar os teus chíos a través de mensaxería instantánea.\n" +"* [Buscar xente ](%%%%action.peoplesearch%%%%) que poidas coñecer ou que comparta os teus intereses. \n" +"* Actualizar as túas [preferencias no perfil](%%%%action.profilesettings%%%%) para decirlle a outros máis sobre ti. \n" +"* Ler os [manuais en liña](%%%%doc.help%%%%) para ollar máis cousas que podes facer aquí. \n" +"\n" +"Grazas por rexistrarte e esperamos que laretexes moito." + +#: ../actions/finishopenidlogin.php:91 +#: actions/finishopenidlogin.php:97 +msgid "Connect" +msgstr "Conectar" + +#: ../actions/finishopenidlogin.php:86 +#: actions/finishopenidlogin.php:92 +msgid "Connect existing account" +msgstr "Conectar con unha conta existente" + +#: ../lib/util.php:332 +#: lib/util.php:348 +#: lib/util.php:377 +msgid "Contact" +msgstr "Contacto" + +#: ../lib/openid.php:178 +#: lib/openid.php:178 +#, php-format +msgid "Could not create OpenID form: %s" +msgstr "Non se pode crear o formulario OpenID: %s" + +#: ../actions/twitapifriendships.php:60 +#: ../actions/twitapifriendships.php:76 +#: actions/twitapifriendships.php:60 +#: actions/twitapifriendships.php:76 +#: actions/twitapifriendships.php:46 +#: actions/twitapifriendships.php:62 +#, php-format +msgid "Could not follow user: %s is already on your list." +msgstr "Non podes seguir a este usuario: %s xa está na túa lista." + +#: ../actions/twitapifriendships.php:53 +#: actions/twitapifriendships.php:53 +#: actions/twitapifriendships.php:39 +msgid "Could not follow user: User not found." +msgstr "Non podes seguir a este usuario: o Usuario non se atopa." + +#: ../lib/openid.php:160 +#: lib/openid.php:160 +#, php-format +msgid "Could not redirect to server: %s" +msgstr "Non se pode redireccionar ao servidor: %s" + +#: ../actions/updateprofile.php:162 +#: actions/updateprofile.php:163 +msgid "Could not save avatar info" +msgstr "Non se pode gardar a información do avatar" + +#: ../actions/updateprofile.php:155 +#: actions/updateprofile.php:156 +msgid "Could not save new profile info" +msgstr "Non se pode gardar a nova información do perfil" + +#: ../lib/subs.php:54 +#: lib/subs.php:61 +#: lib/subs.php:70 +msgid "Could not subscribe other to you." +msgstr "Outro usuario non se puido suscribir a ti." + +#: ../lib/subs.php:46 +#: lib/subs.php:46 +#: lib/subs.php:55 +msgid "Could not subscribe." +msgstr "No se pode suscribir." + +#: ../actions/recoverpassword.php:102 +#: actions/recoverpassword.php:105 +msgid "Could not update user with confirmed email address." +msgstr "Non se puido actualizar o usuario coa dirección de correo electrónico." + +#: ../actions/finishremotesubscribe.php:99 +#: actions/finishremotesubscribe.php:101 +#: actions/finishremotesubscribe.php:112 +msgid "Couldn't convert request tokens to access tokens." +msgstr "Non se pode convertir o token da petición a tokens de acceso." + +#: ../actions/confirmaddress.php:84 +#: ../actions/emailsettings.php:234 +#: ../actions/imsettings.php:218 +#: ../actions/smssettings.php:241 +#: actions/confirmaddress.php:84 +#: actions/emailsettings.php:252 +#: actions/imsettings.php:226 +#: actions/smssettings.php:249 +#: actions/emailsettings.php:256 +#: actions/imsettings.php:231 +#: actions/smssettings.php:251 +msgid "Couldn't delete email confirmation." +msgstr "Non se pode eliminar a confirmación de email." + +#: ../lib/subs.php:103 +#: lib/subs.php:116 +#: lib/subs.php:129 +msgid "Couldn't delete subscription." +msgstr "Non se pode eliminar a subscrición." + +#: ../actions/twitapistatuses.php:93 +#: actions/twitapistatuses.php:98 +#: actions/twitapistatuses.php:82 +msgid "Couldn't find any statuses." +msgstr "Non se puido atopar ningún estado" + +#: ../actions/remotesubscribe.php:127 +#: actions/remotesubscribe.php:136 +#: actions/remotesubscribe.php:148 +msgid "Couldn't get a request token." +msgstr "Non se puido recoller o token de petición." + +#: ../actions/emailsettings.php:205 +#: ../actions/imsettings.php:187 +#: ../actions/smssettings.php:206 +#: actions/emailsettings.php:223 +#: actions/imsettings.php:195 +#: actions/smssettings.php:214 +#: actions/emailsettings.php:229 +#: actions/imsettings.php:200 +#: actions/smssettings.php:216 +msgid "Couldn't insert confirmation code." +msgstr "Non se puido inserir o código de confirmación." + +#: ../actions/finishremotesubscribe.php:180 +#: actions/finishremotesubscribe.php:182 +#: actions/finishremotesubscribe.php:198 +#: actions/finishremotesubscribe.php:216 +msgid "Couldn't insert new subscription." +msgstr "Non se puido inserir a nova subscrición." + +#: ../actions/profilesettings.php:184 +#: ../actions/twitapiaccount.php:96 +#: actions/profilesettings.php:299 +#: actions/twitapiaccount.php:94 +#: actions/profilesettings.php:336 +#: actions/twitapiaccount.php:77 +msgid "Couldn't save profile." +msgstr "Non se puido gardar o perfil." + +#: ../actions/profilesettings.php:161 +#: actions/profilesettings.php:276 +#: actions/profilesettings.php:313 +msgid "Couldn't update user for autosubscribe." +msgstr "Non se puido actualizar o usuario para autosuscrición." + +#: ../actions/emailsettings.php:280 +#: ../actions/emailsettings.php:294 +#: actions/emailsettings.php:298 +#: actions/emailsettings.php:312 +#: actions/emailsettings.php:302 +#: actions/emailsettings.php:316 +msgid "Couldn't update user record." +msgstr "Non se puido actualizar o rexistro de usuario." + +#: ../actions/confirmaddress.php:72 +#: ../actions/emailsettings.php:156 +#: ../actions/emailsettings.php:259 +#: ../actions/imsettings.php:138 +#: ../actions/imsettings.php:243 +#: ../actions/profilesettings.php:141 +#: ../actions/smssettings.php:157 +#: ../actions/smssettings.php:269 +#: actions/confirmaddress.php:72 +#: actions/emailsettings.php:174 +#: actions/emailsettings.php:277 +#: actions/imsettings.php:146 +#: actions/imsettings.php:251 +#: actions/profilesettings.php:256 +#: actions/smssettings.php:165 +#: actions/smssettings.php:277 +#: actions/emailsettings.php:180 +#: actions/emailsettings.php:281 +#: actions/imsettings.php:151 +#: actions/imsettings.php:256 +#: actions/othersettings.php:173 +#: actions/profilesettings.php:293 +#: actions/smspostsettings.php:96 +#: actions/smspostsettings.php:123 +#: actions/smssettings.php:167 +#: actions/smssettings.php:279 +#: actions/smspostsettings.php:101 +#: actions/smspostsettings.php:128 +msgid "Couldn't update user." +msgstr "Non se puido actualizar o usuario." + +#: ../actions/finishopenidlogin.php:84 +#: actions/finishopenidlogin.php:90 +msgid "Create" +msgstr "Crear" + +#: ../actions/finishopenidlogin.php:70 +#: actions/finishopenidlogin.php:76 +msgid "Create a new user with this nickname." +msgstr "Crear un novo usuario con este alcume." + +#: ../actions/finishopenidlogin.php:68 +#: actions/finishopenidlogin.php:74 +msgid "Create new account" +msgstr "Crear nova conta" + +#: ../actions/finishopenidlogin.php:191 +#: actions/finishopenidlogin.php:197 +#: actions/finishopenidlogin.php:208 +msgid "Creating new account for OpenID that already has a user." +msgstr "Creando nova conta para OpenID que xa ten un usuario." + +#: ../actions/imsettings.php:45 +#: actions/imsettings.php:46 +msgid "Current confirmed Jabber/GTalk address." +msgstr "Direccións Jabber/GTalk confirmadas." + +#: ../actions/smssettings.php:46 +#: actions/smssettings.php:46 +msgid "Current confirmed SMS-enabled phone number." +msgstr "Número de teléfono actual confirmado mediante SMS." + +#: ../actions/emailsettings.php:44 +#: actions/emailsettings.php:45 +msgid "Current confirmed email address." +msgstr "Direccións de correo confirmadas actualmente." + +#: ../actions/showstream.php:356 +#: actions/showstream.php:367 +#: actions/showstream.php:418 +msgid "Currently" +msgstr "Actual" + +#: ../classes/Notice.php:72 +#: classes/Notice.php:86 +#: classes/Notice.php:87 +#, php-format +msgid "DB error inserting hashtag: %s" +msgstr "Erro ó inserir o hashtag na BD: %s" + +#: ../lib/util.php:1061 +#: lib/util.php:1110 +#: lib/util.php:1479 +#, php-format +msgid "DB error inserting reply: %s" +msgstr "Erro ó inserir a contestación na BD: %s" + +#: ../actions/deletenotice.php:41 +#: actions/deletenotice.php:41 +msgid "Delete notice" +msgstr "Eliminar chío" + +#: ../actions/profilesettings.php:51 +#: ../actions/register.php:172 +#: actions/profilesettings.php:84 +#: actions/register.php:186 +#: actions/profilesettings.php:86 +#: actions/register.php:213 +msgid "Describe yourself and your interests in 140 chars" +msgstr "Contanos un pouco de ti e dos teus intereses en 140 caractéres." + +#: ../actions/register.php:158 +#: ../actions/register.php:161 +#: ../lib/settingsaction.php:87 +#: actions/register.php:172 +#: actions/register.php:175 +#: lib/settingsaction.php:87 +#: actions/deleteprofile.php:244 +#: actions/register.php:199 +#: actions/register.php:202 +msgid "Email" +msgstr "Correo Electrónico" + +#: ../actions/emailsettings.php:59 +#: actions/emailsettings.php:60 +msgid "Email Address" +msgstr "Enderezo de correo" + +#: ../actions/emailsettings.php:32 +#: actions/emailsettings.php:32 +msgid "Email Settings" +msgstr "Configuración de Correo" + +#: ../actions/register.php:73 +#: actions/register.php:80 +#: actions/register.php:89 +msgid "Email address already exists." +msgstr "O enderezo de correo xa existe." + +#: ../lib/mail.php:90 +#: lib/mail.php:90 +msgid "Email address confirmation" +msgstr "Confirmar correo electrónico" + +#: ../actions/emailsettings.php:61 +#: actions/emailsettings.php:62 +msgid "Email address, like \"UserName@example.org\"" +msgstr "Dirección de correo, coma \"Nomede Usuario@exemplo.org\"" + +#: ../actions/invite.php:129 +#: actions/invite.php:137 +msgid "Email addresses" +msgstr "Enderezos de correo" + +#: ../actions/recoverpassword.php:191 +#: actions/recoverpassword.php:197 +msgid "Enter a nickname or email address." +msgstr "Insire o teu alcume ou enderezo de correo." + +#: ../actions/smssettings.php:64 +#: actions/smssettings.php:64 +msgid "Enter the code you received on your phone." +msgstr "Insire o código que recibiches no teu teléfono." + +#: ../actions/userauthorization.php:137 +#: actions/userauthorization.php:144 +msgid "Error authorizing token" +msgstr "Erro no token de autorización." + +#: ../actions/finishopenidlogin.php:253 +#: actions/finishopenidlogin.php:259 +#: actions/finishopenidlogin.php:274 +msgid "Error connecting user to OpenID." +msgstr "Acounteceu un erro conectando o usuario ó OpenID." + +#: ../actions/finishaddopenid.php:78 +#: actions/finishaddopenid.php:78 +msgid "Error connecting user." +msgstr "Acounteceu un erro ó conectar co usuario" + +#: ../actions/finishremotesubscribe.php:151 +#: actions/finishremotesubscribe.php:153 +#: actions/finishremotesubscribe.php:164 +msgid "Error inserting avatar" +msgstr "Acounteceu un erro ó inserir o avatar" + +#: ../actions/finishremotesubscribe.php:143 +#: actions/finishremotesubscribe.php:145 +#: actions/finishremotesubscribe.php:156 +msgid "Error inserting new profile" +msgstr "Acounteceu un erro ó inserir o novo perfil" + +#: ../actions/finishremotesubscribe.php:167 +#: actions/finishremotesubscribe.php:169 +#: actions/finishremotesubscribe.php:180 +msgid "Error inserting remote profile" +msgstr "Aconteceu un erro ó inserir o perfil remoto" + +#: ../actions/recoverpassword.php:240 +#: actions/recoverpassword.php:246 +msgid "Error saving address confirmation." +msgstr "Acounteceu un erro gardando a confirmación de enderezo." + +#: ../actions/userauthorization.php:140 +#: actions/userauthorization.php:147 +msgid "Error saving remote profile" +msgstr "Acounteceu un erro gardando o perfil remoto." + +#: ../lib/openid.php:226 +#: lib/openid.php:226 +msgid "Error saving the profile." +msgstr "Acounteceu un erro ó gardar o perfil." + +#: ../lib/openid.php:237 +#: lib/openid.php:237 +msgid "Error saving the user." +msgstr "Acounteceu un erro gardando o usuario." + +#: ../actions/password.php:80 +#: actions/profilesettings.php:399 +#: actions/profilesettings.php:445 +msgid "Error saving user; invalid." +msgstr "Acounteceu un erro gardando o usuario: é inválido." + +#: ../actions/login.php:47 +#: ../actions/login.php:73 +#: ../actions/recoverpassword.php:307 +#: ../actions/register.php:98 +#: actions/login.php:47 +#: actions/login.php:73 +#: actions/recoverpassword.php:320 +#: actions/register.php:108 +#: actions/login.php:54 +#: actions/login.php:80 +#: actions/register.php:117 +msgid "Error setting user." +msgstr "Acounteceu un erro configurando o usuario." + +#: ../actions/finishaddopenid.php:83 +#: actions/finishaddopenid.php:83 +msgid "Error updating profile" +msgstr "Acounteceu un erro actualizando o usuario" + +#: ../actions/finishremotesubscribe.php:161 +#: actions/finishremotesubscribe.php:163 +#: actions/finishremotesubscribe.php:174 +msgid "Error updating remote profile" +msgstr "Acounteceu un erro actualizando o perfil remoto" + +#: ../actions/recoverpassword.php:80 +#: actions/recoverpassword.php:80 +msgid "Error with confirmation code." +msgstr "Acounteceu un erro co código de confirmación." + +#: ../actions/finishopenidlogin.php:89 +#: actions/finishopenidlogin.php:95 +msgid "Existing nickname" +msgstr "Alcume existente" + +#: ../lib/util.php:326 +#: lib/util.php:342 +#: lib/util.php:371 +msgid "FAQ" +msgstr "Preguntas frecuentes" + +#: ../actions/avatar.php:115 +#: actions/profilesettings.php:352 +#: actions/profilesettings.php:398 +msgid "Failed updating avatar." +msgstr "Acounteceu un fallo ó actualizar o avatar." + +#: ../actions/all.php:61 +#: ../actions/allrss.php:64 +#: actions/all.php:61 +#: actions/allrss.php:64 +#, php-format +msgid "Feed for friends of %s" +msgstr "Fonte para os amigos de %s" + +#: ../actions/replies.php:65 +#: ../actions/repliesrss.php:80 +#: actions/replies.php:65 +#: actions/repliesrss.php:66 +#, php-format +msgid "Feed for replies to %s" +msgstr "Fonte para as contestacións de %s" + +#: ../actions/tag.php:55 +#: actions/tag.php:55 +#: actions/tag.php:61 +#, php-format +msgid "Feed for tag %s" +msgstr "Fonte para a etiqueta %s" + +#: ../lib/searchaction.php:105 +#: lib/searchaction.php:105 +msgid "Find content of notices" +msgstr "Atopar no contido dos chíos" + +#: ../lib/searchaction.php:101 +#: lib/searchaction.php:101 +msgid "Find people on this site" +msgstr "Atopar xente neste sitio" + +#: ../actions/login.php:122 +#: actions/login.php:142 +msgid "For security reasons, please re-enter your user name and password before changing your settings." +msgstr "Por razóns de seguranza, por favor re-insire o teu nome de usuario e contrasinal antes de cambiar as túas preferenzas." + +#: ../actions/profilesettings.php:44 +#: ../actions/register.php:164 +#: actions/profilesettings.php:77 +#: actions/register.php:178 +#: actions/profilesettings.php:79 +#: actions/register.php:205 +msgid "Full name" +msgstr "Nome completo" + +#: ../actions/profilesettings.php:98 +#: ../actions/register.php:79 +#: ../actions/updateprofile.php:93 +#: actions/profilesettings.php:213 +#: actions/register.php:86 +#: actions/updateprofile.php:94 +#: actions/profilesettings.php:232 +#: actions/register.php:95 +msgid "Full name is too long (max 255 chars)." +msgstr "O nome completo é demasiado longo (max 255 car)." + +#: ../lib/util.php:322 +#: lib/util.php:338 +#: lib/util.php:359 +#: lib/util.php:367 +msgid "Help" +msgstr "Axuda" + +#: ../lib/util.php:298 +#: lib/util.php:314 +#: lib/util.php:343 +msgid "Home" +msgstr "Persoal" + +#: ../actions/profilesettings.php:46 +#: ../actions/register.php:167 +#: actions/profilesettings.php:79 +#: actions/register.php:181 +#: actions/profilesettings.php:81 +#: actions/register.php:208 +msgid "Homepage" +msgstr "Páxina persoal" + +#: ../actions/profilesettings.php:95 +#: ../actions/register.php:76 +#: actions/profilesettings.php:210 +#: actions/register.php:83 +#: actions/profilesettings.php:229 +#: actions/register.php:92 +msgid "Homepage is not a valid URL." +msgstr "A páxina persoal semella que non é unha URL válida." + +#: ../actions/emailsettings.php:91 +#: actions/emailsettings.php:98 +msgid "I want to post notices by email." +msgstr "Quero enviar chíos dende o mail." + +#: ../lib/settingsaction.php:102 +#: lib/settingsaction.php:96 +#: actions/deleteprofile.php:253 +#: lib/settingsaction.php:100 +#: lib/settingsaction.php:102 +msgid "IM" +msgstr "IM" + +#: ../actions/imsettings.php:60 +#: actions/imsettings.php:61 +msgid "IM Address" +msgstr "Enderezo de IM" + +#: ../actions/imsettings.php:33 +#: actions/imsettings.php:33 +msgid "IM Settings" +msgstr "Configuracións de IM" + +#: ../actions/finishopenidlogin.php:88 +#: actions/finishopenidlogin.php:94 +msgid "If you already have an account, login with your username and password to connect it to your OpenID." +msgstr "Se xa tes unha conta, accede co teu usuario e contrasinal para conectar co teu OpenID." + +#: ../actions/openidsettings.php:45 +#: actions/openidsettings.php:46 +msgid "If you want to add an OpenID to your account, enter it in the box below and click \"Add\"." +msgstr "Se queres engadir un enderezo OpenID á tua conta, insirea na caixa de embaixo e fai clic en \"Engadir\"." + +#: ../actions/recoverpassword.php:137 +#: actions/recoverpassword.php:141 +msgid "If you've forgotten or lost your password, you can get a new one sent to the email address you have stored in your account." +msgstr "Se esquenceches ou perdeches a túa contrasinal, podes obter unha nova no enderezo de correo que configuraches na túa conta." + +#: ../actions/emailsettings.php:67 +#: ../actions/smssettings.php:76 +#: actions/emailsettings.php:68 +#: actions/smssettings.php:76 +msgid "Incoming email" +msgstr "Correo Entrante" + +#: ../actions/emailsettings.php:283 +#: actions/emailsettings.php:301 +#: actions/emailsettings.php:305 +msgid "Incoming email address removed." +msgstr "Dirección de correo entrante eliminada." + +#: ../actions/password.php:69 +#: actions/profilesettings.php:388 +#: actions/profilesettings.php:434 +msgid "Incorrect old password" +msgstr "Contrasinal actual incorrecta" + +#: ../actions/login.php:67 +#: actions/login.php:67 +#: actions/login.php:74 +msgid "Incorrect username or password." +msgstr "Usuario ou contrasinal incorrectos." + +#: ../actions/recoverpassword.php:265 +#: actions/recoverpassword.php:271 +msgid "Instructions for recovering your password have been sent to the email address registered to your account." +msgstr "As instruccións para recuperar a túa contrasinal foron enviadas ó enderezo de correo da túa conta." + +#: ../actions/updateprofile.php:114 +#: actions/updateprofile.php:115 +#, php-format +msgid "Invalid avatar URL '%s'" +msgstr "URL do avatar '%s' inválido" + +#: ../actions/invite.php:55 +#: actions/invite.php:62 +#, php-format +msgid "Invalid email address: %s" +msgstr "Dirección de correo Inválida: %s" + +#: ../actions/updateprofile.php:98 +#: actions/updateprofile.php:99 +#, php-format +msgid "Invalid homepage '%s'" +msgstr "Páxina persoal '%s' inválida" + +#: ../actions/updateprofile.php:82 +#: actions/updateprofile.php:83 +#, php-format +msgid "Invalid license URL '%s'" +msgstr "URL de licenza '%s' inválida" + +#: ../actions/postnotice.php:61 +#: actions/postnotice.php:62 +#: actions/postnotice.php:63 +msgid "Invalid notice content" +msgstr "Contido do chío inválido" + +#: ../actions/postnotice.php:67 +#: actions/postnotice.php:68 +#: actions/postnotice.php:69 +msgid "Invalid notice uri" +msgstr "Enderezo de chío inválido" + +#: ../actions/postnotice.php:72 +#: actions/postnotice.php:73 +#: actions/postnotice.php:74 +msgid "Invalid notice url" +msgstr "Enderezo de chío inválido" + +#: ../actions/updateprofile.php:87 +#: actions/updateprofile.php:88 +#, php-format +msgid "Invalid profile URL '%s'." +msgstr "Enderezo de perfil inválido '%s'." + +#: ../actions/remotesubscribe.php:96 +#: actions/remotesubscribe.php:105 +msgid "Invalid profile URL (bad format)" +msgstr "Enderezo de perfil inválido (formato incorrecto)" + +#: ../actions/finishremotesubscribe.php:77 +#: actions/finishremotesubscribe.php:79 +#: actions/finishremotesubscribe.php:78 +msgid "Invalid profile URL returned by server." +msgstr "Enderezo de perfil inválido devolto polo servidor." + +#: ../actions/avatarbynickname.php:37 +#: actions/avatarbynickname.php:37 +msgid "Invalid size." +msgstr "Tamaño inválido." + +#: ../actions/finishopenidlogin.php:235 +#: ../actions/register.php:93 +#: ../actions/register.php:111 +#: actions/finishopenidlogin.php:241 +#: actions/register.php:103 +#: actions/register.php:121 +#: actions/finishopenidlogin.php:256 +#: actions/register.php:112 +#: actions/register.php:130 +msgid "Invalid username or password." +msgstr "Usuario ou contrasinal inválidos." + +#: ../actions/invite.php:79 +#: actions/invite.php:86 +msgid "Invitation(s) sent" +msgstr "Invitación(s) enviada(s)." + +#: ../actions/invite.php:97 +#: actions/invite.php:104 +msgid "Invitation(s) sent to the following people:" +msgstr "Invitación(s) enviada(s) á seguinte xente:" + +#: ../lib/util.php:306 +#: lib/util.php:322 +#: lib/util.php:382 +msgid "Invite" +msgstr "Invitar" + +#: ../actions/invite.php:123 +#: actions/invite.php:130 +msgid "Invite new users" +msgstr "Invitar a novos usuarios" + +#: ../lib/util.php:261 +#: lib/util.php:277 +#: lib/util.php:294 +#, php-format +msgid "It runs the [StatusNet](http://status.net/) microblogging software, version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html)." +msgstr "Correndo o software de microblogaxe [StatusNet](http://status.net/), versión %s, dispoñible baixo licenza [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html)." + +#: ../actions/imsettings.php:173 +#: actions/imsettings.php:181 +#: actions/imsettings.php:186 +msgid "Jabber ID already belongs to another user." +msgstr "O identificador de Jabber xa pertence a outro usuario." + +#: ../actions/imsettings.php:62 +#: actions/imsettings.php:63 +#, php-format +msgid "Jabber or GTalk address, like \"UserName@example.org\". First, make sure to add %s to your buddy list in your IM client or on GTalk." +msgstr "Enderezo Jabber ou GTalk, coma \"NomeUsuario@Exemplo.org\". Primeiro, asegurate de engadir %s á tua lista de contactos no teu cliente de IM ou no GTalk." + +#: ../actions/profilesettings.php:57 +#: actions/profilesettings.php:90 +#: actions/profilesettings.php:98 +msgid "Language" +msgstr "Linguaxe" + +#: ../actions/profilesettings.php:113 +#: actions/profilesettings.php:228 +#: actions/profilesettings.php:247 +msgid "Language is too long (max 50 chars)." +msgstr "A Linguaxe é demasiado longa (max 50 car.)." + +#: ../actions/profilesettings.php:52 +#: ../actions/register.php:173 +#: actions/profilesettings.php:85 +#: actions/register.php:187 +#: actions/profilesettings.php:87 +#: actions/register.php:214 +msgid "Location" +msgstr "Localización" + +#: ../actions/profilesettings.php:104 +#: ../actions/register.php:85 +#: ../actions/updateprofile.php:108 +#: actions/profilesettings.php:219 +#: actions/register.php:92 +#: actions/updateprofile.php:109 +#: actions/profilesettings.php:238 +#: actions/register.php:101 +msgid "Location is too long (max 255 chars)." +msgstr "A localización é demasiado longa (max 255 car.)." + +#: ../actions/login.php:97 +#: ../actions/login.php:106 +#: ../actions/openidlogin.php:68 +#: ../lib/util.php:310 +#: actions/login.php:97 +#: actions/login.php:106 +#: actions/openidlogin.php:77 +#: lib/util.php:326 +#: actions/login.php:104 +#: actions/login.php:113 +#: actions/login.php:129 +#: actions/openidlogin.php:88 +#: lib/util.php:352 +msgid "Login" +msgstr "Inicio de sesión" + +#: ../actions/openidlogin.php:44 +#: actions/openidlogin.php:52 +#: actions/openidlogin.php:60 +#, php-format +msgid "Login with an [OpenID](%%doc.openid%%) account." +msgstr "Acceder con unha conta [OpenID](%%doc.openid%%)." + +#: ../actions/login.php:126 +#: actions/login.php:146 +#, php-format +msgid "Login with your username and password. Don't have a username yet? [Register](%%action.register%%) a new account, or try [OpenID](%%action.openidlogin%%). " +msgstr "Accede co teu nome de usuario e contrasinal. ¿Non tes un todavía?? [Rexistra](%%action.register%%) unha nova conta, ou accede co teu enderezo [OpenID](%%action.openidlogin%%). " + +#: ../lib/util.php:308 +#: lib/util.php:324 +#: lib/util.php:350 +msgid "Logout" +msgstr "Sair" + +#: ../actions/register.php:166 +#: actions/register.php:180 +#: actions/register.php:207 +msgid "Longer name, preferably your \"real\" name" +msgstr "Nome máis longo, preferiblemente o teu nome \"real\"" + +#: ../actions/login.php:110 +#: actions/login.php:110 +#: actions/login.php:118 +msgid "Lost or forgotten password?" +msgstr "¿Perdeches a contrasinal?" + +#: ../actions/emailsettings.php:80 +#: ../actions/smssettings.php:89 +#: actions/emailsettings.php:81 +#: actions/smssettings.php:89 +msgid "Make a new email address for posting to; cancels the old one." +msgstr "Crear unha nova dirección de correo para enviar, elimina a antiga." + +#: ../actions/emailsettings.php:27 +#: actions/emailsettings.php:27 +#, php-format +msgid "Manage how you get email from %%site.name%%." +msgstr "Xestina como recibir correo dende %%site.name%%." + +#: ../actions/showstream.php:300 +#: actions/showstream.php:315 +#: actions/showstream.php:363 +msgid "Member since" +msgstr "Membro dende" + +#: ../actions/userrss.php:70 +#: actions/userrss.php:67 +#, php-format +msgid "Microblog by %s" +msgstr "Microblogue por %s" + +#: ../actions/smssettings.php:304 +#: actions/smssettings.php:314 +#, php-format +msgid "Mobile carrier for your phone. If you know a carrier that accepts SMS over email but isn't listed here, send email to let us know at %s." +msgstr "Operadora móbil do teu teléfono. Se sabes se a operadora acepta SMS sobre email e non está listada aquí, envianos unha mensaxe para incluilo en %s." + +#: ../actions/finishopenidlogin.php:79 +#: ../actions/register.php:188 +#: actions/finishopenidlogin.php:85 +#: actions/register.php:202 +#: actions/register.php:229 +msgid "My text and files are available under " +msgstr "O meu texto e arquivos están dispoñibles baixo licenza " + +#: ../actions/emailsettings.php:82 +#: ../actions/smssettings.php:91 +#: actions/emailsettings.php:83 +#: actions/smssettings.php:91 +msgid "New" +msgstr "Novo" + +#: ../lib/mail.php:144 +#: lib/mail.php:144 +#: lib/mail.php:138 +#, php-format +msgid "New email address for posting to %s" +msgstr "Nova dirección de email para posterar en %s" + +#: ../actions/emailsettings.php:297 +#: actions/emailsettings.php:315 +#: actions/emailsettings.php:319 +msgid "New incoming email address added." +msgstr "Engadida nova dirección de correo entrante." + +#: ../actions/finishopenidlogin.php:71 +#: actions/finishopenidlogin.php:77 +msgid "New nickname" +msgstr "Novo alcume" + +#: ../actions/newnotice.php:87 +#: actions/newnotice.php:96 +#: actions/newnotice.php:141 +msgid "New notice" +msgstr "Novo chío" + +#: ../actions/password.php:41 +#: ../actions/recoverpassword.php:179 +#: actions/profilesettings.php:180 +#: actions/recoverpassword.php:185 +#: actions/profilesettings.php:197 +msgid "New password" +msgstr "Nova contrasinal" + +#: ../actions/recoverpassword.php:314 +#: actions/recoverpassword.php:327 +msgid "New password successfully saved. You are now logged in." +msgstr "A nova contrasinal gardouse correctamente. Xa estas logueado." + +#: ../actions/login.php:101 +#: ../actions/profilesettings.php:41 +#: ../actions/register.php:151 +#: actions/login.php:101 +#: actions/profilesettings.php:74 +#: actions/register.php:165 +#: actions/login.php:108 +#: actions/profilesettings.php:76 +#: actions/register.php:192 +msgid "Nickname" +msgstr "Alcume" + +#: ../actions/finishopenidlogin.php:175 +#: ../actions/profilesettings.php:110 +#: ../actions/register.php:69 +#: actions/finishopenidlogin.php:181 +#: actions/profilesettings.php:225 +#: actions/register.php:76 +#: actions/finishopenidlogin.php:192 +#: actions/profilesettings.php:244 +#: actions/register.php:85 +msgid "Nickname already in use. Try another one." +msgstr "O alcume xa está sendo empregado por outro usuario. Tenta con outro." + +#: ../actions/finishopenidlogin.php:165 +#: ../actions/profilesettings.php:88 +#: ../actions/register.php:67 +#: ../actions/updateprofile.php:77 +#: actions/finishopenidlogin.php:171 +#: actions/profilesettings.php:203 +#: actions/register.php:74 +#: actions/updateprofile.php:78 +#: actions/finishopenidlogin.php:182 +#: actions/profilesettings.php:222 +#: actions/register.php:83 +msgid "Nickname must have only lowercase letters and numbers and no spaces." +msgstr "O alcume debe ter só letras minúsculas e números, e sen espazos." + +#: ../actions/finishopenidlogin.php:170 +#: actions/finishopenidlogin.php:176 +#: actions/finishopenidlogin.php:187 +msgid "Nickname not allowed." +msgstr "Alcume non permitido." + +#: ../actions/remotesubscribe.php:72 +#: actions/remotesubscribe.php:81 +msgid "Nickname of the user you want to follow" +msgstr "Alcume de usuario que queres" + +#: ../actions/recoverpassword.php:162 +#: actions/recoverpassword.php:167 +msgid "Nickname or email" +msgstr "Alcume ou email" + +#: ../actions/deletenotice.php:59 +#: actions/deletenotice.php:60 +#: actions/block.php:104 +msgid "No" +msgstr "No" + +#: ../actions/imsettings.php:156 +#: actions/imsettings.php:164 +#: actions/imsettings.php:169 +msgid "No Jabber ID." +msgstr "Sen Identificador de Jabber." + +#: ../actions/userauthorization.php:129 +#: actions/userauthorization.php:136 +msgid "No authorization request!" +msgstr "Sen petición de autorización!" + +#: ../actions/smssettings.php:181 +#: actions/smssettings.php:189 +#: actions/smssettings.php:191 +msgid "No carrier selected." +msgstr "Non se seleccionou unha operadora." + +#: ../actions/smssettings.php:316 +#: actions/smssettings.php:324 +#: actions/smssettings.php:326 +msgid "No code entered" +msgstr "Non se inseriu ningún código" + +#: ../actions/confirmaddress.php:33 +#: actions/confirmaddress.php:33 +msgid "No confirmation code." +msgstr "Sen código de confirmación." + +#: ../actions/newnotice.php:44 +#: actions/newmessage.php:53 +#: actions/newnotice.php:44 +#: classes/Command.php:197 +#: actions/newmessage.php:52 +#: actions/newnotice.php:53 +msgid "No content!" +msgstr "Sen contido!" + +#: ../actions/emailsettings.php:174 +#: actions/emailsettings.php:192 +#: actions/emailsettings.php:198 +msgid "No email address." +msgstr "Non se inseriu unha dirección de correo" + +#: ../actions/userbyid.php:32 +#: actions/userbyid.php:32 +msgid "No id." +msgstr "Sen id." + +#: ../actions/emailsettings.php:271 +#: actions/emailsettings.php:289 +#: actions/emailsettings.php:293 +msgid "No incoming email address." +msgstr "Non hai direccións de correo entrante" + +#: ../actions/finishremotesubscribe.php:65 +#: actions/finishremotesubscribe.php:67 +#: actions/finishremotesubscribe.php:66 +msgid "No nickname provided by remote server." +msgstr "O servidor remoto non proporcionou un alcume." + +#: ../actions/avatarbynickname.php:27 +#: actions/avatarbynickname.php:27 +msgid "No nickname." +msgstr "Sen alcume." + +#: ../actions/emailsettings.php:222 +#: ../actions/imsettings.php:206 +#: ../actions/smssettings.php:229 +#: actions/emailsettings.php:240 +#: actions/imsettings.php:214 +#: actions/smssettings.php:237 +#: actions/emailsettings.php:244 +#: actions/imsettings.php:219 +#: actions/smssettings.php:239 +msgid "No pending confirmation to cancel." +msgstr "Non hai ningunha confirmación pendente para cancelar." + +#: ../actions/smssettings.php:176 +#: actions/smssettings.php:184 +#: actions/smspostsettings.php:76 +#: actions/smssettings.php:186 +#: actions/smspostsettings.php:81 +msgid "No phone number." +msgstr "Non hai ningún número de teléfono." + +#: ../actions/finishremotesubscribe.php:72 +#: actions/finishremotesubscribe.php:74 +#: actions/finishremotesubscribe.php:73 +msgid "No profile URL returned by server." +msgstr "O servidor non voltou ningún enderezo de perfil." + +#: ../actions/recoverpassword.php:226 +#: actions/recoverpassword.php:232 +msgid "No registered email address for that user." +msgstr "Non hai un enderezo de correo rexistrado para ese usuario." + +#: ../actions/userauthorization.php:49 +#: actions/userauthorization.php:55 +msgid "No request found!" +msgstr "¡Non se atoparon peticións!" + +#: ../actions/noticesearch.php:64 +#: ../actions/peoplesearch.php:64 +#: actions/noticesearch.php:69 +#: actions/peoplesearch.php:69 +#: actions/noticesearch.php:68 +#: actions/peoplesearch.php:59 +msgid "No results" +msgstr "Non se atoparon resultados" + +#: ../actions/avatarbynickname.php:32 +#: actions/avatarbynickname.php:32 +msgid "No size." +msgstr "Sen tamaño." + +#: ../actions/twitapistatuses.php:595 +#: actions/twitapifavorites.php:136 +#: actions/twitapistatuses.php:520 +#: actions/twitapifavorites.php:109 +#: actions/twitapistatuses.php:438 +msgid "No status found with that ID." +msgstr "Non se atopou un estado con ese ID." + +#: ../actions/twitapistatuses.php:555 +#: actions/twitapistatuses.php:478 +#: actions/twitapistatuses.php:411 +msgid "No status with that ID found." +msgstr "Non existe ningún estado con esa ID atopada." + +#: ../actions/openidsettings.php:135 +#: actions/openidsettings.php:144 +msgid "No such OpenID." +msgstr "Ningún OpenID." + +#: ../actions/doc.php:29 +#: actions/doc.php:29 +msgid "No such document." +msgstr "Ningún documento." + +#: ../actions/shownotice.php:32 +#: ../actions/shownotice.php:83 +#: ../lib/deleteaction.php:30 +#: actions/shownotice.php:32 +#: actions/shownotice.php:83 +#: lib/deleteaction.php:30 +#: actions/shownotice.php:38 +#: actions/shownotice.php:114 +msgid "No such notice." +msgstr "Ningún chío." + +#: ../actions/recoverpassword.php:56 +#: actions/recoverpassword.php:56 +msgid "No such recovery code." +msgstr "Ningún código de recuperación." + +#: ../actions/postnotice.php:56 +#: actions/postnotice.php:57 +msgid "No such subscription" +msgstr "Ningunha subscripción" + +#: ../actions/all.php:34 +#: ../actions/allrss.php:35 +#: ../actions/avatarbynickname.php:43 +#: ../actions/foaf.php:40 +#: ../actions/remotesubscribe.php:84 +#: ../actions/remotesubscribe.php:91 +#: ../actions/replies.php:57 +#: ../actions/repliesrss.php:35 +#: ../actions/showstream.php:110 +#: ../actions/userbyid.php:36 +#: ../actions/userrss.php:35 +#: ../actions/xrds.php:35 +#: ../lib/gallery.php:57 +#: ../lib/subs.php:33 +#: ../lib/subs.php:82 +#: actions/all.php:34 +#: actions/allrss.php:35 +#: actions/avatarbynickname.php:43 +#: actions/favoritesrss.php:35 +#: actions/foaf.php:40 +#: actions/ical.php:31 +#: actions/remotesubscribe.php:93 +#: actions/remotesubscribe.php:100 +#: actions/replies.php:57 +#: actions/repliesrss.php:35 +#: actions/showfavorites.php:34 +#: actions/showstream.php:110 +#: actions/userbyid.php:36 +#: actions/userrss.php:35 +#: actions/xrds.php:35 +#: classes/Command.php:120 +#: classes/Command.php:162 +#: classes/Command.php:203 +#: classes/Command.php:237 +#: lib/gallery.php:62 +#: lib/mailbox.php:36 +#: lib/subs.php:33 +#: lib/subs.php:95 +#: actions/showstream.php:157 +#: classes/Command.php:208 +#: classes/Command.php:242 +#: lib/gallery.php:85 +#: lib/subs.php:105 +msgid "No such user." +msgstr "Ningún usuario." + +#: ../actions/recoverpassword.php:211 +#: actions/recoverpassword.php:217 +msgid "No user with that email address or username." +msgstr "Non hai ningún usuario con isa dirección de correo ou nome de usuario." + +#: ../lib/gallery.php:80 +#: lib/gallery.php:85 +#: actions/users.php:59 +msgid "Nobody to show!" +msgstr "Ninguén para amosar" + +#: ../actions/recoverpassword.php:60 +#: actions/recoverpassword.php:60 +msgid "Not a recovery code." +msgstr "Non é un código de recuperación." + +#: ../scripts/maildaemon.php:50 +#: scripts/maildaemon.php:50 +msgid "Not a registered user." +msgstr "Non é un usuario rexistrado." + +#: ../lib/twitterapi.php:226 +#: ../lib/twitterapi.php:247 +#: ../lib/twitterapi.php:332 +#: lib/twitterapi.php:391 +#: lib/twitterapi.php:418 +#: lib/twitterapi.php:502 +#: lib/twitterapi.php:423 +#: lib/twitterapi.php:450 +#: lib/twitterapi.php:534 +msgid "Not a supported data format." +msgstr "Non é un formato de datos soportado." + +#: ../actions/imsettings.php:167 +#: actions/imsettings.php:175 +#: actions/imsettings.php:180 +msgid "Not a valid Jabber ID" +msgstr "Non é un Identificador de Jabber válido" + +#: ../lib/openid.php:131 +#: lib/openid.php:131 +msgid "Not a valid OpenID." +msgstr "Non é un enderezo OpenID válido." + +#: ../actions/emailsettings.php:185 +#: actions/emailsettings.php:203 +#: actions/emailsettings.php:209 +msgid "Not a valid email address" +msgstr "Non é unha dirección de correo válida" + +#: ../actions/register.php:63 +#: actions/register.php:70 +#: actions/register.php:79 +msgid "Not a valid email address." +msgstr "Non é un enderezo de correo válido." + +#: ../actions/profilesettings.php:91 +#: ../actions/register.php:71 +#: actions/profilesettings.php:206 +#: actions/register.php:78 +#: actions/profilesettings.php:225 +#: actions/register.php:87 +msgid "Not a valid nickname." +msgstr "Non é un alcume válido." + +#: ../actions/remotesubscribe.php:120 +#: actions/remotesubscribe.php:129 +msgid "Not a valid profile URL (incorrect services)." +msgstr "Non é un enderezo de perfil válido (servizos incorrectos)." + +#: ../actions/remotesubscribe.php:113 +#: actions/remotesubscribe.php:122 +msgid "Not a valid profile URL (no XRDS defined)." +msgstr "Non é un enderezo de perfil válido (non está definido ningún XRDS)." + +#: ../actions/remotesubscribe.php:104 +#: actions/remotesubscribe.php:113 +msgid "Not a valid profile URL (no YADIS document)." +msgstr "Non é un enderezo de perfil válido (non ten documento YADIS)." + +#: ../actions/avatar.php:95 +#: actions/profilesettings.php:332 +#: actions/profilesettings.php:378 +msgid "Not an image or corrupt file." +msgstr "Non é unha imaxe ou está corrupta." + +#: ../actions/finishremotesubscribe.php:51 +#: actions/finishremotesubscribe.php:53 +#: actions/finishremotesubscribe.php:52 +msgid "Not authorized." +msgstr "Non está autorizado." + +#: ../actions/finishremotesubscribe.php:38 +#: actions/finishremotesubscribe.php:38 +msgid "Not expecting this response!" +msgstr "¡Non esperaba esa resposta!" + +#: ../actions/twitapistatuses.php:422 +#: actions/twitapistatuses.php:361 +#: actions/twitapistatuses.php:304 +msgid "Not found" +msgstr "Non atopado" + +#: ../actions/finishaddopenid.php:29 +#: ../actions/logout.php:33 +#: ../actions/newnotice.php:29 +#: ../actions/subscribe.php:28 +#: ../actions/unsubscribe.php:25 +#: ../lib/deleteaction.php:38 +#: ../lib/settingsaction.php:27 +#: actions/disfavor.php:29 +#: actions/favor.php:30 +#: actions/finishaddopenid.php:29 +#: actions/logout.php:33 +#: actions/newmessage.php:28 +#: actions/newnotice.php:29 +#: actions/subscribe.php:28 +#: actions/unsubscribe.php:25 +#: lib/deleteaction.php:38 +#: lib/settingsaction.php:27 +#: actions/block.php:31 +#: actions/misc.php:27 +#: actions/newnotice.php:30 +#: actions/nudge.php:30 +#: actions/subedit.php:31 +#: actions/unblock.php:31 +msgid "Not logged in." +msgstr "Non está logueado." + +#: ../lib/subs.php:91 +#: lib/subs.php:104 +#: lib/subs.php:117 +msgid "Not subscribed!." +msgstr "Non está suscrito!" + +#: ../actions/opensearch.php:35 +#: actions/opensearch.php:35 +msgid "Notice Search" +msgstr "Procura de Chíos" + +#: ../actions/showstream.php:82 +#: actions/showstream.php:82 +#: actions/showstream.php:111 +#: actions/showstream.php:118 +#: actions/showstream.php:123 +#, php-format +msgid "Notice feed for %s" +msgstr "Fonte de chíos para %s" + +#: ../actions/shownotice.php:39 +#: actions/shownotice.php:39 +#: actions/shownotice.php:45 +msgid "Notice has no profile" +msgstr "O chío non ten perfil" + +#: ../actions/showstream.php:316 +#: actions/showstream.php:331 +#: actions/showstream.php:379 +msgid "Notices" +msgstr "Chíos" + +#: ../actions/tag.php:35 +#: ../actions/tag.php:81 +#: actions/tag.php:35 +#: actions/tag.php:81 +#, php-format +msgid "Notices tagged with %s" +msgstr "Chíos tagueados con %s" + +#: ../actions/password.php:39 +#: actions/profilesettings.php:178 +#: actions/profilesettings.php:195 +msgid "Old password" +msgstr "Contrasinal antiga" + +#: ../lib/settingsaction.php:96 +#: ../lib/util.php:314 +#: lib/settingsaction.php:90 +#: lib/util.php:330 +#: actions/deleteprofile.php:247 +msgid "OpenID" +msgstr "OpenID" + +#: ../actions/finishopenidlogin.php:61 +#: actions/finishopenidlogin.php:66 +msgid "OpenID Account Setup" +msgstr "Configuración de conta OpenID" + +#: ../lib/openid.php:180 +#: lib/openid.php:180 +msgid "OpenID Auto-Submit" +msgstr "Auto-Envío de OpenID" + +#: ../actions/finishaddopenid.php:99 +#: ../actions/finishopenidlogin.php:140 +#: ../actions/openidlogin.php:60 +#: actions/finishaddopenid.php:99 +#: actions/finishopenidlogin.php:146 +#: actions/openidlogin.php:68 +#: actions/finishopenidlogin.php:150 +#: actions/openidlogin.php:76 +msgid "OpenID Login" +msgstr "Acceso OpenID" + +#: ../actions/openidlogin.php:65 +#: ../actions/openidsettings.php:49 +#: actions/openidlogin.php:74 +#: actions/openidsettings.php:50 +#: actions/login.php:125 +#: actions/openidlogin.php:82 +msgid "OpenID URL" +msgstr "Enderezo OpenID" + +#: ../actions/finishaddopenid.php:42 +#: ../actions/finishopenidlogin.php:103 +#: actions/finishaddopenid.php:42 +#: actions/finishopenidlogin.php:109 +msgid "OpenID authentication cancelled." +msgstr "Autenticación OpenID cancelada." + +#: ../actions/finishaddopenid.php:46 +#: ../actions/finishopenidlogin.php:107 +#: actions/finishaddopenid.php:46 +#: actions/finishopenidlogin.php:113 +#, php-format +msgid "OpenID authentication failed: %s" +msgstr "A autenticación OpenID fallou: %s" + +#: ../lib/openid.php:133 +#: lib/openid.php:133 +#, php-format +msgid "OpenID failure: %s" +msgstr "Fallou o OpenID: %s" + +#: ../actions/openidsettings.php:144 +#: actions/openidsettings.php:153 +msgid "OpenID removed." +msgstr "OpenID eliminado." + +#: ../actions/openidsettings.php:37 +#: actions/openidsettings.php:37 +msgid "OpenID settings" +msgstr "Configuracións de OpenID" + +#: ../actions/invite.php:135 +#: actions/invite.php:143 +msgid "Optionally add a personal message to the invitation." +msgstr "Opcionalmente engadir unha mensaxe persoal á invitación." + +#: ../actions/avatar.php:84 +#: actions/profilesettings.php:321 +#: actions/profilesettings.php:367 +msgid "Partial upload." +msgstr "Carga parcial." + +#: ../actions/finishopenidlogin.php:90 +#: ../actions/login.php:102 +#: ../actions/register.php:153 +#: ../lib/settingsaction.php:93 +#: actions/finishopenidlogin.php:96 +#: actions/login.php:102 +#: actions/register.php:167 +#: actions/login.php:109 +#: actions/register.php:194 +msgid "Password" +msgstr "Contrasinal" + +#: ../actions/recoverpassword.php:288 +#: actions/recoverpassword.php:301 +msgid "Password and confirmation do not match." +msgstr "A contrasinal e a súa confirmación non coinciden." + +#: ../actions/recoverpassword.php:284 +#: actions/recoverpassword.php:297 +msgid "Password must be 6 chars or more." +msgstr "A contrasinal debe ter 6 caracteres ou máis." + +#: ../actions/recoverpassword.php:261 +#: ../actions/recoverpassword.php:263 +#: actions/recoverpassword.php:267 +#: actions/recoverpassword.php:269 +msgid "Password recovery requested" +msgstr "Petición de recuperación de contrasinal" + +#: ../actions/password.php:89 +#: ../actions/recoverpassword.php:313 +#: actions/profilesettings.php:408 +#: actions/recoverpassword.php:326 +#: actions/profilesettings.php:454 +msgid "Password saved." +msgstr "Contrasinal gardada." + +#: ../actions/password.php:61 +#: ../actions/register.php:88 +#: actions/profilesettings.php:380 +#: actions/register.php:98 +#: actions/profilesettings.php:426 +#: actions/register.php:107 +msgid "Passwords don't match." +msgstr "As contrasinais non coinciden" + +#: ../lib/searchaction.php:100 +#: lib/searchaction.php:100 +msgid "People" +msgstr "Xente" + +#: ../actions/opensearch.php:33 +#: actions/opensearch.php:33 +msgid "People Search" +msgstr "Procurar xente" + +#: ../actions/peoplesearch.php:33 +#: actions/peoplesearch.php:33 +msgid "People search" +msgstr "Procurar xente." + +#: ../lib/stream.php:50 +#: lib/personal.php:50 +msgid "Personal" +msgstr "Persoal" + +#: ../actions/invite.php:133 +#: actions/invite.php:141 +msgid "Personal message" +msgstr "Mensaxe persoal" + +#: ../actions/smssettings.php:69 +#: actions/smssettings.php:69 +#: actions/smspostsettings.php:49 +#: actions/smspostsettings.php:50 +msgid "Phone number, no punctuation or spaces, with area code" +msgstr "Número de teléfono, sen puntuacións ou espazos, co código de área" + +#: ../actions/userauthorization.php:78 +#: actions/userauthorization.php:84 +msgid "Please check these details to make sure that you want to subscribe to this user's notices. If you didn't just ask to subscribe to someone's notices, click \"Cancel\"." +msgstr "Please check these details to make sure that you want to subscribe to this user's notices. If you didn't just ask to subscribe to someone's notices, click \"Cancel\"." + +#: ../actions/imsettings.php:73 +#: actions/imsettings.php:74 +msgid "Post a notice when my Jabber/GTalk status changes." +msgstr "Post a notice when my Jabber/GTalk status changes." + +#: ../actions/emailsettings.php:85 +#: ../actions/imsettings.php:67 +#: ../actions/smssettings.php:94 +#: actions/emailsettings.php:86 +#: actions/imsettings.php:68 +#: actions/smssettings.php:94 +#: actions/twittersettings.php:70 +msgid "Preferences" +msgstr "Preferencias" + +#: ../actions/emailsettings.php:162 +#: ../actions/imsettings.php:144 +#: ../actions/smssettings.php:163 +#: actions/emailsettings.php:180 +#: actions/imsettings.php:152 +#: actions/smssettings.php:171 +#: actions/emailsettings.php:186 +#: actions/imsettings.php:157 +#: actions/othersettings.php:179 +#: actions/smssettings.php:173 +msgid "Preferences saved." +msgstr "Preferencias gardadas." + +#: ../actions/profilesettings.php:57 +#: actions/profilesettings.php:90 +#: actions/profilesettings.php:98 +msgid "Preferred language" +msgstr "Linguaxe preferida" + +#: ../lib/util.php:328 +#: lib/util.php:344 +#: lib/util.php:373 +msgid "Privacy" +msgstr "Privacidade" + +#: ../classes/Notice.php:95 +#: ../classes/Notice.php:106 +#: classes/Notice.php:109 +#: classes/Notice.php:119 +#: classes/Notice.php:139 +#: classes/Notice.php:149 +msgid "Problem saving notice." +msgstr "Aconteceu un erro ó gardar o chío." + +#: ../lib/settingsaction.php:84 +#: ../lib/stream.php:60 +#: lib/personal.php:60 +#: lib/settingsaction.php:84 +#: actions/deleteprofile.php:241 +msgid "Profile" +msgstr "Perfil" + +#: ../actions/remotesubscribe.php:73 +#: actions/remotesubscribe.php:82 +msgid "Profile URL" +msgstr "Enderezo de perfil" + +#: ../actions/profilesettings.php:34 +#: actions/profilesettings.php:32 +msgid "Profile settings" +msgstr "Configuración de perfil" + +#: ../actions/postnotice.php:51 +#: ../actions/updateprofile.php:52 +#: actions/postnotice.php:52 +#: actions/updateprofile.php:53 +msgid "Profile unknown" +msgstr "Perfil descoñecido" + +#: ../actions/public.php:54 +#: actions/public.php:54 +#: actions/public.php:77 +msgid "Public Stream Feed" +msgstr "Sindicación do Fio Público" + +#: ../actions/public.php:33 +#: actions/public.php:33 +#: lib/stream.php:34 +msgid "Public timeline" +msgstr "Liña de tempo pública" + +#: ../actions/imsettings.php:79 +#: actions/imsettings.php:80 +msgid "Publish a MicroID for my Jabber/GTalk address." +msgstr "Publicar unha MicroID dende a miña dirección de Jabber/GTalk." + +#: ../actions/emailsettings.php:94 +#: actions/emailsettings.php:101 +msgid "Publish a MicroID for my email address." +msgstr "Publicar unha MicroID dende a miña dirección de correo." + +#: ../actions/tag.php:75 +#: ../actions/tag.php:76 +#: actions/tag.php:75 +#: actions/tag.php:76 +msgid "Recent Tags" +msgstr "Tags Recentes" + +#: ../actions/recoverpassword.php:166 +#: actions/recoverpassword.php:171 +msgid "Recover" +msgstr "Recuperar" + +#: ../actions/recoverpassword.php:156 +#: actions/recoverpassword.php:161 +msgid "Recover password" +msgstr "Recuperar contrasinal" + +#: ../actions/recoverpassword.php:67 +#: actions/recoverpassword.php:67 +msgid "Recovery code for unknown user." +msgstr "Código de recuperación para usuario descoñecido." + +#: ../actions/register.php:142 +#: ../actions/register.php:193 +#: ../lib/util.php:312 +#: actions/register.php:152 +#: actions/register.php:207 +#: lib/util.php:328 +#: actions/register.php:181 +#: actions/register.php:234 +#: lib/util.php:354 +msgid "Register" +msgstr "Rexistrar" + +#: ../actions/register.php:28 +#: actions/register.php:28 +#: actions/finishopenidlogin.php:173 +msgid "Registration not allowed." +msgstr "Non se permite o rexistro neste intre." + +#: ../actions/register.php:200 +#: actions/register.php:214 +#: actions/register.php:241 +msgid "Registration successful" +msgstr "Xa estas rexistrado!!" + +#: ../actions/userauthorization.php:120 +#: actions/userauthorization.php:127 +msgid "Reject" +msgstr "Rexeitar" + +#: ../actions/login.php:103 +#: ../actions/register.php:176 +#: actions/login.php:103 +#: actions/register.php:190 +#: actions/login.php:110 +#: actions/openidlogin.php:85 +#: actions/register.php:217 +msgid "Remember me" +msgstr "Lembrarme" + +#: ../actions/updateprofile.php:70 +#: actions/updateprofile.php:71 +msgid "Remote profile with no matching profile" +msgstr "Non hai ningún perfil que coincida co perfil remoto" + +#: ../actions/remotesubscribe.php:65 +#: actions/remotesubscribe.php:73 +msgid "Remote subscribe" +msgstr "Suscrición remota" + +#: ../actions/emailsettings.php:47 +#: ../actions/emailsettings.php:75 +#: ../actions/imsettings.php:48 +#: ../actions/openidsettings.php:106 +#: ../actions/smssettings.php:50 +#: ../actions/smssettings.php:84 +#: actions/emailsettings.php:48 +#: actions/emailsettings.php:76 +#: actions/imsettings.php:49 +#: actions/openidsettings.php:108 +#: actions/smssettings.php:50 +#: actions/smssettings.php:84 +#: actions/twittersettings.php:59 +#: actions/smspostsettings.php:45 +#: actions/twittersettings.php:61 +#: actions/smspostsettings.php:46 +msgid "Remove" +msgstr "Eliminar" + +#: ../actions/openidsettings.php:68 +#: actions/openidsettings.php:69 +msgid "Remove OpenID" +msgstr "Eliminar OpenID" + +#: ../actions/openidsettings.php:73 +#: actions/openidsettings.php:74 +msgid "Removing your only OpenID would make it impossible to log in! If you need to remove it, add another OpenID first." +msgstr "Eliminando o teu enderezo OpenID vaiche ser imposible acceder! Se queres eliminalo, primeiro engade outro enderezo OpenID." + +#: ../lib/stream.php:55 +#: lib/personal.php:55 +msgid "Replies" +msgstr "Respostas" + +#: ../actions/replies.php:47 +#: ../actions/repliesrss.php:76 +#: ../lib/stream.php:56 +#: actions/replies.php:47 +#: actions/repliesrss.php:62 +#: lib/personal.php:56 +#, php-format +msgid "Replies to %s" +msgstr "Replies to %s" + +#: ../actions/recoverpassword.php:183 +#: actions/recoverpassword.php:189 +msgid "Reset" +msgstr "Restaurar" + +#: ../actions/recoverpassword.php:173 +#: actions/recoverpassword.php:178 +msgid "Reset password" +msgstr "Restaurar contrasinal" + +#: ../lib/settingsaction.php:99 +#: lib/settingsaction.php:93 +#: actions/deleteprofile.php:250 +#: actions/subscriptions.php:73 +msgid "SMS" +msgstr "SMS" + +#: ../actions/smssettings.php:67 +#: actions/smssettings.php:67 +#: actions/smspostsettings.php:38 +#: actions/smspostsettings.php:39 +msgid "SMS Phone number" +msgstr "Número de Teléfono do SMS" + +#: ../actions/smssettings.php:33 +#: actions/smssettings.php:33 +msgid "SMS Settings" +msgstr "Configuracións de SMS" + +#: ../lib/mail.php:219 +#: lib/mail.php:225 +#: lib/mail.php:220 +msgid "SMS confirmation" +msgstr "Confirmación de SMS" + +#: ../actions/recoverpassword.php:182 +#: actions/recoverpassword.php:188 +msgid "Same as password above" +msgstr "Igual que a contrasinal de enriba" + +#: ../actions/register.php:156 +#: actions/register.php:170 +#: actions/register.php:197 +msgid "Same as password above. Required." +msgstr "A mesma contrasinal que arriba. Requerido." + +#: ../actions/emailsettings.php:97 +#: ../actions/imsettings.php:81 +#: ../actions/profilesettings.php:67 +#: ../actions/smssettings.php:100 +#: actions/emailsettings.php:104 +#: actions/imsettings.php:82 +#: actions/profilesettings.php:101 +#: actions/smssettings.php:100 +#: actions/twittersettings.php:83 +#: actions/emailsettings.php:108 +#: actions/imsettings.php:85 +#: actions/othersettings.php:56 +#: actions/profilesettings.php:118 +#: actions/subscriptions.php:74 +#: actions/tagother.php:100 +#: actions/twittersettings.php:82 +msgid "Save" +msgstr "Gardar" + +#: ../lib/searchaction.php:84 +#: ../lib/util.php:300 +#: lib/searchaction.php:84 +#: lib/util.php:316 +#: lib/util.php:345 +msgid "Search" +msgstr "Buscar" + +#: ../actions/noticesearch.php:80 +#: actions/noticesearch.php:85 +#: actions/noticesearch.php:84 +msgid "Search Stream Feed" +msgstr "Procura no Fío" + +#: ../actions/noticesearch.php:30 +#: actions/noticesearch.php:30 +#: actions/noticesearch.php:29 +#, php-format +msgid "Search for notices on %%site.name%% by their contents. Separate search terms by spaces; they must be 3 characters or more." +msgstr "Procurar chíos en %%site.name%% polos seus contidos. Separa os termos de procura por espazos, deben ter 3 caracteres ou máis." + +#: ../actions/peoplesearch.php:28 +#: actions/peoplesearch.php:28 +#, php-format +msgid "Search for people on %%site.name%% by their name, location, or interests. Separate the terms by spaces; they must be 3 characters or more." +msgstr "Procurar xente en %%site.name%% pola seu nome, localización, ou intereses. Separa os termos por espazos; deben ter 3 caracteres ou máis." + +#: ../actions/smssettings.php:296 +#: actions/smssettings.php:304 +#: actions/smssettings.php:306 +msgid "Select a carrier" +msgstr "Selecciona unha operadora" + +#: ../actions/invite.php:137 +#: ../lib/util.php:1172 +#: actions/invite.php:145 +#: lib/util.php:1306 +#: lib/util.php:1731 +#: lib/util.php:1705 +#: lib/util.php:2217 +msgid "Send" +msgstr "Enviar" + +#: ../actions/emailsettings.php:73 +#: ../actions/smssettings.php:82 +#: actions/emailsettings.php:74 +#: actions/smssettings.php:82 +msgid "Send email to this address to post new notices." +msgstr "Enviar un correo a esta dirección para enviar novos chíos." + +#: ../actions/emailsettings.php:88 +#: actions/emailsettings.php:89 +msgid "Send me notices of new subscriptions through email." +msgstr "Envíame chios de novas suscricións por email." + +#: ../actions/imsettings.php:70 +#: actions/imsettings.php:71 +msgid "Send me notices through Jabber/GTalk." +msgstr "Enviarme advertencias a través de Jabber/GTalk." + +#: ../actions/smssettings.php:97 +#: actions/smssettings.php:97 +msgid "Send me notices through SMS; I understand I may incur exorbitant charges from my carrier." +msgstr "Enviarme chíos mediante SMS, entendo que a miña operadora poida cobrarme grandes facturas." + +#: ../actions/imsettings.php:76 +#: actions/imsettings.php:77 +msgid "Send me replies through Jabber/GTalk from people I'm not subscribed to." +msgstr "Envíame respostas a través de Jabber/GTalk da xente á que non estou suscrito." + +#: ../lib/util.php:304 +#: lib/util.php:320 +#: lib/util.php:348 +msgid "Settings" +msgstr "Configuración" + +#: ../actions/profilesettings.php:192 +#: actions/profilesettings.php:307 +#: actions/profilesettings.php:353 +msgid "Settings saved." +msgstr "Configuracións gardadas." + +#: ../actions/tag.php:60 +#: actions/tag.php:60 +#: actions/tag.php:67 +msgid "Showing most popular tags from the last week" +msgstr "Amoa os tags máis populares dende a semana pasada" + +#: ../actions/finishaddopenid.php:66 +#: actions/finishaddopenid.php:66 +msgid "Someone else already has this OpenID." +msgstr "Alguen máis ten ese enderezo OpenID." + +#: ../actions/finishopenidlogin.php:42 +#: ../actions/openidsettings.php:126 +#: actions/finishopenidlogin.php:47 +#: actions/openidsettings.php:135 +msgid "Something weird happened." +msgstr "Algo gordo aconteceu." + +#: ../scripts/maildaemon.php:58 +#: scripts/maildaemon.php:58 +msgid "Sorry, no incoming email allowed." +msgstr "Aivá, non se permiten correos entrantes." + +#: ../scripts/maildaemon.php:54 +#: scripts/maildaemon.php:54 +msgid "Sorry, that is not your incoming email address." +msgstr "Ise é un enderezo IM incorrecto." + +#: ../lib/util.php:330 +#: lib/util.php:346 +#: lib/util.php:375 +msgid "Source" +msgstr "Fonte" + +#: ../actions/showstream.php:296 +#: actions/showstream.php:311 +#: actions/showstream.php:359 +#: actions/misc.php:87 +msgid "Statistics" +msgstr "Estatísticas" + +#: ../actions/finishopenidlogin.php:182 +#: ../actions/finishopenidlogin.php:246 +#: actions/finishopenidlogin.php:188 +#: actions/finishopenidlogin.php:252 +#: actions/finishopenidlogin.php:199 +#: actions/finishopenidlogin.php:267 +msgid "Stored OpenID not found." +msgstr "OpenID almacenado non atopado" + +#: ../actions/remotesubscribe.php:75 +#: ../actions/showstream.php:188 +#: ../actions/showstream.php:197 +#: actions/remotesubscribe.php:84 +#: actions/showstream.php:197 +#: actions/showstream.php:206 +#: actions/showstream.php:254 +#: lib/util.php:2137 +msgid "Subscribe" +msgstr "Subscribir" + +#: ../actions/showstream.php:313 +#: ../actions/subscribers.php:27 +#: actions/showstream.php:328 +#: actions/subscribers.php:27 +#: actions/showstream.php:376 +#: lib/gallery.php:136 +#: lib/gallery.php:137 +msgid "Subscribers" +msgstr "Subscritores" + +#: ../actions/userauthorization.php:310 +#: actions/userauthorization.php:322 +msgid "Subscription authorized" +msgstr "Subscrición autorizada" + +#: ../actions/userauthorization.php:320 +#: actions/userauthorization.php:332 +msgid "Subscription rejected" +msgstr "Subscrición rexeitada" + +#: ../actions/showstream.php:230 +#: ../actions/showstream.php:307 +#: ../actions/subscriptions.php:27 +#: actions/showstream.php:240 +#: actions/showstream.php:322 +#: actions/subscriptions.php:27 +#: actions/showstream.php:288 +#: actions/showstream.php:370 +#: lib/gallery.php:131 +#: lib/gallery.php:132 +msgid "Subscriptions" +msgstr "Subscricións" + +#: ../actions/avatar.php:87 +#: actions/profilesettings.php:324 +#: actions/profilesettings.php:370 +msgid "System error uploading file." +msgstr "Aconteceu un erro no sistema namentras se estaba cargando o ficheiro." + +#: ../actions/tag.php:41 +#: ../lib/util.php:301 +#: actions/tag.php:41 +#: lib/util.php:317 +#: actions/profilesettings.php:90 +#: actions/showstream.php:382 +#: actions/tagother.php:96 +#: actions/tagother.php:162 +#: actions/tag.php:47 +#: lib/profilelist.php:126 +#: lib/profilelist.php:128 +msgid "Tags" +msgstr "Tags" + +#: ../lib/searchaction.php:104 +#: lib/searchaction.php:104 +msgid "Text" +msgstr "Texto" + +#: ../actions/noticesearch.php:34 +#: actions/noticesearch.php:34 +#: actions/noticesearch.php:33 +msgid "Text search" +msgstr "Procura de texto" + +#: ../actions/openidsettings.php:140 +#: actions/openidsettings.php:149 +msgid "That OpenID does not belong to you." +msgstr "Ese OpenID non che pertence." + +#: ../actions/confirmaddress.php:52 +#: actions/confirmaddress.php:52 +msgid "That address has already been confirmed." +msgstr "Esa dirección xa foi confirmada." + +#: ../actions/confirmaddress.php:43 +#: actions/confirmaddress.php:43 +msgid "That confirmation code is not for you!" +msgstr "¡Ese código de confirmación non é para ti!" + +#: ../actions/emailsettings.php:191 +#: actions/emailsettings.php:209 +#: actions/emailsettings.php:215 +msgid "That email address already belongs to another user." +msgstr "Este enderezo de correo xa pertence a outro usuario." + +#: ../actions/avatar.php:80 +#: actions/profilesettings.php:317 +#: actions/profilesettings.php:363 +msgid "That file is too big." +msgstr "Ese arquivo é demasiado grande." + +#: ../actions/imsettings.php:170 +#: actions/imsettings.php:178 +#: actions/imsettings.php:183 +msgid "That is already your Jabber ID." +msgstr "Xa é a túa conta de Jabber." + +#: ../actions/emailsettings.php:188 +#: actions/emailsettings.php:206 +#: actions/emailsettings.php:212 +msgid "That is already your email address." +msgstr "Xa é o teu enderezo de correo." + +#: ../actions/smssettings.php:188 +#: actions/smssettings.php:196 +#: actions/smspostsettings.php:83 +#: actions/smssettings.php:198 +#: actions/smspostsettings.php:88 +msgid "That is already your phone number." +msgstr "Xa é o teu número de teléfono." + +#: ../actions/imsettings.php:233 +#: actions/imsettings.php:241 +#: actions/imsettings.php:246 +msgid "That is not your Jabber ID." +msgstr "Esa non é a túa conta Jabber." + +#: ../actions/emailsettings.php:249 +#: actions/emailsettings.php:267 +#: actions/emailsettings.php:271 +msgid "That is not your email address." +msgstr "Esa non é a túa dirección de correo." + +#: ../actions/smssettings.php:257 +#: actions/smssettings.php:265 +#: actions/smspostsettings.php:113 +#: actions/smssettings.php:267 +#: actions/smspostsettings.php:118 +msgid "That is not your phone number." +msgstr "Ese non é o teu número de teléfono." + +#: ../actions/emailsettings.php:226 +#: ../actions/imsettings.php:210 +#: actions/emailsettings.php:244 +#: actions/imsettings.php:218 +#: actions/emailsettings.php:248 +#: actions/imsettings.php:223 +msgid "That is the wrong IM address." +msgstr "Esa é unha enderezo IM incorrecto." + +#: ../actions/smssettings.php:233 +#: actions/smssettings.php:241 +#: actions/smssettings.php:243 +msgid "That is the wrong confirmation number." +msgstr "Ese é un número de confirmación incorrecto." + +#: ../actions/smssettings.php:191 +#: actions/smssettings.php:199 +#: actions/smspostsettings.php:86 +#: actions/smssettings.php:201 +#: actions/smspostsettings.php:91 +msgid "That phone number already belongs to another user." +msgstr "O número de teléfono xa pertence a outro usuario." + +#: ../actions/newnotice.php:49 +#: ../actions/twitapistatuses.php:408 +#: actions/newnotice.php:49 +#: actions/twitapistatuses.php:330 +#: actions/newnotice.php:61 +#: actions/newsmsnotice.php:73 +#: actions/twitapistatuses.php:271 +#: actions/newsmsnotice.php:79 +msgid "That's too long. Max notice size is 140 chars." +msgstr "Iso é demasiado longo. O tamaño máximo para un chío é de 140 caracteres." + +#: ../actions/twitapiaccount.php:74 +#: actions/twitapiaccount.php:72 +#: actions/twitapiaccount.php:58 +msgid "That's too long. Max notice size is 255 chars." +msgstr "Iso é demasiado longo. O tamaño máximo para un chío é de 255 caracteres." + +#: ../actions/confirmaddress.php:92 +#: actions/confirmaddress.php:92 +#, php-format +msgid "The address \"%s\" has been confirmed for your account." +msgstr "A dirección \"%s\" xa foi confirmada para a túa conta." + +#: ../actions/emailsettings.php:264 +#: ../actions/imsettings.php:250 +#: ../actions/smssettings.php:274 +#: actions/emailsettings.php:282 +#: actions/imsettings.php:258 +#: actions/smssettings.php:282 +#: actions/emailsettings.php:286 +#: actions/imsettings.php:263 +#: actions/smspostsettings.php:128 +#: actions/smssettings.php:284 +#: actions/smspostsettings.php:133 +msgid "The address was removed." +msgstr "Enderezo eliminado." + +#: ../actions/userauthorization.php:312 +#: actions/userauthorization.php:324 +msgid "The subscription has been authorized, but no callback URL was passed. Check with the site's instructions for details on how to authorize the subscription. Your subscription token is:" +msgstr "A subscrición foi autorizada, pero ningunha URL de retorno foi proporcionada. Comproba coas instruccións do sitio para máis detalles en como autorizar subscricións. O teu token de subscrición é:" + +#: ../actions/userauthorization.php:322 +#: actions/userauthorization.php:334 +msgid "The subscription has been rejected, but no callback URL was passed. Check with the site's instructions for details on how to fully reject the subscription." +msgstr "The subscription has been rejected, but no callback URL was passed. Check with the site's instructions for details on how to fully reject the subscription." + +#: ../actions/subscribers.php:35 +#: actions/subscribers.php:35 +#, php-format +msgid "These are the people who listen to %s's notices." +msgstr "Esa é a xente que escoita os chíos de %s." + +#: ../actions/subscribers.php:33 +#: actions/subscribers.php:33 +msgid "These are the people who listen to your notices." +msgstr "Esa é a xente que escoita os teus chíos." + +#: ../actions/subscriptions.php:35 +#: actions/subscriptions.php:35 +#, php-format +msgid "These are the people whose notices %s listens to." +msgstr "Esta é a xente á que lle estas a escoitar os chíos %s." + +#: ../actions/subscriptions.php:33 +#: actions/subscriptions.php:33 +msgid "These are the people whose notices you listen to." +msgstr "Esa é a xente á que lle estas a escoitar os seus chíos" + +#: ../actions/invite.php:89 +#: actions/invite.php:96 +msgid "These people are already users and you were automatically subscribed to them:" +msgstr "Esta xente xa é usuario e ti foches suscrito automaticamente a eles:" + +#: ../actions/recoverpassword.php:88 +#: actions/recoverpassword.php:91 +msgid "This confirmation code is too old. Please start again." +msgstr "Ese código de confirmación é demasiado antigo. Comeza de novo." + +#: ../lib/openid.php:195 +#: lib/openid.php:195 +msgid "This form should automatically submit itself. If not, click the submit button to go to your OpenID provider." +msgstr "Este formulario debería enviarse automáticamente. Se non é así, fai clic no botón de enviar para ir ó teu proveedro OpenID." + +#: ../actions/finishopenidlogin.php:56 +#: actions/finishopenidlogin.php:61 +#, php-format +msgid "This is the first time you've logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one." +msgstr "Esta é a primeria vez que accedes a %s polo que debes conectar o teu enderezo OpenID á conta local. Podes ademáis crear unha nova conta, ou conectarte cunha existente, se xa tes unha." + +#: ../actions/twitapifriendships.php:108 +#: ../actions/twitapistatuses.php:586 +#: actions/twitapifavorites.php:127 +#: actions/twitapifriendships.php:108 +#: actions/twitapistatuses.php:511 +#: actions/twitapifavorites.php:94 +#: actions/twitapifriendships.php:82 +#: actions/twitapistatuses.php:428 +msgid "This method requires a POST or DELETE." +msgstr "Este método require un POST ou DELETE." + +#: ../actions/twitapiaccount.php:65 +#: ../actions/twitapifriendships.php:44 +#: ../actions/twitapistatuses.php:381 +#: actions/twitapiaccount.php:63 +#: actions/twitapidirect_messages.php:114 +#: actions/twitapifriendships.php:44 +#: actions/twitapistatuses.php:303 +#: actions/twitapiaccount.php:49 +#: actions/twitapidirect_messages.php:117 +#: actions/twitapifriendships.php:30 +#: actions/twitapistatuses.php:239 +msgid "This method requires a POST." +msgstr "Este método require un POST." + +#: ../lib/util.php:164 +#: lib/util.php:246 +#: lib/util.php:263 +msgid "This page is not available in a media type you accept" +msgstr "Esta páxina non está dispoñíbel no tipo de medio que aceptas" + +#: ../actions/profilesettings.php:63 +#: actions/profilesettings.php:96 +#: actions/profilesettings.php:109 +msgid "Timezone" +msgstr "Fuso Horario" + +#: ../actions/profilesettings.php:107 +#: actions/profilesettings.php:222 +#: actions/profilesettings.php:241 +msgid "Timezone not selected." +msgstr "Fuso Horario non seleccionado" + +#: ../actions/remotesubscribe.php:43 +#: actions/remotesubscribe.php:51 +#, php-format +msgid "To subscribe, you can [login](%%action.login%%), or [register](%%action.register%%) a new account. If you already have an account on a [compatible microblogging site](%%doc.openmublog%%), enter your profile URL below." +msgstr "Para subscribirse, podes [acceder](%%action.login%%), ou [rexistrar](%%action.register%%) unha nova conta. Se xa tes unha conta nun [sitio de microblogaxe compatíbel](%%doc.openmublog%%), insire a dirección do perfil abaixo." + +#: ../actions/twitapifriendships.php:163 +#: actions/twitapifriendships.php:167 +#: actions/twitapifriendships.php:128 +msgid "Two user ids or screen_names must be supplied." +msgstr "Dous identificadores de usuario ou nomes_en_pantalla deben ser proporcionados." + +#: ../actions/profilesettings.php:48 +#: ../actions/register.php:169 +#: actions/profilesettings.php:81 +#: actions/register.php:183 +#: actions/profilesettings.php:83 +#: actions/register.php:210 +msgid "URL of your homepage, blog, or profile on another site" +msgstr "Enderezo da túa páxina persoal, blogue, ou perfil noutro sitio" + +#: ../actions/remotesubscribe.php:74 +#: actions/remotesubscribe.php:83 +msgid "URL of your profile on another compatible microblogging service" +msgstr "Enderezo do teu perfil en outro servizo de microblogaxe compatíbel" + +#: ../actions/emailsettings.php:130 +#: ../actions/imsettings.php:110 +#: ../actions/recoverpassword.php:39 +#: ../actions/smssettings.php:135 +#: actions/emailsettings.php:144 +#: actions/imsettings.php:118 +#: actions/recoverpassword.php:39 +#: actions/smssettings.php:143 +#: actions/twittersettings.php:108 +#: actions/emailsettings.php:148 +#: actions/imsettings.php:121 +#: actions/othersettings.php:146 +#: actions/smspostsettings.php:64 +#: actions/smssettings.php:145 +#: actions/twittersettings.php:199 +#: actions/smspostsettings.php:69 +msgid "Unexpected form submission." +msgstr "Envio de formulario non esperada." + +#: ../actions/recoverpassword.php:276 +#: actions/recoverpassword.php:289 +msgid "Unexpected password reset." +msgstr "Restauración de contrasinal non esperada." + +#: ../index.php:57 +#: index.php:57 +msgid "Unknown action" +msgstr "Acción descoñecida" + +#: ../actions/finishremotesubscribe.php:58 +#: actions/finishremotesubscribe.php:60 +#: actions/finishremotesubscribe.php:59 +msgid "Unknown version of OMB protocol." +msgstr "Versión de protocolo OMB descoñecida." + +#: ../lib/util.php:269 +#: lib/util.php:285 +#: lib/util.php:302 +msgid "Unless otherwise specified, contents of this site are copyright by the contributors and available under the " +msgstr "Aínda que especifiques outro, os contidos de este sitio teñen copyright polos contribuidores e está dispoñible baixo " + +#: ../actions/confirmaddress.php:48 +#: actions/confirmaddress.php:48 +#, php-format +msgid "Unrecognized address type %s" +msgstr "Tipo de enderezo %s non recoñecido" + +#: ../actions/showstream.php:209 +#: actions/showstream.php:219 +#: actions/showstream.php:267 +#: lib/util.php:2153 +msgid "Unsubscribe" +msgstr "Eliminar subscrición" + +#: ../actions/postnotice.php:44 +#: ../actions/updateprofile.php:45 +#: actions/postnotice.php:45 +#: actions/updateprofile.php:46 +msgid "Unsupported OMB version" +msgstr "Versión OMB non soportada" + +#: ../actions/avatar.php:105 +#: actions/profilesettings.php:342 +#: actions/profilesettings.php:388 +msgid "Unsupported image file format." +msgstr "Formato de ficheiro de imaxe non soportado." + +#: ../lib/settingsaction.php:100 +#: lib/settingsaction.php:94 +#: actions/deleteprofile.php:251 +msgid "Updates by SMS" +msgstr "Chíos dende SMS" + +#: ../lib/settingsaction.php:103 +#: lib/settingsaction.php:97 +#: actions/deleteprofile.php:254 +#: lib/settingsaction.php:101 +#: lib/settingsaction.php:103 +msgid "Updates by instant messenger (IM)" +msgstr "Chíos dende mensaxería instantánea (IM)" + +#: ../actions/twitapistatuses.php:241 +#: actions/twitapistatuses.php:158 +#: actions/twitapistatuses.php:126 +#, php-format +msgid "Updates from %1$s and friends on %2$s!" +msgstr "Actualizacións dende %1$s e amigos en %2$s!" + +#: ../actions/twitapistatuses.php:341 +#: actions/twitapistatuses.php:268 +#: actions/twitapistatuses.php:198 +#, php-format +msgid "Updates from %1$s on %2$s!" +msgstr "Actualizacións dende %1$s en %2$s!" + +#: ../actions/avatar.php:68 +#: actions/profilesettings.php:161 +#: actions/profilesettings.php:178 +msgid "Upload" +msgstr "Subir" + +#: ../actions/avatar.php:27 +msgid "Upload a new \"avatar\" (user image) here. You can't edit the picture after you upload it, so make sure it's more or less square. It must be under the site license, also. Use a picture that belongs to you and that you want to share." +msgstr "Sube un novo \"avatar\" (imaxe de usuario) dende aquí. Non podes editar a imaxe despois de subila, polo que asegurate de que é máis ou menos cadrado. Debe estar baixo a licenza do sistio. Emprega unha imaxe que che pertenza e que queiras compartir." + +#: ../lib/settingsaction.php:91 +msgid "Upload a new profile image" +msgstr "Subir unha nova imaxe de usuario" + +#: ../actions/invite.php:114 +#: actions/invite.php:121 +msgid "Use this form to invite your friends and colleagues to use this service." +msgstr "Emprega este formulario para invitar ós teus amigos e colegas a empregar este servizo." + +#: ../actions/register.php:159 +#: ../actions/register.php:162 +#: actions/register.php:173 +#: actions/register.php:176 +#: actions/register.php:200 +#: actions/register.php:203 +msgid "Used only for updates, announcements, and password recovery" +msgstr "Empregado só para actualizacións, novidades, e recuperación de contrasinais" + +#: ../actions/finishremotesubscribe.php:86 +#: actions/finishremotesubscribe.php:88 +#: actions/finishremotesubscribe.php:92 +msgid "User being listened to doesn't exist." +msgstr "O usuario que está sendo escoitado non existe." + +#: ../actions/all.php:41 +#: ../actions/avatarbynickname.php:48 +#: ../actions/foaf.php:47 +#: ../actions/replies.php:41 +#: ../actions/showstream.php:44 +#: ../actions/twitapiaccount.php:82 +#: ../actions/twitapistatuses.php:319 +#: ../actions/twitapistatuses.php:685 +#: ../actions/twitapiusers.php:82 +#: actions/all.php:41 +#: actions/avatarbynickname.php:48 +#: actions/foaf.php:47 +#: actions/replies.php:41 +#: actions/showfavorites.php:41 +#: actions/showstream.php:44 +#: actions/twitapiaccount.php:80 +#: actions/twitapifavorites.php:68 +#: actions/twitapistatuses.php:235 +#: actions/twitapistatuses.php:609 +#: actions/twitapiusers.php:87 +#: lib/mailbox.php:50 +#: actions/showstream.php:57 +#: actions/twitapiaccount.php:66 +#: actions/twitapifavorites.php:40 +#: actions/twitapistatuses.php:163 +#: actions/twitapistatuses.php:492 +#: actions/twitapiusers.php:53 +msgid "User has no profile." +msgstr "O usuario non ten perfil." + +#: ../actions/remotesubscribe.php:71 +#: actions/remotesubscribe.php:80 +msgid "User nickname" +msgstr "Alcume de usuario" + +#: ../actions/twitapiusers.php:75 +#: actions/twitapiusers.php:80 +msgid "User not found." +msgstr "Usuario non atopado." + +#: ../actions/profilesettings.php:63 +#: actions/profilesettings.php:96 +#: actions/profilesettings.php:109 +msgid "What timezone are you normally in?" +msgstr "En que fuso horario estas normalmente?" + +#: ../lib/util.php:1159 +#: lib/util.php:1293 +#: lib/util.php:1689 +#, php-format +msgid "What's up, %s?" +msgstr "¿Que pasa, %s?" + +#: ../actions/profilesettings.php:54 +#: ../actions/register.php:175 +#: actions/profilesettings.php:87 +#: actions/register.php:189 +#: actions/profilesettings.php:89 +#: actions/register.php:216 +msgid "Where you are, like \"City, State (or Region), Country\"" +msgstr "¿Onde estas, coma \"Cidade, Provincia, País\"" + +#: ../actions/updateprofile.php:128 +#: actions/updateprofile.php:129 +#, php-format +msgid "Wrong image type for '%s'" +msgstr "Tipo de imaxe incorrecto para '%s'" + +#: ../actions/updateprofile.php:123 +#: actions/updateprofile.php:124 +#, php-format +msgid "Wrong size image at '%s'" +msgstr "Tamaño de imaxe incorrecto en '%s'" + +#: ../actions/deletenotice.php:63 +#: ../actions/deletenotice.php:72 +#: actions/deletenotice.php:64 +#: actions/deletenotice.php:79 +#: actions/block.php:105 +msgid "Yes" +msgstr "Si" + +#: ../actions/finishaddopenid.php:64 +#: actions/finishaddopenid.php:64 +msgid "You already have this OpenID!" +msgstr "¡Xa tes esa OpenID!" + +#: ../actions/deletenotice.php:37 +#: actions/deletenotice.php:37 +msgid "You are about to permanently delete a notice. Once this is done, it cannot be undone." +msgstr "Vas a eliminar permanentemente este chío. Unha vez feito, xa non hai volta atrás... Quedas avisado!" + +#: ../actions/recoverpassword.php:31 +#: actions/recoverpassword.php:31 +msgid "You are already logged in!" +msgstr "¡Xa estás logueado!" + +#: ../actions/invite.php:81 +#: actions/invite.php:88 +msgid "You are already subscribed to these users:" +msgstr "Xa estas suscrito a estes usuarios:" + +#: ../actions/twitapifriendships.php:128 +#: actions/twitapifriendships.php:128 +#: actions/twitapifriendships.php:102 +msgid "You are not friends with the specified user." +msgstr "No tes amigos con un usuario específico." + +#: ../actions/password.php:27 +msgid "You can change your password here. Choose a good one!" +msgstr "Podes cambiar a túa contrasinal aquí. ¡Escolle unha boa!" + +#: ../actions/register.php:135 +#: actions/register.php:145 +msgid "You can create a new account to start posting notices." +msgstr "Podes crear unha nova conta para comezar a escribir chíos." + +#: ../actions/smssettings.php:28 +#: actions/smssettings.php:28 +#, php-format +msgid "You can receive SMS messages through email from %%site.name%%." +msgstr "Podes recibir mensaxes SMS a través do email dende %%site.name%%." + +#: ../actions/openidsettings.php:86 +#: actions/openidsettings.php:87 +msgid "You can remove an OpenID from your account by clicking the button marked \"Remove\"." +msgstr "Podes eliminar unha OpenID da túa conta facendo clic no botón marcado \"Eliminar\"." + +#: ../actions/imsettings.php:28 +#: actions/imsettings.php:28 +#, php-format +msgid "You can send and receive notices through Jabber/GTalk [instant messages](%%doc.im%%). Configure your address and settings below." +msgstr "Podes enviar e recibir chíos a través de Jabber/GTalk [mensaxes instantáneos](%%doc.im%%). Configura a túa conta e configuracións abaixo." + +#: ../actions/profilesettings.php:27 +#: actions/profilesettings.php:27 +msgid "You can update your personal profile info here so people know more about you." +msgstr "Podes actualizar a túa información do perfil persoal aquí para que a xente che poida coñecer mellor." + +#: ../actions/finishremotesubscribe.php:31 +#: ../actions/remotesubscribe.php:31 +#: actions/finishremotesubscribe.php:31 +#: actions/remotesubscribe.php:31 +#: actions/finishremotesubscribe.php:83 +#: actions/finishremotesubscribe.php:99 +msgid "You can use the local subscription!" +msgstr "¡Podes empregar a túa subscrición local!" + +#: ../actions/finishopenidlogin.php:33 +#: ../actions/register.php:61 +#: actions/finishopenidlogin.php:38 +#: actions/register.php:68 +#: actions/register.php:77 +msgid "You can't register if you don't agree to the license." +msgstr "Non podes rexistrarte se non estas de acordo coa licenza." + +#: ../actions/updateprofile.php:63 +#: actions/updateprofile.php:64 +msgid "You did not send us that profile" +msgstr "Non nos enviaches ese perfil" + +#: ../lib/mail.php:147 +#: lib/mail.php:141 +#, php-format +msgid "" +"You have a new posting address on %1$s.\n" +"\n" +"Send email to %2$s to post new messages.\n" +"\n" +"More email instructions at %3$s.\n" +"\n" +"Faithfully yours,\n" +"%4$s" +msgstr "" +"Tes unha nova dirección de envio de mensaxes en %1$s.\n" +"\n" +"Envia unha mensaxe a %2$s para enviar novas mensaxes.\n" +"\n" +"Hai máis instruccións de envio en %3$s.\n" +"\n" +"Sempre teu...,\n" +"%4$s" + +#: ../actions/twitapistatuses.php:612 +#: actions/twitapistatuses.php:537 +#: actions/twitapistatuses.php:455 +msgid "You may not delete another user's status." +msgstr "Non deberías eliminar o estado de outro usuario" + +#: ../actions/invite.php:31 +#: actions/invite.php:31 +#, php-format +msgid "You must be logged in to invite other users to use %s" +msgstr "Debes estar logueado para invitar a outros usuarios a empregar %s" + +#: ../actions/invite.php:103 +#: actions/invite.php:110 +msgid "You will be notified when your invitees accept the invitation and register on the site. Thanks for growing the community!" +msgstr "Notificaráseche cando os teus invitados acepten unha invitación e se rexistren neste sitio. Grazas por facer crecer o gremio lareteiro!" + +#: ../actions/recoverpassword.php:149 +#: actions/recoverpassword.php:154 +msgid "You've been identified. Enter a new password below. " +msgstr "Foiches identificado. Insire unha nova contrasinal abaixo." + +#: ../actions/openidlogin.php:67 +#: actions/openidlogin.php:76 +#: actions/login.php:127 +#: actions/openidlogin.php:84 +msgid "Your OpenID URL" +msgstr "O teu enderezo OpenID" + +#: ../actions/recoverpassword.php:164 +#: actions/recoverpassword.php:169 +msgid "Your nickname on this server, or your registered email address." +msgstr "O teu alcume neste servidor, ou o teu enderezo rexistrado." + +#: ../actions/openidsettings.php:28 +#: actions/openidsettings.php:28 +#, php-format +msgid "[OpenID](%%doc.openid%%) lets you log into many sites with the same user account. Manage your associated OpenIDs from here." +msgstr "[OpenID](%%doc.openid%%) permiteche acceder en moitos sitios coa mesma conta. Xestina os teus OpenIDs asociados dende aquí." + +#: ../lib/util.php:943 +#: lib/util.php:992 +#: lib/util.php:1349 +msgid "a few seconds ago" +msgstr "fai uns segundos" + +#: ../lib/util.php:955 +#: lib/util.php:1004 +#: lib/util.php:1361 +#, php-format +msgid "about %d days ago" +msgstr "fai %d días" + +#: ../lib/util.php:951 +#: lib/util.php:1000 +#: lib/util.php:1357 +#, php-format +msgid "about %d hours ago" +msgstr "fai %d horas" + +#: ../lib/util.php:947 +#: lib/util.php:996 +#: lib/util.php:1353 +#, php-format +msgid "about %d minutes ago" +msgstr "fai %d minutos" + +#: ../lib/util.php:959 +#: lib/util.php:1008 +#: lib/util.php:1365 +#, php-format +msgid "about %d months ago" +msgstr "fai %d meses" + +#: ../lib/util.php:953 +#: lib/util.php:1002 +#: lib/util.php:1359 +msgid "about a day ago" +msgstr "fai un día" + +#: ../lib/util.php:945 +#: lib/util.php:994 +#: lib/util.php:1351 +msgid "about a minute ago" +msgstr "fai un minuto" + +#: ../lib/util.php:957 +#: lib/util.php:1006 +#: lib/util.php:1363 +msgid "about a month ago" +msgstr "fai un mes" + +#: ../lib/util.php:961 +#: lib/util.php:1010 +#: lib/util.php:1367 +msgid "about a year ago" +msgstr "fai un ano" + +#: ../lib/util.php:949 +#: lib/util.php:998 +#: lib/util.php:1355 +msgid "about an hour ago" +msgstr "fai unha hora" + +#: ../actions/showstream.php:423 +#: ../lib/stream.php:132 +#: actions/showstream.php:441 +#: lib/stream.php:99 +#: lib/noticelist.php:211 +msgid "delete" +msgstr "eliminar" + +#: ../actions/noticesearch.php:130 +#: ../actions/showstream.php:408 +#: ../lib/stream.php:117 +#: actions/noticesearch.php:136 +#: actions/showstream.php:426 +#: lib/stream.php:84 +#: actions/noticesearch.php:135 +#: lib/facebookaction.php:202 +#: lib/noticelist.php:189 +msgid "in reply to..." +msgstr "en contestación a..." + +#: ../actions/noticesearch.php:137 +#: ../actions/showstream.php:415 +#: ../lib/stream.php:124 +#: actions/noticesearch.php:143 +#: actions/showstream.php:433 +#: lib/stream.php:91 +#: actions/noticesearch.php:142 +#: lib/noticelist.php:199 +msgid "reply" +msgstr "contestar" + +#: ../actions/password.php:44 +#: actions/profilesettings.php:183 +#: actions/profilesettings.php:200 +msgid "same as password above" +msgstr "igual á contrasinal de enriba" + +#: ../actions/twitapistatuses.php:755 +#: actions/twitapistatuses.php:678 +#: actions/twitapistatuses.php:543 +msgid "unsupported file type" +msgstr "tipo de ficheiro non soportado" + +#: ../lib/util.php:1309 +#: lib/util.php:1443 +#: lib/facebookaction.php:251 +#: lib/util.php:1842 +msgid "« After" +msgstr "« Despois" + +#: actions/deletenotice.php:74 +#: actions/disfavor.php:43 +#: actions/emailsettings.php:127 +#: actions/favor.php:45 +#: actions/finishopenidlogin.php:33 +#: actions/imsettings.php:105 +#: actions/invite.php:46 +#: actions/newmessage.php:45 +#: actions/openidlogin.php:36 +#: actions/openidsettings.php:123 +#: actions/profilesettings.php:47 +#: actions/recoverpassword.php:282 +#: actions/register.php:42 +#: actions/remotesubscribe.php:40 +#: actions/smssettings.php:124 +#: actions/subscribe.php:44 +#: actions/twittersettings.php:97 +#: actions/unsubscribe.php:41 +#: actions/userauthorization.php:35 +#: actions/block.php:38 +#: actions/deleteprofile.php:127 +#: actions/disfavor.php:47 +#: actions/emailsettings.php:131 +#: actions/favor.php:49 +#: actions/imsettings.php:108 +#: actions/login.php:45 +#: actions/newmessage.php:44 +#: actions/newnotice.php:36 +#: actions/nudge.php:47 +#: actions/othersettings.php:139 +#: actions/profilesettings.php:49 +#: actions/smssettings.php:126 +#: actions/subedit.php:38 +#: actions/tagother.php:113 +#: actions/twittersettings.php:188 +#: actions/unblock.php:38 +msgid "There was a problem with your session token. Try again, please." +msgstr "Houbo un problema co teu token de sesión. Tentao de novo, anda..." + +#: actions/disfavor.php:55 +msgid "This notice is not a favorite!" +msgstr "Este chío non é un favorito!" + +#: actions/disfavor.php:63 +msgid "Could not delete favorite." +msgstr "Non se puido eliminar o favorito." + +#: actions/disfavor.php:72 +msgid "Favor" +msgstr "Gostame" + +#: actions/emailsettings.php:92 +msgid "Send me email when someone adds my notice as a favorite." +msgstr "Enviar un correo cando alguen enganda un chío meu coma favorito." + +#: actions/emailsettings.php:95 +msgid "Send me email when someone sends me a private message." +msgstr "Enviarme un email cando alguén me envíe unha mensaxe privada." + +#: actions/favor.php:53 +#: actions/twitapifavorites.php:142 +#: actions/favor.php:54 +#: actions/twitapifavorites.php:115 +msgid "This notice is already a favorite!" +msgstr "Este chío xa é un favorito!" + +#: actions/favor.php:60 +#: actions/twitapifavorites.php:151 +#: classes/Command.php:132 +#: actions/favor.php:61 +#: actions/twitapifavorites.php:122 +msgid "Could not create favorite." +msgstr "Non se puido crear o favorito." + +#: actions/favor.php:70 +msgid "Disfavor" +msgstr "Non me gosta" + +#: actions/favoritesrss.php:60 +#: actions/showfavorites.php:47 +#: actions/favoritesrss.php:62 +#, php-format +msgid "%s favorite notices" +msgstr "%s chíos favoritos" + +#: actions/favoritesrss.php:64 +#: actions/favoritesrss.php:66 +#, php-format +msgid "Feed of favorite notices of %s" +msgstr "Fonte para os chíos favoritos de %s" + +#: actions/inbox.php:28 +#, php-format +msgid "Inbox for %s - page %d" +msgstr "Band. Entrada para %s - páxina %d" + +#: actions/inbox.php:30 +#, php-format +msgid "Inbox for %s" +msgstr "Band. Entrada para %s" + +#: actions/inbox.php:53 +msgid "This is your inbox, which lists your incoming private messages." +msgstr "Esta é a túa bandexa de entrada, aquí móstranse as túas mensaxes privadas." + +#: actions/invite.php:178 +#, php-format +msgid "" +"%1$s has invited you to join them on %2$s (%3$s).\n" +"\n" +msgstr "Acabas de invitar a %1$s a unirse a %2$s (%3$s).\n" + +#: actions/login.php:104 +msgid "Automatically login in the future; " +msgstr "Loguearse automáticamente no futuro:" + +#: actions/login.php:122 +msgid "For security reasons, please re-enter your " +msgstr "Por razóns de seguranza, por favor re-insire o teu " + +#: actions/login.php:126 +msgid "Login with your username and password. " +msgstr "Accede co teu nome de usuario e contrasinal." + +#: actions/newmessage.php:58 +#: actions/twitapidirect_messages.php:130 +#: actions/newmessage.php:60 +#: actions/twitapidirect_messages.php:136 +msgid "That's too long. Max message size is 140 chars." +msgstr "Iso é demasiado longo. O tamaño máximo para unha mensaxe é de 140 caracteres." + +#: actions/newmessage.php:65 +#: actions/newmessage.php:68 +msgid "No recipient specified." +msgstr "Non se especificou ningún destinatario" + +#: actions/newmessage.php:68 +#: actions/newmessage.php:113 +#: classes/Command.php:206 +#: actions/newmessage.php:71 +#: actions/newmessage.php:116 +#: classes/Command.php:211 +msgid "You can't send a message to this user." +msgstr "Non podes enviar mensaxes a este usurio." + +#: actions/newmessage.php:71 +#: actions/twitapidirect_messages.php:146 +#: classes/Command.php:209 +#: actions/newmessage.php:74 +#: actions/twitapidirect_messages.php:153 +#: classes/Command.php:214 +msgid "Don't send a message to yourself; just say it to yourself quietly instead." +msgstr "Non te envies mensaxes a ti mesmo!! só fala contigo mesmo baixiño, senón vante tomar por tolo." + +#: actions/newmessage.php:108 +#: actions/microsummary.php:32 +#: actions/newmessage.php:111 +msgid "No such user" +msgstr "Non é o usuario" + +#: actions/newmessage.php:117 +#: actions/newmessage.php:120 +msgid "New message" +msgstr "Nova mensaxe" + +#: actions/noticesearch.php:95 +#: actions/noticesearch.php:94 +msgid "Notice without matching profile" +msgstr "Chío sen perfil coincidente" + +#: actions/openidsettings.php:28 +#, php-format +msgid "[OpenID](%%doc.openid%%) lets you log into many sites " +msgstr "[OpenID](%%doc.openid%%) permiteche acceder en moitos sitios" + +#: actions/openidsettings.php:46 +msgid "If you want to add an OpenID to your account, " +msgstr "Se queres engadir un enderezo OpenID á tua conta, " + +#: actions/openidsettings.php:74 +msgid "Removing your only OpenID would make it impossible to log in! " +msgstr "Eliminando o teu enderezo OpenID serache imposíbel acceder!" + +#: actions/openidsettings.php:87 +msgid "You can remove an OpenID from your account " +msgstr "Podes eliminar un identificador OpenID da túa conta " + +#: actions/outbox.php:28 +#, php-format +msgid "Outbox for %s - page %d" +msgstr "Band. Saída para %s - páxina %d" + +#: actions/outbox.php:30 +#, php-format +msgid "Outbox for %s" +msgstr "Band. Saída para %s" + +#: actions/outbox.php:53 +msgid "This is your outbox, which lists private messages you have sent." +msgstr "Esta é a túa band. saída, que mostra as mensaxes privadas que enviaches." + +#: actions/peoplesearch.php:28 +#, php-format +msgid "Search for people on %%site.name%% by their name, location, or interests. " +msgstr "Procurar xente en %%site.name%% polo seu nome, localización, ou intereses. " + +#: actions/profilesettings.php:27 +msgid "You can update your personal profile info here " +msgstr "Podes actualizar a túa información do perfil persoal aquí" + +#: actions/profilesettings.php:115 +#: actions/remotesubscribe.php:320 +#: actions/userauthorization.php:159 +#: actions/userrss.php:76 +#: actions/profilesettings.php:132 +#: actions/remotesubscribe.php:333 +msgid "User without matching profile" +msgstr "Usuario sen un perfil que coincida." + +#: actions/recoverpassword.php:91 +msgid "This confirmation code is too old. " +msgstr "Ese código de confirmación é demasiado antigo." + +#: actions/recoverpassword.php:141 +msgid "If you've forgotten or lost your" +msgstr "Se esquenciches ou perdeches a túa" + +#: actions/recoverpassword.php:154 +msgid "You've been identified. Enter a " +msgstr "Foches identificado. Insire unha " + +#: actions/recoverpassword.php:169 +msgid "Your nickname on this server, " +msgstr "O teu alcume neste servidor, " + +#: actions/recoverpassword.php:271 +msgid "Instructions for recovering your password " +msgstr "Instruccións para recuperar a túa contrasinal." + +#: actions/recoverpassword.php:327 +msgid "New password successfully saved. " +msgstr "A nova contrasinal foi gardada correctamente." + +#: actions/register.php:95 +#: actions/register.php:104 +msgid "Password must be 6 or more characters." +msgstr "A contrasinal debe ter 6 caracteres ou máis." + +#: actions/register.php:216 +#, php-format +msgid "Congratulations, %s! And welcome to %%%%site.name%%%%. From here, you may want to..." +msgstr "Noraboa, %s! E benvido a %%%%site.name%%%%. Dende aquí, igual queres..." + +#: actions/register.php:227 +msgid "(You should receive a message by email momentarily, with " +msgstr "(Deberías recibir unha mensaxe no teu email nun intre, con" + +#: actions/remotesubscribe.php:51 +#, php-format +msgid "To subscribe, you can [login](%%action.login%%)," +msgstr "Para suscribirse, podes [loguearte](%%action.login%%)," + +#: actions/showfavorites.php:61 +#, php-format +msgid "Feed for favorites of %s" +msgstr "Fonte para os favoritos de %s" + +#: actions/showfavorites.php:84 +#: actions/twitapifavorites.php:85 +#: actions/showfavorites.php:88 +#: actions/twitapifavorites.php:57 +msgid "Could not retrieve favorite notices." +msgstr "Non se pode " + +#: actions/showmessage.php:33 +msgid "No such message." +msgstr "Non existe a mensaxe." + +#: actions/showmessage.php:42 +msgid "Only the sender and recipient may read this message." +msgstr "Só o emisor e destinatario poden ler esta mensaxe." + +#: actions/showmessage.php:61 +#, php-format +msgid "Message to %1$s on %2$s" +msgstr "Mensaxe de %1$s en %2$s" + +#: actions/showmessage.php:66 +#, php-format +msgid "Message from %1$s on %2$s" +msgstr "Mensaxe dende %1$s en %2$s" + +#: actions/showstream.php:154 +#: lib/util.php:2164 +msgid "Send a message" +msgstr "Enviar unha mensaxe" + +#: actions/smssettings.php:312 +#, php-format +msgid "Mobile carrier for your phone. " +msgstr "Operadora móbil do teu teléfono." + +#: actions/twitapidirect_messages.php:76 +#: actions/twitapidirect_messages.php:64 +#, php-format +msgid "Direct messages to %s" +msgstr "Mensaxes directas para %s" + +#: actions/twitapidirect_messages.php:77 +#: actions/twitapidirect_messages.php:65 +#, php-format +msgid "All the direct messages sent to %s" +msgstr "Tódalas mensaxes directas enviadas a %s" + +#: actions/twitapidirect_messages.php:81 +#: actions/twitapidirect_messages.php:69 +msgid "Direct Messages You've Sent" +msgstr "Mensaxes Directas que Enviaches." + +#: actions/twitapidirect_messages.php:82 +#: actions/twitapidirect_messages.php:70 +#, php-format +msgid "All the direct messages sent from %s" +msgstr "Tódalas mensaxes directas enviadas dende %s" + +#: actions/twitapidirect_messages.php:128 +#: actions/twitapidirect_messages.php:132 +msgid "No message text!" +msgstr "Non hai mensaxes de texto!" + +#: actions/twitapidirect_messages.php:138 +#: actions/twitapidirect_messages.php:145 +msgid "Recipient user not found." +msgstr "Usuario destinatario non atopado." + +#: actions/twitapidirect_messages.php:141 +#: actions/twitapidirect_messages.php:148 +msgid "Can't send direct messages to users who aren't your friend." +msgstr "Non se pode enviar a mensaxe directa a usuarios dos que non eres amigo." + +#: actions/twitapifavorites.php:92 +#: actions/twitapifavorites.php:64 +#, php-format +msgid "%s / Favorites from %s" +msgstr "%s / Favoritos dende %s" + +#: actions/twitapifavorites.php:95 +#: actions/twitapifavorites.php:67 +#, php-format +msgid "%s updates favorited by %s / %s." +msgstr "%s updates favorited by %s / %s." + +#: actions/twitapifavorites.php:187 +#: lib/mail.php:275 +#: actions/twitapifavorites.php:158 +#: lib/mail.php:293 +#, php-format +msgid "%s added your notice as a favorite" +msgstr "%s gustoulle o teu chío" + +#: actions/twitapifavorites.php:188 +#: lib/mail.php:276 +#, php-format +msgid "" +"%1$s just added your notice from %2$s as one of their favorites.\n" +"\n" +msgstr "" +"A %1$s gustoulle o teu chío %2$s e marcouno como favorito.\n" +"\n" + +#: actions/twittersettings.php:27 +msgid "Add your Twitter account to automatically send your notices to Twitter, " +msgstr "Engade a túa conta de Twitter para enviar automáticamente os teus chíos a Twitter." + +#: actions/twittersettings.php:41 +#: actions/twittersettings.php:43 +msgid "Twitter settings" +msgstr "Configuracións de Twitter" + +#: actions/twittersettings.php:48 +#: actions/twittersettings.php:50 +msgid "Twitter Account" +msgstr "Conta de Twitter" + +#: actions/twittersettings.php:56 +#: actions/twittersettings.php:58 +msgid "Current verified Twitter account." +msgstr "Conta de twitter verificada actualmente." + +#: actions/twittersettings.php:63 +msgid "Twitter Username" +msgstr "Nome de usuario en Twitter" + +#: actions/twittersettings.php:65 +msgid "No spaces, please." +msgstr "Sen espazos, anda..." + +#: actions/twittersettings.php:67 +msgid "Twitter Password" +msgstr "Contrasinal de Twitter" + +#: actions/twittersettings.php:72 +msgid "Automatically send my notices to Twitter." +msgstr "Enviar automáticamente os meus chíos a Twitter." + +#: actions/twittersettings.php:75 +msgid "Send local \"@\" replies to Twitter." +msgstr "Enviar respostas \"@\" locais a Twitter." + +#: actions/twittersettings.php:78 +msgid "Subscribe to my Twitter friends here." +msgstr "Suscribirse ós meus amigos de Twitter aquí." + +#: actions/twittersettings.php:122 +#: actions/twittersettings.php:216 +msgid "Username must have only numbers, upper- and lowercase letters, and underscore (_). 15 chars max." +msgstr "O nome de usuario debe ter só letras, minúsculas e maiúsculas, e números, e sen espazos." + +#: actions/twittersettings.php:128 +#: actions/twittersettings.php:221 +msgid "Could not verify your Twitter credentials!" +msgstr "Non se puideron verificar as túas credenciais en Twitter!" + +#: actions/twittersettings.php:137 +#: actions/twittersettings.php:228 +#, php-format +msgid "Unable to retrieve account information for \"%s\" from Twitter." +msgstr "Non se puido recoller información da túa conta \"%s\" en Twitter." + +#: actions/twittersettings.php:151 +#: actions/twittersettings.php:170 +#: actions/twittersettings.php:234 +#: actions/twittersettings.php:253 +msgid "Unable to save your Twitter settings!" +msgstr "Non se puideron gardar os teus axustes de Twitter!" + +#: actions/twittersettings.php:174 +#: actions/twittersettings.php:261 +msgid "Twitter settings saved." +msgstr "Configuracións de Twitter gardadas." + +#: actions/twittersettings.php:192 +#: actions/twittersettings.php:272 +msgid "That is not your Twitter account." +msgstr "Esa non é a túa conta de Twitter." + +#: actions/twittersettings.php:200 +#: actions/twittersettings.php:208 +#: actions/twittersettings.php:280 +msgid "Couldn't remove Twitter user." +msgstr "Non se puido eliminar o usuario." + +#: actions/twittersettings.php:212 +#: actions/twittersettings.php:284 +msgid "Twitter account removed." +msgstr "Conta de Twitter " + +#: actions/twittersettings.php:225 +#: actions/twittersettings.php:239 +#: actions/twittersettings.php:299 +#: actions/twittersettings.php:310 +#: actions/twittersettings.php:322 +msgid "Couldn't save Twitter preferences." +msgstr "Non se puideron gardar as preferenzas de Twitter." + +#: actions/twittersettings.php:245 +#: actions/twittersettings.php:330 +msgid "Twitter preferences saved." +msgstr "Preferencias de Twitter gardadas." + +#: actions/userauthorization.php:84 +msgid "Please check these details to make sure " +msgstr "Comproba estes datos para asegurarte" + +#: actions/userauthorization.php:324 +msgid "The subscription has been authorized, but no " +msgstr "A suscrición foi autorizada, pero non" + +#: actions/userauthorization.php:334 +msgid "The subscription has been rejected, but no " +msgstr "A suscrición foi rexeitada, pero non" + +#: classes/Channel.php:113 +#: classes/Channel.php:129 +msgid "Command results" +msgstr "Resultados do comando" + +#: classes/Channel.php:148 +#: classes/Channel.php:175 +msgid "Command complete" +msgstr "Comando completo" + +#: classes/Channel.php:158 +#: classes/Channel.php:185 +msgid "Command failed" +msgstr "Comando fallido" + +#: classes/Command.php:39 +msgid "Sorry, this command is not yet implemented." +msgstr "Desculpa, este comando todavía non está implementado." + +#: classes/Command.php:96 +#, php-format +msgid "Subscriptions: %1$s\n" +msgstr "Subscricións: %1$s.\n" + +#: classes/Command.php:125 +#: classes/Command.php:242 +#: classes/Command.php:247 +msgid "User has no last notice" +msgstr "O usuario non ten último chio." + +#: classes/Command.php:146 +msgid "Notice marked as fave." +msgstr "Chío marcado coma favorito." + +#: classes/Command.php:166 +#, php-format +msgid "%1$s (%2$s)" +msgstr "%1$s (%2$s)" + +#: classes/Command.php:169 +#, php-format +msgid "Fullname: %s" +msgstr "Nome completo: %s" + +#: classes/Command.php:172 +#, php-format +msgid "Location: %s" +msgstr "Ubicación: %s" + +#: classes/Command.php:175 +#, php-format +msgid "Homepage: %s" +msgstr "Páxina persoal: %s" + +#: classes/Command.php:178 +#, php-format +msgid "About: %s" +msgstr "Sobre: %s" + +#: classes/Command.php:200 +#: classes/Command.php:202 +#, php-format +msgid "Message too long - maximum is 140 characters, you sent %d" +msgstr "Mensaxe demasiado longa - o máximo é 140 caracteres, ti enviaches %d " + +#: classes/Command.php:214 +#: classes/Command.php:219 +#, php-format +msgid "Direct message to %s sent" +msgstr "Mensaxe directo a %s enviado" + +#: classes/Command.php:216 +#: classes/Command.php:221 +msgid "Error sending direct message." +msgstr "Erro ó enviar a mensaxe directa." + +#: classes/Command.php:263 +#: classes/Command.php:268 +msgid "Specify the name of the user to subscribe to" +msgstr "Especifica o nome do usuario ó que queres suscribirte" + +#: classes/Command.php:270 +#: classes/Command.php:275 +#, php-format +msgid "Subscribed to %s" +msgstr "Suscrito a %s" + +#: classes/Command.php:288 +#: classes/Command.php:293 +msgid "Specify the name of the user to unsubscribe from" +msgstr "Especifica o nome de usuario ó que queres deixar de seguir" + +#: classes/Command.php:295 +#: classes/Command.php:300 +#, php-format +msgid "Unsubscribed from %s" +msgstr "Desuscribir de %s" + +#: classes/Command.php:310 +#: classes/Command.php:330 +#: classes/Command.php:315 +#: classes/Command.php:335 +msgid "Command not yet implemented." +msgstr "Comando non implementado." + +#: classes/Command.php:313 +#: classes/Command.php:318 +msgid "Notification off." +msgstr "Notificación desactivada." + +#: classes/Command.php:315 +#: classes/Command.php:320 +msgid "Can't turn off notification." +msgstr "No se pode desactivar a notificación." + +#: classes/Command.php:333 +#: classes/Command.php:338 +msgid "Notification on." +msgstr "Notificación habilitada." + +#: classes/Command.php:335 +#: classes/Command.php:340 +msgid "Can't turn on notification." +msgstr "Non se pode activar a notificación." + +#: classes/Command.php:344 +msgid "Commands:\n" +msgstr "Comandos:\n" + +#: classes/Message.php:53 +msgid "Could not insert message." +msgstr "Non se pode inserir unha mensaxe." + +#: classes/Message.php:63 +msgid "Could not update message with new URI." +msgstr "Non se puido actualizar a mensaxe coa nova URI." + +#: lib/gallery.php:46 +#: lib/gallery.php:55 +msgid "User without matching profile in system." +msgstr "Usuario sen perfil coincidente no sistema." + +#: lib/mail.php:147 +#, php-format +msgid "" +"You have a new posting address on %1$s.\n" +"\n" +msgstr "" +"Tes novas direccións de posteo en %1$s.\n" +"\n" + +#: lib/mail.php:249 +#: lib/mail.php:265 +#, php-format +msgid "New private message from %s" +msgstr "%s enviouche unha nova mensaxe privada" + +#: lib/mail.php:253 +#, php-format +msgid "" +"%1$s (%2$s) sent you a private message:\n" +"\n" +msgstr "" +"%1$s (%2$s) enviouche unha mensaxe privada:\n" +"\n" + +#: lib/mailbox.php:43 +msgid "Only the user can read their own mailboxes." +msgstr "Só o usuario pode ler os seus propios buzóns." + +#: lib/openid.php:195 +msgid "This form should automatically submit itself. " +msgstr "Este formulario debería enviarse automáticamente él mesmo." + +#: lib/personal.php:65 +msgid "Favorites" +msgstr "Favoritos" + +#: lib/personal.php:66 +#, php-format +msgid "%s's favorite notices" +msgstr "Chíos favoritos de %s" + +#: lib/personal.php:66 +msgid "User" +msgstr "Usuario" + +#: lib/personal.php:75 +msgid "Inbox" +msgstr "Band. Entrada" + +#: lib/personal.php:76 +msgid "Your incoming messages" +msgstr "As túas mensaxes entrantes" + +#: lib/personal.php:80 +msgid "Outbox" +msgstr "Band. Saída" + +#: lib/personal.php:81 +msgid "Your sent messages" +msgstr "As túas mensaxes enviadas" + +#: lib/settingsaction.php:99 +#: actions/deleteprofile.php:256 +#: lib/settingsaction.php:103 +#: lib/settingsaction.php:105 +msgid "Twitter" +msgstr "Twitter" + +#: lib/settingsaction.php:100 +#: actions/deleteprofile.php:257 +#: lib/settingsaction.php:104 +#: lib/settingsaction.php:106 +msgid "Twitter integration options" +msgstr "Opcións de integración de Twitter" + +#: lib/util.php:1718 +#: lib/util.php:2204 +msgid "To" +msgstr "A" + +#: scripts/maildaemon.php:45 +msgid "Could not parse message." +msgstr "Non se puido analizaar a mensaxe." + +#: actions/block.php:45 +#: actions/subedit.php:45 +#: actions/unblock.php:45 +msgid "No profile specified." +msgstr "Non se especificou ningún perfil." + +#: actions/block.php:52 +#: actions/subedit.php:52 +#: actions/tagother.php:45 +#: actions/unblock.php:52 +msgid "No profile with that ID." +msgstr "Non se atopou un perfil con ese ID." + +#: actions/block.php:78 +msgid "Block user" +msgstr "Bloquear usuario" + +#: actions/block.php:81 +msgid "Are you sure you want to block this user? Afterwards, they will be unsubscribed from you, unable to subscribe to you in the future, and you will not be notified of any @-replies from them." +msgstr "Seguro que queres bloquear a este usuario? Despois diso, vai ser de-suscrito do teur perfil, non será capaz de suscribirse a ti nun futuro, e non vas a ser notificado de ningunha resposta-@ del." + +#: actions/block.php:117 +msgid "You have already blocked this user." +msgstr "Xa bloqueaches a este usuario." + +#: actions/block.php:124 +msgid "Failed to save block information." +msgstr "Erro ao gardar información de bloqueo." + +#: actions/deleteprofile.php:25 +msgid "Code not yet ready." +msgstr "Código non implementado." + +#: actions/deleteprofile.php:36 +msgid "Export and delete your user information." +msgstr "Exportar e eliminar a túa información de usuario." + +#: actions/deleteprofile.php:88 +#: actions/deleteprofile.php:119 +msgid "Delete my account" +msgstr "Borrar a miña conta" + +#: actions/deleteprofile.php:89 +msgid "Delete my account confirmation" +msgstr "Confirmación de borrado da miña conta" + +#: actions/deleteprofile.php:117 +msgid "Check if you are sure you want to delete your account." +msgstr "Estas seguro que queres eliminar a tua conta?" + +#: actions/deleteprofile.php:259 +#: lib/settingsaction.php:106 +#: lib/util.php:384 +#: lib/settingsaction.php:108 +msgid "Other" +msgstr "Outros" + +#: actions/deleteprofile.php:260 +#: lib/settingsaction.php:107 +#: lib/settingsaction.php:109 +msgid "Other options" +msgstr "Outras opcions" + +#: actions/disfavor.php:72 +msgid "Add to favorites" +msgstr "Engadir a favoritos" + +#: actions/emailsettings.php:98 +msgid "Allow friends to nudge me and send me an email." +msgstr "Permitir aos amigos darme toques e enviarme correos electrónicos." + +#: actions/facebookremove.php:54 +msgid "Couldn't remove Facebook user." +msgstr "Non se puido eliminar o usuario de Facebook." + +#: actions/favorited.php:31 +#: lib/stream.php:48 +msgid "Popular notices" +msgstr "Chíos populares" + +#: actions/favorited.php:54 +msgid "Showing recently popular notices" +msgstr "Amosando chíos populares recentes" + +#: actions/favor.php:71 +msgid "Disfavor favorite" +msgstr "Desactivar favorito" + +#: actions/featured.php:32 +#: actions/featured.php:54 +#: lib/stream.php:44 +msgid "Featured users" +msgstr "Usuarios destacados" + +#: actions/finishremotesubscribe.php:186 +msgid "That user has blocked you from subscribing." +msgstr "Este usuario non che permite suscribirte a el." + +#: actions/imsettings.php:83 +msgid "Send me notices from public timeline through Jabber/GTalk." +msgstr "Enviarme os chíos da liña de tempo pública a través de Jabber/GTalk." + +#: actions/login.php:120 +msgid "you can also login using OpenID" +msgstr "tamén te podes rexistrar cunha conta OpenID" + +#: actions/microsummary.php:39 +msgid "No current status" +msgstr "Sen estado actual" + +#: actions/misc.php:81 +msgid "Embedded" +msgstr "Embebida" + +#: actions/misc.php:82 +#, php-format +msgid "Embedded %1s" +msgstr "%1s embebida" + +#: actions/misc.php:84 +msgid "WP" +msgstr "WP" + +#: actions/newnotice.php:93 +msgid "Notice posted" +msgstr "Chío publicado" + +#: actions/newnotice.php:116 +#: classes/Channel.php:140 +msgid "Ajax Error" +msgstr "Erro de Ajax" + +#: actions/nudge.php:52 +msgid "This user doesn't allow nudges or hasn't confirmed or set his email yet." +msgstr "Este usuario non permite toques, ou non confirmou ainda o seu correo electrónico." + +#: actions/nudge.php:61 +msgid "Nudge sent" +msgstr "Toque enviado" + +#: actions/othersettings.php:27 +msgid "Manage various other options." +msgstr "Xestionár axustes varios." + +#: actions/othersettings.php:33 +msgid "Other Settings" +msgstr "Outros axustes" + +#: actions/othersettings.php:35 +msgid "URL Auto-shortening" +msgstr "Auto-acortado de URL" + +#: actions/othersettings.php:54 +msgid "Service" +msgstr "Servizo" + +#: actions/othersettings.php:54 +msgid "Automatic shortening service to use." +msgstr "Servizo de acortado automático a usar." + +#: actions/othersettings.php:155 +msgid "URL shortening service is too long (max 50 chars)." +msgstr "Sistema de acortamento de URLs demasiado longo (max 50 car.)." + +#: actions/peopletag.php:33 +#, php-format +msgid "Not a valid people tag: %s" +msgstr "%s non é unha etiqueta de xente válida" + +#: actions/peopletag.php:45 +#, php-format +msgid "Users self-tagged with %s - page %d" +msgstr "Usuarios auto-etiquetados como %s - páxina %d" + +#: actions/peopletag.php:87 +#, php-format +msgid "These are users who have tagged themselves \"%s\" to show a common interest, characteristic, hobby or job." +msgstr "Estes son usuarios que se etiquetaron a sí mesmos como \"%s\" para mostrar intereses comuns, características, aficións ou traballos." + +#: actions/profilesettings.php:92 +msgid "Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated" +msgstr "Etiquetas para o teu usuario (letras, números, -, ., e _), separados por coma ou espazo" + +#: actions/profilesettings.php:116 +msgid "Theme" +msgstr "Tema" + +#: actions/profilesettings.php:116 +msgid "Preferred theme" +msgstr "Tema preferido" + +#: actions/profilesettings.php:259 +#: actions/tagother.php:131 +#, php-format +msgid "Invalid tag: \"%s\"" +msgstr "Etiqueta inválida: '%s'" + +#: actions/profilesettings.php:345 +msgid "Couldn't save tags." +msgstr "Non se puideron gardar as etiquetas." + +#: actions/public.php:68 +#, php-format +msgid "This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service based on the Free Software [StatusNet](http://status.net/) tool. [Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ([Read more](%%doc.help%%))" +msgstr "Esto é %%site.name%%, un servizo de [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) basado na ferramenta de código aberto [StatusNet](http://status.net/). [Únete agora](%%action.register%%) para compartir chíos cos teus amigos, colegas e familia! ([Ler mais](%%doc.help%%))" + +#: actions/public.php:90 +msgid "Could not retrieve public stream." +msgstr "Non se pudo recuperar a liña de tempo publica." + +#: actions/register.php:67 +#: actions/register.php:177 +msgid "Sorry, only invited people can register." +msgstr "Desculpa, só se pode rexistrar a xente con invitación." + +#: actions/register.php:156 +#, php-format +msgid "With this form you can create a new account. You can then post notices and link up to friends and colleagues. (Have an [OpenID](http://openid.net/)? Try our [OpenID registration](%%action.openidlogin%%)!)" +msgstr "Neste formulario podes crear unha conta de usuario. Logo poderás publicar chíos, e suscribirte a amigos. (Tes unha conta [OpenID](http://openid.net/)? Proba o noso [Rexistro OpenID](%%action.openidlogin%%)!)" + +#: actions/remotesubscribe.php:136 +#: actions/remotesubscribe.php:141 +msgid "That's a local profile! Login to subscribe." +msgstr "Este é un perfil local! Rexístrate para suscribirte." + +#: actions/smspostsettings.php:27 +#, php-format +msgid "You can post on %1s sending an sms to %2s in the followin format: '%3s'" +msgstr "Podes publicar chíos en %1s enviando un SMS a %2s co seguinte formato: '%3s'" + +#: actions/smspostsettings.php:32 +#: actions/smspostsettings.php:33 +#: actions/smspostsettings.php:57 +msgid "SMS Post Settings" +msgstr "Configuracións de envio por SMS" + +#: actions/smspostsettings.php:101 +#: actions/smspostsettings.php:106 +msgid "The address was added." +msgstr "Enderezo engadido." + +#: actions/subedit.php:68 +msgid "You are not subscribed to that profile." +msgstr "Non estás suscrito a ese perfil" + +#: actions/subedit.php:81 +msgid "Could not save subscription." +msgstr "Non se pode gardar a subscrición." + +#: actions/subscribe.php:53 +msgid "Not a local user." +msgstr "Non é usuario local." + +#: actions/subscribe.php:67 +msgid "Subscribed" +msgstr "Suscrito" + +#: actions/subscriptions.php:72 +msgid "Jabber" +msgstr "Jabber." + +#: actions/tagother.php:31 +msgid "Not logged in" +msgstr "Non estás logueado." + +#: actions/tagother.php:40 +msgid "No id argument." +msgstr "Non hai argumento id." + +#: actions/tagother.php:56 +msgid "Tag a person" +msgstr "Etiquetar a unha persoa" + +#: actions/tagother.php:98 +msgid "Tags for this user (letters, numbers, -, ., and _), comma- or space- separated" +msgstr "Etiquetas para este usuario (letras, numeros, -, ., e _), separados por coma ou espazo" + +#: actions/tagother.php:120 +msgid "No such profile." +msgstr "Non existe o perfil." + +#: actions/tagother.php:146 +msgid "You can only tag people you are subscribed to or who are subscribed to you." +msgstr "Só podes etiquetar xente á que estás suscrito ou aos que están suscritos a ti." + +#: actions/tagother.php:153 +msgid "Could not save tags." +msgstr "Non se poden gardar as etiquetas." + +#: actions/tagother.php:188 +msgid "Use this form to add tags to your subscribers or subscriptions." +msgstr "Usa este formulario para engadir etiquetas aos teus seguidores ou aos que sigues." + +#: actions/tag.php:66 +msgid "Showing all tags" +msgstr "Amosando tódalas etiquetas" + +#: actions/tagrss.php:33 +msgid "No such tag." +msgstr "Non existe a etiqueta." + +#: actions/tagrss.php:62 +#, php-format +msgid "Microblog tagged with %s" +msgstr "Microblog etiquetado con %s" + +#: actions/twitapiblocks.php:45 +msgid "Block user failed." +msgstr "Bloqueo de usuario fallido." + +#: actions/twitapiblocks.php:66 +msgid "Unblock user failed." +msgstr "Desbloqueo de usuario fallido." + +#: actions/twitapifavorites.php:159 +#: lib/mail.php:294 +#, php-format +msgid "" +"%1$s just added your notice from %2$s as one of their favorites.\n" +"\n" +"In case you forgot, you can see the text of your notice here:\n" +"\n" +"%3$s\n" +"\n" +"You can see the list of %1$s's favorites here:\n" +"\n" +"%4$s\n" +"\n" +"Faithfully yours,\n" +"%5$s\n" +msgstr "" +"%1$s acaba de engadir o teu chío en %2$s como un dos seus favoritos.\n" +"\n" +"Se o olvidaches, podes velo texto do teu chío aquí:\n" +"\n" +"%3$s\n" +"\n" +"Podes vela lista de cíos favoritos de %1$s aquí:\n" +"\n" +"%4$s\n" +"\n" +"Fielmente teu,\n" +"%5$s\n" + +#: actions/twitapiusers.php:46 +msgid "Not found." +msgstr "Non atopado" + +#: actions/twittersettings.php:29 +msgid "Add your Twitter account to automatically send your notices to Twitter, and subscribe to Twitter friends already here." +msgstr "Engade a túa conta de Twitter para enviar automáticamente os teus chíos a Twitter, e suscribirte aos usuarios de twiter que teñas como amigos aqui." + +#: actions/twittersettings.php:63 +msgid "Twitter user name" +msgstr "Nome de usuario en Twitter" + +#: actions/twittersettings.php:67 +msgid "Twitter password" +msgstr "Contrasinal de Twitter" + +#: actions/twittersettings.php:129 +msgid "Twitter Friends" +msgstr "Amigos de Twitter" + +#: actions/unblock.php:73 +msgid "Error removing the block." +msgstr "Acounteceu un erro borrando o bloqueo." + +#: actions/unsubscribe.php:48 +msgid "No profile id in request." +msgstr "Non hai identificador de perfil na peticion." + +#: actions/unsubscribe.php:55 +msgid "No profile with that id." +msgstr "Non se atopou un perfil con ese ID." + +#: actions/unsubscribe.php:69 +msgid "Unsubscribed" +msgstr "De-suscribido" + +#: actions/users.php:38 +#: lib/util.php:378 +msgid "Users list" +msgstr "Lista de usuarios" + +#: classes/Command.php:96 +#, php-format +msgid "" +"Subscriptions: %1$s\n" +"Subscribers: %2$s\n" +"Notices: %3$s" +msgstr "" +"Suscripcións: %1$s\n" +"Suscriptores: %2$s\n" +"Chíos: %3$s" + +#: classes/Command.php:349 +msgid "" +"Commands:\n" +"on - turn on notifications\n" +"off - turn off notifications\n" +"help - show this help\n" +"follow - subscribe to user\n" +"leave - unsubscribe from user\n" +"d - direct message to user\n" +"get - get last notice from user\n" +"whois - get profile info on user\n" +"fav - add user's last notice as a 'fave'\n" +"stats - get your stats\n" +"stop - same as 'off'\n" +"quit - same as 'off'\n" +"sub - same as 'follow'\n" +"unsub - same as 'leave'\n" +"last - same as 'get'\n" +"on - not yet implemented.\n" +"off - not yet implemented.\n" +"nudge - not yet implemented.\n" +"invite - not yet implemented.\n" +"track - not yet implemented.\n" +"untrack - not yet implemented.\n" +"track off - not yet implemented.\n" +"untrack all - not yet implemented.\n" +"tracks - not yet implemented.\n" +"tracking - not yet implemented.\n" +msgstr "" +"Comandos:\n" +"on - activar as notificacións\n" +"off - desactivar as notificacións\n" +"help - mostrar esta axuda\n" +"follow - suscribirse ao usuario\n" +"leave - de-suscribirse do usuario\n" +"d - mensaxe directa ao usuario\n" +"get - lelo último chío do usuario\n" +"whois - amosar informacion do usuario\n" +"fav - engadilo último chío do usuario como favorito\n" +"stats - amosalas túas estatísticas\n" +"stop - o mesmo que 'off'\n" +"quit - o mesmo que 'off'\n" +"sub - o mesmo que 'follow'\n" +"unsub - o mesmo que 'leave'\n" +"last - o mesmo que 'get'\n" +"on - non implementado por agora.\n" +"off - non implementado por agora.\n" +"nudge - non implementado por agora.\n" +"invite - non implementado por agora.\n" +"track - non implementado por agora.\n" +"untrack - non implementado por agora.\n" +"track off - non implementado por agora.\n" +"untrack all - non implementado por agora.\n" +"tracks - non implementado por agora.\n" +"tracking - non implementado por agora.\n" + +#: classes/Notice.php:100 +msgid "Problem saving notice. Unknown user." +msgstr "Aconteceu un erro ó gardar o chío. Usuario descoñecido." + +#: classes/Notice.php:105 +msgid "Too many notices too fast; take a breather and post again in a few minutes." +msgstr "Demasiados chíos en pouco tempo; tomate un respiro e envíao de novo dentro duns minutos." + +#: classes/Notice.php:112 +msgid "You are banned from posting notices on this site." +msgstr "Tes restrinxido o envio de chíos neste sitio." + +#: lib/gallery.php:98 +msgid "Filter tags" +msgstr "Filtrar etiquetas" + +#: lib/gallery.php:104 +msgid "All" +msgstr "Todos" + +#: lib/gallery.php:108 +msgid "Tag" +msgstr "Etiqueta" + +#: lib/gallery.php:109 +msgid "Choose a tag to narrow list" +msgstr "Elixe unha etiqueta para reducila lista" + +#: lib/gallery.php:110 +msgid "Go" +msgstr "Ir" + +#: lib/gallery.php:264 +msgid "Subscriptions navigation" +msgstr "Navegación de subscricións" + +#: lib/gallery.php:270 +#: lib/gallery.php:292 +msgid "List" +msgstr "Lista" + +#: lib/gallery.php:280 +#: lib/gallery.php:294 +msgid "Icons" +msgstr "Iconos" + +#: lib/mail.php:92 +#, php-format +msgid "" +"Hey, %s.\n" +"\n" +"Someone just entered this email address on %s.\n" +"\n" +"If it was you, and you want to confirm your entry, use the URL below:\n" +"\n" +"\t%s\n" +"\n" +"If not, just ignore this message.\n" +"\n" +"Thanks for your time, \n" +"%s\n" +msgstr "" +"Ei, %s.\n" +"\n" +"Alguén introduceu o teu correo electrónico en %s.\n" +"\n" +"Se foches ti, e queres confirmar a entrada, pincha na seguinte URL:\n" +"\n" +"\t%s\n" +"\n" +"Se non, simplemente ignora esta mensaxe.\n" +"\n" +"Grazas polo teu tempo, \n" +"%s\n" + +#: lib/mail.php:232 +#, php-format +msgid "You've been nudged by %s" +msgstr "%s douche un toque" + +#: lib/mail.php:236 +#, php-format +msgid "" +"%1$s (%2$s) is wondering what you are up to these days and is inviting you to post some news.\n" +"\n" +"So let's hear from you :)\n" +"\n" +"%3$s\n" +"\n" +"Don't reply to this email; it won't get to them.\n" +"\n" +"With kind regards,\n" +"%4$s\n" +msgstr "" +"%1$s (%2$s) preguntase que é de ti, e invítate a publicar algun chío.\n" +"\n" +"So let's hear from you :)\n" +"\n" +"%3$s\n" +"\n" +"Don't reply to this email; it won't get to them.\n" +"\n" +"With kind regards,\n" +"%4$s\n" + +#: lib/mail.php:269 +#, php-format +msgid "" +"%1$s (%2$s) sent you a private message:\n" +"\n" +"------------------------------------------------------\n" +"%3$s\n" +"------------------------------------------------------\n" +"\n" +"You can reply to their message here:\n" +"\n" +"%4$s\n" +"\n" +"Don't reply to this email; it won't get to them.\n" +"\n" +"With kind regards,\n" +"%5$s\n" +msgstr "" +"%1$s (%2$s) enviouche unha mensaxe privada:\n" +"\n" +"------------------------------------------------------\n" +"%3$s\n" +"------------------------------------------------------\n" +"\n" +"You can reply to their message here:\n" +"\n" +"%4$s\n" +"\n" +"Don't reply to this email; it won't get to them.\n" +"\n" +"With kind regards,\n" +"%5$s\n" + +#: lib/profilelist.php:146 +msgid "(none)" +msgstr "(nada)" + +#: lib/settingsaction.php:97 +#: lib/settingsaction.php:98 +msgid "SMS Post" +msgstr "Envío por SMS" + +#: lib/settingsaction.php:98 +#: lib/settingsaction.php:99 +msgid "Posts by SMS" +msgstr "Chíos dende SMS" + +#: lib/stream.php:33 +msgid "Public" +msgstr "Público" + +#: lib/stream.php:36 +#: lib/stream.php:37 +msgid "Recent tags" +msgstr "Etiquetas recentes" + +#: lib/stream.php:39 +#: lib/stream.php:40 +msgid "All tags" +msgstr "Tódalas etiquetas" + +#: lib/stream.php:43 +msgid "Featured" +msgstr "Destacado" + +#: lib/stream.php:47 +msgid "Popular" +msgstr "Popular" + +#: lib/subs.php:51 +msgid "User has blocked you." +msgstr "O usuario bloqueoute." + +#: lib/util.php:379 +msgid "Users" +msgstr "Usuarios" + +#: lib/util.php:2118 +msgid "Send a nudge" +msgstr "Dar un toque" + +#: lib/util.php:2122 +msgid "Nudge sent!" +msgstr "Toque enviado!" + +#: lib/util.php:2253 +msgid "Block" +msgstr "Bloquear" + +#: lib/util.php:2257 +msgid "Unblock" +msgstr "Desbloquear" + +#: actions/newsmsnotice.php:29 +#: actions/smspostsettings.php:57 +msgid "SMS Post Disabled!" +msgstr "Publicación de chíos por SMS desactivado!" + +#: actions/badgemisc.php:31 +#, php-format +msgid "%1s in your blog" +msgstr "%1s no teu blogue" + +#: actions/misc.php:85 +msgid "WordPress Plugin" +msgstr "Plugin WordPress" + +#: actions/misc.php:88 +#, php-format +msgid "Graphics and statistics about the state of %1s" +msgstr "Gráficos e estatísticas sobre o estado de %1s" + +#: actions/newsmsnotice.php:35 +#, php-format +msgid "%1s is not a reliable source." +msgstr "%1s non é unha orixe fiable." + +#: actions/newsmsnotice.php:60 +msgid "Unexpected error" +msgstr "Erro inesperado" + +#: actions/newsmsnotice.php:65 +msgid "No such phone" +msgstr "Non existe o telefono" + +#: actions/newsmsnotice.php:75 +msgid "Missing message" +msgstr "Falta a mensaxe" + +#: actions/newsmsnotice.php:91 +msgid "Notice published correctly" +msgstr "Chío publicado correctamente" + +#: actions/wpmisc.php:27 +msgid "If you want to have your notices on your Wordpress, then follow the instructions" +msgstr "Se queres ter os teus chios no teu Wordpress, sigue as instruccións" + +#: actions/wpmisc.php:31 +#, php-format +msgid "%1s on your Wordpress" +msgstr "%1s no teu Wordpress" + +#: actions/badgemisc.php:27 +msgid "If you want to have your notices on your blog/website, then follow the instructions" +msgstr "Se queres ter os teus chios no teu blogue/sitio web, sigue as instruccións" + -- cgit v1.2.3-54-g00ecf From 531c0738daab2d5f9b8324b85b45af7131aec0da Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 16 Oct 2009 17:42:27 +0000 Subject: Workaround for not fully natively set up locales with native gettext... Set "en_US" locale first, then the locale we want for our user. This seems to initialize gettext properly somehow, which I could see when the languages would come up briefly on settings save when changing from a supported language. Definitely works for ga_ES on my Ubuntu system (8.10 intrepid), hopefully reasonably consistent. --- lib/util.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/util.php b/lib/util.php index 9b299cb14..38147e998 100644 --- a/lib/util.php +++ b/lib/util.php @@ -51,13 +51,24 @@ function common_init_locale($language=null) function common_init_language() { mb_internal_encoding('UTF-8'); + + // gettext seems very picky... We first need to setlocale() + // to a locale which _does_ exist on the system, and _then_ + // we can set in another locale that may not be set up + // (say, ga_ES for Galego/Galician) it seems to take it. + common_init_locale("en_US"); + $language = common_language(); - // So we don't have to make people install the gettext locales $locale_set = common_init_locale($language); - bindtextdomain("statusnet", common_config('site','locale_path')); + setlocale(LC_CTYPE, 'C'); + + // So we don't have to make people install the gettext locales + $path = common_config('site','locale_path'); + common_log(LOG_INFO, "binding text domain: $path"); + bindtextdomain("statusnet", $path); bind_textdomain_codeset("statusnet", "UTF-8"); textdomain("statusnet"); - setlocale(LC_CTYPE, 'C'); + if(!$locale_set) { common_log(LOG_INFO, 'Language requested:' . $language . ' - locale could not be set. Perhaps that system locale is not installed.', __FILE__); } -- cgit v1.2.3-54-g00ecf From c3b1f9e77d93f1a6b06617e2d15e7b761167f3fd Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 16 Oct 2009 11:23:50 -0700 Subject: Use short language names for locales for now; this seems to be most compatible with both native gettext (tested on Ubuntu 8.10) and php-gettext (tested on Mac OS X 10.6). --- lib/language.php | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'lib') diff --git a/lib/language.php b/lib/language.php index 666f97b52..7dcb808c9 100644 --- a/lib/language.php +++ b/lib/language.php @@ -101,36 +101,36 @@ function get_nice_language_list() */ function get_all_languages() { return array( - 'bg' => array('q' => 0.8, 'lang' => 'bg_BG', 'name' => 'Bulgarian', 'direction' => 'ltr'), - 'ca' => array('q' => 0.5, 'lang' => 'ca_ES', 'name' => 'Catalan', 'direction' => 'ltr'), - 'cs' => array('q' => 0.5, 'lang' => 'cs_CZ', 'name' => 'Czech', 'direction' => 'ltr'), - 'de' => array('q' => 0.8, 'lang' => 'de_DE', 'name' => 'German', 'direction' => 'ltr'), + 'bg' => array('q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'), + 'ca' => array('q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'), + 'cs' => array('q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'), + 'de' => array('q' => 0.8, 'lang' => 'de', 'name' => 'German', 'direction' => 'ltr'), 'el' => array('q' => 0.1, 'lang' => 'el', 'name' => 'Greek', 'direction' => 'ltr'), - 'en-us' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'), + 'en-us' => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'), 'en-gb' => array('q' => 1, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'), - 'en' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'), - 'es' => array('q' => 1, 'lang' => 'es_ES', 'name' => 'Spanish', 'direction' => 'ltr'), - 'fi' => array('q' => 1, 'lang' => 'fi_FI', 'name' => 'Finnish', 'direction' => 'ltr'), - 'fr-fr' => array('q' => 1, 'lang' => 'fr_FR', 'name' => 'French', 'direction' => 'ltr'), - 'ga' => array('q' => 0.5, 'lang' => 'ga_ES', 'name' => 'Galician', 'direction' => 'ltr'), - 'he' => array('q' => 0.5, 'lang' => 'he_IL', 'name' => 'Hebrew', 'direction' => 'rtl'), - 'it' => array('q' => 1, 'lang' => 'it_IT', 'name' => 'Italian', 'direction' => 'ltr'), - 'jp' => array('q' => 0.5, 'lang' => 'ja_JP', 'name' => 'Japanese', 'direction' => 'ltr'), - 'ko' => array('q' => 0.9, 'lang' => 'ko_KR', 'name' => 'Korean', 'direction' => 'ltr'), - 'mk' => array('q' => 0.5, 'lang' => 'mk_MK', 'name' => 'Macedonian', 'direction' => 'ltr'), - 'nb' => array('q' => 0.1, 'lang' => 'nb_NO', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'), - 'no' => array('q' => 0.1, 'lang' => 'nb_NO', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'), - 'nn' => array('q' => 1, 'lang' => 'nn_NO', 'name' => 'Norwegian (Nynorsk)', 'direction' => 'ltr'), - 'nl' => array('q' => 0.5, 'lang' => 'nl_NL', 'name' => 'Dutch', 'direction' => 'ltr'), - 'pl' => array('q' => 0.5, 'lang' => 'pl_PL', 'name' => 'Polish', 'direction' => 'ltr'), + 'en' => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'), + 'es' => array('q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'), + 'fi' => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'), + 'fr-fr' => array('q' => 1, 'lang' => 'fr', 'name' => 'French', 'direction' => 'ltr'), + 'ga' => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Galician', 'direction' => 'ltr'), + 'he' => array('q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'), + 'it' => array('q' => 1, 'lang' => 'it', 'name' => 'Italian', 'direction' => 'ltr'), + 'jp' => array('q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'), + 'ko' => array('q' => 0.9, 'lang' => 'ko', 'name' => 'Korean', 'direction' => 'ltr'), + 'mk' => array('q' => 0.5, 'lang' => 'mk', 'name' => 'Macedonian', 'direction' => 'ltr'), + 'nb' => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'), + 'no' => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'), + 'nn' => array('q' => 1, 'lang' => 'nn', 'name' => 'Norwegian (Nynorsk)', 'direction' => 'ltr'), + 'nl' => array('q' => 0.5, 'lang' => 'nl', 'name' => 'Dutch', 'direction' => 'ltr'), + 'pl' => array('q' => 0.5, 'lang' => 'pl', 'name' => 'Polish', 'direction' => 'ltr'), 'pt' => array('q' => 0.1, 'lang' => 'pt', 'name' => 'Portuguese', 'direction' => 'ltr'), 'pt-br' => array('q' => 0.9, 'lang' => 'pt_BR', 'name' => 'Portuguese Brazil', 'direction' => 'ltr'), - 'ru' => array('q' => 0.9, 'lang' => 'ru_RU', 'name' => 'Russian', 'direction' => 'ltr'), - 'sv' => array('q' => 0.8, 'lang' => 'sv_SE', 'name' => 'Swedish', 'direction' => 'ltr'), - 'te' => array('q' => 0.3, 'lang' => 'te_IN', 'name' => 'Telugu', 'direction' => 'ltr'), - 'tr' => array('q' => 0.5, 'lang' => 'tr_TR', 'name' => 'Turkish', 'direction' => 'ltr'), - 'uk' => array('q' => 1, 'lang' => 'uk_UA', 'name' => 'Ukrainian', 'direction' => 'ltr'), - 'vi' => array('q' => 0.8, 'lang' => 'vi_VN', 'name' => 'Vietnamese', 'direction' => 'ltr'), + 'ru' => array('q' => 0.9, 'lang' => 'ru', 'name' => 'Russian', 'direction' => 'ltr'), + 'sv' => array('q' => 0.8, 'lang' => 'sv', 'name' => 'Swedish', 'direction' => 'ltr'), + 'te' => array('q' => 0.3, 'lang' => 'te', 'name' => 'Telugu', 'direction' => 'ltr'), + 'tr' => array('q' => 0.5, 'lang' => 'tr', 'name' => 'Turkish', 'direction' => 'ltr'), + 'uk' => array('q' => 1, 'lang' => 'uk', 'name' => 'Ukrainian', 'direction' => 'ltr'), + 'vi' => array('q' => 0.8, 'lang' => 'vi', 'name' => 'Vietnamese', 'direction' => 'ltr'), 'zh-cn' => array('q' => 0.9, 'lang' => 'zh_CN', 'name' => 'Chinese (Simplified)', 'direction' => 'ltr'), 'zh-hant' => array('q' => 0.2, 'lang' => 'zh_TW', 'name' => 'Chinese (Taiwanese)', 'direction' => 'ltr'), ); -- cgit v1.2.3-54-g00ecf From b3c29800fbf7bd44f043e0c9742b207e15fb50f2 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 16 Oct 2009 11:49:27 -0700 Subject: Drop a debug info line that isn't really needed --- lib/util.php | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/util.php b/lib/util.php index 38147e998..d679d717f 100644 --- a/lib/util.php +++ b/lib/util.php @@ -64,7 +64,6 @@ function common_init_language() // So we don't have to make people install the gettext locales $path = common_config('site','locale_path'); - common_log(LOG_INFO, "binding text domain: $path"); bindtextdomain("statusnet", $path); bind_textdomain_codeset("statusnet", "UTF-8"); textdomain("statusnet"); -- cgit v1.2.3-54-g00ecf From 4855cb58daf374404bec9489769cc57431e347f4 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 12 Oct 2009 22:36:17 +0000 Subject: Workaround for Facebook data store API behavior regression, fixes saving of empty notice prefix text in facebook settings. Filed bug upstream at http://bugs.developers.facebook.com/show_bug.cgi?id=7110 Per documentation, saving a pref value of "" or "0" will delete the pref key: http://wiki.developers.facebook.com/index.php/Data.setUserPreference which used to do what we want... Now Facebook throws back an error "Parameter value is required" when we do this. Workaround appends a space to empty string or "0" at save time, then trims the string when we load it. The input string was already trimmed at pref save time, so this won't alter any user-visible behavior. Thanks to ^demon in #mediawiki for pointing out the behavior regression after testing the identi.ca Facebook app! --- actions/facebooksettings.php | 11 +++++++++-- lib/facebookutil.php | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/actions/facebooksettings.php b/actions/facebooksettings.php index 84bdde910..b2b1d6807 100644 --- a/actions/facebooksettings.php +++ b/actions/facebooksettings.php @@ -58,8 +58,15 @@ class FacebooksettingsAction extends FacebookAction $this->flink->set_flags($noticesync, $replysync, false, false); $result = $this->flink->update($original); + if ($prefix == '' || $prefix == '0') { + // Facebook bug: saving empty strings to prefs now fails + // http://bugs.developers.facebook.com/show_bug.cgi?id=7110 + $trimmed = $prefix . ' '; + } else { + $trimmed = substr($prefix, 0, 128); + } $this->facebook->api_client->data_setUserPreference(FACEBOOK_NOTICE_PREFIX, - substr($prefix, 0, 128)); + $trimmed); if ($result === false) { $this->showForm(_('There was a problem saving your sync preferences!')); @@ -101,7 +108,7 @@ class FacebooksettingsAction extends FacebookAction $this->elementStart('li'); - $prefix = $this->facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX); + $prefix = trim($this->facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX)); $this->input('prefix', _('Prefix'), ($prefix) ? $prefix : null, diff --git a/lib/facebookutil.php b/lib/facebookutil.php index ad61b6f0a..c29576b64 100644 --- a/lib/facebookutil.php +++ b/lib/facebookutil.php @@ -99,8 +99,8 @@ function facebookBroadcastNotice($notice) // XXX: Does this call count against our per user FB request limit? // If so we should consider storing verb elsewhere or not storing - $prefix = $facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX, - $fbuid); + $prefix = trim($facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX, + $fbuid)); $status = "$prefix $notice->content"; -- cgit v1.2.3-54-g00ecf From e48efd322279fd67168957635fa30cf4cb46f81b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 12 Oct 2009 22:36:17 +0000 Subject: Workaround for Facebook data store API behavior regression, fixes saving of empty notice prefix text in facebook settings. Filed bug upstream at http://bugs.developers.facebook.com/show_bug.cgi?id=7110 Per documentation, saving a pref value of "" or "0" will delete the pref key: http://wiki.developers.facebook.com/index.php/Data.setUserPreference which used to do what we want... Now Facebook throws back an error "Parameter value is required" when we do this. Workaround appends a space to empty string or "0" at save time, then trims the string when we load it. The input string was already trimmed at pref save time, so this won't alter any user-visible behavior. Thanks to ^demon in #mediawiki for pointing out the behavior regression after testing the identi.ca Facebook app! --- actions/facebooksettings.php | 11 +++++++++-- lib/facebookutil.php | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/actions/facebooksettings.php b/actions/facebooksettings.php index 84bdde910..b2b1d6807 100644 --- a/actions/facebooksettings.php +++ b/actions/facebooksettings.php @@ -58,8 +58,15 @@ class FacebooksettingsAction extends FacebookAction $this->flink->set_flags($noticesync, $replysync, false, false); $result = $this->flink->update($original); + if ($prefix == '' || $prefix == '0') { + // Facebook bug: saving empty strings to prefs now fails + // http://bugs.developers.facebook.com/show_bug.cgi?id=7110 + $trimmed = $prefix . ' '; + } else { + $trimmed = substr($prefix, 0, 128); + } $this->facebook->api_client->data_setUserPreference(FACEBOOK_NOTICE_PREFIX, - substr($prefix, 0, 128)); + $trimmed); if ($result === false) { $this->showForm(_('There was a problem saving your sync preferences!')); @@ -101,7 +108,7 @@ class FacebooksettingsAction extends FacebookAction $this->elementStart('li'); - $prefix = $this->facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX); + $prefix = trim($this->facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX)); $this->input('prefix', _('Prefix'), ($prefix) ? $prefix : null, diff --git a/lib/facebookutil.php b/lib/facebookutil.php index f5121609d..c991c5439 100644 --- a/lib/facebookutil.php +++ b/lib/facebookutil.php @@ -99,8 +99,8 @@ function facebookBroadcastNotice($notice) // XXX: Does this call count against our per user FB request limit? // If so we should consider storing verb elsewhere or not storing - $prefix = $facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX, - $fbuid); + $prefix = trim($facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX, + $fbuid)); $status = "$prefix $notice->content"; -- cgit v1.2.3-54-g00ecf From 109a54c4f08177cce82ef0f4a4ec8cd41ed2ecec Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sat, 17 Oct 2009 00:32:02 +0000 Subject: Changed config flag for importing friends' timeline and added some comments --- lib/default.php | 2 +- plugins/TwitterBridge/TwitterBridgePlugin.php | 79 ++++++++++++++++++++------- 2 files changed, 59 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/default.php b/lib/default.php index 9f3d4b1f9..68029c977 100644 --- a/lib/default.php +++ b/lib/default.php @@ -140,7 +140,7 @@ $default = array('enabled' => true), 'sms' => array('enabled' => true), - 'twitterbridge' => + 'twitterimport' => array('enabled' => false), 'integration' => array('source' => 'StatusNet', # source attribute for Twitter diff --git a/plugins/TwitterBridge/TwitterBridgePlugin.php b/plugins/TwitterBridge/TwitterBridgePlugin.php index 69bec0651..1a27c30cd 100644 --- a/plugins/TwitterBridge/TwitterBridgePlugin.php +++ b/plugins/TwitterBridge/TwitterBridgePlugin.php @@ -1,6 +1,6 @@ . * * @category Plugin - * @package Laconica - * @author Zach Copley + * @package StatusNet + * @author Zach Copley * @copyright 2009 Control Yourself, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://laconi.ca/ */ -if (!defined('LACONICA')) { +if (!defined('STATUSNET')) { exit(1); } @@ -35,8 +35,8 @@ if (!defined('LACONICA')) { * This class allows users to link their Twitter accounts * * @category Plugin - * @package Laconica - * @author Zach Copley + * @package StatusNet + * @author Zach Copley * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://laconi.ca/ * @link http://twitter.com/ @@ -58,17 +58,27 @@ class TwitterBridgePlugin extends Plugin * * Hook for RouterInitialized event. * + * @param Net_URL_Mapper &$m path-to-action mapper + * * @return boolean hook return */ function onRouterInitialized(&$m) { - $m->connect('twitter/authorization', array('action' => 'twitterauthorization')); + $m->connect('twitter/authorization', + array('action' => 'twitterauthorization')); $m->connect('settings/twitter', array('action' => 'twittersettings')); return true; } + /** + * Add the Twitter Settings page to the Connect Settings menu + * + * @param Action &$action The calling page + * + * @return boolean hook return + */ function onEndConnectSettingsNav(&$action) { $action_name = $action->trimmed('action'); @@ -81,38 +91,65 @@ class TwitterBridgePlugin extends Plugin return true; } + /** + * Automatically load the actions and libraries used by the Twitter bridge + * + * @param Class $cls the class + * + * @return boolean hook return + * + */ function onAutoload($cls) { - switch ($cls) - { - case 'TwittersettingsAction': - case 'TwitterauthorizationAction': - require_once(INSTALLDIR.'/plugins/TwitterBridge/' . strtolower(mb_substr($cls, 0, -6)) . '.php'); + switch ($cls) { + case 'TwittersettingsAction': + case 'TwitterauthorizationAction': + include_once INSTALLDIR.'/plugins/TwitterBridge/' . + strtolower(mb_substr($cls, 0, -6)) . '.php'; return false; - case 'TwitterOAuthClient': - require_once(INSTALLDIR.'/plugins/TwitterBridge/twitteroauthclient.php'); + case 'TwitterOAuthClient': + include_once INSTALLDIR.'/plugins/TwitterBridge/twitteroauthclient.php'; return false; - default: + default: return true; } } + /** + * Add a Twitter queue item for each notice + * + * @param Notice $notice the notice + * @param array $transports the list of transports (queues) + * + * @return boolean hook return + */ function onStartEnqueueNotice($notice, $transports) { array_push($transports, 'twitter'); return true; } + /** + * Add Twitter bridge daemons to the list of daemons to start + * + * @param array $daemons the list fo daemons to run + * + * @return boolean hook return + * + */ function onGetValidDaemons($daemons) { - array_push($daemons, INSTALLDIR . '/plugins/TwitterBridge/daemons/twitterqueuehandler.php'); - array_push($daemons, INSTALLDIR . '/plugins/TwitterBridge/daemons/synctwitterfriends.php'); - - if (common_config('twitterbridge', 'enabled')) { - array_push($daemons, INSTALLDIR . '/plugins/TwitterBridge/daemons/twitterstatusfetcher.php'); + array_push($daemons, INSTALLDIR . + '/plugins/TwitterBridge/daemons/twitterqueuehandler.php'); + array_push($daemons, INSTALLDIR . + '/plugins/TwitterBridge/daemons/synctwitterfriends.php'); + + if (common_config('twitterimport', 'enabled')) { + array_push($daemons, INSTALLDIR + . '/plugins/TwitterBridge/daemons/twitterstatusfetcher.php'); } return true; } -} \ No newline at end of file +} -- cgit v1.2.3-54-g00ecf From 9d0e37c4e847f312c39eaf15deb95ff3777c13a6 Mon Sep 17 00:00:00 2001 From: Eric Helgeson Date: Mon, 19 Oct 2009 13:11:55 -0400 Subject: Utilize NICKNAME_FMT constant when creating at replies --- lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.php b/lib/util.php index 9b299cb14..047faeef0 100644 --- a/lib/util.php +++ b/lib/util.php @@ -391,7 +391,7 @@ function common_render_content($text, $notice) { $r = common_render_text($text); $id = $notice->profile_id; - $r = preg_replace('/(^|\s+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r); + $r = preg_replace('/(^|\s+)@(['.NICKNAME_FMT.']{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r); $r = preg_replace('/^T ([A-Z0-9]{1,64}) /e', "'T '.common_at_link($id, '\\1').' '", $r); $r = preg_replace('/(^|\s+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r); $r = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r); -- cgit v1.2.3-54-g00ecf From 728a146ec04acc853c4fd32a44fef6adbee517c2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 19 Oct 2009 17:30:08 -0400 Subject: new codename for 0.8.2 --- README | 2 +- lib/common.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/README b/README index 756219981..c98090b4b 100644 --- a/README +++ b/README @@ -2,7 +2,7 @@ README ------ -StatusNet 0.8.1 ("Second Guessing") +StatusNet 0.8.2 ("Life and How to Live It") 26 Aug 2009 This is the README file for StatusNet (formerly Laconica), the Open diff --git a/lib/common.php b/lib/common.php index 0b4e03184..3de567cd9 100644 --- a/lib/common.php +++ b/lib/common.php @@ -22,7 +22,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } define('STATUSNET_VERSION', '0.8.2dev'); define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility -define('STATUSNET_CODENAME', 'Second Guessing'); +define('STATUSNET_CODENAME', 'Life and How to Live It'); define('AVATAR_PROFILE_SIZE', 96); define('AVATAR_STREAM_SIZE', 48); -- cgit v1.2.3-54-g00ecf From 66fca9e2a87f9b9c55174694c79f567c5c81518a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Mon, 31 Aug 2009 10:59:50 +1200 Subject: some typoes in comments that annoyed me, fixed now --- lib/twitteroauthclient.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/twitteroauthclient.php b/lib/twitteroauthclient.php index e37fa05f0..bad2b74ca 100644 --- a/lib/twitteroauthclient.php +++ b/lib/twitteroauthclient.php @@ -118,7 +118,7 @@ class TwitterOAuthClient extends OAuthClient } /** - * Calls Twitter's /stutuses/update API method + * Calls Twitter's /statuses/update API method * * @param string $status text of the status * @param int $in_reply_to_status_id optional id of the status it's @@ -137,7 +137,7 @@ class TwitterOAuthClient extends OAuthClient } /** - * Calls Twitter's /stutuses/friends_timeline API method + * Calls Twitter's /statuses/friends_timeline API method * * @param int $since_id show statuses after this id * @param int $max_id show statuses before this id @@ -167,7 +167,7 @@ class TwitterOAuthClient extends OAuthClient } /** - * Calls Twitter's /stutuses/friends API method + * Calls Twitter's /statuses/friends API method * * @param int $id id of the user whom you wish to see friends of * @param int $user_id numerical user id @@ -197,7 +197,7 @@ class TwitterOAuthClient extends OAuthClient } /** - * Calls Twitter's /stutuses/friends/ids API method + * Calls Twitter's /statuses/friends/ids API method * * @param int $id id of the user whom you wish to see friends of * @param int $user_id numerical user id -- cgit v1.2.3-54-g00ecf From 490dfc6f5a4480cda3fdee8af66ea4e856cdf0e8 Mon Sep 17 00:00:00 2001 From: Eric Helgeson Date: Mon, 19 Oct 2009 20:08:20 -0400 Subject: Better check if site,server is configured. --- lib/util.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/util.php b/lib/util.php index 047faeef0..0052090f6 100644 --- a/lib/util.php +++ b/lib/util.php @@ -760,12 +760,18 @@ function common_path($relative, $ssl=false) if (is_string(common_config('site', 'sslserver')) && mb_strlen(common_config('site', 'sslserver')) > 0) { $serverpart = common_config('site', 'sslserver'); - } else { + } else if (common_config('site', 'server')) { $serverpart = common_config('site', 'server'); + } else { + common_log(LOG_ERR, 'Site Sever not configured, unable to determine site name.'); } } else { $proto = 'http'; - $serverpart = common_config('site', 'server'); + if (common_config('site', 'server')) { + $serverpart = common_config('site', 'server'); + } else { + common_log(LOG_ERR, 'Site Sever not configured, unable to determine site name.'); + } } return $proto.'://'.$serverpart.'/'.$pathpart.$relative; -- cgit v1.2.3-54-g00ecf From 7539e26951ba9e1529d9a24a7a634861b7d080fe Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 20 Oct 2009 06:05:35 +0000 Subject: - Make Twitter bridge work with unqueuemanager - Add README --- lib/unqueuemanager.php | 5 -- plugins/TwitterBridge/README | 86 +++++++++++++++++++++++++++ plugins/TwitterBridge/TwitterBridgePlugin.php | 42 +++++++++++-- plugins/TwitterBridge/twittersettings.php | 2 +- 4 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 plugins/TwitterBridge/README (limited to 'lib') diff --git a/lib/unqueuemanager.php b/lib/unqueuemanager.php index 6cfe5bcbd..51261bcd7 100644 --- a/lib/unqueuemanager.php +++ b/lib/unqueuemanager.php @@ -48,11 +48,6 @@ class UnQueueManager jabber_public_notice($notice); } break; - case 'twitter': - if ($this->_isLocal($notice)) { - broadcast_twitter($notice); - } - break; case 'facebook': if ($this->_isLocal($notice)) { require_once INSTALLDIR . '/lib/facebookutil.php'; diff --git a/plugins/TwitterBridge/README b/plugins/TwitterBridge/README new file mode 100644 index 000000000..09352188e --- /dev/null +++ b/plugins/TwitterBridge/README @@ -0,0 +1,86 @@ +This Twitter "bridge" plugin allows you to integrate your StatusNet +instance with Twitter. Installing it will allow your users to: + + - automatically post notices to thier Twitter accounts + - automatically subscribe to other Twitter users who are also using + your StatusNet install, if possible (requires running a daemon) + - import their Twitter friends' tweets (requires running a daemon) + +Installation +------------ + +To enable the plugin, add the following to your config.php: + + require_once(INSTALLDIR . '/plugins/TwitterBridge/TwitterBridgePlugin.php'); + $tb = new TwitterBridgePlugin(); + +OAuth is used to to access protected resources on Twitter (as opposed to +HTTP Basic Auth)*. To use Twitter bridging you will need to register +your instance of StatusNet as an application on Twitter +(http://twitter.com/apps), and update the following variables in your +config.php with the consumer key and secret Twitter generates for you: + + $config['twitter']['consumer_key'] = 'YOURKEY'; + $config['twitter']['consumer_secret'] = 'YOURSECRET'; + +When registering your application with Twitter set the type to "Browser" +and your Callback URL to: + + http://example.org/mublog/twitter/authorization + +The default access type should be, "Read & Write". + +* Note: The plugin will still push notices to Twitter for users who + have previously setup the Twitter bridge using their Twitter name and + password under an older versions of StatusNet, but all new Twitter + bridge connections will use OAuth. + +Deamons +------- + +For friend syncing and importing notices running two additional daemon +scripts is necessary (synctwitterfriends.php and +twitterstatusfetcher.php). + +In the daemons subidrectory of the plugin are three scripts: + +* Twitter Friends Syncing (daemons/synctwitterfriends.php) + +Users may set a flag in their settings ("Subscribe to my Twitter friends +here" under the Twitter tab) to have StatusNet attempt to locate and +subscribe to "friends" (people they "follow") on Twitter who also have +accounts on your StatusNet system, and who have previously set up a link +for automatically posting notices to Twitter. + +The plugin will try to start this daemon when you run +scripts/startdaemons.sh. + +* Importing statuses from Twitter (daemons/twitterstatusfetcher.php) + +To allow your users to import their friends' Twitter statuses, you will +need to enable the bidirectional Twitter bridge in your config.php: + + $config['twitterimport']['enabled'] = true; + +The plugin will then start the TwitterStatusFetcher daemon along with the +other daemons when you run scripts/startdaemons.sh. + +Additionally, you will want to set the integration source variable, +which will keep notices posted to Twitter via StatusNet from looping +back. The integration source should be set to the name of your +application, exactly as you specified it on the settings page for your +StatusNet application on Twitter, e.g.: + + $config['integration']['source'] = 'YourApp'; + +* TwitterQueueHandler (daemons/twitterqueuehandler.php) + +This script sends queued notices to Twitter for user who have opted to +set up Twitter bridging. + +It's not strictly necessary to run this queue handler, and sites that +haven't enabled queuing are still able to push notices to Twitter, but +for larger sites and sites that wish to improve performance, this +script allows notices to be sent "offline" via a separate process. + +The plugin will start this script when you run scripts/startdaemons.sh. diff --git a/plugins/TwitterBridge/TwitterBridgePlugin.php b/plugins/TwitterBridge/TwitterBridgePlugin.php index 1a27c30cd..e69567fc7 100644 --- a/plugins/TwitterBridge/TwitterBridgePlugin.php +++ b/plugins/TwitterBridge/TwitterBridgePlugin.php @@ -29,6 +29,8 @@ if (!defined('STATUSNET')) { exit(1); } +require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php'; + /** * Plugin for sending and importing Twitter statuses * @@ -104,11 +106,11 @@ class TwitterBridgePlugin extends Plugin switch ($cls) { case 'TwittersettingsAction': case 'TwitterauthorizationAction': - include_once INSTALLDIR.'/plugins/TwitterBridge/' . + include_once INSTALLDIR . '/plugins/TwitterBridge/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; return false; case 'TwitterOAuthClient': - include_once INSTALLDIR.'/plugins/TwitterBridge/twitteroauthclient.php'; + include_once INSTALLDIR . '/plugins/TwitterBridge/twitteroauthclient.php'; return false; default: return true; @@ -118,17 +120,47 @@ class TwitterBridgePlugin extends Plugin /** * Add a Twitter queue item for each notice * - * @param Notice $notice the notice - * @param array $transports the list of transports (queues) + * @param Notice $notice the notice + * @param array &$transports the list of transports (queues) * * @return boolean hook return */ - function onStartEnqueueNotice($notice, $transports) + function onStartEnqueueNotice($notice, &$transports) { array_push($transports, 'twitter'); return true; } + /** + * broadcast the message when not using queuehandler + * + * @param Notice &$notice the notice + * @param array $queue destination queue + * + * @return boolean hook return + */ + function onUnqueueHandleNotice(&$notice, $queue) + { + if (($queue == 'twitter') && ($this->_isLocal($notice))) { + broadcast_twitter($notice); + return false; + } + return true; + } + + /** + * Determine whether the notice was locally created + * + * @param Notice $notice + * + * @return boolean locality + */ + function _isLocal($notice) + { + return ($notice->is_local == Notice::LOCAL_PUBLIC || + $notice->is_local == Notice::LOCAL_NONPUBLIC); + } + /** * Add Twitter bridge daemons to the list of daemons to start * diff --git a/plugins/TwitterBridge/twittersettings.php b/plugins/TwitterBridge/twittersettings.php index 2afa85ba4..ca22c9553 100644 --- a/plugins/TwitterBridge/twittersettings.php +++ b/plugins/TwitterBridge/twittersettings.php @@ -152,7 +152,7 @@ class TwittersettingsAction extends ConnectSettingsAction false); $this->elementEnd('li'); - if (common_config('twitterbridge','enabled')) { + if (common_config('twitterimport','enabled')) { $this->elementStart('li'); $this->checkbox('noticerecv', _('Import my Friends Timeline.'), -- cgit v1.2.3-54-g00ecf From 78e5a5980a21c04cfdacb993ca3c37bf65d21783 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 20 Oct 2009 16:32:30 -0700 Subject: Extract out Facebook app stuff into a plugin --- actions/facebookhome.php | 275 ------------ actions/facebookinvite.php | 145 ------- actions/facebooklogin.php | 101 ----- actions/facebookremove.php | 67 --- actions/facebooksettings.php | 157 ------- lib/facebookaction.php | 672 ------------------------------ lib/facebookutil.php | 260 ------------ lib/router.php | 8 - plugins/Facebook/FacebookPlugin.php | 151 +++++++ plugins/Facebook/README | 5 + plugins/Facebook/facebookaction.php | 650 +++++++++++++++++++++++++++++ plugins/Facebook/facebookhome.php | 277 ++++++++++++ plugins/Facebook/facebookinvite.php | 145 +++++++ plugins/Facebook/facebooklogin.php | 99 +++++ plugins/Facebook/facebookqueuehandler.php | 74 ++++ plugins/Facebook/facebookremove.php | 69 +++ plugins/Facebook/facebooksettings.php | 159 +++++++ plugins/Facebook/facebookutil.php | 260 ++++++++++++ scripts/facebookqueuehandler.php | 74 ---- scripts/getvaliddaemons.php | 1 - 20 files changed, 1889 insertions(+), 1760 deletions(-) delete mode 100644 actions/facebookhome.php delete mode 100644 actions/facebookinvite.php delete mode 100644 actions/facebooklogin.php delete mode 100644 actions/facebookremove.php delete mode 100644 actions/facebooksettings.php delete mode 100644 lib/facebookaction.php delete mode 100644 lib/facebookutil.php create mode 100644 plugins/Facebook/FacebookPlugin.php create mode 100644 plugins/Facebook/README create mode 100644 plugins/Facebook/facebookaction.php create mode 100644 plugins/Facebook/facebookhome.php create mode 100644 plugins/Facebook/facebookinvite.php create mode 100644 plugins/Facebook/facebooklogin.php create mode 100755 plugins/Facebook/facebookqueuehandler.php create mode 100644 plugins/Facebook/facebookremove.php create mode 100644 plugins/Facebook/facebooksettings.php create mode 100644 plugins/Facebook/facebookutil.php delete mode 100755 scripts/facebookqueuehandler.php (limited to 'lib') diff --git a/actions/facebookhome.php b/actions/facebookhome.php deleted file mode 100644 index 70f205205..000000000 --- a/actions/facebookhome.php +++ /dev/null @@ -1,275 +0,0 @@ -. - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } - -require_once INSTALLDIR.'/lib/facebookaction.php'; - -class FacebookhomeAction extends FacebookAction -{ - - var $page = null; - - function prepare($argarray) - { - parent::prepare($argarray); - - $this->page = $this->trimmed('page'); - - if (!$this->page) { - $this->page = 1; - } - - return true; - } - - function handle($args) - { - parent::handle($args); - - // If the user has opted not to initially allow the app to have - // Facebook status update permission, store that preference. Only - // promt the user the first time she uses the app - if ($this->arg('skip') || $args['fb_sig_request_method'] == 'GET') { - $this->facebook->api_client->data_setUserPreference( - FACEBOOK_PROMPTED_UPDATE_PREF, 'true'); - } - - if ($this->flink) { - - $this->user = $this->flink->getUser(); - - // If this is the first time the user has started the app - // prompt for Facebook status update permission - if (!$this->facebook->api_client->users_hasAppPermission('publish_stream')) { - - if ($this->facebook->api_client->data_getUserPreference( - FACEBOOK_PROMPTED_UPDATE_PREF) != 'true') { - $this->getUpdatePermission(); - return; - } - } - - // Make sure the user's profile box has the lastest notice - $notice = $this->user->getCurrentNotice(); - if ($notice) { - $this->updateProfileBox($notice); - } - - if ($this->arg('status_submit') == 'Send') { - $this->saveNewNotice(); - } - - // User is authenticated and has already been prompted once for - // Facebook status update permission? Then show the main page - // of the app - $this->showPage(); - - } else { - - // User hasn't authenticated yet, prompt for creds - $this->login(); - } - - } - - function login() - { - - $this->showStylesheets(); - - $nickname = common_canonical_nickname($this->trimmed('nickname')); - $password = $this->arg('password'); - - $msg = null; - - if ($nickname) { - - if (common_check_user($nickname, $password)) { - - $user = User::staticGet('nickname', $nickname); - - if (!$user) { - $this->showLoginForm(_("Server error - couldn't get user!")); - } - - $flink = DB_DataObject::factory('foreign_link'); - $flink->user_id = $user->id; - $flink->foreign_id = $this->fbuid; - $flink->service = FACEBOOK_SERVICE; - $flink->created = common_sql_now(); - $flink->set_flags(true, false, false, false); - - $flink_id = $flink->insert(); - - // XXX: Do some error handling here - - $this->setDefaults(); - - $this->getUpdatePermission(); - return; - - } else { - $msg = _('Incorrect username or password.'); - } - } - - $this->showLoginForm($msg); - $this->showFooter(); - - } - - function setDefaults() - { - $this->facebook->api_client->data_setUserPreference( - FACEBOOK_PROMPTED_UPDATE_PREF, 'false'); - } - - function showNoticeForm() - { - $post_action = "$this->app_uri/index.php"; - - $notice_form = new FacebookNoticeForm($this, $post_action, null, - $post_action, $this->user); - $notice_form->show(); - } - - function title() - { - if ($this->page > 1) { - return sprintf(_("%s and friends, page %d"), $this->user->nickname, $this->page); - } else { - return sprintf(_("%s and friends"), $this->user->nickname); - } - } - - function showContent() - { - $notice = $this->user->noticeInbox(($this->page-1) * NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); - - $nl = new NoticeList($notice, $this); - - $cnt = $nl->show(); - - $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, - $this->page, 'index.php', array('nickname' => $this->user->nickname)); - } - - function showNoticeList($notice) - { - - $nl = new NoticeList($notice, $this); - return $nl->show(); - } - - function getUpdatePermission() { - - $this->showStylesheets(); - - $this->elementStart('div', array('class' => 'facebook_guide')); - - $instructions = sprintf(_('If you would like the %s app to automatically update ' . - 'your Facebook status with your latest notice, you need ' . - 'to give it permission.'), $this->app_name); - - $this->elementStart('p'); - $this->element('span', array('id' => 'permissions_notice'), $instructions); - $this->elementEnd('p'); - - $this->elementStart('form', array('method' => 'post', - 'action' => "index.php", - 'id' => 'facebook-skip-permissions')); - - $this->elementStart('ul', array('id' => 'fb-permissions-list')); - $this->elementStart('li', array('id' => 'fb-permissions-item')); - - $next = urlencode("$this->app_uri/index.php"); - $api_key = common_config('facebook', 'apikey'); - - $auth_url = 'http://www.facebook.com/authorize.php?api_key=' . - $api_key . '&v=1.0&ext_perm=publish_stream&next=' . $next . - '&next_cancel=' . $next . '&submit=skip'; - - $this->elementStart('span', array('class' => 'facebook-button')); - $this->element('a', array('href' => $auth_url), - sprintf(_('Okay, do it!'), $this->app_name)); - $this->elementEnd('span'); - - $this->elementEnd('li'); - - $this->elementStart('li', array('id' => 'fb-permissions-item')); - $this->submit('skip', _('Skip')); - $this->elementEnd('li'); - $this->elementEnd('ul'); - - $this->elementEnd('form'); - $this->elementEnd('div'); - - } - - /** - * Generate pagination links - * - * @param boolean $have_before is there something before? - * @param boolean $have_after is there something after? - * @param integer $page current page - * @param string $action current action - * @param array $args rest of query arguments - * - * @return nothing - */ - function pagination($have_before, $have_after, $page, $action, $args=null) - { - - // Does a little before-after block for next/prev page - - // XXX: Fix so this uses common_local_url() if possible. - - if ($have_before || $have_after) { - $this->elementStart('div', array('class' => 'pagination')); - $this->elementStart('dl', null); - $this->element('dt', null, _('Pagination')); - $this->elementStart('dd', null); - $this->elementStart('ul', array('class' => 'nav')); - } - if ($have_before) { - $pargs = array('page' => $page-1); - $newargs = $args ? array_merge($args, $pargs) : $pargs; - $this->elementStart('li', array('class' => 'nav_prev')); - $this->element('a', array('href' => "$action?page=$newargs[page]", 'rel' => 'prev'), - _('After')); - $this->elementEnd('li'); - } - if ($have_after) { - $pargs = array('page' => $page+1); - $newargs = $args ? array_merge($args, $pargs) : $pargs; - $this->elementStart('li', array('class' => 'nav_next')); - $this->element('a', array('href' => "$action?page=$newargs[page]", 'rel' => 'next'), - _('Before')); - $this->elementEnd('li'); - } - if ($have_before || $have_after) { - $this->elementEnd('ul'); - $this->elementEnd('dd'); - $this->elementEnd('dl'); - $this->elementEnd('div'); - } - } - -} diff --git a/actions/facebookinvite.php b/actions/facebookinvite.php deleted file mode 100644 index 6dfc9d688..000000000 --- a/actions/facebookinvite.php +++ /dev/null @@ -1,145 +0,0 @@ -. - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once(INSTALLDIR.'/lib/facebookaction.php'); - -class FacebookinviteAction extends FacebookAction -{ - - function handle($args) - { - parent::handle($args); - $this->showForm(); - } - - /** - * Wrapper for showing a page - * - * Stores an error and shows the page - * - * @param string $error Error, if any - * - * @return void - */ - - function showForm($error=null) - { - $this->error = $error; - $this->showPage(); - } - - /** - * Show the page content - * - * Either shows the registration form or, if registration was successful, - * instructions for using the site. - * - * @return void - */ - - function showContent() - { - if ($this->arg('ids')) { - $this->showSuccessContent(); - } else { - $this->showFormContent(); - } - } - - function showSuccessContent() - { - - $this->element('h2', null, sprintf(_('Thanks for inviting your friends to use %s'), - common_config('site', 'name'))); - $this->element('p', null, _('Invitations have been sent to the following users:')); - - $friend_ids = $_POST['ids']; // XXX: Hmm... is this the best way to access the list? - - $this->elementStart('ul', array('id' => 'facebook-friends')); - - foreach ($friend_ids as $friend) { - $this->elementStart('li'); - $this->element('fb:profile-pic', array('uid' => $friend, 'size' => 'square')); - $this->element('fb:name', array('uid' => $friend, - 'capitalize' => 'true')); - $this->elementEnd('li'); - } - - $this->elementEnd("ul"); - - } - - function showFormContent() - { - $content = sprintf(_('You have been invited to %s'), common_config('site', 'name')) . - htmlentities(''); - - $this->elementStart('fb:request-form', array('action' => 'invite.php', - 'method' => 'post', - 'invite' => 'true', - 'type' => common_config('site', 'name'), - 'content' => $content)); - $this->hidden('invite', 'true'); - $actiontext = sprintf(_('Invite your friends to use %s'), common_config('site', 'name')); - - $multi_params = array('showborder' => 'false'); - $multi_params['actiontext'] = $actiontext; - $multi_params['bypass'] = 'cancel'; - - // Get a list of users who are already using the app for exclusion - $exclude_ids = $this->facebook->api_client->friends_getAppUsers(); - $exclude_ids_csv = null; - - // fbml needs these as a csv string, not an array - if ($exclude_ids) { - $exclude_ids_csv = implode(',', $exclude_ids); - $multi_params['exclude_ids'] = $exclude_ids_csv; - } - - $this->element('fb:multi-friend-selector', $multi_params); - $this->elementEnd('fb:request-form'); - - if ($exclude_ids) { - - $this->element('h2', null, sprintf(_('Friends already using %s:'), - common_config('site', 'name'))); - $this->elementStart('ul', array('id' => 'facebook-friends')); - - foreach ($exclude_ids as $friend) { - $this->elementStart('li'); - $this->element('fb:profile-pic', array('uid' => $friend, 'size' => 'square')); - $this->element('fb:name', array('uid' => $friend, - 'capitalize' => 'true')); - $this->elementEnd('li'); - } - - $this->elementEnd("ul"); - } - } - - function title() - { - return sprintf(_('Send invitations')); - } - -} diff --git a/actions/facebooklogin.php b/actions/facebooklogin.php deleted file mode 100644 index 8ac2477ab..000000000 --- a/actions/facebooklogin.php +++ /dev/null @@ -1,101 +0,0 @@ -. - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } - -require_once(INSTALLDIR.'/lib/facebookaction.php'); - -class FacebookinviteAction extends FacebookAction -{ - - function handle($args) - { - parent::handle($args); - - $this->error = $error; - - if ($this->flink) { - if (!$this->facebook->api_client->users_hasAppPermission('publish_stream') && - $this->facebook->api_client->data_getUserPreference( - FACEBOOK_PROMPTED_UPDATE_PREF) == 'true') { - - echo '

REDIRECT TO HOME

'; - } - } else { - $this->showPage(); - } - } - - - function showContent() - { - - // If the user has opted not to initially allow the app to have - // Facebook status update permission, store that preference. Only - // promt the user the first time she uses the app - if ($this->arg('skip')) { - $this->facebook->api_client->data_setUserPreference( - FACEBOOK_PROMPTED_UPDATE_PREF, 'true'); - } - - if ($this->flink) { - - $this->user = $this->flink->getUser(); - - // If this is the first time the user has started the app - // prompt for Facebook status update permission - if (!$this->facebook->api_client->users_hasAppPermission('publish_stream')) { - - if ($this->facebook->api_client->data_getUserPreference( - FACEBOOK_PROMPTED_UPDATE_PREF) != 'true') { - $this->getUpdatePermission(); - return; - } - } - - } else { - $this->showLoginForm(); - } - - } - - function showSuccessContent() - { - - - - } - - function showFormContent() - { - - - } - - function title() - { - return sprintf(_('Login')); - } - - function redirectHome() - { - - } - -} diff --git a/actions/facebookremove.php b/actions/facebookremove.php deleted file mode 100644 index ae231c0fb..000000000 --- a/actions/facebookremove.php +++ /dev/null @@ -1,67 +0,0 @@ -. - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } - -require_once INSTALLDIR.'/lib/facebookaction.php'; - -class FacebookremoveAction extends FacebookAction -{ - - function handle($args) - { - parent::handle($args); - - $secret = common_config('facebook', 'secret'); - - $sig = ''; - - ksort($_POST); - - foreach ($_POST as $key => $val) { - if (substr($key, 0, 7) == 'fb_sig_') { - $sig .= substr($key, 7) . '=' . $val; - } - } - - $sig .= $secret; - $verify = md5($sig); - - if ($verify == $this->arg('fb_sig')) { - - $flink = Foreign_link::getByForeignID($this->arg('fb_sig_user'), 2); - - common_debug("Removing foreign link to Facebook - local user ID: $flink->user_id, Facebook ID: $flink->foreign_id"); - - $result = $flink->delete(); - - if (!$result) { - common_log_db_error($flink, 'DELETE', __FILE__); - $this->serverError(_('Couldn\'t remove Facebook user.')); - return; - } - - } else { - # Someone bad tried to remove facebook link? - common_log(LOG_ERR, "Someone from $_SERVER[REMOTE_ADDR] " . - 'unsuccessfully tried to remove a foreign link to Facebook!'); - } - } - -} diff --git a/actions/facebooksettings.php b/actions/facebooksettings.php deleted file mode 100644 index b2b1d6807..000000000 --- a/actions/facebooksettings.php +++ /dev/null @@ -1,157 +0,0 @@ -. - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } - -require_once INSTALLDIR.'/lib/facebookaction.php'; - -class FacebooksettingsAction extends FacebookAction -{ - - function handle($args) - { - parent::handle($args); - $this->showPage(); - } - - /** - * Show the page content - * - * Either shows the registration form or, if registration was successful, - * instructions for using the site. - * - * @return void - */ - - function showContent() - { - if ($this->arg('save')) { - $this->saveSettings(); - } else { - $this->showForm(); - } - } - - function saveSettings() { - - $noticesync = $this->arg('noticesync'); - $replysync = $this->arg('replysync'); - $prefix = $this->trimmed('prefix'); - - $original = clone($this->flink); - $this->flink->set_flags($noticesync, $replysync, false, false); - $result = $this->flink->update($original); - - if ($prefix == '' || $prefix == '0') { - // Facebook bug: saving empty strings to prefs now fails - // http://bugs.developers.facebook.com/show_bug.cgi?id=7110 - $trimmed = $prefix . ' '; - } else { - $trimmed = substr($prefix, 0, 128); - } - $this->facebook->api_client->data_setUserPreference(FACEBOOK_NOTICE_PREFIX, - $trimmed); - - if ($result === false) { - $this->showForm(_('There was a problem saving your sync preferences!')); - } else { - $this->showForm(_('Sync preferences saved.'), true); - } - } - - function showForm($msg = null, $success = false) { - - if ($msg) { - if ($success) { - $this->element('fb:success', array('message' => $msg)); - } else { - $this->element('fb:error', array('message' => $msg)); - } - } - - if ($this->facebook->api_client->users_hasAppPermission('publish_stream')) { - - $this->elementStart('form', array('method' => 'post', - 'id' => 'facebook_settings')); - - $this->elementStart('ul', 'form_data'); - - $this->elementStart('li'); - - $this->checkbox('noticesync', _('Automatically update my Facebook status with my notices.'), - ($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND) : true); - - $this->elementEnd('li'); - - $this->elementStart('li'); - - $this->checkbox('replysync', _('Send "@" replies to Facebook.'), - ($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) : true); - - $this->elementEnd('li'); - - $this->elementStart('li'); - - $prefix = trim($this->facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX)); - - $this->input('prefix', _('Prefix'), - ($prefix) ? $prefix : null, - _('A string to prefix notices with.')); - - $this->elementEnd('li'); - - $this->elementStart('li'); - - $this->submit('save', _('Save')); - - $this->elementEnd('li'); - - $this->elementEnd('ul'); - - $this->elementEnd('form'); - - } else { - - $instructions = sprintf(_('If you would like %s to automatically update ' . - 'your Facebook status with your latest notice, you need ' . - 'to give it permission.'), $this->app_name); - - $this->elementStart('p'); - $this->element('span', array('id' => 'permissions_notice'), $instructions); - $this->elementEnd('p'); - - $this->elementStart('ul', array('id' => 'fb-permissions-list')); - $this->elementStart('li', array('id' => 'fb-permissions-item')); - $this->elementStart('fb:prompt-permission', array('perms' => 'publish_stream', - 'next_fbjs' => 'document.setLocation(\'' . "$this->app_uri/settings.php" . '\')')); - $this->element('span', array('class' => 'facebook-button'), - sprintf(_('Allow %s to update my Facebook status'), common_config('site', 'name'))); - $this->elementEnd('fb:prompt-permission'); - $this->elementEnd('li'); - $this->elementEnd('ul'); - } - - } - - function title() - { - return _('Sync preferences'); - } - -} diff --git a/lib/facebookaction.php b/lib/facebookaction.php deleted file mode 100644 index 3f3a8d3b0..000000000 --- a/lib/facebookaction.php +++ /dev/null @@ -1,672 +0,0 @@ -. - * - * @category Faceboook - * @package StatusNet - * @author Zach Copley - * @copyright 2008 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) -{ - exit(1); -} - -require_once INSTALLDIR.'/lib/facebookutil.php'; -require_once INSTALLDIR.'/lib/noticeform.php'; - -class FacebookAction extends Action -{ - - var $facebook = null; - var $fbuid = null; - var $flink = null; - var $action = null; - var $app_uri = null; - var $app_name = null; - - /** - * Constructor - * - * Just wraps the HTMLOutputter constructor. - * - * @param string $output URI to output to, default = stdout - * @param boolean $indent Whether to indent output, default true - * - * @see XMLOutputter::__construct - * @see HTMLOutputter::__construct - */ - function __construct($output='php://output', $indent=true, $facebook=null, $flink=null) - { - parent::__construct($output, $indent); - - $this->facebook = $facebook; - $this->flink = $flink; - - if ($this->flink) { - $this->fbuid = $flink->foreign_id; - $this->user = $flink->getUser(); - } - - $this->args = array(); - } - - function prepare($argarray) - { - parent::prepare($argarray); - - $this->facebook = getFacebook(); - $this->fbuid = $this->facebook->require_login(); - - $this->action = $this->trimmed('action'); - - $app_props = $this->facebook->api_client->Admin_getAppProperties( - array('canvas_name', 'application_name')); - - $this->app_uri = 'http://apps.facebook.com/' . $app_props['canvas_name']; - $this->app_name = $app_props['application_name']; - - $this->flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE); - - return true; - - } - - function showStylesheets() - { - $this->cssLink('css/display.css', 'base'); - $this->cssLink('css/display.css',null,'screen, projection, tv'); - $this->cssLink('css/facebookapp.css', 'base'); - } - - function showScripts() - { - $this->script('js/facebookapp.js'); - } - - /** - * Start an Facebook ready HTML document - * - * For Facebook we don't want to actually output any headers, - * DTD info, etc. Just Stylesheet and JavaScript links. - * - * If $type isn't specified, will attempt to do content negotiation. - * - * @param string $type MIME type to use; default is to do negotation. - * - * @return void - */ - - function startHTML($type=null) - { - $this->showStylesheets(); - $this->showScripts(); - - $this->elementStart('div', array('class' => 'facebook-page')); - } - - /** - * Ends a Facebook ready HTML document - * - * @return void - */ - function endHTML() - { - $this->elementEnd('div'); - $this->endXML(); - } - - /** - * Show notice form. - * - * MAY overload if no notice form needed... or direct message box???? - * - * @return nothing - */ - function showNoticeForm() - { - // don't do it for most of the Facebook pages - } - - function showBody() - { - $this->elementStart('div', array('id' => 'wrap')); - $this->showHeader(); - $this->showCore(); - $this->showFooter(); - $this->elementEnd('div'); - } - - function showAside() - { - } - - function showHead($error, $success) - { - - if ($error) { - $this->element("h1", null, $error); - } - - if ($success) { - $this->element("h1", null, $success); - } - - $this->elementStart('fb:if-section-not-added', array('section' => 'profile')); - $this->elementStart('span', array('id' => 'add_to_profile')); - $this->element('fb:add-section-button', array('section' => 'profile')); - $this->elementEnd('span'); - $this->elementEnd('fb:if-section-not-added'); - - } - - // Make this into a widget later - function showLocalNav() - { - $this->elementStart('ul', array('class' => 'nav')); - - $this->elementStart('li', array('class' => - ($this->action == 'facebookhome') ? 'current' : 'facebook_home')); - $this->element('a', - array('href' => 'index.php', 'title' => _('Home')), _('Home')); - $this->elementEnd('li'); - - if (common_config('invite', 'enabled')) { - $this->elementStart('li', - array('class' => - ($this->action == 'facebookinvite') ? 'current' : 'facebook_invite')); - $this->element('a', - array('href' => 'invite.php', 'title' => _('Invite')), _('Invite')); - $this->elementEnd('li'); - } - - $this->elementStart('li', - array('class' => - ($this->action == 'facebooksettings') ? 'current' : 'facebook_settings')); - $this->element('a', - array('href' => 'settings.php', - 'title' => _('Settings')), _('Settings')); - $this->elementEnd('li'); - - $this->elementEnd('ul'); - } - - /** - * Show header of the page. - * - * Calls template methods - * - * @return nothing - */ - function showHeader() - { - $this->elementStart('div', array('id' => 'header')); - $this->showLogo(); - $this->showNoticeForm(); - $this->elementEnd('div'); - } - - /** - * Show page, a template method. - * - * @return nothing - */ - function showPage($error = null, $success = null) - { - $this->startHTML(); - $this->showHead($error, $success); - $this->showBody(); - $this->endHTML(); - } - - function showInstructions() - { - - $this->elementStart('div', array('class' => 'facebook_guide')); - - $this->elementStart('dl', array('class' => 'system_notice')); - $this->element('dt', null, 'Page Notice'); - - $loginmsg_part1 = _('To use the %s Facebook Application you need to login ' . - 'with your username and password. Don\'t have a username yet? '); - $loginmsg_part2 = _(' a new account.'); - - $this->elementStart('dd'); - $this->elementStart('p'); - $this->text(sprintf($loginmsg_part1, common_config('site', 'name'))); - $this->element('a', - array('href' => common_local_url('register')), _('Register')); - $this->text($loginmsg_part2); - $this->elementEnd('p'); - $this->elementEnd('dd'); - - $this->elementEnd('dl'); - $this->elementEnd('div'); - } - - function showLoginForm($msg = null) - { - - $this->elementStart('div', array('id' => 'content')); - $this->element('h1', null, _('Login')); - - if ($msg) { - $this->element('fb:error', array('message' => $msg)); - } - - $this->showInstructions(); - - $this->elementStart('div', array('id' => 'content_inner')); - - $this->elementStart('form', array('method' => 'post', - 'class' => 'form_settings', - 'id' => 'login', - 'action' => 'index.php')); - - $this->elementStart('fieldset'); - - $this->elementStart('ul', array('class' => 'form_datas')); - $this->elementStart('li'); - $this->input('nickname', _('Nickname')); - $this->elementEnd('li'); - $this->elementStart('li'); - $this->password('password', _('Password')); - $this->elementEnd('li'); - $this->elementEnd('ul'); - - $this->submit('submit', _('Login')); - $this->elementEnd('fieldset'); - $this->elementEnd('form'); - - $this->elementStart('p'); - $this->element('a', array('href' => common_local_url('recoverpassword')), - _('Lost or forgotten password?')); - $this->elementEnd('p'); - - $this->elementEnd('div'); - $this->elementEnd('div'); - - } - - function updateProfileBox($notice) - { - - // Need to include inline CSS for styling the Profile box - - $app_props = $this->facebook->api_client->Admin_getAppProperties(array('icon_url')); - $icon_url = $app_props['icon_url']; - - $style = ''; - - $this->xw->openMemory(); - - $item = new FacebookProfileBoxNotice($notice, $this); - $item->show(); - - $fbml = "$style " . $this->xw->outputMemory(false) . ""; - $fbml .= "$style " . $this->xw->outputMemory(false) . ""; - - $fbml_main = "$style " . $this->xw->outputMemory(false) . ""; - - $this->facebook->api_client->profile_setFBML(null, $this->fbuid, $fbml, null, null, $fbml_main); - - $this->xw->openURI('php://output'); - } - - /** - * Generate pagination links - * - * @param boolean $have_before is there something before? - * @param boolean $have_after is there something after? - * @param integer $page current page - * @param string $action current action - * @param array $args rest of query arguments - * - * @return nothing - */ - function pagination($have_before, $have_after, $page, $action, $args=null) - { - // Does a little before-after block for next/prev page - if ($have_before || $have_after) { - $this->elementStart('div', array('class' => 'pagination')); - $this->elementStart('dl', null); - $this->element('dt', null, _('Pagination')); - $this->elementStart('dd', null); - $this->elementStart('ul', array('class' => 'nav')); - } - if ($have_before) { - $pargs = array('page' => $page-1); - $newargs = $args ? array_merge($args, $pargs) : $pargs; - $this->elementStart('li', array('class' => 'nav_prev')); - $this->element('a', array('href' => "$this->app_uri/$action?page=$newargs[page]", 'rel' => 'prev'), - _('After')); - $this->elementEnd('li'); - } - if ($have_after) { - $pargs = array('page' => $page+1); - $newargs = $args ? array_merge($args, $pargs) : $pargs; - $this->elementStart('li', array('class' => 'nav_next')); - $this->element('a', array('href' => "$this->app_uri/$action?page=$newargs[page]", 'rel' => 'next'), - _('Before')); - $this->elementEnd('li'); - } - if ($have_before || $have_after) { - $this->elementEnd('ul'); - $this->elementEnd('dd'); - $this->elementEnd('dl'); - $this->elementEnd('div'); - } - } - - function saveNewNotice() - { - - $user = $this->flink->getUser(); - - $content = $this->trimmed('status_textarea'); - - if (!$content) { - $this->showPage(_('No notice content!')); - return; - } else { - $content_shortened = common_shorten_links($content); - - if (Notice::contentTooLong($content_shortened)) { - $this->showPage(sprintf(_('That\'s too long. Max notice size is %d chars.'), - Notice::maxContent())); - return; - } - } - - $inter = new CommandInterpreter(); - - $cmd = $inter->handle_command($user, $content_shortened); - - if ($cmd) { - - // XXX fix this - - $cmd->execute(new WebChannel()); - return; - } - - $replyto = $this->trimmed('inreplyto'); - - try { - $notice = Notice::saveNew($user->id, $content, - 'web', 1, ($replyto == 'false') ? null : $replyto); - } catch (Exception $e) { - $this->showPage($e->getMessage()); - return; - } - - common_broadcast_notice($notice); - - // Also update the user's Facebook status - facebookBroadcastNotice($notice); - - } - -} - -class FacebookNoticeForm extends NoticeForm -{ - - var $post_action = null; - - /** - * Constructor - * - * @param HTMLOutputter $out output channel - * @param string $action action to return to, if any - * @param string $content content to pre-fill - */ - - function __construct($out=null, $action=null, $content=null, - $post_action=null, $user=null) - { - parent::__construct($out, $action, $content, $user); - $this->post_action = $post_action; - } - - /** - * Action of the form - * - * @return string URL of the action - */ - - function action() - { - return $this->post_action; - } - -} - -class FacebookNoticeList extends NoticeList -{ - - /** - * constructor - * - * @param Notice $notice stream of notices from DB_DataObject - */ - - function __construct($notice, $out=null) - { - parent::__construct($notice, $out); - } - - /** - * show the list of notices - * - * "Uses up" the stream by looping through it. So, probably can't - * be called twice on the same list. - * - * @return int count of notices listed. - */ - - function show() - { - $this->out->elementStart('div', array('id' =>'notices_primary')); - $this->out->element('h2', null, _('Notices')); - $this->out->elementStart('ul', array('class' => 'notices')); - - $cnt = 0; - - while ($this->notice->fetch() && $cnt <= NOTICES_PER_PAGE) { - $cnt++; - - if ($cnt > NOTICES_PER_PAGE) { - break; - } - - $item = $this->newListItem($this->notice); - $item->show(); - } - - $this->out->elementEnd('ul'); - $this->out->elementEnd('div'); - - return $cnt; - } - - /** - * returns a new list item for the current notice - * - * Overridden to return a Facebook specific list item. - * - * @param Notice $notice the current notice - * - * @return FacebookNoticeListItem a list item for displaying the notice - * formatted for display in the Facebook App. - */ - - function newListItem($notice) - { - return new FacebookNoticeListItem($notice, $this); - } - -} - -class FacebookNoticeListItem extends NoticeListItem -{ - - /** - * constructor - * - * Also initializes the profile attribute. - * - * @param Notice $notice The notice we'll display - */ - - function __construct($notice, $out=null) - { - parent::__construct($notice, $out); - } - - /** - * recipe function for displaying a single notice in the Facebook App. - * - * Overridden to strip out some of the controls that we don't - * want to be available. - * - * @return void - */ - - function show() - { - $this->showStart(); - $this->showNotice(); - $this->showNoticeInfo(); - - // XXX: Need to update to show attachements and controls - - $this->showEnd(); - } - -} - -class FacebookProfileBoxNotice extends FacebookNoticeListItem -{ - - /** - * constructor - * - * Also initializes the profile attribute. - * - * @param Notice $notice The notice we'll display - */ - - function __construct($notice, $out=null) - { - parent::__construct($notice, $out); - } - - /** - * Recipe function for displaying a single notice in the - * Facebook App profile notice box - * - * @return void - */ - - function show() - { - $this->showNotice(); - $this->showNoticeInfo(); - $this->showAppLink(); - } - - function showAppLink() - { - - $this->facebook = getFacebook(); - - $app_props = $this->facebook->api_client->Admin_getAppProperties( - array('canvas_name', 'application_name')); - - $this->app_uri = 'http://apps.facebook.com/' . $app_props['canvas_name']; - $this->app_name = $app_props['application_name']; - - $this->out->elementStart('a', array('id' => 'facebook_statusnet_app', - 'href' => $this->app_uri)); - $this->out->text($this->app_name); - $this->out->elementEnd('a'); - } - -} diff --git a/lib/facebookutil.php b/lib/facebookutil.php deleted file mode 100644 index c991c5439..000000000 --- a/lib/facebookutil.php +++ /dev/null @@ -1,260 +0,0 @@ -. - */ - -require_once INSTALLDIR.'/extlib/facebook/facebook.php'; -require_once INSTALLDIR.'/lib/facebookaction.php'; -require_once INSTALLDIR.'/lib/noticelist.php'; - -define("FACEBOOK_SERVICE", 2); // Facebook is foreign_service ID 2 -define("FACEBOOK_NOTICE_PREFIX", 1); -define("FACEBOOK_PROMPTED_UPDATE_PREF", 2); - -function getFacebook() -{ - static $facebook = null; - - $apikey = common_config('facebook', 'apikey'); - $secret = common_config('facebook', 'secret'); - - if ($facebook === null) { - $facebook = new Facebook($apikey, $secret); - } - - if (empty($facebook)) { - common_log(LOG_ERR, 'Could not make new Facebook client obj!', - __FILE__); - } - - return $facebook; -} - -function isFacebookBound($notice, $flink) { - - if (empty($flink)) { - return false; - } - - // Avoid a loop - - if ($notice->source == 'Facebook') { - common_log(LOG_INFO, "Skipping notice $notice->id because its " . - 'source is Facebook.'); - return false; - } - - // If the user does not want to broadcast to Facebook, move along - - if (!($flink->noticesync & FOREIGN_NOTICE_SEND == FOREIGN_NOTICE_SEND)) { - common_log(LOG_INFO, "Skipping notice $notice->id " . - 'because user has FOREIGN_NOTICE_SEND bit off.'); - return false; - } - - // If it's not a reply, or if the user WANTS to send @-replies, - // then, yeah, it can go to Facebook. - - if (!preg_match('/@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) || - ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY)) { - return true; - } - - return false; - -} - -function facebookBroadcastNotice($notice) -{ - $facebook = getFacebook(); - $flink = Foreign_link::getByUserID($notice->profile_id, FACEBOOK_SERVICE); - - if (isFacebookBound($notice, $flink)) { - - // Okay, we're good to go, update the FB status - - $status = null; - $fbuid = $flink->foreign_id; - $user = $flink->getUser(); - $attachments = $notice->attachments(); - - try { - - // Get the status 'verb' (prefix) the user has set - - // XXX: Does this call count against our per user FB request limit? - // If so we should consider storing verb elsewhere or not storing - - $prefix = trim($facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX, - $fbuid)); - - $status = "$prefix $notice->content"; - - $can_publish = $facebook->api_client->users_hasAppPermission('publish_stream', - $fbuid); - - $can_update = $facebook->api_client->users_hasAppPermission('status_update', - $fbuid); - if (!empty($attachments) && $can_publish == 1) { - $fbattachment = format_attachments($attachments); - $facebook->api_client->stream_publish($status, $fbattachment, - null, null, $fbuid); - common_log(LOG_INFO, - "Posted notice $notice->id w/attachment " . - "to Facebook user's stream (fbuid = $fbuid)."); - } elseif ($can_update == 1 || $can_publish == 1) { - $facebook->api_client->users_setStatus($status, $fbuid, false, true); - common_log(LOG_INFO, - "Posted notice $notice->id to Facebook " . - "as a status update (fbuid = $fbuid)."); - } else { - $msg = "Not sending notice $notice->id to Facebook " . - "because user $user->nickname hasn't given the " . - 'Facebook app \'status_update\' or \'publish_stream\' permission.'; - common_log(LOG_WARNING, $msg); - } - - // Finally, attempt to update the user's profile box - - if ($can_publish == 1 || $can_update == 1) { - updateProfileBox($facebook, $flink, $notice); - } - - } catch (FacebookRestClientException $e) { - - $code = $e->getCode(); - - common_log(LOG_WARNING, 'Facebook returned error code ' . - $code . ': ' . $e->getMessage()); - common_log(LOG_WARNING, - 'Unable to update Facebook status for ' . - "$user->nickname (user id: $user->id)!"); - - if ($code == 200 || $code == 250) { - - // 200 The application does not have permission to operate on the passed in uid parameter. - // 250 Updating status requires the extended permission status_update or publish_stream. - // see: http://wiki.developers.facebook.com/index.php/Users.setStatus#Example_Return_XML - - remove_facebook_app($flink); - - } else { - - // Try sending again later. - - return false; - } - - } - } - - return true; - -} - -function updateProfileBox($facebook, $flink, $notice) { - $fbaction = new FacebookAction($output = 'php://output', - $indent = true, $facebook, $flink); - $fbaction->updateProfileBox($notice); -} - -function format_attachments($attachments) -{ - $fbattachment = array(); - $fbattachment['media'] = array(); - - foreach($attachments as $attachment) - { - if($enclosure = $attachment->getEnclosure()){ - $fbmedia = get_fbmedia_for_attachment($enclosure); - }else{ - $fbmedia = get_fbmedia_for_attachment($attachment); - } - if($fbmedia){ - $fbattachment['media'][]=$fbmedia; - }else{ - $fbattachment['name'] = ($attachment->title ? - $attachment->title : $attachment->url); - $fbattachment['href'] = $attachment->url; - } - } - if(count($fbattachment['media'])>0){ - unset($fbattachment['name']); - unset($fbattachment['href']); - } - return $fbattachment; -} - -/** -* given an File objects, returns an associative array suitable for Facebook media -*/ -function get_fbmedia_for_attachment($attachment) -{ - $fbmedia = array(); - - if (strncmp($attachment->mimetype, 'image/', strlen('image/')) == 0) { - $fbmedia['type'] = 'image'; - $fbmedia['src'] = $attachment->url; - $fbmedia['href'] = $attachment->url; - } else if ($attachment->mimetype == 'audio/mpeg') { - $fbmedia['type'] = 'mp3'; - $fbmedia['src'] = $attachment->url; - }else if ($attachment->mimetype == 'application/x-shockwave-flash') { - $fbmedia['type'] = 'flash'; - - // http://wiki.developers.facebook.com/index.php/Attachment_%28Streams%29 - // says that imgsrc is required... but we have no value to put in it - // $fbmedia['imgsrc']=''; - - $fbmedia['swfsrc'] = $attachment->url; - }else{ - return false; - } - return $fbmedia; -} - -function remove_facebook_app($flink) -{ - - $user = $flink->getUser(); - - common_log(LOG_INFO, 'Removing Facebook App Foreign link for ' . - "user $user->nickname (user id: $user->id)."); - - $result = $flink->delete(); - - if (empty($result)) { - common_log(LOG_ERR, 'Could not remove Facebook App ' . - "Foreign_link for $user->nickname (user id: $user->id)!"); - common_log_db_error($flink, 'DELETE', __FILE__); - } - - // Notify the user that we are removing their FB app access - - $result = mail_facebook_app_removed($user); - - if (!$result) { - - $msg = 'Unable to send email to notify ' . - "$user->nickname (user id: $user->id) " . - 'that their Facebook app link was ' . - 'removed!'; - - common_log(LOG_WARNING, $msg); - } - -} diff --git a/lib/router.php b/lib/router.php index a5b6a9a30..4fb0834fd 100644 --- a/lib/router.php +++ b/lib/router.php @@ -86,14 +86,6 @@ class Router $m->connect('doc/:title', array('action' => 'doc')); - // facebook - - $m->connect('facebook', array('action' => 'facebookhome')); - $m->connect('facebook/index.php', array('action' => 'facebookhome')); - $m->connect('facebook/settings.php', array('action' => 'facebooksettings')); - $m->connect('facebook/invite.php', array('action' => 'facebookinvite')); - $m->connect('facebook/remove', array('action' => 'facebookremove')); - // main stuff is repetitive $main = array('login', 'logout', 'register', 'subscribe', diff --git a/plugins/Facebook/FacebookPlugin.php b/plugins/Facebook/FacebookPlugin.php new file mode 100644 index 000000000..127cf96e6 --- /dev/null +++ b/plugins/Facebook/FacebookPlugin.php @@ -0,0 +1,151 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Facebook plugin to add a StatusNet Facebook application + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class FacebookPlugin extends Plugin +{ + + /** + * Add Facebook app actions to the router table + * + * Hook for RouterInitialized event. + * + * @param Net_URL_Mapper &$m path-to-action mapper + * + * @return boolean hook return + */ + + function onRouterInitialized(&$m) + { + $m->connect('facebook', array('action' => 'facebookhome')); + $m->connect('facebook/index.php', array('action' => 'facebookhome')); + $m->connect('facebook/settings.php', array('action' => 'facebooksettings')); + $m->connect('facebook/invite.php', array('action' => 'facebookinvite')); + $m->connect('facebook/remove', array('action' => 'facebookremove')); + + return true; + } + + /** + * Automatically load the actions and libraries used by the Facebook app + * + * @param Class $cls the class + * + * @return boolean hook return + * + */ + function onAutoload($cls) + { + switch ($cls) { + case 'FacebookAction': + case 'FacebookhomeAction': + case 'FacebookinviteAction': + case 'FacebookremoveAction': + case 'FacebooksettingsAction': + include_once INSTALLDIR . '/plugins/Facebook/' . + strtolower(mb_substr($cls, 0, -6)) . '.php'; + return false; + default: + return true; + } + } + + /** + * Add a Facebook queue item for each notice + * + * @param Notice $notice the notice + * @param array &$transports the list of transports (queues) + * + * @return boolean hook return + */ + function onStartEnqueueNotice($notice, &$transports) + { + array_push($transports, 'facebook'); + return true; + } + + /** + * broadcast the message when not using queuehandler + * + * @param Notice &$notice the notice + * @param array $queue destination queue + * + * @return boolean hook return + */ + function onUnqueueHandleNotice(&$notice, $queue) + { + if (($queue == 'facebook') && ($this->_isLocal($notice))) { + facebookBroadcastNotice($notice); + return false; + } + return true; + } + + /** + * Determine whether the notice was locally created + * + * @param Notice $notice + * + * @return boolean locality + */ + function _isLocal($notice) + { + return ($notice->is_local == Notice::LOCAL_PUBLIC || + $notice->is_local == Notice::LOCAL_NONPUBLIC); + } + + /** + * Add Facebook queuehandler to the list of daemons to start + * + * @param array $daemons the list fo daemons to run + * + * @return boolean hook return + * + */ + function onGetValidDaemons($daemons) + { + array_push($daemons, INSTALLDIR . + '/plugins/Facebook/facebookqueuehandler.php'); + return true; + } + +} \ No newline at end of file diff --git a/plugins/Facebook/README b/plugins/Facebook/README new file mode 100644 index 000000000..a350c5b5b --- /dev/null +++ b/plugins/Facebook/README @@ -0,0 +1,5 @@ + + +TODO: + +- Integrate this and the FB Connect plugin \ No newline at end of file diff --git a/plugins/Facebook/facebookaction.php b/plugins/Facebook/facebookaction.php new file mode 100644 index 000000000..f5ad3d06b --- /dev/null +++ b/plugins/Facebook/facebookaction.php @@ -0,0 +1,650 @@ +. + * + * @category Faceboook + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/plugins/Facebook/facebookutil.php'; +require_once INSTALLDIR . '/lib/noticeform.php'; + +class FacebookAction extends Action +{ + + var $facebook = null; + var $fbuid = null; + var $flink = null; + var $action = null; + var $app_uri = null; + var $app_name = null; + + function __construct($output='php://output', $indent=true, $facebook=null, $flink=null) + { + parent::__construct($output, $indent); + + $this->facebook = $facebook; + $this->flink = $flink; + + if ($this->flink) { + $this->fbuid = $flink->foreign_id; + $this->user = $flink->getUser(); + } + + $this->args = array(); + } + + function prepare($argarray) + { + parent::prepare($argarray); + + $this->facebook = getFacebook(); + $this->fbuid = $this->facebook->require_login(); + + $this->action = $this->trimmed('action'); + + $app_props = $this->facebook->api_client->Admin_getAppProperties( + array('canvas_name', 'application_name')); + + $this->app_uri = 'http://apps.facebook.com/' . $app_props['canvas_name']; + $this->app_name = $app_props['application_name']; + + $this->flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE); + + return true; + + } + + function showStylesheets() + { + $this->cssLink('css/display.css', 'base'); + $this->cssLink('css/display.css',null,'screen, projection, tv'); + $this->cssLink('css/facebookapp.css', 'base'); + } + + function showScripts() + { + $this->script('js/facebookapp.js'); + } + + /** + * Start an Facebook ready HTML document + * + * For Facebook we don't want to actually output any headers, + * DTD info, etc. Just Stylesheet and JavaScript links. + * + * @param string $type MIME type to use; default is to do negotation. + * + * @return void + */ + + function startHTML($type=null) + { + $this->showStylesheets(); + $this->showScripts(); + + $this->elementStart('div', array('class' => 'facebook-page')); + } + + /** + * Ends a Facebook ready HTML document + * + * @return void + */ + function endHTML() + { + $this->elementEnd('div'); + $this->endXML(); + } + + /** + * Show notice form. + * + * @return nothing + */ + function showNoticeForm() + { + // don't do it for most of the Facebook pages + } + + function showBody() + { + $this->elementStart('div', array('id' => 'wrap')); + $this->showHeader(); + $this->showCore(); + $this->showFooter(); + $this->elementEnd('div'); + } + + function showHead($error, $success) + { + + if ($error) { + $this->element("h1", null, $error); + } + + if ($success) { + $this->element("h1", null, $success); + } + + $this->elementStart('fb:if-section-not-added', array('section' => 'profile')); + $this->elementStart('span', array('id' => 'add_to_profile')); + $this->element('fb:add-section-button', array('section' => 'profile')); + $this->elementEnd('span'); + $this->elementEnd('fb:if-section-not-added'); + + } + + // Make this into a widget later + function showLocalNav() + { + $this->elementStart('ul', array('class' => 'nav')); + + $this->elementStart('li', array('class' => + ($this->action == 'facebookhome') ? 'current' : 'facebook_home')); + $this->element('a', + array('href' => 'index.php', 'title' => _('Home')), _('Home')); + $this->elementEnd('li'); + + if (common_config('invite', 'enabled')) { + $this->elementStart('li', + array('class' => + ($this->action == 'facebookinvite') ? 'current' : 'facebook_invite')); + $this->element('a', + array('href' => 'invite.php', 'title' => _('Invite')), _('Invite')); + $this->elementEnd('li'); + } + + $this->elementStart('li', + array('class' => + ($this->action == 'facebooksettings') ? 'current' : 'facebook_settings')); + $this->element('a', + array('href' => 'settings.php', + 'title' => _('Settings')), _('Settings')); + $this->elementEnd('li'); + + $this->elementEnd('ul'); + } + + /** + * Show header of the page. + * + * @return nothing + */ + function showHeader() + { + $this->elementStart('div', array('id' => 'header')); + $this->showLogo(); + $this->showNoticeForm(); + $this->elementEnd('div'); + } + + /** + * Show page, a template method. + * + * @return nothing + */ + function showPage($error = null, $success = null) + { + $this->startHTML(); + $this->showHead($error, $success); + $this->showBody(); + $this->endHTML(); + } + + function showInstructions() + { + + $this->elementStart('div', array('class' => 'facebook_guide')); + + $this->elementStart('dl', array('class' => 'system_notice')); + $this->element('dt', null, 'Page Notice'); + + $loginmsg_part1 = _('To use the %s Facebook Application you need to login ' . + 'with your username and password. Don\'t have a username yet? '); + $loginmsg_part2 = _(' a new account.'); + + $this->elementStart('dd'); + $this->elementStart('p'); + $this->text(sprintf($loginmsg_part1, common_config('site', 'name'))); + $this->element('a', + array('href' => common_local_url('register')), _('Register')); + $this->text($loginmsg_part2); + $this->elementEnd('p'); + $this->elementEnd('dd'); + + $this->elementEnd('dl'); + $this->elementEnd('div'); + } + + function showLoginForm($msg = null) + { + + $this->elementStart('div', array('id' => 'content')); + $this->element('h1', null, _('Login')); + + if ($msg) { + $this->element('fb:error', array('message' => $msg)); + } + + $this->showInstructions(); + + $this->elementStart('div', array('id' => 'content_inner')); + + $this->elementStart('form', array('method' => 'post', + 'class' => 'form_settings', + 'id' => 'login', + 'action' => 'index.php')); + + $this->elementStart('fieldset'); + + $this->elementStart('ul', array('class' => 'form_datas')); + $this->elementStart('li'); + $this->input('nickname', _('Nickname')); + $this->elementEnd('li'); + $this->elementStart('li'); + $this->password('password', _('Password')); + $this->elementEnd('li'); + $this->elementEnd('ul'); + + $this->submit('submit', _('Login')); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + + $this->elementStart('p'); + $this->element('a', array('href' => common_local_url('recoverpassword')), + _('Lost or forgotten password?')); + $this->elementEnd('p'); + + $this->elementEnd('div'); + $this->elementEnd('div'); + + } + + function updateProfileBox($notice) + { + + // Need to include inline CSS for styling the Profile box + + $app_props = $this->facebook->api_client->Admin_getAppProperties(array('icon_url')); + $icon_url = $app_props['icon_url']; + + $style = ''; + + $this->xw->openMemory(); + + $item = new FacebookProfileBoxNotice($notice, $this); + $item->show(); + + $fbml = "$style " . $this->xw->outputMemory(false) . ""; + $fbml .= "$style " . $this->xw->outputMemory(false) . ""; + + $fbml_main = "$style " . $this->xw->outputMemory(false) . ""; + + $this->facebook->api_client->profile_setFBML(null, $this->fbuid, $fbml, null, null, $fbml_main); + + $this->xw->openURI('php://output'); + } + + /** + * Generate pagination links + * + * @param boolean $have_before is there something before? + * @param boolean $have_after is there something after? + * @param integer $page current page + * @param string $action current action + * @param array $args rest of query arguments + * + * @return nothing + */ + function pagination($have_before, $have_after, $page, $action, $args=null) + { + // Does a little before-after block for next/prev page + if ($have_before || $have_after) { + $this->elementStart('div', array('class' => 'pagination')); + $this->elementStart('dl', null); + $this->element('dt', null, _('Pagination')); + $this->elementStart('dd', null); + $this->elementStart('ul', array('class' => 'nav')); + } + if ($have_before) { + $pargs = array('page' => $page-1); + $newargs = $args ? array_merge($args, $pargs) : $pargs; + $this->elementStart('li', array('class' => 'nav_prev')); + $this->element('a', array('href' => "$this->app_uri/$action?page=$newargs[page]", 'rel' => 'prev'), + _('After')); + $this->elementEnd('li'); + } + if ($have_after) { + $pargs = array('page' => $page+1); + $newargs = $args ? array_merge($args, $pargs) : $pargs; + $this->elementStart('li', array('class' => 'nav_next')); + $this->element('a', array('href' => "$this->app_uri/$action?page=$newargs[page]", 'rel' => 'next'), + _('Before')); + $this->elementEnd('li'); + } + if ($have_before || $have_after) { + $this->elementEnd('ul'); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + $this->elementEnd('div'); + } + } + + function saveNewNotice() + { + + $user = $this->flink->getUser(); + + $content = $this->trimmed('status_textarea'); + + if (!$content) { + $this->showPage(_('No notice content!')); + return; + } else { + $content_shortened = common_shorten_links($content); + + if (Notice::contentTooLong($content_shortened)) { + $this->showPage(sprintf(_('That\'s too long. Max notice size is %d chars.'), + Notice::maxContent())); + return; + } + } + + $inter = new CommandInterpreter(); + + $cmd = $inter->handle_command($user, $content_shortened); + + if ($cmd) { + + // XXX fix this + + $cmd->execute(new WebChannel()); + return; + } + + $replyto = $this->trimmed('inreplyto'); + + try { + $notice = Notice::saveNew($user->id, $content, + 'web', 1, ($replyto == 'false') ? null : $replyto); + } catch (Exception $e) { + $this->showPage($e->getMessage()); + return; + } + + common_broadcast_notice($notice); + + // Also update the user's Facebook status + facebookBroadcastNotice($notice); + + } + +} + +class FacebookNoticeForm extends NoticeForm +{ + + var $post_action = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param string $action action to return to, if any + * @param string $content content to pre-fill + */ + + function __construct($out=null, $action=null, $content=null, + $post_action=null, $user=null) + { + parent::__construct($out, $action, $content, $user); + $this->post_action = $post_action; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return $this->post_action; + } + +} + +class FacebookNoticeList extends NoticeList +{ + + /** + * constructor + * + * @param Notice $notice stream of notices from DB_DataObject + */ + + function __construct($notice, $out=null) + { + parent::__construct($notice, $out); + } + + /** + * show the list of notices + * + * "Uses up" the stream by looping through it. So, probably can't + * be called twice on the same list. + * + * @return int count of notices listed. + */ + + function show() + { + $this->out->elementStart('div', array('id' =>'notices_primary')); + $this->out->element('h2', null, _('Notices')); + $this->out->elementStart('ul', array('class' => 'notices')); + + $cnt = 0; + + while ($this->notice->fetch() && $cnt <= NOTICES_PER_PAGE) { + $cnt++; + + if ($cnt > NOTICES_PER_PAGE) { + break; + } + + $item = $this->newListItem($this->notice); + $item->show(); + } + + $this->out->elementEnd('ul'); + $this->out->elementEnd('div'); + + return $cnt; + } + + /** + * returns a new list item for the current notice + * + * Overridden to return a Facebook specific list item. + * + * @param Notice $notice the current notice + * + * @return FacebookNoticeListItem a list item for displaying the notice + * formatted for display in the Facebook App. + */ + + function newListItem($notice) + { + return new FacebookNoticeListItem($notice, $this); + } + +} + +class FacebookNoticeListItem extends NoticeListItem +{ + + /** + * constructor + * + * Also initializes the profile attribute. + * + * @param Notice $notice The notice we'll display + */ + + function __construct($notice, $out=null) + { + parent::__construct($notice, $out); + } + + /** + * recipe function for displaying a single notice in the Facebook App. + * + * Overridden to strip out some of the controls that we don't + * want to be available. + * + * @return void + */ + + function show() + { + $this->showStart(); + $this->showNotice(); + $this->showNoticeInfo(); + + // XXX: Need to update to show attachements and controls + + $this->showEnd(); + } + +} + +class FacebookProfileBoxNotice extends FacebookNoticeListItem +{ + + /** + * constructor + * + * Also initializes the profile attribute. + * + * @param Notice $notice The notice we'll display + */ + + function __construct($notice, $out=null) + { + parent::__construct($notice, $out); + } + + /** + * Recipe function for displaying a single notice in the + * Facebook App profile notice box + * + * @return void + */ + + function show() + { + $this->showNotice(); + $this->showNoticeInfo(); + $this->showAppLink(); + } + + function showAppLink() + { + + $this->facebook = getFacebook(); + + $app_props = $this->facebook->api_client->Admin_getAppProperties( + array('canvas_name', 'application_name')); + + $this->app_uri = 'http://apps.facebook.com/' . $app_props['canvas_name']; + $this->app_name = $app_props['application_name']; + + $this->out->elementStart('a', array('id' => 'facebook_statusnet_app', + 'href' => $this->app_uri)); + $this->out->text($this->app_name); + $this->out->elementEnd('a'); + } + +} diff --git a/plugins/Facebook/facebookhome.php b/plugins/Facebook/facebookhome.php new file mode 100644 index 000000000..91c0cc6b8 --- /dev/null +++ b/plugins/Facebook/facebookhome.php @@ -0,0 +1,277 @@ +. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/plugins/Facebook/facebookaction.php'; + +class FacebookhomeAction extends FacebookAction +{ + + var $page = null; + + function prepare($argarray) + { + parent::prepare($argarray); + + $this->page = $this->trimmed('page'); + + if (!$this->page) { + $this->page = 1; + } + + return true; + } + + function handle($args) + { + parent::handle($args); + + // If the user has opted not to initially allow the app to have + // Facebook status update permission, store that preference. Only + // promt the user the first time she uses the app + if ($this->arg('skip') || $args['fb_sig_request_method'] == 'GET') { + $this->facebook->api_client->data_setUserPreference( + FACEBOOK_PROMPTED_UPDATE_PREF, 'true'); + } + + if ($this->flink) { + + $this->user = $this->flink->getUser(); + + // If this is the first time the user has started the app + // prompt for Facebook status update permission + if (!$this->facebook->api_client->users_hasAppPermission('publish_stream')) { + + if ($this->facebook->api_client->data_getUserPreference( + FACEBOOK_PROMPTED_UPDATE_PREF) != 'true') { + $this->getUpdatePermission(); + return; + } + } + + // Make sure the user's profile box has the lastest notice + $notice = $this->user->getCurrentNotice(); + if ($notice) { + $this->updateProfileBox($notice); + } + + if ($this->arg('status_submit') == 'Send') { + $this->saveNewNotice(); + } + + // User is authenticated and has already been prompted once for + // Facebook status update permission? Then show the main page + // of the app + $this->showPage(); + + } else { + + // User hasn't authenticated yet, prompt for creds + $this->login(); + } + + } + + function login() + { + + $this->showStylesheets(); + + $nickname = common_canonical_nickname($this->trimmed('nickname')); + $password = $this->arg('password'); + + $msg = null; + + if ($nickname) { + + if (common_check_user($nickname, $password)) { + + $user = User::staticGet('nickname', $nickname); + + if (!$user) { + $this->showLoginForm(_("Server error - couldn't get user!")); + } + + $flink = DB_DataObject::factory('foreign_link'); + $flink->user_id = $user->id; + $flink->foreign_id = $this->fbuid; + $flink->service = FACEBOOK_SERVICE; + $flink->created = common_sql_now(); + $flink->set_flags(true, false, false, false); + + $flink_id = $flink->insert(); + + // XXX: Do some error handling here + + $this->setDefaults(); + + $this->getUpdatePermission(); + return; + + } else { + $msg = _('Incorrect username or password.'); + } + } + + $this->showLoginForm($msg); + $this->showFooter(); + + } + + function setDefaults() + { + $this->facebook->api_client->data_setUserPreference( + FACEBOOK_PROMPTED_UPDATE_PREF, 'false'); + } + + function showNoticeForm() + { + $post_action = "$this->app_uri/index.php"; + + $notice_form = new FacebookNoticeForm($this, $post_action, null, + $post_action, $this->user); + $notice_form->show(); + } + + function title() + { + if ($this->page > 1) { + return sprintf(_("%s and friends, page %d"), $this->user->nickname, $this->page); + } else { + return sprintf(_("%s and friends"), $this->user->nickname); + } + } + + function showContent() + { + $notice = $this->user->noticeInbox(($this->page-1) * NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); + + $nl = new NoticeList($notice, $this); + + $cnt = $nl->show(); + + $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, + $this->page, 'index.php', array('nickname' => $this->user->nickname)); + } + + function showNoticeList($notice) + { + + $nl = new NoticeList($notice, $this); + return $nl->show(); + } + + function getUpdatePermission() { + + $this->showStylesheets(); + + $this->elementStart('div', array('class' => 'facebook_guide')); + + $instructions = sprintf(_('If you would like the %s app to automatically update ' . + 'your Facebook status with your latest notice, you need ' . + 'to give it permission.'), $this->app_name); + + $this->elementStart('p'); + $this->element('span', array('id' => 'permissions_notice'), $instructions); + $this->elementEnd('p'); + + $this->elementStart('form', array('method' => 'post', + 'action' => "index.php", + 'id' => 'facebook-skip-permissions')); + + $this->elementStart('ul', array('id' => 'fb-permissions-list')); + $this->elementStart('li', array('id' => 'fb-permissions-item')); + + $next = urlencode("$this->app_uri/index.php"); + $api_key = common_config('facebook', 'apikey'); + + $auth_url = 'http://www.facebook.com/authorize.php?api_key=' . + $api_key . '&v=1.0&ext_perm=publish_stream&next=' . $next . + '&next_cancel=' . $next . '&submit=skip'; + + $this->elementStart('span', array('class' => 'facebook-button')); + $this->element('a', array('href' => $auth_url), + sprintf(_('Okay, do it!'), $this->app_name)); + $this->elementEnd('span'); + + $this->elementEnd('li'); + + $this->elementStart('li', array('id' => 'fb-permissions-item')); + $this->submit('skip', _('Skip')); + $this->elementEnd('li'); + $this->elementEnd('ul'); + + $this->elementEnd('form'); + $this->elementEnd('div'); + + } + + /** + * Generate pagination links + * + * @param boolean $have_before is there something before? + * @param boolean $have_after is there something after? + * @param integer $page current page + * @param string $action current action + * @param array $args rest of query arguments + * + * @return nothing + */ + function pagination($have_before, $have_after, $page, $action, $args=null) + { + + // Does a little before-after block for next/prev page + + // XXX: Fix so this uses common_local_url() if possible. + + if ($have_before || $have_after) { + $this->elementStart('div', array('class' => 'pagination')); + $this->elementStart('dl', null); + $this->element('dt', null, _('Pagination')); + $this->elementStart('dd', null); + $this->elementStart('ul', array('class' => 'nav')); + } + if ($have_before) { + $pargs = array('page' => $page-1); + $newargs = $args ? array_merge($args, $pargs) : $pargs; + $this->elementStart('li', array('class' => 'nav_prev')); + $this->element('a', array('href' => "$action?page=$newargs[page]", 'rel' => 'prev'), + _('After')); + $this->elementEnd('li'); + } + if ($have_after) { + $pargs = array('page' => $page+1); + $newargs = $args ? array_merge($args, $pargs) : $pargs; + $this->elementStart('li', array('class' => 'nav_next')); + $this->element('a', array('href' => "$action?page=$newargs[page]", 'rel' => 'next'), + _('Before')); + $this->elementEnd('li'); + } + if ($have_before || $have_after) { + $this->elementEnd('ul'); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + $this->elementEnd('div'); + } + } + +} diff --git a/plugins/Facebook/facebookinvite.php b/plugins/Facebook/facebookinvite.php new file mode 100644 index 000000000..ecda1717c --- /dev/null +++ b/plugins/Facebook/facebookinvite.php @@ -0,0 +1,145 @@ +. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/plugins/Facebook/facebookaction.php'; + +class FacebookinviteAction extends FacebookAction +{ + + function handle($args) + { + parent::handle($args); + $this->showForm(); + } + + /** + * Wrapper for showing a page + * + * Stores an error and shows the page + * + * @param string $error Error, if any + * + * @return void + */ + + function showForm($error=null) + { + $this->error = $error; + $this->showPage(); + } + + /** + * Show the page content + * + * Either shows the registration form or, if registration was successful, + * instructions for using the site. + * + * @return void + */ + + function showContent() + { + if ($this->arg('ids')) { + $this->showSuccessContent(); + } else { + $this->showFormContent(); + } + } + + function showSuccessContent() + { + + $this->element('h2', null, sprintf(_('Thanks for inviting your friends to use %s'), + common_config('site', 'name'))); + $this->element('p', null, _('Invitations have been sent to the following users:')); + + $friend_ids = $_POST['ids']; // XXX: Hmm... is this the best way to access the list? + + $this->elementStart('ul', array('id' => 'facebook-friends')); + + foreach ($friend_ids as $friend) { + $this->elementStart('li'); + $this->element('fb:profile-pic', array('uid' => $friend, 'size' => 'square')); + $this->element('fb:name', array('uid' => $friend, + 'capitalize' => 'true')); + $this->elementEnd('li'); + } + + $this->elementEnd("ul"); + + } + + function showFormContent() + { + $content = sprintf(_('You have been invited to %s'), common_config('site', 'name')) . + htmlentities(''); + + $this->elementStart('fb:request-form', array('action' => 'invite.php', + 'method' => 'post', + 'invite' => 'true', + 'type' => common_config('site', 'name'), + 'content' => $content)); + $this->hidden('invite', 'true'); + $actiontext = sprintf(_('Invite your friends to use %s'), common_config('site', 'name')); + + $multi_params = array('showborder' => 'false'); + $multi_params['actiontext'] = $actiontext; + $multi_params['bypass'] = 'cancel'; + + // Get a list of users who are already using the app for exclusion + $exclude_ids = $this->facebook->api_client->friends_getAppUsers(); + $exclude_ids_csv = null; + + // fbml needs these as a csv string, not an array + if ($exclude_ids) { + $exclude_ids_csv = implode(',', $exclude_ids); + $multi_params['exclude_ids'] = $exclude_ids_csv; + } + + $this->element('fb:multi-friend-selector', $multi_params); + $this->elementEnd('fb:request-form'); + + if ($exclude_ids) { + + $this->element('h2', null, sprintf(_('Friends already using %s:'), + common_config('site', 'name'))); + $this->elementStart('ul', array('id' => 'facebook-friends')); + + foreach ($exclude_ids as $friend) { + $this->elementStart('li'); + $this->element('fb:profile-pic', array('uid' => $friend, 'size' => 'square')); + $this->element('fb:name', array('uid' => $friend, + 'capitalize' => 'true')); + $this->elementEnd('li'); + } + + $this->elementEnd("ul"); + } + } + + function title() + { + return sprintf(_('Send invitations')); + } + +} diff --git a/plugins/Facebook/facebooklogin.php b/plugins/Facebook/facebooklogin.php new file mode 100644 index 000000000..f77aecca3 --- /dev/null +++ b/plugins/Facebook/facebooklogin.php @@ -0,0 +1,99 @@ +. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/plugins/Facebook/facebookaction.php'; + +class FacebookinviteAction extends FacebookAction +{ + + function handle($args) + { + parent::handle($args); + + $this->error = $error; + + if ($this->flink) { + if (!$this->facebook->api_client->users_hasAppPermission('publish_stream') && + $this->facebook->api_client->data_getUserPreference( + FACEBOOK_PROMPTED_UPDATE_PREF) == 'true') { + + echo '

REDIRECT TO HOME

'; + } + } else { + $this->showPage(); + } + } + + function showContent() + { + + // If the user has opted not to initially allow the app to have + // Facebook status update permission, store that preference. Only + // promt the user the first time she uses the app + if ($this->arg('skip')) { + $this->facebook->api_client->data_setUserPreference( + FACEBOOK_PROMPTED_UPDATE_PREF, 'true'); + } + + if ($this->flink) { + + $this->user = $this->flink->getUser(); + + // If this is the first time the user has started the app + // prompt for Facebook status update permission + if (!$this->facebook->api_client->users_hasAppPermission('publish_stream')) { + + if ($this->facebook->api_client->data_getUserPreference( + FACEBOOK_PROMPTED_UPDATE_PREF) != 'true') { + $this->getUpdatePermission(); + return; + } + } + + } else { + $this->showLoginForm(); + } + + } + + function showSuccessContent() + { + + } + + function showFormContent() + { + + } + + function title() + { + return sprintf(_('Login')); + } + + function redirectHome() + { + + } + +} diff --git a/plugins/Facebook/facebookqueuehandler.php b/plugins/Facebook/facebookqueuehandler.php new file mode 100755 index 000000000..30de59efb --- /dev/null +++ b/plugins/Facebook/facebookqueuehandler.php @@ -0,0 +1,74 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'i::'; +$longoptions = array('id::'); + +$helptext = <<log(LOG_INFO, "INITIALIZE"); + return true; + } + + function handle_notice($notice) + { + return facebookBroadcastNotice($notice); + } + + function finish() + { + } + +} + +if (have_option('i')) { + $id = get_option_value('i'); +} else if (have_option('--id')) { + $id = get_option_value('--id'); +} else if (count($args) > 0) { + $id = $args[0]; +} else { + $id = null; +} + +$handler = new FacebookQueueHandler($id); + +$handler->runOnce(); diff --git a/plugins/Facebook/facebookremove.php b/plugins/Facebook/facebookremove.php new file mode 100644 index 000000000..8531a8e6e --- /dev/null +++ b/plugins/Facebook/facebookremove.php @@ -0,0 +1,69 @@ +. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/plugins/Facebook/facebookaction.php'; + +class FacebookremoveAction extends FacebookAction +{ + + function handle($args) + { + parent::handle($args); + + $secret = common_config('facebook', 'secret'); + + $sig = ''; + + ksort($_POST); + + foreach ($_POST as $key => $val) { + if (substr($key, 0, 7) == 'fb_sig_') { + $sig .= substr($key, 7) . '=' . $val; + } + } + + $sig .= $secret; + $verify = md5($sig); + + if ($verify == $this->arg('fb_sig')) { + + $flink = Foreign_link::getByForeignID($this->arg('fb_sig_user'), 2); + + common_debug("Removing foreign link to Facebook - local user ID: $flink->user_id, Facebook ID: $flink->foreign_id"); + + $result = $flink->delete(); + + if (!$result) { + common_log_db_error($flink, 'DELETE', __FILE__); + $this->serverError(_('Couldn\'t remove Facebook user.')); + return; + } + + } else { + # Someone bad tried to remove facebook link? + common_log(LOG_ERR, "Someone from $_SERVER[REMOTE_ADDR] " . + 'unsuccessfully tried to remove a foreign link to Facebook!'); + } + } + +} diff --git a/plugins/Facebook/facebooksettings.php b/plugins/Facebook/facebooksettings.php new file mode 100644 index 000000000..4bfdfc0ef --- /dev/null +++ b/plugins/Facebook/facebooksettings.php @@ -0,0 +1,159 @@ +. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/facebookaction.php'; + +class FacebooksettingsAction extends FacebookAction +{ + + function handle($args) + { + parent::handle($args); + $this->showPage(); + } + + /** + * Show the page content + * + * Either shows the registration form or, if registration was successful, + * instructions for using the site. + * + * @return void + */ + + function showContent() + { + if ($this->arg('save')) { + $this->saveSettings(); + } else { + $this->showForm(); + } + } + + function saveSettings() { + + $noticesync = $this->arg('noticesync'); + $replysync = $this->arg('replysync'); + $prefix = $this->trimmed('prefix'); + + $original = clone($this->flink); + $this->flink->set_flags($noticesync, $replysync, false, false); + $result = $this->flink->update($original); + + if ($prefix == '' || $prefix == '0') { + // Facebook bug: saving empty strings to prefs now fails + // http://bugs.developers.facebook.com/show_bug.cgi?id=7110 + $trimmed = $prefix . ' '; + } else { + $trimmed = substr($prefix, 0, 128); + } + $this->facebook->api_client->data_setUserPreference(FACEBOOK_NOTICE_PREFIX, + $trimmed); + + if ($result === false) { + $this->showForm(_('There was a problem saving your sync preferences!')); + } else { + $this->showForm(_('Sync preferences saved.'), true); + } + } + + function showForm($msg = null, $success = false) { + + if ($msg) { + if ($success) { + $this->element('fb:success', array('message' => $msg)); + } else { + $this->element('fb:error', array('message' => $msg)); + } + } + + if ($this->facebook->api_client->users_hasAppPermission('publish_stream')) { + + $this->elementStart('form', array('method' => 'post', + 'id' => 'facebook_settings')); + + $this->elementStart('ul', 'form_data'); + + $this->elementStart('li'); + + $this->checkbox('noticesync', _('Automatically update my Facebook status with my notices.'), + ($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND) : true); + + $this->elementEnd('li'); + + $this->elementStart('li'); + + $this->checkbox('replysync', _('Send "@" replies to Facebook.'), + ($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) : true); + + $this->elementEnd('li'); + + $this->elementStart('li'); + + $prefix = trim($this->facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX)); + + $this->input('prefix', _('Prefix'), + ($prefix) ? $prefix : null, + _('A string to prefix notices with.')); + + $this->elementEnd('li'); + + $this->elementStart('li'); + + $this->submit('save', _('Save')); + + $this->elementEnd('li'); + + $this->elementEnd('ul'); + + $this->elementEnd('form'); + + } else { + + $instructions = sprintf(_('If you would like %s to automatically update ' . + 'your Facebook status with your latest notice, you need ' . + 'to give it permission.'), $this->app_name); + + $this->elementStart('p'); + $this->element('span', array('id' => 'permissions_notice'), $instructions); + $this->elementEnd('p'); + + $this->elementStart('ul', array('id' => 'fb-permissions-list')); + $this->elementStart('li', array('id' => 'fb-permissions-item')); + $this->elementStart('fb:prompt-permission', array('perms' => 'publish_stream', + 'next_fbjs' => 'document.setLocation(\'' . "$this->app_uri/settings.php" . '\')')); + $this->element('span', array('class' => 'facebook-button'), + sprintf(_('Allow %s to update my Facebook status'), common_config('site', 'name'))); + $this->elementEnd('fb:prompt-permission'); + $this->elementEnd('li'); + $this->elementEnd('ul'); + } + + } + + function title() + { + return _('Sync preferences'); + } + +} diff --git a/plugins/Facebook/facebookutil.php b/plugins/Facebook/facebookutil.php new file mode 100644 index 000000000..9817837f7 --- /dev/null +++ b/plugins/Facebook/facebookutil.php @@ -0,0 +1,260 @@ +. + */ + +require_once INSTALLDIR . '/extlib/facebook/facebook.php'; +require_once INSTALLDIR . '/plugins/Facebook/facebookaction.php'; +require_once INSTALLDIR . '/lib/noticelist.php'; + +define("FACEBOOK_SERVICE", 2); // Facebook is foreign_service ID 2 +define("FACEBOOK_NOTICE_PREFIX", 1); +define("FACEBOOK_PROMPTED_UPDATE_PREF", 2); + +function getFacebook() +{ + static $facebook = null; + + $apikey = common_config('facebook', 'apikey'); + $secret = common_config('facebook', 'secret'); + + if ($facebook === null) { + $facebook = new Facebook($apikey, $secret); + } + + if (empty($facebook)) { + common_log(LOG_ERR, 'Could not make new Facebook client obj!', + __FILE__); + } + + return $facebook; +} + +function isFacebookBound($notice, $flink) { + + if (empty($flink)) { + return false; + } + + // Avoid a loop + + if ($notice->source == 'Facebook') { + common_log(LOG_INFO, "Skipping notice $notice->id because its " . + 'source is Facebook.'); + return false; + } + + // If the user does not want to broadcast to Facebook, move along + + if (!($flink->noticesync & FOREIGN_NOTICE_SEND == FOREIGN_NOTICE_SEND)) { + common_log(LOG_INFO, "Skipping notice $notice->id " . + 'because user has FOREIGN_NOTICE_SEND bit off.'); + return false; + } + + // If it's not a reply, or if the user WANTS to send @-replies, + // then, yeah, it can go to Facebook. + + if (!preg_match('/@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) || + ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY)) { + return true; + } + + return false; + +} + +function facebookBroadcastNotice($notice) +{ + $facebook = getFacebook(); + $flink = Foreign_link::getByUserID($notice->profile_id, FACEBOOK_SERVICE); + + if (isFacebookBound($notice, $flink)) { + + // Okay, we're good to go, update the FB status + + $status = null; + $fbuid = $flink->foreign_id; + $user = $flink->getUser(); + $attachments = $notice->attachments(); + + try { + + // Get the status 'verb' (prefix) the user has set + + // XXX: Does this call count against our per user FB request limit? + // If so we should consider storing verb elsewhere or not storing + + $prefix = trim($facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX, + $fbuid)); + + $status = "$prefix $notice->content"; + + $can_publish = $facebook->api_client->users_hasAppPermission('publish_stream', + $fbuid); + + $can_update = $facebook->api_client->users_hasAppPermission('status_update', + $fbuid); + if (!empty($attachments) && $can_publish == 1) { + $fbattachment = format_attachments($attachments); + $facebook->api_client->stream_publish($status, $fbattachment, + null, null, $fbuid); + common_log(LOG_INFO, + "Posted notice $notice->id w/attachment " . + "to Facebook user's stream (fbuid = $fbuid)."); + } elseif ($can_update == 1 || $can_publish == 1) { + $facebook->api_client->users_setStatus($status, $fbuid, false, true); + common_log(LOG_INFO, + "Posted notice $notice->id to Facebook " . + "as a status update (fbuid = $fbuid)."); + } else { + $msg = "Not sending notice $notice->id to Facebook " . + "because user $user->nickname hasn't given the " . + 'Facebook app \'status_update\' or \'publish_stream\' permission.'; + common_log(LOG_WARNING, $msg); + } + + // Finally, attempt to update the user's profile box + + if ($can_publish == 1 || $can_update == 1) { + updateProfileBox($facebook, $flink, $notice); + } + + } catch (FacebookRestClientException $e) { + + $code = $e->getCode(); + + common_log(LOG_WARNING, 'Facebook returned error code ' . + $code . ': ' . $e->getMessage()); + common_log(LOG_WARNING, + 'Unable to update Facebook status for ' . + "$user->nickname (user id: $user->id)!"); + + if ($code == 200 || $code == 250) { + + // 200 The application does not have permission to operate on the passed in uid parameter. + // 250 Updating status requires the extended permission status_update or publish_stream. + // see: http://wiki.developers.facebook.com/index.php/Users.setStatus#Example_Return_XML + + remove_facebook_app($flink); + + } else { + + // Try sending again later. + + return false; + } + + } + } + + return true; + +} + +function updateProfileBox($facebook, $flink, $notice) { + $fbaction = new FacebookAction($output = 'php://output', + $indent = true, $facebook, $flink); + $fbaction->updateProfileBox($notice); +} + +function format_attachments($attachments) +{ + $fbattachment = array(); + $fbattachment['media'] = array(); + + foreach($attachments as $attachment) + { + if($enclosure = $attachment->getEnclosure()){ + $fbmedia = get_fbmedia_for_attachment($enclosure); + }else{ + $fbmedia = get_fbmedia_for_attachment($attachment); + } + if($fbmedia){ + $fbattachment['media'][]=$fbmedia; + }else{ + $fbattachment['name'] = ($attachment->title ? + $attachment->title : $attachment->url); + $fbattachment['href'] = $attachment->url; + } + } + if(count($fbattachment['media'])>0){ + unset($fbattachment['name']); + unset($fbattachment['href']); + } + return $fbattachment; +} + +/** +* given an File objects, returns an associative array suitable for Facebook media +*/ +function get_fbmedia_for_attachment($attachment) +{ + $fbmedia = array(); + + if (strncmp($attachment->mimetype, 'image/', strlen('image/')) == 0) { + $fbmedia['type'] = 'image'; + $fbmedia['src'] = $attachment->url; + $fbmedia['href'] = $attachment->url; + } else if ($attachment->mimetype == 'audio/mpeg') { + $fbmedia['type'] = 'mp3'; + $fbmedia['src'] = $attachment->url; + }else if ($attachment->mimetype == 'application/x-shockwave-flash') { + $fbmedia['type'] = 'flash'; + + // http://wiki.developers.facebook.com/index.php/Attachment_%28Streams%29 + // says that imgsrc is required... but we have no value to put in it + // $fbmedia['imgsrc']=''; + + $fbmedia['swfsrc'] = $attachment->url; + }else{ + return false; + } + return $fbmedia; +} + +function remove_facebook_app($flink) +{ + + $user = $flink->getUser(); + + common_log(LOG_INFO, 'Removing Facebook App Foreign link for ' . + "user $user->nickname (user id: $user->id)."); + + $result = $flink->delete(); + + if (empty($result)) { + common_log(LOG_ERR, 'Could not remove Facebook App ' . + "Foreign_link for $user->nickname (user id: $user->id)!"); + common_log_db_error($flink, 'DELETE', __FILE__); + } + + // Notify the user that we are removing their FB app access + + $result = mail_facebook_app_removed($user); + + if (!$result) { + + $msg = 'Unable to send email to notify ' . + "$user->nickname (user id: $user->id) " . + 'that their Facebook app link was ' . + 'removed!'; + + common_log(LOG_WARNING, $msg); + } + +} diff --git a/scripts/facebookqueuehandler.php b/scripts/facebookqueuehandler.php deleted file mode 100755 index e13ac4e85..000000000 --- a/scripts/facebookqueuehandler.php +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env php -. - */ - -define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); - -$shortoptions = 'i::'; -$longoptions = array('id::'); - -$helptext = <<log(LOG_INFO, "INITIALIZE"); - return true; - } - - function handle_notice($notice) - { - return facebookBroadcastNotice($notice); - } - - function finish() - { - } - -} - -if (have_option('i')) { - $id = get_option_value('i'); -} else if (have_option('--id')) { - $id = get_option_value('--id'); -} else if (count($args) > 0) { - $id = $args[0]; -} else { - $id = null; -} - -$handler = new FacebookQueueHandler($id); - -$handler->runOnce(); diff --git a/scripts/getvaliddaemons.php b/scripts/getvaliddaemons.php index 7caea1bb7..99ad41b37 100755 --- a/scripts/getvaliddaemons.php +++ b/scripts/getvaliddaemons.php @@ -39,7 +39,6 @@ $daemons = array(); $daemons[] = INSTALLDIR.'/scripts/pluginqueuehandler.php'; $daemons[] = INSTALLDIR.'/scripts/ombqueuehandler.php'; -$daemons[] = INSTALLDIR.'/scripts/facebookqueuehandler.php'; $daemons[] = INSTALLDIR.'/scripts/pingqueuehandler.php'; if(common_config('xmpp','enabled')) { -- cgit v1.2.3-54-g00ecf From 6199d31acc8d4d52ffcb64ba3949c401b6b927de Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 21 Oct 2009 01:18:36 +0000 Subject: Remove facebook broadcast from unqueuemanager. That's now added by an event. --- lib/unqueuemanager.php | 6 ------ 1 file changed, 6 deletions(-) (limited to 'lib') diff --git a/lib/unqueuemanager.php b/lib/unqueuemanager.php index 51261bcd7..cef9a6588 100644 --- a/lib/unqueuemanager.php +++ b/lib/unqueuemanager.php @@ -48,12 +48,6 @@ class UnQueueManager jabber_public_notice($notice); } break; - case 'facebook': - if ($this->_isLocal($notice)) { - require_once INSTALLDIR . '/lib/facebookutil.php'; - return facebookBroadcastNotice($notice); - } - break; case 'ping': if ($this->_isLocal($notice)) { require_once INSTALLDIR . '/lib/ping.php'; -- cgit v1.2.3-54-g00ecf From 0df85711d6b2b02f8857e9d1b51fda4183be8bf3 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 21 Oct 2009 22:41:23 -0400 Subject: reformat default.php --- lib/default.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/default.php b/lib/default.php index 9f3d4b1f9..9ab8305ed 100644 --- a/lib/default.php +++ b/lib/default.php @@ -145,16 +145,16 @@ $default = 'integration' => array('source' => 'StatusNet', # source attribute for Twitter 'taguri' => $_server.',2009'), # base for tag URIs - 'twitter' => - array('enabled' => true, - 'consumer_key' => null, - 'consumer_secret' => null), + 'twitter' => + array('enabled' => true, + 'consumer_key' => null, + 'consumer_secret' => null), 'memcached' => array('enabled' => false, 'server' => 'localhost', 'base' => null, 'port' => 11211), - 'ping' => + 'ping' => array('notify' => array()), 'inboxes' => array('enabled' => true), # ignored after 0.9.x @@ -200,12 +200,12 @@ $default = 'video/mp4', 'video/quicktime', 'video/mpeg'), - 'file_quota' => 5000000, - 'user_quota' => 50000000, - 'monthly_quota' => 15000000, - 'uploads' => true, - 'filecommand' => '/usr/bin/file', - ), + 'file_quota' => 5000000, + 'user_quota' => 50000000, + 'monthly_quota' => 15000000, + 'uploads' => true, + 'filecommand' => '/usr/bin/file', + ), 'group' => array('maxaliases' => 3, 'desclimit' => null), -- cgit v1.2.3-54-g00ecf From b6b245dc0be75a3358fee560a40a54e280e4f2a7 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 15 Sep 2009 20:27:11 -0400 Subject: start of a location class --- lib/location.php | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 lib/location.php (limited to 'lib') diff --git a/lib/location.php b/lib/location.php new file mode 100644 index 000000000..cbd03f33a --- /dev/null +++ b/lib/location.php @@ -0,0 +1,77 @@ +. + * + * @category Location + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * class for locations + * + * @category Location + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class Location +{ + public $lat; + public $lon; + public $location_id; + public $location_ns; + + var $names; + + static function fromName($name, $language=null) + { + if (is_null($language)) { + $language = common_language(); + } + } + + static function fromId($location_id, $location_ns = null) + { + if (is_null($location_ns)) { + $location_ns = common_config('location', 'namespace'); + } + } + + function getName($language=null) + { + if (is_null($language)) { + $language = common_language(); + } + + if (array_key_exists($this->names, $language)) { + return $this->names[$language]; + } + } +} -- cgit v1.2.3-54-g00ecf From 7b227fd1c06ff6f3ba078b73348dd02419d44e38 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 16 Sep 2009 11:46:10 -0400 Subject: start getting locations from remote services --- lib/default.php | 2 ++ lib/location.php | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/default.php b/lib/default.php index 30e43eefb..c896d5408 100644 --- a/lib/default.php +++ b/lib/default.php @@ -229,4 +229,6 @@ $default = array('contentlimit' => null), 'http' => array('client' => 'curl'), // XXX: should this be the default? + 'location' => + array('namespace' => 1), // 1 = geonames, 2 = Yahoo Where on Earth ); diff --git a/lib/location.php b/lib/location.php index cbd03f33a..f4ce7f67a 100644 --- a/lib/location.php +++ b/lib/location.php @@ -50,11 +50,63 @@ class Location var $names; - static function fromName($name, $language=null) + const geonames = 1; + const whereOnEarth = 2; + + static function fromName($name, $language=null, $location_ns=null) { if (is_null($language)) { $language = common_language(); } + if (is_null($location_ns)) { + $location_ns = common_config('location', 'namespace'); + } + + $location = null; + + if (Event::handle('LocationFromName', array($name, $language, $location_ns, &$location))) { + + switch ($location_ns) { + case Location::geonames: + return Location::fromGeonamesName($name, $language); + break; + case Location::whereOnEarth: + return Location::fromWhereOnEarthName($name, $language); + break; + } + } + + return $location; + } + + static function fromGeonamesName($name, $language) + { + $location = null; + $client = HTTPClient::start(); + + // XXX: break down a name by commas, narrow by each + + $str = http_build_query(array('maxRows' => 1, + 'q' => $name, + 'lang' => $language, + 'type' => 'json')); + + $result = $client->get('http://ws.geonames.org/search?'.$str); + + if ($result->code == "200") { + $rj = json_decode($result->body); + if (count($rj['geonames']) > 0) { + $n = $rj['geonames'][0]; + $location = new Location(); + $location->lat = $n->lat; + $location->lon = $n->lon; + $location->name = $n->name; + $location->location_id = $n->geonameId; + $location->location_ns = Location:geonames; + } + } + + return $location; } static function fromId($location_id, $location_ns = null) -- cgit v1.2.3-54-g00ecf From f58daa873befbaee5a998e69622c046c8a978dee Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Thu, 8 Oct 2009 10:00:31 +0800 Subject: Added getfile action --- lib/router.php | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/router.php b/lib/router.php index 5529e60ac..7455d9cf8 100644 --- a/lib/router.php +++ b/lib/router.php @@ -171,6 +171,10 @@ class Router array('action' => 'attachment_thumbnail'), array('attachment' => '[0-9]+')); + $m->connect('getfile/:filename', + array('action' => 'getfile'), + array('filename' => '[A-Za-z0-9._-]+')); + $m->connect('notice/new', array('action' => 'newnotice')); $m->connect('notice/new?replyto=:replyto', array('action' => 'newnotice'), -- cgit v1.2.3-54-g00ecf From c6d1039901fdfc1f07ecc3c5c2b5c162504dfda2 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 22 Oct 2009 09:59:27 -0700 Subject: Add some doc comments to QueueHandler --- lib/queuehandler.php | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) (limited to 'lib') diff --git a/lib/queuehandler.php b/lib/queuehandler.php index 8c65a97c6..cd43b1e09 100644 --- a/lib/queuehandler.php +++ b/lib/queuehandler.php @@ -27,6 +27,22 @@ define('CLAIM_TIMEOUT', 1200); define('QUEUE_HANDLER_MISS_IDLE', 10); define('QUEUE_HANDLER_HIT_IDLE', 0); +/** + * Base class for queue handlers. + * + * As extensions of the Daemon class, each queue handler has the ability + * to launch itself in the background, at which point it'll pass control + * to the configured QueueManager class to poll for updates. + * + * Subclasses must override at least the following methods: + * - transport + * - start + * - finish + * - handle_notice + * + * Some subclasses will also want to override the idle handler: + * - idle + */ class QueueHandler extends Daemon { @@ -39,6 +55,14 @@ class QueueHandler extends Daemon } } + /** + * How many seconds a polling-based queue manager should wait between + * checks for new items to handle. + * + * Defaults to 60 seconds; override to speed up or slow down. + * + * @return int timeout in seconds + */ function timeout() { return 60; @@ -54,24 +78,69 @@ class QueueHandler extends Daemon return strtolower($this->class_name().'.'.$this->get_id()); } + /** + * Return transport keyword which identifies items this queue handler + * services; must be defined for all subclasses. + * + * Must be 8 characters or less to fit in the queue_item database. + * ex "email", "jabber", "sms", "irc", ... + * + * @return string + */ function transport() { return null; } + /** + * Initialization, run when the queue handler starts. + * If this function indicates failure, the handler run will be aborted. + * + * @fixme run() will abort if this doesn't return true, + * but some subclasses don't bother. + * @return boolean true on success, false on failure + */ function start() { } + /** + * Cleanup, run when the queue handler ends. + * If this function indicates failure, a warning will be logged. + * + * @fixme run() will throw warnings if this doesn't return true, + * but many subclasses don't bother. + * @return boolean true on success, false on failure + */ function finish() { } + /** + * Here's the meat of your queue handler -- you're handed a Notice + * object, which you may do as you will with. + * + * If this function indicates failure, a warning will be logged + * and the item is placed back in the queue to be re-run. + * + * @param Notice $notice + * @return boolean true on success, false on failure + */ function handle_notice($notice) { return true; } + /** + * Setup and start of run loop for this queue handler as a daemon. + * Most of the heavy lifting is passed on to the QueueManager's service() + * method, which passes control back to our handle_notice() method for + * each notice that comes in on the queue. + * + * Most of the time this won't need to be overridden in a subclass. + * + * @return boolean true on success, false on failure + */ function run() { if (!$this->start()) { @@ -100,6 +169,14 @@ class QueueHandler extends Daemon return true; } + /** + * Called by QueueHandler after each handled item or empty polling cycle. + * This is a good time to e.g. service your XMPP connection. + * + * Doesn't need to be overridden if there's no maintenance to do. + * + * @param int $timeout seconds to sleep if there's nothing to do + */ function idle($timeout=0) { if ($timeout > 0) { -- cgit v1.2.3-54-g00ecf From 9c983c383029c9aaa7d0cf04a5fc9973d514dff7 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Oct 2009 15:44:36 -0400 Subject: extract Geonames stuff to a plugin --- lib/location.php | 119 ++++++++++++------- plugins/GeonamesPlugin.php | 281 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 356 insertions(+), 44 deletions(-) create mode 100644 plugins/GeonamesPlugin.php (limited to 'lib') diff --git a/lib/location.php b/lib/location.php index f4ce7f67a..5b7f47102 100644 --- a/lib/location.php +++ b/lib/location.php @@ -34,6 +34,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { /** * class for locations * + * These are stored in the DB as part of notice and profile records, + * but since they're about the same in both, we have a separate class + * for them. + * * @category Location * @package StatusNet * @author Evan Prodromou @@ -48,74 +52,94 @@ class Location public $location_id; public $location_ns; - var $names; + var $names = array(); - const geonames = 1; - const whereOnEarth = 2; + /** + * Constructor that makes a Location from a string name + * + * @param string $name Human-readable name (any kind) + * @param string $language Language, default = common_language() + * + * @return Location Location with that name (or null if not found) + */ - static function fromName($name, $language=null, $location_ns=null) + static function fromName($name, $language=null) { if (is_null($language)) { $language = common_language(); } - if (is_null($location_ns)) { - $location_ns = common_config('location', 'namespace'); - } $location = null; - if (Event::handle('LocationFromName', array($name, $language, $location_ns, &$location))) { + // Let a third-party handle it - switch ($location_ns) { - case Location::geonames: - return Location::fromGeonamesName($name, $language); - break; - case Location::whereOnEarth: - return Location::fromWhereOnEarthName($name, $language); - break; - } - } + Event::handle('LocationFromName', array($name, $language, &$location)); return $location; } - static function fromGeonamesName($name, $language) + /** + * Constructor that makes a Location from an ID + * + * @param integer $id Identifier ID + * @param integer $ns Namespace of the identifier + * @param string $language Language to return name in (default is common) + * + * @return Location The location with this ID (or null if none) + */ + + static function fromId($id, $ns, $language=null) { $location = null; - $client = HTTPClient::start(); - - // XXX: break down a name by commas, narrow by each - - $str = http_build_query(array('maxRows' => 1, - 'q' => $name, - 'lang' => $language, - 'type' => 'json')); - - $result = $client->get('http://ws.geonames.org/search?'.$str); - - if ($result->code == "200") { - $rj = json_decode($result->body); - if (count($rj['geonames']) > 0) { - $n = $rj['geonames'][0]; - $location = new Location(); - $location->lat = $n->lat; - $location->lon = $n->lon; - $location->name = $n->name; - $location->location_id = $n->geonameId; - $location->location_ns = Location:geonames; - } - } + + // Let a third-party handle it + + Event::handle('LocationFromId', array($id, $ns, $language, &$location)); return $location; } - static function fromId($location_id, $location_ns = null) + /** + * Constructor that finds the nearest location to a lat/lon pair + * + * @param float $lat Latitude + * @param float $lon Longitude + * @param string $language Language for results, default = current + * + * @return Location the location found, or null if none found + */ + + static function fromLatLon($lat, $lon, $language=null) { - if (is_null($location_ns)) { - $location_ns = common_config('location', 'namespace'); + if (is_null($language)) { + $language = common_language(); } + + $location = null; + + // Let a third-party handle it + + if (Event::handle('LocationFromLatLon', + array($lat, $lon, $language, &$location))) { + // Default is just the lat/lon pair + + $location = new Location(); + + $location->lat = $lat; + $location->lon = $lon; + } + + return $location; } + /** + * Get the name for this location in the given language + * + * @param string $language language to use, default = current + * + * @return string location name or null if not found + */ + function getName($language=null) { if (is_null($language)) { @@ -124,6 +148,13 @@ class Location if (array_key_exists($this->names, $language)) { return $this->names[$language]; + } else { + $name = null; + Event::handle('LocationNameLanguage', array($this, $language, &$name)); + if (!empty($name)) { + $this->names[$language] = $name; + return $name; + } } } } diff --git a/plugins/GeonamesPlugin.php b/plugins/GeonamesPlugin.php new file mode 100644 index 000000000..934e998c7 --- /dev/null +++ b/plugins/GeonamesPlugin.php @@ -0,0 +1,281 @@ +. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to convert string locations to Geonames IDs and vice versa + * + * This handles most of the events that Location class emits. It uses + * the geonames.org Web service to convert names like 'Montreal, Quebec, Canada' + * into IDs and lat/lon pairs. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso Location + */ + +class GeonamesPlugin extends Plugin +{ + const NAMESPACE = 1; + + /** + * convert a name into a Location object + * + * @param string $name Name to convert + * @param string $language ISO code for anguage the name is in + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + + function onLocationFromName($name, $language, &$location) + { + $client = HTTPClient::start(); + + // XXX: break down a name by commas, narrow by each + + $str = http_build_query(array('maxRows' => 1, + 'q' => $name, + 'lang' => $language, + 'type' => 'json')); + + $result = $client->get('http://ws.geonames.org/search?'.$str); + + if ($result->code == "200") { + $rj = json_decode($result->body); + if (count($rj['geonames']) > 0) { + $n = $rj['geonames'][0]; + + $location = new Location(); + + $location->lat = $n['lat']; + $location->lon = $n['lng']; + $location->names[$language] = $n['name']; + $location->location_id = $n['geonameId']; + $location->location_ns = self::NAMESPACE; + + // handled, don't continue processing! + return false; + } + } + + // Continue processing; we don't have the answer + return true; + } + + /** + * convert an id into a Location object + * + * @param string $id Name to convert + * @param string $ns Name to convert + * @param string $language ISO code for language for results + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + + function onLocationFromId($id, $ns, $language, &$location) + { + if ($ns != self::NAMESPACE) { + // It's not one of our IDs... keep processing + return true; + } + + $client = HTTPClient::start(); + + $str = http_build_query(array('geonameId' => $id, + 'lang' => $language)); + + $result = $client->get('http://ws.geonames.org/hierarchyJSON?'.$str); + + if ($result->code == "200") { + + $rj = json_decode($result->body); + + if (count($rj['geonames']) > 0) { + + $parts = array(); + + foreach ($rj['geonames'] as $level) { + if (in_array($level['fcode'], array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $level['name']; + } + } + + $last = $rj['geonames'][count($rj['geonames'])-1]; + + if (!in_array($level['fcode'], array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $last['name']; + } + + $location = new Location(); + + $location->location_id = $last['geonameId']; + $location->location_ns = self::NAMESPACE; + $location->lat = $last['lat']; + $location->lon = $last['lng']; + $location->names[$language] = implode(', ', array_reverse($parts)); + } + } + + // We're responsible for this NAMESPACE; nobody else + // can resolve it + + return false; + } + + /** + * convert a lat/lon pair into a Location object + * + * Given a lat/lon, we try to find a Location that's around + * it or nearby. We prefer populated places (cities, towns, villages). + * + * @param string $lat Latitude + * @param string $lon Longitude + * @param string $language ISO code for language for results + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + + function onLocationFromLatLon($lat, $lon, $language, &$location) + { + $client = HTTPClient::start(); + + $str = http_build_query(array('lat' => $lat, + 'lng' => $lon, + 'lang' => $language)); + + $result = + $client->get('http://ws.geonames.org/findNearbyPlaceNameJSON?'.$str); + + if ($result->code == "200") { + + $rj = json_decode($result->body); + + if (count($rj['geonames']) > 0) { + + $n = $rj['geonames'][0]; + + $parts = array(); + + $location = new Location(); + + $parts[] = $n['name']; + + if (!empty($n['adminName1'])) { + $parts[] = $n['adminName1']; + } + + if (!empty($n['countryName'])) { + $parts[] = $n['countryName']; + } + + $location->location_id = $n['geonameId']; + $location->location_ns = self::NAMESPACE; + $location->lat = $lat; + $location->lon = $lon; + + $location->names[$language] = implode(', ', $parts); + + // Success! We handled it, so no further processing + + return false; + } + } + + // For some reason we don't know, so pass. + + return true; + } + + /** + * Human-readable name for a location + * + * Given a location, we try to retrieve a human-readable name + * in the target language. + * + * @param Location $location Location to get the name for + * @param string $language ISO code for language to find name in + * @param string &$name Place to put the name + * + * @return boolean whether to continue + */ + + function onLocationNameLanguage($location, $language, &$name) + { + if ($location->location_ns != self::NAMESPACE) { + // It's not one of our IDs... keep processing + return true; + } + + $client = HTTPClient::start(); + + $str = http_build_query(array('geonameId' => $id, + 'lang' => $language)); + + $result = $client->get('http://ws.geonames.org/hierarchyJSON?'.$str); + + if ($result->code == "200") { + + $rj = json_decode($result->body); + + if (count($rj['geonames']) > 0) { + + $parts = array(); + + foreach ($rj['geonames'] as $level) { + if (in_array($level['fcode'], array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $level['name']; + } + } + + $last = $rj['geonames'][count($rj['geonames'])-1]; + + if (!in_array($level['fcode'], array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $last['name']; + } + + if (count($parts)) { + $name = implode(', ', array_reverse($parts)); + return false; + } + } + } + + return true; + } +} -- cgit v1.2.3-54-g00ecf From 69357c49167679043d36a1ae2df4bf2d117cbcd0 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Oct 2009 16:19:56 -0400 Subject: error in order of arguments to array_key_exists in location.php --- lib/location.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/location.php b/lib/location.php index 5b7f47102..048554f0f 100644 --- a/lib/location.php +++ b/lib/location.php @@ -146,7 +146,7 @@ class Location $language = common_language(); } - if (array_key_exists($this->names, $language)) { + if (array_key_exists($language, $this->names)) { return $this->names[$language]; } else { $name = null; -- cgit v1.2.3-54-g00ecf From 65c45986c68709f6e8e981381c35ee1a2468544a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Oct 2009 16:20:56 -0400 Subject: HTTPResponse has default empty headers --- lib/httpclient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/httpclient.php b/lib/httpclient.php index c8c8ae5b2..f16e31e10 100644 --- a/lib/httpclient.php +++ b/lib/httpclient.php @@ -48,7 +48,7 @@ if (!defined('STATUSNET')) { class HTTPResponse { public $code = null; - public $headers = null; + public $headers = array(); public $body = null; } -- cgit v1.2.3-54-g00ecf From b89878511f1f30b4e18858940a5468b8999ab7ea Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 23 Oct 2009 20:31:53 +0000 Subject: Fix three fatal errors when posting from 0.9.x: * OMB remote updates were trying to load nonexistent Laconica_OMB_Service_Consumer class -- fixed to StatusNet_OMB_Service_Consumer. Regression caused during libomb merge. * Twitter processing was still being queued from core when no twitter plugin was present, which triggered an exception from UnqueueHandler; leftover code from before the plugin extraction. * UnqueueHandler's exception caused a fatal error instead because it was missing the "new" keyword. Wouldn't have been seen when testing with the plugin enabled. --- lib/omb.php | 2 +- lib/unqueuemanager.php | 2 +- lib/util.php | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/omb.php b/lib/omb.php index 0566701ff..cd6d6e1b2 100644 --- a/lib/omb.php +++ b/lib/omb.php @@ -87,7 +87,7 @@ function omb_broadcast_notice($notice) common_debug('Posting to ' . $rp->postnoticeurl, __FILE__); /* Post notice. */ - $service = new Laconica_OMB_Service_Consumer( + $service = new StatusNet_OMB_Service_Consumer( array(OMB_ENDPOINT_POSTNOTICE => $rp->postnoticeurl)); try { $service->setToken($rp->token, $rp->secret); diff --git a/lib/unqueuemanager.php b/lib/unqueuemanager.php index 51261bcd7..6c26fac0e 100644 --- a/lib/unqueuemanager.php +++ b/lib/unqueuemanager.php @@ -72,7 +72,7 @@ class UnQueueManager break; default: if (Event::handle('UnqueueHandleNotice', array(&$notice, $queue))) { - throw ServerException("UnQueueManager: Unknown queue: $queue"); + throw new ServerException("UnQueueManager: Unknown queue: $queue"); } } } diff --git a/lib/util.php b/lib/util.php index e641afd4d..55ded7dd4 100644 --- a/lib/util.php +++ b/lib/util.php @@ -906,7 +906,6 @@ function common_broadcast_notice($notice, $remote=false) function common_enqueue_notice($notice) { static $localTransports = array('omb', - 'twitter', 'facebook', 'ping'); -- cgit v1.2.3-54-g00ecf From 2544310e7391343ab655469ef6ac68bbe8ab0a5b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 23 Oct 2009 23:34:48 +0000 Subject: Remove automatic enqueuing for Facebook --- lib/util.php | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/util.php b/lib/util.php index 55ded7dd4..b6e89f0bd 100644 --- a/lib/util.php +++ b/lib/util.php @@ -906,7 +906,6 @@ function common_broadcast_notice($notice, $remote=false) function common_enqueue_notice($notice) { static $localTransports = array('omb', - 'facebook', 'ping'); static $allTransports = array('sms', 'plugin'); -- cgit v1.2.3-54-g00ecf From 6d6de3c1c7b359815aa0381b4cde3fcc8258cc80 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 23 Oct 2009 23:38:45 +0000 Subject: Move Twitter and Facebook-specific mail notifications to their respective plugins --- lib/mail.php | 72 --------------------------------------- plugins/Facebook/facebookutil.php | 35 +++++++++++++++++++ plugins/TwitterBridge/twitter.php | 37 ++++++++++++++++++++ 3 files changed, 72 insertions(+), 72 deletions(-) (limited to 'lib') diff --git a/lib/mail.php b/lib/mail.php index 5bf4d7425..5218059e9 100644 --- a/lib/mail.php +++ b/lib/mail.php @@ -640,75 +640,3 @@ function mail_notify_attn($user, $notice) mail_to_user($user, $subject, $body); } -/** - * Send a mail message to notify a user that her Twitter bridge link - * has stopped working, and therefore has been removed. This can - * happen when the user changes her Twitter password, or otherwise - * revokes access. - * - * @param User $user user whose Twitter bridge link has been removed - * - * @return boolean success flag - */ - -function mail_twitter_bridge_removed($user) -{ - common_init_locale($user->language); - - $profile = $user->getProfile(); - - $subject = sprintf(_('Your Twitter bridge has been disabled.')); - - $site_name = common_config('site', 'name'); - - $body = sprintf(_('Hi, %1$s. We\'re sorry to inform you that your ' . - 'link to Twitter has been disabled. We no longer seem to have ' . - 'permission to update your Twitter status. (Did you revoke ' . - '%3$s\'s access?)' . "\n\n" . - 'You can re-enable your Twitter bridge by visiting your ' . - "Twitter settings page:\n\n\t%2\$s\n\n" . - "Regards,\n%3\$s\n"), - $profile->getBestName(), - common_local_url('twittersettings'), - common_config('site', 'name')); - - common_init_locale(); - return mail_to_user($user, $subject, $body); -} - -/** - * Send a mail message to notify a user that her Facebook Application - * access has been removed. - * - * @param User $user user whose Facebook app link has been removed - * - * @return boolean success flag - */ - -function mail_facebook_app_removed($user) -{ - common_init_locale($user->language); - - $profile = $user->getProfile(); - - $site_name = common_config('site', 'name'); - - $subject = sprintf( - _('Your %1$s Facebook application access has been disabled.', - $site_name)); - - $body = sprintf(_("Hi, %1\$s. We're sorry to inform you that we are " . - 'unable to update your Facebook status from %2$s, and have disabled ' . - 'the Facebook application for your account. This may be because ' . - 'you have removed the Facebook application\'s authorization, or ' . - 'have deleted your Facebook account. You can re-enable the ' . - 'Facebook application and automatic status updating by ' . - "re-installing the %2\$s Facebook application.\n\nRegards,\n\n%2\$s"), - $user->nickname, $site_name); - - common_init_locale(); - return mail_to_user($user, $subject, $body); - -} - - diff --git a/plugins/Facebook/facebookutil.php b/plugins/Facebook/facebookutil.php index da53f35e2..6f50c173a 100644 --- a/plugins/Facebook/facebookutil.php +++ b/plugins/Facebook/facebookutil.php @@ -258,3 +258,38 @@ function remove_facebook_app($flink) } } + +/** + * Send a mail message to notify a user that her Facebook Application + * access has been removed. + * + * @param User $user user whose Facebook app link has been removed + * + * @return boolean success flag + */ + +function mail_facebook_app_removed($user) +{ + common_init_locale($user->language); + + $profile = $user->getProfile(); + + $site_name = common_config('site', 'name'); + + $subject = sprintf( + _('Your %1$s Facebook application access has been disabled.', + $site_name)); + + $body = sprintf(_("Hi, %1\$s. We're sorry to inform you that we are " . + 'unable to update your Facebook status from %2$s, and have disabled ' . + 'the Facebook application for your account. This may be because ' . + 'you have removed the Facebook application\'s authorization, or ' . + 'have deleted your Facebook account. You can re-enable the ' . + 'Facebook application and automatic status updating by ' . + "re-installing the %2\$s Facebook application.\n\nRegards,\n\n%2\$s"), + $user->nickname, $site_name); + + common_init_locale(); + return mail_to_user($user, $subject, $body); + +} diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php index ac1f49c36..1a5248a9b 100644 --- a/plugins/TwitterBridge/twitter.php +++ b/plugins/TwitterBridge/twitter.php @@ -312,3 +312,40 @@ function remove_twitter_link($flink) } } + +/** + * Send a mail message to notify a user that her Twitter bridge link + * has stopped working, and therefore has been removed. This can + * happen when the user changes her Twitter password, or otherwise + * revokes access. + * + * @param User $user user whose Twitter bridge link has been removed + * + * @return boolean success flag + */ + +function mail_twitter_bridge_removed($user) +{ + common_init_locale($user->language); + + $profile = $user->getProfile(); + + $subject = sprintf(_('Your Twitter bridge has been disabled.')); + + $site_name = common_config('site', 'name'); + + $body = sprintf(_('Hi, %1$s. We\'re sorry to inform you that your ' . + 'link to Twitter has been disabled. We no longer seem to have ' . + 'permission to update your Twitter status. (Did you revoke ' . + '%3$s\'s access?)' . "\n\n" . + 'You can re-enable your Twitter bridge by visiting your ' . + "Twitter settings page:\n\n\t%2\$s\n\n" . + "Regards,\n%3\$s\n"), + $profile->getBestName(), + common_local_url('twittersettings'), + common_config('site', 'name')); + + common_init_locale(); + return mail_to_user($user, $subject, $body); +} + -- cgit v1.2.3-54-g00ecf From 0b4390e7f2322a15f16919425de039d555b3e516 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Mon, 26 Oct 2009 10:31:12 -0400 Subject: Make email domain checking optional, as some statusnet installations (such as those behind restrictive corporate firewalls, or on home systems on restrictive connections) cannot connect to any mail systems, and this check will always fail. --- actions/emailsettings.php | 2 +- actions/invite.php | 2 +- actions/register.php | 2 +- config.php.sample | 4 ++++ lib/default.php | 3 ++- plugins/OpenID/finishopenidlogin.php | 2 +- plugins/OpenID/openid.php | 2 +- 7 files changed, 11 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/actions/emailsettings.php b/actions/emailsettings.php index 6eff06c0d..67b991cdc 100644 --- a/actions/emailsettings.php +++ b/actions/emailsettings.php @@ -326,7 +326,7 @@ class EmailsettingsAction extends AccountSettingsAction $this->showForm(_('Cannot normalize that email address')); return; } - if (!Validate::email($email, true)) { + if (!Validate::email($email, common_config('email', 'check_domain'))) { $this->showForm(_('Not a valid email address')); return; } else if ($user->email == $email) { diff --git a/actions/invite.php b/actions/invite.php index 788130c58..3015202e9 100644 --- a/actions/invite.php +++ b/actions/invite.php @@ -68,7 +68,7 @@ class InviteAction extends CurrentUserDesignAction foreach ($addresses as $email) { $email = trim($email); - if (!Validate::email($email, true)) { + if (!Validate::email($email, common_config('email', 'check_domain'))) { $this->showForm(sprintf(_('Invalid email address: %s'), $email)); return; } diff --git a/actions/register.php b/actions/register.php index 100ab7424..a6c1a903a 100644 --- a/actions/register.php +++ b/actions/register.php @@ -191,7 +191,7 @@ class RegisterAction extends Action if (!$this->boolean('license')) { $this->showForm(_('You can\'t register if you don\'t '. 'agree to the license.')); - } else if ($email && !Validate::email($email, true)) { + } else if ($email && !Validate::email($email, common_config('email', 'check_domain'))) { $this->showForm(_('Not a valid email address.')); } else if (!Validate::string($nickname, array('min_length' => 1, 'max_length' => 64, diff --git a/config.php.sample b/config.php.sample index 997c9d6b0..9fccb84f3 100644 --- a/config.php.sample +++ b/config.php.sample @@ -104,6 +104,10 @@ $config['sphinx']['port'] = 3312; // $config['site']['timezone'] = 'Pacific/Auckland'; // $config['site']['language'] = 'en_NZ'; +// When validating user supplied email addresses, validate if the domain +// is running an SMTP server. +// $config['mail']['check_domain'] = true; + // Email info, used for all outbound email // $config['mail']['notifyfrom'] = 'microblog@example.net'; // $config['mail']['domain'] = 'microblog.example.net'; diff --git a/lib/default.php b/lib/default.php index 30e43eefb..9f64cc9e4 100644 --- a/lib/default.php +++ b/lib/default.php @@ -84,7 +84,8 @@ $default = 'image' => 'http://i.creativecommons.org/l/by/3.0/80x15.png'), 'mail' => array('backend' => 'mail', - 'params' => null), + 'params' => null, + 'domain_check' => true), 'nickname' => array('blacklist' => array(), 'featured' => array()), diff --git a/plugins/OpenID/finishopenidlogin.php b/plugins/OpenID/finishopenidlogin.php index 50a9c15c8..ff0b451d3 100644 --- a/plugins/OpenID/finishopenidlogin.php +++ b/plugins/OpenID/finishopenidlogin.php @@ -265,7 +265,7 @@ class FinishopenidloginAction extends Action $fullname = ''; } - if (!empty($sreg['email']) && Validate::email($sreg['email'], true)) { + if (!empty($sreg['email']) && Validate::email($sreg['email'], common_config('email', 'check_domain'))) { $email = $sreg['email']; } else { $email = ''; diff --git a/plugins/OpenID/openid.php b/plugins/OpenID/openid.php index 0944117c0..b76497c28 100644 --- a/plugins/OpenID/openid.php +++ b/plugins/OpenID/openid.php @@ -241,7 +241,7 @@ function oid_update_user(&$user, &$sreg) $orig_user = clone($user); - if ($sreg['email'] && Validate::email($sreg['email'], true)) { + if ($sreg['email'] && Validate::email($sreg['email'], common_config('email', 'check_domain'))) { $user->email = $sreg['email']; } -- cgit v1.2.3-54-g00ecf From 922ee7b3b292689806b0b94a9eb94fe08a204751 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Tue, 27 Oct 2009 21:48:56 -0400 Subject: Implemented reply # command, allowing users to favorite specific notices by the notice id --- lib/command.php | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/command.php b/lib/command.php index 11d40b8e1..6ece01b96 100644 --- a/lib/command.php +++ b/lib/command.php @@ -124,18 +124,30 @@ class FavCommand extends Command function execute($channel) { + if(substr($this->other,0,1)=='#'){ + //replying to a specific notice_id - $recipient = - common_relative_profile($this->user, common_canonical_nickname($this->other)); + $notice = Notice::staticGet(substr($this->other,1)); + if (!$notice) { + $channel->error($this->user, _('Notice with that id does not exist')); + return; + } + $recipient = $notice->getProfile(); + }else{ + //replying to a given user's last notice - if (!$recipient) { - $channel->error($this->user, _('No such user.')); - return; - } - $notice = $recipient->getCurrentNotice(); - if (!$notice) { - $channel->error($this->user, _('User has no last notice')); - return; + $recipient = + common_relative_profile($this->user, common_canonical_nickname($this->other)); + + if (!$recipient) { + $channel->error($this->user, _('No such user.')); + return; + } + $notice = $recipient->getCurrentNotice(); + if (!$notice) { + $channel->error($this->user, _('User has no last notice')); + return; + } } $fave = Fave::addNew($this->user, $notice); @@ -497,6 +509,7 @@ class HelpCommand extends Command "get - get last notice from user\n". "whois - get profile info on user\n". "fav - add user's last notice as a 'fave'\n". + "fav # - add notice with the given id as a 'fave'\n". "join - join group\n". "drop - leave group\n". "stats - get your stats\n". -- cgit v1.2.3-54-g00ecf From 93a6e83d5ddb48ccae80ba9f5c62c63888724083 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 27 Oct 2009 19:11:18 -0700 Subject: Extract media upload stuff into its own library class. --- actions/newnotice.php | 188 +++-------------------------- lib/mediafile.php | 322 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 341 insertions(+), 169 deletions(-) create mode 100644 lib/mediafile.php (limited to 'lib') diff --git a/actions/newnotice.php b/actions/newnotice.php index 9ee031f93..5100e79e1 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -33,7 +33,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -require_once INSTALLDIR.'/lib/noticelist.php'; +require_once INSTALLDIR . '/lib/noticelist.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; /** * Action for posting new notices @@ -113,33 +114,6 @@ class NewnoticeAction extends Action } } - function getUploadedFileType() { - require_once 'MIME/Type.php'; - - $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); - $cmd = common_config('attachments', 'filecommand'); - - $filetype = MIME_Type::autoDetect($_FILES['attach']['tmp_name']); - if (in_array($filetype, common_config('attachments', 'supported'))) { - return $filetype; - } - $media = MIME_Type::getMedia($filetype); - if ('application' !== $media) { - $hint = sprintf(_(' Try using another %s format.'), $media); - } else { - $hint = ''; - } - $this->clientError(sprintf( - _('%s is not a supported filetype on this server.'), $filetype) . $hint); - } - - function isRespectsQuota($user) { - $file = new File; - $ret = $file->isRespectsQuota($user,$_FILES['attach']['size']); - if (true === $ret) return true; - $this->clientError($ret); - } - /** * Save a new notice, based on arguments * @@ -189,78 +163,35 @@ class NewnoticeAction extends Action $replyto = 'false'; } - if (isset($_FILES['attach']['error'])) { - switch ($_FILES['attach']['error']) { - case UPLOAD_ERR_NO_FILE: - // no file uploaded, nothing to do - break; + $upload = null; - case UPLOAD_ERR_OK: - $mimetype = $this->getUploadedFileType(); - if (!$this->isRespectsQuota($user)) { - die('clientError() should trigger an exception before reaching here.'); - } - break; - - case UPLOAD_ERR_INI_SIZE: - $this->clientError(_('The uploaded file exceeds the upload_max_filesize directive in php.ini.')); - - case UPLOAD_ERR_FORM_SIZE: - $this->clientError(_('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.')); - - case UPLOAD_ERR_PARTIAL: - $this->clientError(_('The uploaded file was only partially uploaded.')); + common_debug('looking for attachment'); - case UPLOAD_ERR_NO_TMP_DIR: - $this->clientError(_('Missing a temporary folder.')); + $upload = MediaFile::fromUpload('attach'); - case UPLOAD_ERR_CANT_WRITE: - $this->clientError(_('Failed to write file to disk.')); + common_debug("uploaded file = " . var_export($upload, true)); - case UPLOAD_ERR_EXTENSION: - $this->clientError(_('File upload stopped by extension.')); + if (isset($upload)) { + common_debug('newNotice: found an upload'); - default: - die('Should never reach here.'); - } - } - - if (isset($mimetype)) { - $filename = $this->saveFile($mimetype); - if (empty($filename)) { - $this->clientError(_('Couldn\'t save file.')); - } + $content_shortened .= ' ' . $upload->shortUrl(); - $fileRecord = $this->storeFile($filename, $mimetype); + common_debug('content w/upload = ' . $content_shortened); - $fileurl = common_local_url('attachment', - array('attachment' => $fileRecord->id)); - - // not sure this is necessary -- Zach - $this->maybeAddRedir($fileRecord->id, $fileurl); - - $short_fileurl = common_shorten_url($fileurl); - if (!$short_fileurl) { - // todo -- Consider forcing default shortener if none selected? - $short_fileurl = $fileurl; - } - $content_shortened .= ' ' . $short_fileurl; - - if (Notice::contentTooLong($content_shortened)) { - $this->deleteFile($filename); - $this->clientError(sprintf(_('Max notice size is %d chars, including attachment URL.'), - Notice::maxContent())); - } - - // Also, not sure this is necessary -- Zach - $this->maybeAddRedir($fileRecord->id, $short_fileurl); + if (Notice::contentTooLong($content_shortened)) { + $upload->delete(); + $this->clientError(sprintf(_('Max notice size is %d chars, including attachment URL.'), + Notice::maxContent())); + } else { + common_debug('content not too long'); + } } $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1, ($replyto == 'false') ? null : $replyto); - if (isset($mimetype)) { - $this->attachFile($notice, $fileRecord); + if (isset($upload)) { + $upload->attachToNotice($notice); } common_broadcast_notice($notice); @@ -288,87 +219,6 @@ class NewnoticeAction extends Action } } - function saveFile($mimetype) { - - $cur = common_current_user(); - - if (empty($cur)) { - $this->serverError(_('Somehow lost the login in saveFile')); - } - - $basename = basename($_FILES['attach']['name']); - - $filename = File::filename($cur->getProfile(), $basename, $mimetype); - - $filepath = File::path($filename); - - if (move_uploaded_file($_FILES['attach']['tmp_name'], $filepath)) { - return $filename; - } else { - $this->clientError(_('File could not be moved to destination directory.')); - } - } - - function deleteFile($filename) - { - $filepath = File::path($filename); - @unlink($filepath); - } - - function storeFile($filename, $mimetype) { - - $file = new File; - $file->filename = $filename; - - $file->url = File::url($filename); - - $filepath = File::path($filename); - - $file->size = filesize($filepath); - $file->date = time(); - $file->mimetype = $mimetype; - - $file_id = $file->insert(); - - if (!$file_id) { - common_log_db_error($file, "INSERT", __FILE__); - $this->clientError(_('There was a database error while saving your file. Please try again.')); - } - - return $file; - } - - function rememberFile($file, $short) - { - $this->maybeAddRedir($file->id, $short); - } - - function maybeAddRedir($file_id, $url) - { - $file_redir = File_redirection::staticGet('url', $url); - - if (empty($file_redir)) { - $file_redir = new File_redirection; - $file_redir->url = $url; - $file_redir->file_id = $file_id; - - $result = $file_redir->insert(); - - if (!$result) { - common_log_db_error($file_redir, "INSERT", __FILE__); - $this->clientError(_('There was a database error while saving your file. Please try again.')); - } - } - } - - function attachFile($notice, $filerec) - { - File_to_post::processNew($filerec->id, $notice->id); - - $this->maybeAddRedir($filerec->id, - common_local_url('file', array('notice' => $notice->id))); - } - /** * Show an Ajax-y error message * diff --git a/lib/mediafile.php b/lib/mediafile.php new file mode 100644 index 000000000..be101cdca --- /dev/null +++ b/lib/mediafile.php @@ -0,0 +1,322 @@ +. + * + * @category Media + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +class MediaFile +{ + + var $filename = null; + var $fileRecord = null; + var $user = null; + var $fileurl = null; + var $short_fileurl = null; + var $mimetype = null; + + function __construct($user = null, $filename = null, $mimetype = null) + { + if ($user == null) { + $this->user = common_current_user(); + } + + common_debug('in MediaFile constructor'); + + $this->filename = $filename; + $this->mimetype = $mimetype; + + common_debug('storing file'); + $this->fileRecord = $this->storeFile(); + common_debug('finished storing file'); + + $this->fileurl = common_local_url('attachment', + array('attachment' => $this->fileRecord->id)); + + common_debug('$this->fileurl() = ' . $this->fileurl); + + // not sure this is necessary -- Zach + $this->maybeAddRedir($this->fileRecord->id, $this->fileurl); + + common_debug('shortening file url'); + $this->short_fileurl = common_shorten_url($this->fileurl); + common_debug('shortened file url = ' . $short_fileurl); + + // Also, not sure this is necessary -- Zach + $this->maybeAddRedir($this->fileRecord->id, $this->short_fileurl); + + common_debug("MediaFile: end of constructor"); + } + + function attachToNotice($notice) + { + common_debug('MediaFile::attachToNotice() -- doing File_to_post'); + File_to_post::processNew($this->fileRecord->id, $notice->id); + common_debug('MediaFile done doing File_to_post'); + + $this->maybeAddRedir($this->fileRecord->id, + common_local_url('file', array('notice' => $notice->id))); + } + + function shortUrl() + { + return $this->short_fileurl; + } + + function delete() + { + $filepath = File::path($this->filename); + @unlink($filepath); + } + + function storeFile() { + + $file = new File; + $file->filename = $this->filename; + + common_debug('storing ' . $this->filename); + + $file->url = File::url($this->filename); + common_debug('file->url = ' . $file->url); + + $filepath = File::path($this->filename); + common_debug('filepath = ' . $filepath); + + $file->size = filesize($filepath); + $file->date = time(); + $file->mimetype = $this->mimetype; + + $file_id = $file->insert(); + + if (!$file_id) { + + common_debug("storeFile: problem inserting new file"); + common_log_db_error($file, "INSERT", __FILE__); + throw new ClientException(_('There was a database error while saving your file. Please try again.')); + } + + common_debug('finished storing file'); + + return $file; + } + + function rememberFile($file, $short) + { + $this->maybeAddRedir($file->id, $short); + } + + function maybeAddRedir($file_id, $url) + { + + common_debug("maybeAddRedir: looking up url: $url for file id $file_id"); + + $file_redir = File_redirection::staticGet('url', $url); + + if (empty($file_redir)) { + + common_debug("maybeAddRedir: $url is not in the db"); + + $file_redir = new File_redirection; + $file_redir->url = $url; + $file_redir->file_id = $file_id; + + $result = $file_redir->insert(); + + if (!$result) { + common_log_db_error($file_redir, "INSERT", __FILE__); + throw new ClientException(_('There was a database error while saving your file. Please try again.')); + } + } else { + + common_debug("maybeAddRedir: no need to add $url, it's already in the db"); + } + } + + static function fromUpload($param = 'media') + { + common_debug("fromUpload: param = $param"); + + if (!isset($_FILES[$param]['error'])){ + common_debug('no file found'); + return; + } + + switch ($_FILES[$param]['error']) { + case UPLOAD_ERR_OK: // success, jump out + break; + case UPLOAD_ERR_INI_SIZE: + throw new ClientException(_('The uploaded file exceeds the ' . + 'upload_max_filesize directive in php.ini.')); + return; + case UPLOAD_ERR_FORM_SIZE: + throw new ClientException( + _('The uploaded file exceeds the MAX_FILE_SIZE directive' . + ' that was specified in the HTML form.')); + return; + case UPLOAD_ERR_PARTIAL: + @unlink($_FILES[$param]['tmp_name']); + throw new ClientException(_('The uploaded file was only' . + ' partially uploaded.')); + return; + case UPLOAD_ERR_NO_TMP_DIR: + throw new ClientException(_('Missing a temporary folder.')); + return; + case UPLOAD_ERR_CANT_WRITE: + throw new ClientException(_('Failed to write file to disk.')); + return; + case UPLOAD_ERR_EXTENSION: + throw new ClientException(_('File upload stopped by extension.')); + return; + default: + throw new ClientException(_('System error uploading file.')); + return; + } + + $user = common_current_user(); + + if (!MediaFile::respectsQuota($user, $_FILES['attach']['size'])) { + + // Should never actually get here + + @unlink($_FILES[$param]['tmp_name']); + throw new ClientException(_('File exceeds user\'s quota!')); + return; + } + + $mimetype = MediaFile::getUploadedFileType($_FILES[$param]['tmp_name']); + + $filename = null; + + if (isset($mimetype)) { + + $basename = basename($_FILES[$param]['name']); + $filename = File::filename($user->getProfile(), $basename, $mimetype); + $filepath = File::path($filename); + + common_debug("filepath = " . $filepath); + + $result = move_uploaded_file($_FILES[$param]['tmp_name'], $filepath); + + if (!$result) { + throw new ClientException(_('File could not be moved to destination directory.')); + return; + } + + } else { + throw new ClientException(_('Could not determine file\'s mime-type!')); + return; + } + + return new MediaFile($user, $filename, $mimetype); + } + + static function fromFilehandle($user, $fh) { + + $stream = stream_get_meta_data($fh); + + if (MediaFile::respectsQuota($user, filesize($stream['uri']))) { + + // Should never actually get here + + throw new ClientException(_('File exceeds user\'s quota!')); + return; + } + + $mimetype = MediaFile::getUploadedFileType($fh); + + $filename = null; + + if (isset($mimetype)) { + + $filename = File::filename($user->getProfile(), "email", $mimetype); + + $filepath = File::path($filename); + + $result = copy($stream['uri'], $filepath) && chmod($filepath, 0664); + + if (!$result) { + throw new ClientException(_('File could not be moved to destination directory.' . + $stream['uri'] . ' ' . $filepath)); + } + } else { + throw new ClientException(_('Could not determine file\'s mime-type!')); + return; + } + + return new MediaFile($user, $filename, $mimetype); + } + + static function getUploadedFileType($f) { + require_once 'MIME/Type.php'; + + common_debug("in getUploadedFileType"); + + $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); + $cmd = common_config('attachments', 'filecommand'); + + $filetype = null; + + if (is_string($f)) { + + // assuming a filename + + $filetype = MIME_Type::autoDetect($f); + } else { + + // assuming a filehandle + + $stream = stream_get_meta_data($f); + $filetype = MIME_Type::autoDetect($stream['uri']); + } + + if (in_array($filetype, common_config('attachments', 'supported'))) { + return $filetype; + } + $media = MIME_Type::getMedia($filetype); + if ('application' !== $media) { + $hint = sprintf(_(' Try using another %s format.'), $media); + } else { + $hint = ''; + } + throw new ClientException(sprintf( + _('%s is not a supported filetype on this server.'), $filetype) . $hint); + } + + static function respectsQuota($user, $filesize) + { + $file = new File; + $result = $file->isRespectsQuota($user, $filesize); + if ($result === true) { + return true; + } else { + throw new ClientException($result); + } + } + +} \ No newline at end of file -- cgit v1.2.3-54-g00ecf From da16dc05a81e2fcc1ed89fe16717f2652265f23a Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Tue, 27 Oct 2009 22:30:21 -0400 Subject: Added a new "reply" command --- lib/command.php | 71 ++++++++++++++++++++++++++++++++++++++++++++-- lib/commandinterpreter.php | 11 +++++++ 2 files changed, 80 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/command.php b/lib/command.php index 6ece01b96..d7ae11361 100644 --- a/lib/command.php +++ b/lib/command.php @@ -125,7 +125,7 @@ class FavCommand extends Command function execute($channel) { if(substr($this->other,0,1)=='#'){ - //replying to a specific notice_id + //favoriting a specific notice_id $notice = Notice::staticGet(substr($this->other,1)); if (!$notice) { @@ -134,7 +134,7 @@ class FavCommand extends Command } $recipient = $notice->getProfile(); }else{ - //replying to a given user's last notice + //favoriting a given user's last notice $recipient = common_relative_profile($this->user, common_canonical_nickname($this->other)); @@ -359,6 +359,71 @@ class MessageCommand extends Command } } +class ReplyCommand extends Command +{ + var $other = null; + var $text = null; + function __construct($user, $other, $text) + { + parent::__construct($user); + $this->other = $other; + $this->text = $text; + } + + function execute($channel) + { + if(substr($this->other,0,1)=='#'){ + //replying to a specific notice_id + + $notice = Notice::staticGet(substr($this->other,1)); + if (!$notice) { + $channel->error($this->user, _('Notice with that id does not exist')); + return; + } + $recipient = $notice->getProfile(); + }else{ + //replying to a given user's last notice + + $recipient = + common_relative_profile($this->user, common_canonical_nickname($this->other)); + + if (!$recipient) { + $channel->error($this->user, _('No such user.')); + return; + } + $notice = $recipient->getCurrentNotice(); + if (!$notice) { + $channel->error($this->user, _('User has no last notice')); + return; + } + } + + $len = mb_strlen($this->text); + + if ($len == 0) { + $channel->error($this->user, _('No content!')); + return; + } + + $this->text = common_shorten_links($this->text); + + if (Notice::contentTooLong($this->text)) { + $channel->error($this->user, sprintf(_('Notice too long - maximum is %d characters, you sent %d'), + Notice::maxContent(), mb_strlen($this->text))); + return; + } + + $notice = Notice::saveNew($this->user->id, $this->text, $channel->source(), 1, + $notice->id); + if ($notice) { + $channel->output($this->user, sprintf(_('Reply to %s sent'), $recipient->nickname)); + } else { + $channel->error($this->user, _('Error saving notice.')); + } + common_broadcast_notice($notice); + } +} + class GetCommand extends Command { @@ -510,6 +575,8 @@ class HelpCommand extends Command "whois - get profile info on user\n". "fav - add user's last notice as a 'fave'\n". "fav # - add notice with the given id as a 'fave'\n". + "reply # - reply to notice with a given id\n". + "reply - reply to the last notice from user\n". "join - join group\n". "drop - leave group\n". "stats - get your stats\n". diff --git a/lib/commandinterpreter.php b/lib/commandinterpreter.php index 60fc4c3c4..b921a17cc 100644 --- a/lib/commandinterpreter.php +++ b/lib/commandinterpreter.php @@ -134,6 +134,17 @@ class CommandInterpreter } else { return new MessageCommand($user, $other, $extra); } + case 'r': + case 'reply': + if (!$arg) { + return null; + } + list($other, $extra) = $this->split_arg($arg); + if (!$extra) { + return null; + } else { + return new ReplyCommand($user, $other, $extra); + } case 'whois': if (!$arg) { return null; -- cgit v1.2.3-54-g00ecf From 5f42023f97fca2c3b5fbd0da2d5e333e4cc2f109 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Tue, 27 Oct 2009 22:45:00 -0400 Subject: implement the nudge command --- lib/command.php | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/command.php b/lib/command.php index d7ae11361..9efa40696 100644 --- a/lib/command.php +++ b/lib/command.php @@ -73,7 +73,7 @@ class UntrackCommand extends UnimplementedCommand } } -class NudgeCommand extends UnimplementedCommand +class NudgeCommand extends Command { var $other = null; function __construct($user, $other) @@ -81,6 +81,26 @@ class NudgeCommand extends UnimplementedCommand parent::__construct($user); $this->other = $other; } + function execute($channel) + { + $recipient = User::staticGet('nickname', $this->other); + if(! $recipient){ + $channel->error($this->user, sprintf(_('Could not find a user with nickname %s'), + $this->other)); + }else{ + if ($recipient->id == $this->user->id) { + $channel->error($this->user, _('It does not make a lot of sense to nudge yourself!')); + }else{ + if ($recipient->email && $recipient->emailnotifynudge) { + mail_notify_nudge($this->user, $recipient); + } + // XXX: notify by IM + // XXX: notify by SMS + $channel->output($this->user, sprintf(_('Nudge sent to %s'), + $recipient->nickname)); + } + } + } } class InviteCommand extends UnimplementedCommand @@ -587,7 +607,7 @@ class HelpCommand extends Command "last - same as 'get'\n". "on - not yet implemented.\n". "off - not yet implemented.\n". - "nudge - not yet implemented.\n". + "nudge - remind a user to update.\n". "invite - not yet implemented.\n". "track - not yet implemented.\n". "untrack - not yet implemented.\n". -- cgit v1.2.3-54-g00ecf From 5fd7ed5b149ba74d9f5044f1d5d18f7adf48ff78 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Tue, 27 Oct 2009 23:31:49 -0400 Subject: Display user avatar in the XMPP message Include notice id and conversation link the XMPP message Thanks to Deepspawn for this idea and initial code --- lib/jabber.php | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/jabber.php b/lib/jabber.php index 3dcdce5db..73f2ec660 100644 --- a/lib/jabber.php +++ b/lib/jabber.php @@ -176,6 +176,7 @@ function jabber_format_entry($profile, $notice) $xs = new XMLStringer(); $xs->elementStart('html', array('xmlns' => 'http://jabber.org/protocol/xhtml-im')); $xs->elementStart('body', array('xmlns' => 'http://www.w3.org/1999/xhtml')); + $xs->element("img", array('src'=> $profile->avatarUrl(AVATAR_MINI_SIZE) , 'alt' => $profile->nickname)); $xs->element('a', array('href' => $profile->profileurl), $profile->nickname); $xs->text(": "); @@ -184,6 +185,11 @@ function jabber_format_entry($profile, $notice) } else { $xs->raw(common_render_content($notice->content, $notice)); } + $xs->raw(" "); + $xs->element('a', array( + 'href'=>common_local_url('conversation', + array('id' => $notice->conversation)).'#notice-'.$notice->id + ),sprintf(_('notice id: %s'),$notice->id)); $xs->elementEnd('body'); $xs->elementEnd('html'); -- cgit v1.2.3-54-g00ecf From 2d0aba49d913b7d4598073d5ff56237f736731d0 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 27 Oct 2009 21:45:56 -0700 Subject: Implement media upload in the API --- actions/apistatusesupdate.php | 72 ++++++++++++++++++++-------- lib/mediafile.php | 106 +++++++++++++++++++++--------------------- 2 files changed, 106 insertions(+), 72 deletions(-) (limited to 'lib') diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php index 0d71e1512..b7128d084 100644 --- a/actions/apistatusesupdate.php +++ b/actions/apistatusesupdate.php @@ -38,6 +38,7 @@ if (!defined('STATUSNET')) { } require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; /** * Updates the authenticating user's status (posts a notice). @@ -60,7 +61,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction var $source = null; var $status = null; var $in_reply_to_status_id = null; - static $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api'); /** @@ -76,25 +76,8 @@ class ApiStatusesUpdateAction extends ApiAuthAction { parent::prepare($args); - $this->user = $this->auth_user; - - if (empty($this->user)) { - $this->clientError(_('No such user!'), 404, $this->format); - return false; - } - + $this->user = $this->auth_user; $this->status = $this->trimmed('status'); - - if (empty($this->status)) { - $this->clientError( - 'Client must provide a \'status\' parameter with a value.', - 400, - $this->format - ); - - return false; - } - $this->source = $this->trimmed('source'); if (empty($this->source) || in_array($source, $this->reserved_sources)) { @@ -129,6 +112,27 @@ class ApiStatusesUpdateAction extends ApiAuthAction return; } + if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) { + $this->clientError(sprintf(_('The server was unable to handle ' . + 'that much POST data (%s bytes) due to its current configuration.'), + $_SERVER['CONTENT_LENGTH'])); + return; + } + + if (empty($this->user)) { + $this->clientError(_('No such user!'), 404, $this->format); + return; + } + + if (empty($this->status)) { + $this->clientError( + 'Client must provide a \'status\' parameter with a value.', + 400, + $this->format + ); + return; + } + $status_shortened = common_shorten_links($this->status); if (Notice::contentTooLong($status_shortened)) { @@ -187,14 +191,42 @@ class ApiStatusesUpdateAction extends ApiAuthAction } } + $upload = null; + + common_debug('looking for attachment'); + + $upload = MediaFile::fromUpload('media', $this->user); + + common_debug("uploaded file = " . var_export($upload, true)); + + if (isset($upload)) { + common_debug('newNotice: found an upload'); + + $status_shortened .= ' ' . $upload->shortUrl(); + + common_debug('content w/upload = ' . $status_shortened); + + if (Notice::contentTooLong($status_shortened)) { + $upload->delete(); + $this->clientError(sprintf(_('Max notice size is %d chars, including attachment URL.'), + Notice::maxContent())); + } else { + common_debug('content not too long'); + } + } + $this->notice = Notice::saveNew( $this->user->id, - html_entity_decode($this->status, ENT_NOQUOTES, 'UTF-8'), + html_entity_decode($status_shortened, ENT_NOQUOTES, 'UTF-8'), $this->source, 1, $reply_to ); + if (isset($upload)) { + $upload->attachToNotice($this->notice); + } + common_broadcast_notice($this->notice); } diff --git a/lib/mediafile.php b/lib/mediafile.php index be101cdca..31868daec 100644 --- a/lib/mediafile.php +++ b/lib/mediafile.php @@ -31,9 +31,9 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -class MediaFile +class MediaFile { - + var $filename = null; var $fileRecord = null; var $user = null; @@ -46,16 +46,16 @@ class MediaFile if ($user == null) { $this->user = common_current_user(); } - + common_debug('in MediaFile constructor'); - + $this->filename = $filename; $this->mimetype = $mimetype; - + common_debug('storing file'); $this->fileRecord = $this->storeFile(); common_debug('finished storing file'); - + $this->fileurl = common_local_url('attachment', array('attachment' => $this->fileRecord->id)); @@ -67,24 +67,24 @@ class MediaFile common_debug('shortening file url'); $this->short_fileurl = common_shorten_url($this->fileurl); common_debug('shortened file url = ' . $short_fileurl); - + // Also, not sure this is necessary -- Zach $this->maybeAddRedir($this->fileRecord->id, $this->short_fileurl); - + common_debug("MediaFile: end of constructor"); } - + function attachToNotice($notice) { common_debug('MediaFile::attachToNotice() -- doing File_to_post'); File_to_post::processNew($this->fileRecord->id, $notice->id); common_debug('MediaFile done doing File_to_post'); - + $this->maybeAddRedir($this->fileRecord->id, - common_local_url('file', array('notice' => $notice->id))); + common_local_url('file', array('notice' => $notice->id))); } - - function shortUrl() + + function shortUrl() { return $this->short_fileurl; } @@ -115,12 +115,12 @@ class MediaFile $file_id = $file->insert(); if (!$file_id) { - + common_debug("storeFile: problem inserting new file"); common_log_db_error($file, "INSERT", __FILE__); throw new ClientException(_('There was a database error while saving your file. Please try again.')); } - + common_debug('finished storing file'); return $file; @@ -133,15 +133,15 @@ class MediaFile function maybeAddRedir($file_id, $url) { - + common_debug("maybeAddRedir: looking up url: $url for file id $file_id"); $file_redir = File_redirection::staticGet('url', $url); if (empty($file_redir)) { - + common_debug("maybeAddRedir: $url is not in the db"); - + $file_redir = new File_redirection; $file_redir->url = $url; $file_redir->file_id = $file_id; @@ -153,20 +153,24 @@ class MediaFile throw new ClientException(_('There was a database error while saving your file. Please try again.')); } } else { - + common_debug("maybeAddRedir: no need to add $url, it's already in the db"); } } - static function fromUpload($param = 'media') + static function fromUpload($param = 'media', $user = null) { common_debug("fromUpload: param = $param"); - + + if (empty($user)) { + $user = common_current_user(); + } + if (!isset($_FILES[$param]['error'])){ common_debug('no file found'); return; } - + switch ($_FILES[$param]['error']) { case UPLOAD_ERR_OK: // success, jump out break; @@ -183,7 +187,7 @@ class MediaFile @unlink($_FILES[$param]['tmp_name']); throw new ClientException(_('The uploaded file was only' . ' partially uploaded.')); - return; + return; case UPLOAD_ERR_NO_TMP_DIR: throw new ClientException(_('Missing a temporary folder.')); return; @@ -197,24 +201,22 @@ class MediaFile throw new ClientException(_('System error uploading file.')); return; } - - $user = common_current_user(); - + if (!MediaFile::respectsQuota($user, $_FILES['attach']['size'])) { - + // Should never actually get here - + @unlink($_FILES[$param]['tmp_name']); throw new ClientException(_('File exceeds user\'s quota!')); return; } $mimetype = MediaFile::getUploadedFileType($_FILES[$param]['tmp_name']); - + $filename = null; - + if (isset($mimetype)) { - + $basename = basename($_FILES[$param]['name']); $filename = File::filename($user->getProfile(), $basename, $mimetype); $filepath = File::path($filename); @@ -222,44 +224,44 @@ class MediaFile common_debug("filepath = " . $filepath); $result = move_uploaded_file($_FILES[$param]['tmp_name'], $filepath); - - if (!$result) { + + if (!$result) { throw new ClientException(_('File could not be moved to destination directory.')); return; } - + } else { throw new ClientException(_('Could not determine file\'s mime-type!')); return; } - + return new MediaFile($user, $filename, $mimetype); } - static function fromFilehandle($user, $fh) { + static function fromFilehandle($fh, $user) { $stream = stream_get_meta_data($fh); if (MediaFile::respectsQuota($user, filesize($stream['uri']))) { - + // Should never actually get here - + throw new ClientException(_('File exceeds user\'s quota!')); return; } $mimetype = MediaFile::getUploadedFileType($fh); - + $filename = null; - + if (isset($mimetype)) { $filename = File::filename($user->getProfile(), "email", $mimetype); $filepath = File::path($filename); - + $result = copy($stream['uri'], $filepath) && chmod($filepath, 0664); - + if (!$result) { throw new ClientException(_('File could not be moved to destination directory.' . $stream['uri'] . ' ' . $filepath)); @@ -268,33 +270,33 @@ class MediaFile throw new ClientException(_('Could not determine file\'s mime-type!')); return; } - + return new MediaFile($user, $filename, $mimetype); } static function getUploadedFileType($f) { require_once 'MIME/Type.php'; - + common_debug("in getUploadedFileType"); $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); $cmd = common_config('attachments', 'filecommand'); - + $filetype = null; - + if (is_string($f)) { - + // assuming a filename - + $filetype = MIME_Type::autoDetect($f); } else { - + // assuming a filehandle - + $stream = stream_get_meta_data($f); $filetype = MIME_Type::autoDetect($stream['uri']); } - + if (in_array($filetype, common_config('attachments', 'supported'))) { return $filetype; } @@ -308,7 +310,7 @@ class MediaFile _('%s is not a supported filetype on this server.'), $filetype) . $hint); } - static function respectsQuota($user, $filesize) + static function respectsQuota($user, $filesize) { $file = new File; $result = $file->isRespectsQuota($user, $filesize); -- cgit v1.2.3-54-g00ecf From 10df75f9a074f2987b3525cd70a129bd26972ebb Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 28 Oct 2009 16:07:57 -0400 Subject: add StartInitializeRouter event --- EVENTS.txt | 3 + lib/router.php | 948 +++++++++++++++++++++++++++++---------------------------- 2 files changed, 478 insertions(+), 473 deletions(-) (limited to 'lib') diff --git a/EVENTS.txt b/EVENTS.txt index 5d34a9e13..d989557e6 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -129,6 +129,9 @@ StartSubGroupNav: Showing the subscriptions group nav menu EndSubGroupNav: At the end of the subscriptions group nav menu - $action: the current action +StartInitializeRouter: Before the router instance has been initialized; good place to add routes +- $m: the Net_URL_Mapper that has just been set up + RouterInitialized: After the router instance has been initialized - $m: the Net_URL_Mapper that has just been set up diff --git a/lib/router.php b/lib/router.php index 4fb0834fd..dedf73c86 100644 --- a/lib/router.php +++ b/lib/router.php @@ -71,562 +71,564 @@ class Router { $m = Net_URL_Mapper::getInstance(); - // In the "root" + if (Event::handle('StartInitializeRouter', array(&$m))) { - $m->connect('', array('action' => 'public')); - $m->connect('rss', array('action' => 'publicrss')); - $m->connect('featuredrss', array('action' => 'featuredrss')); - $m->connect('favoritedrss', array('action' => 'favoritedrss')); - $m->connect('opensearch/people', array('action' => 'opensearch', - 'type' => 'people')); - $m->connect('opensearch/notice', array('action' => 'opensearch', - 'type' => 'notice')); + // In the "root" - // docs + $m->connect('', array('action' => 'public')); + $m->connect('rss', array('action' => 'publicrss')); + $m->connect('featuredrss', array('action' => 'featuredrss')); + $m->connect('favoritedrss', array('action' => 'favoritedrss')); + $m->connect('opensearch/people', array('action' => 'opensearch', + 'type' => 'people')); + $m->connect('opensearch/notice', array('action' => 'opensearch', + 'type' => 'notice')); - $m->connect('doc/:title', array('action' => 'doc')); + // docs - // main stuff is repetitive + $m->connect('doc/:title', array('action' => 'doc')); - $main = array('login', 'logout', 'register', 'subscribe', - 'unsubscribe', 'confirmaddress', 'recoverpassword', - 'invite', 'favor', 'disfavor', 'sup', - 'block', 'unblock', 'subedit', - 'groupblock', 'groupunblock'); + // main stuff is repetitive - foreach ($main as $a) { - $m->connect('main/'.$a, array('action' => $a)); - } + $main = array('login', 'logout', 'register', 'subscribe', + 'unsubscribe', 'confirmaddress', 'recoverpassword', + 'invite', 'favor', 'disfavor', 'sup', + 'block', 'unblock', 'subedit', + 'groupblock', 'groupunblock'); - $m->connect('main/sup/:seconds', array('action' => 'sup'), - array('seconds' => '[0-9]+')); + foreach ($main as $a) { + $m->connect('main/'.$a, array('action' => $a)); + } - $m->connect('main/tagother/:id', array('action' => 'tagother')); + $m->connect('main/sup/:seconds', array('action' => 'sup'), + array('seconds' => '[0-9]+')); - $m->connect('main/oembed', - array('action' => 'oembed')); + $m->connect('main/tagother/:id', array('action' => 'tagother')); - // these take a code + $m->connect('main/oembed', + array('action' => 'oembed')); - foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) { - $m->connect('main/'.$c.'/:code', array('action' => $c)); - } + // these take a code - // exceptional + foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) { + $m->connect('main/'.$c.'/:code', array('action' => $c)); + } - $m->connect('main/remote', array('action' => 'remotesubscribe')); - $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+')); + // exceptional - foreach (Router::$bare as $action) { - $m->connect('index.php?action=' . $action, array('action' => $action)); - } + $m->connect('main/remote', array('action' => 'remotesubscribe')); + $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+')); - // settings + foreach (Router::$bare as $action) { + $m->connect('index.php?action=' . $action, array('action' => $action)); + } - foreach (array('profile', 'avatar', 'password', 'im', - 'email', 'sms', 'userdesign', 'other') as $s) { - $m->connect('settings/'.$s, array('action' => $s.'settings')); - } + // settings + + foreach (array('profile', 'avatar', 'password', 'im', + 'email', 'sms', 'userdesign', 'other') as $s) { + $m->connect('settings/'.$s, array('action' => $s.'settings')); + } - // search + // search - foreach (array('group', 'people', 'notice') as $s) { - $m->connect('search/'.$s, array('action' => $s.'search')); - $m->connect('search/'.$s.'?q=:q', - array('action' => $s.'search'), + foreach (array('group', 'people', 'notice') as $s) { + $m->connect('search/'.$s, array('action' => $s.'search')); + $m->connect('search/'.$s.'?q=:q', + array('action' => $s.'search'), + array('q' => '.+')); + } + + // The second of these is needed to make the link work correctly + // when inserted into the page. The first is needed to match the + // route on the way in. Seems to be another Net_URL_Mapper bug to me. + $m->connect('search/notice/rss', array('action' => 'noticesearchrss')); + $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'), array('q' => '.+')); - } - // The second of these is needed to make the link work correctly - // when inserted into the page. The first is needed to match the - // route on the way in. Seems to be another Net_URL_Mapper bug to me. - $m->connect('search/notice/rss', array('action' => 'noticesearchrss')); - $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'), - array('q' => '.+')); - - $m->connect('attachment/:attachment', - array('action' => 'attachment'), - array('attachment' => '[0-9]+')); - - $m->connect('attachment/:attachment/ajax', - array('action' => 'attachment_ajax'), - array('attachment' => '[0-9]+')); - - $m->connect('attachment/:attachment/thumbnail', - array('action' => 'attachment_thumbnail'), - array('attachment' => '[0-9]+')); - - $m->connect('notice/new', array('action' => 'newnotice')); - $m->connect('notice/new?replyto=:replyto', - array('action' => 'newnotice'), - array('replyto' => '[A-Za-z0-9_-]+')); - $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto', - array('action' => 'newnotice'), - array('replyto' => '[A-Za-z0-9_-]+'), - array('inreplyto' => '[0-9]+')); - - $m->connect('notice/:notice/file', - array('action' => 'file'), - array('notice' => '[0-9]+')); - - $m->connect('notice/:notice', - array('action' => 'shownotice'), - array('notice' => '[0-9]+')); - $m->connect('notice/delete', array('action' => 'deletenotice')); - $m->connect('notice/delete/:notice', - array('action' => 'deletenotice'), - array('notice' => '[0-9]+')); - - // conversation - - $m->connect('conversation/:id', - array('action' => 'conversation'), - array('id' => '[0-9]+')); - - $m->connect('message/new', array('action' => 'newmessage')); - $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '[A-Za-z0-9_-]+')); - $m->connect('message/:message', - array('action' => 'showmessage'), - array('message' => '[0-9]+')); - - $m->connect('user/:id', - array('action' => 'userbyid'), - array('id' => '[0-9]+')); - - $m->connect('tags/', array('action' => 'publictagcloud')); - $m->connect('tag/', array('action' => 'publictagcloud')); - $m->connect('tags', array('action' => 'publictagcloud')); - $m->connect('tag', array('action' => 'publictagcloud')); - $m->connect('tag/:tag/rss', - array('action' => 'tagrss'), - array('tag' => '[a-zA-Z0-9]+')); - $m->connect('tag/:tag', - array('action' => 'tag'), - array('tag' => '[\pL\pN_\-\.]{1,64}')); - - $m->connect('peopletag/:tag', - array('action' => 'peopletag'), - array('tag' => '[a-zA-Z0-9]+')); - - $m->connect('featured/', array('action' => 'featured')); - $m->connect('featured', array('action' => 'featured')); - $m->connect('favorited/', array('action' => 'favorited')); - $m->connect('favorited', array('action' => 'favorited')); - - // groups - - $m->connect('group/new', array('action' => 'newgroup')); - - foreach (array('edit', 'join', 'leave') as $v) { - $m->connect('group/:nickname/'.$v, - array('action' => $v.'group'), + $m->connect('attachment/:attachment', + array('action' => 'attachment'), + array('attachment' => '[0-9]+')); + + $m->connect('attachment/:attachment/ajax', + array('action' => 'attachment_ajax'), + array('attachment' => '[0-9]+')); + + $m->connect('attachment/:attachment/thumbnail', + array('action' => 'attachment_thumbnail'), + array('attachment' => '[0-9]+')); + + $m->connect('notice/new', array('action' => 'newnotice')); + $m->connect('notice/new?replyto=:replyto', + array('action' => 'newnotice'), + array('replyto' => '[A-Za-z0-9_-]+')); + $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto', + array('action' => 'newnotice'), + array('replyto' => '[A-Za-z0-9_-]+'), + array('inreplyto' => '[0-9]+')); + + $m->connect('notice/:notice/file', + array('action' => 'file'), + array('notice' => '[0-9]+')); + + $m->connect('notice/:notice', + array('action' => 'shownotice'), + array('notice' => '[0-9]+')); + $m->connect('notice/delete', array('action' => 'deletenotice')); + $m->connect('notice/delete/:notice', + array('action' => 'deletenotice'), + array('notice' => '[0-9]+')); + + // conversation + + $m->connect('conversation/:id', + array('action' => 'conversation'), + array('id' => '[0-9]+')); + + $m->connect('message/new', array('action' => 'newmessage')); + $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '[A-Za-z0-9_-]+')); + $m->connect('message/:message', + array('action' => 'showmessage'), + array('message' => '[0-9]+')); + + $m->connect('user/:id', + array('action' => 'userbyid'), + array('id' => '[0-9]+')); + + $m->connect('tags/', array('action' => 'publictagcloud')); + $m->connect('tag/', array('action' => 'publictagcloud')); + $m->connect('tags', array('action' => 'publictagcloud')); + $m->connect('tag', array('action' => 'publictagcloud')); + $m->connect('tag/:tag/rss', + array('action' => 'tagrss'), + array('tag' => '[a-zA-Z0-9]+')); + $m->connect('tag/:tag', + array('action' => 'tag'), + array('tag' => '[\pL\pN_\-\.]{1,64}')); + + $m->connect('peopletag/:tag', + array('action' => 'peopletag'), + array('tag' => '[a-zA-Z0-9]+')); + + $m->connect('featured/', array('action' => 'featured')); + $m->connect('featured', array('action' => 'featured')); + $m->connect('favorited/', array('action' => 'favorited')); + $m->connect('favorited', array('action' => 'favorited')); + + // groups + + $m->connect('group/new', array('action' => 'newgroup')); + + foreach (array('edit', 'join', 'leave') as $v) { + $m->connect('group/:nickname/'.$v, + array('action' => $v.'group'), + array('nickname' => '[a-zA-Z0-9]+')); + } + + foreach (array('members', 'logo', 'rss', 'designsettings') as $n) { + $m->connect('group/:nickname/'.$n, + array('action' => 'group'.$n), + array('nickname' => '[a-zA-Z0-9]+')); + } + + $m->connect('group/:nickname/foaf', + array('action' => 'foafgroup'), array('nickname' => '[a-zA-Z0-9]+')); - } - foreach (array('members', 'logo', 'rss', 'designsettings') as $n) { - $m->connect('group/:nickname/'.$n, - array('action' => 'group'.$n), + $m->connect('group/:nickname/blocked', + array('action' => 'blockedfromgroup'), array('nickname' => '[a-zA-Z0-9]+')); - } - - $m->connect('group/:nickname/foaf', - array('action' => 'foafgroup'), - array('nickname' => '[a-zA-Z0-9]+')); - - $m->connect('group/:nickname/blocked', - array('action' => 'blockedfromgroup'), - array('nickname' => '[a-zA-Z0-9]+')); - - $m->connect('group/:nickname/makeadmin', - array('action' => 'makeadmin'), - array('nickname' => '[a-zA-Z0-9]+')); - - $m->connect('group/:id/id', - array('action' => 'groupbyid'), - array('id' => '[0-9]+')); - - $m->connect('group/:nickname', - array('action' => 'showgroup'), - array('nickname' => '[a-zA-Z0-9]+')); - - $m->connect('group/', array('action' => 'groups')); - $m->connect('group', array('action' => 'groups')); - $m->connect('groups/', array('action' => 'groups')); - $m->connect('groups', array('action' => 'groups')); - - // Twitter-compatible API - - // statuses API - - $m->connect('api/statuses/public_timeline.:format', - array('action' => 'ApiTimelinePublic', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/friends_timeline.:format', - array('action' => 'ApiTimelineFriends', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/friends_timeline/:id.:format', - array('action' => 'ApiTimelineFriends', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - $m->connect('api/statuses/home_timeline.:format', - array('action' => 'ApiTimelineFriends', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/home_timeline/:id.:format', - array('action' => 'ApiTimelineFriends', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/user_timeline.:format', - array('action' => 'ApiTimelineUser', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/user_timeline/:id.:format', - array('action' => 'ApiTimelineUser', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/mentions.:format', - array('action' => 'ApiTimelineMentions', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/mentions/:id.:format', - array('action' => 'ApiTimelineMentions', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - $m->connect('api/statuses/replies.:format', - array('action' => 'ApiTimelineMentions', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/replies/:id.:format', - array('action' => 'ApiTimelineMentions', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/friends.:format', - array('action' => 'ApiUserFriends', - 'format' => '(xml|json)')); - - $m->connect('api/statuses/friends/:id.:format', - array('action' => 'ApiUserFriends', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('group/:nickname/makeadmin', + array('action' => 'makeadmin'), + array('nickname' => '[a-zA-Z0-9]+')); - $m->connect('api/statuses/followers.:format', - array('action' => 'ApiUserFollowers', - 'format' => '(xml|json)')); + $m->connect('group/:id/id', + array('action' => 'groupbyid'), + array('id' => '[0-9]+')); - $m->connect('api/statuses/followers/:id.:format', - array('action' => 'ApiUserFollowers', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('group/:nickname', + array('action' => 'showgroup'), + array('nickname' => '[a-zA-Z0-9]+')); - $m->connect('api/statuses/show.:format', - array('action' => 'ApiStatusesShow', - 'format' => '(xml|json)')); + $m->connect('group/', array('action' => 'groups')); + $m->connect('group', array('action' => 'groups')); + $m->connect('groups/', array('action' => 'groups')); + $m->connect('groups', array('action' => 'groups')); + + // Twitter-compatible API + + // statuses API + + $m->connect('api/statuses/public_timeline.:format', + array('action' => 'ApiTimelinePublic', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/friends_timeline.:format', + array('action' => 'ApiTimelineFriends', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/friends_timeline/:id.:format', + array('action' => 'ApiTimelineFriends', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + $m->connect('api/statuses/home_timeline.:format', + array('action' => 'ApiTimelineFriends', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/home_timeline/:id.:format', + array('action' => 'ApiTimelineFriends', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); - $m->connect('api/statuses/show/:id.:format', - array('action' => 'ApiStatusesShow', - 'id' => '[0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/statuses/user_timeline.:format', + array('action' => 'ApiTimelineUser', + 'format' => '(xml|json|rss|atom)')); - $m->connect('api/statuses/update.:format', - array('action' => 'ApiStatusesUpdate', - 'format' => '(xml|json)')); + $m->connect('api/statuses/user_timeline/:id.:format', + array('action' => 'ApiTimelineUser', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/mentions.:format', + array('action' => 'ApiTimelineMentions', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/mentions/:id.:format', + array('action' => 'ApiTimelineMentions', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/replies.:format', + array('action' => 'ApiTimelineMentions', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/replies/:id.:format', + array('action' => 'ApiTimelineMentions', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/friends.:format', + array('action' => 'ApiUserFriends', + 'format' => '(xml|json)')); + + $m->connect('api/statuses/friends/:id.:format', + array('action' => 'ApiUserFriends', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/statuses/destroy.:format', - array('action' => 'ApiStatusesDestroy', - 'format' => '(xml|json)')); + $m->connect('api/statuses/followers.:format', + array('action' => 'ApiUserFollowers', + 'format' => '(xml|json)')); - $m->connect('api/statuses/destroy/:id.:format', - array('action' => 'ApiStatusesDestroy', - 'id' => '[0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/statuses/followers/:id.:format', + array('action' => 'ApiUserFollowers', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - // users + $m->connect('api/statuses/show.:format', + array('action' => 'ApiStatusesShow', + 'format' => '(xml|json)')); - $m->connect('api/users/show/:id.:format', - array('action' => 'ApiUserShow', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/statuses/show/:id.:format', + array('action' => 'ApiStatusesShow', + 'id' => '[0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/users/:method', - array('action' => 'api', - 'apiaction' => 'users'), - array('method' => 'show(\.(xml|json))?')); + $m->connect('api/statuses/update.:format', + array('action' => 'ApiStatusesUpdate', + 'format' => '(xml|json)')); - // direct messages + $m->connect('api/statuses/destroy.:format', + array('action' => 'ApiStatusesDestroy', + 'format' => '(xml|json)')); + $m->connect('api/statuses/destroy/:id.:format', + array('action' => 'ApiStatusesDestroy', + 'id' => '[0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/direct_messages.:format', - array('action' => 'ApiDirectMessage', - 'format' => '(xml|json|rss|atom)')); + // users - $m->connect('api/direct_messages/sent.:format', - array('action' => 'ApiDirectMessage', - 'format' => '(xml|json|rss|atom)', - 'sent' => true)); + $m->connect('api/users/show/:id.:format', + array('action' => 'ApiUserShow', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/direct_messages/new.:format', - array('action' => 'ApiDirectMessageNew', - 'format' => '(xml|json)')); + $m->connect('api/users/:method', + array('action' => 'api', + 'apiaction' => 'users'), + array('method' => 'show(\.(xml|json))?')); - // friendships + // direct messages - $m->connect('api/friendships/show.:format', - array('action' => 'ApiFriendshipsShow', - 'format' => '(xml|json)')); + $m->connect('api/direct_messages.:format', + array('action' => 'ApiDirectMessage', + 'format' => '(xml|json|rss|atom)')); - $m->connect('api/friendships/exists.:format', - array('action' => 'ApiFriendshipsExists', - 'format' => '(xml|json)')); + $m->connect('api/direct_messages/sent.:format', + array('action' => 'ApiDirectMessage', + 'format' => '(xml|json|rss|atom)', + 'sent' => true)); - $m->connect('api/friendships/create.:format', - array('action' => 'ApiFriendshipsCreate', - 'format' => '(xml|json)')); + $m->connect('api/direct_messages/new.:format', + array('action' => 'ApiDirectMessageNew', + 'format' => '(xml|json)')); - $m->connect('api/friendships/destroy.:format', - array('action' => 'ApiFriendshipsDestroy', - 'format' => '(xml|json)')); + // friendships - $m->connect('api/friendships/create/:id.:format', - array('action' => 'ApiFriendshipsCreate', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/friendships/show.:format', + array('action' => 'ApiFriendshipsShow', + 'format' => '(xml|json)')); - $m->connect('api/friendships/destroy/:id.:format', - array('action' => 'ApiFriendshipsDestroy', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/friendships/exists.:format', + array('action' => 'ApiFriendshipsExists', + 'format' => '(xml|json)')); - // Social graph + $m->connect('api/friendships/create.:format', + array('action' => 'ApiFriendshipsCreate', + 'format' => '(xml|json)')); - $m->connect('api/friends/ids/:id.:format', - array('action' => 'apiFriends', - 'ids_only' => true)); + $m->connect('api/friendships/destroy.:format', + array('action' => 'ApiFriendshipsDestroy', + 'format' => '(xml|json)')); - $m->connect('api/followers/ids/:id.:format', - array('action' => 'apiFollowers', - 'ids_only' => true)); + $m->connect('api/friendships/create/:id.:format', + array('action' => 'ApiFriendshipsCreate', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/friends/ids.:format', - array('action' => 'apiFriends', - 'ids_only' => true)); + $m->connect('api/friendships/destroy/:id.:format', + array('action' => 'ApiFriendshipsDestroy', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/followers/ids.:format', - array('action' => 'apiFollowers', - 'ids_only' => true)); + // Social graph - // account + $m->connect('api/friends/ids/:id.:format', + array('action' => 'apiFriends', + 'ids_only' => true)); - $m->connect('api/account/verify_credentials.:format', - array('action' => 'ApiAccountVerifyCredentials')); + $m->connect('api/followers/ids/:id.:format', + array('action' => 'apiFollowers', + 'ids_only' => true)); - // special case where verify_credentials is called w/out a format + $m->connect('api/friends/ids.:format', + array('action' => 'apiFriends', + 'ids_only' => true)); - $m->connect('api/account/verify_credentials', - array('action' => 'ApiAccountVerifyCredentials')); + $m->connect('api/followers/ids.:format', + array('action' => 'apiFollowers', + 'ids_only' => true)); - $m->connect('api/account/rate_limit_status.:format', - array('action' => 'ApiAccountRateLimitStatus')); + // account - // favorites + $m->connect('api/account/verify_credentials.:format', + array('action' => 'ApiAccountVerifyCredentials')); - $m->connect('api/favorites.:format', - array('action' => 'ApiTimelineFavorites', - 'format' => '(xml|json|rss|atom)')); + // special case where verify_credentials is called w/out a format - $m->connect('api/favorites/:id.:format', - array('action' => 'ApiTimelineFavorites', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xmljson|rss|atom)')); + $m->connect('api/account/verify_credentials', + array('action' => 'ApiAccountVerifyCredentials')); - $m->connect('api/favorites/create/:id.:format', - array('action' => 'ApiFavoriteCreate', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/account/rate_limit_status.:format', + array('action' => 'ApiAccountRateLimitStatus')); - $m->connect('api/favorites/destroy/:id.:format', - array('action' => 'ApiFavoriteDestroy', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + // favorites - // notifications + $m->connect('api/favorites.:format', + array('action' => 'ApiTimelineFavorites', + 'format' => '(xml|json|rss|atom)')); - $m->connect('api/notifications/:method/:argument', - array('action' => 'api', - 'apiaction' => 'favorites')); + $m->connect('api/favorites/:id.:format', + array('action' => 'ApiTimelineFavorites', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xmljson|rss|atom)')); - // blocks + $m->connect('api/favorites/create/:id.:format', + array('action' => 'ApiFavoriteCreate', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/blocks/create/:id.:format', - array('action' => 'ApiBlockCreate', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/favorites/destroy/:id.:format', + array('action' => 'ApiFavoriteDestroy', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/blocks/destroy/:id.:format', - array('action' => 'ApiBlockDestroy', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - // help + // notifications - $m->connect('api/help/test.:format', - array('action' => 'ApiHelpTest', - 'format' => '(xml|json)')); + $m->connect('api/notifications/:method/:argument', + array('action' => 'api', + 'apiaction' => 'favorites')); - // statusnet + // blocks - $m->connect('api/statusnet/version.:format', - array('action' => 'ApiStatusnetVersion', - 'format' => '(xml|json)')); + $m->connect('api/blocks/create/:id.:format', + array('action' => 'ApiBlockCreate', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/statusnet/config.:format', - array('action' => 'ApiStatusnetConfig', - 'format' => '(xml|json)')); + $m->connect('api/blocks/destroy/:id.:format', + array('action' => 'ApiBlockDestroy', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + // help - // For older methods, we provide "laconica" base action - - $m->connect('api/laconica/version.:format', - array('action' => 'ApiStatusnetVersion', - 'format' => '(xml|json)')); - - $m->connect('api/laconica/config.:format', - array('action' => 'ApiStatusnetConfig', - 'format' => '(xml|json)')); + $m->connect('api/help/test.:format', + array('action' => 'ApiHelpTest', + 'format' => '(xml|json)')); - // Groups and tags are newer than 0.8.1 so no backward-compatibility - // necessary + // statusnet - // Groups - //'list' has to be handled differently, as php will not allow a method to be named 'list' + $m->connect('api/statusnet/version.:format', + array('action' => 'ApiStatusnetVersion', + 'format' => '(xml|json)')); - $m->connect('api/statusnet/groups/timeline/:id.:format', - array('action' => 'ApiTimelineGroup', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xmljson|rss|atom)')); + $m->connect('api/statusnet/config.:format', + array('action' => 'ApiStatusnetConfig', + 'format' => '(xml|json)')); - $m->connect('api/statusnet/groups/show.:format', - array('action' => 'ApiGroupShow', - 'format' => '(xml|json)')); + // For older methods, we provide "laconica" base action + + $m->connect('api/laconica/version.:format', + array('action' => 'ApiStatusnetVersion', + 'format' => '(xml|json)')); + + $m->connect('api/laconica/config.:format', + array('action' => 'ApiStatusnetConfig', + 'format' => '(xml|json)')); - $m->connect('api/statusnet/groups/show/:id.:format', - array('action' => 'ApiGroupShow', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/join.:format', - array('action' => 'ApiGroupJoin', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/join/:id.:format', - array('action' => 'ApiGroupJoin', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/leave.:format', - array('action' => 'ApiGroupLeave', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/leave/:id.:format', - array('action' => 'ApiGroupLeave', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/is_member.:format', - array('action' => 'ApiGroupIsMember', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/list.:format', - array('action' => 'ApiGroupList', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statusnet/groups/list/:id.:format', - array('action' => 'ApiGroupList', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statusnet/groups/list_all.:format', - array('action' => 'ApiGroupListAll', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statusnet/groups/membership.:format', - array('action' => 'ApiGroupMembership', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/membership/:id.:format', - array('action' => 'ApiGroupMembership', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/create.:format', - array('action' => 'ApiGroupCreate', - 'format' => '(xml|json)')); - // Tags - $m->connect('api/statusnet/tags/timeline/:tag.:format', - array('action' => 'ApiTimelineTag', - 'format' => '(xmljson|rss|atom)')); - - // search - $m->connect('api/search.atom', array('action' => 'twitapisearchatom')); - $m->connect('api/search.json', array('action' => 'twitapisearchjson')); - $m->connect('api/trends.json', array('action' => 'twitapitrends')); - - // user stuff - - foreach (array('subscriptions', 'subscribers', - 'nudge', 'all', 'foaf', 'xrds', - 'replies', 'inbox', 'outbox', 'microsummary') as $a) { - $m->connect(':nickname/'.$a, - array('action' => $a), + // Groups and tags are newer than 0.8.1 so no backward-compatibility + // necessary + + // Groups + //'list' has to be handled differently, as php will not allow a method to be named 'list' + + $m->connect('api/statusnet/groups/timeline/:id.:format', + array('action' => 'ApiTimelineGroup', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xmljson|rss|atom)')); + + $m->connect('api/statusnet/groups/show.:format', + array('action' => 'ApiGroupShow', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/show/:id.:format', + array('action' => 'ApiGroupShow', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/join.:format', + array('action' => 'ApiGroupJoin', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/join/:id.:format', + array('action' => 'ApiGroupJoin', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/leave.:format', + array('action' => 'ApiGroupLeave', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/leave/:id.:format', + array('action' => 'ApiGroupLeave', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/is_member.:format', + array('action' => 'ApiGroupIsMember', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/list.:format', + array('action' => 'ApiGroupList', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statusnet/groups/list/:id.:format', + array('action' => 'ApiGroupList', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statusnet/groups/list_all.:format', + array('action' => 'ApiGroupListAll', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statusnet/groups/membership.:format', + array('action' => 'ApiGroupMembership', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/membership/:id.:format', + array('action' => 'ApiGroupMembership', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/create.:format', + array('action' => 'ApiGroupCreate', + 'format' => '(xml|json)')); + // Tags + $m->connect('api/statusnet/tags/timeline/:tag.:format', + array('action' => 'ApiTimelineTag', + 'format' => '(xmljson|rss|atom)')); + + // search + $m->connect('api/search.atom', array('action' => 'twitapisearchatom')); + $m->connect('api/search.json', array('action' => 'twitapisearchjson')); + $m->connect('api/trends.json', array('action' => 'twitapitrends')); + + // user stuff + + foreach (array('subscriptions', 'subscribers', + 'nudge', 'all', 'foaf', 'xrds', + 'replies', 'inbox', 'outbox', 'microsummary') as $a) { + $m->connect(':nickname/'.$a, + array('action' => $a), + array('nickname' => '[a-zA-Z0-9]{1,64}')); + } + + foreach (array('subscriptions', 'subscribers') as $a) { + $m->connect(':nickname/'.$a.'/:tag', + array('action' => $a), + array('tag' => '[a-zA-Z0-9]+', + 'nickname' => '[a-zA-Z0-9]{1,64}')); + } + + foreach (array('rss', 'groups') as $a) { + $m->connect(':nickname/'.$a, + array('action' => 'user'.$a), + array('nickname' => '[a-zA-Z0-9]{1,64}')); + } + + foreach (array('all', 'replies', 'favorites') as $a) { + $m->connect(':nickname/'.$a.'/rss', + array('action' => $a.'rss'), + array('nickname' => '[a-zA-Z0-9]{1,64}')); + } + + $m->connect(':nickname/favorites', + array('action' => 'showfavorites'), array('nickname' => '[a-zA-Z0-9]{1,64}')); - } - foreach (array('subscriptions', 'subscribers') as $a) { - $m->connect(':nickname/'.$a.'/:tag', - array('action' => $a), - array('tag' => '[a-zA-Z0-9]+', + $m->connect(':nickname/avatar/:size', + array('action' => 'avatarbynickname'), + array('size' => '(original|96|48|24)', 'nickname' => '[a-zA-Z0-9]{1,64}')); - } - - foreach (array('rss', 'groups') as $a) { - $m->connect(':nickname/'.$a, - array('action' => 'user'.$a), - array('nickname' => '[a-zA-Z0-9]{1,64}')); - } - - foreach (array('all', 'replies', 'favorites') as $a) { - $m->connect(':nickname/'.$a.'/rss', - array('action' => $a.'rss'), - array('nickname' => '[a-zA-Z0-9]{1,64}')); - } - $m->connect(':nickname/favorites', - array('action' => 'showfavorites'), - array('nickname' => '[a-zA-Z0-9]{1,64}')); + $m->connect(':nickname/tag/:tag/rss', + array('action' => 'userrss'), + array('nickname' => '[a-zA-Z0-9]{1,64}'), + array('tag' => '[a-zA-Z0-9]+')); - $m->connect(':nickname/avatar/:size', - array('action' => 'avatarbynickname'), - array('size' => '(original|96|48|24)', - 'nickname' => '[a-zA-Z0-9]{1,64}')); + $m->connect(':nickname/tag/:tag', + array('action' => 'showstream'), + array('nickname' => '[a-zA-Z0-9]{1,64}'), + array('tag' => '[a-zA-Z0-9]+')); - $m->connect(':nickname/tag/:tag/rss', - array('action' => 'userrss'), - array('nickname' => '[a-zA-Z0-9]{1,64}'), - array('tag' => '[a-zA-Z0-9]+')); - - $m->connect(':nickname/tag/:tag', - array('action' => 'showstream'), - array('nickname' => '[a-zA-Z0-9]{1,64}'), - array('tag' => '[a-zA-Z0-9]+')); - - $m->connect(':nickname', - array('action' => 'showstream'), - array('nickname' => '[a-zA-Z0-9]{1,64}')); + $m->connect(':nickname', + array('action' => 'showstream'), + array('nickname' => '[a-zA-Z0-9]{1,64}')); - Event::handle('RouterInitialized', array($m)); + Event::handle('RouterInitialized', array($m)); + } return $m; } -- cgit v1.2.3-54-g00ecf From 764770388138135550be22a08feb0485987e430a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 28 Oct 2009 17:28:00 -0400 Subject: rogue n in curlclient.php --- lib/curlclient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/curlclient.php b/lib/curlclient.php index 36fc7d157..c307c2984 100644 --- a/lib/curlclient.php +++ b/lib/curlclient.php @@ -1,4 +1,4 @@ -n Date: Wed, 28 Oct 2009 22:24:20 +0000 Subject: Rework MailDaemon to use the MediaFile class for uploads --- lib/mediafile.php | 2 +- scripts/maildaemon.php | 140 ++++++------------------------------------------- 2 files changed, 18 insertions(+), 124 deletions(-) (limited to 'lib') diff --git a/lib/mediafile.php b/lib/mediafile.php index 31868daec..4aa8211c3 100644 --- a/lib/mediafile.php +++ b/lib/mediafile.php @@ -242,7 +242,7 @@ class MediaFile $stream = stream_get_meta_data($fh); - if (MediaFile::respectsQuota($user, filesize($stream['uri']))) { + if (!MediaFile::respectsQuota($user, filesize($stream['uri']))) { // Should never actually get here diff --git a/scripts/maildaemon.php b/scripts/maildaemon.php index 84dff8912..b4e4d9f08 100755 --- a/scripts/maildaemon.php +++ b/scripts/maildaemon.php @@ -29,6 +29,7 @@ END_OF_HELP; require_once INSTALLDIR.'/scripts/commandline.inc'; require_once(INSTALLDIR . '/lib/mail.php'); +require_once(INSTALLDIR . '/lib/mediafile.php'); require_once('Mail/mimeDecode.php'); # FIXME: we use both Mail_mimeDecode and mailparse @@ -71,43 +72,27 @@ class MailerDaemon 'Max notice size is %d chars.'), Notice::maxContent())); } - $fileRecords = array(); - foreach($attachments as $attachment){ - $mimetype = $this->getUploadedFileType($attachment); - $stream = stream_get_meta_data($attachment); - if (!$this->isRespectsQuota($user,filesize($stream['uri']))) { - die('error() should trigger an exception before reaching here.'); - } - $filename = $this->saveFile($user, $attachment,$mimetype); - - fclose($attachment); - - if (empty($filename)) { - $this->error($from,_('Couldn\'t save file.')); - } - $fileRecord = $this->storeFile($filename, $mimetype); - $fileRecords[] = $fileRecord; - $fileurl = common_local_url('attachment', - array('attachment' => $fileRecord->id)); + $mediafiles = array(); - // not sure this is necessary -- Zach - $this->maybeAddRedir($fileRecord->id, $fileurl); + foreach($attachments as $attachment){ - $short_fileurl = common_shorten_url($fileurl); - $msg .= ' ' . $short_fileurl; + $mf = null; - if (Notice::contentTooLong($msg)) { - $this->deleteFile($filename); - $this->error($from, sprintf(_('Max notice size is %d chars, including attachment URL.'), - Notice::maxContent())); + try { + $mf = MediaFile::fromFileHandle($attachment, $user); + } catch(ClientException $ce) { + $this->error($from, $ce->getMessage()); } - // Also, not sure this is necessary -- Zach - $this->maybeAddRedir($fileRecord->id, $short_fileurl); + $msg .= ' ' . $mf->shortUrl(); + + array_push($mediafiles, $mf); + fclose($attachment); } - $err = $this->add_notice($user, $msg, $fileRecords); + $err = $this->add_notice($user, $msg, $mediafiles); + if (is_string($err)) { $this->error($from, $err); return false; @@ -116,89 +101,6 @@ class MailerDaemon } } - function saveFile($user, $attachment, $mimetype) { - - $filename = File::filename($user->getProfile(), "email", $mimetype); - - $filepath = File::path($filename); - - $stream = stream_get_meta_data($attachment); - if (copy($stream['uri'], $filepath) && chmod($filepath,0664)) { - return $filename; - } else { - $this->error(null,_('File could not be moved to destination directory.' . $stream['uri'] . ' ' . $filepath)); - } - } - - function storeFile($filename, $mimetype) { - - $file = new File; - $file->filename = $filename; - - $file->url = File::url($filename); - - $filepath = File::path($filename); - - $file->size = filesize($filepath); - $file->date = time(); - $file->mimetype = $mimetype; - - $file_id = $file->insert(); - - if (!$file_id) { - common_log_db_error($file, "INSERT", __FILE__); - $this->error(null,_('There was a database error while saving your file. Please try again.')); - } - - return $file; - } - - function maybeAddRedir($file_id, $url) - { - $file_redir = File_redirection::staticGet('url', $url); - - if (empty($file_redir)) { - $file_redir = new File_redirection; - $file_redir->url = $url; - $file_redir->file_id = $file_id; - - $result = $file_redir->insert(); - - if (!$result) { - common_log_db_error($file_redir, "INSERT", __FILE__); - $this->error(null,_('There was a database error while saving your file. Please try again.')); - } - } - } - - function getUploadedFileType($fileHandle) { - require_once 'MIME/Type.php'; - - $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); - $cmd = common_config('attachments', 'filecommand'); - - $stream = stream_get_meta_data($fileHandle); - $filetype = MIME_Type::autoDetect($stream['uri']); - if (in_array($filetype, common_config('attachments', 'supported'))) { - return $filetype; - } - $media = MIME_Type::getMedia($filetype); - if ('application' !== $media) { - $hint = sprintf(_(' Try using another %s format.'), $media); - } else { - $hint = ''; - } - $this->error(null,sprintf( - _('%s is not a supported filetype on this server.'), $filetype) . $hint); - } - - function isRespectsQuota($user,$fileSize) { - $file = new File; - $ret = $file->isRespectsQuota($user,$fileSize); - if (true === $ret) return true; - $this->error(null,$ret); - } - function error($from, $msg) { file_put_contents("php://stderr", $msg . "\n"); @@ -258,7 +160,7 @@ class MailerDaemon common_log($level, 'MailDaemon: '.$msg); } - function add_notice($user, $msg, $fileRecords) + function add_notice($user, $msg, $mediafiles) { try { $notice = Notice::saveNew($user->id, $msg, 'mail'); @@ -266,8 +168,8 @@ class MailerDaemon $this->log(LOG_ERR, $e->getMessage()); return $e->getMessage(); } - foreach($fileRecords as $fileRecord){ - $this->attachFile($notice, $fileRecord); + foreach($mediafiles as $mf){ + $mf->attachToNotice($notice); } common_broadcast_notice($notice); $this->log(LOG_INFO, @@ -275,14 +177,6 @@ class MailerDaemon return true; } - function attachFile($notice, $filerec) - { - File_to_post::processNew($filerec->id, $notice->id); - - $this->maybeAddRedir($filerec->id, - common_local_url('file', array('notice' => $notice->id))); - } - function parse_message($fname) { $contents = file_get_contents($fname); -- cgit v1.2.3-54-g00ecf From e5a2f895a074a6eaaf8184f101503b1520ed780b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 28 Oct 2009 17:12:22 -0700 Subject: Rearanged a couple things & removed debugging statements --- actions/apistatusesupdate.php | 46 +++++++++++++++------------------- actions/newnotice.php | 26 ++++++++------------ lib/mediafile.php | 57 ++++++------------------------------------- 3 files changed, 36 insertions(+), 93 deletions(-) (limited to 'lib') diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php index b7128d084..3a030f0fe 100644 --- a/actions/apistatusesupdate.php +++ b/actions/apistatusesupdate.php @@ -112,10 +112,12 @@ class ApiStatusesUpdateAction extends ApiAuthAction return; } - if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) { - $this->clientError(sprintf(_('The server was unable to handle ' . - 'that much POST data (%s bytes) due to its current configuration.'), - $_SERVER['CONTENT_LENGTH'])); + if (empty($this->status)) { + $this->clientError( + 'Client must provide a \'status\' parameter with a value.', + 400, + $this->format + ); return; } @@ -124,12 +126,10 @@ class ApiStatusesUpdateAction extends ApiAuthAction return; } - if (empty($this->status)) { - $this->clientError( - 'Client must provide a \'status\' parameter with a value.', - 400, - $this->format - ); + // Workaround for PHP returning empty $_FILES when POST length > PHP settings + + if (empty($_POST) && ($_SERVER['CONTENT_LENGTH'] > 0)) { + $this->clientError(_('Unable to handle that much POST data!')); return; } @@ -192,27 +192,19 @@ class ApiStatusesUpdateAction extends ApiAuthAction } $upload = null; - - common_debug('looking for attachment'); - $upload = MediaFile::fromUpload('media', $this->user); - common_debug("uploaded file = " . var_export($upload, true)); - if (isset($upload)) { - common_debug('newNotice: found an upload'); - - $status_shortened .= ' ' . $upload->shortUrl(); - - common_debug('content w/upload = ' . $status_shortened); + $status_shortened .= ' ' . $upload->shortUrl(); - if (Notice::contentTooLong($status_shortened)) { - $upload->delete(); - $this->clientError(sprintf(_('Max notice size is %d chars, including attachment URL.'), - Notice::maxContent())); - } else { - common_debug('content not too long'); - } + if (Notice::contentTooLong($status_shortened)) { + $upload->delete(); + $msg = _( + 'Max notice size is %d chars, ' . + 'including attachment URL.' + ); + $this->clientError(sprintf($msg, Notice::maxContent())); + } } $this->notice = Notice::saveNew( diff --git a/actions/newnotice.php b/actions/newnotice.php index 5100e79e1..59fb9f461 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -164,27 +164,21 @@ class NewnoticeAction extends Action } $upload = null; - - common_debug('looking for attachment'); - $upload = MediaFile::fromUpload('attach'); - common_debug("uploaded file = " . var_export($upload, true)); - if (isset($upload)) { - common_debug('newNotice: found an upload'); - $content_shortened .= ' ' . $upload->shortUrl(); + $content_shortened .= ' ' . $upload->shortUrl(); - common_debug('content w/upload = ' . $content_shortened); - - if (Notice::contentTooLong($content_shortened)) { - $upload->delete(); - $this->clientError(sprintf(_('Max notice size is %d chars, including attachment URL.'), - Notice::maxContent())); - } else { - common_debug('content not too long'); - } + if (Notice::contentTooLong($content_shortened)) { + $upload->delete(); + $this->clientError( + sprintf( + _('Max notice size is %d chars, including attachment URL.'), + Notice::maxContent() + ) + ); + } } $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1, diff --git a/lib/mediafile.php b/lib/mediafile.php index 4aa8211c3..40f37ba61 100644 --- a/lib/mediafile.php +++ b/lib/mediafile.php @@ -47,39 +47,21 @@ class MediaFile $this->user = common_current_user(); } - common_debug('in MediaFile constructor'); - - $this->filename = $filename; - $this->mimetype = $mimetype; - - common_debug('storing file'); + $this->filename = $filename; + $this->mimetype = $mimetype; $this->fileRecord = $this->storeFile(); - common_debug('finished storing file'); $this->fileurl = common_local_url('attachment', array('attachment' => $this->fileRecord->id)); - common_debug('$this->fileurl() = ' . $this->fileurl); - - // not sure this is necessary -- Zach $this->maybeAddRedir($this->fileRecord->id, $this->fileurl); - - common_debug('shortening file url'); $this->short_fileurl = common_shorten_url($this->fileurl); - common_debug('shortened file url = ' . $short_fileurl); - - // Also, not sure this is necessary -- Zach $this->maybeAddRedir($this->fileRecord->id, $this->short_fileurl); - - common_debug("MediaFile: end of constructor"); } function attachToNotice($notice) { - common_debug('MediaFile::attachToNotice() -- doing File_to_post'); File_to_post::processNew($this->fileRecord->id, $notice->id); - common_debug('MediaFile done doing File_to_post'); - $this->maybeAddRedir($this->fileRecord->id, common_local_url('file', array('notice' => $notice->id))); } @@ -98,31 +80,21 @@ class MediaFile function storeFile() { $file = new File; - $file->filename = $this->filename; - - common_debug('storing ' . $this->filename); - - $file->url = File::url($this->filename); - common_debug('file->url = ' . $file->url); - - $filepath = File::path($this->filename); - common_debug('filepath = ' . $filepath); - $file->size = filesize($filepath); - $file->date = time(); + $file->filename = $this->filename; + $file->url = File::url($this->filename); + $filepath = File::path($this->filename); + $file->size = filesize($filepath); + $file->date = time(); $file->mimetype = $this->mimetype; $file_id = $file->insert(); if (!$file_id) { - - common_debug("storeFile: problem inserting new file"); common_log_db_error($file, "INSERT", __FILE__); throw new ClientException(_('There was a database error while saving your file. Please try again.')); } - common_debug('finished storing file'); - return $file; } @@ -133,15 +105,10 @@ class MediaFile function maybeAddRedir($file_id, $url) { - - common_debug("maybeAddRedir: looking up url: $url for file id $file_id"); - $file_redir = File_redirection::staticGet('url', $url); if (empty($file_redir)) { - common_debug("maybeAddRedir: $url is not in the db"); - $file_redir = new File_redirection; $file_redir->url = $url; $file_redir->file_id = $file_id; @@ -152,22 +119,16 @@ class MediaFile common_log_db_error($file_redir, "INSERT", __FILE__); throw new ClientException(_('There was a database error while saving your file. Please try again.')); } - } else { - - common_debug("maybeAddRedir: no need to add $url, it's already in the db"); } } static function fromUpload($param = 'media', $user = null) { - common_debug("fromUpload: param = $param"); - if (empty($user)) { $user = common_current_user(); } if (!isset($_FILES[$param]['error'])){ - common_debug('no file found'); return; } @@ -221,8 +182,6 @@ class MediaFile $filename = File::filename($user->getProfile(), $basename, $mimetype); $filepath = File::path($filename); - common_debug("filepath = " . $filepath); - $result = move_uploaded_file($_FILES[$param]['tmp_name'], $filepath); if (!$result) { @@ -277,8 +236,6 @@ class MediaFile static function getUploadedFileType($f) { require_once 'MIME/Type.php'; - common_debug("in getUploadedFileType"); - $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); $cmd = common_config('attachments', 'filecommand'); -- cgit v1.2.3-54-g00ecf From e0dbc47f8e9ef86dbb652d816173e1bfc8188807 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 28 Oct 2009 17:47:14 -0700 Subject: Fixed header comment. --- lib/mediafile.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/mediafile.php b/lib/mediafile.php index 40f37ba61..d4d184dd0 100644 --- a/lib/mediafile.php +++ b/lib/mediafile.php @@ -1,8 +1,10 @@ * @author Zach Copley * @copyright 2008-2009 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 -- cgit v1.2.3-54-g00ecf From f0abc0fe15a54da468cf37e748041bba4f362e53 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 29 Oct 2009 13:18:51 +0100 Subject: Updated bookmarklet. Created its own action --- actions/bookmarklet.php | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ doc-src/bookmarklet | 2 +- js/util.js | 3 ++ lib/router.php | 2 ++ 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 actions/bookmarklet.php (limited to 'lib') diff --git a/actions/bookmarklet.php b/actions/bookmarklet.php new file mode 100644 index 000000000..f253399e4 --- /dev/null +++ b/actions/bookmarklet.php @@ -0,0 +1,75 @@ +. + * + * @category Bookmarklet + * @package StatusNet + * @author Sarven Capadisli + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/actions/newnotice.php'; + +/** + * Action for posting a notice + * + * @category Bookmarklet + * @package StatusNet + * @author Sarven Capadisli + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class BookmarkletAction extends NewnoticeAction +{ + function showTitle() + { + $this->element('title', null, _('Post to '.common_config('site', 'name'))); + } + + function showHeader() + { + $this->elementStart('div', array('id' => 'header')); + $this->elementStart('address'); + $this->element('a', array('class' => 'url', + 'href' => common_local_url('public')), + ''); + $this->elementEnd('address'); + if (common_logged_in()) { + $this->showNoticeForm(); + } + $this->elementEnd('div'); + } + + function showCore() + { + } + + function showFooter() + { + } +} + diff --git a/doc-src/bookmarklet b/doc-src/bookmarklet index e5ded7702..0aa9e1e49 100644 --- a/doc-src/bookmarklet +++ b/doc-src/bookmarklet @@ -2,4 +2,4 @@ A bookmarklet is a small piece of javascript code used as a bookmark. This one w Drag-and-drop the following link to your bookmarks bar or right-click it and add it to your browser favorites to keep it handy. -Post to %%site.name%% +Post to %%site.name%% diff --git a/js/util.js b/js/util.js index 0a943512f..b079388e4 100644 --- a/js/util.js +++ b/js/util.js @@ -241,6 +241,9 @@ $(document).ready(function(){ alert(result); } else { + if($('body')[0].id == 'bookmarklet') { + self.close(); + } if ($("#command_result", xml).length > 0) { var result = document._importNode($("p", xml).get(0), true); result = result.textContent || result.innerHTML; diff --git a/lib/router.php b/lib/router.php index dedf73c86..2fd255fe6 100644 --- a/lib/router.php +++ b/lib/router.php @@ -179,6 +179,8 @@ class Router array('action' => 'deletenotice'), array('notice' => '[0-9]+')); + $m->connect('bookmarklet/new', array('action' => 'bookmarklet')); + // conversation $m->connect('conversation/:id', -- cgit v1.2.3-54-g00ecf From a6ed4e5bf7e7c15bc649c91451bbe7b6aa1f0735 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 29 Oct 2009 14:49:00 -0400 Subject: a location method for getting an URL --- lib/location.php | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/location.php b/lib/location.php index 048554f0f..c9411b55d 100644 --- a/lib/location.php +++ b/lib/location.php @@ -47,10 +47,11 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { class Location { - public $lat; - public $lon; - public $location_id; - public $location_ns; + public $lat; + public $lon; + public $location_id; + public $location_ns; + private $_url; var $names = array(); @@ -157,4 +158,33 @@ class Location } } } + + /** + * Get an URL suitable for this location + * + * @return string URL for this location or NULL + */ + + function getURL() + { + if ($this->_url == false) { // cached failure + return null; + } else if (is_string($this->_url)) { // cached value + return $this->_url; + } + + $url = null; + + Event::handle('LocationUrl', array($this, &$url)); + + // Save it for later + + if (is_null($url)) { + $this->_url = false; + } else { + $this->_url = $url; + } + + return $this->_url; + } } -- cgit v1.2.3-54-g00ecf From 5b0809f4a3619e8123b486f7e910913dc4e10086 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 29 Oct 2009 16:15:49 -0400 Subject: fix caching in location.php --- lib/location.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/location.php b/lib/location.php index c9411b55d..bbfc15a36 100644 --- a/lib/location.php +++ b/lib/location.php @@ -91,6 +91,10 @@ class Location static function fromId($id, $ns, $language=null) { + if (is_null($language)) { + $language = common_language(); + } + $location = null; // Let a third-party handle it @@ -167,9 +171,9 @@ class Location function getURL() { - if ($this->_url == false) { // cached failure - return null; - } else if (is_string($this->_url)) { // cached value + // Keep one cached + + if (is_string($this->_url)) { return $this->_url; } @@ -177,14 +181,8 @@ class Location Event::handle('LocationUrl', array($this, &$url)); - // Save it for later - - if (is_null($url)) { - $this->_url = false; - } else { - $this->_url = $url; - } + $this->_url = $url; - return $this->_url; + return $url; } } -- cgit v1.2.3-54-g00ecf From 49dd54315ffd98b84e64d0377d4c26c07ec4084e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 29 Oct 2009 16:16:02 -0400 Subject: show notice location in notice list --- lib/noticelist.php | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'lib') diff --git a/lib/noticelist.php b/lib/noticelist.php index 6c296f82a..8b3015cc3 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -199,6 +199,7 @@ class NoticeListItem extends Widget { $this->out->elementStart('div', 'entry-content'); $this->showNoticeLink(); + $this->showNoticeLocation(); $this->showNoticeSource(); $this->showContext(); $this->out->elementEnd('div'); @@ -369,6 +370,44 @@ class NoticeListItem extends Widget $this->out->elementEnd('a'); } + /** + * show the notice location + * + * shows the notice location in the correct language. + * + * If an URL is available, makes a link. Otherwise, just a span. + * + * @return void + */ + + function showNoticeLocation() + { + $id = $this->notice->id; + + $location = $this->notice->getLocation(); + + if (empty($location)) { + return; + } + + $name = $location->getName(); + + if (empty($name)) { + // XXX: Could be a translation issue. Fall back to... something? + return; + } + + $url = $location->getUrl(); + + if (empty($url)) { + $this->out->element('span', array('class' => 'location'), $name); + } else { + $this->out->element('a', array('class' => 'location', + 'href' => $url), + $name); + } + } + /** * Show the source of the notice * -- cgit v1.2.3-54-g00ecf From 7f5fbee2e36889fae1d1c5043d76625e197e39ea Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Fri, 30 Oct 2009 09:17:19 +1300 Subject: give some suggestions back to the user when no config file found, and a link to the installer --- lib/common.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/common.php b/lib/common.php index e29456ed4..667826f39 100644 --- a/lib/common.php +++ b/lib/common.php @@ -185,7 +185,14 @@ function _have_config() } // XXX: Throw a conniption if database not installed - +// XXX: Find a way to use htmlwriter for this instead of handcoded markup +if (!_have_config()) { + echo '

'. _('No configuation file found. ') .'

'; + echo '

'. _('I looked for configuration files in the following places: ') .'
'. implode($_config_files, '
'); + echo '

'. _('You make wish run the installer to fix this.') .'

'; + echo ''. _('Go to the installer.') .''; + exit; +} // Fixup for statusnet.ini $_db_name = substr($config['db']['database'], strrpos($config['db']['database'], '/') + 1); -- cgit v1.2.3-54-g00ecf From 54696f7c46684234bbeb48747b37f934ffd0d393 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Thu, 29 Oct 2009 16:01:25 -0400 Subject: Moved the public XRDS from the OpenID plugin to core Added 4 new events involved in XRDS: StartUserXRDS, EndUserXRDS, StartPublicXRDS, EndPublicXRDS Added OpenID provider functionality (no delegation support [yet]) --- EVENTS.txt | 16 ++++++ actions/public.php | 7 +++ actions/publicxrds.php | 81 ++++++++++++++++++++++++++ actions/xrds.php | 106 +++++++++++++++++++++------------- lib/router.php | 3 + lib/xrdsoutputter.php | 97 ++++++++++++++++++++++++++++++++ plugins/OpenID/OpenIDPlugin.php | 63 ++++++++++++++++++--- plugins/OpenID/openid.php | 8 +++ plugins/OpenID/openidserver.php | 96 +++++++++++++++++++++++++++++++ plugins/OpenID/publicxrds.php | 122 ---------------------------------------- 10 files changed, 431 insertions(+), 168 deletions(-) create mode 100644 actions/publicxrds.php create mode 100644 lib/xrdsoutputter.php create mode 100644 plugins/OpenID/openidserver.php delete mode 100644 plugins/OpenID/publicxrds.php (limited to 'lib') diff --git a/EVENTS.txt b/EVENTS.txt index d989557e6..a8a77390f 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -458,3 +458,19 @@ StartProfileListItemActionElements: Showing the profile list actions (prepend a EndProfileListItemActionElements: Showing profile list actions (append a button here) - $item: ProfileListItem widget + +StartUserXRDS: Start XRDS output (right after the opening XRDS tag) +- $action: the current action +- &$xrdsoutputter - XRDSOutputter object to write to + +EndUserXRDS: End XRDS output (right before the closing XRDS tag) +- $action: the current action +- &$xrdsoutputter - XRDSOutputter object to write to + +StartPublicXRDS: Start XRDS output (right after the opening XRDS tag) +- $action: the current action +- &$xrdsoutputter - XRDSOutputter object to write to + +EndPublicXRDS: End XRDS output (right before the closing XRDS tag) +- $action: the current action +- &$xrdsoutputter - XRDSOutputter object to write to diff --git a/actions/public.php b/actions/public.php index 73fad182a..4b71e5853 100644 --- a/actions/public.php +++ b/actions/public.php @@ -131,6 +131,13 @@ class PublicAction extends Action return _('Public timeline'); } } + + function extraHead() + { + parent::extraHead(); + $this->element('meta', array('http-equiv' => 'X-XRDS-Location', + 'content' => common_local_url('publicxrds'))); + } /** * Output elements for RSS and Atom feeds diff --git a/actions/publicxrds.php b/actions/publicxrds.php new file mode 100644 index 000000000..5fd4eead7 --- /dev/null +++ b/actions/publicxrds.php @@ -0,0 +1,81 @@ + + * @author Robin Millette + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; +require_once INSTALLDIR.'/lib/xrdsoutputter.php'; + +/** + * Public XRDS + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @author Robin Millette + * @author Craig Andrews + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @todo factor out similarities with XrdsAction + */ +class PublicxrdsAction extends Action +{ + /** + * Is read only? + * + * @return boolean true + */ + function isReadOnly($args) + { + return true; + } + + /** + * Class handler. + * + * @param array $args array of arguments + * + * @return nothing + */ + function handle($args) + { + parent::handle($args); + $xrdsOutputter = new XRDSOutputter(); + $xrdsOutputter->startXRDS(); + Event::handle('StartPublicXRDS', array($this,&$xrdsOutputter)); + Event::handle('EndPublicXRDS', array($this,&$xrdsOutputter)); + $xrdsOutputter->endXRDS(); + } +} + diff --git a/actions/xrds.php b/actions/xrds.php index 8ba89fec0..8f09557d1 100644 --- a/actions/xrds.php +++ b/actions/xrds.php @@ -36,6 +36,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { require_once INSTALLDIR.'/lib/omb.php'; require_once INSTALLDIR.'/extlib/libomb/service_provider.php'; require_once INSTALLDIR.'/extlib/libomb/xrds_mapper.php'; +require_once INSTALLDIR.'/lib/xrdsoutputter.php'; /** * XRDS for OpenMicroBlogging @@ -49,6 +50,8 @@ require_once INSTALLDIR.'/extlib/libomb/xrds_mapper.php'; */ class XrdsAction extends Action { + var $user; + /** * Is read only? * @@ -58,60 +61,87 @@ class XrdsAction extends Action { return true; } - - /** - * Class handler. - * - * @param array $args query arguments - * - * @return void - */ - function handle($args) + + function prepare($args) { - parent::handle($args); + parent::prepare($args); $nickname = $this->trimmed('nickname'); - $user = User::staticGet('nickname', $nickname); - if (!$user) { + $this->user = User::staticGet('nickname', $nickname); + if (!$this->user) { $this->clientError(_('No such user.')); return; } - $this->showXrds($user); + return true; } /** - * Show XRDS for a user. + * Class handler. * - * @param class $user XRDS for this user. + * @param array $args query arguments * * @return void */ - function showXrds($user) + function handle($args) { - $srv = new OMB_Service_Provider(profile_to_omb_profile($user->uri, - $user->getProfile())); - /* Use libomb’s default XRDS Writer. */ - $xrds_writer = null; - $srv->writeXRDS(new Laconica_XRDS_Mapper(), $xrds_writer); - } -} + parent::handle($args); + $xrdsOutputter = new XRDSOutputter(); + $xrdsOutputter->startXRDS(); -class Laconica_XRDS_Mapper implements OMB_XRDS_Mapper -{ - protected $urls; + Event::handle('StartUserXRDS', array($this,&$xrdsOutputter)); - public function __construct() - { - $this->urls = array( - OAUTH_ENDPOINT_REQUEST => 'requesttoken', - OAUTH_ENDPOINT_AUTHORIZE => 'userauthorization', - OAUTH_ENDPOINT_ACCESS => 'accesstoken', - OMB_ENDPOINT_POSTNOTICE => 'postnotice', - OMB_ENDPOINT_UPDATEPROFILE => 'updateprofile'); - } + //oauth + $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xml:id' => 'oauth', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + $xrdsOutputter->element('Type', null, 'xri://$xrds*simple'); + $xrdsOutputter->showXrdsService(OAUTH_ENDPOINT_REQUEST, + common_local_url('requesttoken'), + array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY, OAUTH_HMAC_SHA1)); + $xrdsOutputter->showXrdsService( OAUTH_ENDPOINT_AUTHORIZE, + common_local_url('userauthorization'), + array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY, OAUTH_HMAC_SHA1), + null, + $this->user->getIdentifierURI()); + $xrdsOutputter->showXrdsService(OAUTH_ENDPOINT_ACCESS, + common_local_url('accesstoken'), + array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY, OAUTH_HMAC_SHA1), + null, + $this->user->getIdentifierURI()); + $xrdsOutputter->showXrdsService(OAUTH_ENDPOINT_RESOURCE, + null, + array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY, OAUTH_HMAC_SHA1), + null, + $this->user->getIdentifierURI()); + $xrdsOutputter->elementEnd('XRD'); + + //omb + $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xml:id' => 'oauth', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + $xrdsOutputter->element('Type', null, 'xri://$xrds*simple'); + $xrdsOutputter->showXrdsService(OMB_ENDPOINT_POSTNOTICE, + common_local_url('postnotice')); + $xrdsOutputter->showXrdsService(OMB_ENDPOINT_UPDATEPROFILE, + common_local_url('updateprofile')); + $xrdsOutputter->elementEnd('XRD'); + + //misc + $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xml:id' => 'oauth', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + $xrdsOutputter->showXrdsService(OAUTH_DISCOVERY, + '#oauth'); + $xrdsOutputter->showXrdsService(OMB_VERSION, + '#omb'); + $xrdsOutputter->elementEnd('XRD'); - public function getURL($action) - { - return common_local_url($this->urls[$action]); + Event::handle('EndUserXRDS', array($this,&$xrdsOutputter)); + + $xrdsOutputter->endXRDS(); + } } ?> diff --git a/lib/router.php b/lib/router.php index 2fd255fe6..0dd130ab0 100644 --- a/lib/router.php +++ b/lib/router.php @@ -108,6 +108,9 @@ class Router $m->connect('main/oembed', array('action' => 'oembed')); + $m->connect('main/xrds', + array('action' => 'publicxrds')); + // these take a code foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) { diff --git a/lib/xrdsoutputter.php b/lib/xrdsoutputter.php new file mode 100644 index 000000000..0e228e293 --- /dev/null +++ b/lib/xrdsoutputter.php @@ -0,0 +1,97 @@ +. + * + * @category Output + * @package StatusNet + * @author Craig Andrews + * @copyright 2008 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/xmloutputter.php'; + +/** + * Low-level generator for XRDS XML + * + * @category Output + * @package StatusNet + * @author Craig Andrews + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Action + * @see XMLOutputter + */ + +class XRDSOutputter extends XMLOutputter +{ + public function startXRDS() + { + header('Content-Type: application/xrds+xml'); + $this->startXML(); + $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds')); + } + + public function endXRDS() + { + $this->elementEnd('XRDS'); + $this->endXML(); + } + + /** + * Show service. + * + * @param string $type XRDS type + * @param string $uri URI + * @param array $params type parameters, null by default + * @param array $sigs type signatures, null by default + * @param string $localId local ID, null by default + * + * @return void + */ + function showXrdsService($type, $uri, $params=null, $sigs=null, $localId=null) + { + $this->elementStart('Service'); + if ($uri) { + $this->element('URI', null, $uri); + } + $this->element('Type', null, $type); + if ($params) { + foreach ($params as $param) { + $this->element('Type', null, $param); + } + } + if ($sigs) { + foreach ($sigs as $sig) { + $this->element('Type', null, $sig); + } + } + if ($localId) { + $this->element('LocalID', null, $localId); + } + $this->elementEnd('Service'); + } +} diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php index 81e3ed9c4..5ebee2cbe 100644 --- a/plugins/OpenID/OpenIDPlugin.php +++ b/plugins/OpenID/OpenIDPlugin.php @@ -62,17 +62,59 @@ class OpenIDPlugin extends Plugin * @return boolean hook return */ - function onRouterInitialized($m) + function onStartInitializeRouter($m) { $m->connect('main/openid', array('action' => 'openidlogin')); + $m->connect('main/openidtrust', array('action' => 'openidtrust')); $m->connect('settings/openid', array('action' => 'openidsettings')); - $m->connect('xrds', array('action' => 'publicxrds')); $m->connect('index.php?action=finishopenidlogin', array('action' => 'finishopenidlogin')); $m->connect('index.php?action=finishaddopenid', array('action' => 'finishaddopenid')); - + $m->connect('main/openidserver', array('action' => 'openidserver')); + return true; } + function onEndPublicXRDS($action, &$xrdsOutputter) + { + $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + $xrdsOutputter->element('Type', null, 'xri://$xrds*simple'); + //consumer + foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) { + $xrdsOutputter->showXrdsService(Auth_OpenID_RP_RETURN_TO_URL_TYPE, + common_local_url($finish)); + } + //provider + $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/server', + common_local_url('openidserver'), + null, + null, + 'http://specs.openid.net/auth/2.0/identifier_select'); + $xrdsOutputter->elementEnd('XRD'); + } + + function onEndUserXRDS($action, &$xrdsOutputter) + { + $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xml:id' => 'openid', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + $xrdsOutputter->element('Type', null, 'xri://$xrds*simple'); + + //consumer + $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/return_to', + common_local_url('finishopenidlogin')); + + //provider + $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/signon', + common_local_url('openidserver'), + null, + null, + common_profile_url($action->user->nickname)); + $xrdsOutputter->elementEnd('XRD'); + } + function onEndLoginGroupNav(&$action) { $action_name = $action->trimmed('action'); @@ -107,6 +149,7 @@ class OpenIDPlugin extends Plugin case 'XrdsAction': case 'PublicxrdsAction': case 'OpenidsettingsAction': + case 'OpenidserverAction': require_once(INSTALLDIR.'/plugins/OpenID/' . strtolower(mb_substr($cls, 0, -6)) . '.php'); return false; case 'User_openid': @@ -152,12 +195,16 @@ class OpenIDPlugin extends Plugin function onEndShowHeadElements($action) { - if ($action->trimmed('action') == 'public') { - // for client side of OpenID authentication - $action->element('meta', array('http-equiv' => 'X-XRDS-Location', - 'content' => common_local_url('publicxrds'))); + if($action instanceof ShowstreamAction){ + $action->element('link', array('rel' => 'openid2.provider', + 'href' => common_local_url('openidserver'))); + $action->element('link', array('rel' => 'openid2.local_id', + 'href' => $action->profile->profileurl)); + $action->element('link', array('rel' => 'openid.server', + 'href' => common_local_url('openidserver'))); + $action->element('link', array('rel' => 'openid.delegate', + 'href' => $action->profile->profileurl)); } - return true; } diff --git a/plugins/OpenID/openid.php b/plugins/OpenID/openid.php index b76497c28..ff7a93899 100644 --- a/plugins/OpenID/openid.php +++ b/plugins/OpenID/openid.php @@ -23,6 +23,7 @@ require_once(INSTALLDIR.'/plugins/OpenID/User_openid.php'); require_once('Auth/OpenID.php'); require_once('Auth/OpenID/Consumer.php'); +require_once('Auth/OpenID/Server.php'); require_once('Auth/OpenID/SReg.php'); require_once('Auth/OpenID/MySQLStore.php'); @@ -50,6 +51,13 @@ function oid_consumer() return $consumer; } +function oid_server() +{ + $store = oid_store(); + $server = new Auth_OpenID_Server($store, common_local_url('openidserver')); + return $server; +} + function oid_clear_last() { oid_set_last(''); diff --git a/plugins/OpenID/openidserver.php b/plugins/OpenID/openidserver.php new file mode 100644 index 000000000..198a1a328 --- /dev/null +++ b/plugins/OpenID/openidserver.php @@ -0,0 +1,96 @@ +. + * + * @category Settings + * @package StatusNet + * @author Craig Andrews + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/action.php'; +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; + +/** + * Settings for OpenID + * + * Lets users add, edit and delete OpenIDs from their account + * + * @category Settings + * @package StatusNet + * @author Craig Andrews + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class OpenidserverAction extends Action +{ + + function handle($args) + { + parent::handle($args); + $oserver = oid_server(); + $request = $oserver->decodeRequest(); + if (in_array($request->mode, array('checkid_immediate', + 'checkid_setup'))) { + $cur = common_current_user(); + error_log("Request identity: " . $request->identity); + if(!$cur){ + /* Go log in, and then come back. */ + common_set_returnto($_SERVER['REQUEST_URI']); + common_redirect(common_local_url('login')); + return; + }else if(common_profile_url($cur->nickname) == $request->identity || $request->idSelect()){ + $response = &$request->answer(true, null, common_profile_url($cur->nickname)); + } else if ($request->immediate) { + $response = &$request->answer(false); + } else { + //invalid + $this->clientError(sprintf(_('You are not authorized to use the identity %s'),$request->identity),$code=403); + } + } else { + $response = &$oserver->handleRequest($request); + } + + if($response){ + $webresponse = $oserver->encodeResponse($response); + + if ($webresponse->code != AUTH_OPENID_HTTP_OK) { + header(sprintf("HTTP/1.1 %d ", $webresponse->code), + true, $webresponse->code); + } + + if($webresponse->headers){ + foreach ($webresponse->headers as $k => $v) { + header("$k: $v"); + } + } + $this->raw($webresponse->body); + }else{ + $this->clientError(_('Just an OpenID provider. Nothing to see here, move along...'),$code=500); + } + } +} diff --git a/plugins/OpenID/publicxrds.php b/plugins/OpenID/publicxrds.php deleted file mode 100644 index 1b2b359ca..000000000 --- a/plugins/OpenID/publicxrds.php +++ /dev/null @@ -1,122 +0,0 @@ - - * @author Robin Millette - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ - * - * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2008, 2009, StatusNet, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR.'/plugins/OpenID/openid.php'; - -/** - * Public XRDS for OpenID - * - * @category Action - * @package StatusNet - * @author Evan Prodromou - * @author Robin Millette - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ - * - * @todo factor out similarities with XrdsAction - */ -class PublicxrdsAction extends Action -{ - /** - * Is read only? - * - * @return boolean true - */ - function isReadOnly($args) - { - return true; - } - - /** - * Class handler. - * - * @param array $args array of arguments - * - * @return nothing - */ - function handle($args) - { - parent::handle($args); - header('Content-Type: application/xrds+xml'); - $this->startXML(); - $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds')); - $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', - 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', - 'version' => '2.0')); - $this->element('Type', null, 'xri://$xrds*simple'); - foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) { - $this->showService(Auth_OpenID_RP_RETURN_TO_URL_TYPE, - common_local_url($finish)); - } - $this->elementEnd('XRD'); - $this->elementEnd('XRDS'); - $this->endXML(); - } - - /** - * Show service. - * - * @param string $type XRDS type - * @param string $uri URI - * @param array $params type parameters, null by default - * @param array $sigs type signatures, null by default - * @param string $localId local ID, null by default - * - * @return void - */ - function showService($type, $uri, $params=null, $sigs=null, $localId=null) - { - $this->elementStart('Service'); - if ($uri) { - $this->element('URI', null, $uri); - } - $this->element('Type', null, $type); - if ($params) { - foreach ($params as $param) { - $this->element('Type', null, $param); - } - } - if ($sigs) { - foreach ($sigs as $sig) { - $this->element('Type', null, $sig); - } - } - if ($localId) { - $this->element('LocalID', null, $localId); - } - $this->elementEnd('Service'); - } -} - -- cgit v1.2.3-54-g00ecf From c49564647a081ff5a82defa197b796306a7a064e Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Thu, 29 Oct 2009 16:26:56 -0400 Subject: whitespace adjustments for doxygen --- lib/xrdsoutputter.php | 1 - plugins/OpenID/openidserver.php | 1 - 2 files changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/xrdsoutputter.php b/lib/xrdsoutputter.php index 0e228e293..4b77ed5a3 100644 --- a/lib/xrdsoutputter.php +++ b/lib/xrdsoutputter.php @@ -45,7 +45,6 @@ require_once INSTALLDIR.'/lib/xmloutputter.php'; * @see Action * @see XMLOutputter */ - class XRDSOutputter extends XMLOutputter { public function startXRDS() diff --git a/plugins/OpenID/openidserver.php b/plugins/OpenID/openidserver.php index 198a1a328..a6b18608d 100644 --- a/plugins/OpenID/openidserver.php +++ b/plugins/OpenID/openidserver.php @@ -45,7 +45,6 @@ require_once INSTALLDIR.'/plugins/OpenID/openid.php'; * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class OpenidserverAction extends Action { -- cgit v1.2.3-54-g00ecf From d5951ebce69b9b98d0425b7c7aabc2061d6b9ea8 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Fri, 30 Oct 2009 10:02:37 +1300 Subject: Revert "give some suggestions back to the user when no config file found, and a link to the installer" This reverts commit 7f5fbee2e36889fae1d1c5043d76625e197e39ea. --- lib/common.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/common.php b/lib/common.php index 667826f39..e29456ed4 100644 --- a/lib/common.php +++ b/lib/common.php @@ -185,14 +185,7 @@ function _have_config() } // XXX: Throw a conniption if database not installed -// XXX: Find a way to use htmlwriter for this instead of handcoded markup -if (!_have_config()) { - echo '

'. _('No configuation file found. ') .'

'; - echo '

'. _('I looked for configuration files in the following places: ') .'
'. implode($_config_files, '
'); - echo '

'. _('You make wish run the installer to fix this.') .'

'; - echo ''. _('Go to the installer.') .''; - exit; -} + // Fixup for statusnet.ini $_db_name = substr($config['db']['database'], strrpos($config['db']['database'], '/') + 1); -- cgit v1.2.3-54-g00ecf From f1daca16e2f34cce7d25cb1dbe144fc027fcc1fd Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Fri, 30 Oct 2009 10:03:25 +1300 Subject: give some suggestions back to the user when no config file found, and a link to the installer --- lib/common.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/common.php b/lib/common.php index e29456ed4..2c2f6869e 100644 --- a/lib/common.php +++ b/lib/common.php @@ -185,7 +185,14 @@ function _have_config() } // XXX: Throw a conniption if database not installed - +// XXX: Find a way to use htmlwriter for this instead of handcoded markup +if (!_have_config()) { + echo '

'. _('No configuation file found. ') .'

'; + echo '

'. _('I looked for configuration files in the following places: ') .'
'. implode($_config_files, '
'); + echo '

'. _('You may wish to run the installer to fix this.') .'

'; + echo ''. _('Go to the installer.') .''; + exit; +} // Fixup for statusnet.ini $_db_name = substr($config['db']['database'], strrpos($config['db']['database'], '/') + 1); -- cgit v1.2.3-54-g00ecf From 48f33f781a91db7178a4f5046885a74dc484e629 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 31 Oct 2009 17:16:37 +0100 Subject: Using 'form_notice' class instead of 'form' to group both forms --- lib/messageform.php | 13 ++++++++++++- lib/noticeform.php | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/messageform.php b/lib/messageform.php index e25ebfa08..b034be312 100644 --- a/lib/messageform.php +++ b/lib/messageform.php @@ -80,10 +80,21 @@ class MessageForm extends Form /** * ID of the form * - * @return int ID of the form + * @return string ID of the form */ function id() + { + return 'form_notice-direct'; + } + + /** + * Class of the form + * + * @return string class of the form + */ + + function formClass() { return 'form_notice'; } diff --git a/lib/noticeform.php b/lib/noticeform.php index 9864d15eb..1be011c18 100644 --- a/lib/noticeform.php +++ b/lib/noticeform.php @@ -105,7 +105,7 @@ class NoticeForm extends Form /** * ID of the form * - * @return int ID of the form + * @return string ID of the form */ function id() @@ -113,6 +113,17 @@ class NoticeForm extends Form return 'form_notice'; } + /** + * Class of the form + * + * @return string class of the form + */ + + function formClass() + { + return 'form_notice'; + } + /** * Action of the form * -- cgit v1.2.3-54-g00ecf From 9f7d390ad1ade40887398f0454e5aab4f0243fb9 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 31 Oct 2009 12:18:38 -0400 Subject: update version to rc2 --- lib/common.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/common.php b/lib/common.php index 3de567cd9..016b04481 100644 --- a/lib/common.php +++ b/lib/common.php @@ -19,7 +19,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -define('STATUSNET_VERSION', '0.8.2dev'); +define('STATUSNET_VERSION', '0.8.2rc2'); define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility define('STATUSNET_CODENAME', 'Life and How to Live It'); -- cgit v1.2.3-54-g00ecf From 4056a26017ddbb83a32777e8e5f5aeb7289b5e57 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 31 Oct 2009 14:32:12 -0400 Subject: Revert "Added getfile action" This reverts commit f58daa873befbaee5a998e69622c046c8a978dee. --- lib/router.php | 4 ---- 1 file changed, 4 deletions(-) (limited to 'lib') diff --git a/lib/router.php b/lib/router.php index 7455d9cf8..5529e60ac 100644 --- a/lib/router.php +++ b/lib/router.php @@ -171,10 +171,6 @@ class Router array('action' => 'attachment_thumbnail'), array('attachment' => '[0-9]+')); - $m->connect('getfile/:filename', - array('action' => 'getfile'), - array('filename' => '[A-Za-z0-9._-]+')); - $m->connect('notice/new', array('action' => 'newnotice')); $m->connect('notice/new?replyto=:replyto', array('action' => 'newnotice'), -- cgit v1.2.3-54-g00ecf From 521bd68d7cafba2d534b22105133138d727e9119 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 31 Oct 2009 14:43:01 -0400 Subject: revert revert on getfile --- lib/router.php | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/router.php b/lib/router.php index 2fd255fe6..3223b0a26 100644 --- a/lib/router.php +++ b/lib/router.php @@ -577,6 +577,10 @@ class Router $m->connect('api/search.json', array('action' => 'twitapisearchjson')); $m->connect('api/trends.json', array('action' => 'twitapitrends')); + $m->connect('getfile/:filename', + array('action' => 'getfile'), + array('filename' => '[A-Za-z0-9._-]+')); + // user stuff foreach (array('subscriptions', 'subscribers', -- cgit v1.2.3-54-g00ecf