Merge branch '0.7.x' into 0.8.x

Conflicts:
	classes/Notice.php
	classes/Profile.php
	lib/common.php
	lib/util.php
	scripts/getvaliddaemons.php
	scripts/stopdaemons.sh
This commit is contained in:
Evan Prodromou 2009-06-08 11:55:32 -07:00
commit adfb79a9bb
26 changed files with 600 additions and 482 deletions

60
README
View File

@ -2,8 +2,8 @@
README README
------ ------
Laconica 0.7.3 ("You Are The Everything") Laconica 0.7.4 ("Can't Get There From Here")
7 April 2009 29 May 2009
This is the README file for Laconica, the Open Source microblogging This is the README file for Laconica, the Open Source microblogging
platform. It includes installation instructions, descriptions of platform. It includes installation instructions, descriptions of
@ -71,29 +71,29 @@ for additional terms.
New this version New this version
================ ================
This is a minor bug-fix and feature release since version 0.7.2.1, This is a minor bug-fix and feature release since version 0.7.3,
released Mar 11 2009. Notable changes this version: released Apr 4 2009. Notable changes this version:
- A plugin to allow a templating language for customization - Improved handling of UTF-8 characters. The new code is *not* backwards
- A plugin for Piwik Analytics engine compatible by default; see "Upgrading" below for instructions on
- A bookmarklet for posting a notice about a Web page you're reading converting existing databases to the correct character set.
- A welcome notice ('welcomebot') and default subscription for new users - Unroll joins for large queries. This greatly enhanced database
- Support for SSL for some or all pages on the site performance -- up to 50x for some queries -- at the expense of making
- Better handling of empty notice lists on many pages an extra DB hit for some queries.
- Major improvements to the Twitter friend-sync offline processing - Added an optional plugin to use WikiHashtags
- subscribers, subscriptions, groups are listed on the Personal page. (http://hashtags.wikia.com/) for the sidebar on hashtag pages.
- "Invite" link restored to main menu - Optimized Twitter friend synchronization.
- Better memory handling in FOAF output - Better error handling for Ajax posting of notices, including
- Fix for SUP support (FriendFeed) HTTP errors and timeouts.
- Correct and intelligent redirect HTTP status codes - Experimental Comet plugin -- supports the cometd and the Bayeux
- Fix DB collations for search and sort protocol. Using this plugin, you can show "real time" updates on the
- Better H1s and Titles using user full names public and tag pages. However, server configuration is complex.
- Fixes to make the linkback plugin operational - If queues are enabled, update inboxes and memcached off-line. Speeds
- Better indication that a notice is being published by Ajax (spinner) up posting considerably.
- Better and unified Atom output - Correctly shorten links posted through XMPP.
- Hiding "register" and "join now" messages when site is closed - <link> elements for pagination, supported by some browsers like Opera.
- ping, twitter and facebook queuehandlers working better - Corrected date format in search API.
- Updated RPM spec - Made the public XRDS file work correctly.
Prerequisites 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 1. Unpack the tarball you downloaded on your Web server. Usually a
command like this will work: 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 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 may have to unpack the tarball on your local computer and FTP the
files to the server.) 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 2. Move the tarball to a directory of your choosing in your Web root
directory. Usually something like this will work: 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 This will make your Laconica instance available in the mublog path of
your server, like "http://example.net/mublog". "microblog" or 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 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 tracking the "git" version of the software, you will probably want
to upgrade and keep your existing data. There is no automated upgrade 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. to the end first before trying them.
0. Download Laconica and set up all the prerequisites as if you were 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 T_STRING") in the browser, check to see that you don't have any
conflicts in your code. 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 section above, and all your users' 'Personal' tabs are empty, read the
"Notice inboxes" section above. "Notice inboxes" section above.
@ -1330,6 +1330,8 @@ if anyone's been overlooked in error.
* Ken Sedgwick * Ken Sedgwick
* Brian Hendrickson * Brian Hendrickson
* Tobias Diekershoff * Tobias Diekershoff
* Dan Moore
* Fil
Thanks also to the developers of our upstream library code and to the 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, thousands of people who have tried out Identi.ca, installed Laconi.ca,

View File

@ -144,8 +144,8 @@ class ApiAction extends Action
} }
if (in_array($fullname, $bareauth)) { if (in_array($fullname, $bareauth)) {
# bareauth: only needs auth if without an argument # bareauth: only needs auth if without an argument or query param specifying user
if ($this->api_arg) { if ($this->api_arg || $this->arg('id') || is_numeric($this->arg('user_id')) || $this->arg('screen_name')) {
return false; return false;
} else { } else {
return true; return true;

View File

@ -122,7 +122,7 @@ class ShownoticeAction extends Action
function lastModified() function lastModified()
{ {
return max(strtotime($this->notice->created), return max(strtotime($this->notice->modified),
strtotime($this->profile->modified), strtotime($this->profile->modified),
($this->avatar) ? strtotime($this->avatar->modified) : 0); ($this->avatar) ? strtotime($this->avatar->modified) : 0);
} }

View File

@ -98,9 +98,31 @@ class TwitapiaccountAction extends TwitterapiAction
$this->serverError(_('API method under construction.'), $code=501); $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) function rate_limit_status($args, $apidata)
{ {
parent::handle($args); 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);
} }
} }

View File

@ -43,7 +43,7 @@ class Twitapidirect_messagesAction extends TwitterapiAction
$count = $this->arg('count'); $count = $this->arg('count');
$since = $this->arg('since'); $since = $this->arg('since');
$since_id = $this->arg('since_id'); $since_id = $this->arg('since_id');
$before_id = $this->arg('before_id'); $max_id = $this->arg('max_id');
$page = $this->arg('page'); $page = $this->arg('page');
@ -74,8 +74,8 @@ class Twitapidirect_messagesAction extends TwitterapiAction
$link = $server . $user->nickname . '/outbox'; $link = $server . $user->nickname . '/outbox';
} }
if ($before_id) { if ($max_id) {
$message->whereAdd("id < $before_id"); $message->whereAdd("id <= $max_id");
} }
if ($since_id) { if ($since_id) {

View File

@ -45,22 +45,21 @@ class TwitapistatusesAction extends TwitterapiAction
$page = $this->arg('page'); $page = $this->arg('page');
$since_id = $this->arg('since_id'); $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) { if (!$page) {
$page = 1; $page = 1;
} }
if (!$since_id) { if (!$since_id) {
$since_id = 0; $since_id = 0;
} }
if (!$before_id) { if (!$max_id) {
$before_id = 0; $max_id = 0;
} }
$since = strtotime($this->arg('since')); $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) { if ($notice) {
@ -97,7 +96,7 @@ class TwitapistatusesAction extends TwitterapiAction
$since_id = $this->arg('since_id'); $since_id = $this->arg('since_id');
$count = $this->arg('count'); $count = $this->arg('count');
$page = $this->arg('page'); $page = $this->arg('page');
$before_id = $this->arg('before_id'); $max_id = $this->arg('max_id');
if (!$page) { if (!$page) {
$page = 1; $page = 1;
@ -111,9 +110,8 @@ class TwitapistatusesAction extends TwitterapiAction
$since_id = 0; $since_id = 0;
} }
// NOTE: before_id is an extension to Twitter API -- TB if (!$max_id) {
if (!$before_id) { $max_id = 0;
$before_id = 0;
} }
$since = strtotime($this->arg('since')); $since = strtotime($this->arg('since'));
@ -133,7 +131,7 @@ class TwitapistatusesAction extends TwitterapiAction
$link = common_local_url('all', array('nickname' => $user->nickname)); $link = common_local_url('all', array('nickname' => $user->nickname));
$subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename); $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']) { switch($apidata['content-type']) {
case 'xml': case 'xml':
@ -184,7 +182,7 @@ class TwitapistatusesAction extends TwitterapiAction
$since = $this->arg('since'); $since = $this->arg('since');
$since_id = $this->arg('since_id'); $since_id = $this->arg('since_id');
$page = $this->arg('page'); $page = $this->arg('page');
$before_id = $this->arg('before_id'); $max_id = $this->arg('max_id');
if (!$page) { if (!$page) {
$page = 1; $page = 1;
@ -198,9 +196,8 @@ class TwitapistatusesAction extends TwitterapiAction
$since_id = 0; $since_id = 0;
} }
// NOTE: before_id is an extensions to Twitter API -- TB if (!$max_id) {
if (!$before_id) { $max_id = 0;
$before_id = 0;
} }
$since = strtotime($this->arg('since')); $since = strtotime($this->arg('since'));
@ -220,7 +217,7 @@ class TwitapistatusesAction extends TwitterapiAction
# XXX: since # 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']) { switch($apidata['content-type']) {
case 'xml': case 'xml':
@ -353,7 +350,7 @@ class TwitapistatusesAction extends TwitterapiAction
$count = $this->arg('count'); $count = $this->arg('count');
$page = $this->arg('page'); $page = $this->arg('page');
$since_id = $this->arg('since_id'); $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); $user = $this->get_user($apidata['api_arg'], $apidata);
$this->auth_user = $apidata['user']; $this->auth_user = $apidata['user'];
@ -380,15 +377,14 @@ class TwitapistatusesAction extends TwitterapiAction
$since_id = 0; $since_id = 0;
} }
// NOTE: before_id is an extension to Twitter API -- TB if (!$max_id) {
if (!$before_id) { $max_id = 0;
$before_id = 0;
} }
$since = strtotime($this->arg('since')); $since = strtotime($this->arg('since'));
$notice = $user->getReplies((($page-1)*20), $notice = $user->getReplies((($page-1)*20),
$count, $since_id, $before_id, $since); $count, $since_id, $max_id, $since);
$notices = array(); $notices = array();
while ($notice->fetch()) { while ($notice->fetch()) {

View File

@ -25,110 +25,61 @@ class TwitapiusersAction extends TwitterapiAction
{ {
function show($args, $apidata) function show($args, $apidata)
{ {
parent::handle($args); 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); $this->clientError(_('API method not found!'), $code = 404);
return; return;
} }
$user = null;
$email = $this->arg('email');
$user_id = $this->arg('user_id');
if ($email) { $user = null;
$user = User::staticGet('email', $email); $email = $this->arg('email');
} elseif ($user_id) { $user_id = $this->arg('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;
}
$profile = $user->getProfile(); // XXX: email field deprecated in Twitter's API
if (!$profile) { // XXX: Also: need to add screen_name param
common_server_error(_('User has no profile.'));
return;
}
$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 if (!$user) {
$twitter_user['created_at'] = $this->date_twitter($profile->created); $this->client_error(_('Not found.'), 404, $apidata['content-type']);
return;
}
$subbed = DB_DataObject::factory('subscription'); $profile = $user->getProfile();
$subbed->subscriber = $profile->id;
$subbed_count = (int) $subbed->count() - 1;
$notices = DB_DataObject::factory('notice'); if (!$profile) {
$notices->profile_id = $profile->id; common_server_error(_('User has no profile.'));
$notice_count = (int) $notices->count(); return;
}
$twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0; $twitter_user = $this->twitter_user_array($profile, true);
$twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0;
// Other fields Twitter sends... if ($apidata['content-type'] == 'xml') {
$twitter_user['profile_background_color'] = ''; $this->init_document('xml');
$twitter_user['profile_background_image_url'] = ''; $this->show_twitter_xml_user($twitter_user);
$twitter_user['profile_text_color'] = ''; $this->end_document('xml');
$twitter_user['profile_link_color'] = ''; } elseif ($apidata['content-type'] == 'json') {
$twitter_user['profile_sidebar_fill_color'] = ''; $this->init_document('json');
$twitter_user['profile_sidebar_border_color'] = ''; $this->show_json_objects($twitter_user);
$twitter_user['profile_background_tile'] = false; $this->end_document('json');
} else {
$faves = DB_DataObject::factory('fave'); // This is in case 'show' was called via /account/verify_credentials
$faves->user_id = $user->id; // without a format (xml or json).
$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).
header('Content-Type: text/html; charset=utf-8'); header('Content-Type: text/html; charset=utf-8');
print 'Authorized'; print 'Authorized';
} }
} }
} }

View File

@ -46,7 +46,7 @@ class Fave extends Memcached_DataObject
return $ids; 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(); $fav = new Fave();
@ -59,8 +59,8 @@ class Fave extends Memcached_DataObject
$fav->whereAdd('notice_id > ' . $since_id); $fav->whereAdd('notice_id > ' . $since_id);
} }
if ($before_id != 0) { if ($max_id != 0) {
$fav->whereAdd('notice_id < ' . $before_id); $fav->whereAdd('notice_id <= ' . $max_id);
} }
if (!is_null($since)) { if (!is_null($since)) {

View File

@ -239,8 +239,14 @@ class Memcached_DataObject extends DB_DataObject
$result = parent::_connect(); $result = parent::_connect();
if (!$exists) { if (!$exists) {
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
if (common_config('db', 'utf8')) { if (common_config('db', 'type') == 'mysql' &&
$DB->query('SET NAMES "utf8"'); 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; return $result;

View File

@ -124,6 +124,8 @@ class Notice extends Memcached_DataObject
$profile = Profile::staticGet($profile_id); $profile = Profile::staticGet($profile_id);
$final = common_shorten_links($content);
if (!$profile) { if (!$profile) {
common_log(LOG_ERR, 'Problem saving notice. Unknown user.'); common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
return _('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.'); 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.'); 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.'); 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->reply_to = $reply_to;
$notice->created = common_sql_now(); $notice->created = common_sql_now();
$notice->content = $content; $notice->content = $final;
$notice->rendered = common_render_content($content, $notice); $notice->rendered = common_render_content($final, $notice);
$notice->source = $source; $notice->source = $source;
$notice->uri = $uri; $notice->uri = $uri;
@ -202,13 +204,9 @@ class Notice extends Memcached_DataObject
$notice->saveReplies(); $notice->saveReplies();
$notice->saveTags(); $notice->saveTags();
$notice->saveGroups();
if (common_config('queue', 'enabled')) { $notice->addToInboxes();
$notice->addToAuthorInbox(); $notice->saveGroups();
} else {
$notice->addToInboxes();
}
$notice->query('COMMIT'); $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 # Clear the cache for subscribed users, so they'll update at next request
# XXX: someone clever could prepend instead of clearing the cache # XXX: someone clever could prepend instead of clearing the cache
if (common_config('memcached', 'enabled')) { $notice->blowCaches();
if (common_config('queue', 'enabled')) {
$notice->blowAuthorCaches();
} else {
$notice->blowCaches();
}
}
return $notice; return $notice;
} }
@ -297,17 +289,6 @@ class Notice extends Memcached_DataObject
$this->blowGroupCache($blowLast); $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) function blowGroupCache($blowLast=false)
{ {
$cache = common_memcache(); $cache = common_memcache();
@ -443,22 +424,22 @@ class Notice extends Memcached_DataObject
# XXX: too many args; we need to move to named params or even a separate # XXX: too many args; we need to move to named params or even a separate
# class for notice streams # 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')) { if (common_config('memcached', 'enabled')) {
# Skip the cache if this is a since, since_id or before_id qry # Skip the cache if this is a since, since_id or max_id qry
if ($since_id > 0 || $before_id > 0 || $since) { if ($since_id > 0 || $max_id > 0 || $since) {
return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since); return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since);
} else { } else {
return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order); 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; $needAnd = false;
$needWhere = true; $needWhere = true;
@ -480,7 +461,7 @@ class Notice extends Memcached_DataObject
$qry .= ' notice.id > ' . $since_id; $qry .= ' notice.id > ' . $since_id;
} }
if ($before_id > 0) { if ($max_id > 0) {
if ($needWhere) { if ($needWhere) {
$qry .= ' WHERE '; $qry .= ' WHERE ';
@ -489,7 +470,7 @@ class Notice extends Memcached_DataObject
$qry .= ' AND '; $qry .= ' AND ';
} }
$qry .= ' notice.id < ' . $before_id; $qry .= ' notice.id <= ' . $max_id;
} }
if ($since) { if ($since) {
@ -647,17 +628,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'), $ids = Notice::stream(array('Notice', '_publicStreamDirect'),
array(), array(),
'public', 'public',
$offset, $limit, $since_id, $before_id, $since); $offset, $limit, $since_id, $max_id, $since);
return Notice::getStreamByIds($ids); 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(); $notice = new Notice();
@ -681,8 +662,8 @@ class Notice extends Memcached_DataObject
$notice->whereAdd('id > ' . $since_id); $notice->whereAdd('id > ' . $since_id);
} }
if ($before_id != 0) { if ($max_id != 0) {
$notice->whereAdd('id < ' . $before_id); $notice->whereAdd('id <= ' . $max_id);
} }
if (!is_null($since)) { if (!is_null($since)) {
@ -726,33 +707,6 @@ class Notice extends Memcached_DataObject
return; 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() function saveGroups()
{ {
$enabled = common_config('inboxes', 'enabled'); $enabled = common_config('inboxes', 'enabled');
@ -1024,15 +978,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(); $cache = common_memcache();
if (empty($cache) || 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) { ($offset + $limit) > NOTICE_CACHE_WINDOW) {
return call_user_func_array($fn, array_merge($args, array($offset, $limit, $since_id, 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); $idkey = common_cache_key($cachekey);

View File

@ -43,15 +43,15 @@ class Notice_inbox extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###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'), return Notice::stream(array('Notice_inbox', '_streamDirect'),
array($user_id), array($user_id),
'notice_inbox:by_user:'.$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(); $inbox = new Notice_inbox();
@ -61,8 +61,8 @@ class Notice_inbox extends Memcached_DataObject
$inbox->whereAdd('notice_id > ' . $since_id); $inbox->whereAdd('notice_id > ' . $since_id);
} }
if ($before_id != 0) { if ($max_id != 0) {
$inbox->whereAdd('notice_id < ' . $before_id); $inbox->whereAdd('notice_id <= ' . $max_id);
} }
if (!is_null($since)) { if (!is_null($since)) {

View File

@ -46,7 +46,7 @@ class Notice_tag extends Memcached_DataObject
return Notice::getStreamByIds($ids); 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(); $nt = new Notice_tag();
@ -59,8 +59,8 @@ class Notice_tag extends Memcached_DataObject
$nt->whereAdd('notice_id > ' . $since_id); $nt->whereAdd('notice_id > ' . $since_id);
} }
if ($before_id != 0) { if ($max_id != 0) {
$nt->whereAdd('notice_id < ' . $before_id); $nt->whereAdd('notice_id < ' . $max_id);
} }
if (!is_null($since)) { if (!is_null($since)) {

View File

@ -170,7 +170,7 @@ class Profile extends Memcached_DataObject
$ids = Notice::stream(array($this, '_streamDirect'), $ids = Notice::stream(array($this, '_streamDirect'),
array(), array(),
'profile:notice_ids:' . $this->id, 'profile:notice_ids:' . $this->id,
$offset, $limit, $since_id, $before_id, $since); $offset, $limit, $since_id, $max_id, $since);
return Notice::getStreamByIds($ids); return Notice::getStreamByIds($ids);
} }
@ -225,8 +225,8 @@ class Profile extends Memcached_DataObject
$notice->whereAdd('id > ' . $since_id); $notice->whereAdd('id > ' . $since_id);
} }
if ($before_id != 0) { if ($max_id != 0) {
$notice->whereAdd('id < ' . $before_id); $notice->whereAdd('id <= ' . $max_id);
} }
if (!is_null($since)) { if (!is_null($since)) {

View File

@ -22,16 +22,16 @@ class Reply extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###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'), $ids = Notice::stream(array('Reply', '_streamDirect'),
array($user_id), array($user_id),
'reply:stream:' . $user_id, 'reply:stream:' . $user_id,
$offset, $limit, $since_id, $before_id, $since); $offset, $limit, $since_id, $max_id, $since);
return $ids; 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 = new Reply();
$reply->profile_id = $user_id; $reply->profile_id = $user_id;
@ -40,8 +40,8 @@ class Reply extends Memcached_DataObject
$reply->whereAdd('notice_id > ' . $since_id); $reply->whereAdd('notice_id > ' . $since_id);
} }
if ($before_id != 0) { if ($max_id != 0) {
$reply->whereAdd('notice_id < ' . $before_id); $reply->whereAdd('notice_id < ' . $max_id);
} }
if (!is_null($since)) { if (!is_null($since)) {

View File

@ -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) 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); $ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id, $since);
common_debug("Ids = " . implode(',', $ids));
return Notice::getStreamByIds($ids); return Notice::getStreamByIds($ids);
} }

View File

@ -58,7 +58,7 @@ class User_group extends Memcached_DataObject
return Notice::getStreamByIds($ids); 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(); $inbox = new Group_inbox();
@ -71,8 +71,8 @@ class User_group extends Memcached_DataObject
$inbox->whereAdd('notice_id > ' . $since_id); $inbox->whereAdd('notice_id > ' . $since_id);
} }
if ($before_id != 0) { if ($max_id != 0) {
$inbox->whereAdd('notice_id < ' . $before_id); $inbox->whereAdd('notice_id <= ' . $max_id);
} }
if (!is_null($since)) { if (!is_null($since)) {

View File

@ -2,7 +2,9 @@ INSERT INTO notice_source
(code, name, url, created) (code, name, url, created)
VALUES VALUES
('adium', 'Adium', 'http://www.adiumx.com/', now()), ('adium', 'Adium', 'http://www.adiumx.com/', now()),
('Afficheur', 'Afficheur', 'http://afficheur.sourceforge.jp/', now()),
('AgentSolo.com','AgentSolo.com','http://www.agentsolo.com/', 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()), ('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', now()),
('bti','bti','http://gregkh.github.com/bti/', now()), ('bti','bti','http://gregkh.github.com/bti/', now()),
('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()), ('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()),
@ -11,6 +13,7 @@ VALUES
('eventbox','EventBox','http://thecosmicmachine.com/eventbox/ ', now()), ('eventbox','EventBox','http://thecosmicmachine.com/eventbox/ ', now()),
('Facebook','Facebook','http://apps.facebook.com/identica/', now()), ('Facebook','Facebook','http://apps.facebook.com/identica/', now()),
('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', 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()), ('gravity', 'Gravity', 'http://mobileways.de/gravity', now()),
('Gwibber','Gwibber','http://launchpad.net/gwibber', now()), ('Gwibber','Gwibber','http://launchpad.net/gwibber', now()),
('HelloTxt','HelloTxt','http://hellotxt.com/', now()), ('HelloTxt','HelloTxt','http://hellotxt.com/', now()),

View File

@ -335,6 +335,7 @@ function mail_broadcast_notice_sms($notice)
"FROM $UT JOIN subscription " . "FROM $UT JOIN subscription " .
"ON $UT.id = subscription.subscriber " . "ON $UT.id = subscription.subscriber " .
'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' . 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' .
'AND subscription.subscribed != subscription.subscriber ' .
"AND $UT.smsemail IS NOT null " . "AND $UT.smsemail IS NOT null " .
"AND $UT.smsnotify = 1 " . "AND $UT.smsnotify = 1 " .
'AND subscription.sms = 1 '); 'AND subscription.sms = 1 ');

View File

@ -159,13 +159,9 @@ function omb_post_notice($notice, $remote_profile, $subscription)
function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret) 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); $user = User::staticGet('id', $notice->profile_id);
if (!$user) { if (!$user) {
common_debug('Failed to get user for notice ' . $notice->id . ', profile = ' . $notice->profile_id, __FILE__);
return false; return false;
} }
@ -208,8 +204,6 @@ function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret)
$req->to_postdata(), $req->to_postdata(),
array('User-Agent: Laconica/' . LACONICA_VERSION)); 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 if ($result->status == 403) { # not authorized, don't send again
common_debug('403 result, deleting subscription', __FILE__); common_debug('403 result, deleting subscription', __FILE__);
# FIXME: figure out how to delete this # FIXME: figure out how to delete this
@ -286,14 +280,10 @@ function omb_update_profile($profile, $remote_profile, $subscription)
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); $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(), $result = $fetcher->post($req->get_normalized_http_url(),
$req->to_postdata(), $req->to_postdata(),
array('User-Agent: Laconica/' . LACONICA_VERSION)); array('User-Agent: Laconica/' . LACONICA_VERSION));
common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__);
if (empty($result) || !$result) { if (empty($result) || !$result) {
common_debug("Unable to contact " . $req->get_normalized_http_url()); common_debug("Unable to contact " . $req->get_normalized_http_url());
} else if ($result->status == 403) { # not authorized, don't send again } else if ($result->status == 403) { # not authorized, don't send again

View File

@ -54,7 +54,7 @@ class TwitterapiAction extends Action
/** /**
* Overrides XMLOutputter::element to write booleans as strings (true|false). * Overrides XMLOutputter::element to write booleans as strings (true|false).
* See that method's documentation for more info. * See that method's documentation for more info.
* *
* @param string $tag Element type or tagname * @param string $tag Element type or tagname
* @param array $attrs Array of element attributes, as * @param array $attrs Array of element attributes, as
* key-value pairs * key-value pairs
@ -70,24 +70,85 @@ class TwitterapiAction extends Action
return parent::element($tag, $attrs, $content); return parent::element($tag, $attrs, $content);
} }
function twitter_user_array($profile, $get_notice=false) function twitter_user_array($profile, $get_notice=false)
{ {
$twitter_user = array(); $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['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); $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['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) { if ($get_notice) {
$notice = $profile->getCurrentNotice(); $notice = $profile->getCurrentNotice();
@ -612,7 +673,27 @@ class TwitterapiAction extends Action
function get_user($id, $apidata=null) function get_user($id, $apidata=null)
{ {
if (!$id) { 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)) { } else if (is_numeric($id)) {
return User::staticGet($id); return User::staticGet($id);
} else { } else {

View File

@ -900,6 +900,34 @@ function common_enqueue_notice($notice)
return $result; 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);
>>>>>>> 0.7.x:lib/util.php
}
common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport);
return true;
}
function common_real_broadcast($notice, $remote=false) function common_real_broadcast($notice, $remote=false)
{ {
$success = true; $success = true;

View File

@ -35,107 +35,334 @@ define('LACONICA', true);
require_once(INSTALLDIR . '/lib/common.php'); require_once(INSTALLDIR . '/lib/common.php');
require_once('DB.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)) { if (array_key_exists('max_date', $args)) {
return; $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)) { if (PEAR::isError($db)) {
return; 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)) { if (!$succ) {
echo "ERROR: " . $sth->getMessage() . "\n"; echo "ERROR: couldn't set charset\n";
return; $db->disconnect();
} return NULL;
}
$sql = 'SELECT id, content, rendered FROM notice ' . $result = $db->autoCommit(true);
'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));
if (PEAR::isError($result)) { if (PEAR::isError($result)) {
echo "ERROR: " . $result->getMessage() . "\n"; echo "ERROR: " . $result->getMessage() . "\n";
continue; $db->disconnect();
return NULL;
} }
$cnt = $dbu->affectedRows(); return $db;
}
if ($cnt != 1) { function fixup()
echo "ERROR: 0 rows affected\n"; {
continue; $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); $sql = 'SELECT id, content, rendered FROM notice ' .
$notice->decache(); '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) $max_date = ($argc > 1) ? $argv[1] : null;
{ $max_id = ($argc > 2) ? $argv[2] : null;
$db = DB::connect(common_config('db', 'database'), $min_id = ($argc > 3) ? $argv[3] : null;
array('persistent' => false));
if (PEAR::isError($db)) { $fixer = new UTF8FixerUpper(array('max_date' => $max_date,
echo "ERROR: " . $db->getMessage() . "\n"; 'max_notice' => $max_id,
return NULL; '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);

View File

@ -25,7 +25,6 @@
* daemon names. * daemon names.
*/ */
# Abort if called from a web server # Abort if called from a web server
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
print "This script must be run from the command line\n"; print "This script must be run from the command line\n";
@ -51,5 +50,4 @@ echo "ombqueuehandler.php ";
echo "twitterqueuehandler.php "; echo "twitterqueuehandler.php ";
echo "facebookqueuehandler.php "; echo "facebookqueuehandler.php ";
echo "pingqueuehandler.php "; echo "pingqueuehandler.php ";
echo "inboxqueuehandler.php ";
echo "smsqueuehandler.php "; echo "smsqueuehandler.php ";

View File

@ -1,69 +0,0 @@
#!/usr/bin/env php
<?php
/*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2008,2009 Control Yourself, 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 <http://www.gnu.org/licenses/>.
*/
// 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();

View File

@ -1,70 +0,0 @@
#!/usr/bin/env php
<?php
/*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2008,2009 Control Yourself, 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 <http://www.gnu.org/licenses/>.
*/
// 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();

View File

@ -24,8 +24,7 @@ SDIR=`dirname $0`
DIR=`php $SDIR/getpiddir.php` DIR=`php $SDIR/getpiddir.php`
for f in jabberhandler ombhandler publichandler smshandler pinghandler \ for f in jabberhandler ombhandler publichandler smshandler pinghandler \
xmppconfirmhandler xmppdaemon twitterhandler facebookhandler \ xmppconfirmhandler xmppdaemon twitterhandler facebookhandler; do
memcachehandler inboxhandler twitterstatusfetcher; do
FILES="$DIR/$f.*.pid" FILES="$DIR/$f.*.pid"
for ff in "$FILES" ; do for ff in "$FILES" ; do