Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly

This commit is contained in:
abjectio 2015-08-18 22:55:28 +02:00
commit 91c1ab6017
76 changed files with 1688 additions and 2518 deletions

View File

@ -615,12 +615,12 @@ EndCheckPassword: After checking a username/password pair
- $authenticatedUser: User object if credentials match a user, else null. - $authenticatedUser: User object if credentials match a user, else null.
StartChangePassword: Before changing a password StartChangePassword: Before changing a password
- $user: user - Profile $target: The profile of the User that is changing password
- $oldpassword: the user's old password - $oldpassword: the user's old password
- $newpassword: the desired new password - $newpassword: the desired new password
EndChangePassword: After changing a password EndChangePassword: After changing a password
- $user: user - Profile $target: The profile of the User that just changed its password
StartHashPassword: Generate a hashed version of the password (like a salted crypt) StartHashPassword: Generate a hashed version of the password (like a salted crypt)
- &$hashed: Hashed version of the password, later put in the database - &$hashed: Hashed version of the password, later put in the database

View File

@ -76,11 +76,11 @@ class AvatarsettingsAction extends SettingsAction
/** /**
* Content area of the page * Content area of the page
* *
* Shows a form for uploading an avatar. * Shows a form for uploading an avatar. Currently overrides FormAction's showContent
* since we haven't made classes out of AvatarCropForm and AvatarUploadForm.
* *
* @return void * @return void
*/ */
function showContent() function showContent()
{ {
if ($this->mode == 'crop') { if ($this->mode == 'crop') {
@ -243,51 +243,18 @@ class AvatarsettingsAction extends SettingsAction
$this->elementEnd('form'); $this->elementEnd('form');
} }
/** protected function doPost()
* Handle a post
*
* We mux on the button name to figure out what the user actually wanted.
*
* @return void
*/
function handlePost()
{ {
// Workaround for PHP returning empty $_POST and $_FILES when POST
// length > post_max_size in php.ini
if (empty($_FILES)
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return;
}
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if (Event::handle('StartAvatarSaveForm', array($this))) { if (Event::handle('StartAvatarSaveForm', array($this))) {
if ($this->arg('upload')) { if ($this->trimmed('upload')) {
$this->uploadAvatar(); return $this->uploadAvatar();
} else if ($this->arg('crop')) { } else if ($this->trimmed('crop')) {
$this->cropAvatar(); return $this->cropAvatar();
} else if ($this->arg('delete')) { } else if ($this->trimmed('delete')) {
$this->deleteAvatar(); return $this->deleteAvatar();
} else { } else {
// TRANS: Unexpected validation error on avatar upload form. // TRANS: Unexpected validation error on avatar upload form.
$this->showForm(_('Unexpected form submission.')); throw new ClientException(_('Unexpected form submission.'));
} }
Event::handle('EndAvatarSaveForm', array($this)); Event::handle('EndAvatarSaveForm', array($this));
} }
@ -303,21 +270,12 @@ class AvatarsettingsAction extends SettingsAction
*/ */
function uploadAvatar() function uploadAvatar()
{ {
try { // ImageFile throws exception if something goes wrong, which we'll
// pick up and show as an error message above the form.
$imagefile = ImageFile::fromUpload('avatarfile'); $imagefile = ImageFile::fromUpload('avatarfile');
} catch (Exception $e) {
$this->showForm($e->getMessage());
return;
}
if ($imagefile === null) {
// TRANS: Validation error on avatar upload form when no file was uploaded.
$this->showForm(_('No file uploaded.'));
return;
}
$cur = common_current_user();
$type = $imagefile->preferredType(); $type = $imagefile->preferredType();
$filename = Avatar::filename($cur->id, $filename = Avatar::filename($this->scoped->getID(),
image_type_to_extension($type), image_type_to_extension($type),
null, null,
'tmp'.common_timestamp()); 'tmp'.common_timestamp());
@ -338,8 +296,7 @@ class AvatarsettingsAction extends SettingsAction
$this->mode = 'crop'; $this->mode = 'crop';
// TRANS: Avatar upload form instruction after uploading a file. // TRANS: Avatar upload form instruction after uploading a file.
$this->showForm(_('Pick a square area of the image to be your avatar.'), return _('Pick a square area of the image to be your avatar.');
true);
} }
/** /**
@ -351,13 +308,12 @@ class AvatarsettingsAction extends SettingsAction
{ {
$filedata = $_SESSION['FILEDATA']; $filedata = $_SESSION['FILEDATA'];
if (!$filedata) { if (empty($filedata)) {
// TRANS: Server error displayed if an avatar upload went wrong somehow server side. // TRANS: Server error displayed if an avatar upload went wrong somehow server side.
$this->serverError(_('Lost our file data.')); throw new ServerException(_('Lost our file data.'));
} }
$file_d = ($filedata['width'] > $filedata['height']) $file_d = min($filedata['width'], $filedata['height']);
? $filedata['height'] : $filedata['width'];
$dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0; $dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0;
$dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0; $dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
@ -369,11 +325,8 @@ class AvatarsettingsAction extends SettingsAction
'x' => $dest_x, 'y' => $dest_y, 'x' => $dest_x, 'y' => $dest_y,
'w' => $dest_w, 'h' => $dest_h); 'w' => $dest_w, 'h' => $dest_h);
$user = common_current_user();
$profile = $user->getProfile();
$imagefile = new ImageFile(null, $filedata['filepath']); $imagefile = new ImageFile(null, $filedata['filepath']);
$filename = Avatar::filename($profile->getID(), image_type_to_extension($imagefile->preferredType()), $filename = Avatar::filename($this->scoped->getID(), image_type_to_extension($imagefile->preferredType()),
$size, common_timestamp()); $size, common_timestamp());
try { try {
$imagefile->resizeTo(Avatar::path($filename), $box); $imagefile->resizeTo(Avatar::path($filename), $box);
@ -385,16 +338,16 @@ class AvatarsettingsAction extends SettingsAction
} }
} }
if ($profile->setOriginal($filename)) { if ($this->scoped->setOriginal($filename)) {
@unlink($filedata['filepath']); @unlink($filedata['filepath']);
unset($_SESSION['FILEDATA']); unset($_SESSION['FILEDATA']);
$this->mode = 'upload'; $this->mode = 'upload';
// TRANS: Success message for having updated a user avatar. // TRANS: Success message for having updated a user avatar.
$this->showForm(_('Avatar updated.'), true); return _('Avatar updated.');
} else {
// TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
$this->showForm(_('Failed updating avatar.'));
} }
// TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
throw new ServerException(_('Failed updating avatar.'));
} }
/** /**
@ -404,13 +357,10 @@ class AvatarsettingsAction extends SettingsAction
*/ */
function deleteAvatar() function deleteAvatar()
{ {
$user = common_current_user(); Avatar::deleteFromProfile($this->scoped);
$profile = $user->getProfile();
Avatar::deleteFromProfile($profile);
// TRANS: Success message for deleting a user avatar. // TRANS: Success message for deleting a user avatar.
$this->showForm(_('Avatar deleted.'), true); return _('Avatar deleted.');
} }
/** /**

View File

@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Settings for email * Settings for email
@ -112,8 +108,8 @@ class EmailsettingsAction extends SettingsAction
// TRANS: Button label to remove a confirmed e-mail address. // TRANS: Button label to remove a confirmed e-mail address.
$this->submit('remove', _m('BUTTON','Remove')); $this->submit('remove', _m('BUTTON','Remove'));
} else { } else {
try {
$confirm = $this->getConfirmation(); $confirm = $this->getConfirmation();
if ($confirm) {
$this->element('p', array('id' => 'form_unconfirmed'), $confirm->address); $this->element('p', array('id' => 'form_unconfirmed'), $confirm->address);
$this->element('p', array('class' => 'form_note'), $this->element('p', array('class' => 'form_note'),
// TRANS: Form note in e-mail settings form. // TRANS: Form note in e-mail settings form.
@ -123,12 +119,12 @@ class EmailsettingsAction extends SettingsAction
$this->hidden('email', $confirm->address); $this->hidden('email', $confirm->address);
// TRANS: Button label to cancel an e-mail address confirmation procedure. // TRANS: Button label to cancel an e-mail address confirmation procedure.
$this->submit('cancel', _m('BUTTON','Cancel')); $this->submit('cancel', _m('BUTTON','Cancel'));
} else { } catch (NoResultException $e) {
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label for e-mail address input in e-mail settings form. // TRANS: Field label for e-mail address input in e-mail settings form.
$this->input('email', _('Email address'), $this->input('email', _('Email address'),
($this->arg('email')) ? $this->arg('email') : null, $this->trimmed('email') ?: null,
// TRANS: Instructions for e-mail address input form. Do not translate // TRANS: Instructions for e-mail address input form. Do not translate
// TRANS: "example.org". It is one of the domain names reserved for // TRANS: "example.org". It is one of the domain names reserved for
// TRANS: use in examples by http://www.rfc-editor.org/rfc/rfc2606.txt. // TRANS: use in examples by http://www.rfc-editor.org/rfc/rfc2606.txt.
@ -231,12 +227,6 @@ class EmailsettingsAction extends SettingsAction
_('Allow friends to nudge me and send me an email.'), _('Allow friends to nudge me and send me an email.'),
$user->emailnotifynudge); $user->emailnotifynudge);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li');
$this->checkbox('emailmicroid',
// TRANS: Checkbox label in e-mail preferences form.
_('Publish a MicroID for my email address.'),
$user->emailmicroid);
$this->elementEnd('li');
Event::handle('EndEmailFormData', array($this, $this->scoped)); Event::handle('EndEmailFormData', array($this, $this->scoped));
} }
$this->elementEnd('ul'); $this->elementEnd('ul');
@ -254,56 +244,36 @@ class EmailsettingsAction extends SettingsAction
*/ */
function getConfirmation() function getConfirmation()
{ {
$user = common_current_user();
$confirm = new Confirm_address(); $confirm = new Confirm_address();
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->address_type = 'email'; $confirm->address_type = 'email';
if ($confirm->find(true)) { if ($confirm->find(true)) {
return $confirm; return $confirm;
} else {
return null;
}
} }
/** throw new NoResultException($confirm);
* Handle posts }
*
* Since there are a lot of different options on the page, we protected function doPost()
* figure out what we're supposed to do based on which button was
* pushed
*
* @return void
*/
function handlePost()
{ {
// CSRF protection if ($this->arg('save')) {
$token = $this->trimmed('token'); return $this->savePreferences();
if (!$token || $token != common_session_token()) { } else if ($this->arg('add')) {
// TRANS: Client error displayed when the session token does not match or is not given. return $this->addAddress();
$this->show_form(_('There was a problem with your session token. '. } else if ($this->arg('cancel')) {
'Try again, please.')); return $this->cancelConfirmation();
return; } else if ($this->arg('remove')) {
return $this->removeAddress();
} else if ($this->arg('removeincoming')) {
return $this->removeIncoming();
} else if ($this->arg('newincoming')) {
return $this->newIncoming();
} }
if ($this->arg('save')) {
$this->savePreferences();
} else if ($this->arg('add')) {
$this->addAddress();
} else if ($this->arg('cancel')) {
$this->cancelConfirmation();
} else if ($this->arg('remove')) {
$this->removeAddress();
} else if ($this->arg('removeincoming')) {
$this->removeIncoming();
} else if ($this->arg('newincoming')) {
$this->newIncoming();
} else {
// TRANS: Message given submitting a form with an unknown action in e-mail settings. // TRANS: Message given submitting a form with an unknown action in e-mail settings.
$this->showForm(_('Unexpected form submission.')); throw new ClientException(_('Unexpected form submission.'));
}
} }
/** /**
@ -313,25 +283,21 @@ class EmailsettingsAction extends SettingsAction
*/ */
function savePreferences() function savePreferences()
{ {
$user = $this->scoped->getUser();
if (Event::handle('StartEmailSaveForm', array($this, $this->scoped))) { if (Event::handle('StartEmailSaveForm', array($this, $this->scoped))) {
$emailnotifysub = $this->booleanintstring('emailnotifysub'); $emailnotifysub = $this->booleanintstring('emailnotifysub');
$emailnotifymsg = $this->booleanintstring('emailnotifymsg'); $emailnotifymsg = $this->booleanintstring('emailnotifymsg');
$emailnotifynudge = $this->booleanintstring('emailnotifynudge'); $emailnotifynudge = $this->booleanintstring('emailnotifynudge');
$emailnotifyattn = $this->booleanintstring('emailnotifyattn'); $emailnotifyattn = $this->booleanintstring('emailnotifyattn');
$emailmicroid = $this->booleanintstring('emailmicroid');
$emailpost = $this->booleanintstring('emailpost'); $emailpost = $this->booleanintstring('emailpost');
$user = $this->scoped->getUser();
$user->query('BEGIN'); $user->query('BEGIN');
$original = clone($user); $original = clone($user);
$user->emailnotifysub = $emailnotifysub; $user->emailnotifysub = $emailnotifysub;
$user->emailnotifymsg = $emailnotifymsg; $user->emailnotifymsg = $emailnotifymsg;
$user->emailnotifynudge = $emailnotifynudge; $user->emailnotifynudge = $emailnotifynudge;
$user->emailnotifyattn = $emailnotifyattn; $user->emailnotifyattn = $emailnotifyattn;
$user->emailmicroid = $emailmicroid;
$user->emailpost = $emailpost; $user->emailpost = $emailpost;
$result = $user->update($original); $result = $user->update($original);
@ -340,16 +306,15 @@ class EmailsettingsAction extends SettingsAction
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
$user->query('ROLLBACK'); $user->query('ROLLBACK');
// TRANS: Server error thrown on database error updating e-mail preferences. // TRANS: Server error thrown on database error updating e-mail preferences.
$this->serverError(_('Could not update user.')); throw new ServerException(_('Could not update user.'));
} }
$user->query('COMMIT'); $user->query('COMMIT');
Event::handle('EndEmailSaveForm', array($this, $this->scoped)); Event::handle('EndEmailSaveForm', array($this, $this->scoped));
// TRANS: Confirmation message for successful e-mail preferences save.
$this->showForm(_('Email preferences saved.'), true);
} }
// TRANS: Confirmation message for successful e-mail preferences save.
return _('Email preferences saved.');
} }
/** /**
@ -359,38 +324,32 @@ class EmailsettingsAction extends SettingsAction
*/ */
function addAddress() function addAddress()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$email = $this->trimmed('email'); $email = $this->trimmed('email');
// Some validation // Some validation
if (!$email) { if (empty($email)) {
// TRANS: Message given saving e-mail address without having provided one. // TRANS: Message given saving e-mail address without having provided one.
$this->showForm(_('No email address.')); throw new ClientException(_('No email address.'));
return;
} }
$email = common_canonical_email($email); $email = common_canonical_email($email);
if (!$email) { if (empty($email)) {
// TRANS: Message given saving e-mail address that cannot be normalised. // TRANS: Message given saving e-mail address that cannot be normalised.
$this->showForm(_('Cannot normalize that email address.')); throw new ClientException(_('Cannot normalize that email address.'));
return;
} }
if (!Validate::email($email, common_config('email', 'check_domain'))) { if (!Validate::email($email, common_config('email', 'check_domain'))) {
// TRANS: Message given saving e-mail address that not valid. // TRANS: Message given saving e-mail address that not valid.
$this->showForm(_('Not a valid email address.')); throw new ClientException(_('Not a valid email address.'));
return;
} else if ($user->email == $email) { } else if ($user->email == $email) {
// TRANS: Message given saving e-mail address that is already set. // TRANS: Message given saving e-mail address that is already set.
$this->showForm(_('That is already your email address.')); throw new ClientException(_('That is already your email address.'));
return;
} else if ($this->emailExists($email)) { } else if ($this->emailExists($email)) {
// TRANS: Message given saving e-mail address that is already set for another user. // TRANS: Message given saving e-mail address that is already set for another user.
$this->showForm(_('That email address already belongs '. throw new ClientException(_('That email address already belongs to another user.'));
'to another user.'));
return;
} }
if (Event::handle('StartAddEmailAddress', array($user, $email))) { if (Event::handle('StartAddEmailAddress', array($user, $email))) {
@ -399,7 +358,7 @@ class EmailsettingsAction extends SettingsAction
$confirm->address = $email; $confirm->address = $email;
$confirm->address_type = 'email'; $confirm->address_type = 'email';
$confirm->user_id = $user->id; $confirm->user_id = $user->getID();
$confirm->code = common_confirmation_code(64); $confirm->code = common_confirmation_code(64);
$result = $confirm->insert(); $result = $confirm->insert();
@ -407,21 +366,19 @@ class EmailsettingsAction extends SettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($confirm, 'INSERT', __FILE__); common_log_db_error($confirm, 'INSERT', __FILE__);
// TRANS: Server error thrown on database error adding e-mail confirmation code. // TRANS: Server error thrown on database error adding e-mail confirmation code.
$this->serverError(_('Could not insert confirmation code.')); throw new ServerException(_('Could not insert confirmation code.'));
} }
common_debug('Sending confirmation address for user '.$user->id.' to email '.$email); common_debug('Sending confirmation address for user '.$user->getID().' to email '.$email);
mail_confirm_address($user, $confirm->code, $user->nickname, $email); mail_confirm_address($user, $confirm->code, $user->getNickname(), $email);
Event::handle('EndAddEmailAddress', array($user, $email)); Event::handle('EndAddEmailAddress', array($user, $email));
} }
// TRANS: Message given saving valid e-mail address that is to be confirmed. // TRANS: Message given saving valid e-mail address that is to be confirmed.
$msg = _('A confirmation code was sent to the email address you added. '. return _('A confirmation code was sent to the email address you added. '.
'Check your inbox (and spam box!) for the code and instructions '. 'Check your inbox (and spam box!) for the code and instructions '.
'on how to use it.'); 'on how to use it.');
$this->showForm($msg, true);
} }
/** /**
@ -431,31 +388,29 @@ class EmailsettingsAction extends SettingsAction
*/ */
function cancelConfirmation() function cancelConfirmation()
{ {
$email = $this->arg('email'); $email = $this->trimmed('email');
try {
$confirm = $this->getConfirmation(); $confirm = $this->getConfirmation();
if ($confirm->address !== $email) {
if (!$confirm) {
// TRANS: Message given canceling e-mail address confirmation that is not pending.
$this->showForm(_('No pending confirmation to cancel.'));
return;
}
if ($confirm->address != $email) {
// TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address. // TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address.
$this->showForm(_('That is the wrong email address.')); throw new ClientException(_('That is the wrong email address.'));
return; }
} catch (NoResultException $e) {
// TRANS: Message given canceling e-mail address confirmation that is not pending.
throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
} }
$result = $confirm->delete(); $result = $confirm->delete();
if (!$result) { if ($result === false) {
common_log_db_error($confirm, 'DELETE', __FILE__); common_log_db_error($confirm, 'DELETE', __FILE__);
// TRANS: Server error thrown on database error canceling e-mail address confirmation. // TRANS: Server error thrown on database error canceling e-mail address confirmation.
$this->serverError(_('Could not delete email confirmation.')); throw new ServerException(_('Could not delete email confirmation.'));
} }
// TRANS: Message given after successfully canceling e-mail address confirmation. // TRANS: Message given after successfully canceling e-mail address confirmation.
$this->showForm(_('Email confirmation cancelled.'), true); return _('Email confirmation cancelled.');
} }
/** /**
@ -467,26 +422,22 @@ class EmailsettingsAction extends SettingsAction
{ {
$user = common_current_user(); $user = common_current_user();
$email = $this->arg('email'); $email = $this->trimmed('email');
// Maybe an old tab open...? // Maybe an old tab open...?
if ($user->email !== $email) {
if ($user->email != $email) {
// TRANS: Message given trying to remove an e-mail address that is not // TRANS: Message given trying to remove an e-mail address that is not
// TRANS: registered for the active user. // TRANS: registered for the active user.
$this->showForm(_('That is not your email address.')); throw new ClientException(_('That is not your email address.'));
return;
} }
$original = clone($user); $original = clone($user);
$user->email = null; $user->email = null;
// Throws exception on failure. Also performs it within a transaction. // Throws exception on failure. Also performs it within a transaction.
$user->updateWithKeys($original); $user->updateWithKeys($original);
// TRANS: Message given after successfully removing a registered e-mail address. // TRANS: Message given after successfully removing a registered e-mail address.
$this->showForm(_('The email address was removed.'), true); return _('The email address was removed.');
} }
/** /**
@ -498,22 +449,19 @@ class EmailsettingsAction extends SettingsAction
{ {
$user = common_current_user(); $user = common_current_user();
if (!$user->incomingemail) { if (empty($user->incomingemail)) {
// TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set. // TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set.
$this->showForm(_('No incoming email address.')); throw new AlreadyFulfilledException(_('No incoming email address.'));
return;
} }
$orig = clone($user); $orig = clone($user);
$user->incomingemail = null; $user->incomingemail = null;
$user->emailpost = 0; $user->emailpost = 0;
// Throws exception on failure. Also performs it within a transaction. // Throws exception on failure. Also performs it within a transaction.
$user->updateWithKeys($orig); $user->updateWithKeys($orig);
// TRANS: Message given after successfully removing an incoming e-mail address. // TRANS: Message given after successfully removing an incoming e-mail address.
$this->showForm(_('Incoming email address removed.'), true); return _('Incoming email address removed.');
} }
/** /**
@ -524,17 +472,14 @@ class EmailsettingsAction extends SettingsAction
function newIncoming() function newIncoming()
{ {
$user = common_current_user(); $user = common_current_user();
$orig = clone($user); $orig = clone($user);
$user->incomingemail = mail_new_incoming_address(); $user->incomingemail = mail_new_incoming_address();
$user->emailpost = 1; $user->emailpost = 1;
// Throws exception on failure. Also performs it within a transaction. // Throws exception on failure. Also performs it within a transaction.
$user->updateWithKeys($orig); $user->updateWithKeys($orig);
// TRANS: Message given after successfully adding an incoming e-mail address. // TRANS: Message given after successfully adding an incoming e-mail address.
$this->showForm(_('New incoming email address added.'), true); return _('New incoming email address added.');
} }
/** /**
@ -553,10 +498,10 @@ class EmailsettingsAction extends SettingsAction
$other = User::getKV('email', $email); $other = User::getKV('email', $email);
if (!$other) { if (!$other instanceof User) {
return false; return false;
} else { }
return $other->id != $user->id; return $other->id != $user->id;
} }
}
} }

View File

@ -28,12 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/feedlist.php';
/** /**
* Permalink for a group * Permalink for a group
@ -47,53 +42,22 @@ require_once INSTALLDIR.'/lib/feedlist.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class GroupbyidAction extends Action class GroupbyidAction extends ManagedAction
{ {
/** group we're viewing. */ /** group we're viewing. */
var $group = null; protected $group = null;
/**
* Is this page read-only?
*
* @return boolean true
*/
function isReadOnly($args) function isReadOnly($args)
{ {
return true; return true;
} }
function prepare($args) protected function doPreparation()
{ {
parent::prepare($args); $this->group = User_group::getByID($this->arg('id'));
$id = $this->arg('id');
if (!$id) {
// TRANS: Client error displayed referring to a group's permalink without providing a group ID.
$this->clientError(_('No ID.'));
} }
common_debug("Got ID $id"); public function showPage()
$this->group = User_group::getKV('id', $id);
if (!$this->group) {
// TRANS: Client error displayed referring to a group's permalink for a non-existing group ID.
$this->clientError(_('No such group.'), 404);
}
return true;
}
/**
* Handle the request
*
* Shows a profile for the group, some controls, and a list of
* group notices.
*
* @return void
*/
function handle($args)
{ {
common_redirect($this->group->homeUrl(), 303); common_redirect($this->group->homeUrl(), 303);
} }

View File

@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Settings for Jabber/XMPP integration * Settings for Jabber/XMPP integration
@ -118,8 +116,8 @@ class ImsettingsAction extends SettingsAction
// TRANS: Button label to remove a confirmed IM address. // TRANS: Button label to remove a confirmed IM address.
$this->submit('remove', _m('BUTTON','Remove')); $this->submit('remove', _m('BUTTON','Remove'));
} else { } else {
try {
$confirm = $this->getConfirmation($transport); $confirm = $this->getConfirmation($transport);
if ($confirm) {
$this->element('p', 'form_unconfirmed', $confirm->address); $this->element('p', 'form_unconfirmed', $confirm->address);
// TRANS: Form note in IM settings form. // TRANS: Form note in IM settings form.
$this->element('p', 'form_note', $this->element('p', 'form_note',
@ -134,7 +132,7 @@ class ImsettingsAction extends SettingsAction
$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'));
} else { } catch (NoResultException $e) {
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label for IM address. // TRANS: Field label for IM address.
@ -179,8 +177,6 @@ class ImsettingsAction extends SettingsAction
// TRANS: Checkbox label in IM preferences form. // TRANS: Checkbox label in IM preferences form.
array('name'=>'replies', 'description'=>_('Send me replies '. array('name'=>'replies', 'description'=>_('Send me replies '.
'from people I\'m not subscribed to.')), '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) foreach($preferences as $preference)
{ {
@ -211,57 +207,35 @@ class ImsettingsAction extends SettingsAction
*/ */
function getConfirmation($transport) function getConfirmation($transport)
{ {
$user = common_current_user();
$confirm = new Confirm_address(); $confirm = new Confirm_address();
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->address_type = $transport; $confirm->address_type = $transport;
if ($confirm->find(true)) { if ($confirm->find(true)) {
return $confirm; return $confirm;
} else {
return null;
}
} }
/** throw new NoResultException($confirm);
* Handle posts to this form }
*
* Based on the button that was pressed, muxes out to other functions protected function doPost()
* to do the actual task requested.
*
* All sub-functions reload the form with a message -- success or failure.
*
* @return void
*/
function handlePost()
{ {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if ($this->arg('save')) { if ($this->arg('save')) {
$this->savePreferences(); return $this->savePreferences();
} else if ($this->arg('add')) { } else if ($this->arg('add')) {
$this->addAddress(); return $this->addAddress();
} else if ($this->arg('cancel')) { } else if ($this->arg('cancel')) {
$this->cancelConfirmation(); return $this->cancelConfirmation();
} else if ($this->arg('remove')) { } else if ($this->arg('remove')) {
$this->removeAddress(); return $this->removeAddress();
} else {
// TRANS: Message given submitting a form with an unknown action in Instant Messaging settings.
$this->showForm(_('Unexpected form submission.'));
} }
// TRANS: Message given submitting a form with an unknown action in Instant Messaging settings.
throw new ClientException(_('Unexpected form submission.'));
} }
/** /**
* Save user's Jabber preferences * Save user's XMPP preferences
* *
* These are the checkboxes at the bottom of the page. They're used to * These are the checkboxes at the bottom of the page. They're used to
* set different settings * set different settings
@ -270,14 +244,12 @@ class ImsettingsAction extends SettingsAction
*/ */
function savePreferences() function savePreferences()
{ {
$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->query('BEGIN');
$user_im_prefs->user_id = $user->id; $user_im_prefs->user_id = $this->scoped->getID();
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');
do do
{ {
$original = clone($user_im_prefs); $original = clone($user_im_prefs);
@ -289,15 +261,15 @@ class ImsettingsAction extends SettingsAction
$result = $new->update($original); $result = $new->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
// TRANS: Server error thrown on database error updating IM preferences. // TRANS: Server error thrown on database error updating IM preferences.
$this->serverError(_('Could not update IM preferences.')); throw new ServerException(_('Could not update IM preferences.'));
} }
}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); return _('Preferences saved.');
} }
/** /**
@ -310,49 +282,42 @@ class ImsettingsAction extends SettingsAction
*/ */
function addAddress() function addAddress()
{ {
$user = common_current_user();
$screenname = $this->trimmed('screenname'); $screenname = $this->trimmed('screenname');
$transport = $this->trimmed('transport'); $transport = $this->trimmed('transport');
// Some validation // Some validation
if (!$screenname) { if (empty($screenname)) {
// TRANS: Message given saving IM address without having provided one. // TRANS: Message given saving IM address without having provided one.
$this->showForm(_('No screenname.')); throw new ClientException(_('No screenname.'));
return;
} }
if (!$transport) { if (empty($transport)) {
// TRANS: Form validation error when no transport is available setting an IM address. // TRANS: Form validation error when no transport is available setting an IM address.
$this->showForm(_('No transport.')); throw new ClientException(_('No transport.'));
return;
} }
Event::handle('NormalizeImScreenname', array($transport, &$screenname)); Event::handle('NormalizeImScreenname', array($transport, &$screenname));
if (!$screenname) { if (empty($screenname)) {
// TRANS: Message given saving IM address that cannot be normalised. // TRANS: Message given saving IM address that cannot be normalised.
$this->showForm(_('Cannot normalize that screenname.')); throw new ClientException(_('Cannot normalize that screenname.'));
return;
} }
$valid = false; $valid = false;
Event::handle('ValidateImScreenname', array($transport, $screenname, &$valid)); Event::handle('ValidateImScreenname', array($transport, $screenname, &$valid));
if (!$valid) { if (!$valid) {
// TRANS: Message given saving IM address that not valid. // TRANS: Message given saving IM address that not valid.
$this->showForm(_('Not a valid screenname.')); throw new ClientException(_('Not a valid screenname.'));
return;
} else if ($this->screennameExists($transport, $screenname)) { } else if ($this->screennameExists($transport, $screenname)) {
// TRANS: Message given saving IM address that is already set for another user. // TRANS: Message given saving IM address that is already set for another user.
$this->showForm(_('Screenname already belongs to another user.')); throw new ClientException(_('Screenname already belongs to another user.'));
return;
} }
$confirm = new Confirm_address(); $confirm = new Confirm_address();
$confirm->address = $screenname; $confirm->address = $screenname;
$confirm->address_type = $transport; $confirm->address_type = $transport;
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->code = common_confirmation_code(64); $confirm->code = common_confirmation_code(64);
$confirm->sent = common_sql_now(); $confirm->sent = common_sql_now();
$confirm->claimed = common_sql_now(); $confirm->claimed = common_sql_now();
@ -365,13 +330,10 @@ class ImsettingsAction extends SettingsAction
$this->serverError(_('Could not insert confirmation code.')); $this->serverError(_('Could not insert confirmation code.'));
} }
Event::handle('SendImConfirmationCode', array($transport, $screenname, $confirm->code, $user)); Event::handle('SendImConfirmationCode', array($transport, $screenname, $confirm->code, $this->scoped));
// TRANS: Message given saving valid IM address that is to be confirmed. // TRANS: Message given saving valid IM address that is to be confirmed.
$msg = _('A confirmation code was sent '. return _('A confirmation code was sent to the IM address you added.');
'to the IM address you added.');
$this->showForm($msg, true);
} }
/** /**
@ -386,29 +348,27 @@ class ImsettingsAction extends SettingsAction
$screenname = $this->trimmed('screenname'); $screenname = $this->trimmed('screenname');
$transport = $this->trimmed('transport'); $transport = $this->trimmed('transport');
try {
$confirm = $this->getConfirmation($transport); $confirm = $this->getConfirmation($transport);
if (!$confirm) {
// TRANS: Message given canceling Instant Messaging address confirmation that is not pending.
$this->showForm(_('No pending confirmation to cancel.'));
return;
}
if ($confirm->address != $screenname) { if ($confirm->address != $screenname) {
// TRANS: Message given canceling IM address confirmation for the wrong IM address. // TRANS: Message given canceling IM address confirmation for the wrong IM address.
$this->showForm(_('That is the wrong IM address.')); throw new ClientException(_('That is the wrong IM address.'));
return; }
} catch (NoResultException $e) {
// TRANS: Message given canceling Instant Messaging address confirmation that is not pending.
throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
} }
$result = $confirm->delete(); $result = $confirm->delete();
if (!$result) { if ($result === false) {
common_log_db_error($confirm, 'DELETE', __FILE__); common_log_db_error($confirm, 'DELETE', __FILE__);
// TRANS: Server error thrown on database error canceling IM address confirmation. // TRANS: Server error thrown on database error canceling IM address confirmation.
$this->serverError(_('Could not delete confirmation.')); throw new ServerException(_('Could not delete confirmation.'));
} }
// TRANS: Message given after successfully canceling IM address confirmation. // TRANS: Message given after successfully canceling IM address confirmation.
$this->showForm(_('IM confirmation cancelled.'), true); return _('IM confirmation cancelled.');
} }
/** /**
@ -420,34 +380,32 @@ class ImsettingsAction extends SettingsAction
*/ */
function removeAddress() function removeAddress()
{ {
$user = common_current_user();
$screenname = $this->trimmed('screenname'); $screenname = $this->trimmed('screenname');
$transport = $this->trimmed('transport'); $transport = $this->trimmed('transport');
// Maybe an old tab open...? // Maybe an old tab open...?
$user_im_prefs = new User_im_prefs(); $user_im_prefs = new User_im_prefs();
$user_im_prefs->user_id = $user->id; $user_im_prefs->user_id = $this->scoped->getID();
if(! ($user_im_prefs->find() && $user_im_prefs->fetch())) { $user_im_prefs->transport = $transport;
if (!$user_im_prefs->find(true)) {
// TRANS: Message given trying to remove an IM address that is not // TRANS: Message given trying to remove an IM address that is not
// TRANS: registered for the active user. // TRANS: registered for the active user.
$this->showForm(_('That is not your screenname.')); throw new AlreadyFulfilledException(_('There were no preferences stored for this transport.'));
return;
} }
$result = $user_im_prefs->delete(); $result = $user_im_prefs->delete();
if (!$result) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
// TRANS: Server error thrown on database error removing a registered IM address. // TRANS: Server error thrown on database error removing a registered IM address.
$this->serverError(_('Could not update user IM preferences.')); throw new ServerException(_('Could not update user IM preferences.'));
} }
// XXX: unsubscribe to the old address // XXX: unsubscribe to the old address
// TRANS: Message given after successfully removing a registered Instant Messaging address. // TRANS: Message given after successfully removing a registered Instant Messaging address.
$this->showForm(_('The IM address was removed.'), true); return _('The IM address was removed.');
} }
/** /**
@ -463,15 +421,9 @@ class ImsettingsAction extends SettingsAction
function screennameExists($transport, $screenname) function screennameExists($transport, $screenname)
{ {
$user = common_current_user();
$user_im_prefs = new User_im_prefs(); $user_im_prefs = new User_im_prefs();
$user_im_prefs->transport = $transport; $user_im_prefs->transport = $transport;
$user_im_prefs->screenname = $screenname; $user_im_prefs->screenname = $screenname;
if($user_im_prefs->find() && $user_im_prefs->fetch()){ return $user_im_prefs->find(true) ? true : false;
return true;
}else{
return false;
}
} }
} }

View File

@ -41,7 +41,7 @@ if (!defined('GNUSOCIAL')) { exit(1); }
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class NewApplicationAction extends FormAction class NewApplicationAction extends SettingsAction
{ {
function title() function title()
{ {
@ -54,6 +54,7 @@ class NewApplicationAction extends FormAction
if ($this->arg('cancel')) { if ($this->arg('cancel')) {
common_redirect(common_local_url('oauthappssettings'), 303); common_redirect(common_local_url('oauthappssettings'), 303);
} elseif ($this->arg('save')) { } elseif ($this->arg('save')) {
//trySave will never return, just throw exception or redirect
$this->trySave(); $this->trySave();
} }
@ -72,7 +73,7 @@ class NewApplicationAction extends FormAction
return _('Use this form to register a new application.'); return _('Use this form to register a new application.');
} }
private function trySave() protected function trySave()
{ {
$name = $this->trimmed('name'); $name = $this->trimmed('name');
$description = $this->trimmed('description'); $description = $this->trimmed('description');
@ -137,7 +138,7 @@ class NewApplicationAction extends FormAction
$app->query('BEGIN'); $app->query('BEGIN');
$app->name = $name; $app->name = $name;
$app->owner = $this->scoped->id; $app->owner = $this->scoped->getID();
$app->description = $description; $app->description = $description;
$app->source_url = $source_url; $app->source_url = $source_url;
$app->organization = $organization; $app->organization = $organization;

View File

@ -27,11 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR . '/lib/applicationlist.php';
/** /**
* Show a user's registered OAuth applications * Show a user's registered OAuth applications
@ -47,19 +43,11 @@ require_once INSTALLDIR . '/lib/applicationlist.php';
class OauthappssettingsAction extends SettingsAction class OauthappssettingsAction extends SettingsAction
{ {
var $page = 0; protected $page = null;
function prepare($args) protected function doPreparation()
{ {
parent::prepare($args); $this->page = $this->int('page') ?: 1;
$this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
if (!common_logged_in()) {
// TRANS: Message displayed to an anonymous user trying to view OAuth application list.
$this->clientError(_('You must be logged in to list your applications.'));
}
return true;
} }
/** /**
@ -86,21 +74,13 @@ class OauthappssettingsAction extends SettingsAction
return _('Applications you have registered'); return _('Applications you have registered');
} }
/**
* Content area of the page
*
* @return void
*/
function showContent() function showContent()
{ {
$user = common_current_user();
$offset = ($this->page - 1) * APPS_PER_PAGE; $offset = ($this->page - 1) * APPS_PER_PAGE;
$limit = APPS_PER_PAGE + 1; $limit = APPS_PER_PAGE + 1;
$application = new Oauth_application(); $application = new Oauth_application();
$application->owner = $user->id; $application->owner = $this->scoped->getID();
$application->whereAdd("name != 'anonymous'"); $application->whereAdd("name != 'anonymous'");
$application->limit($offset, $limit); $application->limit($offset, $limit);
$application->orderBy('created DESC'); $application->orderBy('created DESC');
@ -109,7 +89,7 @@ class OauthappssettingsAction extends SettingsAction
$cnt = 0; $cnt = 0;
if ($application) { if ($application) {
$al = new ApplicationList($application, $user, $this); $al = new ApplicationList($application, $this->scoped, $this);
$cnt = $al->show(); $cnt = $al->show();
if (0 == $cnt) { if (0 == $cnt) {
$this->showEmptyListMessage(); $this->showEmptyListMessage();
@ -135,34 +115,11 @@ class OauthappssettingsAction extends SettingsAction
function showEmptyListMessage() function showEmptyListMessage()
{ {
// TRANS: Empty list message on page with OAuth applications. // TRANS: Empty list message on page with OAuth applications. Markup allowed
$message = sprintf(_('You have not registered any applications yet.')); $message = sprintf(_('You have not registered any applications yet.'));
$this->elementStart('div', 'guide'); $this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message)); $this->raw(common_markup_to_html($message));
$this->elementEnd('div'); $this->elementEnd('div');
} }
/**
* Handle posts to this form
*
* Based on the button that was pressed, muxes out to other functions
* to do the actual task requested.
*
* All sub-functions reload the form with a message -- success or failure.
*
* @return void
*/
function handlePost()
{
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
}
} }

View File

@ -27,11 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR . '/lib/applicationlist.php';
/** /**
* Show connected OAuth applications * Show connected OAuth applications
@ -47,14 +43,13 @@ require_once INSTALLDIR . '/lib/applicationlist.php';
class OauthconnectionssettingsAction extends SettingsAction class OauthconnectionssettingsAction extends SettingsAction
{ {
var $page = null; var $page = null;
var $oauth_token = null;
function prepare($args) protected $oauth_token = null;
protected function doPreparation()
{ {
parent::prepare($args);
$this->oauth_token = $this->arg('oauth_token'); $this->oauth_token = $this->arg('oauth_token');
$this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; $this->page = $this->int('page') ?: 1;
return true;
} }
/** /**
@ -87,18 +82,15 @@ class OauthconnectionssettingsAction extends SettingsAction
function showContent() function showContent()
{ {
$user = common_current_user();
$profile = $user->getProfile();
$offset = ($this->page - 1) * APPS_PER_PAGE; $offset = ($this->page - 1) * APPS_PER_PAGE;
$limit = APPS_PER_PAGE + 1; $limit = APPS_PER_PAGE + 1;
$connection = $user->getConnectedApps($offset, $limit); $connection = $this->scoped->getConnectedApps($offset, $limit);
$cnt = 0; $cnt = 0;
if (!empty($connection)) { if (!empty($connection)) {
$cal = new ConnectedAppsList($connection, $user, $this); $cal = new ConnectedAppsList($connection, $this->scoped, $this);
$cnt = $cal->show(); $cnt = $cal->show();
} }
@ -111,7 +103,7 @@ class OauthconnectionssettingsAction extends SettingsAction
$cnt > APPS_PER_PAGE, $cnt > APPS_PER_PAGE,
$this->page, $this->page,
'connectionssettings', 'connectionssettings',
array('nickname' => $user->nickname) array('nickname' => $this->scoped->getNickname())
); );
} }
@ -125,24 +117,14 @@ class OauthconnectionssettingsAction extends SettingsAction
* *
* @return void * @return void
*/ */
function handlePost() protected function doPost()
{ {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if ($this->arg('revoke')) { if ($this->arg('revoke')) {
$this->revokeAccess($this->oauth_token); return $this->revokeAccess($this->oauth_token);
} else {
// TRANS: Client error when submitting a form with unexpected information.
$this->clientError(_('Unexpected form submission.'), 401);
} }
// TRANS: Client error when submitting a form with unexpected information.
throw new ClientException(_('Unexpected form submission.'), 401);
} }
/** /**

View File

@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/** /**
* Old-school settings * Old-school settings
@ -77,35 +73,23 @@ class OldschoolsettingsAction extends SettingsAction
* @return boolean true * @return boolean true
*/ */
function prepare($argarray) protected function doPreparation()
{ {
if (!common_config('oldschool', 'enabled')) { if (!common_config('oldschool', 'enabled')) {
throw new ClientException("Old-school settings not enabled."); throw new ClientException("Old-school settings not enabled.");
} }
parent::prepare($argarray);
return true;
} }
/** function doPost()
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handlePost()
{ {
$user = common_current_user(); $osp = Old_school_prefs::getKV('user_id', $this->scoped->getID());
$osp = Old_school_prefs::getKV('user_id', $user->id);
$orig = null; $orig = null;
if (!empty($osp)) { if (!empty($osp)) {
$orig = clone($osp); $orig = clone($osp);
} else { } else {
$osp = new Old_school_prefs(); $osp = new Old_school_prefs();
$osp->user_id = $user->id; $osp->user_id = $this->scoped->getID();
$osp->created = common_sql_now(); $osp->created = common_sql_now();
} }
@ -113,34 +97,25 @@ class OldschoolsettingsAction extends SettingsAction
$osp->stream_nicknames = $this->boolean('stream_nicknames'); $osp->stream_nicknames = $this->boolean('stream_nicknames');
$osp->modified = common_sql_now(); $osp->modified = common_sql_now();
if (!empty($orig)) { if ($orig instanceof Old_school_prefs) {
$osp->update($orig); $osp->update($orig);
} else { } else {
$osp->insert(); $osp->insert();
} }
// TRANS: Confirmation shown when user profile settings are saved. // TRANS: Confirmation shown when user profile settings are saved.
$this->showForm(_('Settings saved.'), true); return _('Settings saved.');
return;
}
function showContent()
{
$user = common_current_user();
$form = new OldSchoolForm($this, $user);
$form->show();
} }
} }
class OldSchoolForm extends Form class OldSchoolSettingsForm extends Form
{ {
var $user; var $user;
function __construct($out, $user) function __construct(Action $out)
{ {
parent::__construct($out); parent::__construct($out);
$this->user = $user; $this->user = $out->getScoped()->getUser();
} }
/** /**

View File

@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('STATUSNET')) { exit(1); }
exit(1);
}
/** /**
* Change password * Change password
@ -77,18 +73,8 @@ class PasswordsettingsAction extends SettingsAction
$this->autofocus('oldpassword'); $this->autofocus('oldpassword');
} }
/**
* Content area of the page
*
* Shows a form for changing the password
*
* @return void
*/
function showContent() function showContent()
{ {
$user = common_current_user();
$this->elementStart('form', array('method' => 'POST', $this->elementStart('form', array('method' => 'POST',
'id' => 'form_password', 'id' => 'form_password',
'class' => 'form_settings', 'class' => 'form_settings',
@ -102,7 +88,7 @@ class PasswordsettingsAction extends SettingsAction
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
// Users who logged in with OpenID won't have a pwd // Users who logged in with OpenID won't have a pwd
if ($user->password) { if ($this->scoped->hasPassword()) {
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label on page where to change password. // TRANS: Field label on page where to change password.
$this->password('oldpassword', _('Old password')); $this->password('oldpassword', _('Old password'));
@ -129,29 +115,8 @@ class PasswordsettingsAction extends SettingsAction
$this->elementEnd('form'); $this->elementEnd('form');
} }
/** protected function doPost()
* Handle a post
*
* Validate input and save changes. Reload the form with a success
* or error message.
*
* @return void
*/
function handlePost()
{ {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
$user = common_current_user();
assert(!is_null($user)); // should already be checked
// FIXME: scrub input // FIXME: scrub input
$newpassword = $this->arg('newpassword'); $newpassword = $this->arg('newpassword');
@ -161,49 +126,44 @@ class PasswordsettingsAction extends SettingsAction
if (strlen($newpassword) < 6) { if (strlen($newpassword) < 6) {
// TRANS: Form validation error on page where to change password. // TRANS: Form validation error on page where to change password.
$this->showForm(_('Password must be 6 or more characters.')); throw new ClientException(_('Password must be 6 or more characters.'));
return;
} else if (0 != strcmp($newpassword, $confirm)) { } else if (0 != strcmp($newpassword, $confirm)) {
// TRANS: Form validation error on password change when password confirmation does not match. // TRANS: Form validation error on password change when password confirmation does not match.
$this->showForm(_('Passwords do not match.')); throw new ClientException(_('Passwords do not match.'));
return;
} }
if ($user->password) { $oldpassword = null;
if ($this->scoped->hasPassword()) {
$oldpassword = $this->arg('oldpassword'); $oldpassword = $this->arg('oldpassword');
if (!common_check_user($user->nickname, $oldpassword)) { if (!common_check_user($this->scoped->getNickname(), $oldpassword)) {
// TRANS: Form validation error on page where to change password. // TRANS: Form validation error on page where to change password.
$this->showForm(_('Incorrect old password.')); throw new ClientException(_('Incorrect old password.'));
return;
} }
}else{
$oldpassword = null;
} }
$success = false; if (Event::handle('StartChangePassword', array($this->scoped, $oldpassword, $newpassword))) {
if(Event::handle('StartChangePassword', array($user, $oldpassword, $newpassword))){
//no handler changed the password, so change the password internally //no handler changed the password, so change the password internally
$user = $this->scoped->getUser();
$original = clone($user); $original = clone($user);
$user->password = common_munge_password($newpassword, $user->id); $user->password = common_munge_password($newpassword, $this->scoped);
$val = $user->validate(); $val = $user->validate();
if ($val !== true) { if ($val !== true) {
// TRANS: Form validation error on page where to change password. // TRANS: Form validation error on page where to change password.
$this->showForm(_('Error saving user; invalid.')); throw new ServerException(_('Error saving user; invalid.'));
return;
} }
if (!$user->update($original)) { if (!$user->update($original)) {
// TRANS: Server error displayed on page where to change password when password change // TRANS: Server error displayed on page where to change password when password change
// TRANS: could not be made because of a server error. // TRANS: could not be made because of a server error.
$this->serverError(_('Cannot save new password.')); throw new ServerException(_('Cannot save new password.'));
} }
Event::handle('EndChangePassword', array($user)); Event::handle('EndChangePassword', array($this->scoped));
} }
// TRANS: Form validation notice on page where to change password. // TRANS: Form validation notice on page where to change password.
$this->showForm(_('Password saved.'), true); return _('Password saved.');
} }
} }

View File

@ -82,7 +82,6 @@ class ProfilesettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
$profile = $this->scoped;
$user = $this->scoped->getUser(); $user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
@ -100,7 +99,7 @@ class ProfilesettingsAction extends SettingsAction
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('nickname', _('Nickname'), $this->input('nickname', _('Nickname'),
$this->arg('nickname') ?: $profile->nickname, $this->trimmed('nickname') ?: $this->scoped->getNickname(),
// TRANS: Tooltip for field label in form for profile settings. // TRANS: Tooltip for field label in form for profile settings.
_('1-64 lowercase letters or numbers, no punctuation or spaces.'), _('1-64 lowercase letters or numbers, no punctuation or spaces.'),
null, false, // "name" (will be set to id), then "required" null, false, // "name" (will be set to id), then "required"
@ -111,12 +110,12 @@ class ProfilesettingsAction extends SettingsAction
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('fullname', _('Full name'), $this->input('fullname', _('Full name'),
($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname); $this->trimmed('fullname') ?: $this->scoped->getFullname());
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('homepage', _('Homepage'), $this->input('homepage', _('Homepage'),
($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage, $this->trimmed('homepage') ?: $this->scoped->getHomepage(),
// TRANS: Tooltip for field label in form for profile settings. // TRANS: Tooltip for field label in form for profile settings.
_('URL of your homepage, blog, or profile on another site.')); _('URL of your homepage, blog, or profile on another site.'));
$this->elementEnd('li'); $this->elementEnd('li');
@ -137,13 +136,13 @@ class ProfilesettingsAction extends SettingsAction
// TRANS: Text area label in form for profile settings where users can provide // TRANS: Text area label in form for profile settings where users can provide
// TRANS: their biography. // TRANS: their biography.
$this->textarea('bio', _('Bio'), $this->textarea('bio', _('Bio'),
($this->arg('bio')) ? $this->arg('bio') : $profile->bio, $this->trimmed('bio') ?: $this->scoped->getDescription(),
$bioInstr); $bioInstr);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('location', _('Location'), $this->input('location', _('Location'),
($this->arg('location')) ? $this->arg('location') : $profile->location, $this->trimmed('location') ?: $this->scoped->location,
// TRANS: Tooltip for field label in form for profile settings. // TRANS: Tooltip for field label in form for profile settings.
_('Where you are, like "City, State (or Region), Country".')); _('Where you are, like "City, State (or Region), Country".'));
$this->elementEnd('li'); $this->elementEnd('li');
@ -152,14 +151,14 @@ class ProfilesettingsAction extends SettingsAction
// TRANS: Checkbox label in form for profile settings. // TRANS: Checkbox label in form for profile settings.
$this->checkbox('sharelocation', _('Share my current location when posting notices'), $this->checkbox('sharelocation', _('Share my current location when posting notices'),
($this->arg('sharelocation')) ? ($this->arg('sharelocation')) ?
$this->arg('sharelocation') : $this->scoped->shareLocation()); $this->boolean('sharelocation') : $this->scoped->shareLocation());
$this->elementEnd('li'); $this->elementEnd('li');
} }
Event::handle('EndProfileFormData', array($this)); Event::handle('EndProfileFormData', array($this));
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('tags', _('Tags'), $this->input('tags', _('Tags'),
($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()), $this->trimmed('tags') ?: implode(' ', Profile_tag::getSelfTagsArray($this->scoped)),
// TRANS: Tooltip for field label in form for profile settings. // TRANS: Tooltip for field label in form for profile settings.
_('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated.')); _('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated.'));
$this->elementEnd('li'); $this->elementEnd('li');
@ -228,17 +227,8 @@ class ProfilesettingsAction extends SettingsAction
* *
* @return void * @return void
*/ */
function handlePost() protected function doPost()
{ {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Form validation error.
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if (Event::handle('StartProfileSaveForm', array($this))) { if (Event::handle('StartProfileSaveForm', array($this))) {
// $nickname will only be set if this changenick value is true. // $nickname will only be set if this changenick value is true.
@ -246,15 +236,13 @@ class ProfilesettingsAction extends SettingsAction
try { try {
$nickname = Nickname::normalize($this->trimmed('nickname'), true); $nickname = Nickname::normalize($this->trimmed('nickname'), true);
} catch (NicknameTakenException $e) { } catch (NicknameTakenException $e) {
// Abort only if the nickname is occupied by another local profile // Abort only if the nickname is occupied by _another_ local user profile
if ($e->profile->id != $this->scoped->id) { if (!$this->scoped->sameAs($e->profile)) {
$this->showForm($e->getMessage()); throw $e;
return;
} }
$nickname = Nickname::normalize($this->trimmed('nickname')); // without in-use check this time // Since the variable wasn't set before the exception was thrown, let's run
} catch (NicknameException $e) { // the normalize sequence again, but without in-use check this time.
$this->showForm($e->getMessage()); $nickname = Nickname::normalize($this->trimmed('nickname'));
return;
} }
} }
@ -273,33 +261,27 @@ class ProfilesettingsAction extends SettingsAction
if (!is_null($homepage) && (strlen($homepage) > 0) && if (!is_null($homepage) && (strlen($homepage) > 0) &&
!common_valid_http_url($homepage)) { !common_valid_http_url($homepage)) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Homepage is not a valid URL.')); throw new ClientException(_('Homepage is not a valid URL.'));
return; } else if (!is_null($fullname) && mb_strlen($fullname) > 191) {
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Full name is too long (maximum 255 characters).')); throw new ClientException(_('Full name is too long (maximum 191 characters).'));
return;
} else if (Profile::bioTooLong($bio)) { } else if (Profile::bioTooLong($bio)) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
// TRANS: Plural form is used based on the maximum number of allowed // TRANS: Plural form is used based on the maximum number of allowed
// TRANS: characters for the biography (%d). // TRANS: characters for the biography (%d).
$this->showForm(sprintf(_m('Bio is too long (maximum %d character).', throw new ClientException(sprintf(_m('Bio is too long (maximum %d character).',
'Bio is too long (maximum %d characters).', 'Bio is too long (maximum %d characters).',
Profile::maxBio()), Profile::maxBio()),
Profile::maxBio())); Profile::maxBio()));
return; } else if (!is_null($location) && mb_strlen($location) > 191) {
} else if (!is_null($location) && mb_strlen($location) > 255) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Location is too long (maximum 255 characters).')); throw new ClientException(_('Location is too long (maximum 191 characters).'));
return;
} else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) { } else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Timezone not selected.')); throw new ClientException(_('Timezone not selected.'));
return;
} else if (!is_null($language) && strlen($language) > 50) { } else if (!is_null($language) && strlen($language) > 50) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Language is too long (maximum 50 characters).')); throw new ClientException(_('Language is too long (maximum 50 characters).'));
return;
} }
$tags = array(); $tags = array();
@ -315,15 +297,14 @@ class ProfilesettingsAction extends SettingsAction
if (!common_valid_profile_tag($tag)) { if (!common_valid_profile_tag($tag)) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
// TRANS: %s is an invalid tag. // TRANS: %s is an invalid tag.
$this->showForm(sprintf(_('Invalid tag: "%s".'), $tag)); throw new ClientException(sprintf(_('Invalid tag: "%s".'), $tag));
return;
} }
$tag_priv[$tag] = $private; $tag_priv[$tag] = $private;
} }
} }
$user = common_current_user(); $user = $this->scoped->getUser();
$user->query('BEGIN'); $user->query('BEGIN');
// $user->nickname is updated through Profile->update(); // $user->nickname is updated through Profile->update();
@ -346,54 +327,53 @@ class ProfilesettingsAction extends SettingsAction
$result = $user->update($original); $result = $user->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error thrown when user profile settings could not be updated to // TRANS: Server error thrown when user profile settings could not be updated to
// TRANS: automatically subscribe to any subscriber. // TRANS: automatically subscribe to any subscriber.
$this->serverError(_('Could not update user for autosubscribe or subscribe_policy.')); throw new ServerException(_('Could not update user for autosubscribe or subscribe_policy.'));
} }
// Re-initialize language environment if it changed // Re-initialize language environment if it changed
common_init_language(); common_init_language();
} }
$profile = $user->getProfile(); $original = clone($this->scoped);
$orig_profile = clone($profile); if (common_config('profile', 'changenick') == true && $this->scoped->getNickname() !== $nickname) {
if (common_config('profile', 'changenick') == true && $profile->nickname !== $nickname) {
assert(Nickname::normalize($nickname)===$nickname); assert(Nickname::normalize($nickname)===$nickname);
common_debug("Changing user nickname from '{$profile->nickname}' to '{$nickname}'."); common_debug("Changing user nickname from '{$this->scoped->getNickname()}' to '{$nickname}'.");
$profile->nickname = $nickname; $this->scoped->nickname = $nickname;
$profile->profileurl = common_profile_url($profile->nickname); $this->scoped->profileurl = common_profile_url($this->scoped->getNickname());
} }
$profile->fullname = $fullname; $this->scoped->fullname = $fullname;
$profile->homepage = $homepage; $this->scoped->homepage = $homepage;
$profile->bio = $bio; $this->scoped->bio = $bio;
$profile->location = $location; $this->scoped->location = $location;
$loc = Location::fromName($location); $loc = Location::fromName($location);
if (empty($loc)) { if (empty($loc)) {
$profile->lat = null; $this->scoped->lat = null;
$profile->lon = null; $this->scoped->lon = null;
$profile->location_id = null; $this->scoped->location_id = null;
$profile->location_ns = null; $this->scoped->location_ns = null;
} else { } else {
$profile->lat = $loc->lat; $this->scoped->lat = $loc->lat;
$profile->lon = $loc->lon; $this->scoped->lon = $loc->lon;
$profile->location_id = $loc->location_id; $this->scoped->location_id = $loc->location_id;
$profile->location_ns = $loc->location_ns; $this->scoped->location_ns = $loc->location_ns;
} }
if (common_config('location', 'share') == 'user') { if (common_config('location', 'share') == 'user') {
$exists = false; $exists = false;
$prefs = User_location_prefs::getKV('user_id', $user->id); $prefs = User_location_prefs::getKV('user_id', $this->scoped->getID());
if (empty($prefs)) { if (empty($prefs)) {
$prefs = new User_location_prefs(); $prefs = new User_location_prefs();
$prefs->user_id = $user->id; $prefs->user_id = $this->scoped->getID();
$prefs->created = common_sql_now(); $prefs->created = common_sql_now();
} else { } else {
$exists = true; $exists = true;
@ -410,42 +390,37 @@ class ProfilesettingsAction extends SettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__); common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error thrown when user profile location preference settings could not be updated. // TRANS: Server error thrown when user profile location preference settings could not be updated.
$this->serverError(_('Could not save location prefs.')); throw new ServerException(_('Could not save location prefs.'));
} }
} }
common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__); common_debug('Old profile: ' . common_log_objstring($original), __FILE__);
common_debug('New profile: ' . common_log_objstring($profile), __FILE__); common_debug('New profile: ' . common_log_objstring($this->scoped), __FILE__);
$result = $profile->update($orig_profile); $result = $this->scoped->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($profile, 'UPDATE', __FILE__); common_log_db_error($this->scoped, 'UPDATE', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error thrown when user profile settings could not be saved. // TRANS: Server error thrown when user profile settings could not be saved.
$this->serverError(_('Could not save profile.')); throw new ServerException(_('Could not save profile.'));
} }
// Set the user tags // Set the user tags
$result = $user->setSelfTags($tags, $tag_priv); $result = Profile_tag::setSelfTags($this->scoped, $tags, $tag_priv);
if (!$result) {
// TRANS: Server error thrown when user profile settings tags could not be saved.
$this->serverError(_('Could not save tags.'));
}
$user->query('COMMIT'); $user->query('COMMIT');
Event::handle('EndProfileSaveForm', array($this)); Event::handle('EndProfileSaveForm', array($this));
// TRANS: Confirmation shown when user profile settings are saved. // TRANS: Confirmation shown when user profile settings are saved.
$this->showForm(_('Settings saved.'), true); return _('Settings saved.');
} }
} }
function showAside() { function showAside() {
$user = common_current_user();
$this->elementStart('div', array('id' => 'aside_primary', $this->elementStart('div', array('id' => 'aside_primary',
'class' => 'aside')); 'class' => 'aside'));
@ -453,7 +428,7 @@ class ProfilesettingsAction extends SettingsAction
'class' => 'section')); 'class' => 'section'));
$this->elementStart('ul'); $this->elementStart('ul');
if (Event::handle('StartProfileSettingsActions', array($this))) { if (Event::handle('StartProfileSettingsActions', array($this))) {
if ($user->hasRight(Right::BACKUPACCOUNT)) { if ($this->scoped->hasRight(Right::BACKUPACCOUNT)) {
$this->elementStart('li'); $this->elementStart('li');
$this->element('a', $this->element('a',
array('href' => common_local_url('backupaccount')), array('href' => common_local_url('backupaccount')),
@ -461,7 +436,7 @@ class ProfilesettingsAction extends SettingsAction
_('Backup account')); _('Backup account'));
$this->elementEnd('li'); $this->elementEnd('li');
} }
if ($user->hasRight(Right::DELETEACCOUNT)) { if ($this->scoped->hasRight(Right::DELETEACCOUNT)) {
$this->elementStart('li'); $this->elementStart('li');
$this->element('a', $this->element('a',
array('href' => common_local_url('deleteaccount')), array('href' => common_local_url('deleteaccount')),
@ -469,7 +444,7 @@ class ProfilesettingsAction extends SettingsAction
_('Delete account')); _('Delete account'));
$this->elementEnd('li'); $this->elementEnd('li');
} }
if ($user->hasRight(Right::RESTOREACCOUNT)) { if ($this->scoped->hasRight(Right::RESTOREACCOUNT)) {
$this->elementStart('li'); $this->elementStart('li');
$this->element('a', $this->element('a',
array('href' => common_local_url('restoreaccount')), array('href' => common_local_url('restoreaccount')),

View File

@ -325,7 +325,7 @@ class RecoverpasswordAction extends Action
$original = clone($user); $original = clone($user);
$user->password = common_munge_password($newpassword, $user->id); $user->password = common_munge_password($newpassword, $user->getProfile());
if (!$user->update($original)) { if (!$user->update($original)) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);

View File

@ -222,25 +222,12 @@ class ShownoticeAction extends ManagedAction
/** /**
* Extra <head> content * Extra <head> content
* *
* We show the microid(s) for the author, if any. * Facebook OpenGraph metadata.
* *
* @return void * @return void
*/ */
function extraHead() function extraHead()
{ {
$user = User::getKV($this->profile->id);
if (!$user instanceof User) {
return;
}
if ($user->emailmicroid && $user->email && $this->notice->uri) {
$id = new Microid('mailto:'. $user->email,
$this->notice->uri);
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
}
// Extras to aid in sharing notices to Facebook // Extras to aid in sharing notices to Facebook
$avatarUrl = $this->profile->avatarUrl(AVATAR_PROFILE_SIZE); $avatarUrl = $this->profile->avatarUrl(AVATAR_PROFILE_SIZE);
$this->element('meta', array('property' => 'og:image', $this->element('meta', array('property' => 'og:image',

View File

@ -47,38 +47,6 @@ if (!defined('GNUSOCIAL')) { exit(1); }
*/ */
class ShowstreamAction extends NoticestreamAction class ShowstreamAction extends NoticestreamAction
{ {
protected $target = null;
protected function doPreparation()
{
// showstream requires a nickname
$nickname_arg = $this->trimmed('nickname');
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
if ($this->arg('page') && $this->arg('page') != 1) {
$args['page'] = $this->arg['page'];
}
common_redirect(common_local_url($this->getActionName(), $args), 301);
}
try {
$user = User::getByNickname($nickname);
} catch (NoSuchUserException $e) {
$group = Local_group::getKV('nickname', $nickname);
if ($group instanceof Local_group) {
common_redirect($group->getProfile()->getUrl());
}
// No user nor group found, throw the NoSuchUserException again
throw $e;
}
$this->target = $user->getProfile();
}
public function getStream() public function getStream()
{ {
if (empty($this->tag)) { if (empty($this->tag)) {
@ -195,13 +163,6 @@ class ShowstreamAction extends NoticestreamAction
'content' => $this->target->getDescription())); 'content' => $this->target->getDescription()));
} }
if ($this->target->isLocal() && $this->target->getUser()->emailmicroid && $this->target->getUser()->email && $this->target->getUrl()) {
$id = new Microid('mailto:'.$this->target->getUser()->email,
$this->selfUrl());
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
}
// See https://wiki.mozilla.org/Microsummaries // See https://wiki.mozilla.org/Microsummaries
$this->element('link', array('rel' => 'microsummary', $this->element('link', array('rel' => 'microsummary',

View File

@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Settings for SMS * Settings for SMS
@ -45,6 +43,14 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
class SmssettingsAction extends SettingsAction class SmssettingsAction extends SettingsAction
{ {
protected function doPreparation()
{
if (!common_config('sms', 'enabled')) {
// TRANS: Message given in the SMS settings if SMS is not enabled on the site.
throw new ServerException(_('SMS is not available.'));
}
}
/** /**
* Title of the page * Title of the page
* *
@ -86,14 +92,7 @@ class SmssettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
if (!common_config('sms', 'enabled')) { $user = $this->scoped->getUser();
$this->element('div', array('class' => 'error'),
// TRANS: Message given in the SMS settings if SMS is not enabled on the site.
_('SMS is not available.'));
return;
}
$user = common_current_user();
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_sms', 'id' => 'form_settings_sms',
@ -118,8 +117,8 @@ class SmssettingsAction extends SettingsAction
// TRANS: Button label to remove a confirmed SMS address. // TRANS: Button label to remove a confirmed SMS address.
$this->submit('remove', _m('BUTTON','Remove')); $this->submit('remove', _m('BUTTON','Remove'));
} else { } else {
try {
$confirm = $this->getConfirmation(); $confirm = $this->getConfirmation();
if ($confirm) {
$carrier = Sms_carrier::getKV($confirm->address_extra); $carrier = Sms_carrier::getKV($confirm->address_extra);
$this->element('p', 'form_unconfirmed', $this->element('p', 'form_unconfirmed',
$confirm->address . ' (' . $carrier->name . ')'); $confirm->address . ' (' . $carrier->name . ')');
@ -141,7 +140,7 @@ class SmssettingsAction extends SettingsAction
$this->elementEnd('ul'); $this->elementEnd('ul');
// TRANS: Button label to confirm SMS confirmation code in SMS settings. // TRANS: Button label to confirm SMS confirmation code in SMS settings.
$this->submit('confirm', _m('BUTTON','Confirm')); $this->submit('confirm', _m('BUTTON','Confirm'));
} else { } catch (NoResultException $e) {
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label for SMS phone number input in SMS settings form. // TRANS: Field label for SMS phone number input in SMS settings form.
@ -216,60 +215,38 @@ class SmssettingsAction extends SettingsAction
*/ */
function getConfirmation() function getConfirmation()
{ {
$user = common_current_user();
$confirm = new Confirm_address(); $confirm = new Confirm_address();
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->address_type = 'sms'; $confirm->address_type = 'sms';
if ($confirm->find(true)) { if ($confirm->find(true)) {
return $confirm; return $confirm;
} else {
return null;
}
} }
/** throw new NoResultException($confirm);
* Handle posts to this form }
*
* Based on the button that was pressed, muxes out to other functions protected function doPost()
* to do the actual task requested.
*
* All sub-functions reload the form with a message -- success or failure.
*
* @return void
*/
function handlePost()
{ {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if ($this->arg('save')) { if ($this->arg('save')) {
$this->savePreferences(); return $this->savePreferences();
} else if ($this->arg('add')) { } else if ($this->arg('add')) {
$this->addAddress(); return $this->addAddress();
} else if ($this->arg('cancel')) { } else if ($this->arg('cancel')) {
$this->cancelConfirmation(); return $this->cancelConfirmation();
} else if ($this->arg('remove')) { } else if ($this->arg('remove')) {
$this->removeAddress(); return $this->removeAddress();
} else if ($this->arg('removeincoming')) { } else if ($this->arg('removeincoming')) {
$this->removeIncoming(); return $this->removeIncoming();
} else if ($this->arg('newincoming')) { } else if ($this->arg('newincoming')) {
$this->newIncoming(); return $this->newIncoming();
} else if ($this->arg('confirm')) { } else if ($this->arg('confirm')) {
$this->confirmCode(); return $this->confirmCode();
} else {
// TRANS: Message given submitting a form with an unknown action in SMS settings.
$this->showForm(_('Unexpected form submission.'));
} }
// TRANS: Message given submitting a form with an unknown action in SMS settings.
throw new ClientException(_('Unexpected form submission.'));
} }
/** /**
@ -281,30 +258,26 @@ class SmssettingsAction extends SettingsAction
*/ */
function savePreferences() function savePreferences()
{ {
$smsnotify = $this->boolean('smsnotify'); $user = $this->scoped->getUser();
$user = common_current_user();
assert(!is_null($user)); // should already be checked
$user->query('BEGIN'); $user->query('BEGIN');
$original = clone($user); $original = clone($user);
$user->smsnotify = $smsnotify; $user->smsnotify = $this->boolean('smsnotify');
$result = $user->update($original); $result = $user->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
// TRANS: Server error thrown on database error updating SMS preferences. // TRANS: Server error thrown on database error updating SMS preferences.
$this->serverError(_('Could not update user.')); throw new ServerException(_('Could not update user.'));
} }
$user->query('COMMIT'); $user->query('COMMIT');
// TRANS: Confirmation message for successful SMS preferences save. // TRANS: Confirmation message for successful SMS preferences save.
$this->showForm(_('SMS preferences saved.'), true); return _('SMS preferences saved.');
} }
/** /**
@ -324,28 +297,24 @@ class SmssettingsAction extends SettingsAction
// Some validation // Some validation
if (!$sms) { if (empty($sms)) {
// TRANS: Message given saving SMS phone number without having provided one. // TRANS: Message given saving SMS phone number without having provided one.
$this->showForm(_('No phone number.')); throw new ClientException(_('No phone number.'));
return;
} }
if (!$carrier_id) { if (empty($carrier_id)) {
// TRANS: Message given saving SMS phone number without having selected a carrier. // TRANS: Message given saving SMS phone number without having selected a carrier.
$this->showForm(_('No carrier selected.')); throw new ClientException(_('No carrier selected.'));
return;
} }
$sms = common_canonical_sms($sms); $sms = common_canonical_sms($sms);
if ($user->sms == $sms) { if ($user->sms === $sms) {
// TRANS: Message given saving SMS phone number that is already set. // TRANS: Message given saving SMS phone number that is already set.
$this->showForm(_('That is already your phone number.')); throw new AlreadyFulfilledException(_('That is already your phone number.'));
return;
} else if ($this->smsExists($sms)) { } else if ($this->smsExists($sms)) {
// TRANS: Message given saving SMS phone number that is already set for another user. // TRANS: Message given saving SMS phone number that is already set for another user.
$this->showForm(_('That phone number already belongs to another user.')); throw new ClientException(_('That phone number already belongs to another user.'));
return;
} }
$confirm = new Confirm_address(); $confirm = new Confirm_address();
@ -353,7 +322,7 @@ class SmssettingsAction extends SettingsAction
$confirm->address = $sms; $confirm->address = $sms;
$confirm->address_extra = $carrier_id; $confirm->address_extra = $carrier_id;
$confirm->address_type = 'sms'; $confirm->address_type = 'sms';
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->code = common_confirmation_code(40); $confirm->code = common_confirmation_code(40);
$result = $confirm->insert(); $result = $confirm->insert();
@ -371,11 +340,9 @@ class SmssettingsAction extends SettingsAction
$carrier->toEmailAddress($sms)); $carrier->toEmailAddress($sms));
// TRANS: Message given saving valid SMS phone number that is to be confirmed. // TRANS: Message given saving valid SMS phone number that is to be confirmed.
$msg = _('A confirmation code was sent to the phone number you added. '. return _('A confirmation code was sent to the phone number you added. '.
'Check your phone for the code and instructions '. 'Check your phone for the code and instructions '.
'on how to use it.'); 'on how to use it.');
$this->showForm($msg, true);
} }
/** /**
@ -390,29 +357,27 @@ class SmssettingsAction extends SettingsAction
$sms = $this->trimmed('sms'); $sms = $this->trimmed('sms');
$carrier = $this->trimmed('carrier'); $carrier = $this->trimmed('carrier');
try {
$confirm = $this->getConfirmation(); $confirm = $this->getConfirmation();
if (!$confirm) {
// TRANS: Message given canceling SMS phone number confirmation that is not pending.
$this->showForm(_('No pending confirmation to cancel.'));
return;
}
if ($confirm->address != $sms) { if ($confirm->address != $sms) {
// TRANS: Message given canceling SMS phone number confirmation for the wrong phone number. // TRANS: Message given canceling SMS phone number confirmation for the wrong phone number.
$this->showForm(_('That is the wrong confirmation number.')); throw new ClientException(_('That is the wrong confirmation number.'));
return; }
} catch (NoResultException $e) {
// TRANS: Message given canceling SMS phone number confirmation that is not pending.
throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
} }
$result = $confirm->delete(); $result = $confirm->delete();
if (!$result) { if ($result === false) {
common_log_db_error($confirm, 'DELETE', __FILE__); common_log_db_error($confirm, 'DELETE', __FILE__);
// TRANS: Server error thrown on database error canceling SMS phone number confirmation. // TRANS: Server error thrown on database error canceling SMS phone number confirmation.
$this->serverError(_('Could not delete SMS confirmation.')); throw new ServerException(_('Could not delete SMS confirmation.'));
} }
// TRANS: Message given after successfully canceling SMS phone number confirmation. // TRANS: Message given after successfully canceling SMS phone number confirmation.
$this->showForm(_('SMS confirmation cancelled.'), true); return _('SMS confirmation cancelled.');
} }
/** /**
@ -422,7 +387,7 @@ class SmssettingsAction extends SettingsAction
*/ */
function removeAddress() function removeAddress()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$sms = $this->arg('sms'); $sms = $this->arg('sms');
$carrier = $this->arg('carrier'); $carrier = $this->arg('carrier');
@ -432,8 +397,7 @@ class SmssettingsAction extends SettingsAction
if ($user->sms != $sms) { if ($user->sms != $sms) {
// TRANS: Message given trying to remove an SMS phone number that is not // TRANS: Message given trying to remove an SMS phone number that is not
// TRANS: registered for the active user. // TRANS: registered for the active user.
$this->showForm(_('That is not your phone number.')); throw new ClientException(_('That is not your phone number.'));
return;
} }
$original = clone($user); $original = clone($user);
@ -446,7 +410,7 @@ class SmssettingsAction extends SettingsAction
$user->updateWithKeys($original); $user->updateWithKeys($original);
// TRANS: Message given after successfully removing a registered SMS phone number. // TRANS: Message given after successfully removing a registered SMS phone number.
$this->showForm(_('The SMS phone number was removed.'), true); return _('The SMS phone number was removed.');
} }
/** /**
@ -460,15 +424,13 @@ class SmssettingsAction extends SettingsAction
*/ */
function smsExists($sms) function smsExists($sms)
{ {
$user = common_current_user();
$other = User::getKV('sms', $sms); $other = User::getKV('sms', $sms);
if (!$other) { if (!$other instanceof User) {
return false; return false;
} else {
return $other->id != $user->id;
} }
return !$this->scoped->sameAs($other->getProfile());
} }
/** /**
@ -519,15 +481,12 @@ class SmssettingsAction extends SettingsAction
{ {
$code = $this->trimmed('code'); $code = $this->trimmed('code');
if (!$code) { if (empty($code)) {
// TRANS: Message given saving SMS phone number confirmation code without having provided one. // TRANS: Message given saving SMS phone number confirmation code without having provided one.
$this->showForm(_('No code entered.')); throw new ClientException(_('No code entered.'));
return;
} }
common_redirect(common_local_url('confirmaddress', common_redirect(common_local_url('confirmaddress', array('code' => $code)), 303);
array('code' => $code)),
303);
} }
/** /**
@ -541,8 +500,7 @@ class SmssettingsAction extends SettingsAction
if (!$user->incomingemail) { if (!$user->incomingemail) {
// TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set. // TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set.
$this->showForm(_('No incoming email address.')); throw new ClientException(_('No incoming email address.'));
return;
} }
$orig = clone($user); $orig = clone($user);
@ -553,7 +511,7 @@ class SmssettingsAction extends SettingsAction
$user->updateWithKeys($orig); $user->updateWithKeys($orig);
// TRANS: Confirmation text after updating SMS settings. // TRANS: Confirmation text after updating SMS settings.
$this->showForm(_('Incoming email address removed.'), true); return _('Incoming email address removed.');
} }
/** /**
@ -565,7 +523,7 @@ class SmssettingsAction extends SettingsAction
*/ */
function newIncoming() function newIncoming()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$orig = clone($user); $orig = clone($user);
@ -575,6 +533,6 @@ class SmssettingsAction extends SettingsAction
$user->updateWithKeys($orig); $user->updateWithKeys($orig);
// TRANS: Confirmation text after updating SMS settings. // TRANS: Confirmation text after updating SMS settings.
$this->showForm(_('New incoming email address added.'), true); return _('New incoming email address added.');
} }
} }

View File

@ -28,9 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Miscellaneous settings actions * Miscellaneous settings actions
@ -83,7 +81,7 @@ class UrlsettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_other', 'id' => 'form_settings_other',
@ -154,31 +152,13 @@ class UrlsettingsAction extends SettingsAction
$this->elementEnd('form'); $this->elementEnd('form');
} }
/** protected function doPost()
* Handle a post
*
* Saves the changes to url-shortening prefs and shows a success or failure
* message.
*
* @return void
*/
function handlePost()
{ {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
$urlshorteningservice = $this->trimmed('urlshorteningservice'); $urlshorteningservice = $this->trimmed('urlshorteningservice');
if (!is_null($urlshorteningservice) && strlen($urlshorteningservice) > 50) { if (!is_null($urlshorteningservice) && strlen($urlshorteningservice) > 50) {
// TRANS: Form validation error for form "Other settings" in user profile. // TRANS: Form validation error for form "Other settings" in user profile.
$this->showForm(_('URL shortening service is too long (maximum 50 characters).')); throw new ClientException(_('URL shortening service is too long (maximum 50 characters).'));
return;
} }
$maxurllength = $this->trimmed('maxurllength'); $maxurllength = $this->trimmed('maxurllength');
@ -195,9 +175,7 @@ class UrlsettingsAction extends SettingsAction
throw new ClientException(_('Invalid number for maximum notice length.')); throw new ClientException(_('Invalid number for maximum notice length.'));
} }
$user = common_current_user(); $user = $this->scoped->getUser();
assert(!is_null($user)); // should already be checked
$user->query('BEGIN'); $user->query('BEGIN');
@ -209,14 +187,15 @@ class UrlsettingsAction extends SettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error displayed when "Other" settings in user profile could not be updated on the server. // TRANS: Server error displayed when "Other" settings in user profile could not be updated on the server.
$this->serverError(_('Could not update user.')); throw new ServerException(_('Could not update user.'));
} }
$prefs = User_urlshortener_prefs::getPrefs($user); $prefs = User_urlshortener_prefs::getPrefs($user);
$orig = null; $orig = null;
if (empty($prefs)) { if (!$prefs instanceof User_urlshortener_prefs) {
$prefs = new User_urlshortener_prefs(); $prefs = new User_urlshortener_prefs();
$prefs->user_id = $user->id; $prefs->user_id = $user->id;
@ -229,13 +208,14 @@ class UrlsettingsAction extends SettingsAction
$prefs->maxurllength = $maxurllength; $prefs->maxurllength = $maxurllength;
$prefs->maxnoticelength = $maxnoticelength; $prefs->maxnoticelength = $maxnoticelength;
if (!empty($orig)) { if ($orig instanceof User_urlshortener_prefs) {
$result = $prefs->update($orig); $result = $prefs->update($orig);
} else { } else {
$result = $prefs->insert(); $result = $prefs->insert();
} }
if (!$result) { if ($result === null) {
$user->query('ROLLBACK');
// TRANS: Server exception thrown in profile URL settings when preferences could not be saved. // TRANS: Server exception thrown in profile URL settings when preferences could not be saved.
throw new ServerException(_('Error saving user URL shortening preferences.')); throw new ServerException(_('Error saving user URL shortening preferences.'));
} }
@ -243,6 +223,6 @@ class UrlsettingsAction extends SettingsAction
$user->query('COMMIT'); $user->query('COMMIT');
// TRANS: Confirmation message after saving preferences. // TRANS: Confirmation message after saving preferences.
$this->showForm(_('Preferences saved.'), true); return _('Preferences saved.');
} }
} }

View File

@ -27,18 +27,20 @@ class UserrssAction extends TargetedRss10Action
protected function doStreamPreparation() protected function doStreamPreparation()
{ {
parent::doStreamPreparation();
$this->tag = $this->trimmed('tag'); $this->tag = $this->trimmed('tag');
} }
protected function getNotices() protected function getNotices()
{ {
if (!empty($this->tag)) { if (!empty($this->tag)) {
$stream = $this->target->getTaggedNotices($this->tag, 0, $this->limit); $stream = $this->getTarget()->getTaggedNotices($this->tag, 0, $this->limit);
return $stream->fetchAll(); return $stream->fetchAll();
} }
// otherwise we fetch a normal user stream // otherwise we fetch a normal user stream
$stream = $this->target->getNotices(0, $this->limit); $stream = $this->getTarget()->getNotices(0, $this->limit);
return $stream->fetchAll(); return $stream->fetchAll();
} }

View File

@ -56,34 +56,37 @@ class Foreign_link extends Managed_DataObject
static function getByUserID($user_id, $service) static function getByUserID($user_id, $service)
{ {
if (empty($user_id) || empty($service)) { if (empty($user_id) || empty($service)) {
return null; throw new ServerException('Empty user_id or service for Foreign_link::getByUserID');
} }
$flink = new Foreign_link(); $flink = new Foreign_link();
$flink->service = $service; $flink->service = $service;
$flink->user_id = $user_id; $flink->user_id = $user_id;
$flink->limit(1); $flink->limit(1);
$result = $flink->find(true); if (!$flink->find(true)) {
throw new NoResultException($flink);
}
return empty($result) ? null : $flink; return $flink;
} }
static function getByForeignID($foreign_id, $service) static function getByForeignID($foreign_id, $service)
{ {
if (empty($foreign_id) || empty($service)) { if (empty($foreign_id) || empty($service)) {
return null; throw new ServerException('Empty foreign_id or service for Foreign_link::getByForeignID');
} else { }
$flink = new Foreign_link(); $flink = new Foreign_link();
$flink->service = $service; $flink->service = $service;
$flink->foreign_id = $foreign_id; $flink->foreign_id = $foreign_id;
$flink->limit(1); $flink->limit(1);
$result = $flink->find(true); if (!$flink->find(true)) {
throw new NoResultException($flink);
return empty($result) ? null : $flink;
} }
return $flink;
} }
function set_flags($noticesend, $noticerecv, $replysync, $friendsync) function set_flags($noticesend, $noticerecv, $replysync, $friendsync)
@ -124,21 +127,21 @@ class Foreign_link extends Managed_DataObject
$fuser->limit(1); $fuser->limit(1);
if ($fuser->find(true)) { if (!$fuser->find(true)) {
return $fuser; throw new NoResultException($fuser);
} }
return null; return $fuser;
} }
function getUser() function getUser()
{ {
return User::getKV($this->user_id); return Profile::getByID($this->user_id)->getUser();
} }
function getProfile() function getProfile()
{ {
return Profile::getKV('id', $this->user_id); return Profile::getByID($this->user_id);
} }
// Make sure we only ever delete one record at a time // Make sure we only ever delete one record at a time

View File

@ -41,33 +41,39 @@ class Foreign_user extends Managed_DataObject
); );
} }
static function getForeignUser($id, $service) { static function getForeignUser($id, $service)
{
if (empty($id) || empty($service)) {
throw new ServerException('Empty foreign user id or service for Foreign_user::getForeignUser');
}
$fuser = new Foreign_user(); $fuser = new Foreign_user();
$fuser->id = $id; $fuser->id = $id;
$fuser->service = $service; $fuser->service = $service;
$fuser->limit(1); $fuser->limit(1);
$result = $fuser->find(true); if (!$fuser->find(true)) {
throw new NoResultException($fuser);
}
return empty($result) ? null : $fuser; return $fuser;
} }
static function getByNickname($nickname, $service) static function getByNickname($nickname, $service)
{ {
if (empty($nickname) || empty($service)) { if (empty($nickname) || empty($service)) {
return null; throw new ServerException('Empty nickname or service for Foreign_user::getByNickname');
} else { }
$fuser = new Foreign_user(); $fuser = new Foreign_user();
$fuser->service = $service; $fuser->service = $service;
$fuser->nickname = $nickname; $fuser->nickname = $nickname;
$fuser->limit(1); $fuser->limit(1);
$result = $fuser->find(true); if (!$fuser->find(true)) {
throw new NoResultException($fuser);
return empty($result) ? null : $fuser;
} }
return $fuser;
} }
} }

View File

@ -74,7 +74,7 @@ class Memcached_DataObject extends Safe_DataObject
{ {
$obj = new $cls; $obj = new $cls;
// php-compatible, for settype(), datatype // PHP compatible datatype for settype() below
$colType = $obj->columnType($keyCol); $colType = $obj->columnType($keyCol);
if (!in_array($colType, array('integer', 'int'))) { if (!in_array($colType, array('integer', 'int'))) {

View File

@ -523,18 +523,13 @@ class Notice extends Managed_DataObject
// Handle repeat case // Handle repeat case
if (isset($repeat_of)) { if (!empty($options['repeat_of'])) {
// Check for a private one // Check for a private one
$repeat = Notice::getKV('id', $repeat_of); $repeat = Notice::getByID($options['repeat_of']);
if (!($repeat instanceof Notice)) { if ($profile->sameAs($repeat->getProfile())) {
// TRANS: Client exception thrown in notice when trying to repeat a missing or deleted notice.
throw new ClientException(_('Cannot repeat; original notice is missing or deleted.'));
}
if ($profile->id == $repeat->profile_id) {
// TRANS: Client error displayed when trying to repeat an own notice. // TRANS: Client error displayed when trying to repeat an own notice.
throw new ClientException(_('You cannot repeat your own notice.')); throw new ClientException(_('You cannot repeat your own notice.'));
} }
@ -610,12 +605,13 @@ class Notice extends Managed_DataObject
if (empty($notice->conversation) and !empty($options['conversation'])) { if (empty($notice->conversation) and !empty($options['conversation'])) {
$conv = Conversation::getKV('uri', $options['conversation']); $conv = Conversation::getKV('uri', $options['conversation']);
if ($conv instanceof Conversation) { if ($conv instanceof Conversation) {
common_debug('Conversation stitched together from (probably) reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').'); common_debug('Conversation stitched together from (probably) a reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
$notice->conversation = $conv->id; $notice->conversation = $conv->id;
} else { } else {
// Conversation URI was not found, so we must create it. But we can't create it // Conversation URI was not found, so we must create it. But we can't create it
// until we have a Notice ID because of the database layout... // until we have a Notice ID because of the database layout...
$notice->tmp_conv_uri = $options['conversation']; // $options['conversation'] will be used later after the $notice->insert();
common_debug('Conversation URI not found, so we have to create it after inserting this Notice: '.$options['conversation']);
} }
} else { } else {
// If we're not using the attached conversation URI let's remove it // If we're not using the attached conversation URI let's remove it
@ -677,6 +673,7 @@ class Notice extends Managed_DataObject
if (empty($notice->conversation)) { if (empty($notice->conversation)) {
$orig = clone($notice); $orig = clone($notice);
// $act->context->conversation will be null if it was not provided // $act->context->conversation will be null if it was not provided
$conv = Conversation::create($notice, $options['conversation']); $conv = Conversation::create($notice, $options['conversation']);
$notice->conversation = $conv->id; $notice->conversation = $conv->id;
$notice->update($orig); $notice->update($orig);
@ -853,6 +850,22 @@ class Notice extends Managed_DataObject
if (is_null($scope)) { if (is_null($scope)) {
$scope = $reply->scope; $scope = $reply->scope;
} }
} else {
// If we don't know the reply, we might know the conversation!
// This will happen if a known remote user replies to an
// unknown remote user - within a known conversation.
if (empty($stored->conversation) and !empty($act->context->conversation)) {
$conv = Conversation::getKV('uri', $act->context->conversation);
if ($conv instanceof Conversation) {
common_debug('Conversation stitched together from (probably) a reply activity to unknown remote user. Activity creation time ('.$stored->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
$stored->conversation = $conv->id;
} else {
// Conversation URI was not found, so we must create it. But we can't create it
// until we have a Notice ID because of the database layout...
// $options['conversation'] will be used later after the $stored->insert();
common_debug('Conversation URI from activity context not found, so we have to create it after inserting this Notice: '.$act->context->conversation);
}
}
} }
if ($act->context instanceof ActivityContext) { if ($act->context instanceof ActivityContext) {
@ -898,10 +911,11 @@ class Notice extends Managed_DataObject
throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString()); throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString());
} }
// If it's not part of a conversation, it's // If it's not part of a conversation, it's the beginning
// the beginning of a new conversation. // of a new one (or belongs to a previously unknown URI).
if (empty($stored->conversation)) { if (empty($stored->conversation)) {
// $act->context->conversation will be null if it was not provided // $act->context->conversation will be null if it was not provided
common_debug('Creating a new conversation for stored notice ID='.$stored->getID().' with URI: '.$act->context->conversation);
$conv = Conversation::create($stored, $act->context->conversation); $conv = Conversation::create($stored, $act->context->conversation);
$stored->conversation = $conv->id; $stored->conversation = $conv->id;
} }
@ -1656,32 +1670,22 @@ class Notice extends Managed_DataObject
protected $_replies = array(); protected $_replies = array();
/** /**
* Pull the complete list of @-reply targets for this notice. * Pull the complete list of @-mentioned profile IDs for this notice.
* *
* @return array of integer profile ids * @return array of integer profile ids
*/ */
function getReplies() function getReplies()
{ {
if (isset($this->_replies[$this->id])) { if (!isset($this->_replies[$this->getID()])) {
return $this->_replies[$this->id]; $mentions = Reply::multiGet('notice_id', array($this->getID()));
$this->_replies[$this->getID()] = $mentions->fetchAll('profile_id');
} }
return $this->_replies[$this->getID()];
$replyMap = Reply::listGet('notice_id', array($this->id));
$ids = array();
foreach ($replyMap[$this->id] as $reply) {
$ids[] = $reply->profile_id;
}
$this->_replies[$this->id] = $ids;
return $ids;
} }
function _setReplies($replies) function _setReplies($replies)
{ {
$this->_replies[$this->id] = $replies; $this->_replies[$this->getID()] = $replies;
} }
/** /**

View File

@ -138,6 +138,18 @@ class Profile extends Managed_DataObject
return true; return true;
} }
// Returns false if the user has no password (which will always
// be the case for remote users). This can be the case for OpenID
// logins or other mechanisms which don't store a password hash.
public function hasPassword()
{
try {
return !empty($this->getUser()->hasPassword());
} catch (NoSuchUserException $e) {
return false;
}
}
public function getObjectType() public function getObjectType()
{ {
// FIXME: More types... like peopletags and whatever // FIXME: More types... like peopletags and whatever
@ -865,6 +877,11 @@ class Profile extends Managed_DataObject
function delete($useWhere=false) function delete($useWhere=false)
{ {
// just in case it hadn't been done before... (usually set before adding deluser to queue handling!)
if (!$this->hasRole(Profile_role::DELETED)) {
$this->grantRole(Profile_role::DELETED);
}
$this->_deleteNotices(); $this->_deleteNotices();
$this->_deleteSubscriptions(); $this->_deleteSubscriptions();
$this->_deleteTags(); $this->_deleteTags();
@ -1395,6 +1412,11 @@ class Profile extends Managed_DataObject
return $this->fullname; return $this->fullname;
} }
public function getHomepage()
{
return $this->homepage;
}
public function getDescription() public function getDescription()
{ {
return $this->bio; return $this->bio;
@ -1623,4 +1645,9 @@ class Profile extends Managed_DataObject
public function setPref($namespace, $topic, $data) { public function setPref($namespace, $topic, $data) {
return Profile_prefs::setData($this, $namespace, $topic, $data); return Profile_prefs::setData($this, $namespace, $topic, $data);
} }
public function getConnectedApps($offset=0, $limit=null)
{
return $this->getUser()->getConnectedApps($offset, $limit);
}
} }

View File

@ -2,22 +2,15 @@
/** /**
* Table Definition for profile_tag * Table Definition for profile_tag
*/ */
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Profile_tag extends Managed_DataObject class Profile_tag extends Managed_DataObject
{ {
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'profile_tag'; // table name public $__table = 'profile_tag'; // table name
public $tagger; // int(4) primary_key not_null public $tagger; // int(4) primary_key not_null
public $tagged; // int(4) primary_key not_null public $tagged; // int(4) primary_key not_null
public $tag; // varchar(64) primary_key not_null public $tag; // varchar(64) primary_key not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
public static function schemaDef() public static function schemaDef()
{ {
return array( return array(
@ -52,6 +45,16 @@ class Profile_tag extends Managed_DataObject
return Profile_list::pkeyGet(array('tagger' => $this->tagger, 'tag' => $this->tag)); return Profile_list::pkeyGet(array('tagger' => $this->tagger, 'tag' => $this->tag));
} }
static function getSelfTagsArray(Profile $target)
{
return self::getTagsArray($target->getID(), $target->getID(), $target);
}
static function setSelfTags(Profile $target, array $newtags, array $privacy=array())
{
return self::setTags($target->getID(), $target->getID(), $newtags, $privacy);
}
static function getTags($tagger, $tagged, $auth_user=null) { static function getTags($tagger, $tagged, $auth_user=null) {
$profile_list = new Profile_list(); $profile_list = new Profile_list();
@ -88,7 +91,7 @@ class Profile_tag extends Managed_DataObject
return $profile_list; return $profile_list;
} }
static function getTagsArray($tagger, $tagged, $auth_user_id=null) static function getTagsArray($tagger, $tagged, Profile $scoped=null)
{ {
$ptag = new Profile_tag(); $ptag = new Profile_tag();
@ -100,7 +103,7 @@ class Profile_tag extends Managed_DataObject
'and profile_tag.tagged = %d ', 'and profile_tag.tagged = %d ',
$tagger, $tagged); $tagger, $tagged);
if ($auth_user_id != $tagger) { if (!$scoped instanceof Profile || $scoped->getID() !== $tagger) {
$qry .= 'and profile_list.private = 0'; $qry .= 'and profile_list.private = 0';
} }
@ -115,10 +118,10 @@ class Profile_tag extends Managed_DataObject
return $tags; return $tags;
} }
static function setTags($tagger, $tagged, $newtags, $privacy=array()) { static function setTags($tagger, $tagged, array $newtags, array $privacy=array()) {
$newtags = array_unique($newtags); $newtags = array_unique($newtags);
$oldtags = self::getTagsArray($tagger, $tagged, $tagger); $oldtags = self::getTagsArray($tagger, $tagged, Profile::getByID($tagger));
$ptag = new Profile_tag(); $ptag = new Profile_tag();
@ -149,19 +152,18 @@ class Profile_tag extends Managed_DataObject
'tag' => $tag)); 'tag' => $tag));
# if tag already exists, return it # if tag already exists, return it
if(!empty($ptag)) { if ($ptag instanceof Profile_tag) {
return $ptag; return $ptag;
} }
$tagger_profile = Profile::getKV('id', $tagger); $tagger_profile = Profile::getByID($tagger);
$tagged_profile = Profile::getKV('id', $tagged); $tagged_profile = Profile::getByID($tagged);
if (Event::handle('StartTagProfile', array($tagger_profile, $tagged_profile, $tag))) { if (Event::handle('StartTagProfile', array($tagger_profile, $tagged_profile, $tag))) {
if (!$tagger_profile->canTag($tagged_profile)) { if (!$tagger_profile->canTag($tagged_profile)) {
// TRANS: Client exception thrown trying to set a tag for a user that cannot be tagged. // TRANS: Client exception thrown trying to set a tag for a user that cannot be tagged.
throw new ClientException(_('You cannot tag this user.')); throw new ClientException(_('You cannot tag this user.'));
return false;
} }
$tags = new Profile_list(); $tags = new Profile_list();
@ -174,7 +176,6 @@ class Profile_tag extends Managed_DataObject
'which is the maximum allowed number of tags. ' . 'which is the maximum allowed number of tags. ' .
'Try using or deleting some existing tags.'), 'Try using or deleting some existing tags.'),
common_config('peopletag', 'maxtags'))); common_config('peopletag', 'maxtags')));
return false;
} }
$plist = new Profile_list(); $plist = new Profile_list();
@ -188,7 +189,6 @@ class Profile_tag extends Managed_DataObject
'which is the maximum allowed number. ' . 'which is the maximum allowed number. ' .
'Try unlisting others first.'), 'Try unlisting others first.'),
common_config('peopletag', 'maxpeople'), $tag)); common_config('peopletag', 'maxpeople'), $tag));
return false;
} }
$newtag = new Profile_tag(); $newtag = new Profile_tag();
@ -199,9 +199,9 @@ class Profile_tag extends Managed_DataObject
$result = $newtag->insert(); $result = $newtag->insert();
if (!$result) { if (!$result) {
common_log_db_error($newtag, 'INSERT', __FILE__); common_log_db_error($newtag, 'INSERT', __FILE__);
$plist->query('ROLLBACK');
return false; return false;
} }
@ -212,7 +212,6 @@ class Profile_tag extends Managed_DataObject
$newtag->delete(); $newtag->delete();
$profile_list->delete(); $profile_list->delete();
throw $e; throw $e;
return false;
} }
$profile_list->taggedCount(true); $profile_list->taggedCount(true);
@ -233,12 +232,11 @@ class Profile_tag extends Managed_DataObject
if (Event::handle('StartUntagProfile', array($ptag))) { if (Event::handle('StartUntagProfile', array($ptag))) {
$orig = clone($ptag); $orig = clone($ptag);
$result = $ptag->delete(); $result = $ptag->delete();
if (!$result) { if ($result === false) {
common_log_db_error($this, 'DELETE', __FILE__); common_log_db_error($this, 'DELETE', __FILE__);
return false; return false;
} }
Event::handle('EndUntagProfile', array($orig)); Event::handle('EndUntagProfile', array($orig));
if ($result) {
$profile_list = Profile_list::pkeyGet(array('tag' => $tag, 'tagger' => $tagger)); $profile_list = Profile_list::pkeyGet(array('tag' => $tag, 'tagger' => $tagger));
if (!empty($profile_list)) { if (!empty($profile_list)) {
$profile_list->taggedCount(true); $profile_list->taggedCount(true);
@ -246,8 +244,6 @@ class Profile_tag extends Managed_DataObject
self::blowCaches($tagger, $tagged); self::blowCaches($tagger, $tagged);
return true; return true;
} }
return false;
}
} }
// @fixme: move this to Profile_list? // @fixme: move this to Profile_list?

View File

@ -40,7 +40,7 @@ class Queue_item extends Managed_DataObject
* @param mixed $transports name of a single queue or array of queues to pull from * @param mixed $transports name of a single queue or array of queues to pull from
* If not specified, checks all queues in the system. * If not specified, checks all queues in the system.
*/ */
static function top($transports=null) { static function top($transports=null, array $ignored_transports=array()) {
$qi = new Queue_item(); $qi = new Queue_item();
if ($transports) { if ($transports) {
@ -52,6 +52,11 @@ class Queue_item extends Managed_DataObject
$qi->transport = $transports; $qi->transport = $transports;
} }
} }
if (!empty($ignored_transports)) {
// @fixme use safer escaping
$list = implode("','", array_map(array($qi, 'escape'), $ignored_transports));
$qi->whereAdd("transport NOT IN ('$list')");
}
$qi->orderBy('created'); $qi->orderBy('created');
$qi->whereAdd('claimed is null'); $qi->whereAdd('claimed is null');

View File

@ -42,7 +42,6 @@ class User extends Managed_DataObject
public $emailnotifynudge; // tinyint(1) default_1 public $emailnotifynudge; // tinyint(1) default_1
public $emailnotifymsg; // tinyint(1) default_1 public $emailnotifymsg; // tinyint(1) default_1
public $emailnotifyattn; // tinyint(1) default_1 public $emailnotifyattn; // tinyint(1) default_1
public $emailmicroid; // tinyint(1) default_1
public $language; // varchar(50) public $language; // varchar(50)
public $timezone; // varchar(50) public $timezone; // varchar(50)
public $emailpost; // tinyint(1) default_1 public $emailpost; // tinyint(1) default_1
@ -77,7 +76,6 @@ class User extends Managed_DataObject
'emailnotifynudge' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of nudges'), 'emailnotifynudge' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of nudges'),
'emailnotifymsg' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of direct messages'), 'emailnotifymsg' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of direct messages'),
'emailnotifyattn' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of @-replies'), 'emailnotifyattn' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of @-replies'),
'emailmicroid' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'whether to publish email microid'),
'language' => array('type' => 'varchar', 'length' => 50, 'description' => 'preferred language'), 'language' => array('type' => 'varchar', 'length' => 50, 'description' => 'preferred language'),
'timezone' => array('type' => 'varchar', 'length' => 50, 'description' => 'timezone'), 'timezone' => array('type' => 'varchar', 'length' => 50, 'description' => 'timezone'),
'emailpost' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Post by email'), 'emailpost' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Post by email'),
@ -276,9 +274,7 @@ class User extends Managed_DataObject
$user->emailnotifynudge = 1; $user->emailnotifynudge = 1;
$user->emailnotifymsg = 1; $user->emailnotifymsg = 1;
$user->emailnotifyattn = 1; $user->emailnotifyattn = 1;
$user->emailmicroid = 1;
$user->emailpost = 1; $user->emailpost = 1;
$user->jabbermicroid = 1;
$user->created = common_sql_now(); $user->created = common_sql_now();
@ -303,7 +299,7 @@ class User extends Managed_DataObject
} }
if (!empty($password)) { // may not have a password for OpenID users if (!empty($password)) { // may not have a password for OpenID users
$user->password = common_munge_password($password, $id); $user->password = common_munge_password($password);
} }
$result = $user->insert(); $result = $user->insert();
@ -466,16 +462,6 @@ class User extends Managed_DataObject
return $this->getProfile()->getNotices($offset, $limit, $since_id, $before_id); return $this->getProfile()->getNotices($offset, $limit, $since_id, $before_id);
} }
function getSelfTags()
{
return Profile_tag::getTagsArray($this->id, $this->id, $this->id);
}
function setSelfTags($newtags, $privacy)
{
return Profile_tag::setTags($this->id, $this->id, $newtags, $privacy);
}
function block(Profile $other) function block(Profile $other)
{ {
// Add a new block record // Add a new block record
@ -612,8 +598,10 @@ class User extends Managed_DataObject
} }
try { try {
if (!$this->hasRole(Profile_role::DELETED)) {
$profile = $this->getProfile(); $profile = $this->getProfile();
$profile->delete(); $profile->delete();
}
} catch (UserNoProfileException $unp) { } catch (UserNoProfileException $unp) {
common_log(LOG_INFO, "User {$this->nickname} has no profile; continuing deletion."); common_log(LOG_INFO, "User {$this->nickname} has no profile; continuing deletion.");
} }
@ -1019,6 +1007,11 @@ class User extends Managed_DataObject
return $this->getProfile()->isPrivateStream(); return $this->getProfile()->isPrivateStream();
} }
public function hasPassword()
{
return !empty($this->password);
}
public function delPref($namespace, $topic) public function delPref($namespace, $topic)
{ {
return $this->getProfile()->delPref($namespace, $topic); return $this->getProfile()->delPref($namespace, $topic);

View File

@ -40,7 +40,6 @@ class User_im_prefs extends Managed_DataObject
public $transport; // varchar(191) not_null not 255 because utf8mb4 takes more space public $transport; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $notify; // tinyint(1) public $notify; // tinyint(1)
public $replies; // tinyint(1) public $replies; // tinyint(1)
public $microid; // tinyint(1)
public $updatefrompresence; // tinyint(1) public $updatefrompresence; // tinyint(1)
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00 public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -57,7 +56,6 @@ class User_im_prefs extends Managed_DataObject
'transport' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'), 'transport' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'),
'notify' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Notify when a new notice is sent'), 'notify' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Notify when a new notice is sent'),
'replies' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Send replies from people not subscribed to'), 'replies' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Send replies from people not subscribed to'),
'microid' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'Publish a MicroID'),
'updatefrompresence' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Send replies from people not subscribed to.'), 'updatefrompresence' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Send replies from people not subscribed to.'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),

View File

@ -93,7 +93,17 @@ function Auth_OpenID_pct_encoded_replace_unreserved($mo)
function Auth_OpenID_pct_encoded_replace($mo) function Auth_OpenID_pct_encoded_replace($mo)
{ {
return chr(intval($mo[1], 16)); $code = intval($mo[1], 16);
// Prevent request splitting by ignoring newline and space characters
if($code === 0xA || $code === 0xD || $code === ord(' '))
{
return $mo[0];
}
else
{
return chr($code);
}
} }
function Auth_OpenID_remove_dot_segments($path) function Auth_OpenID_remove_dot_segments($path)

View File

@ -127,9 +127,9 @@ function mfNamesFromClass($class, $prefix='h-') {
$matches = array(); $matches = array();
foreach ($classes as $classname) { foreach ($classes as $classname) {
$compare_classname = strtolower(' ' . $classname); $compare_classname = ' ' . $classname;
$compare_prefix = strtolower(' ' . $prefix); $compare_prefix = ' ' . $prefix;
if (stristr($compare_classname, $compare_prefix) !== false && ($compare_classname != $compare_prefix)) { if (strstr($compare_classname, $compare_prefix) !== false && ($compare_classname != $compare_prefix)) {
$matches[] = ($prefix === 'h-') ? $classname : substr($classname, strlen($prefix)); $matches[] = ($prefix === 'h-') ? $classname : substr($classname, strlen($prefix));
} }
} }
@ -153,13 +153,18 @@ function nestedMfPropertyNamesFromClass($class) {
$class = str_replace(array(' ', ' ', "\n"), ' ', $class); $class = str_replace(array(' ', ' ', "\n"), ' ', $class);
foreach (explode(' ', $class) as $classname) { foreach (explode(' ', $class) as $classname) {
foreach ($prefixes as $prefix) { foreach ($prefixes as $prefix) {
$compare_classname = strtolower(' ' . $classname); // Check if $classname is a valid property classname for $prefix.
if (stristr($compare_classname, $prefix) && ($compare_classname != $prefix)) { if (mb_substr($classname, 0, mb_strlen($prefix)) == $prefix && $classname != $prefix) {
$propertyNames = array_merge($propertyNames, mfNamesFromClass($classname, ltrim($prefix))); $propertyName = mb_substr($classname, mb_strlen($prefix));
$propertyNames[$propertyName][] = $prefix;
} }
} }
} }
foreach ($propertyNames as $property => $prefixes) {
$propertyNames[$property] = array_unique($prefixes);
}
return $propertyNames; return $propertyNames;
} }
@ -192,28 +197,27 @@ function convertTimeFormat($time) {
$hh = $mm = $ss = ''; $hh = $mm = $ss = '';
preg_match('/(\d{1,2}):?(\d{2})?:?(\d{2})?(a\.?m\.?|p\.?m\.?)?/i', $time, $matches); preg_match('/(\d{1,2}):?(\d{2})?:?(\d{2})?(a\.?m\.?|p\.?m\.?)?/i', $time, $matches);
// if no am/pm specified // If no am/pm is specified:
if (empty($matches[4])) { if (empty($matches[4])) {
return $time; return $time;
} } else {
// else am/pm specified // Otherwise, am/pm is specified.
else {
$meridiem = strtolower(str_replace('.', '', $matches[4])); $meridiem = strtolower(str_replace('.', '', $matches[4]));
// hours // Hours.
$hh = $matches[1]; $hh = $matches[1];
// add 12 to the pm hours // Add 12 to hours if pm applies.
if ($meridiem == 'pm' && ($hh < 12)) { if ($meridiem == 'pm' && ($hh < 12)) {
$hh += 12; $hh += 12;
} }
$hh = str_pad($hh, 2, '0', STR_PAD_LEFT); $hh = str_pad($hh, 2, '0', STR_PAD_LEFT);
// minutes // Minutes.
$mm = (empty($matches[2]) ) ? '00' : $matches[2]; $mm = (empty($matches[2]) ) ? '00' : $matches[2];
// seconds, only if supplied // Seconds, only if supplied.
if (!empty($matches[3])) { if (!empty($matches[3])) {
$ss = $matches[3]; $ss = $matches[3];
} }
@ -443,7 +447,7 @@ class Parser {
public function parseU(\DOMElement $u) { public function parseU(\DOMElement $u) {
if (($u->tagName == 'a' or $u->tagName == 'area') and $u->getAttribute('href') !== null) { if (($u->tagName == 'a' or $u->tagName == 'area') and $u->getAttribute('href') !== null) {
$uValue = $u->getAttribute('href'); $uValue = $u->getAttribute('href');
} elseif ($u->tagName == 'img' and $u->getAttribute('src') !== null) { } elseif (in_array($u->tagName, array('img', 'audio', 'video', 'source')) and $u->getAttribute('src') !== null) {
$uValue = $u->getAttribute('src'); $uValue = $u->getAttribute('src');
} elseif ($u->tagName == 'object' and $u->getAttribute('data') !== null) { } elseif ($u->tagName == 'object' and $u->getAttribute('data') !== null) {
$uValue = $u->getAttribute('data'); $uValue = $u->getAttribute('data');
@ -591,16 +595,16 @@ class Parser {
$dtValue = $dt->nodeValue; $dtValue = $dt->nodeValue;
} }
if ( preg_match('/(\d{4}-\d{2}-\d{2})/', $dtValue, $matches) ) { if (preg_match('/(\d{4}-\d{2}-\d{2})/', $dtValue, $matches)) {
$dates[] = $matches[0]; $dates[] = $matches[0];
} }
} }
/** /**
* if $dtValue is only a time and there are recently parsed dates, * if $dtValue is only a time and there are recently parsed dates,
* form the full date-time using the most recnetly parsed dt- value * form the full date-time using the most recently parsed dt- value
*/ */
if ( (preg_match('/^\d{1,2}:\d{1,2}(Z?[+|-]\d{2}:?\d{2})?/', $dtValue) or preg_match('/^\d{1,2}[a|p]m/', $dtValue)) && !empty($dates) ) { if ((preg_match('/^\d{1,2}:\d{1,2}(Z?[+|-]\d{2}:?\d{2})?/', $dtValue) or preg_match('/^\d{1,2}[a|p]m/', $dtValue)) && !empty($dates)) {
$dtValue = convertTimeFormat($dtValue); $dtValue = convertTimeFormat($dtValue);
$dtValue = end($dates) . 'T' . unicodeTrim($dtValue, 'T'); $dtValue = end($dates) . 'T' . unicodeTrim($dtValue, 'T');
} }
@ -665,6 +669,8 @@ class Parser {
if (null === $result) if (null === $result)
continue; continue;
// In most cases, the value attribute of the nested microformat should be the p- parsed value of the elemnt.
// The only times this is different is when the microformat is nested under certain prefixes, which are handled below.
$result['value'] = $this->parseP($subMF); $result['value'] = $this->parseP($subMF);
// Does this µf have any property names other than h-*? // Does this µf have any property names other than h-*?
@ -672,8 +678,19 @@ class Parser {
if (!empty($properties)) { if (!empty($properties)) {
// Yes! Its a nested property µf // Yes! Its a nested property µf
foreach ($properties as $property) { foreach ($properties as $property => $prefixes) {
$return[$property][] = $result; // Note: handling microformat nesting under multiple conflicting prefixes is not currently specified by the mf2 parsing spec.
$prefixSpecificResult = $result;
if (in_array('p-', $prefixes)) {
$prefixSpecificResult['value'] = $prefixSpecificResult['properties']['name'][0];
} elseif (in_array('e-', $prefixes)) {
$eParsedResult = $this->parseE($subMF);
$prefixSpecificResult['html'] = $eParsedResult['html'];
$prefixSpecificResult['value'] = $eParsedResult['value'];
} elseif (in_array('u-', $prefixes)) {
$prefixSpecificResult['value'] = $this->parseU($subMF);
}
$return[$property][] = $prefixSpecificResult;
} }
} else { } else {
// No, its a child µf // No, its a child µf
@ -689,6 +706,11 @@ class Parser {
$this->elementPrefixParsed($subMF, 'e'); $this->elementPrefixParsed($subMF, 'e');
} }
if($e->tagName == 'area') {
$coords = $e->getAttribute('coords');
$shape = $e->getAttribute('shape');
}
// Handle p-* // Handle p-*
foreach ($this->xpath->query('.//*[contains(concat(" ", @class) ," p-")]', $e) as $p) { foreach ($this->xpath->query('.//*[contains(concat(" ", @class) ," p-")]', $e) as $p) {
if ($this->isElementParsed($p, 'p')) if ($this->isElementParsed($p, 'p'))
@ -762,7 +784,7 @@ class Parser {
if (!array_key_exists('name', $return)) { if (!array_key_exists('name', $return)) {
try { try {
// Look for img @alt // Look for img @alt
if ($e->tagName == 'img' and $e->getAttribute('alt') != '') if (($e->tagName == 'img' or $e->tagName == 'area') and $e->getAttribute('alt') != '')
throw new Exception($e->getAttribute('alt')); throw new Exception($e->getAttribute('alt'));
if ($e->tagName == 'abbr' and $e->hasAttribute('title')) if ($e->tagName == 'abbr' and $e->hasAttribute('title'))
@ -770,15 +792,36 @@ class Parser {
// Look for nested img @alt // Look for nested img @alt
foreach ($this->xpath->query('./img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) { foreach ($this->xpath->query('./img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
if ($em->getAttribute('alt') != '') $emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt')); throw new Exception($em->getAttribute('alt'));
} }
}
// Look for nested area @alt
foreach ($this->xpath->query('./area[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
$emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt'));
}
}
// Look for double nested img @alt // Look for double nested img @alt
foreach ($this->xpath->query('./*[count(preceding-sibling::*)+count(following-sibling::*)=0]/img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) { foreach ($this->xpath->query('./*[count(preceding-sibling::*)+count(following-sibling::*)=0]/img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
if ($em->getAttribute('alt') != '') $emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt')); throw new Exception($em->getAttribute('alt'));
} }
}
// Look for double nested img @alt
foreach ($this->xpath->query('./*[count(preceding-sibling::*)+count(following-sibling::*)=0]/area[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
$emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt'));
}
}
throw new Exception($e->nodeValue); throw new Exception($e->nodeValue);
} catch (Exception $exc) { } catch (Exception $exc) {
@ -812,14 +855,26 @@ class Parser {
// Check for u-url // Check for u-url
if (!array_key_exists('url', $return)) { if (!array_key_exists('url', $return)) {
// Look for img @src // Look for img @src
if ($e->tagName == 'a') if ($e->tagName == 'a' or $e->tagName == 'area')
$url = $e->getAttribute('href'); $url = $e->getAttribute('href');
// Look for nested img @src // Look for nested a @href
foreach ($this->xpath->query('./a[count(preceding-sibling::a)+count(following-sibling::a)=0]', $e) as $em) { foreach ($this->xpath->query('./a[count(preceding-sibling::a)+count(following-sibling::a)=0]', $e) as $em) {
$emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames)) {
$url = $em->getAttribute('href'); $url = $em->getAttribute('href');
break; break;
} }
}
// Look for nested area @src
foreach ($this->xpath->query('./area[count(preceding-sibling::area)+count(following-sibling::area)=0]', $e) as $em) {
$emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames)) {
$url = $em->getAttribute('href');
break;
}
}
if (!empty($url)) if (!empty($url))
$return['url'][] = $this->resolveUrl($url); $return['url'][] = $this->resolveUrl($url);
@ -833,8 +888,18 @@ class Parser {
'type' => $mfTypes, 'type' => $mfTypes,
'properties' => $return 'properties' => $return
); );
if (!empty($children))
if (!empty($shape)) {
$parsed['shape'] = $shape;
}
if (!empty($coords)) {
$parsed['coords'] = $coords;
}
if (!empty($children)) {
$parsed['children'] = array_values(array_filter($children)); $parsed['children'] = array_values(array_filter($children));
}
return $parsed; return $parsed;
} }
@ -873,6 +938,15 @@ class Parser {
if ($hyperlink->hasAttribute('hreflang')) if ($hyperlink->hasAttribute('hreflang'))
$alt['hreflang'] = $hyperlink->getAttribute('hreflang'); $alt['hreflang'] = $hyperlink->getAttribute('hreflang');
if ($hyperlink->hasAttribute('title'))
$alt['title'] = $hyperlink->getAttribute('title');
if ($hyperlink->hasAttribute('type'))
$alt['type'] = $hyperlink->getAttribute('type');
if ($hyperlink->nodeValue)
$alt['text'] = $hyperlink->nodeValue;
$alternates[] = $alt; $alternates[] = $alt;
} else { } else {
foreach ($linkRels as $rel) { foreach ($linkRels as $rel) {
@ -1013,7 +1087,7 @@ class Parser {
'hentry' => 'h-entry', 'hentry' => 'h-entry',
'hrecipe' => 'h-recipe', 'hrecipe' => 'h-recipe',
'hresume' => 'h-resume', 'hresume' => 'h-resume',
'hevent' => 'h-event', 'vevent' => 'h-event',
'hreview' => 'h-review', 'hreview' => 'h-review',
'hproduct' => 'h-product' 'hproduct' => 'h-product'
); );
@ -1084,7 +1158,7 @@ class Parser {
'skill' => 'p-skill', 'skill' => 'p-skill',
'affiliation' => 'p-affiliation h-card', 'affiliation' => 'p-affiliation h-card',
), ),
'hevent' => array( 'vevent' => array(
'dtstart' => 'dt-start', 'dtstart' => 'dt-start',
'dtend' => 'dt-end', 'dtend' => 'dt-end',
'duration' => 'dt-duration', 'duration' => 'dt-duration',

View File

@ -26,20 +26,20 @@ require_once 'OAuth.php';
*/ */
class ApiGNUsocialOAuthDataStore extends OAuthDataStore class ApiGNUsocialOAuthDataStore extends OAuthDataStore
{ {
function lookup_consumer($consumerKey) function lookup_consumer($consumer_key)
{ {
$con = Consumer::getKV('consumer_key', $consumerKey); $con = Consumer::getKV('consumer_key', $consumer_key);
if (!$con instanceof Consumer) { if (!$con instanceof Consumer) {
// Create an anon consumer and anon application if one // Create an anon consumer and anon application if one
// doesn't exist already // doesn't exist already
if ($consumerKey == 'anonymous') { if ($consumer_key == 'anonymous') {
common_debug("API OAuth - creating anonymous consumer"); common_debug("API OAuth - creating anonymous consumer");
$con = new Consumer(); $con = new Consumer();
$con->consumer_key = $consumerKey; $con->consumer_key = $consumer_key;
$con->consumer_secret = $consumerKey; $con->consumer_secret = $consumer_key;
$con->created = common_sql_now(); $con->created = common_sql_now();
$result = $con->insert(); $result = $con->insert();
@ -388,7 +388,7 @@ class ApiGNUsocialOAuthDataStore extends OAuthDataStore
* *
* @return OAuthToken $token a new unauthorized OAuth request token * @return OAuthToken $token a new unauthorized OAuth request token
*/ */
function new_request_token($consumer, $callback) function new_request_token($consumer, $callback = null)
{ {
$t = new Token(); $t = new Token();
$t->consumer_key = $consumer->key; $t->consumer_key = $consumer->key;
@ -473,13 +473,13 @@ class ApiGNUsocialOAuthDataStore extends OAuthDataStore
* @param type $token_key * @param type $token_key
* @return OAuthToken * @return OAuthToken
*/ */
function lookup_token($consumer, $token_type, $token_key) function lookup_token($consumer, $token_type, $token)
{ {
$t = new Token(); $t = new Token();
if (!is_null($consumer)) { if (!is_null($consumer)) {
$t->consumer_key = $consumer->key; $t->consumer_key = $consumer->key;
} }
$t->tok = $token_key; $t->tok = $token;
$t->type = ($token_type == 'access') ? 1 : 0; $t->type = ($token_type == 'access') ? 1 : 0;
if ($t->find(true)) { if ($t->find(true)) {
return new OAuthToken($t->tok, $t->secret); return new OAuthToken($t->tok, $t->secret);

View File

@ -27,13 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR . '/lib/widget.php';
define('APPS_PER_PAGE', 20);
/** /**
* Widget to show a list of OAuth applications * Widget to show a list of OAuth applications
@ -52,16 +46,12 @@ class ApplicationList extends Widget
/** Owner of this list */ /** Owner of this list */
var $owner = null; var $owner = null;
/** Action object using us. */ function __construct($application, Profile $owner, Action $out=null)
var $action = null;
function __construct($application, $owner=null, $action=null)
{ {
parent::__construct($action); parent::__construct($out);
$this->application = $application; $this->application = $application;
$this->owner = $owner; $this->owner = $owner;
$this->action = $action;
} }
function show() function show()
@ -75,7 +65,7 @@ class ApplicationList extends Widget
if($cnt > APPS_PER_PAGE) { if($cnt > APPS_PER_PAGE) {
break; break;
} }
$this->showapplication(); $this->showApplication();
} }
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
@ -85,8 +75,6 @@ class ApplicationList extends Widget
function showApplication() function showApplication()
{ {
$user = common_current_user();
$this->out->elementStart('li', array('class' => 'application h-entry', $this->out->elementStart('li', array('class' => 'application h-entry',
'id' => 'oauthclient-' . $this->application->id)); 'id' => 'oauthclient-' . $this->application->id));
@ -119,140 +107,3 @@ class ApplicationList extends Widget
return; return;
} }
} }
/**
* Widget to show a list of connected OAuth clients
*
* @category Application
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ConnectedAppsList extends Widget
{
/** Current connected application query */
var $connection = null;
/** Owner of this list */
var $owner = null;
/** Action object using us. */
var $action = null;
function __construct($connection, $owner=null, $action=null)
{
parent::__construct($action);
common_debug("ConnectedAppsList constructor");
$this->connection = $connection;
$this->owner = $owner;
$this->action = $action;
}
/* Override this in subclasses. */
function showOwnerControls()
{
return;
}
function show()
{
$this->out->elementStart('ul', 'applications');
$cnt = 0;
while ($this->connection->fetch()) {
$cnt++;
if($cnt > APPS_PER_PAGE) {
break;
}
$this->showConnection();
}
$this->out->elementEnd('ul');
return $cnt;
}
function showConnection()
{
$app = Oauth_application::getKV('id', $this->connection->application_id);
$this->out->elementStart('li', array('class' => 'application h-entry',
'id' => 'oauthclient-' . $app->id));
$this->out->elementStart('a', array('href' => $app->source_url,
'class' => 'h-card p-name'));
if (!empty($app->icon)) {
$this->out->element('img', array('src' => $app->icon,
'class' => 'avatar u-photo'));
}
if ($app->name != 'anonymous') {
$this->out->text($app->name);
} else {
// TRANS: Name for an anonymous application in application list.
$this->out->element('span', 'p-name', _('Unknown application'));
}
$this->out->elementEnd('a');
if ($app->name != 'anonymous') {
// @todo FIXME: i18n trouble.
// TRANS: Message has a leading space and a trailing space. Used in application list.
// TRANS: Before this message the application name is put, behind it the organisation that manages it.
$this->out->raw(_(' by '));
$this->out->element('a', array('href' => $app->homepage,
'class' => 'h-card'),
$app->organization);
}
// TRANS: Application access type
$readWriteText = _('read-write');
// TRANS: Application access type
$readOnlyText = _('read-only');
$access = ($this->connection->access_type & Oauth_application::$writeAccess)
? $readWriteText : $readOnlyText;
$modifiedDate = common_date_string($this->connection->modified);
// TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only")
$txt = sprintf(_('Approved %1$s - "%2$s" access.'), $modifiedDate, $access);
// @todo FIXME: i18n trouble.
$this->out->raw(" - $txt");
if (!empty($app->description)) {
$this->out->element(
'p', array('class' => 'application_description'),
$app->description
);
}
$this->out->element(
'p', array(
'class' => 'access_token'),
// TRANS: Access token in the application list.
// TRANS: %s are the first 7 characters of the access token.
sprintf(_('Access token starting with: %s'), substr($this->connection->token, 0, 7))
);
$this->out->elementStart(
'form',
array(
'id' => 'form_revoke_app',
'class' => 'form_revoke_app',
'method' => 'POST',
'action' => common_local_url('oauthconnectionssettings')
)
);
$this->out->elementStart('fieldset');
$this->out->hidden('oauth_token', $this->connection->token);
$this->out->hidden('token', common_session_token());
// TRANS: Button label in application list to revoke access to user data.
$this->out->submit('revoke', _m('BUTTON','Revoke'));
$this->out->elementEnd('fieldset');
$this->out->elementEnd('form');
$this->out->elementEnd('li');
}
}

View File

@ -201,13 +201,13 @@ abstract class AuthenticationPlugin extends Plugin
} }
} }
function onStartChangePassword($user,$oldpassword,$newpassword) function onStartChangePassword(Profile $target ,$oldpassword, $newpassword)
{ {
if($this->password_changeable){ if($this->password_changeable){
$user_username = new User_username(); $user_username = new User_username();
$user_username->user_id=$user->id; $user_username->user_id = $target->getID();
$user_username->provider_name=$this->provider_name; $user_username->provider_name=$this->provider_name;
if($user_username->find() && $user_username->fetch()){ if ($user_username->find(true)) {
$authenticated = $this->checkPassword($user_username->username, $oldpassword); $authenticated = $this->checkPassword($user_username->username, $oldpassword);
if($authenticated){ if($authenticated){
$result = $this->changePassword($user_username->username,$oldpassword,$newpassword); $result = $this->changePassword($user_username->username,$oldpassword,$newpassword);

163
lib/connectedappslist.php Normal file
View File

@ -0,0 +1,163 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Widget to show a list of OAuth applications
*
* 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 Application
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Widget to show a list of connected OAuth clients
*
* @category Application
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ConnectedAppsList extends Widget
{
/** Current connected application query */
var $connection = null;
/** Owner of this list */
var $owner = null;
function __construct($connection, Profile $owner, Action $out=null)
{
parent::__construct($out);
common_debug("ConnectedAppsList constructor");
$this->connection = $connection;
$this->owner = $owner;
}
/* Override this in subclasses. */
function showOwnerControls()
{
return;
}
function show()
{
$this->out->elementStart('ul', 'applications');
$cnt = 0;
while ($this->connection->fetch()) {
$cnt++;
if($cnt > APPS_PER_PAGE) {
break;
}
$this->showConnection();
}
$this->out->elementEnd('ul');
return $cnt;
}
function showConnection()
{
$app = Oauth_application::getKV('id', $this->connection->application_id);
$this->out->elementStart('li', array('class' => 'application h-entry',
'id' => 'oauthclient-' . $app->id));
$this->out->elementStart('a', array('href' => $app->source_url,
'class' => 'h-card p-name'));
if (!empty($app->icon)) {
$this->out->element('img', array('src' => $app->icon,
'class' => 'avatar u-photo'));
}
if ($app->name != 'anonymous') {
$this->out->text($app->name);
} else {
// TRANS: Name for an anonymous application in application list.
$this->out->element('span', 'p-name', _('Unknown application'));
}
$this->out->elementEnd('a');
if ($app->name != 'anonymous') {
// @todo FIXME: i18n trouble.
// TRANS: Message has a leading space and a trailing space. Used in application list.
// TRANS: Before this message the application name is put, behind it the organisation that manages it.
$this->out->raw(_(' by '));
$this->out->element('a', array('href' => $app->homepage,
'class' => 'h-card'),
$app->organization);
}
// TRANS: Application access type
$readWriteText = _('read-write');
// TRANS: Application access type
$readOnlyText = _('read-only');
$access = ($this->connection->access_type & Oauth_application::$writeAccess)
? $readWriteText : $readOnlyText;
$modifiedDate = common_date_string($this->connection->modified);
// TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only")
$txt = sprintf(_('Approved %1$s - "%2$s" access.'), $modifiedDate, $access);
// @todo FIXME: i18n trouble.
$this->out->raw(" - $txt");
if (!empty($app->description)) {
$this->out->element(
'p', array('class' => 'application_description'),
$app->description
);
}
$this->out->element(
'p', array(
'class' => 'access_token'),
// TRANS: Access token in the application list.
// TRANS: %s are the first 7 characters of the access token.
sprintf(_('Access token starting with: %s'), substr($this->connection->token, 0, 7))
);
$this->out->elementStart(
'form',
array(
'id' => 'form_revoke_app',
'class' => 'form_revoke_app',
'method' => 'POST',
'action' => common_local_url('oauthconnectionssettings')
)
);
$this->out->elementStart('fieldset');
$this->out->hidden('oauth_token', $this->connection->token);
$this->out->hidden('token', common_session_token());
// TRANS: Button label in application list to revoke access to user data.
$this->out->submit('revoke', _m('BUTTON','Revoke'));
$this->out->elementEnd('fieldset');
$this->out->elementEnd('form');
$this->out->elementEnd('li');
}
}

View File

@ -72,7 +72,7 @@ class DBQueueManager extends QueueManager
public function poll() public function poll()
{ {
//$this->_log(LOG_DEBUG, 'Checking for notices...'); //$this->_log(LOG_DEBUG, 'Checking for notices...');
$qi = Queue_item::top($this->activeQueues()); $qi = Queue_item::top($this->activeQueues(), $this->getIgnoredTransports());
if (!$qi instanceof Queue_item) { if (!$qi instanceof Queue_item) {
//$this->_log(LOG_DEBUG, 'No notices waiting; idling.'); //$this->_log(LOG_DEBUG, 'No notices waiting; idling.');
return false; return false;

View File

@ -129,7 +129,7 @@ $default =
'biolimit' => null, 'biolimit' => null,
'changenick' => false, 'changenick' => false,
'backup' => true, 'backup' => true,
'restore' => true, 'restore' => false,
'delete' => false, 'delete' => false,
'move' => true), 'move' => true),
'image' => 'image' =>

View File

@ -74,8 +74,13 @@ class DelUserQueueHandler extends QueueHandler
$qm = QueueManager::get(); $qm = QueueManager::get();
$qm->enqueue($user, 'deluser'); $qm->enqueue($user, 'deluser');
} else { } else {
// Out of notices? Let's finish deleting this guy! // Out of notices? Let's finish deleting this profile!
try {
$user->getProfile()->delete();
} catch (UserNoProfileException $e) {
// in case a profile didn't exist for some reason, just delete the User directly
$user->delete(); $user->delete();
}
common_log(LOG_INFO, "User $user->id $user->nickname deleted."); common_log(LOG_INFO, "User $user->id $user->nickname deleted.");
return true; return true;
} }

View File

@ -37,6 +37,7 @@ define('NOTICES_PER_PAGE', 20);
define('PROFILES_PER_PAGE', 20); define('PROFILES_PER_PAGE', 20);
define('MESSAGES_PER_PAGE', 20); define('MESSAGES_PER_PAGE', 20);
define('GROUPS_PER_PAGE', 20); define('GROUPS_PER_PAGE', 20);
define('APPS_PER_PAGE', 20);
define('GROUPS_PER_MINILIST', 8); define('GROUPS_PER_MINILIST', 8);
define('PROFILES_PER_MINILIST', 8); define('PROFILES_PER_MINILIST', 8);

View File

@ -36,35 +36,6 @@ class GalleryAction extends ProfileAction
parent::handle(); parent::handle();
} }
protected function doPreparation()
{
// showstream requires a nickname
$nickname_arg = $this->arg('nickname');
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
if ($this->arg('page') && $this->arg('page') != 1) {
$args['page'] = $this->arg['page'];
}
common_redirect(common_local_url($this->getActionName(), $args), 301);
}
$this->user = User::getKV('nickname', $nickname);
if (!$this->user) {
$group = Local_group::getKV('nickname', $nickname);
if ($group instanceof Local_group) {
common_redirect($group->getProfile()->getUrl());
}
// TRANS: Client error displayed when calling a profile action without specifying a user.
$this->clientError(_('No such user.'), 404);
}
$this->target = $this->user->getProfile();
}
function showContent() function showContent()
{ {
$this->showTagsDropdown(); $this->showTagsDropdown();

View File

@ -189,6 +189,8 @@ class ImageFile
case UPLOAD_ERR_NO_FILE: case UPLOAD_ERR_NO_FILE:
// No file; probably just a non-AJAX submission. // No file; probably just a non-AJAX submission.
throw new ClientException(_('No file uploaded.'));
default: default:
common_log(LOG_ERR, __METHOD__ . ": Unknown upload error " . $_FILES[$param]['error']); common_log(LOG_ERR, __METHOD__ . ": Unknown upload error " . $_FILES[$param]['error']);
// TRANS: Exception thrown when uploading an image fails for an unknown reason. // TRANS: Exception thrown when uploading an image fails for an unknown reason.

View File

@ -126,17 +126,6 @@ abstract class ImPlugin extends Plugin
*/ */
abstract function daemonScreenname(); abstract function daemonScreenname();
/**
* 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 ========================\ //========================UTILITY FUNCTIONS USEFUL TO IMPLEMENTATIONS - MISC ========================\
/** /**
@ -254,11 +243,11 @@ abstract class ImPlugin extends Plugin
* *
* @param string $screenname screenname sending to * @param string $screenname screenname sending to
* @param string $code the confirmation code * @param string $code the confirmation code
* @param User $user user sending to * @param Profile $target For whom the code is valid for
* *
* @return boolean success value * @return boolean success value
*/ */
function sendConfirmationCode($screenname, $code, $user) function sendConfirmationCode($screenname, $code, Profile $target)
{ {
// TRANS: Body text for confirmation code e-mail. // TRANS: Body text for confirmation code e-mail.
// TRANS: %1$s is a user nickname, %2$s is the StatusNet sitename, // TRANS: %1$s is a user nickname, %2$s is the StatusNet sitename,
@ -269,7 +258,7 @@ abstract class ImPlugin extends Plugin
' . (If you cannot click it, copy-and-paste it into the ' . ' . (If you cannot click it, copy-and-paste it into the ' .
'address bar of your browser). If that user is not you, ' . 'address bar of your browser). If that user is not you, ' .
'or if you did not request this confirmation, just ignore this message.'), 'or if you did not request this confirmation, just ignore this message.'),
$user->nickname, common_config('site', 'name'), $this->getDisplayName(), common_local_url('confirmaddress', null, array('code' => $code))); $target->getNickname(), common_config('site', 'name'), $this->getDisplayName(), common_local_url('confirmaddress', null, array('code' => $code)));
return $this->sendMessage($screenname, $body); return $this->sendMessage($screenname, $body);
} }
@ -571,25 +560,12 @@ abstract class ImPlugin extends Plugin
$user_im_prefs->user_id = $action->notice->getProfile()->getID(); $user_im_prefs->user_id = $action->notice->getProfile()->getID();
$user_im_prefs->transport = $this->transport; $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()));
}
} elseif ($action instanceof ShowstreamAction) { } elseif ($action instanceof ShowstreamAction) {
$user_im_prefs = new User_im_prefs(); $user_im_prefs = new User_im_prefs();
$user_im_prefs->user_id = $action->getTarget()->getID(); $user_im_prefs->user_id = $action->getTarget()->getID();
$user_im_prefs->transport = $this->transport; $user_im_prefs->transport = $this->transport;
if ($user_im_prefs->find() && $user_im_prefs->fetch() && $user_im_prefs->microid && $action->getTarget()->getUrl()) {
$id = new Microid($this->microiduri($user_im_prefs->screenname),
$action->selfUrl());
$action->element('meta', array('name' => 'microid',
'content' => $id->toString()));
}
} }
} }
@ -618,11 +594,11 @@ abstract class ImPlugin extends Plugin
'daemonScreenname' => $this->daemonScreenname()); 'daemonScreenname' => $this->daemonScreenname());
} }
function onSendImConfirmationCode($transport, $screenname, $code, $user) function onSendImConfirmationCode($transport, $screenname, $code, Profile $target)
{ {
if($transport == $this->transport) if($transport == $this->transport)
{ {
$this->sendConfirmationCode($screenname, $code, $user); $this->sendConfirmationCode($screenname, $code, $target);
return false; return false;
} }
} }

View File

@ -1,97 +0,0 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Microid class
*
* 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 ID
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* A class for microids
*
* @category ID
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @see http://microid.org/
*/
class Microid
{
/** Agent part of the ID. */
var $agent = null;
/** Resource part of the ID. */
var $resource = null;
/**
* Constructor
*
* @param string $agent Agent of the ID
* @param string $resource Resource part
*/
function __construct($agent, $resource)
{
$this->agent = $agent;
$this->resource = $resource;
}
/**
* Generate a MicroID string
*
* @return string MicroID for agent and resource
*/
function toString()
{
$agent_proto = $this->_getProto($this->agent);
$resource_proto = $this->_getProto($this->resource);
return $agent_proto.'+'.$resource_proto.':sha1:'.
sha1(sha1($this->agent).sha1($this->resource));
}
/**
* Utility for getting the protocol part of a URI
*
* @param string $uri URI to parse
*
* @return string scheme part of the URI
*/
function _getProto($uri)
{
$colon = strpos($uri, ':');
return substr($uri, 0, $colon);
}
}

View File

@ -48,6 +48,36 @@ abstract class ProfileAction extends ManagedAction
protected $target = null; // Profile that we're showing protected $target = null; // Profile that we're showing
protected function doPreparation()
{
// showstream requires a nickname
$nickname_arg = $this->trimmed('nickname');
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
if ($this->arg('page') && $this->arg('page') != 1) {
$args['page'] = $this->arg['page'];
}
common_redirect(common_local_url($this->getActionName(), $args), 301);
}
try {
$user = User::getByNickname($nickname);
} catch (NoSuchUserException $e) {
$group = Local_group::getKV('nickname', $nickname);
if ($group instanceof Local_group) {
common_redirect($group->getProfile()->getUrl());
}
// No user nor group found, throw the NoSuchUserException again
throw $e;
}
$this->target = $user->getProfile();
}
protected function prepare(array $args=array()) protected function prepare(array $args=array())
{ {
// this will call ->doPreparation() which child classes use to set $this->target // this will call ->doPreparation() which child classes use to set $this->target
@ -65,11 +95,6 @@ abstract class ProfileAction extends ManagedAction
return true; return true;
} }
protected function profileActionPreparation()
{
// Nothing to do by default.
}
public function getTarget() public function getTarget()
{ {
if (!$this->target instanceof Profile) { if (!$this->target instanceof Profile) {

View File

@ -43,6 +43,7 @@ abstract class QueueManager extends IoManager
protected $handlers = array(); protected $handlers = array();
protected $groups = array(); protected $groups = array();
protected $activeGroups = array(); protected $activeGroups = array();
protected $ignoredTransports = array();
/** /**
* Factory function to pull the appropriate QueueManager object * Factory function to pull the appropriate QueueManager object
@ -255,6 +256,17 @@ abstract class QueueManager extends IoManager
return array_keys($queues); return array_keys($queues);
} }
function getIgnoredTransports()
{
return array_keys($this->ignoredTransports);
}
function ignoreTransport($transport)
{
// key is used for uniqueness, value doesn't mean anything
$this->ignoredTransports[$transport] = true;
}
/** /**
* Initialize the list of queue handlers for the current site. * Initialize the list of queue handlers for the current site.
* *

View File

@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Base class for settings group of actions * Base class for settings group of actions
@ -43,113 +41,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @see Widget * @see Widget
*/ */
class SettingsAction extends Action class SettingsAction extends FormAction
{ {
/**
* A message for the user.
*/
var $msg = null;
/**
* Whether the message is a good one or a bad one.
*/
var $success = false;
/**
* Handle input and output a page
*
* @param array $args $_REQUEST arguments
*
* @return void
*/
function handle($args)
{
parent::handle($args);
if (!common_logged_in()) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user.
$this->clientError(_('Not logged in.'));
} else if (!common_is_real_login()) {
// Cookie theft means that automatic logins can't
// change important settings or see private info, and
// _all_ our settings are important
common_set_returnto($this->selfUrl());
$user = common_current_user();
if (Event::handle('RedirectToLogin', array($this, $user))) {
common_redirect(common_local_url('login'), 303);
}
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePost();
} else {
$this->showForm();
}
}
/**
* Handle a POST request
*
* @return boolean success flag
*/
function handlePost()
{
return false;
}
/**
* show the settings form
*
* @param string $msg an extra message for the user
* @param string $success good message or bad message?
*
* @return void
*/
function showForm($msg=null, $success=false)
{
$this->msg = $msg;
$this->success = $success;
$this->showPage();
}
/**
* show human-readable instructions for the page
*
* @return void
*/
function showPageNotice()
{
if ($this->msg) {
$this->element('div', ($this->success) ? 'success' : 'error',
$this->msg);
} else {
$inst = $this->getInstructions();
$output = common_markup_to_html($inst);
$this->elementStart('div', 'instructions');
$this->raw($output);
$this->elementEnd('div');
}
}
/**
* instructions recipe for sub-classes
*
* Subclasses should override this to return readable instructions. They'll
* be processed by common_markup_to_html().
*
* @return string instructions text
*/
function getInstructions()
{
return '';
}
/** /**
* Show the local navigation menu * Show the local navigation menu
* *

View File

@ -110,7 +110,6 @@ class PublicSite extends SiteProfileSettings
'plugins' => array( 'plugins' => array(
'core' => self::corePlugins(), 'core' => self::corePlugins(),
'default' => array_merge(self::defaultPlugins(), array( 'default' => array_merge(self::defaultPlugins(), array(
'ExtendedProfile' => array(),
'RegisterThrottle' => array(), 'RegisterThrottle' => array(),
)) ))
), ),

View File

@ -39,6 +39,11 @@ class TargetedRss10Action extends Rss10Action
$this->target = User::getByNickname($this->trimmed('nickname'))->getProfile(); $this->target = User::getByNickname($this->trimmed('nickname'))->getProfile();
} }
public function getTarget()
{
return $this->target;
}
function getImage() function getImage()
{ {
return $this->target->avatarUrl(AVATAR_PROFILE_SIZE); return $this->target->avatarUrl(AVATAR_PROFILE_SIZE);

View File

@ -195,7 +195,7 @@ class ThreadedNoticeListItem extends NoticeListItem
function showEnd() function showEnd()
{ {
$max = $this->initialItems(); $max = $this->initialItems();
if (!$this->repeat) { if (!$this->repeat instanceof Notice) {
$stream = new ConversationNoticeStream($this->notice->conversation, $this->userProfile); $stream = new ConversationNoticeStream($this->notice->conversation, $this->userProfile);
$notice = $stream->getNotices(0, $max + 2); $notice = $stream->getNotices(0, $max + 2);
$notices = array(); $notices = array();

View File

@ -210,7 +210,7 @@ function common_language()
/** /**
* Salted, hashed passwords are stored in the DB. * Salted, hashed passwords are stored in the DB.
*/ */
function common_munge_password($password, $id, Profile $profile=null) function common_munge_password($password, Profile $profile=null)
{ {
$hashed = null; $hashed = null;
@ -245,8 +245,7 @@ function common_check_user($nickname, $password)
} }
if ($user instanceof User && !empty($password)) { if ($user instanceof User && !empty($password)) {
if (0 == strcmp(common_munge_password($password, $user->id), if (0 == strcmp(common_munge_password($password, $user->getProfile()), $user->password)) {
$user->password)) {
//internal checking passed //internal checking passed
$authenticatedUser = $user; $authenticatedUser = $user;
} }
@ -710,7 +709,6 @@ function common_find_mentions($text, Notice $notice)
// Is it a reply? // Is it a reply?
if ($notice instanceof Notice) {
try { try {
$origNotice = $notice->getParent(); $origNotice = $notice->getParent();
$origAuthor = $origNotice->getProfile(); $origAuthor = $origNotice->getProfile();
@ -718,18 +716,15 @@ function common_find_mentions($text, Notice $notice)
$ids = $origNotice->getReplies(); $ids = $origNotice->getReplies();
foreach ($ids as $id) { foreach ($ids as $id) {
$repliedTo = Profile::getKV('id', $id); try {
if ($repliedTo instanceof Profile) { $repliedTo = Profile::getByID($id);
$origMentions[$repliedTo->nickname] = $repliedTo; $origMentions[$repliedTo->getNickname()] = $repliedTo;
} catch (NoResultException $e) {
// continue foreach
} }
} }
} catch (NoProfileException $e) {
common_log(LOG_WARNING, sprintf('Notice %d author profile id %d does not exist', $origNotice->id, $origNotice->profile_id));
} catch (NoParentNoticeException $e) { } catch (NoParentNoticeException $e) {
// This notice is not in reply to anything // It wasn't a reply to anything, so we can't harvest nickname-relations.
} catch (Exception $e) {
common_log(LOG_WARNING, __METHOD__ . ' got exception ' . get_class($e) . ' : ' . $e->getMessage());
}
} }
$matches = common_find_mentions_raw($text); $matches = common_find_mentions_raw($text);

View File

@ -63,12 +63,15 @@ class XMLOutputter
* *
* Initializes the wrapped XMLWriter. * Initializes the wrapped XMLWriter.
* *
* @param string $output URL for outputting, defaults to stdout * @param string $output URL for outputting, if null it defaults to stdout ('php://output')
* @param boolean $indent Whether to indent output, default true * @param boolean $indent Whether to indent output, default true
*/ */
function __construct($output='php://output', $indent=null) function __construct($output=null, $indent=null)
{ {
if (is_null($output)) {
$output = 'php://output';
}
$this->xw = new XMLWriter(); $this->xw = new XMLWriter();
$this->xw->openURI($output); $this->xw->openURI($output);
if(is_null($indent)) { if(is_null($indent)) {

View File

@ -110,17 +110,17 @@ class AuthCryptPlugin extends AuthenticationPlugin
* EVENTS * EVENTS
*/ */
public function onStartChangePassword($user, $oldpassword, $newpassword) public function onStartChangePassword(Profile $target, $oldpassword, $newpassword)
{ {
if (!$this->checkPassword($user->nickname, $oldpassword)) { if (!$this->checkPassword($target->getNickname(), $oldpassword)) {
// if we ARE in overwrite mode, test password with common_check_user // if we ARE in overwrite mode, test password with common_check_user
if (!$this->overwrite || !common_check_user($user->nickname, $oldpassword)) { if (!$this->overwrite || !common_check_user($target->getNickname(), $oldpassword)) {
// either we're not in overwrite mode, or the password was incorrect // either we're not in overwrite mode, or the password was incorrect
return !$this->authoritative; return !$this->authoritative;
} }
// oldpassword was apparently ok // oldpassword was apparently ok
} }
$changed = $this->changePassword($user->nickname, $oldpassword, $newpassword); $changed = $this->changePassword($target->getNickname(), $oldpassword, $newpassword);
return (!$changed && empty($this->authoritative)); return (!$changed && empty($this->authoritative));
} }

View File

@ -17,9 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
class ProfileDetailSettingsAction extends ProfileSettingsAction class ProfileDetailSettingsAction extends ProfileSettingsAction
{ {
@ -29,18 +27,6 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
return _m('Extended profile settings'); return _m('Extended profile settings');
} }
/**
* Instructions for use
*
* @return instructions for use
*/
function getInstructions()
{
// TRANS: Usage instructions for profile settings.
return _m('You can update your personal profile info here '.
'so people know more about you.');
}
function showStylesheets() { function showStylesheets() {
parent::showStylesheets(); parent::showStylesheets();
$this->cssLink('plugins/ExtendedProfile/css/profiledetail.css'); $this->cssLink('plugins/ExtendedProfile/css/profiledetail.css');
@ -53,36 +39,21 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
return true; return true;
} }
function handlePost() protected function doPost()
{ {
// CSRF protection if ($this->arg('save')) {
$token = $this->trimmed('token'); return $this->saveDetails();
if (!$token || $token != common_session_token()) {
$this->showForm(
// TRANS: Client error displayed when the session token does not match or is not given.
_m('There was a problem with your session token. '
. 'Try again, please.'
)
);
return;
} }
if ($this->arg('save')) {
$this->saveDetails();
} else {
// TRANS: Message given submitting a form with an unknown action. // TRANS: Message given submitting a form with an unknown action.
$this->showForm(_m('Unexpected form submission.')); throw new ClientException(_m('Unexpected form submission.'));
}
} }
function showContent() function showContent()
{ {
$cur = common_current_user();
$profile = $cur->getProfile();
$widget = new ExtendedProfileWidget( $widget = new ExtendedProfileWidget(
$this, $this,
$profile, $this->scoped,
ExtendedProfileWidget::EDITABLE ExtendedProfileWidget::EDITABLE
); );
$widget->show(); $widget->show();
@ -92,12 +63,7 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
{ {
common_debug(var_export($_POST, true)); common_debug(var_export($_POST, true));
$user = common_current_user(); $this->saveStandardProfileDetails();
try {
$this->saveStandardProfileDetails($user);
$profile = $user->getProfile();
$simpleFieldNames = array('title', 'spouse', 'kids', 'manager'); $simpleFieldNames = array('title', 'spouse', 'kids', 'manager');
$dateFieldNames = array('birthday'); $dateFieldNames = array('birthday');
@ -105,7 +71,7 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
foreach ($simpleFieldNames as $name) { foreach ($simpleFieldNames as $name) {
$value = $this->trimmed('extprofile-' . $name); $value = $this->trimmed('extprofile-' . $name);
if (!empty($value)) { if (!empty($value)) {
$this->saveField($user, $name, $value); $this->saveField($name, $value);
} }
} }
@ -113,7 +79,6 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
$value = $this->trimmed('extprofile-' . $name); $value = $this->trimmed('extprofile-' . $name);
$dateVal = $this->parseDate($name, $value); $dateVal = $this->parseDate($name, $value);
$this->saveField( $this->saveField(
$user,
$name, $name,
null, null,
null, null,
@ -122,19 +87,14 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
); );
} }
$this->savePhoneNumbers($user); $this->savePhoneNumbers();
$this->saveIms($user); $this->saveIms();
$this->saveWebsites($user); $this->saveWebsites();
$this->saveExperiences($user); $this->saveExperiences();
$this->saveEducations($user); $this->saveEducations();
} catch (Exception $e) {
$this->showForm($e->getMessage(), false);
return;
}
// TRANS: Success message after saving extended profile details. // TRANS: Success message after saving extended profile details.
$this->showForm(_m('Details saved.'), true); return _m('Details saved.');
} }
@ -168,15 +128,14 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
return null; return null;
} }
function savePhoneNumbers($user) { function savePhoneNumbers() {
$phones = $this->findPhoneNumbers(); $phones = $this->findPhoneNumbers();
$this->removeAll($user, 'phone'); $this->removeAll('phone');
$i = 0; $i = 0;
foreach($phones as $phone) { foreach($phones as $phone) {
if (!empty($phone['value'])) { if (!empty($phone['value'])) {
++$i; ++$i;
$this->saveField( $this->saveField(
$user,
'phone', 'phone',
$phone['value'], $phone['value'],
$phone['rel'], $phone['rel'],
@ -226,15 +185,14 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
return $imArray; return $imArray;
} }
function saveIms($user) { function saveIms() {
$ims = $this->findIms(); $ims = $this->findIms();
$this->removeAll($user, 'im'); $this->removeAll('im');
$i = 0; $i = 0;
foreach($ims as $im) { foreach($ims as $im) {
if (!empty($im['value'])) { if (!empty($im['value'])) {
++$i; ++$i;
$this->saveField( $this->saveField(
$user,
'im', 'im',
$im['value'], $im['value'],
$im['rel'], $im['rel'],
@ -262,9 +220,9 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
return $wsArray; return $wsArray;
} }
function saveWebsites($user) { function saveWebsites() {
$sites = $this->findWebsites(); $sites = $this->findWebsites();
$this->removeAll($user, 'website'); $this->removeAll('website');
$i = 0; $i = 0;
foreach($sites as $site) { foreach($sites as $site) {
if (!empty($site['value']) && !common_valid_http_url($site['value'])) { if (!empty($site['value']) && !common_valid_http_url($site['value'])) {
@ -276,7 +234,6 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
if (!empty($site['value'])) { if (!empty($site['value'])) {
++$i; ++$i;
$this->saveField( $this->saveField(
$user,
'website', 'website',
$site['value'], $site['value'],
$site['rel'], $site['rel'],
@ -317,20 +274,19 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
return $expArray; return $expArray;
} }
function saveExperiences($user) { function saveExperiences() {
common_debug('save experiences'); common_debug('save experiences');
$experiences = $this->findExperiences(); $experiences = $this->findExperiences();
$this->removeAll($user, 'company'); $this->removeAll('company');
$this->removeAll($user, 'start'); $this->removeAll('start');
$this->removeAll($user, 'end'); // also stores 'current' $this->removeAll('end'); // also stores 'current'
$i = 0; $i = 0;
foreach($experiences as $experience) { foreach($experiences as $experience) {
if (!empty($experience['company'])) { if (!empty($experience['company'])) {
++$i; ++$i;
$this->saveField( $this->saveField(
$user,
'company', 'company',
$experience['company'], $experience['company'],
null, null,
@ -338,7 +294,6 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
); );
$this->saveField( $this->saveField(
$user,
'start', 'start',
null, null,
null, null,
@ -349,7 +304,6 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
// Save "current" employer indicator in rel // Save "current" employer indicator in rel
if ($experience['current']) { if ($experience['current']) {
$this->saveField( $this->saveField(
$user,
'end', 'end',
null, null,
'current', // rel 'current', // rel
@ -357,7 +311,6 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
); );
} else { } else {
$this->saveField( $this->saveField(
$user,
'end', 'end',
null, null,
null, null,
@ -399,44 +352,40 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
} }
function saveEducations($user) { function saveEducations() {
common_debug('save education'); common_debug('save education');
$edus = $this->findEducations(); $edus = $this->findEducations();
common_debug(var_export($edus, true)); common_debug(var_export($edus, true));
$this->removeAll($user, 'school'); $this->removeAll('school');
$this->removeAll($user, 'degree'); $this->removeAll('degree');
$this->removeAll($user, 'degree_descr'); $this->removeAll('degree_descr');
$this->removeAll($user, 'school_start'); $this->removeAll('school_start');
$this->removeAll($user, 'school_end'); $this->removeAll('school_end');
$i = 0; $i = 0;
foreach($edus as $edu) { foreach($edus as $edu) {
if (!empty($edu['school'])) { if (!empty($edu['school'])) {
++$i; ++$i;
$this->saveField( $this->saveField(
$user,
'school', 'school',
$edu['school'], $edu['school'],
null, null,
$i $i
); );
$this->saveField( $this->saveField(
$user,
'degree', 'degree',
$edu['degree'], $edu['degree'],
null, null,
$i $i
); );
$this->saveField( $this->saveField(
$user,
'degree_descr', 'degree_descr',
$edu['description'], $edu['description'],
null, null,
$i $i
); );
$this->saveField( $this->saveField(
$user,
'school_start', 'school_start',
null, null,
null, null,
@ -445,7 +394,6 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
); );
$this->saveField( $this->saveField(
$user,
'school_end', 'school_end',
null, null,
null, null,
@ -491,35 +439,33 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
/** /**
* Save an extended profile field as a Profile_detail * Save an extended profile field as a Profile_detail
* *
* @param User $user the current user
* @param string $name field name * @param string $name field name
* @param string $value field value * @param string $value field value
* @param string $rel field rel (type) * @param string $rel field rel (type)
* @param int $index index (fields can have multiple values) * @param int $index index (fields can have multiple values)
* @param date $date related date * @param date $date related date
*/ */
function saveField($user, $name, $value, $rel = null, $index = null, $date = null) function saveField($name, $value, $rel = null, $index = null, $date = null)
{ {
$profile = $user->getProfile();
$detail = new Profile_detail(); $detail = new Profile_detail();
$detail->profile_id = $profile->id; $detail->profile_id = $this->scoped->getID();
$detail->field_name = $name; $detail->field_name = $name;
$detail->value_index = $index; $detail->value_index = $index;
$result = $detail->find(true); $result = $detail->find(true);
if (empty($result)) { if (!$result instanceof Profile_detail) {
$detial->value_index = $index; $detail->value_index = $index;
$detail->rel = $rel; $detail->rel = $rel;
$detail->field_value = $value; $detail->field_value = $value;
$detail->date = $date; $detail->date = $date;
$detail->created = common_sql_now(); $detail->created = common_sql_now();
$result = $detail->insert(); $result = $detail->insert();
if (empty($result)) { if ($result === false) {
common_log_db_error($detail, 'INSERT', __FILE__); common_log_db_error($detail, 'INSERT', __FILE__);
// TRANS: Server error displayed when a field could not be saved in the database. // TRANS: Server error displayed when a field could not be saved in the database.
$this->serverError(_m('Could not save profile details.')); throw new ServerException(_m('Could not save profile details.'));
} }
} else { } else {
$orig = clone($detail); $orig = clone($detail);
@ -529,21 +475,20 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
$detail->date = $date; $detail->date = $date;
$result = $detail->update($orig); $result = $detail->update($orig);
if (empty($result)) { if ($result === false) {
common_log_db_error($detail, 'UPDATE', __FILE__); common_log_db_error($detail, 'UPDATE', __FILE__);
// TRANS: Server error displayed when a field could not be saved in the database. // TRANS: Server error displayed when a field could not be saved in the database.
$this->serverError(_m('Could not save profile details.')); throw new ServerException(_m('Could not save profile details.'));
} }
} }
$detail->free(); $detail->free();
} }
function removeAll($user, $name) function removeAll($name)
{ {
$profile = $user->getProfile();
$detail = new Profile_detail(); $detail = new Profile_detail();
$detail->profile_id = $profile->id; $detail->profile_id = $this->scoped->getID();
$detail->field_name = $name; $detail->field_name = $name;
$detail->delete(); $detail->delete();
$detail->free(); $detail->free();
@ -554,10 +499,8 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
* *
* XXX: There's a lot of dupe code here from ProfileSettingsAction. * XXX: There's a lot of dupe code here from ProfileSettingsAction.
* Do not want. * Do not want.
*
* @param User $user the current user
*/ */
function saveStandardProfileDetails($user) function saveStandardProfileDetails()
{ {
$fullname = $this->trimmed('extprofile-fullname'); $fullname = $this->trimmed('extprofile-fullname');
$location = $this->trimmed('extprofile-location'); $location = $this->trimmed('extprofile-location');
@ -581,54 +524,47 @@ class ProfileDetailSettingsAction extends ProfileSettingsAction
} }
} }
$profile = $user->getProfile(); $oldTags = Profile_tag::getSelfTagsArray($this->scoped);
$oldTags = $user->getSelfTags();
$newTags = array_diff($tags, $oldTags); $newTags = array_diff($tags, $oldTags);
if ($fullname != $profile->fullname if ($fullname != $this->scoped->getFullname()
|| $location != $profile->location || $location != $this->scoped->location
|| !empty($newTags) || !empty($newTags)
|| $bio != $profile->bio) { || $bio != $this->scoped->getDescription()) {
$orig = clone($profile); $orig = clone($this->scoped);
$profile->nickname = $user->nickname; // Skipping nickname change here until we add logic for when the site allows it or not
$profile->fullname = $fullname; // old Profilesettings will still let us do that.
$profile->bio = $bio;
$profile->location = $location; $this->scoped->fullname = $fullname;
$this->scoped->bio = $bio;
$this->scoped->location = $location;
$loc = Location::fromName($location); $loc = Location::fromName($location);
if (empty($loc)) { if (empty($loc)) {
$profile->lat = null; $this->scoped->lat = null;
$profile->lon = null; $this->scoped->lon = null;
$profile->location_id = null; $this->scoped->location_id = null;
$profile->location_ns = null; $this->scoped->location_ns = null;
} else { } else {
$profile->lat = $loc->lat; $this->scoped->lat = $loc->lat;
$profile->lon = $loc->lon; $this->scoped->lon = $loc->lon;
$profile->location_id = $loc->location_id; $this->scoped->location_id = $loc->location_id;
$profile->location_ns = $loc->location_ns; $this->scoped->location_ns = $loc->location_ns;
} }
$profile->profileurl = common_profile_url($user->nickname); $result = $this->scoped->update($orig);
$result = $profile->update($orig);
if ($result === false) { if ($result === false) {
common_log_db_error($profile, 'UPDATE', __FILE__); common_log_db_error($this->scoped, 'UPDATE', __FILE__);
// TRANS: Server error thrown when user profile settings could not be saved. // TRANS: Server error thrown when user profile settings could not be saved.
$this->serverError(_m('Could not save profile.')); throw new ServerException(_m('Could not save profile.'));
} }
// Set the user tags // Set the user tags
$result = $user->setSelfTags($tags); $result = Profile_tag::setSelfTags($this->scoped, $tags);
if (!$result) {
// TRANS: Server error thrown when user profile settings tags could not be saved.
$this->serverError(_m('Could not save tags.'));
}
Event::handle('EndProfileSaveForm', array($this)); Event::handle('EndProfileSaveForm', array($this));
} }

View File

@ -52,7 +52,7 @@ class ExtendedProfile
function loadFields() function loadFields()
{ {
$detail = new Profile_detail(); $detail = new Profile_detail();
$detail->profile_id = $this->profile->id; $detail->profile_id = $this->profile->getID();
$detail->find(); $detail->find();
$fields = array(); $fields = array();
@ -71,7 +71,7 @@ class ExtendedProfile
*/ */
function getTags() function getTags()
{ {
return implode(' ', $this->user->getSelfTags()); return implode(' ', Profile_tag::getSelfTagsArray($this->profile));
} }
/** /**

View File

@ -519,13 +519,10 @@ class FacebookfinishloginAction extends Action
function tryLogin() function tryLogin()
{ {
try {
$flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE); $flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE);
if (!empty($flink)) {
$user = $flink->getUser(); $user = $flink->getUser();
if (!empty($user)) {
common_log( common_log(
LOG_INFO, LOG_INFO,
sprintf( sprintf(
@ -544,9 +541,8 @@ class FacebookfinishloginAction extends Action
setcookie('fb_access_token', '', time() - 3600); // one hour ago setcookie('fb_access_token', '', time() - 3600); // one hour ago
$this->goHome($user->nickname); $this->goHome($user->nickname);
}
} else { } catch (NoResultException $e) {
$this->showForm(null, $this->bestNewNickname()); $this->showForm(null, $this->bestNewNickname());
} }
} }

View File

@ -26,9 +26,7 @@
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Edit user settings for Facebook * Edit user settings for Facebook
@ -43,19 +41,11 @@ if (!defined('STATUSNET')) {
*/ */
class FacebooksettingsAction extends SettingsAction { class FacebooksettingsAction extends SettingsAction {
private $facebook; // Facebook PHP-SDK client obj private $facebook; // Facebook PHP-SDK client obj
private $flink;
private $user;
/** protected $flink;
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($args) {
parent::prepare($args);
protected function doPreparation()
{
$this->facebook = new Facebook( $this->facebook = new Facebook(
array( array(
'appId' => common_config('facebook', 'appid'), 'appId' => common_config('facebook', 'appid'),
@ -64,36 +54,21 @@ class FacebooksettingsAction extends SettingsAction {
) )
); );
$this->user = common_current_user();
$this->flink = Foreign_link::getByUserID( $this->flink = Foreign_link::getByUserID(
$this->user->id, $this->scoped->getID(),
FACEBOOK_SERVICE FACEBOOK_SERVICE
); );
return true;
}
/*
* Check the sessions token and dispatch
*/
function handlePost($args) {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->showForm(
// TRANS: Client error displayed when the session token does not match or is not given.
_m('There was a problem with your session token. Try again, please.')
);
return;
} }
protected function doPost()
{
if ($this->arg('save')) { if ($this->arg('save')) {
$this->saveSettings(); return $this->saveSettings();
} else if ($this->arg('disconnect')) { } else if ($this->arg('disconnect')) {
$this->disconnect(); return $this->disconnect();
} }
throw new ClientException(_('No action to take on POST.'));
} }
/** /**
@ -122,7 +97,9 @@ class FacebooksettingsAction extends SettingsAction {
* @return void * @return void
*/ */
function showContent() { function showContent() {
if (!empty($this->flink)) { if (!$this->flink instanceof Foreign_link) {
throw new ServerException(_m('You have not linked this account to Facebook.'));
}
$this->elementStart( $this->elementStart(
'form', 'form',
@ -166,7 +143,7 @@ class FacebooksettingsAction extends SettingsAction {
'noticesync', 'noticesync',
// TRANS: Checkbox label in Facebook settings. // TRANS: Checkbox label in Facebook settings.
_m('Publish my notices to Facebook.'), _m('Publish my notices to Facebook.'),
($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND) : true $this->flink->noticesync & FOREIGN_NOTICE_SEND
); );
$this->elementEnd('li'); $this->elementEnd('li');
@ -177,7 +154,7 @@ class FacebooksettingsAction extends SettingsAction {
'replysync', 'replysync',
// TRANS: Checkbox label in Facebook settings. // TRANS: Checkbox label in Facebook settings.
_m('Send "@" replies to Facebook.'), _m('Send "@" replies to Facebook.'),
($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) : true $this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY
); );
$this->elementEnd('li'); $this->elementEnd('li');
@ -196,7 +173,7 @@ class FacebooksettingsAction extends SettingsAction {
// TRANS: Fieldset legend for form to disconnect from Facebook. // TRANS: Fieldset legend for form to disconnect from Facebook.
$this->element('legend', null, _m('Disconnect my account from Facebook')); $this->element('legend', null, _m('Disconnect my account from Facebook'));
if (empty($this->user->password)) { if (!$this->scoped->hasPassword()) {
$this->elementStart('p', array('class' => 'form_guide')); $this->elementStart('p', array('class' => 'form_guide'));
$msg = sprintf( $msg = sprintf(
@ -225,7 +202,6 @@ class FacebooksettingsAction extends SettingsAction {
$this->elementEnd('form'); $this->elementEnd('form');
} }
}
/* /*
* Save the user's Facebook settings * Save the user's Facebook settings
@ -242,11 +218,10 @@ class FacebooksettingsAction extends SettingsAction {
if ($result === false) { if ($result === false) {
// TRANS: Notice in case saving of synchronisation preferences fail. // TRANS: Notice in case saving of synchronisation preferences fail.
$this->showForm(_m('There was a problem saving your sync preferences.')); throw new ServerException(_m('There was a problem saving your sync preferences.'));
} else {
// TRANS: Confirmation that synchronisation settings have been saved into the system.
$this->showForm(_m('Sync preferences saved.'), true);
} }
// TRANS: Confirmation that synchronisation settings have been saved into the system.
return _m('Sync preferences saved.');
} }
/* /*
@ -258,12 +233,12 @@ class FacebooksettingsAction extends SettingsAction {
$this->flink = null; $this->flink = null;
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'DELETE', __FILE__); common_log_db_error($this->flink, 'DELETE', __FILE__);
// TRANS: Server error displayed when deleting the link to a Facebook account fails. // TRANS: Server error displayed when deleting the link to a Facebook account fails.
$this->serverError(_m('Could not delete link to Facebook.')); throw new ServerException(_m('Could not delete link to Facebook.'));
} }
// TRANS: Confirmation message. StatusNet account was unlinked from Facebook. // TRANS: Confirmation message. GNU social account was unlinked from Facebook.
$this->showForm(_m('You have disconnected from Facebook.'), true); return _m('You have disconnected this account from Facebook.');
} }
} }

View File

@ -66,13 +66,12 @@ class Facebookclient
$this->notice = $notice; $this->notice = $notice;
$profile_id = $profile ? $profile->id : $notice->profile_id; $profile_id = $profile ? $profile->id : $notice->profile_id;
$this->flink = Foreign_link::getByUserID( try {
$profile_id, $this->flink = Foreign_link::getByUserID($profile_id, FACEBOOK_SERVICE);
FACEBOOK_SERVICE
);
if (!empty($this->flink)) {
$this->user = $this->flink->getUser(); $this->user = $this->flink->getUser();
} catch (NoResultException $e) {
// at least $this->flink could've gotten set to something,
// but the logic that was here before didn't care, so let's not care either
} }
} }
@ -918,12 +917,9 @@ class Facebookclient
static function addFacebookUser($fbuser) static function addFacebookUser($fbuser)
{ {
// remove any existing, possibly outdated, record // remove any existing, possibly outdated, record
$luser = Foreign_user::getForeignUser($fbuser->id, FACEBOOK_SERVICE); try {
$fuser = Foreign_user::getForeignUser($fbuser->id, FACEBOOK_SERVICE);
if (!empty($luser)) { $result = $fuser->delete();
$result = $luser->delete();
if ($result != false) { if ($result != false) {
common_log( common_log(
LOG_INFO, LOG_INFO,
@ -935,6 +931,8 @@ class Facebookclient
__FILE__ __FILE__
); );
} }
} catch (NoResultException $e) {
// no old foreign users exist for this id
} }
$fuser = new Foreign_user(); $fuser = new Foreign_user();

View File

@ -1977,6 +1977,38 @@ class Ostatus_profile extends Managed_DataObject
return $oprofile->localProfile(); return $oprofile->localProfile();
} }
public function updateUriKeys($profile_uri, array $hints=array())
{
$orig = clone($this);
common_debug('URIFIX These identities both say they are each other: "'.$orig->uri.'" and "'.$profile_uri.'"');
$this->uri = $profile_uri;
if (array_key_exists('feedurl', $hints)) {
if (!empty($this->feeduri)) {
common_debug('URIFIX Changing FeedSub ['.$feedsub->id.'] feeduri "'.$feedsub->uri.'" to "'.$hints['feedurl']);
$feedsub = FeedSub::getKV('uri', $this->feeduri);
$feedorig = clone($feedsub);
$feedsub->uri = $hints['feedurl'];
$feedsub->updateWithKeys($feedorig);
} else {
common_debug('URIFIX Old Ostatus_profile did not have feedurl set, ensuring feed: '.$hints['feedurl']);
FeedSub::ensureFeed($hints['feedurl']);
}
$this->feeduri = $hints['feedurl'];
}
if (array_key_exists('salmon', $hints)) {
common_debug('URIFIX Changing Ostatus_profile salmonuri from "'.$this->salmonuri.'" to "'.$hints['salmon'].'"');
$this->salmonuri = $hints['salmon'];
}
common_debug('URIFIX Updating Ostatus_profile URI for '.$orig->uri.' to '.$this->uri);
$this->updateWithKeys($orig, 'uri'); // 'uri' is the primary key column
common_debug('URIFIX Subscribing/renewing feedsub for Ostatus_profile '.$this->uri);
$this->subscribe();
}
} }
/** /**

View File

@ -259,12 +259,7 @@ class SalmonAction extends Action
// Step 4: Is the newly introduced https://example.com/user/1 URI in the list of aliases // Step 4: Is the newly introduced https://example.com/user/1 URI in the list of aliases
// presented by http://example.com/user/1 (i.e. do they both say they are the same identity?) // presented by http://example.com/user/1 (i.e. do they both say they are the same identity?)
if (in_array($e->object_uri, $doublecheck_aliases)) { if (in_array($e->object_uri, $doublecheck_aliases)) {
common_debug('URIFIX These identities both say they are each other: "'.$aliased_uri.'" and "'.$e->object_uri.'"'); $oprofile->updateUriKeys($e->object_uri, DiscoveryHints::fromXRD($xrd));
$orig = clone($oprofile);
$oprofile->uri = $e->object_uri;
common_debug('URIFIX Updating Ostatus_profile URI for '.$aliased_uri.' to '.$oprofile->uri);
$oprofile->updateWithKeys($orig, 'uri'); // 'uri' is the primary key column
unset($orig);
$this->oprofile = $oprofile; $this->oprofile = $oprofile;
break; // don't iterate through aliases anymore break; // don't iterate through aliases anymore
} }

View File

@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/plugins/OpenID/openid.php'; require_once INSTALLDIR.'/plugins/OpenID/openid.php';
@ -86,8 +84,6 @@ class OpenidsettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
$user = common_current_user();
if (!common_config('openid', 'trusted_provider')) { if (!common_config('openid', 'trusted_provider')) {
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_openid_add', 'id' => 'form_settings_openid_add',
@ -115,7 +111,7 @@ class OpenidsettingsAction extends SettingsAction
} }
$oid = new User_openid(); $oid = new User_openid();
$oid->user_id = $user->id; $oid->user_id = $this->scoped->getID();
$cnt = $oid->find(); $cnt = $oid->find();
@ -123,7 +119,7 @@ class OpenidsettingsAction extends SettingsAction
// TRANS: Header on OpenID settings page. // TRANS: Header on OpenID settings page.
$this->element('h2', null, _m('HEADER','Remove OpenID')); $this->element('h2', null, _m('HEADER','Remove OpenID'));
if ($cnt == 1 && !$user->password) { if ($cnt == 1 && !$this->scoped->hasPassword()) {
$this->element('p', 'form_guide', $this->element('p', 'form_guide',
// TRANS: Form guide. // TRANS: Form guide.
@ -184,7 +180,7 @@ class OpenidsettingsAction extends SettingsAction
'this list to deny it access to your OpenID.')); 'this list to deny it access to your OpenID.'));
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$user_openid_trustroot = new User_openid_trustroot(); $user_openid_trustroot = new User_openid_trustroot();
$user_openid_trustroot->user_id=$user->id; $user_openid_trustroot->user_id = $this->scoped->getID();
if($user_openid_trustroot->find()) { if($user_openid_trustroot->find()) {
while($user_openid_trustroot->fetch()) { while($user_openid_trustroot->fetch()) {
$this->elementStart('li'); $this->elementStart('li');
@ -203,7 +199,7 @@ class OpenidsettingsAction extends SettingsAction
$this->submit('settings_openid_trustroots_action-submit', _m('BUTTON','Remove'), 'submit', 'remove_trustroots'); $this->submit('settings_openid_trustroots_action-submit', _m('BUTTON','Remove'), 'submit', 'remove_trustroots');
$this->elementEnd('fieldset'); $this->elementEnd('fieldset');
$prefs = User_openid_prefs::getKV('user_id', $user->id); $prefs = User_openid_prefs::getKV('user_id', $this->scoped->getID());
$this->elementStart('fieldset'); $this->elementStart('fieldset');
$this->element('legend', null, _m('LEGEND','Preferences')); $this->element('legend', null, _m('LEGEND','Preferences'));
@ -224,38 +220,29 @@ class OpenidsettingsAction extends SettingsAction
* *
* @return void * @return void
*/ */
function handlePost() protected function doPost()
{ {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->showForm(_m('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if ($this->arg('add')) { if ($this->arg('add')) {
if (common_config('openid', 'trusted_provider')) { if (common_config('openid', 'trusted_provider')) {
// TRANS: Form validation error if no OpenID providers can be added. // TRANS: Form validation error if no OpenID providers can be added.
$this->showForm(_m('Cannot add new providers.')); throw new ServerException(_m('Cannot add new providers.'));
} else { } else {
$result = oid_authenticate($this->trimmed('openid_url'), $result = oid_authenticate($this->trimmed('openid_url'), 'finishaddopenid');
'finishaddopenid');
if (is_string($result)) { // error message if (is_string($result)) { // error message
$this->showForm($result); throw new ServerException($result);
} }
return _('Added new provider.');
} }
} else if ($this->arg('remove')) { } else if ($this->arg('remove')) {
$this->removeOpenid(); return $this->removeOpenid();
} else if($this->arg('remove_trustroots')) { } else if($this->arg('remove_trustroots')) {
$this->removeTrustroots(); return $this->removeTrustroots();
} else if($this->arg('save_prefs')) { } else if($this->arg('save_prefs')) {
$this->savePrefs(); return $this->savePrefs();
} else {
// TRANS: Unexpected form validation error.
$this->showForm(_m('Something weird happened.'));
} }
// TRANS: Unexpected form validation error.
throw new ServerException(_m('No known action for POST.'));
} }
/** /**
@ -268,26 +255,20 @@ class OpenidsettingsAction extends SettingsAction
*/ */
function removeTrustroots() function removeTrustroots()
{ {
$user = common_current_user(); $trustroots = $this->arg('openid_trustroot', array());
$trustroots = $this->arg('openid_trustroot');
if($trustroots) {
foreach($trustroots as $trustroot) { foreach($trustroots as $trustroot) {
$user_openid_trustroot = User_openid_trustroot::pkeyGet( $user_openid_trustroot = User_openid_trustroot::pkeyGet(
array('user_id'=>$user->id, 'trustroot'=>$trustroot)); array('user_id'=>$this->scoped->getID(), 'trustroot'=>$trustroot));
if($user_openid_trustroot) { if($user_openid_trustroot) {
$user_openid_trustroot->delete(); $user_openid_trustroot->delete();
} else { } else {
// TRANS: Form validation error when trying to remove a non-existing trustroot. // TRANS: Form validation error when trying to remove a non-existing trustroot.
$this->showForm(_m('No such OpenID trustroot.')); throw new ClientException(_m('No such OpenID trustroot.'));
return;
} }
} }
// TRANS: Success message after removing trustroots. // TRANS: Success message after removing trustroots.
$this->showForm(_m('Trustroots removed.'), true); return _m('Trustroots removed.');
} else {
$this->showForm();
}
return;
} }
/** /**
@ -300,25 +281,19 @@ class OpenidsettingsAction extends SettingsAction
*/ */
function removeOpenid() function removeOpenid()
{ {
$openid_url = $this->trimmed('openid_url'); $oid = User_openid::getKV('canonical', $this->trimmed('openid_url'));
$oid = User_openid::getKV('canonical', $openid_url); if (!$oid instanceof User_openid) {
if (!$oid) {
// TRANS: Form validation error for a non-existing OpenID. // TRANS: Form validation error for a non-existing OpenID.
$this->showForm(_m('No such OpenID.')); throw new ClientException(_m('No such OpenID.'));
return;
} }
$cur = common_current_user(); if ($this->scoped->getID() !== $oid->user_id) {
if (!$cur || $oid->user_id != $cur->id) {
// TRANS: Form validation error if OpenID is connected to another user. // TRANS: Form validation error if OpenID is connected to another user.
$this->showForm(_m('That OpenID does not belong to you.')); throw new ClientException(_m('That OpenID does not belong to you.'));
return;
} }
$oid->delete(); $oid->delete();
// TRANS: Success message after removing an OpenID. // TRANS: Success message after removing an OpenID.
$this->showForm(_m('OpenID removed.'), true); return _m('OpenID removed.');
return;
} }
/** /**
@ -331,18 +306,12 @@ class OpenidsettingsAction extends SettingsAction
*/ */
function savePrefs() function savePrefs()
{ {
$cur = common_current_user();
if (empty($cur)) {
throw new ClientException(_("Not logged in."));
}
$orig = null; $orig = null;
$prefs = User_openid_prefs::getKV('user_id', $cur->id); $prefs = User_openid_prefs::getKV('user_id', $this->scoped->getID());
if (empty($prefs)) { if (!$prefs instanceof User_openid_prefs) {
$prefs = new User_openid_prefs(); $prefs = new User_openid_prefs();
$prefs->user_id = $cur->id; $prefs->user_id = $this->scoped->getID();
$prefs->created = common_sql_now(); $prefs->created = common_sql_now();
} else { } else {
$orig = clone($prefs); $orig = clone($prefs);
@ -350,13 +319,12 @@ class OpenidsettingsAction extends SettingsAction
$prefs->hide_profile_link = $this->booleanintstring('hide_profile_link'); $prefs->hide_profile_link = $this->booleanintstring('hide_profile_link');
if (empty($orig)) { if ($orig instanceof User_openid_prefs) {
$prefs->insert();
} else {
$prefs->update($orig); $prefs->update($orig);
} else {
$prefs->insert();
} }
$this->showForm(_m('OpenID preferences saved.'), true); return _m('OpenID preferences saved.');
return;
} }
} }

View File

@ -131,13 +131,15 @@ function oid_check_immediate($openid_url, $backto=null)
function oid_authenticate($openid_url, $returnto, $immediate=false) function oid_authenticate($openid_url, $returnto, $immediate=false)
{ {
if (!common_valid_http_url($openid_url)) {
throw new ClientException(_m('No valid URL provided for OpenID.'));
}
$consumer = oid_consumer(); $consumer = oid_consumer();
if (!$consumer) { if (!$consumer) {
// TRANS: OpenID plugin server error. // TRANS: OpenID plugin server error.
common_server_error(_m('Cannot instantiate OpenID consumer object.')); throw new ServerException(_m('Cannot instantiate OpenID consumer object.'));
return false;
} }
common_ensure_session(); common_ensure_session();
@ -148,12 +150,12 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
if (!$auth_request) { if (!$auth_request) {
common_log(LOG_ERR, __METHOD__ . ": mystery fail contacting $openid_url"); common_log(LOG_ERR, __METHOD__ . ": mystery fail contacting $openid_url");
// TRANS: OpenID plugin message. Given when an OpenID is not valid. // TRANS: OpenID plugin message. Given when an OpenID is not valid.
return _m('Not a valid OpenID.'); throw new ServerException(_m('Not a valid OpenID.'));
} else if (Auth_OpenID::isFailure($auth_request)) { } else if (Auth_OpenID::isFailure($auth_request)) {
common_log(LOG_ERR, __METHOD__ . ": OpenID fail to $openid_url: $auth_request->message"); common_log(LOG_ERR, __METHOD__ . ": OpenID fail to $openid_url: $auth_request->message");
// TRANS: OpenID plugin server error. Given when the OpenID authentication request fails. // TRANS: OpenID plugin server error. Given when the OpenID authentication request fails.
// TRANS: %s is the failure message. // TRANS: %s is the failure message.
return sprintf(_m('OpenID failure: %s.'), $auth_request->message); throw new ServerException(sprintf(_m('OpenID failure: %s.'), $auth_request->message));
} }
$sreg_request = Auth_OpenID_SRegRequest::build(// Required $sreg_request = Auth_OpenID_SRegRequest::build(// Required
@ -199,14 +201,12 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
$redirect_url = $auth_request->redirectURL($trust_root, $redirect_url = $auth_request->redirectURL($trust_root,
$process_url, $process_url,
$immediate); $immediate);
if (!$redirect_url) { if (Auth_OpenID::isFailure($redirect_url)) {
} else if (Auth_OpenID::isFailure($redirect_url)) {
// TRANS: OpenID plugin server error. Given when the OpenID authentication request cannot be redirected. // TRANS: OpenID plugin server error. Given when the OpenID authentication request cannot be redirected.
// TRANS: %s is the failure message. // TRANS: %s is the failure message.
return sprintf(_m('Could not redirect to server: %s.'), $redirect_url->message); throw new ServerException(sprintf(_m('Could not redirect to server: %s.'), $redirect_url->message));
} else {
common_redirect($redirect_url, 303);
} }
common_redirect($redirect_url, 303);
/* /*
} else { } else {
// Generate form markup and render it. // Generate form markup and render it.

View File

@ -87,6 +87,13 @@ class OpportunisticQueueManager extends DBQueueManager
$this->_fail($qi, true); // true here means "releaseOnly", so no error statistics since it's not an _error_ $this->_fail($qi, true); // true here means "releaseOnly", so no error statistics since it's not an _error_
} }
protected function _fail(Queue_item $qi, $releaseOnly=false)
{
parent::_fail($qi, $releaseOnly);
$this->_log(LOG_DEBUG, "[{$qi->transport}:item {$qi->id}] Ignoring this transport for the rest of this execution");
$this->ignoreTransport($qi->transport);
}
/** /**
* Takes care of running through the queue items, returning when * Takes care of running through the queue items, returning when
* the limits setup in __construct are met. * the limits setup in __construct are met.

View File

@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
class PollSettingsAction extends SettingsAction class PollSettingsAction extends SettingsAction
{ {
@ -56,143 +54,36 @@ class PollSettingsAction extends SettingsAction
return _m('Set your poll preferences'); return _m('Set your poll preferences');
} }
/** protected function getForm()
* Show the form for Poll
*
* @return void
*/
function showContent()
{ {
$user = common_current_user(); $prefs = User_poll_prefs::getKV('user_id', $this->scoped->getID());
$prefs = User_poll_prefs::getKV('user_id', $user->id);
$form = new PollPrefsForm($this, $prefs); $form = new PollPrefsForm($this, $prefs);
return $form;
$form->show();
} }
/** protected function doPost()
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handlePost()
{ {
$user = common_current_user(); $upp = User_poll_prefs::getKV('user_id', $this->scoped->getID());
$upp = User_poll_prefs::getKV('user_id', $user->id);
$orig = null; $orig = null;
if (!empty($upp)) { if ($upp instanceof User_poll_prefs) {
$orig = clone($upp); $orig = clone($upp);
} else { } else {
$upp = new User_poll_prefs(); $upp = new User_poll_prefs();
$upp->user_id = $user->id; $upp->user_id = $this->scoped->getID();
$upp->created = common_sql_now(); $upp->created = common_sql_now();
} }
$upp->hide_responses = $this->boolean('hide_responses'); $upp->hide_responses = $this->boolean('hide_responses');
$upp->modified = common_sql_now(); $upp->modified = common_sql_now();
if (!empty($orig)) { if ($orig instanceof User_poll_prefs) {
$upp->update($orig); $upp->update($orig);
} else { } else {
$upp->insert(); $upp->insert();
} }
// TRANS: Confirmation shown when user profile settings are saved. // TRANS: Confirmation shown when user profile settings are saved.
$this->showForm(_('Settings saved.'), true); return _('Settings saved.');
return;
}
}
class PollPrefsForm extends Form
{
var $prefs;
function __construct($out, $prefs)
{
parent::__construct($out);
$this->prefs = $prefs;
}
/**
* Visible or invisible data elements
*
* Display the form fields that make up the data of the form.
* Sub-classes should overload this to show their data.
*
* @return void
*/
function formData()
{
$this->elementStart('fieldset');
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->checkbox('hide_responses',
_('Do not deliver poll responses to my home timeline'),
(!empty($this->prefs) && $this->prefs->hide_responses));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
}
/**
* Buttons for form actions
*
* Submit and cancel buttons (or whatever)
* Sub-classes should overload this to show their own buttons.
*
* @return void
*/
function formActions()
{
$this->submit('submit', _('Save'));
}
/**
* ID of the form
*
* Should be unique on the page. Sub-classes should overload this
* to show their own IDs.
*
* @return int ID of the form
*/
function id()
{
return 'form_poll_prefs';
}
/**
* Action of the form.
*
* URL to post to. Should be overloaded by subclasses to give
* somewhere to post to.
*
* @return string URL to post to
*/
function action()
{
return common_local_url('pollsettings');
}
/**
* Class of the form. May include space-separated list of multiple classes.
*
* @return string the form's class
*/
function formClass()
{
return 'form_settings';
} }
} }

View File

@ -0,0 +1,87 @@
<?php
if (!defined('GNUSOCIAL')) { exit(1); }
class PollPrefsForm extends Form
{
function __construct(Action $out, User_poll_prefs $prefs=null)
{
parent::__construct($out);
$this->prefs = $prefs;
}
/**
* Visible or invisible data elements
*
* Display the form fields that make up the data of the form.
* Sub-classes should overload this to show their data.
*
* @return void
*/
function formData()
{
$this->elementStart('fieldset');
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->checkbox('hide_responses',
_('Do not deliver poll responses to my home timeline'),
($this->prefs instanceof User_poll_prefs && $this->prefs->hide_responses));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
}
/**
* Buttons for form actions
*
* Submit and cancel buttons (or whatever)
* Sub-classes should overload this to show their own buttons.
*
* @return void
*/
function formActions()
{
$this->submit('submit', _('Save'));
}
/**
* ID of the form
*
* Should be unique on the page. Sub-classes should overload this
* to show their own IDs.
*
* @return int ID of the form
*/
function id()
{
return 'form_poll_prefs';
}
/**
* Action of the form.
*
* URL to post to. Should be overloaded by subclasses to give
* somewhere to post to.
*
* @return string URL to post to
*/
function action()
{
return common_local_url('pollsettings');
}
/**
* Class of the form. May include space-separated list of multiple classes.
*
* @return string the form's class
*/
function formClass()
{
return 'form_settings';
}
}

View File

@ -157,7 +157,7 @@ class ConfirmfirstemailAction extends Action
$orig = clone($this->user); $orig = clone($this->user);
$this->user->password = common_munge_password($this->password, $this->user->id); $this->user->password = common_munge_password($this->password, $this->user->getProfile());
$this->user->update($orig); $this->user->update($orig);

View File

@ -25,7 +25,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
class MirrorSettingsAction extends SettingsAction class MirrorSettingsAction extends SettingsAction
{ {
@ -62,9 +62,8 @@ class MirrorSettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
$user = common_current_user();
$provider = $this->trimmed('provider'); $provider = $this->trimmed('provider');
if ($provider) { if (!empty($provider) || GNUsocial::isAjax()) {
$this->showAddFeedForm($provider); $this->showAddFeedForm($provider);
} else { } else {
$this->elementStart('div', array('id' => 'add-mirror')); $this->elementStart('div', array('id' => 'add-mirror'));
@ -72,7 +71,7 @@ class MirrorSettingsAction extends SettingsAction
$this->elementEnd('div'); $this->elementEnd('div');
$mirror = new SubMirror(); $mirror = new SubMirror();
$mirror->subscriber = $user->id; $mirror->subscriber = $this->scoped->getID();
if ($mirror->find()) { if ($mirror->find()) {
while ($mirror->fetch()) { while ($mirror->fetch()) {
$this->showFeedForm($mirror); $this->showFeedForm($mirror);
@ -87,14 +86,12 @@ class MirrorSettingsAction extends SettingsAction
$form->show(); $form->show();
} }
function showFeedForm($mirror) function showFeedForm(SubMirror $mirror)
{ {
$profile = Profile::getKV('id', $mirror->subscribed); $profile = Profile::getByID($mirror->subscribed);
if ($profile) {
$form = new EditMirrorForm($this, $profile); $form = new EditMirrorForm($this, $profile);
$form->show(); $form->show();
} }
}
function showAddFeedForm() function showAddFeedForm()
{ {
@ -112,41 +109,6 @@ class MirrorSettingsAction extends SettingsAction
$form->show(); $form->show();
} }
/**
*
* @param array $args
*
* @todo move the ajax display handling to common code
*/
function handle($args)
{
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
// TRANS: Title for page with form to add a mirror feed provider on.
$this->element('title', null, _m('Provider add'));
$this->elementEnd('head');
$this->elementStart('body');
$this->showAddFeedForm();
$this->elementEnd('body');
$this->endHTML();
} else {
return parent::handle($args);
}
}
/**
* Handle a POST request
*
* Muxes to different sub-functions based on which button was pushed
*
* @return void
*/
function handlePost()
{
}
/** /**
* Show the local navigation menu * Show the local navigation menu
* *

View File

@ -26,9 +26,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once __DIR__ . '/twitter.php'; require_once __DIR__ . '/twitter.php';
@ -387,12 +385,11 @@ class TwitterBridgePlugin extends Plugin
{ {
$n2s = Notice_to_status::getKV('notice_id', $notice->id); $n2s = Notice_to_status::getKV('notice_id', $notice->id);
if (!empty($n2s)) { if ($n2s instanceof Notice_to_status) {
$flink = Foreign_link::getByUserID($notice->profile_id, try {
TWITTER_SERVICE); // twitter service $flink = Foreign_link::getByUserID($notice->profile_id, TWITTER_SERVICE); // twitter service
} catch (NoResultException $e) {
if (empty($flink)) {
return true; return true;
} }
@ -424,15 +421,14 @@ class TwitterBridgePlugin extends Plugin
*/ */
function onEndFavorNotice(Profile $profile, Notice $notice) function onEndFavorNotice(Profile $profile, Notice $notice)
{ {
$flink = Foreign_link::getByUserID($profile->id, try {
TWITTER_SERVICE); // twitter service $flink = Foreign_link::getByUserID($profile->getID(), TWITTER_SERVICE); // twitter service
} catch (NoResultException $e) {
if (empty($flink)) {
return true; return true;
} }
if (!TwitterOAuthClient::isPackedToken($flink->credentials)) { if (!TwitterOAuthClient::isPackedToken($flink->credentials)) {
$this->log(LOG_INFO, "Skipping fave processing for {$profile->id} since link is not OAuth."); $this->log(LOG_INFO, "Skipping fave processing for {$profile->getID()} since link is not OAuth.");
return true; return true;
} }
@ -464,10 +460,9 @@ class TwitterBridgePlugin extends Plugin
*/ */
function onEndDisfavorNotice(Profile $profile, Notice $notice) function onEndDisfavorNotice(Profile $profile, Notice $notice)
{ {
$flink = Foreign_link::getByUserID($profile->id, try {
TWITTER_SERVICE); // twitter service $flink = Foreign_link::getByUserID($profile->getID(), TWITTER_SERVICE); // twitter service
} catch (NoResultException $e) {
if (empty($flink)) {
return true; return true;
} }
@ -516,16 +511,15 @@ class TwitterBridgePlugin extends Plugin
{ {
$fuser = null; $fuser = null;
try {
$flink = Foreign_link::getByUserID($profile->id, TWITTER_SERVICE); $flink = Foreign_link::getByUserID($profile->id, TWITTER_SERVICE);
if (!empty($flink)) {
$fuser = $flink->getForeignUser(); $fuser = $flink->getForeignUser();
if (!empty($fuser)) {
$links[] = array("href" => $fuser->uri, $links[] = array("href" => $fuser->uri,
"text" => sprintf(_("@%s on Twitter"), $fuser->nickname), "text" => sprintf(_("@%s on Twitter"), $fuser->nickname),
"image" => $this->path("icons/twitter-bird-white-on-blue.png")); "image" => $this->path("icons/twitter-bird-white-on-blue.png"));
} } catch (NoResultException $e) {
// no foreign link and/or user for Twitter on this profile ID
} }
return true; return true;
@ -569,16 +563,17 @@ class TwitterBridgePlugin extends Plugin
if( count($noticeArray) != 1 ) { break; } if( count($noticeArray) != 1 ) { break; }
$post = $noticeArray[0]; $post = $noticeArray[0];
try {
$flink = Foreign_link::getByUserID($post->profile_id, TWITTER_SERVICE); $flink = Foreign_link::getByUserID($post->profile_id, TWITTER_SERVICE);
if( $flink ) { // Our local user has registered Twitter Gateway
$fuser = Foreign_user::getForeignUser($flink->foreign_id, TWITTER_SERVICE); $fuser = Foreign_user::getForeignUser($flink->foreign_id, TWITTER_SERVICE);
if( $fuser ) { // Got nickname for local user's Twitter account
$action->element('meta', array('name' => 'twitter:creator', $action->element('meta', array('name' => 'twitter:creator',
'content' => '@'.$fuser->nickname)); 'content' => '@'.$fuser->nickname));
} } catch (NoResultException $e) {
// no foreign link and/or user for Twitter on this profile ID
} }
break; break;
default: break; default:
break;
} }
return true; return true;

View File

@ -28,9 +28,10 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
require_once dirname(__DIR__) . '/twitter.php'; require_once dirname(__DIR__) . '/twitter.php';
require_once INSTALLDIR . '/lib/oauthclient.php';
/** /**
* Class for doing OAuth authentication against Twitter * Class for doing OAuth authentication against Twitter
@ -48,61 +49,39 @@ require_once dirname(__DIR__) . '/twitter.php';
* @link http://status.net/ * @link http://status.net/
* *
*/ */
class TwitterauthorizationAction extends Action class TwitterauthorizationAction extends FormAction
{ {
var $twuid = null; var $twuid = null;
var $tw_fields = null; var $tw_fields = null;
var $access_token = null; var $access_token = null;
var $signin = null;
var $verifier = null; var $verifier = null;
/** protected $needLogin = false; // authorization page can also be used to create a new user
* Initialize class members. Looks for 'oauth_token' parameter.
*
* @param array $args misc. arguments
*
* @return boolean true
*/
function prepare($args)
{
parent::prepare($args);
$this->signin = $this->boolean('signin'); protected function doPreparation()
{
$this->oauth_token = $this->arg('oauth_token'); $this->oauth_token = $this->arg('oauth_token');
$this->verifier = $this->arg('oauth_verifier'); $this->verifier = $this->arg('oauth_verifier');
return true; if ($this->scoped instanceof Profile) {
} try {
$flink = Foreign_link::getByUserID($this->scoped->getID(), TWITTER_SERVICE);
/** $fuser = $flink->getForeignUser();
* Handler method
*
* @param array $args is ignored since it's now passed in in prepare()
*
* @return nothing
*/
function handle($args)
{
parent::handle($args);
if (common_logged_in()) {
$user = common_current_user();
$flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
// If there's already a foreign link record and a foreign user // If there's already a foreign link record and a foreign user
// (no exceptions were thrown when fetching either of them...)
// it means the accounts are already linked, and this is unecessary. // it means the accounts are already linked, and this is unecessary.
// So go back. // So go back.
if (isset($flink)) {
$fuser = $flink->getForeignUser();
if (!empty($fuser)) {
common_redirect(common_local_url('twittersettings')); common_redirect(common_local_url('twittersettings'));
} catch (NoResultException $e) {
// but if we don't have a foreign user linked, let's continue authorization procedure.
} }
} }
} }
if ($_SERVER['REQUEST_METHOD'] == 'POST') { protected function doPost()
{
// User was not logged in to StatusNet before // User was not logged in to StatusNet before
$this->twuid = $this->trimmed('twuid'); $this->twuid = $this->trimmed('twuid');
@ -112,63 +91,39 @@ class TwitterauthorizationAction extends Action
$this->access_token = new OAuthToken($this->trimmed('access_token_key'), $this->trimmed('access_token_secret')); $this->access_token = new OAuthToken($this->trimmed('access_token_key'), $this->trimmed('access_token_secret'));
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->showForm(_m('There was a problem with your session token. Try again, please.'));
return;
}
if ($this->arg('create')) { if ($this->arg('create')) {
common_debug('TwitterBridgeDebug - POST with create');
if (!$this->boolean('license')) { if (!$this->boolean('license')) {
// TRANS: Form validation error displayed when the checkbox to agree to the license has not been checked. // TRANS: Form validation error displayed when the checkbox to agree to the license has not been checked.
$this->showForm(_m('You cannot register if you do not agree to the license.'), throw new ClientException(_m('You cannot register if you do not agree to the license.'));
$this->trimmed('newname'));
return;
} }
$this->createNewUser(); return $this->createNewUser();
} else if ($this->arg('connect')) { } elseif ($this->arg('connect')) {
$this->connectNewUser(); common_debug('TwitterBridgeDebug - POST with connect');
} else { return $this->connectNewUser();
common_debug('Twitter bridge - ' . print_r($this->args, true));
// TRANS: Form validation error displayed when an unhandled error occurs.
$this->showForm(_m('Something weird happened.'),
$this->trimmed('newname'));
} }
} else {
// $this->oauth_token is only populated once Twitter authorizes our
// request token. If it's empty we're at the beginning of the auth
// process
if (empty($this->oauth_token)) { common_debug('TwitterBridgeDebug - ' . print_r($this->args, true));
$this->authorizeRequestToken(); // TRANS: Form validation error displayed when an unhandled error occurs.
} else { throw new ClientException(_m('No known action for POST.'));
$this->saveAccessToken();
}
}
} }
/** /**
* Asks Twitter for a request token, and then redirects to Twitter * Asks Twitter for a request token, and then redirects to Twitter
* to authorize it. * to authorize it.
*
* @return nothing
*/ */
function authorizeRequestToken() protected function authorizeRequestToken()
{ {
try { try {
// Get a new request token and authorize it // Get a new request token and authorize it
$client = new TwitterOAuthClient(); $client = new TwitterOAuthClient();
$req_tok = $client->getRequestToken(); $req_tok = $client->getTwitterRequestToken();
// Sock the request token away in the session temporarily // Sock the request token away in the session temporarily
$_SESSION['twitter_request_token'] = $req_tok->key; $_SESSION['twitter_request_token'] = $req_tok->key;
$_SESSION['twitter_request_token_secret'] = $req_tok->secret; $_SESSION['twitter_request_token_secret'] = $req_tok->secret;
$auth_link = $client->getAuthorizeLink($req_tok, $this->signin); $auth_link = $client->getTwitterAuthorizeLink($req_tok, $this->boolean('signin'));
} catch (OAuthClientException $e) { } catch (OAuthClientException $e) {
$msg = sprintf( $msg = sprintf(
'OAuth client error - code: %1s, msg: %2s', 'OAuth client error - code: %1s, msg: %2s',
@ -176,10 +131,8 @@ class TwitterauthorizationAction extends Action
$e->getMessage() $e->getMessage()
); );
common_log(LOG_INFO, 'Twitter bridge - ' . $msg); common_log(LOG_INFO, 'Twitter bridge - ' . $msg);
$this->serverError(
// TRANS: Server error displayed when linking to a Twitter account fails. // TRANS: Server error displayed when linking to a Twitter account fails.
_m('Could not link your Twitter account.') throw new ServerException(_m('Could not link your Twitter account.'));
);
} }
common_redirect($auth_link); common_redirect($auth_link);
@ -197,25 +150,19 @@ class TwitterauthorizationAction extends Action
// token we sent them // token we sent them
if ($_SESSION['twitter_request_token'] != $this->oauth_token) { if ($_SESSION['twitter_request_token'] != $this->oauth_token) {
$this->serverError(
// TRANS: Server error displayed when linking to a Twitter account fails because of an incorrect oauth_token. // TRANS: Server error displayed when linking to a Twitter account fails because of an incorrect oauth_token.
_m('Could not link your Twitter account: oauth_token mismatch.') throw new ServerException(_m('Could not link your Twitter account: oauth_token mismatch.'));
);
} }
$twitter_user = null; $twitter_user = null;
try { try {
$client = new TwitterOAuthClient($_SESSION['twitter_request_token'], $_SESSION['twitter_request_token_secret']);
$client = new TwitterOAuthClient($_SESSION['twitter_request_token'],
$_SESSION['twitter_request_token_secret']);
// Exchange the request token for an access token // Exchange the request token for an access token
$atok = $client->getTwitterAccessToken($this->verifier);
$atok = $client->getAccessToken($this->verifier);
// Test the access token and get the user's Twitter info // Test the access token and get the user's Twitter info
$client = new TwitterOAuthClient($atok->key, $atok->secret); $client = new TwitterOAuthClient($atok->key, $atok->secret);
$twitter_user = $client->verifyCredentials(); $twitter_user = $client->verifyCredentials();
@ -226,17 +173,14 @@ class TwitterauthorizationAction extends Action
$e->getMessage() $e->getMessage()
); );
common_log(LOG_INFO, 'Twitter bridge - ' . $msg); common_log(LOG_INFO, 'Twitter bridge - ' . $msg);
$this->serverError(
// TRANS: Server error displayed when linking to a Twitter account fails. // TRANS: Server error displayed when linking to a Twitter account fails.
_m('Could not link your Twitter account.') throw new ServerException(_m('Could not link your Twitter account.'));
);
} }
if (common_logged_in()) { if ($this->scoped instanceof Profile) {
// Save the access token and Twitter user info // Save the access token and Twitter user info
$user = common_current_user(); $this->saveForeignLink($this->scoped->getID(), $twitter_user->id, $atok);
$this->saveForeignLink($user->id, $twitter_user->id, $atok);
save_twitter_user($twitter_user->id, $twitter_user->screen_name); save_twitter_user($twitter_user->id, $twitter_user->screen_name);
} else { } else {
@ -245,7 +189,7 @@ class TwitterauthorizationAction extends Action
$this->tw_fields = array("screen_name" => $twitter_user->screen_name, $this->tw_fields = array("screen_name" => $twitter_user->screen_name,
"fullname" => $twitter_user->name); "fullname" => $twitter_user->name);
$this->access_token = $atok; $this->access_token = $atok;
$this->tryLogin(); return $this->tryLogin();
} }
// Clean up the the mess we made in the session // Clean up the the mess we made in the session
@ -297,24 +241,20 @@ class TwitterauthorizationAction extends Action
$flink_id = $flink->insert(); $flink_id = $flink->insert();
// We want to make sure we got a numerical >0 value, not just failed the insert (which would be === false)
if (empty($flink_id)) { if (empty($flink_id)) {
common_log_db_error($flink, 'INSERT', __FILE__); common_log_db_error($flink, 'INSERT', __FILE__);
// TRANS: Server error displayed when linking to a Twitter account fails. // TRANS: Server error displayed when linking to a Twitter account fails.
$this->serverError(_m('Could not link your Twitter account.')); throw new ServerException(_m('Could not link your Twitter account.'));
} }
return $flink_id; return $flink_id;
} }
function showPageNotice() function getInstructions()
{ {
if ($this->error) {
$this->element('div', array('class' => 'error'), $this->error);
} else {
$this->element('div', 'instructions',
// TRANS: Page instruction. %s is the StatusNet sitename. // TRANS: Page instruction. %s is the StatusNet sitename.
sprintf(_m('This is the first time you have logged into %s so we must connect your Twitter account to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name'))); return sprintf(_m('This is the first time you have logged into %s so we must connect your Twitter account to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name'));
}
} }
function title() function title()
@ -323,16 +263,20 @@ class TwitterauthorizationAction extends Action
return _m('Twitter Account Setup'); return _m('Twitter Account Setup');
} }
function showForm($error=null, $username=null) public function showPage()
{ {
$this->error = $error; // $this->oauth_token is only populated once Twitter authorizes our
$this->username = $username; // request token. If it's empty we're at the beginning of the auth
// process
$this->showPage(); if (empty($this->error)) {
if (empty($this->oauth_token)) {
// authorizeRequestToken either throws an exception or redirects
$this->authorizeRequestToken();
} else {
$this->saveAccessToken();
}
} }
function showPage()
{
parent::showPage(); parent::showPage();
} }
@ -343,11 +287,6 @@ class TwitterauthorizationAction extends Action
*/ */
function showContent() function showContent()
{ {
if (!empty($this->message_text)) {
$this->element('p', null, $this->message);
return;
}
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_twitter_connect', 'id' => 'form_settings_twitter_connect',
'class' => 'form_settings', 'class' => 'form_settings',
@ -363,8 +302,8 @@ class TwitterauthorizationAction extends Action
$this->hidden('tw_fields_name', $this->tw_fields['fullname']); $this->hidden('tw_fields_name', $this->tw_fields['fullname']);
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
// Don't allow new account creation if site is flagged as invite only // Only allow new account creation if site is not flagged invite-only
if (common_config('site', 'inviteonly') == false) { if (!common_config('site', 'inviteonly')) {
$this->elementStart('fieldset'); $this->elementStart('fieldset');
$this->element('legend', null, $this->element('legend', null,
// TRANS: Fieldset legend. // TRANS: Fieldset legend.
@ -380,7 +319,7 @@ class TwitterauthorizationAction extends Action
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label. // TRANS: Field label.
$this->input('newname', _m('New nickname'), $this->input('newname', _m('New nickname'),
($this->username) ? $this->username : '', $this->username ?: '',
// TRANS: Field title for nickname field. // TRANS: Field title for nickname field.
_m('1-64 lowercase letters or numbers, no punctuation or spaces.')); _m('1-64 lowercase letters or numbers, no punctuation or spaces.'));
$this->elementEnd('li'); $this->elementEnd('li');
@ -479,46 +418,43 @@ class TwitterauthorizationAction extends Action
return ''; return '';
} }
function message($msg) protected function createNewUser()
{
$this->message_text = $msg;
$this->showPage();
}
function createNewUser()
{ {
common_debug('TwitterBridgeDebug - createNewUser');
if (!Event::handle('StartRegistrationTry', array($this))) { if (!Event::handle('StartRegistrationTry', array($this))) {
return; common_debug('TwitterBridgeDebug - StartRegistrationTry failed');
// TRANS: Client error displayed when trying to create a new user but a plugin aborted the process.
throw new ClientException(_m('Registration of new user was aborted, maybe you failed a captcha?'));
} }
if (common_config('site', 'closed')) { if (common_config('site', 'closed')) {
common_debug('TwitterBridgeDebug - site is closed for registrations');
// TRANS: Client error displayed when trying to create a new user while creating new users is not allowed. // TRANS: Client error displayed when trying to create a new user while creating new users is not allowed.
$this->clientError(_m('Registration not allowed.')); throw new ClientException(_m('Registration not allowed.'));
} }
$invite = null; $invite = null;
if (common_config('site', 'inviteonly')) { if (common_config('site', 'inviteonly')) {
common_debug('TwitterBridgeDebug - site is inviteonly');
$code = $_SESSION['invitecode']; $code = $_SESSION['invitecode'];
if (empty($code)) { if (empty($code)) {
// TRANS: Client error displayed when trying to create a new user while creating new users is not allowed. // TRANS: Client error displayed when trying to create a new user while creating new users is not allowed.
$this->clientError(_m('Registration not allowed.')); throw new ClientException(_m('Registration not allowed.'));
} }
$invite = Invitation::getKV($code); $invite = Invitation::getKV('code', $code);
if (empty($invite)) { if (!$invite instanceof Invite) {
common_debug('TwitterBridgeDebug - and we failed the invite code test');
// TRANS: Client error displayed when trying to create a new user with an invalid invitation code. // TRANS: Client error displayed when trying to create a new user with an invalid invitation code.
$this->clientError(_m('Not a valid invitation code.')); throw new ClientException(_m('Not a valid invitation code.'));
} }
} }
try { common_debug('TwitterBridgeDebug - trying our nickname: '.$this->trimmed('newname'));
// Nickname::normalize throws exception if the nickname is taken
$nickname = Nickname::normalize($this->trimmed('newname'), true); $nickname = Nickname::normalize($this->trimmed('newname'), true);
} catch (NicknameException $e) {
$this->showForm($e->getMessage());
return;
}
$fullname = trim($this->tw_fields['fullname']); $fullname = trim($this->tw_fields['fullname']);
@ -533,23 +469,18 @@ class TwitterauthorizationAction extends Action
$args['email'] = $email; $args['email'] = $email;
} }
try { common_debug('TwitterBridgeDebug - registering user with args:'.var_export($args,true));
$user = User::register($args); $user = User::register($args);
} catch (Exception $e) {
$this->serverError($e->getMessage());
}
$result = $this->saveForeignLink($user->id, common_debug('TwitterBridgeDebug - registered the user and saving foreign link for '.$user->id);
$this->saveForeignLink($user->id,
$this->twuid, $this->twuid,
$this->access_token); $this->access_token);
common_debug('TwitterBridgeDebug - saving twitter user after creating new local user '.$user->id);
save_twitter_user($this->twuid, $this->tw_fields['screen_name']); save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
if (!$result) {
// TRANS: Server error displayed when connecting a user to a Twitter user has failed.
$this->serverError(_m('Error connecting user to Twitter.'));
}
common_set_user($user); common_set_user($user);
common_real_login(true); common_real_login(true);
@ -569,28 +500,23 @@ class TwitterauthorizationAction extends Action
if (!common_check_user($nickname, $password)) { if (!common_check_user($nickname, $password)) {
// TRANS: Form validation error displayed when connecting an existing user to a Twitter user fails because // TRANS: Form validation error displayed when connecting an existing user to a Twitter user fails because
// TRANS: the provided username and/or password are incorrect. // TRANS: the provided username and/or password are incorrect.
$this->showForm(_m('Invalid username or password.')); throw new ClientException(_m('Invalid username or password.'));
return;
} }
$user = User::getKV('nickname', $nickname); $user = User::getKV('nickname', $nickname);
if (!empty($user)) { if ($user instanceof User) {
common_debug('TwitterBridge Plugin - ' . common_debug('TwitterBridge Plugin - ' .
"Legit user to connect to Twitter: $nickname"); "Legit user to connect to Twitter: $nickname");
} }
$result = $this->saveForeignLink($user->id, // throws exception on failure
$this->saveForeignLink($user->id,
$this->twuid, $this->twuid,
$this->access_token); $this->access_token);
save_twitter_user($this->twuid, $this->tw_fields['screen_name']); save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
if (!$result) {
// TRANS: Server error displayed connecting a user to a Twitter user has failed.
$this->serverError(_m('Error connecting user to Twitter.'));
}
common_debug('TwitterBridge Plugin - ' . common_debug('TwitterBridge Plugin - ' .
"Connected Twitter user $this->twuid to local user $user->id"); "Connected Twitter user $this->twuid to local user $user->id");
@ -618,34 +544,30 @@ class TwitterauthorizationAction extends Action
common_redirect(common_local_url('twittersettings'), 303); common_redirect(common_local_url('twittersettings'), 303);
} }
function tryLogin() protected function tryLogin()
{ {
common_debug('TwitterBridge Plugin - ' . common_debug('TwitterBridge Plugin - ' .
"Trying login for Twitter user $this->twuid."); "Trying login for Twitter user $this->twuid.");
$flink = Foreign_link::getByForeignID($this->twuid, try {
TWITTER_SERVICE); $flink = Foreign_link::getByForeignID($this->twuid, TWITTER_SERVICE);
if (!empty($flink)) {
$user = $flink->getUser(); $user = $flink->getUser();
if (!empty($user)) {
common_debug('TwitterBridge Plugin - ' . common_debug('TwitterBridge Plugin - ' .
"Logged in Twitter user $flink->foreign_id as user $user->id ($user->nickname)"); "Logged in Twitter user $flink->foreign_id as user $user->id ($user->nickname)");
common_set_user($user); common_set_user($user);
common_real_login(true); common_real_login(true);
$this->goHome($user->nickname); $this->goHome($user->nickname);
} catch (NoResultException $e) {
// Either no Foreign_link was found or not the user connected to it.
// Let's just continue to allow creating or logging in as a new user.
} }
common_debug("TwitterBridge Plugin - No flink found for twuid: {$this->twuid} - new user");
} else { // FIXME: what do we want to do here? I forgot
return;
common_debug('TwitterBridge Plugin - ' . throw new ServerException(_m('No foreign link found for Twitter user'));
"No flink found for twuid: $this->twuid - new user");
$this->showForm(null, $this->bestNewNickname());
}
} }
function goHome($nickname) function goHome($nickname)

View File

@ -28,9 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once dirname(__DIR__) . '/twitter.php'; require_once dirname(__DIR__) . '/twitter.php';
@ -46,20 +44,8 @@ require_once dirname(__DIR__) . '/twitter.php';
* *
* @see SettingsAction * @see SettingsAction
*/ */
class TwitterloginAction extends Action class TwitterloginAction extends LoginAction
{ {
function handle($args)
{
parent::handle($args);
if (common_is_real_login()) {
// TRANS: Client error displayed when trying to log in using Twitter while already logged in to StatusNet.
$this->clientError(_m('Already logged in.'));
}
$this->showPage();
}
function title() function title()
{ {
// TRANS: Title for login using Twitter page. // TRANS: Title for login using Twitter page.
@ -72,15 +58,6 @@ class TwitterloginAction extends Action
return _m('Login with your Twitter account'); return _m('Login with your Twitter account');
} }
function showPageNotice()
{
$instr = $this->getInstructions();
$output = common_markup_to_html($instr);
$this->elementStart('div', 'instructions');
$this->raw($output);
$this->elementEnd('div');
}
function showContent() function showContent()
{ {
$this->elementStart('a', array('href' => common_local_url('twitterauthorization', $this->elementStart('a', array('href' => common_local_url('twitterauthorization',

View File

@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once dirname(__DIR__) . '/twitter.php'; require_once dirname(__DIR__) . '/twitter.php';
@ -46,6 +44,18 @@ require_once dirname(__DIR__) . '/twitter.php';
*/ */
class TwittersettingsAction extends ProfileSettingsAction class TwittersettingsAction extends ProfileSettingsAction
{ {
protected $flink = null;
protected $fuser = null;
protected function doPreparation()
{
try {
$this->flink = Foreign_link::getByUserID($this->scoped->getID(), TWITTER_SERVICE);
$this->fuser = $this->flink->getForeignUser();
} catch (NoResultException $e) {
// No foreign link found for this user!
}
}
/** /**
* Title of the page * Title of the page
* *
@ -81,19 +91,6 @@ class TwittersettingsAction extends ProfileSettingsAction
*/ */
function showContent() function showContent()
{ {
$user = common_current_user();
$profile = $user->getProfile();
$fuser = null;
$flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
if (!empty($flink)) {
$fuser = $flink->getForeignUser();
}
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_twitter', 'id' => 'form_settings_twitter',
'class' => 'form_settings', 'class' => 'form_settings',
@ -104,21 +101,11 @@ class TwittersettingsAction extends ProfileSettingsAction
$this->elementStart('fieldset', array('id' => 'settings_twitter_account')); $this->elementStart('fieldset', array('id' => 'settings_twitter_account'));
if (empty($fuser)) { if ($this->fuser instanceof Foreign_user) {
$this->elementStart('ul', 'form_data');
$this->elementStart('li', array('id' => 'settings_twitter_login_button'));
$this->element('a', array('href' => common_local_url('twitterauthorization')),
// TRANS: Link description to connect to a Twitter account.
'Connect my Twitter account');
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
} else {
// TRANS: Fieldset legend. // TRANS: Fieldset legend.
$this->element('legend', null, _m('Twitter account')); $this->element('legend', null, _m('Twitter account'));
$this->elementStart('p', array('id' => 'form_confirmed')); $this->elementStart('p', array('id' => 'form_confirmed'));
$this->element('a', array('href' => $fuser->uri), $fuser->nickname); $this->element('a', array('href' => $this->fuser->uri), $this->fuser->nickname);
$this->elementEnd('p'); $this->elementEnd('p');
$this->element('p', 'form_note', $this->element('p', 'form_note',
// TRANS: Form note when a Twitter account has been connected. // TRANS: Form note when a Twitter account has been connected.
@ -130,7 +117,7 @@ class TwittersettingsAction extends ProfileSettingsAction
// TRANS: Fieldset legend. // TRANS: Fieldset legend.
$this->element('legend', null, _m('Disconnect my account from Twitter')); $this->element('legend', null, _m('Disconnect my account from Twitter'));
if (!$user->password) { if (!$this->scoped->hasPassword()) {
$this->elementStart('p', array('class' => 'form_guide')); $this->elementStart('p', array('class' => 'form_guide'));
// TRANS: Form guide. %s is a URL to the password settings. // TRANS: Form guide. %s is a URL to the password settings.
// TRANS: This message contains a Markdown link in the form [description](link). // TRANS: This message contains a Markdown link in the form [description](link).
@ -165,25 +152,19 @@ class TwittersettingsAction extends ProfileSettingsAction
$this->checkbox('noticesend', $this->checkbox('noticesend',
// TRANS: Checkbox label. // TRANS: Checkbox label.
_m('Automatically send my notices to Twitter.'), _m('Automatically send my notices to Twitter.'),
($flink) ? $this->flink->noticesync & FOREIGN_NOTICE_SEND);
($flink->noticesync & FOREIGN_NOTICE_SEND) :
true);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
$this->checkbox('replysync', $this->checkbox('replysync',
// TRANS: Checkbox label. // TRANS: Checkbox label.
_m('Send local "@" replies to Twitter.'), _m('Send local "@" replies to Twitter.'),
($flink) ? $this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY);
($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) :
true);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
$this->checkbox('friendsync', $this->checkbox('friendsync',
// TRANS: Checkbox label. // TRANS: Checkbox label.
_m('Subscribe to my Twitter friends here.'), _m('Subscribe to my Twitter friends here.'),
($flink) ? $this->flink->friendsync & FOREIGN_FRIEND_RECV);
($flink->friendsync & FOREIGN_FRIEND_RECV) :
false);
$this->elementEnd('li'); $this->elementEnd('li');
if (common_config('twitterimport','enabled')) { if (common_config('twitterimport','enabled')) {
@ -191,30 +172,36 @@ class TwittersettingsAction extends ProfileSettingsAction
$this->checkbox('noticerecv', $this->checkbox('noticerecv',
// TRANS: Checkbox label. // TRANS: Checkbox label.
_m('Import my friends timeline.'), _m('Import my friends timeline.'),
($flink) ? $this->flink->noticesync & FOREIGN_NOTICE_RECV);
($flink->noticesync & FOREIGN_NOTICE_RECV) :
false);
$this->elementEnd('li'); $this->elementEnd('li');
} else { } else {
// preserve setting even if bidrection bridge toggled off // preserve setting even if bidrection bridge toggled off
if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) { if ($this->flink->noticesync & FOREIGN_NOTICE_RECV) {
$this->hidden('noticerecv', true, 'noticerecv'); $this->hidden('noticerecv', true, 'noticerecv');
} }
} }
$this->elementEnd('ul'); $this->elementEnd('ul');
if ($flink) { if ($this->flink instanceof Foreign_link) {
// TRANS: Button text for saving Twitter integration settings. // TRANS: Button text for saving Twitter integration settings.
$this->submit('save', _m('BUTTON','Save')); $this->submit('save', _m('BUTTON','Save'));
} else { } else {
// TRANS: Button text for adding Twitter integration. // TRANS: Button text for adding Twitter integration.
$this->submit('add', _m('BUTTON','Add')); $this->submit('add', _m('BUTTON','Add'));
} }
} else {
$this->elementStart('ul', 'form_data');
$this->elementStart('li', array('id' => 'settings_twitter_login_button'));
$this->element('a', array('href' => common_local_url('twitterauthorization')),
// TRANS: Link description to connect to a Twitter account.
'Connect my Twitter account');
$this->elementEnd('li');
$this->elementEnd('ul');
}
$this->elementEnd('fieldset'); $this->elementEnd('fieldset');
}
$this->elementEnd('form'); $this->elementEnd('form');
} }
@ -229,25 +216,15 @@ class TwittersettingsAction extends ProfileSettingsAction
* *
* @return void * @return void
*/ */
function handlePost() protected function doPost()
{ {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->showForm(_m('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if ($this->arg('save')) { if ($this->arg('save')) {
$this->savePreferences(); return $this->savePreferences();
} else if ($this->arg('disconnect')) { } else if ($this->arg('disconnect')) {
$this->removeTwitterAccount(); return $this->removeTwitterAccount();
} else {
// TRANS: Client error displayed when the submitted form contains unexpected data.
$this->showForm(_m('Unexpected form submission.'));
} }
// TRANS: Client error displayed when the submitted form contains unexpected data.
throw new ClientException(_m('Unexpected form submission.'));
} }
/** /**
@ -255,26 +232,26 @@ class TwittersettingsAction extends ProfileSettingsAction
* *
* @return void * @return void
*/ */
function removeTwitterAccount() protected function removeTwitterAccount()
{ {
$user = common_current_user(); if (!$this->flink instanceof Foreign_link) {
$flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE); // TRANS: Error message possibly displayed when trying to remove a connected Twitter account when there isn't one connected.
throw new AlreadyFulfilledException(_m('No Twitter connection to remove.'));
if (empty($flink)) {
// TRANS: Client error displayed when trying to remove a connected Twitter account when there isn't one connected.
$this->clientError(_m('No Twitter connection to remove.'));
} }
$result = $flink->safeDelete(); $result = $this->flink->safeDelete();
if (empty($result)) { if ($result === false) {
common_log_db_error($flink, 'DELETE', __FILE__); common_log_db_error($this->flink, 'DELETE', __FILE__);
// TRANS: Server error displayed when trying to remove a connected Twitter account fails. // TRANS: Server error displayed when trying to remove a connected Twitter account fails.
$this->serverError(_m('Could not remove Twitter user.')); throw new ServerException(_m('Could not remove Twitter user.'));
} }
$this->flink = null;
$this->fuser = null;
// TRANS: Success message displayed after disconnecting a Twitter account. // TRANS: Success message displayed after disconnecting a Twitter account.
$this->showForm(_m('Twitter account disconnected.'), true); return _m('Twitter account disconnected.');
} }
/** /**
@ -282,43 +259,36 @@ class TwittersettingsAction extends ProfileSettingsAction
* *
* @return void * @return void
*/ */
function savePreferences() protected function savePreferences()
{ {
$noticesend = $this->boolean('noticesend'); $noticesend = $this->boolean('noticesend');
$noticerecv = $this->boolean('noticerecv'); $noticerecv = $this->boolean('noticerecv');
$friendsync = $this->boolean('friendsync'); $friendsync = $this->boolean('friendsync');
$replysync = $this->boolean('replysync'); $replysync = $this->boolean('replysync');
$user = common_current_user(); if (!$this->flink instanceof Foreign_link) {
$flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE); common_log_db_error($this->flink, 'SELECT', __FILE__);
if (empty($flink)) {
common_log_db_error($flink, 'SELECT', __FILE__);
// @todo FIXME: Shouldn't this be a serverError()?
// TRANS: Server error displayed when saving Twitter integration preferences fails. // TRANS: Server error displayed when saving Twitter integration preferences fails.
$this->showForm(_m('Could not save Twitter preferences.')); throw new ServerException(_m('Your account is not linked to Twitter.'));
return;
} }
$original = clone($flink); $original = clone($this->flink);
$wasReceiving = (bool)($original->noticesync & FOREIGN_NOTICE_RECV); $wasReceiving = (bool)($original->noticesync & FOREIGN_NOTICE_RECV);
$flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync); $this->flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
$result = $flink->update($original); $result = $this->flink->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($flink, 'UPDATE', __FILE__); common_log_db_error($this->flink, 'UPDATE', __FILE__);
// @todo FIXME: Shouldn't this be a serverError()?
// TRANS: Server error displayed when saving Twitter integration preferences fails. // TRANS: Server error displayed when saving Twitter integration preferences fails.
$this->showForm(_m('Could not save Twitter preferences.')); throw new ServerException(_m('Could not save Twitter preferences.'));
return;
} }
if ($wasReceiving xor $noticerecv) { if ($wasReceiving xor $noticerecv) {
$this->notifyDaemon($flink->foreign_id, $noticerecv); $this->notifyDaemon($this->flink->foreign_id, $noticerecv);
} }
// TRANS: Success message after saving Twitter integration preferences. // TRANS: Success message after saving Twitter integration preferences.
$this->showForm(_m('Twitter preferences saved.'), true); return _m('Twitter preferences saved.');
} }
/** /**

View File

@ -104,6 +104,7 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
return $flinks; return $flinks;
} }
// FIXME: make it so we can force a Foreign_link here without colliding with parent
function childTask($flink) { function childTask($flink) {
// Each child ps needs its own DB connection // Each child ps needs its own DB connection
@ -124,7 +125,7 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
unset($_DB_DATAOBJECT['CONNECTIONS']); unset($_DB_DATAOBJECT['CONNECTIONS']);
} }
function fetchTwitterFriends($flink) function fetchTwitterFriends(Foreign_link $flink)
{ {
$friends = array(); $friends = array();
@ -192,8 +193,14 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
return $friends; return $friends;
} }
function subscribeTwitterFriends($flink) function subscribeTwitterFriends(Foreign_link $flink)
{ {
try {
$profile = $flink->getProfile();
} catch (NoResultException $e) {
common_log(LOG_WARNING, 'Foreign_link has no matching local profile for local ID: '.$flink->user_id);
}
$friends = $this->fetchTwitterFriends($flink); $friends = $this->fetchTwitterFriends($flink);
if (empty($friends)) { if (empty($friends)) {
@ -203,8 +210,6 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
return false; return false;
} }
$profile = $flink->getProfile();
foreach ($friends as $friend) { foreach ($friends as $friend) {
$friend_name = $friend->screen_name; $friend_name = $friend->screen_name;
@ -219,24 +224,19 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
continue; continue;
} }
// Check to see if there's a related local user // Check to see if there's a related local user and try to subscribe
try {
$friend_flink = Foreign_link::getByForeignID($friend_id, $friend_flink = Foreign_link::getByForeignID($friend_id, TWITTER_SERVICE);
TWITTER_SERVICE);
if (!empty($friend_flink)) {
// Get associated user and subscribe her // Get associated user and subscribe her
$friend_profile = $friend_flink->getProfile();
$friend_profile = Profile::getKV('id', $friend_flink->user_id);
if ($friend_profile instanceof Profile) {
try {
$other = Profile::getKV('id', $invites->user_id);
Subscription::start($profile, $friend_profile); Subscription::start($profile, $friend_profile);
common_log(LOG_INFO, common_log(LOG_INFO,
$this->name() . ' - Subscribed ' . $this->name() . ' - Subscribed ' .
"{$friend_profile->nickname} to {$profile->nickname}."); "{$friend_profile->nickname} to {$profile->nickname}.");
} catch (NoResultException $e) {
// either no foreign link for this friend's foreign ID or no profile found on local ID.
} catch (Exception $e) { } catch (Exception $e) {
common_debug($this->name() . common_debug($this->name() .
' - Tried and failed subscribing ' . ' - Tried and failed subscribing ' .
@ -244,8 +244,6 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
$e->getMessage()); $e->getMessage());
} }
} }
}
}
return true; return true;
} }

View File

@ -128,6 +128,7 @@ class TwitterStatusFetcher extends ParallelizingDaemon
return $flinks; return $flinks;
} }
// FIXME: make it so we can force a Foreign_link here without colliding with parent
function childTask($flink) { function childTask($flink) {
// Each child ps needs its own DB connection // Each child ps needs its own DB connection
@ -149,14 +150,8 @@ class TwitterStatusFetcher extends ParallelizingDaemon
unset($_DB_DATAOBJECT['CONNECTIONS']); unset($_DB_DATAOBJECT['CONNECTIONS']);
} }
function getTimeline($flink, $timelineUri = 'home_timeline') function getTimeline(Foreign_link $flink, $timelineUri = 'home_timeline')
{ {
if (empty($flink)) {
common_log(LOG_ERR, $this->name() .
" - Can't retrieve Foreign_link for foreign ID $fid");
return;
}
common_log(LOG_DEBUG, $this->name() . ' - Trying to get ' . $timelineUri . common_log(LOG_DEBUG, $this->name() . ' - Trying to get ' . $timelineUri .
' timeline for Twitter user ' . $flink->foreign_id); ' timeline for Twitter user ' . $flink->foreign_id);

View File

@ -51,8 +51,8 @@ class TweetInQueueHandler extends QueueHandler
$importer = new TwitterImport(); $importer = new TwitterImport();
$notice = $importer->importStatus($status); $notice = $importer->importStatus($status);
if ($notice instanceof Notice) { if ($notice instanceof Notice) {
try {
$flink = Foreign_link::getByForeignID($receiver, TWITTER_SERVICE); $flink = Foreign_link::getByForeignID($receiver, TWITTER_SERVICE);
if ($flink instanceof Foreign_link) {
common_log(LOG_DEBUG, "TweetInQueueHandler - Got flink so add notice ". common_log(LOG_DEBUG, "TweetInQueueHandler - Got flink so add notice ".
$notice->id." to attentions for user ".$flink->user_id); $notice->id." to attentions for user ".$flink->user_id);
try { try {
@ -63,7 +63,7 @@ class TweetInQueueHandler extends QueueHandler
common_log(LOG_ERR, "Failed adding notice {$notice->id} to attentions for user {$flink->user_id}: " . common_log(LOG_ERR, "Failed adding notice {$notice->id} to attentions for user {$flink->user_id}: " .
$e->getMessage()); $e->getMessage());
} }
} else { } catch (NoResultException $e) {
common_log(LOG_DEBUG, "TweetInQueueHandler - No flink found for foreign user ".$receiver); common_log(LOG_DEBUG, "TweetInQueueHandler - No flink found for foreign user ".$receiver);
} }
} }

View File

@ -542,17 +542,17 @@ class TwitterImport
} }
foreach ($status->entities->user_mentions as $mention) { foreach ($status->entities->user_mentions as $mention) {
try {
$flink = Foreign_link::getByForeignID($mention->id, TWITTER_SERVICE); $flink = Foreign_link::getByForeignID($mention->id, TWITTER_SERVICE);
if (!empty($flink)) { $user = $flink->getUser();
$user = User::getKV('id', $flink->user_id);
if (!empty($user)) {
$reply = new Reply(); $reply = new Reply();
$reply->notice_id = $notice->id; $reply->notice_id = $notice->id;
$reply->profile_id = $user->id; $reply->profile_id = $user->id;
$reply->modified = $notice->created; $reply->modified = $notice->created;
common_log(LOG_INFO, __METHOD__ . ": saving reply: notice {$notice->id} to profile {$user->id}"); common_log(LOG_INFO, __METHOD__ . ": saving reply: notice {$notice->id} to profile {$user->id}");
$id = $reply->insert(); $id = $reply->insert();
} } catch (NoResultException $e) {
common_log(LOG_WARNING, 'No local user found for Foreign_link with local User id: '.$flink->user_id);
} }
} }
} }

View File

@ -111,7 +111,7 @@ class TwitterOAuthClient extends OAuthClient
* *
* @return OAuthToken $token the request token * @return OAuthToken $token the request token
*/ */
function getRequestToken() function getTwitterRequestToken()
{ {
return parent::getRequestToken( return parent::getRequestToken(
self::$requestTokenURL, self::$requestTokenURL,
@ -126,7 +126,7 @@ class TwitterOAuthClient extends OAuthClient
* *
* @return the link * @return the link
*/ */
function getAuthorizeLink($request_token, $signin = false) function getTwitterAuthorizeLink($request_token, $signin = false)
{ {
$url = ($signin) ? self::$signinUrl : self::$authorizeURL; $url = ($signin) ? self::$signinUrl : self::$authorizeURL;
@ -142,7 +142,7 @@ class TwitterOAuthClient extends OAuthClient
* *
* @return OAuthToken $token the access token * @return OAuthToken $token the access token
*/ */
function getAccessToken($verifier = null) function getTwitterAccessToken($verifier = null)
{ {
return parent::getAccessToken( return parent::getAccessToken(
self::$accessTokenURL, self::$accessTokenURL,

View File

@ -62,12 +62,7 @@ if (have_option('n')) {
*/ */
function twitterAuthForUser(User $user) function twitterAuthForUser(User $user)
{ {
$flink = Foreign_link::getByUserID($user->id, $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
TWITTER_SERVICE);
if (!$flink) {
throw new ServerException("No Twitter config for this user.");
}
$token = TwitterOAuthClient::unpackToken($flink->credentials); $token = TwitterOAuthClient::unpackToken($flink->credentials);
if (!$token) { if (!$token) {
throw new ServerException("No Twitter OAuth credentials for this user."); throw new ServerException("No Twitter OAuth credentials for this user.");

View File

@ -63,12 +63,7 @@ if (have_option('n')) {
*/ */
function twitterAuthForUser(User $user) function twitterAuthForUser(User $user)
{ {
$flink = Foreign_link::getByUserID($user->id, $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
TWITTER_SERVICE);
if (!$flink) {
throw new ServerException("No Twitter config for this user.");
}
$token = TwitterOAuthClient::unpackToken($flink->credentials); $token = TwitterOAuthClient::unpackToken($flink->credentials);
if (!$token) { if (!$token) {
throw new ServerException("No Twitter OAuth credentials for this user."); throw new ServerException("No Twitter OAuth credentials for this user.");

View File

@ -28,16 +28,17 @@ function add_twitter_user($twitter_id, $screen_name)
// Clear out any bad old foreign_users with the new user's legit URL // Clear out any bad old foreign_users with the new user's legit URL
// This can happen when users move around or fakester accounts get // This can happen when users move around or fakester accounts get
// repoed, and things like that. // repoed, and things like that.
$luser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE); try {
$fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);
if (!empty($luser)) { $result = $fuser->delete();
$result = $luser->delete();
if ($result != false) { if ($result != false) {
common_log( common_log(
LOG_INFO, LOG_INFO,
"Twitter bridge - removed old Twitter user: $screen_name ($twitter_id)." "Twitter bridge - removed old Twitter user: $screen_name ($twitter_id)."
); );
} }
} catch (NoResultException $e) {
// no old foreign users exist for this id
} }
$fuser = new Foreign_user(); $fuser = new Foreign_user();
@ -49,9 +50,8 @@ function add_twitter_user($twitter_id, $screen_name)
$fuser->created = common_sql_now(); $fuser->created = common_sql_now();
$result = $fuser->insert(); $result = $fuser->insert();
if (empty($result)) { if ($result === false) {
common_log(LOG_WARNING, common_log(LOG_WARNING, "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name.");
"Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name.");
common_log_db_error($fuser, 'INSERT', __FILE__); common_log_db_error($fuser, 'INSERT', __FILE__);
} else { } else {
common_log(LOG_INFO, common_log(LOG_INFO,
@ -66,11 +66,10 @@ function save_twitter_user($twitter_id, $screen_name)
{ {
// Check to see whether the Twitter user is already in the system, // Check to see whether the Twitter user is already in the system,
// and update its screen name and uri if so. // and update its screen name and uri if so.
try {
$fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE); $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);
if (!empty($fuser)) {
// Delete old record if Twitter user changed screen name // Delete old record if Twitter user changed screen name
if ($fuser->nickname != $screen_name) { if ($fuser->nickname != $screen_name) {
$oldname = $fuser->nickname; $oldname = $fuser->nickname;
$fuser->delete(); $fuser->delete();
@ -80,11 +79,13 @@ function save_twitter_user($twitter_id, $screen_name)
$screen_name, $screen_name,
$oldname)); $oldname));
} }
} else { } catch (NoResultException $e) {
// Kill any old, invalid records for this screen name // No old users exist for this id
$fuser = Foreign_user::getByNickname($screen_name, TWITTER_SERVICE);
if (!empty($fuser)) { // Kill any old, invalid records for this screen name
// XXX: Is this really only supposed to be run if the above getForeignUser fails?
try {
$fuser = Foreign_user::getByNickname($screen_name, TWITTER_SERVICE);
$fuser->delete(); $fuser->delete();
common_log( common_log(
LOG_INFO, LOG_INFO,
@ -95,6 +96,8 @@ function save_twitter_user($twitter_id, $screen_name)
$fuser->id $fuser->id
) )
); );
} catch (NoResultException $e) {
// No old users exist for this screen_name
} }
} }
@ -178,11 +181,15 @@ function twitter_id($status, $field='id')
*/ */
function broadcast_twitter($notice) function broadcast_twitter($notice)
{ {
$flink = Foreign_link::getByUserID($notice->profile_id, try {
TWITTER_SERVICE); $flink = Foreign_link::getByUserID($notice->profile_id, TWITTER_SERVICE);
} catch (NoResultException $e) {
// Alright so don't broadcast it then! (since there's no foreign link)
return true;
}
// Don't bother with basic auth, since it's no longer allowed // Don't bother with basic auth, since it's no longer allowed
if (!empty($flink) && TwitterOAuthClient::isPackedToken($flink->credentials)) { if (TwitterOAuthClient::isPackedToken($flink->credentials)) {
if (is_twitter_bound($notice, $flink)) { if (is_twitter_bound($notice, $flink)) {
if (!empty($notice->repeat_of) && is_twitter_notice($notice->repeat_of)) { if (!empty($notice->repeat_of) && is_twitter_notice($notice->repeat_of)) {
$retweet = retweet_notice($flink, Notice::getKV('id', $notice->repeat_of)); $retweet = retweet_notice($flink, Notice::getKV('id', $notice->repeat_of));
@ -270,8 +277,13 @@ function twitter_update_params($notice)
return $params; return $params;
} }
function broadcast_oauth($notice, $flink) { function broadcast_oauth($notice, Foreign_link $flink) {
try {
$user = $flink->getUser(); $user = $flink->getUser();
} catch (ServerException $e) {
common_log(LOG_WARNING, 'Discarding broadcast_oauth for notice '.$notice->id.' because of exception: '.$e->getMessage());
return true;
}
$statustxt = format_status($notice); $statustxt = format_status($notice);
$params = twitter_update_params($notice); $params = twitter_update_params($notice);

View File

@ -308,11 +308,6 @@ class XmppPlugin extends ImPlugin
return true; return true;
} }
function microiduri($screenname)
{
return 'xmpp:' . $screenname;
}
function sendMessage($screenname, $body) function sendMessage($screenname, $body)
{ {
$this->queuedConnection()->message($screenname, $body, 'chat'); $this->queuedConnection()->message($screenname, $body, 'chat');