]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.9.x' into 1.0.x
authorCraig Andrews <candrews@integralblue.com>
Sun, 18 Apr 2010 23:21:15 +0000 (19:21 -0400)
committerCraig Andrews <candrews@integralblue.com>
Sun, 18 Apr 2010 23:21:15 +0000 (19:21 -0400)
Conflicts:
actions/confirmaddress.php
actions/imsettings.php

1  2 
actions/apiaccountupdatedeliverydevice.php
actions/confirmaddress.php
actions/imsettings.php
actions/shownotice.php
classes/User.php
lib/adminpanelaction.php
lib/command.php
lib/implugin.php
lib/statusnet.php
lib/util.php

index 4bd6c326f8d25e129ab9d5ff234e0191eb2ed269,05d19c22dea8d63ab5e3ee849daafe88d1df8f19..d42d25a6180be75d7ec211d11a0fb82f4e5acdcd
@@@ -103,7 -103,7 +103,7 @@@ class ApiAccountUpdateDeliveryDeviceAct
              $this->clientError(
                  _(
                      'You must specify a parameter named ' .
-                     '\'device\' with a value of one of: sms, im, none'
+                     '\'device\' with a value of one of: sms, im, none.'
                  )
              );
              return;
          if (strtolower($this->device) == 'sms') {
              $this->user->smsnotify = true;
          } elseif (strtolower($this->device) == 'im') {
 -            $this->user->jabbernotify = true;
 +            //TODO IM is pluginized now, so what should we do?
 +            //Enable notifications for all IM plugins?
 +            //For now, don't do anything
 +            //$this->user->jabbernotify = true;
          } elseif (strtolower($this->device == 'none')) {
              $this->user->smsnotify    = false;
 -            $this->user->jabbernotify = false;
 +            //TODO IM is pluginized now, so what should we do?
 +            //Disable notifications for all IM plugins?
 +            //For now, don't do anything
 +            //$this->user->jabbernotify = false;
          }
  
          $result = $this->user->update($original);
index eaf1c91c1a5c897053583c455b8e38affcc4908b,8bf8c8c4d4d7a75de8c27ba5029421d88b3c0aac..f92db3ec452b3deccc72321352553387da80d466
@@@ -49,7 -49,7 +49,7 @@@ class ConfirmaddressAction extends Acti
  {
      /** type of confirmation. */
  
 -    var $type = null;
 +    var $address;
  
      /**
       * Accept a confirmation code
              return;
          }
          $type = $confirm->address_type;
 -        if (!in_array($type, array('email', 'jabber', 'sms'))) {
 -            // TRANS: Server error for an unknow address type, which can be 'email', 'jabber', or 'sms'.
 -            $this->serverError(sprintf(_('Unrecognized address type %s.'), $type));
 +        $transports = array();
 +        Event::handle('GetImTransports', array(&$transports));
 +        if (!in_array($type, array('email', 'sms')) && !in_array($type, array_keys($transports))) {
++            // TRANS: Server error for an unknown address type, which can be 'email', 'sms', or the name of an IM network (such as 'xmpp' or 'aim')
 +            $this->serverError(sprintf(_('Unrecognized address type %s'), $type));
              return;
          }
 -        if ($cur->$type == $confirm->address) {
 -            // TRANS: Client error for an already confirmed email/jabbel/sms address.
 -            $this->clientError(_('That address has already been confirmed.'));
 -            return;
 -        }
 -
 +        $this->address = $confirm->address;
          $cur->query('BEGIN');
 +        if (in_array($type, array('email', 'sms')))
 +        {
 +            if ($cur->$type == $confirm->address) {
 +                $this->clientError(_('That address has already been confirmed.'));
 +                return;
 +            }
 +
 +            $orig_user = clone($cur);
 +
 +            $cur->$type = $confirm->address;
 +
 +            if ($type == 'sms') {
 +                $cur->carrier  = ($confirm->address_extra)+0;
 +                $carrier       = Sms_carrier::staticGet($cur->carrier);
 +                $cur->smsemail = $carrier->toEmailAddress($cur->sms);
 +            }
 +
 +            $result = $cur->updateKeys($orig_user);
 +
 +            if (!$result) {
 +                common_log_db_error($cur, 'UPDATE', __FILE__);
 +                $this->serverError(_('Couldn\'t update user.'));
 +                return;
 +            }
 +
 +            if ($type == 'email') {
 +                $cur->emailChanged();
 +            }
 +
 +        } else {
 +
 +            $user_im_prefs = new User_im_prefs();
 +            $user_im_prefs->transport = $confirm->address_type;
 +            $user_im_prefs->user_id = $cur->id;
 +            if ($user_im_prefs->find() && $user_im_prefs->fetch()) {
 +                if($user_im_prefs->screenname == $confirm->address){
 +                    $this->clientError(_('That address has already been confirmed.'));
 +                    return;
 +                }
 +                $user_im_prefs->screenname = $confirm->address;
 +                $result = $user_im_prefs->update();
 +
 +                if (!$result) {
 +                    common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
 +                    $this->serverError(_('Couldn\'t update user im preferences.'));
 +                    return;
 +                }
 +            }else{
 +                $user_im_prefs = new User_im_prefs();
 +                $user_im_prefs->screenname = $confirm->address;
 +                $user_im_prefs->transport = $confirm->address_type;
 +                $user_im_prefs->user_id = $cur->id;
 +                $result = $user_im_prefs->insert();
 +
 +                if (!$result) {
 +                    common_log_db_error($user_im_prefs, 'INSERT', __FILE__);
 +                    $this->serverError(_('Couldn\'t insert user im preferences.'));
 +                    return;
 +                }
 +            }
  
 -        $orig_user = clone($cur);
 -
 -        $cur->$type = $confirm->address;
 -
 -        if ($type == 'sms') {
 -            $cur->carrier  = ($confirm->address_extra)+0;
 -            $carrier       = Sms_carrier::staticGet($cur->carrier);
 -            $cur->smsemail = $carrier->toEmailAddress($cur->sms);
 -        }
 -
 -        $result = $cur->updateKeys($orig_user);
 -
 -        if (!$result) {
 -            common_log_db_error($cur, 'UPDATE', __FILE__);
 -            $this->serverError(_('Couldn\'t update user.'));
 -            return;
 -        }
 -
 -        if ($type == 'email') {
 -            $cur->emailChanged();
          }
  
          $result = $confirm->delete();
          }
  
          $cur->query('COMMIT');
 -
 -        $this->type = $type;
          $this->showPage();
      }
  
      function showContent()
      {
          $cur  = common_current_user();
 -        $type = $this->type;
  
          $this->element('p', null,
                         sprintf(_('The address "%s" has been '.
                                   'confirmed for your account.'),
 -                               $cur->$type));
 +                               $this->address));
      }
  }
diff --combined actions/imsettings.php
index fe1864f0d1cb6c326af4762cc57ff2c0b84c1190,6691c8dad76e95f53cbcf621810a3490ea813f95..2c2606b76c78765a93f69c74ff8a7ef96b37d5f1
@@@ -31,6 -31,9 +31,6 @@@ if (!defined('STATUSNET') && !defined('
      exit(1);
  }
  
 -require_once INSTALLDIR.'/lib/connectsettingsaction.php';
 -require_once INSTALLDIR.'/lib/jabber.php';
 -
  /**
   * Settings for Jabber/XMPP integration
   *
@@@ -53,6 -56,7 +53,7 @@@ class ImsettingsAction extends ConnectS
  
      function title()
      {
+         // TRANS: Title for instance messaging settings.
          return _('IM settings');
      }
  
  
      function getInstructions()
      {
+         // TRANS: Instant messaging settings page instructions.
+         // TRANS: [instant messages] is link text, "(%%doc.im%%)" is the link.
+         // TRANS: the order and formatting of link text and link should remain unchanged.
          return _('You can send and receive notices through '.
 -                 'Jabber/GTalk [instant messages](%%doc.im%%). '.
 -                 'Configure your address and settings below.');
 +                 'instant messaging [instant messages](%%doc.im%%). '.
 +                 'Configure your addresses and settings below.');
      }
  
      /**
  
      function showContent()
      {
 -        if (!common_config('xmpp', 'enabled')) {
 +        $transports = array();
 +        Event::handle('GetImTransports', array(&$transports));
 +        if (! $transports) {
              $this->element('div', array('class' => 'error'),
 -                           // TRANS: Message given in the IM settings if XMPP is not enabled on the site.
++                           // TRANS: Message given in the IM settings if IM is not enabled on the site.
                             _('IM is not available.'));
              return;
          }
  
          $user = common_current_user();
 -        $this->elementStart('form', array('method' => 'post',
 -                                          'id' => 'form_settings_im',
 -                                          'class' => 'form_settings',
 -                                          'action' =>
 -                                          common_local_url('imsettings')));
 -        $this->elementStart('fieldset', array('id' => 'settings_im_address'));
 -        // TRANS: Form legend for IM settings form.
 -        $this->element('legend', null, _('IM address'));
 -        $this->hidden('token', common_session_token());
 -
 -        if ($user->jabber) {
 -            $this->element('p', 'form_confirmed', $user->jabber);
 -            // TRANS: Form note in IM settings form.
 -            $this->element('p', 'form_note',
 -                           _('Current confirmed Jabber/GTalk address.'));
 -            $this->hidden('jabber', $user->jabber);
 -            // TRANS: Button label to remove a confirmed IM address.
 -            $this->submit('remove', _m('BUTTON','Remove'));
 -        } else {
 -            $confirm = $this->getConfirmation();
 -            if ($confirm) {
 -                $this->element('p', 'form_unconfirmed', $confirm->address);
 +
 +        $user_im_prefs_by_transport = array();
 +        
 +        foreach($transports as $transport=>$transport_info)
 +        {
 +            $this->elementStart('form', array('method' => 'post',
 +                                              'id' => 'form_settings_im',
 +                                              'class' => 'form_settings',
 +                                              'action' =>
 +                                              common_local_url('imsettings')));
 +            $this->elementStart('fieldset', array('id' => 'settings_im_address'));
++            // TRANS: Form legend for IM settings form.
 +            $this->element('legend', null, $transport_info['display']);
 +            $this->hidden('token', common_session_token());
 +            $this->hidden('transport', $transport);
 +
 +            if ($user_im_prefs = User_im_prefs::pkeyGet( array('transport' => $transport, 'user_id' => $user->id) )) {
 +                $user_im_prefs_by_transport[$transport] = $user_im_prefs;
 +                $this->element('p', 'form_confirmed', $user_im_prefs->screenname);
++                // TRANS: Form note in IM settings form.
                  $this->element('p', 'form_note',
 -                               // TRANS: Form note in IM settings form.
 -                               // TRANS: %s is the IM address set for the site.
 -                               sprintf(_('Awaiting confirmation on this address. '.
 -                                         'Check your Jabber/GTalk account for a '.
 -                                         'message with further instructions. '.
 -                                         '(Did you add %s to your buddy list?)'),
 -                                       jabber_daemon_address()));
 -                $this->hidden('jabber', $confirm->address);
 -                // TRANS: Button label to cancel an IM address confirmation procedure.
 -                $this->submit('cancel', _m('BUTTON','Cancel'));
 +                               sprintf(_('Current confirmed %s address.'),$transport_info['display']));
 +                $this->hidden('screenname', $user_im_prefs->screenname);
-                 $this->submit('remove', _('Remove'));
++                // TRANS: Button label to remove a confirmed IM address.
++                $this->submit('remove', _m('BUTTON','Remove'));
              } else {
 -                $this->elementStart('ul', 'form_data');
 -                $this->elementStart('li');
 -                // TRANS: Field label for IM address input in IM settings form.
 -                $this->input('jabber', _('IM address'),
 -                             ($this->arg('jabber')) ? $this->arg('jabber') : null,
 -                             // TRANS: IM address input field instructions in IM settings form.
 -                             // TRANS: %s is the IM address set for the site.
 -                             sprintf(_('Jabber or GTalk address, '.
 -                                       'like "UserName@example.org". '.
 -                                       'First, make sure to add %s to your '.
 -                                       'buddy list in your IM client or on GTalk.'),
 -                                     jabber_daemon_address()));
 -                $this->elementEnd('li');
 -                $this->elementEnd('ul');
 -                // TRANS: Button label for adding an IM address in IM settings form.
 -                $this->submit('add', _m('BUTTON','Add'));
 +                $confirm = $this->getConfirmation($transport);
 +                if ($confirm) {
 +                    $this->element('p', 'form_unconfirmed', $confirm->address);
++                    // TRANS: Form note in IM settings form.
 +                    $this->element('p', 'form_note',
++                                   // TRANS: Form note in IM settings form.
++                                   // TRANS: %s is the IM address set for the site.
 +                                   sprintf(_('Awaiting confirmation on this address. '.
 +                                             'Check your %s account for a '.
-                                              'message with further instructions.'),
-                                            $transport_info['display']));
++                                             'message with further instructions. '.
++                                             '(Did you add %s to your buddy list?)'),
++                                             $transport_info['display'],
++                                             $transport_info['daemon_screenname'],
++                                             jabber_daemon_address()));
 +                    $this->hidden('screenname', $confirm->address);
-                     $this->submit('cancel', _('Cancel'));
++                    // TRANS: Button label to cancel an IM address confirmation procedure.
++                    $this->submit('cancel', _m('BUTTON','Cancel'));
 +                } else {
 +                    $this->elementStart('ul', 'form_data');
 +                    $this->elementStart('li');
 +                    $this->input('screenname', _('IM address'),
 +                                 ($this->arg('screenname')) ? $this->arg('screenname') : null,
 +                                 sprintf(_('%s screenname.'),
 +                                         $transport_info['display']));
 +                    $this->elementEnd('li');
 +                    $this->elementEnd('ul');
-                     $this->submit('add', _('Add'));
++                    // TRANS: Button label for adding an IM address in IM settings form.
++                    $this->submit('add', _m('BUTTON','Add'));
 +                }
              }
-             $this->element('th', null, _('Preferences'));
 +            $this->elementEnd('fieldset');
 +            $this->elementEnd('form');
 +        }
 +
 +        if($user_im_prefs_by_transport)
 +        {
 +            $this->elementStart('form', array('method' => 'post',
 +                                              'id' => 'form_settings_im',
 +                                              'class' => 'form_settings',
 +                                              'action' =>
 +                                              common_local_url('imsettings')));
 +            $this->elementStart('fieldset', array('id' => 'settings_im_preferences'));
 +            $this->element('legend', null, _('Preferences'));
 +            $this->hidden('token', common_session_token());
 +            $this->elementStart('table');
 +            $this->elementStart('tr');
-             $this->submit('save', _('Save'));
++            // TRANS: Header for IM preferences form.
++            $this->element('th', null, _('IM Preferences'));
 +            foreach($user_im_prefs_by_transport as $transport=>$user_im_prefs)
 +            {
 +                $this->element('th', null, $transports[$transport]['display']);
 +            }
 +            $this->elementEnd('tr');
 +            $preferences = array(
++                // TRANS: Checkbox label in IM preferences form.
 +                array('name'=>'notify', 'description'=>_('Send me notices')),
++                // TRANS: Checkbox label in IM preferences form.
 +                array('name'=>'updatefrompresence', 'description'=>_('Post a notice when my status changes.')),
++                // TRANS: Checkbox label in IM preferences form.
 +                array('name'=>'replies', 'description'=>_('Send me replies '.
 +                              'from people I\'m not subscribed to.')),
++                // TRANS: Checkbox label in IM preferences form.
 +                array('name'=>'microid', 'description'=>_('Publish a MicroID'))
 +            );
 +            foreach($preferences as $preference)
 +            {
 +                $this->elementStart('tr');
 +                foreach($user_im_prefs_by_transport as $transport=>$user_im_prefs)
 +                {
 +                    $preference_name = $preference['name'];
 +                    $this->elementStart('td');
 +                    $this->checkbox($transport . '_' . $preference['name'],
 +                                $preference['description'],
 +                                $user_im_prefs->$preference_name);
 +                    $this->elementEnd('td');
 +                }
 +                $this->elementEnd('tr');
 +            }
 +            $this->elementEnd('table');
++            // TRANS: Button label to save IM preferences.
++            $this->submit('save', _m('BUTTON','Save'));
 +            $this->elementEnd('fieldset');
 +            $this->elementEnd('form');
          }
 -        $this->elementEnd('fieldset');
 -        
 -        $this->elementStart('fieldset', array('id' => 'settings_im_preferences'));
 -        // TRANS: Form legend for IM preferences form.
 -        $this->element('legend', null, _('IM preferences'));
 -        $this->elementStart('ul', 'form_data');
 -        $this->elementStart('li');
 -        $this->checkbox('jabbernotify',
 -                        // TRANS: Checkbox label in IM preferences form.
 -                        _('Send me notices through Jabber/GTalk.'),
 -                        $user->jabbernotify);
 -        $this->elementEnd('li');
 -        $this->elementStart('li');
 -        $this->checkbox('updatefrompresence',
 -                        // TRANS: Checkbox label in IM preferences form.
 -                        _('Post a notice when my Jabber/GTalk status changes.'),
 -                        $user->updatefrompresence);
 -        $this->elementEnd('li');
 -        $this->elementStart('li');
 -        $this->checkbox('jabberreplies',
 -                        // TRANS: Checkbox label in IM preferences form.
 -                        _('Send me replies through Jabber/GTalk '.
 -                          'from people I\'m not subscribed to.'),
 -                        $user->jabberreplies);
 -        $this->elementEnd('li');
 -        $this->elementStart('li');
 -        $this->checkbox('jabbermicroid',
 -                        // TRANS: Checkbox label in IM preferences form.
 -                        _('Publish a MicroID for my Jabber/GTalk address.'),
 -                        $user->jabbermicroid);
 -        $this->elementEnd('li');
 -        $this->elementEnd('ul');
 -        // TRANS: Button label to save IM preferences.
 -        $this->submit('save', _m('BUTTON','Save'));
 -        $this->elementEnd('fieldset');
 -        $this->elementEnd('form');
      }
  
      /**
       * @return Confirm_address address object for this user
       */
  
 -    function getConfirmation()
 +    function getConfirmation($transport)
      {
          $user = common_current_user();
  
          $confirm = new Confirm_address();
  
          $confirm->user_id      = $user->id;
 -        $confirm->address_type = 'jabber';
 +        $confirm->address_type = $transport;
  
          if ($confirm->find(true)) {
              return $confirm;
          } else if ($this->arg('remove')) {
              $this->removeAddress();
          } else {
+             // TRANS: Message given submitting a form with an unknown action in IM settings.
              $this->showForm(_('Unexpected form submission.'));
          }
      }
  
      function savePreferences()
      {
 -        $jabbernotify       = $this->boolean('jabbernotify');
 -        $updatefrompresence = $this->boolean('updatefrompresence');
 -        $jabberreplies      = $this->boolean('jabberreplies');
 -        $jabbermicroid      = $this->boolean('jabbermicroid');
 -
          $user = common_current_user();
  
 -        assert(!is_null($user)); // should already be checked
 -
 -        $user->query('BEGIN');
 -
 -        $original = clone($user);
 -
 -        $user->jabbernotify       = $jabbernotify;
 -        $user->updatefrompresence = $updatefrompresence;
 -        $user->jabberreplies      = $jabberreplies;
 -        $user->jabbermicroid      = $jabbermicroid;
 -
 -        $result = $user->update($original);
 -
 -        if ($result === false) {
 -            common_log_db_error($user, 'UPDATE', __FILE__);
 -            // TRANS: Server error thrown on database error updating IM preferences.
 -            $this->serverError(_('Couldn\'t update user.'));
 -            return;
 +        $user_im_prefs = new User_im_prefs();
 +        $user_im_prefs->user_id = $user->id;
 +        if($user_im_prefs->find() && $user_im_prefs->fetch())
 +        {
 +            $preferences = array('notify', 'updatefrompresence', 'replies', 'microid');
 +            $user_im_prefs->query('BEGIN');
 +            do
 +            {
 +                $original = clone($user_im_prefs);
 +                foreach($preferences as $preference)
 +                {
 +                    $user_im_prefs->$preference = $this->boolean($user_im_prefs->transport . '_' . $preference);
 +                }
 +                $result = $user_im_prefs->update($original);
 +
 +                if ($result === false) {
 +                    common_log_db_error($user, 'UPDATE', __FILE__);
++                    // TRANS: Server error thrown on database error updating IM preferences.
 +                    $this->serverError(_('Couldn\'t update IM preferences.'));
 +                    return;
 +                }
 +            }while($user_im_prefs->fetch());
 +            $user_im_prefs->query('COMMIT');
          }
 -
 -        $user->query('COMMIT');
 -
+         // TRANS: Confirmation message for successful IM preferences save.
          $this->showForm(_('Preferences saved.'), true);
      }
  
       * Sends a confirmation to the address given
       *
       * Stores a confirmation record and sends out a
 -     * Jabber message with the confirmation info.
 +     * message with the confirmation info.
       *
       * @return void
       */
      {
          $user = common_current_user();
  
 -        $jabber = $this->trimmed('jabber');
 +        $screenname = $this->trimmed('screenname');
 +        $transport = $this->trimmed('transport');
  
          // Some validation
  
 -        if (!$jabber) {
 +        if (!$screenname) {
+             // TRANS: Message given saving IM address without having provided one.
 -            $this->showForm(_('No Jabber ID.'));
 +            $this->showForm(_('No screenname.'));
              return;
          }
  
 -        $jabber = jabber_normalize_jid($jabber);
 +        if (!$transport) {
 +            $this->showForm(_('No transport.'));
 +            return;
 +        }
  
 -        if (!$jabber) {
 +        Event::handle('NormalizeImScreenname', array($transport, &$screenname));
 +
 +        if (!$screenname) {
+             // TRANS: Message given saving IM address that cannot be normalised.
 -            $this->showForm(_('Cannot normalize that Jabber ID'));
 +            $this->showForm(_('Cannot normalize that screenname'));
              return;
          }
 -        if (!jabber_valid_base_jid($jabber, common_config('email', 'domain_check'))) {
 +        $valid = false;
 +        Event::handle('ValidateImScreenname', array($transport, $screenname, &$valid));
 +        if (!$valid) {
+             // TRANS: Message given saving IM address that not valid.
 -            $this->showForm(_('Not a valid Jabber ID'));
 -            return;
 -        } else if ($user->jabber == $jabber) {
 -            // TRANS: Message given saving IM address that is already set.
 -            $this->showForm(_('That is already your Jabber ID.'));
 +            $this->showForm(_('Not a valid screenname'));
              return;
 -        } else if ($this->jabberExists($jabber)) {
 +        } else if ($this->screennameExists($transport, $screenname)) {
+             // TRANS: Message given saving IM address that is already set for another user.
 -            $this->showForm(_('Jabber ID already belongs to another user.'));
 +            $this->showForm(_('Screenname already belongs to another user.'));
              return;
          }
  
          $confirm = new Confirm_address();
  
 -        $confirm->address      = $jabber;
 -        $confirm->address_type = 'jabber';
 +        $confirm->address      = $screenname;
 +        $confirm->address_type = $transport;
          $confirm->user_id      = $user->id;
          $confirm->code         = common_confirmation_code(64);
          $confirm->sent         = common_sql_now();
  
          if ($result === false) {
              common_log_db_error($confirm, 'INSERT', __FILE__);
+             // TRANS: Server error thrown on database error adding IM confirmation code.
              $this->serverError(_('Couldn\'t insert confirmation code.'));
              return;
          }
  
 -        jabber_confirm_address($confirm->code,
 -                               $user->nickname,
 -                               $jabber);
 +        Event::handle('SendImConfirmationCode', array($transport, $screenname, $confirm->code, $user));
  
 -        // TRANS: %s is the IM address set for the site.
 -        $msg = sprintf(_('A confirmation code was sent '.
 -                         'to the IM address you added. '.
 -                         'You must approve %s for '.
 -                         'sending messages to you.'),
 -                       jabber_daemon_address());
+         // TRANS: Message given saving valid IM address that is to be confirmed.
 +        $msg = _('A confirmation code was sent '.
 +                         'to the IM address you added.');
  
          $this->showForm($msg, true);
      }
  
      function cancelConfirmation()
      {
 -        $jabber = $this->arg('jabber');
 +        $screenname = $this->trimmed('screenname');
 +        $transport = $this->trimmed('transport');
  
 -        $confirm = $this->getConfirmation();
 +        $confirm = $this->getConfirmation($transport);
  
          if (!$confirm) {
+             // TRANS: Message given canceling IM address confirmation that is not pending.
              $this->showForm(_('No pending confirmation to cancel.'));
              return;
          }
 -        if ($confirm->address != $jabber) {
 +        if ($confirm->address != $screenname) {
+             // TRANS: Message given canceling IM address confirmation for the wrong IM address.
              $this->showForm(_('That is the wrong IM address.'));
              return;
          }
  
          if (!$result) {
              common_log_db_error($confirm, 'DELETE', __FILE__);
 -            $this->serverError(_('Couldn\'t delete IM confirmation.'));
+             // TRANS: Server error thrown on database error canceling IM address confirmation.
 +            $this->serverError(_('Couldn\'t delete confirmation.'));
              return;
          }
  
-         $this->showForm(_('Confirmation cancelled.'), true);
+         // TRANS: Message given after successfully canceling IM address confirmation.
+         $this->showForm(_('IM confirmation cancelled.'), true);
      }
  
      /**
      {
          $user = common_current_user();
  
 -        $jabber = $this->arg('jabber');
 +        $screenname = $this->trimmed('screenname');
 +        $transport = $this->trimmed('transport');
  
          // Maybe an old tab open...?
  
 -        if ($user->jabber != $jabber) {
 +        $user_im_prefs = new User_im_prefs();
 +        $user_im_prefs->user_id = $user->id;
 +        if(! ($user_im_prefs->find() && $user_im_prefs->fetch())) {
+             // TRANS: Message given trying to remove an IM address that is not
+             // TRANS: registered for the active user.
 -            $this->showForm(_('That is not your Jabber ID.'));
 +            $this->showForm(_('That is not your screenname.'));
              return;
          }
  
 -        $user->query('BEGIN');
 -
 -        $original = clone($user);
 -
 -        $user->jabber = null;
 -
 -        $result = $user->updateKeys($original);
 +        $result = $user_im_prefs->delete();
  
          if (!$result) {
              common_log_db_error($user, 'UPDATE', __FILE__);
+             // TRANS: Server error thrown on database error removing a registered IM address.
 +            $this->serverError(_('Couldn\'t update user im prefs.'));
+             $this->serverError(_('Couldn\'t update user.'));
              return;
          }
 -        $user->query('COMMIT');
  
          // XXX: unsubscribe to the old address
  
-         $this->showForm(_('The address was removed.'), true);
+         // TRANS: Message given after successfully removing a registered IM address.
+         $this->showForm(_('The IM address was removed.'), true);
      }
  
      /**
 -     * Does this Jabber ID exist?
 +     * Does this screenname exist?
       *
       * Checks if we already have another user with this address.
       *
 -     * @param string $jabber Address to check
 +     * @param string $transport Transport to check
 +     * @param string $screenname Screenname to check
       *
 -     * @return boolean whether the Jabber ID exists
 +     * @return boolean whether the screenname exists
       */
  
 -    function jabberExists($jabber)
 +    function screennameExists($transport, $screenname)
      {
          $user = common_current_user();
  
 -        $other = User::staticGet('jabber', $jabber);
 -
 -        if (!$other) {
 +        $user_im_prefs = new User_im_prefs();
 +        $user_im_prefs->transport = $transport;
 +        $user_im_prefs->screenname = $screenname;
 +        if($user_im_prefs->find() && $user_im_prefs->fetch()){
 +            return true;
 +        }else{
              return false;
 -        } else {
 -            return $other->id != $user->id;
          }
      }
  }
diff --combined actions/shownotice.php
index 12e1d77f805480b3cb6bc39c6d5bdd75ac5da2a7,9c5d83441b8688e6ed08b52cf22c9dfdf71f6ba5..77ba2ce9fda913ba217803b9bc9cd105e54bc155
@@@ -97,7 -97,7 +97,7 @@@ class ShownoticeAction extends OwnerDes
          $this->profile = $this->notice->getProfile();
  
          if (empty($this->profile)) {
-             $this->serverError(_('Notice has no profile'), 500);
+             $this->serverError(_('Notice has no profile.'), 500);
              return false;
          }
  
                                           'content' => $id->toString()));
          }
  
 -        if ($user->jabbermicroid && $user->jabber && $this->notice->uri) {
 -            $id = new Microid('xmpp:', $user->jabber,
 -                              $this->notice->uri);
 -            $this->element('meta', array('name' => 'microid',
 -                                         'content' => $id->toString()));
 -        }
          $this->element('link',array('rel'=>'alternate',
              'type'=>'application/json+oembed',
              'href'=>common_local_url(
diff --combined classes/User.php
index bec05f8cfcf5866ee72111bdda7457035a84c31d,1928a3c62fb1735b1c9ecab9a01595f6064ca191..4626a7ca1ece5e12eeea98b599d03df08a5acef9
@@@ -48,6 -48,11 +48,6 @@@ class User extends Memcached_DataObjec
      public $language;                        // varchar(50)
      public $timezone;                        // varchar(50)
      public $emailpost;                       // tinyint(1)   default_1
 -    public $jabber;                          // varchar(255)  unique_key
 -    public $jabbernotify;                    // tinyint(1)
 -    public $jabberreplies;                   // tinyint(1)
 -    public $jabbermicroid;                   // tinyint(1)   default_1
 -    public $updatefrompresence;              // tinyint(1)
      public $sms;                             // varchar(64)  unique_key
      public $carrier;                         // int(4)
      public $smsnotify;                       // tinyint(1)
@@@ -88,7 -93,7 +88,7 @@@
      {
          $this->_connect();
          $parts = array();
 -        foreach (array('nickname', 'email', 'jabber', 'incomingemail', 'sms', 'carrier', 'smsemail', 'language', 'timezone') as $k) {
 +        foreach (array('nickname', 'email', 'incomingemail', 'sms', 'carrier', 'smsemail', 'language', 'timezone') as $k) {
              if (strcmp($this->$k, $orig->$k) != 0) {
                  $parts[] = $k . ' = ' . $this->_quote($this->$k);
              }
              return false;
          }
  
-         Subscription::cancel($other, $this->getProfile());
+         $self = $this->getProfile();
+         if (Subscription::exists($other, $self)) {
+             Subscription::cancel($other, $self);
+         }
  
          $block->query('COMMIT');
  
diff --combined lib/adminpanelaction.php
index d87981b6a70d621873cb5f53411a371dfff574ea,e22804fc8214b283f1398a470e2d9ed08f1ec8ef..6c9947608c3e00210bd6eb7871e906e582bc2bda
@@@ -69,7 -69,7 +69,7 @@@ class AdminPanelAction extends Actio
          // User must be logged in.
  
          if (!common_logged_in()) {
-             // TRANS: Client error message
+             // TRANS: Client error message thrown when trying to access the admin panel while not logged in.
              $this->clientError(_('Not logged in.'));
              return false;
          }
@@@ -94,7 -94,7 +94,7 @@@
          // User must have the right to change admin settings
  
          if (!$user->hasRight(Right::CONFIGURESITE)) {
-             // TRANS: Client error message
+             // TRANS: Client error message thrown when a user tries to change admin settings but has no access rights.
              $this->clientError(_('You cannot make changes to this site.'));
              return false;
          }
          $name = mb_substr($name, 0, -10);
  
          if (!self::canAdmin($name)) {
-             // TRANS: Client error message
+             // TRANS: Client error message throw when a certain panel's settings cannot be changed.
              $this->clientError(_('Changes to that panel are not allowed.'), 403);
              return false;
          }
  
      function showForm()
      {
-         // TRANS: Client error message
+         // TRANS: Client error message.
          $this->clientError(_('showForm() not implemented.'));
          return;
      }
              $result = $config->delete();
              if (!$result) {
                  common_log_db_error($config, 'DELETE', __FILE__);
-                 // TRANS: Client error message
+                 // TRANS: Client error message thrown if design settings could not be deleted in
+                 // TRANS: the admin panel Design.
                  $this->clientError(_("Unable to delete design setting."));
                  return null;
              }
@@@ -407,14 -408,6 +408,14 @@@ class AdminPanelNav extends Widge
                                       $menu_title, $action_name == 'snapshotadminpanel', 'nav_snapshot_admin_panel');
              }
  
 +            if (AdminPanelAction::canAdmin('plugins')) {
 +                // TRANS: Menu item title/tooltip
 +                $menu_title = _('Plugins configuration');
 +                // TRANS: Menu item for site administration
 +                $this->out->menuItem(common_local_url('pluginsadminpanel'), _('Plugins'),
 +                                     $menu_title, $action_name == 'pluginsadminpanel', 'nav_design_admin_panel');
 +            }
 +
              Event::handle('EndAdminPanelNav', array($this));
          }
          $this->action->elementEnd('ul');
diff --combined lib/command.php
index 084c61fd1a119ad5dbb082535a15750e73856b04,a769dc1fc641275c8dbb45afa899d28d9a9abe72..30db9d0691762bdfa2372ebee8326f13e1140263
@@@ -122,6 -122,8 +122,8 @@@ class Comman
          }
          Event::handle('EndCommandGetProfile', array($this, $arg, &$profile));
          if (!$profile) {
+             // TRANS: Message given requesting a profile for a non-existing user.
+             // TRANS: %s is the nickname of the user for which the profile could not be found.
              throw new CommandException(sprintf(_('Could not find a user with nickname %s'), $arg));
          }
          return $profile;
          }
          Event::handle('EndCommandGetUser', array($this, $arg, &$user));
          if (!$user){
+             // TRANS: Message given getting a non-existing user.
+             // TRANS: %s is the nickname of the user that could not be found.
              throw new CommandException(sprintf(_('Could not find a local user with nickname %s'),
                                 $arg));
          }
@@@ -225,6 -229,8 +229,8 @@@ class NudgeCommand extends Comman
              }
              // XXX: notify by IM
              // XXX: notify by SMS
+             // TRANS: Message given having nudged another user.
+             // TRANS: %s is the nickname of the user that was nudged.
              $channel->output($this->user, sprintf(_('Nudge sent to %s'),
                             $recipient->nickname));
          }
@@@ -328,12 -334,16 +334,16 @@@ class JoinCommand extends Comman
                  Event::handle('EndJoinGroup', array($group, $cur));
              }
          } catch (Exception $e) {
-             $channel->error($cur, sprintf(_('Could not join user %s to group %s'),
+             // TRANS: Message given having failed to add a user to a group.
+             // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
+             $channel->error($cur, sprintf(_('Could not join user %1$s to group %2$s'),
                                            $cur->nickname, $group->nickname));
              return;
          }
  
-         $channel->output($cur, sprintf(_('%s joined group %s'),
+         // TRANS: Message given having added a user to a group.
+         // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
+         $channel->output($cur, sprintf(_('%1$s joined group %2$s'),
                                                $cur->nickname,
                                                $group->nickname));
      }
@@@ -370,12 -380,16 +380,16 @@@ class DropCommand extends Comman
                  Event::handle('EndLeaveGroup', array($group, $cur));
              }
          } catch (Exception $e) {
-             $channel->error($cur, sprintf(_('Could not remove user %s to group %s'),
+             // TRANS: Message given having failed to remove a user from a group.
+             // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
+             $channel->error($cur, sprintf(_('Could not remove user %1$s from group %2$s'),
                                            $cur->nickname, $group->nickname));
              return;
          }
  
-         $channel->output($cur, sprintf(_('%s left group %s'),
+         // TRANS: Message given having removed a user from a group.
+         // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
+         $channel->output($cur, sprintf(_('%1$s left group %2$s'),
                                                $cur->nickname,
                                                $group->nickname));
      }
@@@ -395,18 -409,24 +409,24 @@@ class WhoisCommand extends Comman
      {
          $recipient = $this->getProfile($this->other);
  
+         // TRANS: Whois output.
+         // TRANS: %1$s nickname of the queried user, %2$s is their profile URL.
          $whois = sprintf(_("%1\$s (%2\$s)"), $recipient->nickname,
                           $recipient->profileurl);
          if ($recipient->fullname) {
+             // TRANS: Whois output. %s is the full name of the queried user.
              $whois .= "\n" . sprintf(_('Fullname: %s'), $recipient->fullname);
          }
          if ($recipient->location) {
+             // TRANS: Whois output. %s is the location of the queried user.
              $whois .= "\n" . sprintf(_('Location: %s'), $recipient->location);
          }
          if ($recipient->homepage) {
+             // TRANS: Whois output. %s is the homepage of the queried user.
              $whois .= "\n" . sprintf(_('Homepage: %s'), $recipient->homepage);
          }
          if ($recipient->bio) {
+             // TRANS: Whois output. %s is the bio information of the queried user.
              $whois .= "\n" . sprintf(_('About: %s'), $recipient->bio);
          }
          $channel->output($this->user, $whois);
@@@ -447,7 -467,9 +467,9 @@@ class MessageCommand extends Comman
          $this->text = common_shorten_links($this->text);
  
          if (Message::contentTooLong($this->text)) {
-             $channel->error($this->user, sprintf(_('Message too long - maximum is %d characters, you sent %d'),
+             // TRANS: Message given if content is too long.
+             // TRANS: %1$d is the maximum number of characters, %2$d is the number of submitted characters.
+             $channel->error($this->user, sprintf(_('Message too long - maximum is %1$d characters, you sent %2$d'),
                                                   Message::maxContent(), mb_strlen($this->text)));
              return;
          }
          $message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source());
          if ($message) {
              $message->notify();
+             // TRANS: Message given have sent a direct message to another user.
+             // TRANS: %s is the name of the other user.
              $channel->output($this->user, sprintf(_('Direct message to %s sent'), $this->other));
          } else {
              $channel->error($this->user, _('Error sending direct message.'));
@@@ -500,6 -524,8 +524,8 @@@ class RepeatCommand extends Comman
  
          if ($repeat) {
  
+             // TRANS: Message given having repeated a notice from another user.
+             // TRANS: %s is the name of the user for which the notice was repeated.
              $channel->output($this->user, sprintf(_('Notice from %s repeated'), $recipient->nickname));
          } else {
              $channel->error($this->user, _('Error repeating notice.'));
@@@ -652,7 -678,7 +678,7 @@@ class OffCommand extends Comman
      }
      function handle($channel)
      {
 -        if ($other) {
 +        if ($this->other) {
              $channel->error($this->user, _("Command not yet implemented."));
          } else {
              if ($channel->off($this->user)) {
@@@ -675,7 -701,7 +701,7 @@@ class OnCommand extends Comman
  
      function handle($channel)
      {
 -        if ($other) {
 +        if ($this->other) {
              $channel->error($this->user, _("Command not yet implemented."));
          } else {
              if ($channel->on($this->user)) {
diff --combined lib/implugin.php
index 018b0ecee08e51c7b9c7c57571d8249dac2d91d4,0000000000000000000000000000000000000000..7302859a47d73d728d0e1045ec7b0df9621d84ef
mode 100644,000000..100644
--- /dev/null
@@@ -1,613 -1,0 +1,615 @@@
-         $transports[$this->transport] = array('display' => $this->getDisplayName());
 +<?php
 +/**
 + * StatusNet, the distributed open-source microblogging tool
 + *
 + * Superclass for plugins that do instant messaging
 + *
 + * PHP version 5
 + *
 + * LICENCE: 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/>.
 + *
 + * @category  Plugin
 + * @package   StatusNet
 + * @author    Craig Andrews <candrews@integralblue.com>
 + * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 + * @link      http://status.net/
 + */
 +
 +if (!defined('STATUSNET') && !defined('LACONICA')) {
 +    exit(1);
 +}
 +
 +/**
 + * Superclass for plugins that do authentication
 + *
 + * Implementations will likely want to override onStartIoManagerClasses() so that their
 + *   IO manager is used
 + *
 + * @category Plugin
 + * @package  StatusNet
 + * @author   Craig Andrews <candrews@integralblue.com>
 + * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 + * @link     http://status.net/
 + */
 +
 +abstract class ImPlugin extends Plugin
 +{
 +    //name of this IM transport
 +    public $transport = null;
 +    //list of screennames that should get all public notices
 +    public $public = array();
 +
 +    /**
 +     * normalize a screenname for comparison
 +     *
 +     * @param string $screenname screenname to normalize
 +     *
 +     * @return string an equivalent screenname in normalized form
 +     */
 +    abstract function normalize($screenname);
 +
 +
 +    /**
 +     * validate (ensure the validity of) a screenname
 +     *
 +     * @param string $screenname screenname to validate
 +     *
 +     * @return boolean
 +     */
 +    abstract function validate($screenname);
 +
 +    /**
 +     * get the internationalized/translated display name of this IM service
 +     *
 +     * @return string
 +     */
 +    abstract function getDisplayName();
 +
 +    /**
 +     * send a single notice to a given screenname
 +     * The implementation should put raw data, ready to send, into the outgoing
 +     *   queue using enqueue_outgoing_raw()
 +     *
 +     * @param string $screenname screenname to send to
 +     * @param Notice $notice notice to send
 +     *
 +     * @return boolean success value
 +     */
 +    function send_notice($screenname, $notice)
 +    {
 +        return $this->send_message($screenname, $this->format_notice($notice));
 +    }
 +
 +    /**
 +     * send a message (text) to a given screenname
 +     * The implementation should put raw data, ready to send, into the outgoing
 +     *   queue using enqueue_outgoing_raw()
 +     *
 +     * @param string $screenname screenname to send to
 +     * @param Notice $body text to send
 +     *
 +     * @return boolean success value
 +     */
 +    abstract function send_message($screenname, $body);
 +
 +    /**
 +     * receive a raw message
 +     * Raw IM data is taken from the incoming queue, and passed to this function.
 +     * It should parse the raw message and call handle_incoming()
 +     *
 +     * @param object $data raw IM data
 +     *
 +     * @return boolean success value
 +     */
 +    abstract function receive_raw_message($data);
 +
 +    /**
 +     * get the screenname of the daemon that sends and receives message for this service
 +     *
 +     * @return string screenname of this plugin
 +     */
 +    abstract function daemon_screenname();
 +
 +    /**
 +     * get the microid uri of a given screenname
 +     *
 +     * @param string $screenname screenname
 +     *
 +     * @return string microid uri
 +     */
 +    function microiduri($screenname)
 +    {
 +        return $this->transport . ':' . $screenname;    
 +    }
 +    //========================UTILITY FUNCTIONS USEFUL TO IMPLEMENTATIONS - MISC ========================\
 +
 +    /**
 +     * Put raw message data (ready to send) into the outgoing queue
 +     *
 +     * @param object $data
 +     */
 +    function enqueue_outgoing_raw($data)
 +    {
 +        $qm = QueueManager::get();
 +        $qm->enqueue($data, $this->transport . '-out');
 +    }
 +
 +    /**
 +     * Put raw message data (received, ready to be processed) into the incoming queue
 +     *
 +     * @param object $data
 +     */
 +    function enqueue_incoming_raw($data)
 +    {
 +        $qm = QueueManager::get();
 +        $qm->enqueue($data, $this->transport . '-in');
 +    }
 +
 +    /**
 +     * given a screenname, get the corresponding user
 +     *
 +     * @param string $screenname
 +     *
 +     * @return User user
 +     */
 +    function get_user($screenname)
 +    {
 +        $user_im_prefs = $this->get_user_im_prefs_from_screenname($screenname);
 +        if($user_im_prefs){
 +            $user = User::staticGet('id', $user_im_prefs->user_id);
 +            $user_im_prefs->free();
 +            return $user;
 +        }else{
 +            return false;
 +        }
 +    }
 +
 +
 +    /**
 +     * given a screenname, get the User_im_prefs object for this transport
 +     *
 +     * @param string $screenname
 +     *
 +     * @return User_im_prefs user_im_prefs
 +     */
 +    function get_user_im_prefs_from_screenname($screenname)
 +    {
 +        if($user_im_prefs = User_im_prefs::pkeyGet( array('transport' => $this->transport, 'screenname' => $screenname) )){
 +            return $user_im_prefs;
 +        }else{
 +            return false;
 +        }
 +    }
 +
 +
 +    /**
 +     * given a User, get their screenname
 +     *
 +     * @param User $user
 +     *
 +     * @return string screenname of that user
 +     */
 +    function get_screenname($user)
 +    {
 +        $user_im_prefs = $this->get_user_im_prefs_from_user($user);
 +        if($user_im_prefs){
 +            return $user_im_prefs->screenname;
 +        }else{
 +            return false;
 +        }
 +    }
 +
 +
 +    /**
 +     * given a User, get their User_im_prefs
 +     *
 +     * @param User $user
 +     *
 +     * @return User_im_prefs user_im_prefs of that 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) )){
 +            return $user_im_prefs;
 +        }else{
 +            return false;
 +        }
 +    }
 +    //========================UTILITY FUNCTIONS USEFUL TO IMPLEMENTATIONS - SENDING ========================\
 +    /**
 +     * Send a message to a given screenname from the site
 +     *
 +     * @param string $screenname screenname to send the message to
 +     * @param string $msg message contents to send
 +     *
 +     * @param boolean success
 +     */
 +    protected function send_from_site($screenname, $msg)
 +    {
 +        $text = '['.common_config('site', 'name') . '] ' . $msg;
 +        $this->send_message($screenname, $text);
 +    }
 +
 +    /**
 +     * send a confirmation code to a user
 +     *
 +     * @param string $screenname screenname sending to
 +     * @param string $code the confirmation code
 +     * @param User $user user sending to
 +     *
 +     * @return boolean success value
 +     */
 +    function send_confirmation_code($screenname, $code, $user)
 +    {
 +        $body = sprintf(_('User "%s" on %s has said that your %s screenname belongs to them. ' .
 +          'If that\'s true, you can confirm by clicking on this URL: ' .
 +          '%s' .
 +          ' . (If you cannot click it, copy-and-paste it into the ' .
 +          'address bar of your browser). If that user isn\'t you, ' .
 +          'or if you didn\'t request this confirmation, just ignore this message.'),
 +          $user->nickname, common_config('site', 'name'), $this->getDisplayName(), common_local_url('confirmaddress', array('code' => $code)));
 +
 +        return $this->send_message($screenname, $body);
 +    }
 +
 +    /**
 +     * send a notice to all public listeners
 +     *
 +     * For notices that are generated on the local system (by users), we can optionally
 +     * forward them to remote listeners by XMPP.
 +     *
 +     * @param Notice $notice notice to broadcast
 +     *
 +     * @return boolean success flag
 +     */
 +
 +    function public_notice($notice)
 +    {
 +        // Now, users who want everything
 +
 +        // FIXME PRIV don't send out private messages here
 +        // XXX: should we send out non-local messages if public,localonly
 +        // = false? I think not
 +
 +        foreach ($this->public as $screenname) {
 +            common_log(LOG_INFO,
 +                       'Sending notice ' . $notice->id .
 +                       ' to public listener ' . $screenname,
 +                       __FILE__);
 +            $this->send_notice($screenname, $notice);
 +        }
 +
 +        return true;
 +    }
 +
 +    /**
 +     * broadcast a notice to all subscribers and reply recipients
 +     *
 +     * This function will send a notice to all subscribers on the local server
 +     * who have IM addresses, and have IM notification enabled, and
 +     * have this subscription enabled for IM. It also sends the notice to
 +     * all recipients of @-replies who have IM addresses and IM notification
 +     * enabled. This is really the heart of IM distribution in StatusNet.
 +     *
 +     * @param Notice $notice The notice to broadcast
 +     *
 +     * @return boolean success flag
 +     */
 +
 +    function broadcast_notice($notice)
 +    {
 +
 +        $ni = $notice->whoGets();
 +
 +        foreach ($ni as $user_id => $reason) {
 +            $user = User::staticGet($user_id);
 +            if (empty($user)) {
 +                // either not a local user, or just not found
 +                continue;
 +            }
 +            $user_im_prefs = $this->get_user_im_prefs_from_user($user);
 +            if(!$user_im_prefs || !$user_im_prefs->notify){
 +                continue;
 +            }
 +
 +            switch ($reason) {
 +            case NOTICE_INBOX_SOURCE_REPLY:
 +                if (!$user_im_prefs->replies) {
 +                    continue 2;
 +                }
 +                break;
 +            case NOTICE_INBOX_SOURCE_SUB:
 +                $sub = Subscription::pkeyGet(array('subscriber' => $user->id,
 +                                                   'subscribed' => $notice->profile_id));
 +                if (empty($sub) || !$sub->jabber) {
 +                    continue 2;
 +                }
 +                break;
 +            case NOTICE_INBOX_SOURCE_GROUP:
 +                break;
 +            default:
 +                throw new Exception(sprintf(_("Unknown inbox source %d."), $reason));
 +            }
 +
 +            common_log(LOG_INFO,
 +                       'Sending notice ' . $notice->id . ' to ' . $user_im_prefs->screenname,
 +                       __FILE__);
 +            $this->send_notice($user_im_prefs->screenname, $notice);
 +            $user_im_prefs->free();
 +        }
 +
 +        return true;
 +    }
 +
 +    /**
 +     * makes a plain-text formatted version of a notice, suitable for IM distribution
 +     *
 +     * @param Notice  $notice  notice being sent
 +     *
 +     * @return string plain-text version of the notice, with user nickname prefixed
 +     */
 +
 +    function format_notice($notice)
 +    {
 +        $profile = $notice->getProfile();
 +        return $profile->nickname . ': ' . $notice->content . ' [' . $notice->id . ']';
 +    }
 +    //========================UTILITY FUNCTIONS USEFUL TO IMPLEMENTATIONS - RECEIVING ========================\
 +
 +    /**
 +     * Attempt to handle a message as a command
 +     * @param User $user user the message is from
 +     * @param string $body message text
 +     * @return boolean true if the message was a command and was executed, false if it was not a command
 +     */
 +    protected function handle_command($user, $body)
 +    {
 +        $inter = new CommandInterpreter();
 +        $cmd = $inter->handle_command($user, $body);
 +        if ($cmd) {
 +            $chan = new IMChannel($this);
 +            $cmd->execute($chan);
 +            return true;
 +        } else {
 +            return false;
 +        }
 +    }
 +
 +    /**
 +     * Is some text an autoreply message?
 +     * @param string $txt message text
 +     * @return boolean true if autoreply
 +     */
 +    protected function is_autoreply($txt)
 +    {
 +        if (preg_match('/[\[\(]?[Aa]uto[-\s]?[Rr]e(ply|sponse)[\]\)]/', $txt)) {
 +            return true;
 +        } else if (preg_match('/^System: Message wasn\'t delivered. Offline storage size was exceeded.$/', $txt)) {
 +            return true;
 +        } else {
 +            return false;
 +        }
 +    }
 +
 +    /**
 +     * Is some text an OTR message?
 +     * @param string $txt message text
 +     * @return boolean true if OTR
 +     */
 +    protected function is_otr($txt)
 +    {
 +        if (preg_match('/^\?OTR/', $txt)) {
 +            return true;
 +        } else {
 +            return false;
 +        }
 +    }
 +
 +    /**
 +     * Helper for handling incoming messages
 +     * Your incoming message handler will probably want to call this function
 +     *
 +     * @param string $from screenname the message was sent from
 +     * @param string $message message contents
 +     *
 +     * @param boolean success
 +     */
 +    protected function handle_incoming($from, $notice_text)
 +    {
 +        $user = $this->get_user($from);
 +        // For common_current_user to work
 +        global $_cur;
 +        $_cur = $user;
 +
 +        if (!$user) {
 +            $this->send_from_site($from, 'Unknown user; go to ' .
 +                             common_local_url('imsettings') .
 +                             ' to add your address to your account');
 +            common_log(LOG_WARNING, 'Message from unknown user ' . $from);
 +            return;
 +        }
 +        if ($this->handle_command($user, $notice_text)) {
 +            common_log(LOG_INFO, "Command message by $from handled.");
 +            return;
 +        } else if ($this->is_autoreply($notice_text)) {
 +            common_log(LOG_INFO, 'Ignoring auto reply from ' . $from);
 +            return;
 +        } else if ($this->is_otr($notice_text)) {
 +            common_log(LOG_INFO, 'Ignoring OTR from ' . $from);
 +            return;
 +        } else {
 +
 +            common_log(LOG_INFO, 'Posting a notice from ' . $user->nickname);
 +
 +            $this->add_notice($from, $user, $notice_text);
 +        }
 +
 +        $user->free();
 +        unset($user);
 +        unset($_cur);
 +        unset($message);
 +    }
 +
 +    /**
 +     * Helper for handling incoming messages
 +     * Your incoming message handler will probably want to call this function
 +     *
 +     * @param string $from screenname the message was sent from
 +     * @param string $message message contents
 +     *
 +     * @param boolean success
 +     */
 +    protected function add_notice($screenname, $user, $body)
 +    {
 +        $body = trim(strip_tags($body));
 +        $content_shortened = common_shorten_links($body);
 +        if (Notice::contentTooLong($content_shortened)) {
 +          $this->send_from_site($screenname, sprintf(_('Message too long - maximum is %1$d characters, you sent %2$d.'),
 +                                          Notice::maxContent(),
 +                                          mb_strlen($content_shortened)));
 +          return;
 +        }
 +
 +        try {
 +            $notice = Notice::saveNew($user->id, $content_shortened, $this->transport);
 +        } catch (Exception $e) {
 +            common_log(LOG_ERR, $e->getMessage());
 +            $this->send_from_site($from, $e->getMessage());
 +            return;
 +        }
 +
 +        common_broadcast_notice($notice);
 +        common_log(LOG_INFO,
 +                   'Added notice ' . $notice->id . ' from user ' . $user->nickname);
 +        $notice->free();
 +        unset($notice);
 +    }
 +
 +    //========================EVENT HANDLERS========================\
 +    
 +    /**
 +     * Register notice queue handler
 +     *
 +     * @param QueueManager $manager
 +     *
 +     * @return boolean hook return
 +     */
 +    function onEndInitializeQueueManager($manager)
 +    {
 +        $manager->connect($this->transport . '-in', new ImReceiverQueueHandler($this), 'im');
 +        $manager->connect($this->transport, new ImQueueHandler($this));
 +        $manager->connect($this->transport . '-out', new ImSenderQueueHandler($this), 'im');
 +        return true;
 +    }
 +
 +    function onStartImDaemonIoManagers(&$classes)
 +    {
 +        //$classes[] = new ImManager($this); // handles sending/receiving/pings/reconnects
 +        return true;
 +    }
 +
 +    function onStartEnqueueNotice($notice, &$transports)
 +    {
 +        $profile = Profile::staticGet($notice->profile_id);
 +
 +        if (!$profile) {
 +            common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
 +                       'unknown profile ' . common_log_objstring($notice),
 +                       __FILE__);
 +        }else{
 +            $transports[] = $this->transport;
 +        }
 +
 +        return true;
 +    }
 +
 +    function onEndShowHeadElements($action)
 +    {
 +        $aname = $action->trimmed('action');
 +
 +        if ($aname == 'shownotice') {
 +
 +            $user_im_prefs = new User_im_prefs();
 +            $user_im_prefs->user_id = $action->profile->id;
 +            $user_im_prefs->transport = $this->transport;
 +
 +            if ($user_im_prefs->find() && $user_im_prefs->fetch() && $user_im_prefs->microid && $action->notice->uri) {
 +                $id = new Microid($this->microiduri($user_im_prefs->screenname),
 +                                  $action->notice->uri);
 +                $action->element('meta', array('name' => 'microid',
 +                                             'content' => $id->toString()));
 +            }
 +
 +        } else if ($aname == 'showstream') {
 +
 +            $user_im_prefs = new User_im_prefs();
 +            $user_im_prefs->user_id = $action->user->id;
 +            $user_im_prefs->transport = $this->transport;
 +
 +            if ($user_im_prefs->find() && $user_im_prefs->fetch() && $user_im_prefs->microid && $action->profile->profileurl) {
 +                $id = new Microid($this->microiduri($user_im_prefs->screenname),
 +                                  $action->selfUrl());
 +                $action->element('meta', array('name' => 'microid',
 +                                               'content' => $id->toString()));
 +            }
 +        }
 +    }
 +
 +    function onNormalizeImScreenname($transport, &$screenname)
 +    {
 +        if($transport == $this->transport)
 +        {
 +            $screenname = $this->normalize($screenname);
 +            return false;
 +        }
 +    }
 +
 +    function onValidateImScreenname($transport, $screenname, &$valid)
 +    {
 +        if($transport == $this->transport)
 +        {
 +            $valid = $this->validate($screenname);
 +            return false;
 +        }
 +    }
 +
 +    function onGetImTransports(&$transports)
 +    {
++        $transports[$this->transport] = array(
++            'display' => $this->getDisplayName(),
++            'daemon_screenname' => $this->daemon_screenname());
 +    }
 +
 +    function onSendImConfirmationCode($transport, $screenname, $code, $user)
 +    {
 +        if($transport == $this->transport)
 +        {
 +            $this->send_confirmation_code($screenname, $code, $user);
 +            return false;
 +        }
 +    }
 +
 +    function onUserDeleteRelated($user, &$tables)
 +    {
 +        $tables[] = 'User_im_prefs';
 +        return true;
 +    }
 +
 +    function initialize()
 +    {
 +        if(is_null($this->transport)){
 +            throw new Exception('transport cannot be null');
 +        }
 +    }
 +}
diff --combined lib/statusnet.php
index 98f25c8a08e15b9ed7644c14d7a07b004e62d942,2aa73486eb100ad996cca777f497d1509cd24bda..ac5d10134252e8a19e28bd5ea2a30e5f3b457850
@@@ -31,6 -31,7 +31,7 @@@ class StatusNe
  {
      protected static $have_config;
      protected static $is_api;
+     protected static $plugins = array();
  
      /**
       * Configure and instantiate a plugin into the current configuration.
                  $inst->$aname = $avalue;
              }
          }
+         // Record activated plugins for later display/config dump
+         self::$plugins[] = array($name, $attrs);
          return true;
      }
  
+     /**
+      * Get a list of activated plugins in this process.
+      * @return array of (string $name, array $args) pairs
+      */
+     public static function getActivePlugins()
+     {
+         return self::$plugins;
+     }
      /**
       * Initialize, or re-initialize, StatusNet global configuration
       * and plugins.
      {
          // Load default plugins
          foreach (common_config('plugins', 'default') as $name => $params) {
 +            $key = 'disable-' . $name;
 +            if (common_config('plugins', $key)) {
 +                continue;
 +            }
 +
              if (is_null($params)) {
                  addPlugin($name);
              } else if (is_array($params)) {
          global $_server, $_path, $config;
  
          Event::clearHandlers();
+         self::$plugins = array();
  
          // try to figure out where we are. $server and $path
          // can be set by including module, else we guess based
          }
  
          // Backwards compatibility
 -
          if (array_key_exists('memcached', $config)) {
              if ($config['memcached']['enabled']) {
                  addPlugin('Memcache', array('servers' => $config['memcached']['server']));
                  $config['cache']['base'] = $config['memcached']['base'];
              }
          }
 +        if (array_key_exists('xmpp', $config)) {
 +            if ($config['xmpp']['enabled']) {
 +                addPlugin('xmpp', array(
 +                    'server' => $config['xmpp']['server'],
 +                    'port' => $config['xmpp']['port'],
 +                    'user' => $config['xmpp']['user'],
 +                    'resource' => $config['xmpp']['resource'],
 +                    'encryption' => $config['xmpp']['encryption'],
 +                    'password' => $config['xmpp']['password'],
 +                    'host' => $config['xmpp']['host'],
 +                    'debug' => $config['xmpp']['debug'],
 +                    'public' => $config['xmpp']['public']
 +                ));
 +            }
 +        }
      }
  }
  
diff --combined lib/util.php
index 96d21bc59cf471792b9a13f6e0b33085c9ba63fc,b11bb06e3c2496ef4b88c766054f1daff644b296..2e987a23a36a8690e8d7a25b9c36a54d2058972e
@@@ -862,7 -862,14 +862,14 @@@ function common_xml_safe_str($str
  function common_tag_link($tag)
  {
      $canonical = common_canonical_tag($tag);
-     $url = common_local_url('tag', array('tag' => $canonical));
+     if (common_config('singleuser', 'enabled')) {
+         // regular TagAction isn't set up in 1user mode
+         $url = common_local_url('showstream',
+                                 array('nickname' => common_config('singleuser', 'nickname'),
+                                       'tag' => $canonical));
+     } else {
+         $url = common_local_url('tag', array('tag' => $canonical));
+     }
      $xs = new XMLStringer();
      $xs->elementStart('span', 'tag');
      $xs->element('a', array('href' => $url,
@@@ -1043,24 -1050,38 +1050,38 @@@ function common_date_string($dt
      if ($now < $t) { // that shouldn't happen!
          return common_exact_date($dt);
      } else if ($diff < 60) {
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return _('a few seconds ago');
      } else if ($diff < 92) {
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return _('about a minute ago');
      } else if ($diff < 3300) {
+         // XXX: should support plural.
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return sprintf(_('about %d minutes ago'), round($diff/60));
      } else if ($diff < 5400) {
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return _('about an hour ago');
      } else if ($diff < 22 * 3600) {
+         // XXX: should support plural.
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return sprintf(_('about %d hours ago'), round($diff/3600));
      } else if ($diff < 37 * 3600) {
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return _('about a day ago');
      } else if ($diff < 24 * 24 * 3600) {
+         // XXX: should support plural.
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return sprintf(_('about %d days ago'), round($diff/(24*3600)));
      } else if ($diff < 46 * 24 * 3600) {
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return _('about a month ago');
      } else if ($diff < 330 * 24 * 3600) {
+         // XXX: should support plural.
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return sprintf(_('about %d months ago'), round($diff/(30*24*3600)));
      } else if ($diff < 480 * 24 * 3600) {
+         // TRANS: Used in notices to indicate when the notice was made compared to now.
          return _('about a year ago');
      } else {
          return common_exact_date($dt);
@@@ -1176,10 -1197,19 +1197,10 @@@ function common_enqueue_notice($notice
          $transports[] = 'plugin';
      }
  
 -    $xmpp = common_config('xmpp', 'enabled');
 -
 -    if ($xmpp) {
 -        $transports[] = 'jabber';
 -    }
 -
      // @fixme move these checks into QueueManager and/or individual handlers
      if ($notice->is_local == Notice::LOCAL_PUBLIC ||
          $notice->is_local == Notice::LOCAL_NONPUBLIC) {
          $transports = array_merge($transports, $localTransports);
 -        if ($xmpp) {
 -            $transports[] = 'public';
 -        }
      }
  
      if (Event::handle('StartEnqueueNotice', array($notice, &$transports))) {
@@@ -1263,12 -1293,38 +1284,38 @@@ function common_mtrand($bytes
      return $enc;
  }
  
+ /**
+  * Record the given URL as the return destination for a future
+  * form submission, to be read by common_get_returnto().
+  * 
+  * @param string $url
+  * 
+  * @fixme as a session-global setting, this can allow multiple forms
+  * to conflict and overwrite each others' returnto destinations if
+  * the user has multiple tabs or windows open.
+  * 
+  * Should refactor to index with a token or otherwise only pass the
+  * data along its intended path.
+  */
  function common_set_returnto($url)
  {
      common_ensure_session();
      $_SESSION['returnto'] = $url;
  }
  
+ /**
+  * Fetch a return-destination URL previously recorded by
+  * common_set_returnto().
+  * 
+  * @return mixed URL string or null
+  * 
+  * @fixme as a session-global setting, this can allow multiple forms
+  * to conflict and overwrite each others' returnto destinations if
+  * the user has multiple tabs or windows open.
+  * 
+  * Should refactor to index with a token or otherwise only pass the
+  * data along its intended path.
+  */
  function common_get_returnto()
  {
      common_ensure_session();