diff --git a/README b/README index 9cbe84f0e5..2099f94d62 100644 --- a/README +++ b/README @@ -2,8 +2,8 @@ README ------ -Laconica 0.7.3 ("You Are The Everything") -7 April 2009 +Laconica 0.7.4 ("Can't Get There From Here") +29 May 2009 This is the README file for Laconica, the Open Source microblogging platform. It includes installation instructions, descriptions of @@ -71,29 +71,29 @@ for additional terms. New this version ================ -This is a minor bug-fix and feature release since version 0.7.2.1, -released Mar 11 2009. Notable changes this version: +This is a minor bug-fix and feature release since version 0.7.3, +released Apr 4 2009. Notable changes this version: -- A plugin to allow a templating language for customization -- A plugin for Piwik Analytics engine -- A bookmarklet for posting a notice about a Web page you're reading -- A welcome notice ('welcomebot') and default subscription for new users -- Support for SSL for some or all pages on the site -- Better handling of empty notice lists on many pages -- Major improvements to the Twitter friend-sync offline processing -- subscribers, subscriptions, groups are listed on the Personal page. -- "Invite" link restored to main menu -- Better memory handling in FOAF output -- Fix for SUP support (FriendFeed) -- Correct and intelligent redirect HTTP status codes -- Fix DB collations for search and sort -- Better H1s and Titles using user full names -- Fixes to make the linkback plugin operational -- Better indication that a notice is being published by Ajax (spinner) -- Better and unified Atom output -- Hiding "register" and "join now" messages when site is closed -- ping, twitter and facebook queuehandlers working better -- Updated RPM spec +- Improved handling of UTF-8 characters. The new code is *not* backwards + compatible by default; see "Upgrading" below for instructions on + converting existing databases to the correct character set. +- Unroll joins for large queries. This greatly enhanced database + performance -- up to 50x for some queries -- at the expense of making + an extra DB hit for some queries. +- Added an optional plugin to use WikiHashtags + (http://hashtags.wikia.com/) for the sidebar on hashtag pages. +- Optimized Twitter friend synchronization. +- Better error handling for Ajax posting of notices, including + HTTP errors and timeouts. +- Experimental Comet plugin -- supports the cometd and the Bayeux + protocol. Using this plugin, you can show "real time" updates on the + public and tag pages. However, server configuration is complex. +- If queues are enabled, update inboxes and memcached off-line. Speeds + up posting considerably. +- Correctly shorten links posted through XMPP. +- elements for pagination, supported by some browsers like Opera. +- Corrected date format in search API. +- Made the public XRDS file work correctly. Prerequisites ============= @@ -197,9 +197,9 @@ especially if you've previously installed PHP/MySQL packages. 1. Unpack the tarball you downloaded on your Web server. Usually a command like this will work: - tar zxf laconica-0.7.3.tar.gz + tar zxf laconica-0.7.4.tar.gz - ...which will make a laconica-0.7.3 subdirectory in your current + ...which will make a laconica-0.7.4 subdirectory in your current directory. (If you don't have shell access on your Web server, you may have to unpack the tarball on your local computer and FTP the files to the server.) @@ -207,7 +207,7 @@ especially if you've previously installed PHP/MySQL packages. 2. Move the tarball to a directory of your choosing in your Web root directory. Usually something like this will work: - mv laconica-0.7.3 /var/www/mublog + mv laconica-0.7.4 /var/www/mublog This will make your Laconica instance available in the mublog path of your server, like "http://example.net/mublog". "microblog" or @@ -704,7 +704,7 @@ with this situation. If you've been using Laconica 0.6, 0.5 or lower, or if you've been tracking the "git" version of the software, you will probably want to upgrade and keep your existing data. There is no automated upgrade -procedure in Laconica 0.7.3. Try these step-by-step instructions; read +procedure in Laconica 0.7.4. Try these step-by-step instructions; read to the end first before trying them. 0. Download Laconica and set up all the prerequisites as if you were @@ -1239,7 +1239,7 @@ repository (see below), and you get a compilation error ("unexpected T_STRING") in the browser, check to see that you don't have any conflicts in your code. -If you upgraded to Laconica 0.7.3 without reading the "Notice inboxes" +If you upgraded to Laconica 0.7.4 without reading the "Notice inboxes" section above, and all your users' 'Personal' tabs are empty, read the "Notice inboxes" section above. @@ -1330,6 +1330,8 @@ if anyone's been overlooked in error. * Ken Sedgwick * Brian Hendrickson * Tobias Diekershoff +* Dan Moore +* Fil Thanks also to the developers of our upstream library code and to the thousands of people who have tried out Identi.ca, installed Laconi.ca, diff --git a/actions/all.php b/actions/all.php index a53bbea07b..03179a2468 100644 --- a/actions/all.php +++ b/actions/all.php @@ -69,17 +69,6 @@ class AllAction extends ProfileAction sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))); } - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'all', array('nickname' => $this->user->nickname)); - } - function showLocalNav() { $nav = new PersonalGroupNav($this); diff --git a/actions/api.php b/actions/api.php index b25ba99f30..b8da852b53 100644 --- a/actions/api.php +++ b/actions/api.php @@ -144,8 +144,8 @@ class ApiAction extends Action } if (in_array($fullname, $bareauth)) { - # bareauth: only needs auth if without an argument - if ($this->api_arg) { + # bareauth: only needs auth if without an argument or query param specifying user + if ($this->api_arg || $this->arg('id') || is_numeric($this->arg('user_id')) || $this->arg('screen_name')) { return false; } else { return true; diff --git a/actions/favorited.php b/actions/favorited.php index 7e31303e3b..c902d80f53 100644 --- a/actions/favorited.php +++ b/actions/favorited.php @@ -221,15 +221,4 @@ class FavoritedAction extends Action $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page, 'favorited'); } - - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'favorited'); - } } diff --git a/actions/groupmembers.php b/actions/groupmembers.php index 909935bec8..21e5ebbaa1 100644 --- a/actions/groupmembers.php +++ b/actions/groupmembers.php @@ -137,15 +137,4 @@ class GroupmembersAction extends Action $this->page, 'groupmembers', array('nickname' => $this->group->nickname)); } - - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'groupmembers', array('nickname' => $this->group->nickname)); - } } diff --git a/actions/groups.php b/actions/groups.php index e20acce706..26b52a5fcd 100644 --- a/actions/groups.php +++ b/actions/groups.php @@ -129,15 +129,4 @@ class GroupsAction extends Action $gbm = new GroupsByMembersSection($this); $gbm->show(); } - - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'groups', array('nickname' => $this->group->nickname)); - } } diff --git a/actions/inbox.php b/actions/inbox.php index 7b5cf2d203..f14ba631fd 100644 --- a/actions/inbox.php +++ b/actions/inbox.php @@ -46,15 +46,15 @@ require_once INSTALLDIR.'/lib/mailbox.php'; class InboxAction extends MailboxAction { - + /** * Title of the page * * @return string page title */ - + function title() - { + { if ($this->page > 1) { return sprintf(_("Inbox for %s - page %d"), $this->user->nickname, $this->page); @@ -63,22 +63,11 @@ class InboxAction extends MailboxAction } } - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'inbox', array('nickname' => $this->user->nickname)); - } - /** * Retrieve the messages for this user and this page * * Does a query for the right messages - * + * * @return Message data object with stream for messages * * @see MailboxAction::getMessages() @@ -95,7 +84,7 @@ class InboxAction extends MailboxAction if ($message->find()) { return $message; - } else { + } else { return null; } } diff --git a/actions/outbox.php b/actions/outbox.php index deef1cc870..a875e9ad95 100644 --- a/actions/outbox.php +++ b/actions/outbox.php @@ -62,22 +62,11 @@ class OutboxAction extends MailboxAction } } - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'outbox', array('nickname' => $this->user->nickname)); - } - /** * retrieve the messages for this user and this page * * Does a query for the right messages - * + * * @return Message data object with stream for messages * * @see MailboxAction::getMessages() diff --git a/actions/public.php b/actions/public.php index d2f9da6460..27153f1315 100644 --- a/actions/public.php +++ b/actions/public.php @@ -135,17 +135,6 @@ class PublicAction extends Action _('Public Stream Feed (Atom)'))); } - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'public'); - } - /** * Extra head elements * diff --git a/actions/replies.php b/actions/replies.php index dfb520d649..eac4d0a3ae 100644 --- a/actions/replies.php +++ b/actions/replies.php @@ -138,17 +138,6 @@ class RepliesAction extends Action return array(new Feed(Feed::RSS1, $rssurl, $rsstitle)); } - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'replies', array('nickname' => $this->user->nickname)); - } - /** * show the personal group nav * diff --git a/actions/showfavorites.php b/actions/showfavorites.php index eed62a2ab3..865045337a 100644 --- a/actions/showfavorites.php +++ b/actions/showfavorites.php @@ -150,18 +150,6 @@ class ShowfavoritesAction extends Action return array(new Feed(Feed::RSS1, $feedurl, $feedtitle)); } - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'showfavorites', array('nickname' => $this->user->nickname)); - } - - /** * show the personal group nav * diff --git a/actions/showgroup.php b/actions/showgroup.php index a7df397273..29b6fa1e61 100644 --- a/actions/showgroup.php +++ b/actions/showgroup.php @@ -311,17 +311,6 @@ class ShowgroupAction extends Action $this->group->nickname))); } - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'showgroup', array('nickname' => $this->group->nickname)); - } - /** * Fill in the sidebar. * diff --git a/actions/shownotice.php b/actions/shownotice.php index 34c8a8e946..b0d973a991 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -122,7 +122,7 @@ class ShownoticeAction extends Action function lastModified() { - return max(strtotime($this->notice->created), + return max(strtotime($this->notice->modified), strtotime($this->profile->modified), ($this->avatar) ? strtotime($this->avatar->modified) : 0); } diff --git a/actions/showstream.php b/actions/showstream.php index 678a3174c1..e2f4e24d43 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -147,17 +147,6 @@ class ShowstreamAction extends ProfileAction sprintf(_('FOAF for %s'), $this->user->nickname))); } - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'showstream', array('nickname' => $this->user->nickname)); - } - function extraHead() { // for remote subscriptions etc. diff --git a/actions/tag.php b/actions/tag.php index e9351d2419..f5ca06f055 100644 --- a/actions/tag.php +++ b/actions/tag.php @@ -76,17 +76,6 @@ class TagAction extends Action sprintf(_('Feed for tag %s'), $this->tag))); } - /** - * Output document relationship links - * - * @return void - */ - function showRelationshipLinks() - { - $this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME - $this->page, 'tag', array('tag' => $this->tag)); - } - function showPageNotice() { return sprintf(_('Messages tagged "%s", most recent first'), $this->tag); diff --git a/actions/twitapiaccount.php b/actions/twitapiaccount.php index 68a18cb57b..8b956f897a 100644 --- a/actions/twitapiaccount.php +++ b/actions/twitapiaccount.php @@ -98,9 +98,31 @@ class TwitapiaccountAction extends TwitterapiAction $this->serverError(_('API method under construction.'), $code=501); } + // We don't have a rate limit, but some clients check this method. + // It always returns the same thing: 100 hit left. function rate_limit_status($args, $apidata) { parent::handle($args); - $this->serverError(_('API method under construction.'), $code=501); + + $type = $apidata['content-type']; + $this->init_document($type); + + if ($apidata['content-type'] == 'xml') { + $this->elementStart('hash'); + $this->element('remaining-hits', array('type' => 'integer'), 100); + $this->element('hourly-limit', array('type' => 'integer'), 100); + $this->element('reset-time', array('type' => 'datetime'), null); + $this->element('reset_time_in_seconds', array('type' => 'integer'), 0); + $this->elementEnd('hash'); + } elseif ($apidata['content-type'] == 'json') { + + $out = array('reset_time_in_seconds' => 0, + 'remaining_hits' => 100, + 'hourly_limit' => 100, + 'reset_time' => ''); + print json_encode($out); + } + + $this->end_document($type); } } diff --git a/actions/twitapidirect_messages.php b/actions/twitapidirect_messages.php index 7101db8df5..d2dbdb619b 100644 --- a/actions/twitapidirect_messages.php +++ b/actions/twitapidirect_messages.php @@ -43,7 +43,7 @@ class Twitapidirect_messagesAction extends TwitterapiAction $count = $this->arg('count'); $since = $this->arg('since'); $since_id = $this->arg('since_id'); - $before_id = $this->arg('before_id'); + $max_id = $this->arg('max_id'); $page = $this->arg('page'); @@ -74,8 +74,8 @@ class Twitapidirect_messagesAction extends TwitterapiAction $link = $server . $user->nickname . '/outbox'; } - if ($before_id) { - $message->whereAdd("id < $before_id"); + if ($max_id) { + $message->whereAdd("id <= $max_id"); } if ($since_id) { diff --git a/actions/twitapistatuses.php b/actions/twitapistatuses.php index 3abeba3672..1fbde6639f 100644 --- a/actions/twitapistatuses.php +++ b/actions/twitapistatuses.php @@ -45,22 +45,21 @@ class TwitapistatusesAction extends TwitterapiAction $page = $this->arg('page'); $since_id = $this->arg('since_id'); - $before_id = $this->arg('before_id'); + $max_id = $this->arg('max_id'); - // NOTE: page, since_id, and before_id are extensions to Twitter API -- TB if (!$page) { $page = 1; } if (!$since_id) { $since_id = 0; } - if (!$before_id) { - $before_id = 0; + if (!$max_id) { + $max_id = 0; } $since = strtotime($this->arg('since')); - $notice = Notice::publicStream((($page-1)*$MAX_PUBSTATUSES), $MAX_PUBSTATUSES, $since_id, $before_id, $since); + $notice = Notice::publicStream((($page-1)*$MAX_PUBSTATUSES), $MAX_PUBSTATUSES, $since_id, $max_id, $since); if ($notice) { @@ -97,7 +96,7 @@ class TwitapistatusesAction extends TwitterapiAction $since_id = $this->arg('since_id'); $count = $this->arg('count'); $page = $this->arg('page'); - $before_id = $this->arg('before_id'); + $max_id = $this->arg('max_id'); if (!$page) { $page = 1; @@ -111,9 +110,8 @@ class TwitapistatusesAction extends TwitterapiAction $since_id = 0; } - // NOTE: before_id is an extension to Twitter API -- TB - if (!$before_id) { - $before_id = 0; + if (!$max_id) { + $max_id = 0; } $since = strtotime($this->arg('since')); @@ -133,7 +131,7 @@ class TwitapistatusesAction extends TwitterapiAction $link = common_local_url('all', array('nickname' => $user->nickname)); $subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename); - $notice = $user->noticesWithFriends(($page-1)*20, $count, $since_id, $before_id, $since); + $notice = $user->noticesWithFriends(($page-1)*20, $count, $since_id, $max_id, $since); switch($apidata['content-type']) { case 'xml': @@ -184,7 +182,7 @@ class TwitapistatusesAction extends TwitterapiAction $since = $this->arg('since'); $since_id = $this->arg('since_id'); $page = $this->arg('page'); - $before_id = $this->arg('before_id'); + $max_id = $this->arg('max_id'); if (!$page) { $page = 1; @@ -198,9 +196,8 @@ class TwitapistatusesAction extends TwitterapiAction $since_id = 0; } - // NOTE: before_id is an extensions to Twitter API -- TB - if (!$before_id) { - $before_id = 0; + if (!$max_id) { + $max_id = 0; } $since = strtotime($this->arg('since')); @@ -220,7 +217,7 @@ class TwitapistatusesAction extends TwitterapiAction # XXX: since - $notice = $user->getNotices((($page-1)*20), $count, $since_id, $before_id, $since); + $notice = $user->getNotices((($page-1)*20), $count, $since_id, $max_id, $since); switch($apidata['content-type']) { case 'xml': @@ -353,7 +350,7 @@ class TwitapistatusesAction extends TwitterapiAction $count = $this->arg('count'); $page = $this->arg('page'); $since_id = $this->arg('since_id'); - $before_id = $this->arg('before_id'); + $max_id = $this->arg('max_id'); $user = $this->get_user($apidata['api_arg'], $apidata); $this->auth_user = $apidata['user']; @@ -380,15 +377,14 @@ class TwitapistatusesAction extends TwitterapiAction $since_id = 0; } - // NOTE: before_id is an extension to Twitter API -- TB - if (!$before_id) { - $before_id = 0; + if (!$max_id) { + $max_id = 0; } $since = strtotime($this->arg('since')); $notice = $user->getReplies((($page-1)*20), - $count, $since_id, $before_id, $since); + $count, $since_id, $max_id, $since); $notices = array(); while ($notice->fetch()) { diff --git a/actions/twitapiusers.php b/actions/twitapiusers.php index 1542cfb33e..b90bbfa985 100644 --- a/actions/twitapiusers.php +++ b/actions/twitapiusers.php @@ -25,110 +25,61 @@ class TwitapiusersAction extends TwitterapiAction { function show($args, $apidata) - { + { parent::handle($args); - if (!in_array($apidata['content-type'], array('xml', 'json'))) { + if (!in_array($apidata['content-type'], array('xml', 'json'))) { $this->clientError(_('API method not found!'), $code = 404); return; } - - $user = null; - $email = $this->arg('email'); - $user_id = $this->arg('user_id'); - if ($email) { - $user = User::staticGet('email', $email); - } elseif ($user_id) { - $user = $this->get_user($user_id); - } elseif (isset($apidata['api_arg'])) { - $user = $this->get_user($apidata['api_arg']); - } elseif (isset($apidata['user'])) { - $user = $apidata['user']; - } - - if (!$user) { - // XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach - $this->client_error(_('Not found.'), 404, $apidata['content-type']); - return; - } + $user = null; + $email = $this->arg('email'); + $user_id = $this->arg('user_id'); - $profile = $user->getProfile(); + // XXX: email field deprecated in Twitter's API - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } + // XXX: Also: need to add screen_name param - $twitter_user = $this->twitter_user_array($profile, true); + if ($email) { + $user = User::staticGet('email', $email); + } elseif ($user_id) { + $user = $this->get_user($user_id); + } elseif (isset($apidata['api_arg'])) { + $user = $this->get_user($apidata['api_arg']); + } elseif (isset($apidata['user'])) { + $user = $apidata['user']; + } - // Add in extended user fields offered up by this method - $twitter_user['created_at'] = $this->date_twitter($profile->created); + if (!$user) { + $this->client_error(_('Not found.'), 404, $apidata['content-type']); + return; + } - $subbed = DB_DataObject::factory('subscription'); - $subbed->subscriber = $profile->id; - $subbed_count = (int) $subbed->count() - 1; + $profile = $user->getProfile(); - $notices = DB_DataObject::factory('notice'); - $notices->profile_id = $profile->id; - $notice_count = (int) $notices->count(); + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } - $twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0; - $twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0; + $twitter_user = $this->twitter_user_array($profile, true); - // Other fields Twitter sends... - $twitter_user['profile_background_color'] = ''; - $twitter_user['profile_background_image_url'] = ''; - $twitter_user['profile_text_color'] = ''; - $twitter_user['profile_link_color'] = ''; - $twitter_user['profile_sidebar_fill_color'] = ''; - $twitter_user['profile_sidebar_border_color'] = ''; - $twitter_user['profile_background_tile'] = false; + if ($apidata['content-type'] == 'xml') { + $this->init_document('xml'); + $this->show_twitter_xml_user($twitter_user); + $this->end_document('xml'); + } elseif ($apidata['content-type'] == 'json') { + $this->init_document('json'); + $this->show_json_objects($twitter_user); + $this->end_document('json'); + } else { - $faves = DB_DataObject::factory('fave'); - $faves->user_id = $user->id; - $faves_count = (int) $faves->count(); - $twitter_user['favourites_count'] = $faves_count; - - $timezone = 'UTC'; - - if ($user->timezone) { - $timezone = $user->timezone; - } - - $t = new DateTime; - $t->setTimezone(new DateTimeZone($timezone)); - $twitter_user['utc_offset'] = $t->format('Z'); - $twitter_user['time_zone'] = $timezone; - - if (isset($apidata['user'])) { - - $twitter_user['following'] = $apidata['user']->isSubscribed($profile); - - // Notifications on? - $sub = Subscription::pkeyGet(array('subscriber' => - $apidata['user']->id, 'subscribed' => $profile->id)); - - if ($sub) { - $twitter_user['notifications'] = ($sub->jabber || $sub->sms); - } - } - - if ($apidata['content-type'] == 'xml') { - $this->init_document('xml'); - $this->show_twitter_xml_user($twitter_user); - $this->end_document('xml'); - } elseif ($apidata['content-type'] == 'json') { - $this->init_document('json'); - $this->show_json_objects($twitter_user); - $this->end_document('json'); - } else { - - // This is in case 'show' was called via /account/verify_credentials - // without a format (xml or json). + // This is in case 'show' was called via /account/verify_credentials + // without a format (xml or json). header('Content-Type: text/html; charset=utf-8'); print 'Authorized'; } - } + } } diff --git a/classes/Fave.php b/classes/Fave.php index 915b4572ff..572334ce4f 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -46,7 +46,7 @@ class Fave extends Memcached_DataObject return $ids; } - function _streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since) + function _streamDirect($user_id, $offset, $limit, $since_id, $max_id, $since) { $fav = new Fave(); @@ -59,8 +59,8 @@ class Fave extends Memcached_DataObject $fav->whereAdd('notice_id > ' . $since_id); } - if ($before_id != 0) { - $fav->whereAdd('notice_id < ' . $before_id); + if ($max_id != 0) { + $fav->whereAdd('notice_id <= ' . $max_id); } if (!is_null($since)) { diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 52ad4100fc..33ac70dd04 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -239,8 +239,14 @@ class Memcached_DataObject extends DB_DataObject $result = parent::_connect(); if (!$exists) { $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; - if (common_config('db', 'utf8')) { - $DB->query('SET NAMES "utf8"'); + if (common_config('db', 'type') == 'mysql' && + common_config('db', 'utf8')) { + $conn = $DB->connection; + if ($DB instanceof DB_mysqli) { + mysqli_set_charset($conn, 'utf8'); + } else if ($DB instanceof DB_mysql) { + mysql_set_charset('utf8', $conn); + } } } return $result; diff --git a/classes/Notice.php b/classes/Notice.php index 530c6daa7d..e4dade7806 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -124,6 +124,8 @@ class Notice extends Memcached_DataObject $profile = Profile::staticGet($profile_id); + $final = common_shorten_links($content); + if (!$profile) { common_log(LOG_ERR, 'Problem saving notice. Unknown user.'); return _('Problem saving notice. Unknown user.'); @@ -134,7 +136,7 @@ class Notice extends Memcached_DataObject return _('Too many notices too fast; take a breather and post again in a few minutes.'); } - if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $content)) { + if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $final)) { common_log(LOG_WARNING, 'Dupe posting by profile #' . $profile_id . '; throttled.'); return _('Too many duplicate messages too quickly; take a breather and post again in a few minutes.'); } @@ -165,8 +167,8 @@ class Notice extends Memcached_DataObject $notice->reply_to = $reply_to; $notice->created = common_sql_now(); - $notice->content = $content; - $notice->rendered = common_render_content($content, $notice); + $notice->content = $final; + $notice->rendered = common_render_content($final, $notice); $notice->source = $source; $notice->uri = $uri; @@ -202,13 +204,9 @@ class Notice extends Memcached_DataObject $notice->saveReplies(); $notice->saveTags(); - $notice->saveGroups(); - if (common_config('queue', 'enabled')) { - $notice->addToAuthorInbox(); - } else { - $notice->addToInboxes(); - } + $notice->addToInboxes(); + $notice->saveGroups(); $notice->query('COMMIT'); @@ -218,13 +216,7 @@ class Notice extends Memcached_DataObject # Clear the cache for subscribed users, so they'll update at next request # XXX: someone clever could prepend instead of clearing the cache - if (common_config('memcached', 'enabled')) { - if (common_config('queue', 'enabled')) { - $notice->blowAuthorCaches(); - } else { - $notice->blowCaches(); - } - } + $notice->blowCaches(); return $notice; } @@ -307,17 +299,6 @@ class Notice extends Memcached_DataObject $this->blowGroupCache($blowLast); } - function blowAuthorCaches($blowLast=false) - { - // Clear the user's cache - $cache = common_memcache(); - if (!empty($cache)) { - $cache->delete(common_cache_key('notice_inbox:by_user:'.$this->profile_id)); - } - $this->blowNoticeCache($blowLast); - $this->blowPublicCache($blowLast); - } - function blowGroupCache($blowLast=false) { $cache = common_memcache(); @@ -453,22 +434,22 @@ class Notice extends Memcached_DataObject # XXX: too many args; we need to move to named params or even a separate # class for notice streams - static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $order=null, $since=null) { + static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $order=null, $since=null) { if (common_config('memcached', 'enabled')) { - # Skip the cache if this is a since, since_id or before_id qry - if ($since_id > 0 || $before_id > 0 || $since) { - return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since); + # Skip the cache if this is a since, since_id or max_id qry + if ($since_id > 0 || $max_id > 0 || $since) { + return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since); } else { return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order); } } - return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since); + return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since); } - static function getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since) { + static function getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since) { $needAnd = false; $needWhere = true; @@ -490,7 +471,7 @@ class Notice extends Memcached_DataObject $qry .= ' notice.id > ' . $since_id; } - if ($before_id > 0) { + if ($max_id > 0) { if ($needWhere) { $qry .= ' WHERE '; @@ -499,7 +480,7 @@ class Notice extends Memcached_DataObject $qry .= ' AND '; } - $qry .= ' notice.id < ' . $before_id; + $qry .= ' notice.id <= ' . $max_id; } if ($since) { @@ -657,17 +638,17 @@ class Notice extends Memcached_DataObject } } - function publicStream($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null) + function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null) { $ids = Notice::stream(array('Notice', '_publicStreamDirect'), array(), 'public', - $offset, $limit, $since_id, $before_id, $since); + $offset, $limit, $since_id, $max_id, $since); return Notice::getStreamByIds($ids); } - function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null) + function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null) { $notice = new Notice(); @@ -691,8 +672,8 @@ class Notice extends Memcached_DataObject $notice->whereAdd('id > ' . $since_id); } - if ($before_id != 0) { - $notice->whereAdd('id < ' . $before_id); + if ($max_id != 0) { + $notice->whereAdd('id <= ' . $max_id); } if (!is_null($since)) { @@ -736,33 +717,6 @@ class Notice extends Memcached_DataObject return; } - function addToAuthorInbox() - { - $enabled = common_config('inboxes', 'enabled'); - - if ($enabled === true || $enabled === 'transitional') { - $user = User::staticGet('id', $this->profile_id); - if (empty($user)) { - return; - } - $inbox = new Notice_inbox(); - $UT = common_config('db','type')=='pgsql'?'"user"':'user'; - $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created) ' . - "SELECT $UT.id, " . $this->id . ", '" . $this->created . "' " . - "FROM $UT " . - "WHERE $UT.id = " . $this->profile_id . ' ' . - 'AND NOT EXISTS (SELECT user_id, notice_id ' . - 'FROM notice_inbox ' . - "WHERE user_id = " . $this->profile_id . ' '. - 'AND notice_id = ' . $this->id . ' )'; - if ($enabled === 'transitional') { - $qry .= " AND $UT.inboxed = 1"; - } - $inbox->query($qry); - } - return; - } - function saveGroups() { $enabled = common_config('inboxes', 'enabled'); @@ -1034,15 +988,15 @@ class Notice extends Memcached_DataObject } } - function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $since=null, $tag=null) + function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null) { $cache = common_memcache(); if (empty($cache) || - $since_id != 0 || $before_id != 0 || !is_null($since) || + $since_id != 0 || $max_id != 0 || (!is_null($since) && $since > 0) || ($offset + $limit) > NOTICE_CACHE_WINDOW) { return call_user_func_array($fn, array_merge($args, array($offset, $limit, $since_id, - $before_id, $since, $tag))); + $max_id, $since))); } $idkey = common_cache_key($cachekey); diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php index dec14b0d18..8a27e17478 100644 --- a/classes/Notice_inbox.php +++ b/classes/Notice_inbox.php @@ -43,15 +43,15 @@ class Notice_inbox extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function stream($user_id, $offset, $limit, $since_id, $before_id, $since) + function stream($user_id, $offset, $limit, $since_id, $max_id, $since) { return Notice::stream(array('Notice_inbox', '_streamDirect'), array($user_id), 'notice_inbox:by_user:'.$user_id, - $offset, $limit, $since_id, $before_id, $since); + $offset, $limit, $since_id, $max_id, $since); } - function _streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since) + function _streamDirect($user_id, $offset, $limit, $since_id, $max_id, $since) { $inbox = new Notice_inbox(); @@ -61,8 +61,8 @@ class Notice_inbox extends Memcached_DataObject $inbox->whereAdd('notice_id > ' . $since_id); } - if ($before_id != 0) { - $inbox->whereAdd('notice_id < ' . $before_id); + if ($max_id != 0) { + $inbox->whereAdd('notice_id <= ' . $max_id); } if (!is_null($since)) { diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php index e5b7722430..758a665947 100644 --- a/classes/Notice_tag.php +++ b/classes/Notice_tag.php @@ -46,7 +46,7 @@ class Notice_tag extends Memcached_DataObject return Notice::getStreamByIds($ids); } - function _streamDirect($tag, $offset, $limit, $since_id, $before_id, $since) + function _streamDirect($tag, $offset, $limit, $since_id, $max_id, $since) { $nt = new Notice_tag(); @@ -59,8 +59,8 @@ class Notice_tag extends Memcached_DataObject $nt->whereAdd('notice_id > ' . $since_id); } - if ($before_id != 0) { - $nt->whereAdd('notice_id < ' . $before_id); + if ($max_id != 0) { + $nt->whereAdd('notice_id < ' . $max_id); } if (!is_null($since)) { diff --git a/classes/Profile.php b/classes/Profile.php index afc0ea4f74..4a459b9740 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -170,7 +170,7 @@ class Profile extends Memcached_DataObject $ids = Notice::stream(array($this, '_streamDirect'), array(), 'profile:notice_ids:' . $this->id, - $offset, $limit, $since_id, $before_id, $since); + $offset, $limit, $since_id, $max_id, $since); return Notice::getStreamByIds($ids); } @@ -225,8 +225,8 @@ class Profile extends Memcached_DataObject $notice->whereAdd('id > ' . $since_id); } - if ($before_id != 0) { - $notice->whereAdd('id < ' . $before_id); + if ($max_id != 0) { + $notice->whereAdd('id <= ' . $max_id); } if (!is_null($since)) { diff --git a/classes/Reply.php b/classes/Reply.php index 4439053b44..49b1e05e51 100644 --- a/classes/Reply.php +++ b/classes/Reply.php @@ -22,16 +22,16 @@ class Reply extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null) { $ids = Notice::stream(array('Reply', '_streamDirect'), array($user_id), 'reply:stream:' . $user_id, - $offset, $limit, $since_id, $before_id, $since); + $offset, $limit, $since_id, $max_id, $since); return $ids; } - function _streamDirect($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + function _streamDirect($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null) { $reply = new Reply(); $reply->profile_id = $user_id; @@ -40,8 +40,8 @@ class Reply extends Memcached_DataObject $reply->whereAdd('notice_id > ' . $since_id); } - if ($before_id != 0) { - $reply->whereAdd('notice_id < ' . $before_id); + if ($max_id != 0) { + $reply->whereAdd('notice_id < ' . $max_id); } if (!is_null($since)) { diff --git a/classes/User.php b/classes/User.php index ea8ba40817..08a166d5ae 100644 --- a/classes/User.php +++ b/classes/User.php @@ -403,7 +403,6 @@ class User extends Memcached_DataObject function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) { $ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id, $since); - common_debug("Ids = " . implode(',', $ids)); return Notice::getStreamByIds($ids); } diff --git a/classes/User_group.php b/classes/User_group.php index 7cc31e7026..a135015bac 100644 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -58,7 +58,7 @@ class User_group extends Memcached_DataObject return Notice::getStreamByIds($ids); } - function _streamDirect($offset, $limit, $since_id, $before_id, $since) + function _streamDirect($offset, $limit, $since_id, $max_id, $since) { $inbox = new Group_inbox(); @@ -71,8 +71,8 @@ class User_group extends Memcached_DataObject $inbox->whereAdd('notice_id > ' . $since_id); } - if ($before_id != 0) { - $inbox->whereAdd('notice_id < ' . $before_id); + if ($max_id != 0) { + $inbox->whereAdd('notice_id <= ' . $max_id); } if (!is_null($since)) { diff --git a/db/notice_source.sql b/db/notice_source.sql index d5a280b82c..983ea91502 100644 --- a/db/notice_source.sql +++ b/db/notice_source.sql @@ -2,7 +2,9 @@ INSERT INTO notice_source (code, name, url, created) VALUES ('adium', 'Adium', 'http://www.adiumx.com/', now()), + ('Afficheur', 'Afficheur', 'http://afficheur.sourceforge.jp/', now()), ('AgentSolo.com','AgentSolo.com','http://www.agentsolo.com/', now()), + ('anyio', 'Any.IO', 'http://any.io/', now()), ('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', now()), ('bti','bti','http://gregkh.github.com/bti/', now()), ('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()), @@ -11,6 +13,7 @@ VALUES ('eventbox','EventBox','http://thecosmicmachine.com/eventbox/ ', now()), ('Facebook','Facebook','http://apps.facebook.com/identica/', now()), ('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', now()), + ('get2gnow', 'get2gnow', 'http://uberchicgeekchick.com/?projects=get2gnow', now()), ('gravity', 'Gravity', 'http://mobileways.de/gravity', now()), ('Gwibber','Gwibber','http://launchpad.net/gwibber', now()), ('HelloTxt','HelloTxt','http://hellotxt.com/', now()), diff --git a/js/util.js b/js/util.js index b712ba8e2b..786b763eee 100644 --- a/js/util.js +++ b/js/util.js @@ -178,7 +178,14 @@ $(document).ready(function(){ $('#form_notice').append(document._importNode($(".error", xhr.responseXML).get(0), true)); } else { - alert("Sorry! We had trouble sending your notice ("+xhr.status+" "+xhr.statusText+"). Please report the problem to the site administrator if this happens again."); + var HTTP20x30x = [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307]; + if(jQuery.inArray(parseInt(xhr.status), HTTP20x30x) < 0) { + alert("Sorry! We had trouble sending your notice ("+xhr.status+" "+xhr.statusText+"). Please report the problem to the site administrator if this happens again."); + } + else { + $("#notice_data-text").val(""); + counter(); + } } } }, diff --git a/lib/action.php b/lib/action.php index 6a69d26518..7643e5afb8 100644 --- a/lib/action.php +++ b/lib/action.php @@ -124,7 +124,6 @@ class Action extends HTMLOutputter // lawsuit $this->showShortcutIcon(); $this->showStylesheets(); $this->showScripts(); - $this->showRelationshipLinks(); $this->showOpenSearch(); $this->showFeeds(); $this->showDescription(); @@ -267,19 +266,6 @@ class Action extends HTMLOutputter // lawsuit } } - /** - * Show document relationship links - * - * SHOULD overload - * - * @return nothing - */ - function showRelationshipLinks() - { - // output elements with appropriate HTML4.01 link types: - // http://www.w3.org/TR/html401/types.html#type-links - } - /** * Show OpenSearch headers * @@ -1063,36 +1049,4 @@ class Action extends HTMLOutputter // lawsuit { return null; } - - /** - * Generate document metadata for sequential navigation - * - * @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 sequenceRelationships($have_next, $have_previous, $page, $action, $args=null) - { - // Outputs machine-readable pagination in elements. - // Pattern taken from $this->pagination() method. - - // "next" is equivalent to "after" - if ($have_next) { - $pargs = array('page' => $page-1); - $this->element('link', array('rel' => 'next', - 'href' => common_local_url($action, $args, $pargs), - 'title' => _('Next'))); - } - // "previous" is equivalent to "before" - if ($have_previous=true) { // FIXME - $pargs = array('page' => $page+1); - $this->element('link', array('rel' => 'prev', - 'href' => common_local_url($action, $args, $pargs), - 'title' => _('Previous'))); - } - } } diff --git a/lib/mail.php b/lib/mail.php index 27a1d99dcb..4e1f1dbb1d 100644 --- a/lib/mail.php +++ b/lib/mail.php @@ -335,6 +335,7 @@ function mail_broadcast_notice_sms($notice) "FROM $UT JOIN subscription " . "ON $UT.id = subscription.subscriber " . 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' . + 'AND subscription.subscribed != subscription.subscriber ' . "AND $UT.smsemail IS NOT null " . "AND $UT.smsnotify = 1 " . 'AND subscription.sms = 1 '); diff --git a/lib/omb.php b/lib/omb.php index e8e1acc413..40cb847dfa 100644 --- a/lib/omb.php +++ b/lib/omb.php @@ -159,13 +159,9 @@ function omb_post_notice($notice, $remote_profile, $subscription) function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret) { - - common_debug('Posting notice ' . $notice->id . ' to ' . $postnoticeurl, __FILE__); - $user = User::staticGet('id', $notice->profile_id); if (!$user) { - common_debug('Failed to get user for notice ' . $notice->id . ', profile = ' . $notice->profile_id, __FILE__); return false; } @@ -208,8 +204,6 @@ function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret) $req->to_postdata(), array('User-Agent: Laconica/' . LACONICA_VERSION)); - common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__); - if ($result->status == 403) { # not authorized, don't send again common_debug('403 result, deleting subscription', __FILE__); # FIXME: figure out how to delete this @@ -286,14 +280,10 @@ function omb_update_profile($profile, $remote_profile, $subscription) $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); - common_debug('request URL = '.$req->get_normalized_http_url(), __FILE__); - common_debug('postdata = '.$req->to_postdata(), __FILE__); $result = $fetcher->post($req->get_normalized_http_url(), $req->to_postdata(), array('User-Agent: Laconica/' . LACONICA_VERSION)); - common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__); - if (empty($result) || !$result) { common_debug("Unable to contact " . $req->get_normalized_http_url()); } else if ($result->status == 403) { # not authorized, don't send again diff --git a/lib/twitterapi.php b/lib/twitterapi.php index caf8c07163..ca8b03cdcd 100644 --- a/lib/twitterapi.php +++ b/lib/twitterapi.php @@ -54,7 +54,7 @@ class TwitterapiAction extends Action /** * Overrides XMLOutputter::element to write booleans as strings (true|false). * See that method's documentation for more info. - * + * * @param string $tag Element type or tagname * @param array $attrs Array of element attributes, as * key-value pairs @@ -70,24 +70,85 @@ class TwitterapiAction extends Action return parent::element($tag, $attrs, $content); } - + function twitter_user_array($profile, $get_notice=false) { - $twitter_user = array(); - $twitter_user['name'] = $profile->getBestName(); - $twitter_user['followers_count'] = $this->count_subscriptions($profile); - $twitter_user['screen_name'] = $profile->nickname; - $twitter_user['description'] = ($profile->bio) ? $profile->bio : null; - $twitter_user['location'] = ($profile->location) ? $profile->location : null; $twitter_user['id'] = intval($profile->id); + $twitter_user['name'] = $profile->getBestName(); + $twitter_user['screen_name'] = $profile->nickname; + $twitter_user['location'] = ($profile->location) ? $profile->location : null; + $twitter_user['description'] = ($profile->bio) ? $profile->bio : null; $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() : + Avatar::defaultImage(AVATAR_STREAM_SIZE); - $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE); - $twitter_user['protected'] = false; # not supported by Laconica yet $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null; + $twitter_user['protected'] = false; # not supported by Laconica yet + $twitter_user['followers_count'] = $this->count_subscriptions($profile); + + // 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'] = ''; + $twitter_user['profile_sidebar_border_color'] = ''; + + $subbed = DB_DataObject::factory('subscription'); + $subbed->subscriber = $profile->id; + $subbed_count = (int) $subbed->count() - 1; + $twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0; + + $twitter_user['created_at'] = $this->date_twitter($profile->created); + + $faves = DB_DataObject::factory('fave'); + $faves->user_id = $user->id; + $faves_count = (int) $faves->count(); + $twitter_user['favourites_count'] = $faves_count; // British spelling! + + // Need to pull up the user for some of this + $user = User::staticGet($profile->id); + + $timezone = 'UTC'; + + if ($user->timezone) { + $timezone = $user->timezone; + } + + $t = new DateTime; + $t->setTimezone(new DateTimeZone($timezone)); + + $twitter_user['utc_offset'] = $t->format('Z'); + $twitter_user['time_zone'] = $timezone; + + // To be supported some day, perhaps + $twitter_user['profile_background_image_url'] = ''; + $twitter_user['profile_background_tile'] = false; + + $notices = DB_DataObject::factory('notice'); + $notices->profile_id = $profile->id; + $notice_count = (int) $notices->count(); + + $twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0; + + // Is the requesting user following this user? + $twitter_user['following'] = false; + $twitter_user['notifications'] = false; + + if (isset($apidata['user'])) { + + $twitter_user['following'] = $apidata['user']->isSubscribed($profile); + + // Notifications on? + $sub = Subscription::pkeyGet(array('subscriber' => + $apidata['user']->id, 'subscribed' => $profile->id)); + + if ($sub) { + $twitter_user['notifications'] = ($sub->jabber || $sub->sms); + } + } if ($get_notice) { $notice = $profile->getCurrentNotice(); @@ -612,7 +673,27 @@ class TwitterapiAction extends Action function get_user($id, $apidata=null) { if (!$id) { - return $apidata['user']; + + // Twitter supports these other ways of passing the user ID + if (is_numeric($this->arg('id'))) { + return User::staticGet($this->arg('id')); + } else if ($this->arg('id')) { + $nickname = common_canonical_nickname($this->arg('id')); + return User::staticGet('nickname', $nickname); + } else if ($this->arg('user_id')) { + // This is to ensure that a non-numeric user_id still + // overrides screen_name even if it doesn't get used + if (is_numeric($this->arg('user_id'))) { + return User::staticGet('id', $this->arg('user_id')); + } + } else if ($this->arg('screen_name')) { + $nickname = common_canonical_nickname($this->arg('screen_name')); + return User::staticGet('nickname', $nickname); + } else { + // Fall back to trying the currently authenticated user + return $apidata['user']; + } + } else if (is_numeric($id)) { return User::staticGet($id); } else { diff --git a/lib/util.php b/lib/util.php index ab12723098..b3a94a5a01 100644 --- a/lib/util.php +++ b/lib/util.php @@ -900,6 +900,33 @@ function common_enqueue_notice($notice) return $result; } +function common_post_inbox_transports() +{ + $transports = array('omb', 'sms'); + + if (common_config('xmpp', 'enabled')) { + $transports = array_merge($transports, array('jabber', 'public')); + } + + return $transports; +} + +function common_enqueue_notice_transport($notice, $transport) +{ + $qi = new Queue_item(); + $qi->notice_id = $notice->id; + $qi->transport = $transport; + $qi->created = $notice->created; + $result = $qi->insert(); + if (!$result) { + $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); + common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message); + throw new ServerException('DB error inserting queue item: ' . $last_error->message); + } + common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport); + return true; +} + function common_real_broadcast($notice, $remote=false) { $success = true; diff --git a/scripts/fixup_utf8.php b/scripts/fixup_utf8.php index e5021ff343..1693760914 100644 --- a/scripts/fixup_utf8.php +++ b/scripts/fixup_utf8.php @@ -35,107 +35,334 @@ define('LACONICA', true); require_once(INSTALLDIR . '/lib/common.php'); require_once('DB.php'); -function fixup_utf8($id) { +class UTF8FixerUpper +{ + var $dbl = null; + var $dbu = null; + var $args = array(); - $dbl = doConnect('latin1'); + function __construct($args) + { + $this->args = $args; - if (empty($dbl)) { - return; + if (array_key_exists('max_date', $args)) { + $this->max_date = strftime('%Y-%m-%d %H:%M:%S', strtotime($args['max_date'])); + } else { + $this->max_date = strftime('%Y-%m-%d %H:%M:%S', time()); + } + + $this->dbl = $this->doConnect('latin1'); + + if (empty($this->dbl)) { + return; + } + + $this->dbu = $this->doConnect('utf8'); + + if (empty($this->dbu)) { + return; + } } - $dbu = doConnect('utf8'); + function doConnect($charset) + { + $db = DB::connect(common_config('db', 'database'), + array('persistent' => false)); - if (empty($dbu)) { - return; - } + if (PEAR::isError($db)) { + echo "ERROR: " . $db->getMessage() . "\n"; + return NULL; + } - // Do a separate DB connection + $conn = $db->connection; - $sth = $dbu->prepare("UPDATE notice SET content = UNHEX(?), rendered = UNHEX(?) WHERE id = ?"); + $succ = mysqli_set_charset($conn, $charset); - if (PEAR::isError($sth)) { - echo "ERROR: " . $sth->getMessage() . "\n"; - return; - } + if (!$succ) { + echo "ERROR: couldn't set charset\n"; + $db->disconnect(); + return NULL; + } - $sql = 'SELECT id, content, rendered FROM notice ' . - 'WHERE LENGTH(content) != CHAR_LENGTH(content)'; - - if (!empty($id)) { - $sql .= ' AND id < ' . $id; - } - - $sql .= ' ORDER BY id DESC'; - - $rn = $dbl->query($sql); - - if (PEAR::isError($rn)) { - echo "ERROR: " . $rn->getMessage() . "\n"; - return; - } - - echo "Number of rows: " . $rn->numRows() . "\n"; - - $notice = array(); - - while (DB_OK == $rn->fetchInto($notice)) { - - $id = ($notice[0])+0; - $content = bin2hex($notice[1]); - $rendered = bin2hex($notice[2]); - - echo "$id..."; - - $result =& $dbu->execute($sth, array($content, $rendered, $id)); + $result = $db->autoCommit(true); if (PEAR::isError($result)) { echo "ERROR: " . $result->getMessage() . "\n"; - continue; + $db->disconnect(); + return NULL; } - $cnt = $dbu->affectedRows(); + return $db; + } - if ($cnt != 1) { - echo "ERROR: 0 rows affected\n"; - continue; + function fixup() + { + $this->fixupNotices($this->args['max_notice'], + $this->args['min_notice']); + $this->fixupProfiles(); + $this->fixupGroups(); + $this->fixupMessages(); + } + + function fixupNotices($max_id, $min_id) { + + // Do a separate DB connection + + $sth = $this->dbu->prepare("UPDATE notice SET content = UNHEX(?), rendered = UNHEX(?) WHERE id = ?"); + + if (PEAR::isError($sth)) { + echo "ERROR: " . $sth->getMessage() . "\n"; + return; } - $notice = Notice::staticGet('id', $id); - $notice->decache(); + $sql = 'SELECT id, content, rendered FROM notice ' . + 'WHERE LENGTH(content) != CHAR_LENGTH(content) '. + 'AND modified < "'.$this->max_date.'" '; - echo "OK\n"; + if (!empty($max_id)) { + $sql .= ' AND id <= ' . $max_id; + } + + if (!empty($min_id)) { + $sql .= ' AND id >= ' . $min_id; + } + + $sql .= ' ORDER BY id DESC'; + + $rn = $this->dbl->query($sql); + + if (PEAR::isError($rn)) { + echo "ERROR: " . $rn->getMessage() . "\n"; + return; + } + + echo "Number of rows: " . $rn->numRows() . "\n"; + + $notice = array(); + + while (DB_OK == $rn->fetchInto($notice)) { + + $id = ($notice[0])+0; + $content = bin2hex($notice[1]); + $rendered = bin2hex($notice[2]); + + echo "$id..."; + + $result =& $this->dbu->execute($sth, array($content, $rendered, $id)); + + if (PEAR::isError($result)) { + echo "ERROR: " . $result->getMessage() . "\n"; + continue; + } + + $cnt = $this->dbu->affectedRows(); + + if ($cnt != 1) { + echo "ERROR: 0 rows affected\n"; + continue; + } + + $notice = Notice::staticGet('id', $id); + $notice->decache(); + $notice->free(); + + echo "OK\n"; + } + } + + function fixupProfiles() + { + // Do a separate DB connection + + $sth = $this->dbu->prepare("UPDATE profile SET ". + "fullname = UNHEX(?),". + "location = UNHEX(?), ". + "bio = UNHEX(?) ". + "WHERE id = ?"); + + if (PEAR::isError($sth)) { + echo "ERROR: " . $sth->getMessage() . "\n"; + return; + } + + $sql = 'SELECT id, fullname, location, bio FROM profile ' . + 'WHERE (LENGTH(fullname) != CHAR_LENGTH(fullname) '. + 'OR LENGTH(location) != CHAR_LENGTH(location) '. + 'OR LENGTH(bio) != CHAR_LENGTH(bio)) '. + 'AND modified < "'.$this->max_date.'" '. + ' ORDER BY modified DESC'; + + $rn = $this->dbl->query($sql); + + if (PEAR::isError($rn)) { + echo "ERROR: " . $rn->getMessage() . "\n"; + return; + } + + echo "Number of rows: " . $rn->numRows() . "\n"; + + $profile = array(); + + while (DB_OK == $rn->fetchInto($profile)) { + + $id = ($profile[0])+0; + $fullname = bin2hex($profile[1]); + $location = bin2hex($profile[2]); + $bio = bin2hex($profile[3]); + + echo "$id..."; + + $result =& $this->dbu->execute($sth, array($fullname, $location, $bio, $id)); + + if (PEAR::isError($result)) { + echo "ERROR: " . $result->getMessage() . "\n"; + continue; + } + + $cnt = $this->dbu->affectedRows(); + + if ($cnt != 1) { + echo "ERROR: 0 rows affected\n"; + continue; + } + + $profile = Profile::staticGet('id', $id); + $profile->decache(); + $profile->free(); + + echo "OK\n"; + } + } + + function fixupGroups() + { + // Do a separate DB connection + + $sth = $this->dbu->prepare("UPDATE user_group SET ". + "fullname = UNHEX(?),". + "location = UNHEX(?), ". + "description = UNHEX(?) ". + "WHERE id = ?"); + + if (PEAR::isError($sth)) { + echo "ERROR: " . $sth->getMessage() . "\n"; + return; + } + + $sql = 'SELECT id, fullname, location, description FROM user_group ' . + 'WHERE LENGTH(fullname) != CHAR_LENGTH(fullname) '. + 'OR LENGTH(location) != CHAR_LENGTH(location) '. + 'OR LENGTH(description) != CHAR_LENGTH(description) '; + 'AND modified < "'.$this->max_date.'" '. + 'ORDER BY modified DESC'; + + $rn = $this->dbl->query($sql); + + if (PEAR::isError($rn)) { + echo "ERROR: " . $rn->getMessage() . "\n"; + return; + } + + echo "Number of rows: " . $rn->numRows() . "\n"; + + $user_group = array(); + + while (DB_OK == $rn->fetchInto($user_group)) { + + $id = ($user_group[0])+0; + $fullname = bin2hex($user_group[1]); + $location = bin2hex($user_group[2]); + $description = bin2hex($user_group[3]); + + echo "$id..."; + + $result =& $this->dbu->execute($sth, array($fullname, $location, $description, $id)); + + if (PEAR::isError($result)) { + echo "ERROR: " . $result->getMessage() . "\n"; + continue; + } + + $cnt = $this->dbu->affectedRows(); + + if ($cnt != 1) { + echo "ERROR: 0 rows affected\n"; + continue; + } + + $user_group = User_group::staticGet('id', $id); + $user_group->decache(); + $user_group->free(); + + echo "OK\n"; + } + } + + function fixupMessages() { + + // Do a separate DB connection + + $sth = $this->dbu->prepare("UPDATE message SET content = UNHEX(?), rendered = UNHEX(?) WHERE id = ?"); + + if (PEAR::isError($sth)) { + echo "ERROR: " . $sth->getMessage() . "\n"; + return; + } + + $sql = 'SELECT id, content, rendered FROM message ' . + 'WHERE LENGTH(content) != CHAR_LENGTH(content) '. + 'AND modified < "'.$this->max_date.'" '. + 'ORDER BY id DESC'; + + $rn = $this->dbl->query($sql); + + if (PEAR::isError($rn)) { + echo "ERROR: " . $rn->getMessage() . "\n"; + return; + } + + echo "Number of rows: " . $rn->numRows() . "\n"; + + $message = array(); + + while (DB_OK == $rn->fetchInto($message)) { + + $id = ($message[0])+0; + $content = bin2hex($message[1]); + $rendered = bin2hex($message[2]); + + echo "$id..."; + + $result =& $this->dbu->execute($sth, array($content, $rendered, $id)); + + if (PEAR::isError($result)) { + echo "ERROR: " . $result->getMessage() . "\n"; + continue; + } + + $cnt = $this->dbu->affectedRows(); + + if ($cnt != 1) { + echo "ERROR: 0 rows affected\n"; + continue; + } + + $message = Message::staticGet('id', $id); + $message->decache(); + $message->free(); + + echo "OK\n"; + } } } -function doConnect($charset) -{ - $db = DB::connect(common_config('db', 'database'), - array('persistent' => false)); +$max_date = ($argc > 1) ? $argv[1] : null; +$max_id = ($argc > 2) ? $argv[2] : null; +$min_id = ($argc > 3) ? $argv[3] : null; - if (PEAR::isError($db)) { - echo "ERROR: " . $db->getMessage() . "\n"; - return NULL; - } +$fixer = new UTF8FixerUpper(array('max_date' => $max_date, + 'max_notice' => $max_id, + 'min_notice' => $min_id)); - $result = $db->query("SET NAMES $charset"); +$fixer->fixup(); - if (PEAR::isError($result)) { - echo "ERROR: " . $result->getMessage() . "\n"; - $db->disconnect(); - return NULL; - } - - $result = $db->autoCommit(true); - - if (PEAR::isError($result)) { - echo "ERROR: " . $result->getMessage() . "\n"; - $db->disconnect(); - return NULL; - } - - return $db; -} - -$id = ($argc > 1) ? $argv[1] : null; - -fixup_utf8($id); diff --git a/scripts/getvaliddaemons.php b/scripts/getvaliddaemons.php index a10233e69f..4e49f9bd4b 100755 --- a/scripts/getvaliddaemons.php +++ b/scripts/getvaliddaemons.php @@ -25,7 +25,6 @@ * daemon names. */ - # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { print "This script must be run from the command line\n"; @@ -51,5 +50,4 @@ echo "ombqueuehandler.php "; echo "twitterqueuehandler.php "; echo "facebookqueuehandler.php "; echo "pingqueuehandler.php "; -echo "inboxqueuehandler.php "; echo "smsqueuehandler.php "; diff --git a/scripts/inboxqueuehandler.php b/scripts/inboxqueuehandler.php deleted file mode 100755 index 73d31e8542..0000000000 --- a/scripts/inboxqueuehandler.php +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env php -. - */ - -// Abort if called from a web server - -if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); -} - -define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); -define('LACONICA', true); - -require_once(INSTALLDIR . '/lib/common.php'); -require_once(INSTALLDIR . '/lib/queuehandler.php'); - -set_error_handler('common_error_handler'); - -class InboxQueueHandler extends QueueHandler -{ - function transport() - { - return 'inbox'; - } - - function start() { - $this->log(LOG_INFO, "INITIALIZE"); - return true; - } - - function handle_notice($notice) - { - $this->log(LOG_INFO, "Distributing notice to inboxes for $notice->id"); - $notice->addToInboxes(); - $notice->blowSubsCache(); - return true; - } - - function finish() { - } -} - -ini_set("max_execution_time", "0"); -ini_set("max_input_time", "0"); -set_time_limit(0); -mb_internal_encoding('UTF-8'); - -$id = ($argc > 1) ? $argv[1] : null; - -$handler = new InboxQueueHandler($id); - -$handler->runOnce(); diff --git a/scripts/memcachedqueuehandler.php b/scripts/memcachedqueuehandler.php deleted file mode 100755 index 185b781f75..0000000000 --- a/scripts/memcachedqueuehandler.php +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env php -. - */ - -// Abort if called from a web server - -if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); -} - -define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); -define('LACONICA', true); - -require_once(INSTALLDIR . '/lib/common.php'); -require_once(INSTALLDIR . '/lib/queuehandler.php'); - -set_error_handler('common_error_handler'); - -class MemcachedQueueHandler extends QueueHandler -{ - function transport() - { - return 'memcache'; - } - - function start() { - $this->log(LOG_INFO, "INITIALIZE"); - return true; - } - - function handle_notice($notice) - { - // XXX: fork here - $this->log(LOG_INFO, "Blowing memcached for $notice->id"); - $notice->blowCaches(); - return true; - } - - function finish() { - } - -} - -ini_set("max_execution_time", "0"); -ini_set("max_input_time", "0"); -set_time_limit(0); -mb_internal_encoding('UTF-8'); - -$id = ($argc > 1) ? $argv[1] : null; - -$handler = new MemcachedQueueHandler($id); - -$handler->runOnce(); diff --git a/scripts/stopdaemons.sh b/scripts/stopdaemons.sh index 764037e8ff..2134b4ab00 100755 --- a/scripts/stopdaemons.sh +++ b/scripts/stopdaemons.sh @@ -24,8 +24,7 @@ SDIR=`dirname $0` DIR=`php $SDIR/getpiddir.php` for f in jabberhandler ombhandler publichandler smshandler pinghandler \ - xmppconfirmhandler xmppdaemon twitterhandler facebookhandler \ - memcachehandler inboxhandler twitterstatusfetcher; do + xmppconfirmhandler xmppdaemon twitterhandler facebookhandler; do FILES="$DIR/$f.*.pid" for ff in "$FILES" ; do