IM cleanup on 1.0.x branch:

* Fake_XMPP back to Queued_XMPP, refactor how we use it and don't create objects and load classes until we need them.
* fix fatal error in IM settings while waiting for a Jabber confirmation.
* Caching fix for user_im_prefs
* fix for saving multiple transport settings
* some fixes for AIM & using normalized addresses for lookups
This commit is contained in:
Brion Vibber 2010-04-30 14:41:54 -07:00
parent e3e90b4c27
commit 5414396a2e
8 changed files with 89 additions and 37 deletions

View File

@ -133,8 +133,7 @@ class ImsettingsAction extends ConnectSettingsAction
'message with further instructions. '. 'message with further instructions. '.
'(Did you add %s to your buddy list?)'), '(Did you add %s to your buddy list?)'),
$transport_info['display'], $transport_info['display'],
$transport_info['daemon_screenname'], $transport_info['daemon_screenname']));
jabber_daemon_address()));
$this->hidden('screenname', $confirm->address); $this->hidden('screenname', $confirm->address);
// TRANS: Button label to cancel an IM address confirmation procedure. // TRANS: Button label to cancel an IM address confirmation procedure.
$this->submit('cancel', _m('BUTTON','Cancel')); $this->submit('cancel', _m('BUTTON','Cancel'));
@ -163,12 +162,11 @@ class ImsettingsAction extends ConnectSettingsAction
'action' => 'action' =>
common_local_url('imsettings'))); common_local_url('imsettings')));
$this->elementStart('fieldset', array('id' => 'settings_im_preferences')); $this->elementStart('fieldset', array('id' => 'settings_im_preferences'));
$this->element('legend', null, _('Preferences')); // TRANS: Header for IM preferences form.
$this->element('legend', null, _('IM Preferences'));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
$this->elementStart('table'); $this->elementStart('table');
$this->elementStart('tr'); $this->elementStart('tr');
// TRANS: Header for IM preferences form.
$this->element('th', null, _('IM Preferences'));
foreach($user_im_prefs_by_transport as $transport=>$user_im_prefs) foreach($user_im_prefs_by_transport as $transport=>$user_im_prefs)
{ {
$this->element('th', null, $transports[$transport]['display']); $this->element('th', null, $transports[$transport]['display']);
@ -278,19 +276,20 @@ class ImsettingsAction extends ConnectSettingsAction
$user = common_current_user(); $user = common_current_user();
$user_im_prefs = new User_im_prefs(); $user_im_prefs = new User_im_prefs();
$user_im_prefs->query('BEGIN');
$user_im_prefs->user_id = $user->id; $user_im_prefs->user_id = $user->id;
if($user_im_prefs->find() && $user_im_prefs->fetch()) if($user_im_prefs->find() && $user_im_prefs->fetch())
{ {
$preferences = array('notify', 'updatefrompresence', 'replies', 'microid'); $preferences = array('notify', 'updatefrompresence', 'replies', 'microid');
$user_im_prefs->query('BEGIN');
do do
{ {
$original = clone($user_im_prefs); $original = clone($user_im_prefs);
$new = clone($user_im_prefs);
foreach($preferences as $preference) foreach($preferences as $preference)
{ {
$user_im_prefs->$preference = $this->boolean($user_im_prefs->transport . '_' . $preference); $new->$preference = $this->boolean($new->transport . '_' . $preference);
} }
$result = $user_im_prefs->update($original); $result = $new->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
@ -299,8 +298,8 @@ class ImsettingsAction extends ConnectSettingsAction
return; return;
} }
}while($user_im_prefs->fetch()); }while($user_im_prefs->fetch());
$user_im_prefs->query('COMMIT');
} }
$user_im_prefs->query('COMMIT');
// TRANS: Confirmation message for successful IM preferences save. // TRANS: Confirmation message for successful IM preferences save.
$this->showForm(_('Preferences saved.'), true); $this->showForm(_('Preferences saved.'), true);
} }

View File

@ -68,4 +68,27 @@ class User_im_prefs extends Memcached_DataObject
{ {
return array(false,false); return array(false,false);
} }
/**
* We have two compound keys with unique constraints:
* (transport, user_id) which is our primary key, and
* (transport, screenname) which is an additional constraint.
*
* Currently there's not a way to represent that second key
* in the general keys list, so we're adding it here to the
* list of keys to use for caching, ensuring that it gets
* cleared as well when we change.
*
* @return array of cache keys
*/
function _allCacheKeys()
{
$ukeys = 'transport,screenname';
$uvals = $this->transport . ',' . $this->screenname;
$ckeys = parent::_allCacheKeys();
$ckeys[] = $this->cacheKey($this->tableName(), $ukeys, $uvals);
return $ckeys;
}
} }

View File

@ -647,8 +647,10 @@ modified = 384
[user_im_prefs__keys] [user_im_prefs__keys]
user_id = K user_id = K
transport = K transport = K
transport = U ; There's another unique index on (transport, screenname)
screenname = U ; but we have no way to represent a compound index other than
; the primary key in here. To ensure proper cache purging,
; we need to tweak the class.
[user_urlshortener_prefs] [user_urlshortener_prefs]
user_id = 129 user_id = 129

View File

@ -108,9 +108,14 @@ abstract class ImPlugin extends Plugin
* Raw IM data is taken from the incoming queue, and passed to this function. * Raw IM data is taken from the incoming queue, and passed to this function.
* It should parse the raw message and call handle_incoming() * It should parse the raw message and call handle_incoming()
* *
* Returning false may CAUSE REPROCESSING OF THE QUEUE ITEM, and should
* be used for temporary failures only. For permanent failures such as
* unrecognized addresses, return true to indicate your processing has
* completed.
*
* @param object $data raw IM data * @param object $data raw IM data
* *
* @return boolean success value * @return boolean true if processing completed, false for temporary failures
*/ */
abstract function receive_raw_message($data); abstract function receive_raw_message($data);
@ -185,7 +190,10 @@ abstract class ImPlugin extends Plugin
*/ */
function get_user_im_prefs_from_screenname($screenname) function get_user_im_prefs_from_screenname($screenname)
{ {
if($user_im_prefs = User_im_prefs::pkeyGet( array('transport' => $this->transport, 'screenname' => $screenname) )){ $user_im_prefs = User_im_prefs::pkeyGet(
array('transport' => $this->transport,
'screenname' => $this->normalize($screenname)));
if ($user_im_prefs) {
return $user_im_prefs; return $user_im_prefs;
} else { } else {
return false; return false;
@ -220,7 +228,10 @@ abstract class ImPlugin extends Plugin
*/ */
function get_user_im_prefs_from_user($user) function get_user_im_prefs_from_user($user)
{ {
if($user_im_prefs = User_im_prefs::pkeyGet( array('transport' => $this->transport, 'user_id' => $user->id) )){ $user_im_prefs = User_im_prefs::pkeyGet(
array('transport' => $this->transport,
'user_id' => $user->id));
if ($user_im_prefs){
return $user_im_prefs; return $user_im_prefs;
} else { } else {
return false; return false;

View File

@ -126,6 +126,11 @@ class AimPlugin extends ImPlugin
return true; return true;
} }
/**
* Accept a queued input message.
*
* @return true if processing completed, false if message should be reprocessed
*/
function receive_raw_message($message) function receive_raw_message($message)
{ {
$info=Aim::getMessageInfo($message); $info=Aim::getMessageInfo($message);
@ -133,7 +138,9 @@ class AimPlugin extends ImPlugin
$user = $this->get_user($from); $user = $this->get_user($from);
$notice_text = $info['message']; $notice_text = $info['message'];
return $this->handle_incoming($from, $notice_text); $this->handle_incoming($from, $notice_text);
return true;
} }
function initialize(){ function initialize(){

View File

@ -6,7 +6,7 @@ add "addPlugin('aim',
array('setting'=>'value', 'setting2'=>'value2', ...);" array('setting'=>'value', 'setting2'=>'value2', ...);"
to the bottom of your config.php to the bottom of your config.php
The daemon included with this plugin must be running. It will be started by scripts/imdaemon.php included with StatusNet must be running. It will be started by
the plugin along with their other daemons when you run scripts/startdaemons.sh. the plugin along with their other daemons when you run scripts/startdaemons.sh.
See the StatusNet README for more about queuing and daemons. See the StatusNet README for more about queuing and daemons.

View File

@ -2,7 +2,7 @@
/** /**
* StatusNet, the distributed open-source microblogging tool * StatusNet, the distributed open-source microblogging tool
* *
* Instead of sending XMPP messages, retrieve the raw XML that would be sent * Queue-mediated proxy class for outgoing XMPP messages.
* *
* PHP version 5 * PHP version 5
* *
@ -31,13 +31,17 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1); exit(1);
} }
class Fake_XMPP extends XMPPHP_XMPP class Queued_XMPP extends XMPPHP_XMPP
{ {
public $would_be_sent = null; /**
* Reference to the XmppPlugin object we're hooked up to.
*/
public $plugin;
/** /**
* Constructor * Constructor
* *
* @param XmppPlugin $plugin
* @param string $host * @param string $host
* @param integer $port * @param integer $port
* @param string $user * @param string $user
@ -47,8 +51,10 @@ class Fake_XMPP extends XMPPHP_XMPP
* @param boolean $printlog * @param boolean $printlog
* @param string $loglevel * @param string $loglevel
*/ */
public function __construct($host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null) public function __construct($plugin, $host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null)
{ {
$this->plugin = $plugin;
parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel); parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel);
// We use $host to connect, but $server to build JIDs if specified. // We use $host to connect, but $server to build JIDs if specified.
@ -73,7 +79,7 @@ class Fake_XMPP extends XMPPHP_XMPP
*/ */
public function send($msg, $timeout=NULL) public function send($msg, $timeout=NULL)
{ {
$this->would_be_sent = $msg; $this->plugin->enqueue_outgoing_raw($msg);
} }
//@{ //@{
@ -110,5 +116,6 @@ class Fake_XMPP extends XMPPHP_XMPP
throw new Exception("Can't read stream from fake XMPP."); throw new Exception("Can't read stream from fake XMPP.");
} }
//@} //@}
} }

View File

@ -60,8 +60,6 @@ class XmppPlugin extends ImPlugin
public $transport = 'xmpp'; public $transport = 'xmpp';
protected $fake_xmpp;
function getDisplayName(){ function getDisplayName(){
return _m('XMPP/Jabber/GTalk'); return _m('XMPP/Jabber/GTalk');
} }
@ -292,7 +290,7 @@ class XmppPlugin extends ImPlugin
require_once 'XMPP.php'; require_once 'XMPP.php';
return false; return false;
case 'Sharing_XMPP': case 'Sharing_XMPP':
case 'Fake_XMPP': case 'Queued_XMPP':
require_once $dir . '/'.$cls.'.php'; require_once $dir . '/'.$cls.'.php';
return false; return false;
case 'XmppManager': case 'XmppManager':
@ -317,9 +315,7 @@ class XmppPlugin extends ImPlugin
function send_message($screenname, $body) function send_message($screenname, $body)
{ {
$this->fake_xmpp->message($screenname, $body, 'chat'); $this->queuedConnection()->message($screenname, $body, 'chat');
$this->enqueue_outgoing_raw($this->fake_xmpp->would_be_sent);
return true;
} }
function send_notice($screenname, $notice) function send_notice($screenname, $notice)
@ -327,8 +323,7 @@ class XmppPlugin extends ImPlugin
$msg = $this->format_notice($notice); $msg = $this->format_notice($notice);
$entry = $this->format_entry($notice); $entry = $this->format_entry($notice);
$this->fake_xmpp->message($screenname, $msg, 'chat', null, $entry); $this->queuedConnection()->message($screenname, $msg, 'chat', null, $entry);
$this->enqueue_outgoing_raw($this->fake_xmpp->would_be_sent);
return true; return true;
} }
@ -385,10 +380,19 @@ class XmppPlugin extends ImPlugin
return true; return true;
} }
return $this->handle_incoming($from, $pl['body']); $this->handle_incoming($from, $pl['body']);
return true;
} }
function initialize(){ /**
* Build a queue-proxied XMPP interface object. Any outgoing messages
* will be run back through us for enqueing rather than sent directly.
*
* @return Queued_XMPP
* @throws Exception if server settings are invalid.
*/
function queuedConnection(){
if(!isset($this->server)){ if(!isset($this->server)){
throw new Exception("must specify a server"); throw new Exception("must specify a server");
} }
@ -402,7 +406,7 @@ class XmppPlugin extends ImPlugin
throw new Exception("must specify a password"); throw new Exception("must specify a password");
} }
$this->fake_xmpp = new Fake_XMPP($this->host ? return new Queued_XMPP($this, $this->host ?
$this->host : $this->host :
$this->server, $this->server,
$this->port, $this->port,
@ -415,7 +419,6 @@ class XmppPlugin extends ImPlugin
$this->debug ? $this->debug ?
XMPPHP_Log::LEVEL_VERBOSE : null XMPPHP_Log::LEVEL_VERBOSE : null
); );
return true;
} }
function onPluginVersion(&$versions) function onPluginVersion(&$versions)