diff --git a/EVENTS.txt b/EVENTS.txt index cfba97403b..2f91a305f0 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -615,12 +615,12 @@ EndCheckPassword: After checking a username/password pair - $authenticatedUser: User object if credentials match a user, else null. StartChangePassword: Before changing a password -- $user: user +- Profile $target: The profile of the User that is changing password - $oldpassword: the user's old password - $newpassword: the desired new 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) - &$hashed: Hashed version of the password, later put in the database diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php index 1f31cbdafe..4b618eb9be 100644 --- a/actions/avatarsettings.php +++ b/actions/avatarsettings.php @@ -76,11 +76,11 @@ class AvatarsettingsAction extends SettingsAction /** * 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 */ - function showContent() { if ($this->mode == 'crop') { @@ -243,52 +243,19 @@ class AvatarsettingsAction extends SettingsAction $this->elementEnd('form'); } - /** - * Handle a post - * - * We mux on the button name to figure out what the user actually wanted. - * - * @return void - */ - function handlePost() + protected function doPost() { - // 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 ($this->arg('upload')) { - $this->uploadAvatar(); - } else if ($this->arg('crop')) { - $this->cropAvatar(); - } else if ($this->arg('delete')) { - $this->deleteAvatar(); - } else { - // TRANS: Unexpected validation error on avatar upload form. - $this->showForm(_('Unexpected form submission.')); - } + if ($this->trimmed('upload')) { + return $this->uploadAvatar(); + } else if ($this->trimmed('crop')) { + return $this->cropAvatar(); + } else if ($this->trimmed('delete')) { + return $this->deleteAvatar(); + } else { + // TRANS: Unexpected validation error on avatar upload form. + throw new ClientException(_('Unexpected form submission.')); + } Event::handle('EndAvatarSaveForm', array($this)); } } @@ -303,21 +270,12 @@ class AvatarsettingsAction extends SettingsAction */ function uploadAvatar() { - try { - $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; - } + // 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'); - $cur = common_current_user(); $type = $imagefile->preferredType(); - $filename = Avatar::filename($cur->id, + $filename = Avatar::filename($this->scoped->getID(), image_type_to_extension($type), null, 'tmp'.common_timestamp()); @@ -338,8 +296,7 @@ class AvatarsettingsAction extends SettingsAction $this->mode = 'crop'; // TRANS: Avatar upload form instruction after uploading a file. - $this->showForm(_('Pick a square area of the image to be your avatar.'), - true); + return _('Pick a square area of the image to be your avatar.'); } /** @@ -351,13 +308,12 @@ class AvatarsettingsAction extends SettingsAction { $filedata = $_SESSION['FILEDATA']; - if (!$filedata) { + if (empty($filedata)) { // 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']) - ? $filedata['height'] : $filedata['width']; + $file_d = min($filedata['width'], $filedata['height']); $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; @@ -369,11 +325,8 @@ class AvatarsettingsAction extends SettingsAction 'x' => $dest_x, 'y' => $dest_y, 'w' => $dest_w, 'h' => $dest_h); - $user = common_current_user(); - $profile = $user->getProfile(); - $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()); try { $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']); unset($_SESSION['FILEDATA']); $this->mode = 'upload'; // TRANS: Success message for having updated a user avatar. - $this->showForm(_('Avatar updated.'), true); - } 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.')); + return _('Avatar updated.'); } + + // 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() { - $user = common_current_user(); - $profile = $user->getProfile(); - - Avatar::deleteFromProfile($profile); + Avatar::deleteFromProfile($this->scoped); // TRANS: Success message for deleting a user avatar. - $this->showForm(_('Avatar deleted.'), true); + return _('Avatar deleted.'); } /** diff --git a/actions/emailsettings.php b/actions/emailsettings.php index a0f111c0d5..c02f1cdfad 100644 --- a/actions/emailsettings.php +++ b/actions/emailsettings.php @@ -28,11 +28,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - - +if (!defined('GNUSOCIAL')) { exit(1); } /** * Settings for email @@ -112,8 +108,8 @@ class EmailsettingsAction extends SettingsAction // TRANS: Button label to remove a confirmed e-mail address. $this->submit('remove', _m('BUTTON','Remove')); } else { - $confirm = $this->getConfirmation(); - if ($confirm) { + try { + $confirm = $this->getConfirmation(); $this->element('p', array('id' => 'form_unconfirmed'), $confirm->address); $this->element('p', array('class' => 'form_note'), // TRANS: Form note in e-mail settings form. @@ -123,12 +119,12 @@ class EmailsettingsAction extends SettingsAction $this->hidden('email', $confirm->address); // TRANS: Button label to cancel an e-mail address confirmation procedure. $this->submit('cancel', _m('BUTTON','Cancel')); - } else { + } catch (NoResultException $e) { $this->elementStart('ul', 'form_data'); $this->elementStart('li'); // TRANS: Field label for e-mail address input in e-mail settings form. $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: "example.org". It is one of the domain names reserved for // 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.'), $user->emailnotifynudge); $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)); } $this->elementEnd('ul'); @@ -254,56 +244,36 @@ class EmailsettingsAction extends SettingsAction */ function getConfirmation() { - $user = common_current_user(); - $confirm = new Confirm_address(); - $confirm->user_id = $user->id; + $confirm->user_id = $this->scoped->getID(); $confirm->address_type = 'email'; if ($confirm->find(true)) { return $confirm; - } else { - return null; } + + throw new NoResultException($confirm); } - /** - * Handle posts - * - * Since there are a lot of different options on the page, we - * figure out what we're supposed to do based on which button was - * pushed - * - * @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->show_form(_('There was a problem with your session token. '. - 'Try again, please.')); - return; + if ($this->arg('save')) { + return $this->savePreferences(); + } else if ($this->arg('add')) { + return $this->addAddress(); + } else if ($this->arg('cancel')) { + return $this->cancelConfirmation(); + } 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. - $this->showForm(_('Unexpected form submission.')); - } + // TRANS: Message given submitting a form with an unknown action in e-mail settings. + throw new ClientException(_('Unexpected form submission.')); } /** @@ -313,25 +283,21 @@ class EmailsettingsAction extends SettingsAction */ function savePreferences() { - $user = $this->scoped->getUser(); - if (Event::handle('StartEmailSaveForm', array($this, $this->scoped))) { $emailnotifysub = $this->booleanintstring('emailnotifysub'); $emailnotifymsg = $this->booleanintstring('emailnotifymsg'); $emailnotifynudge = $this->booleanintstring('emailnotifynudge'); $emailnotifyattn = $this->booleanintstring('emailnotifyattn'); - $emailmicroid = $this->booleanintstring('emailmicroid'); $emailpost = $this->booleanintstring('emailpost'); + $user = $this->scoped->getUser(); $user->query('BEGIN'); - $original = clone($user); $user->emailnotifysub = $emailnotifysub; $user->emailnotifymsg = $emailnotifymsg; $user->emailnotifynudge = $emailnotifynudge; $user->emailnotifyattn = $emailnotifyattn; - $user->emailmicroid = $emailmicroid; $user->emailpost = $emailpost; $result = $user->update($original); @@ -340,16 +306,15 @@ class EmailsettingsAction extends SettingsAction common_log_db_error($user, 'UPDATE', __FILE__); $user->query('ROLLBACK'); // 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'); 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() { - $user = common_current_user(); + $user = $this->scoped->getUser(); $email = $this->trimmed('email'); // Some validation - if (!$email) { + if (empty($email)) { // TRANS: Message given saving e-mail address without having provided one. - $this->showForm(_('No email address.')); - return; + throw new ClientException(_('No email address.')); } $email = common_canonical_email($email); - if (!$email) { + if (empty($email)) { // TRANS: Message given saving e-mail address that cannot be normalised. - $this->showForm(_('Cannot normalize that email address.')); - return; + throw new ClientException(_('Cannot normalize that email address.')); } if (!Validate::email($email, common_config('email', 'check_domain'))) { // TRANS: Message given saving e-mail address that not valid. - $this->showForm(_('Not a valid email address.')); - return; + throw new ClientException(_('Not a valid email address.')); } else if ($user->email == $email) { // TRANS: Message given saving e-mail address that is already set. - $this->showForm(_('That is already your email address.')); - return; + throw new ClientException(_('That is already your email address.')); } else if ($this->emailExists($email)) { // TRANS: Message given saving e-mail address that is already set for another user. - $this->showForm(_('That email address already belongs '. - 'to another user.')); - return; + throw new ClientException(_('That email address already belongs to another user.')); } if (Event::handle('StartAddEmailAddress', array($user, $email))) { @@ -399,7 +358,7 @@ class EmailsettingsAction extends SettingsAction $confirm->address = $email; $confirm->address_type = 'email'; - $confirm->user_id = $user->id; + $confirm->user_id = $user->getID(); $confirm->code = common_confirmation_code(64); $result = $confirm->insert(); @@ -407,21 +366,19 @@ class EmailsettingsAction extends SettingsAction if ($result === false) { common_log_db_error($confirm, 'INSERT', __FILE__); // 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); - mail_confirm_address($user, $confirm->code, $user->nickname, $email); + common_debug('Sending confirmation address for user '.$user->getID().' to email '.$email); + mail_confirm_address($user, $confirm->code, $user->getNickname(), $email); Event::handle('EndAddEmailAddress', array($user, $email)); } // 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 '. 'on how to use it.'); - - $this->showForm($msg, true); } /** @@ -431,31 +388,29 @@ class EmailsettingsAction extends SettingsAction */ function cancelConfirmation() { - $email = $this->arg('email'); + $email = $this->trimmed('email'); - $confirm = $this->getConfirmation(); - - if (!$confirm) { + try { + $confirm = $this->getConfirmation(); + if ($confirm->address !== $email) { + // TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address. + throw new ClientException(_('That is the wrong email address.')); + } + } catch (NoResultException $e) { // 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. - $this->showForm(_('That is the wrong email address.')); - return; + throw new AlreadyFulfilledException(_('No pending confirmation to cancel.')); } $result = $confirm->delete(); - if (!$result) { + if ($result === false) { common_log_db_error($confirm, 'DELETE', __FILE__); // 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. - $this->showForm(_('Email confirmation cancelled.'), true); + return _('Email confirmation cancelled.'); } /** @@ -467,26 +422,22 @@ class EmailsettingsAction extends SettingsAction { $user = common_current_user(); - $email = $this->arg('email'); + $email = $this->trimmed('email'); // 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: registered for the active user. - $this->showForm(_('That is not your email address.')); - return; + throw new ClientException(_('That is not your email address.')); } $original = clone($user); - $user->email = null; - // Throws exception on failure. Also performs it within a transaction. $user->updateWithKeys($original); // 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(); - 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. - $this->showForm(_('No incoming email address.')); - return; + throw new AlreadyFulfilledException(_('No incoming email address.')); } $orig = clone($user); - $user->incomingemail = null; $user->emailpost = 0; - // Throws exception on failure. Also performs it within a transaction. $user->updateWithKeys($orig); // 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() { $user = common_current_user(); - $orig = clone($user); - $user->incomingemail = mail_new_incoming_address(); $user->emailpost = 1; - // Throws exception on failure. Also performs it within a transaction. $user->updateWithKeys($orig); // 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); - if (!$other) { + if (!$other instanceof User) { return false; - } else { - return $other->id != $user->id; } + + return $other->id != $user->id; } } diff --git a/actions/groupbyid.php b/actions/groupbyid.php index b82a861e97..de87ec5c67 100644 --- a/actions/groupbyid.php +++ b/actions/groupbyid.php @@ -28,12 +28,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR.'/lib/noticelist.php'; -require_once INSTALLDIR.'/lib/feedlist.php'; +if (!defined('GNUSOCIAL')) { exit(1); } /** * 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 * @link http://status.net/ */ -class GroupbyidAction extends Action +class GroupbyidAction extends ManagedAction { /** group we're viewing. */ - var $group = null; + protected $group = null; - /** - * Is this page read-only? - * - * @return boolean true - */ function isReadOnly($args) { return true; } - function prepare($args) + protected function doPreparation() { - parent::prepare($args); - - $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"); - - $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; + $this->group = User_group::getByID($this->arg('id')); } - /** - * Handle the request - * - * Shows a profile for the group, some controls, and a list of - * group notices. - * - * @return void - */ - function handle($args) + public function showPage() { common_redirect($this->group->homeUrl(), 303); } diff --git a/actions/imsettings.php b/actions/imsettings.php index 92fff45a7d..40bea10e68 100644 --- a/actions/imsettings.php +++ b/actions/imsettings.php @@ -27,9 +27,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} +if (!defined('GNUSOCIAL')) { exit(1); } /** * Settings for Jabber/XMPP integration @@ -118,8 +116,8 @@ class ImsettingsAction extends SettingsAction // TRANS: Button label to remove a confirmed IM address. $this->submit('remove', _m('BUTTON','Remove')); } else { - $confirm = $this->getConfirmation($transport); - if ($confirm) { + try { + $confirm = $this->getConfirmation($transport); $this->element('p', 'form_unconfirmed', $confirm->address); // TRANS: Form note in IM settings form. $this->element('p', 'form_note', @@ -134,7 +132,7 @@ class ImsettingsAction extends SettingsAction $this->hidden('screenname', $confirm->address); // TRANS: Button label to cancel an IM address confirmation procedure. $this->submit('cancel', _m('BUTTON','Cancel')); - } else { + } catch (NoResultException $e) { $this->elementStart('ul', 'form_data'); $this->elementStart('li'); // TRANS: Field label for IM address. @@ -179,8 +177,6 @@ class ImsettingsAction extends SettingsAction // TRANS: Checkbox label in IM preferences form. array('name'=>'replies', 'description'=>_('Send me replies '. 'from people I\'m not subscribed to.')), - // TRANS: Checkbox label in IM preferences form. - array('name'=>'microid', 'description'=>_('Publish a MicroID')) ); foreach($preferences as $preference) { @@ -211,57 +207,35 @@ class ImsettingsAction extends SettingsAction */ function getConfirmation($transport) { - $user = common_current_user(); - $confirm = new Confirm_address(); - $confirm->user_id = $user->id; + $confirm->user_id = $this->scoped->getID(); $confirm->address_type = $transport; if ($confirm->find(true)) { 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 - * to do the actual task requested. - * - * All sub-functions reload the form with a message -- success or failure. - * - * @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('save')) { - $this->savePreferences(); + return $this->savePreferences(); } else if ($this->arg('add')) { - $this->addAddress(); + return $this->addAddress(); } else if ($this->arg('cancel')) { - $this->cancelConfirmation(); + return $this->cancelConfirmation(); } else if ($this->arg('remove')) { - $this->removeAddress(); - } else { - // TRANS: Message given submitting a form with an unknown action in Instant Messaging settings. - $this->showForm(_('Unexpected form submission.')); + return $this->removeAddress(); } + // 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 * set different settings @@ -270,14 +244,12 @@ class ImsettingsAction extends SettingsAction */ function savePreferences() { - $user = common_current_user(); - $user_im_prefs = new User_im_prefs(); $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()) { - $preferences = array('notify', 'updatefrompresence', 'replies', 'microid'); + $preferences = array('notify', 'updatefrompresence', 'replies'); do { $original = clone($user_im_prefs); @@ -289,15 +261,15 @@ class ImsettingsAction extends SettingsAction $result = $new->update($original); 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. - $this->serverError(_('Could not update IM preferences.')); + throw new ServerException(_('Could not update IM preferences.')); } }while($user_im_prefs->fetch()); } $user_im_prefs->query('COMMIT'); // 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() { - $user = common_current_user(); - $screenname = $this->trimmed('screenname'); $transport = $this->trimmed('transport'); // Some validation - if (!$screenname) { + if (empty($screenname)) { // TRANS: Message given saving IM address without having provided one. - $this->showForm(_('No screenname.')); - return; + throw new ClientException(_('No screenname.')); } - if (!$transport) { + if (empty($transport)) { // TRANS: Form validation error when no transport is available setting an IM address. - $this->showForm(_('No transport.')); - return; + throw new ClientException(_('No transport.')); } Event::handle('NormalizeImScreenname', array($transport, &$screenname)); - if (!$screenname) { + if (empty($screenname)) { // TRANS: Message given saving IM address that cannot be normalised. - $this->showForm(_('Cannot normalize that screenname.')); - return; + throw new ClientException(_('Cannot normalize that screenname.')); } $valid = false; Event::handle('ValidateImScreenname', array($transport, $screenname, &$valid)); if (!$valid) { // TRANS: Message given saving IM address that not valid. - $this->showForm(_('Not a valid screenname.')); - return; + throw new ClientException(_('Not a valid screenname.')); } else if ($this->screennameExists($transport, $screenname)) { // TRANS: Message given saving IM address that is already set for another user. - $this->showForm(_('Screenname already belongs to another user.')); - return; + throw new ClientException(_('Screenname already belongs to another user.')); } $confirm = new Confirm_address(); $confirm->address = $screenname; $confirm->address_type = $transport; - $confirm->user_id = $user->id; + $confirm->user_id = $this->scoped->getID(); $confirm->code = common_confirmation_code(64); $confirm->sent = common_sql_now(); $confirm->claimed = common_sql_now(); @@ -365,13 +330,10 @@ class ImsettingsAction extends SettingsAction $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. - $msg = _('A confirmation code was sent '. - 'to the IM address you added.'); - - $this->showForm($msg, true); + return _('A confirmation code was sent to the IM address you added.'); } /** @@ -386,29 +348,27 @@ class ImsettingsAction extends SettingsAction $screenname = $this->trimmed('screenname'); $transport = $this->trimmed('transport'); - $confirm = $this->getConfirmation($transport); - - if (!$confirm) { + try { + $confirm = $this->getConfirmation($transport); + if ($confirm->address != $screenname) { + // TRANS: Message given canceling IM address confirmation for the wrong IM address. + throw new ClientException(_('That is the wrong IM address.')); + } + } catch (NoResultException $e) { // TRANS: Message given canceling Instant Messaging address confirmation that is not pending. - $this->showForm(_('No pending confirmation to cancel.')); - return; - } - if ($confirm->address != $screenname) { - // TRANS: Message given canceling IM address confirmation for the wrong IM address. - $this->showForm(_('That is the wrong IM address.')); - return; + throw new AlreadyFulfilledException(_('No pending confirmation to cancel.')); } $result = $confirm->delete(); - if (!$result) { + if ($result === false) { common_log_db_error($confirm, 'DELETE', __FILE__); // 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. - $this->showForm(_('IM confirmation cancelled.'), true); + return _('IM confirmation cancelled.'); } /** @@ -420,34 +380,32 @@ class ImsettingsAction extends SettingsAction */ function removeAddress() { - $user = common_current_user(); - $screenname = $this->trimmed('screenname'); $transport = $this->trimmed('transport'); // Maybe an old tab open...? $user_im_prefs = new User_im_prefs(); - $user_im_prefs->user_id = $user->id; - if(! ($user_im_prefs->find() && $user_im_prefs->fetch())) { + $user_im_prefs->user_id = $this->scoped->getID(); + $user_im_prefs->transport = $transport; + if (!$user_im_prefs->find(true)) { // TRANS: Message given trying to remove an IM address that is not // TRANS: registered for the active user. - $this->showForm(_('That is not your screenname.')); - return; + throw new AlreadyFulfilledException(_('There were no preferences stored for this transport.')); } $result = $user_im_prefs->delete(); - if (!$result) { - common_log_db_error($user, 'UPDATE', __FILE__); + if ($result === false) { + common_log_db_error($user_im_prefs, 'UPDATE', __FILE__); // 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 // 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) { - $user = common_current_user(); - $user_im_prefs = new User_im_prefs(); $user_im_prefs->transport = $transport; $user_im_prefs->screenname = $screenname; - if($user_im_prefs->find() && $user_im_prefs->fetch()){ - return true; - }else{ - return false; - } + return $user_im_prefs->find(true) ? true : false; } } diff --git a/actions/newapplication.php b/actions/newapplication.php index 37bede0d72..5032bb7495 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -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 * @link http://status.net/ */ -class NewApplicationAction extends FormAction +class NewApplicationAction extends SettingsAction { function title() { @@ -54,6 +54,7 @@ class NewApplicationAction extends FormAction if ($this->arg('cancel')) { common_redirect(common_local_url('oauthappssettings'), 303); } elseif ($this->arg('save')) { + //trySave will never return, just throw exception or redirect $this->trySave(); } @@ -72,7 +73,7 @@ class NewApplicationAction extends FormAction return _('Use this form to register a new application.'); } - private function trySave() + protected function trySave() { $name = $this->trimmed('name'); $description = $this->trimmed('description'); @@ -137,7 +138,7 @@ class NewApplicationAction extends FormAction $app->query('BEGIN'); $app->name = $name; - $app->owner = $this->scoped->id; + $app->owner = $this->scoped->getID(); $app->description = $description; $app->source_url = $source_url; $app->organization = $organization; diff --git a/actions/oauthappssettings.php b/actions/oauthappssettings.php index 29e6d56073..43e9b33663 100644 --- a/actions/oauthappssettings.php +++ b/actions/oauthappssettings.php @@ -27,11 +27,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR . '/lib/applicationlist.php'; +if (!defined('GNUSOCIAL')) { exit(1); } /** * Show a user's registered OAuth applications @@ -47,19 +43,11 @@ require_once INSTALLDIR . '/lib/applicationlist.php'; class OauthappssettingsAction extends SettingsAction { - var $page = 0; + protected $page = null; - function prepare($args) + protected function doPreparation() { - parent::prepare($args); - $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; + $this->page = $this->int('page') ?: 1; } /** @@ -86,21 +74,13 @@ class OauthappssettingsAction extends SettingsAction return _('Applications you have registered'); } - /** - * Content area of the page - * - * @return void - */ - function showContent() { - $user = common_current_user(); - $offset = ($this->page - 1) * APPS_PER_PAGE; $limit = APPS_PER_PAGE + 1; $application = new Oauth_application(); - $application->owner = $user->id; + $application->owner = $this->scoped->getID(); $application->whereAdd("name != 'anonymous'"); $application->limit($offset, $limit); $application->orderBy('created DESC'); @@ -109,7 +89,7 @@ class OauthappssettingsAction extends SettingsAction $cnt = 0; if ($application) { - $al = new ApplicationList($application, $user, $this); + $al = new ApplicationList($application, $this->scoped, $this); $cnt = $al->show(); if (0 == $cnt) { $this->showEmptyListMessage(); @@ -135,34 +115,11 @@ class OauthappssettingsAction extends SettingsAction 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.')); $this->elementStart('div', 'guide'); $this->raw(common_markup_to_html($message)); $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; - } - } } diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php index 9aa3ad434f..0c5a143443 100644 --- a/actions/oauthconnectionssettings.php +++ b/actions/oauthconnectionssettings.php @@ -27,11 +27,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR . '/lib/applicationlist.php'; +if (!defined('GNUSOCIAL')) { exit(1); } /** * Show connected OAuth applications @@ -46,15 +42,14 @@ require_once INSTALLDIR . '/lib/applicationlist.php'; */ class OauthconnectionssettingsAction extends SettingsAction { - var $page = null; - var $oauth_token = null; + var $page = null; - function prepare($args) + protected $oauth_token = null; + + protected function doPreparation() { - parent::prepare($args); $this->oauth_token = $this->arg('oauth_token'); - $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; - return true; + $this->page = $this->int('page') ?: 1; } /** @@ -87,18 +82,15 @@ class OauthconnectionssettingsAction extends SettingsAction function showContent() { - $user = common_current_user(); - $profile = $user->getProfile(); - $offset = ($this->page - 1) * APPS_PER_PAGE; $limit = APPS_PER_PAGE + 1; - $connection = $user->getConnectedApps($offset, $limit); + $connection = $this->scoped->getConnectedApps($offset, $limit); $cnt = 0; if (!empty($connection)) { - $cal = new ConnectedAppsList($connection, $user, $this); + $cal = new ConnectedAppsList($connection, $this->scoped, $this); $cnt = $cal->show(); } @@ -111,7 +103,7 @@ class OauthconnectionssettingsAction extends SettingsAction $cnt > APPS_PER_PAGE, $this->page, 'connectionssettings', - array('nickname' => $user->nickname) + array('nickname' => $this->scoped->getNickname()) ); } @@ -125,24 +117,14 @@ class OauthconnectionssettingsAction extends SettingsAction * * @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')) { - $this->revokeAccess($this->oauth_token); - } else { - // TRANS: Client error when submitting a form with unexpected information. - $this->clientError(_('Unexpected form submission.'), 401); + return $this->revokeAccess($this->oauth_token); } + + // TRANS: Client error when submitting a form with unexpected information. + throw new ClientException(_('Unexpected form submission.'), 401); } /** diff --git a/actions/oldschoolsettings.php b/actions/oldschoolsettings.php index 25ec13e481..5c07195478 100644 --- a/actions/oldschoolsettings.php +++ b/actions/oldschoolsettings.php @@ -28,11 +28,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET')) { - // This check helps protect against security problems; - // your code file can't be executed directly from the web. - exit(1); -} +if (!defined('GNUSOCIAL')) { exit(1); } /** * Old-school settings @@ -77,35 +73,23 @@ class OldschoolsettingsAction extends SettingsAction * @return boolean true */ - function prepare($argarray) + protected function doPreparation() { if (!common_config('oldschool', 'enabled')) { throw new ClientException("Old-school settings not enabled."); } - parent::prepare($argarray); - return true; } - /** - * Handler method - * - * @param array $argarray is ignored since it's now passed in in prepare() - * - * @return void - */ - - function handlePost() + function doPost() { - $user = common_current_user(); - - $osp = Old_school_prefs::getKV('user_id', $user->id); + $osp = Old_school_prefs::getKV('user_id', $this->scoped->getID()); $orig = null; if (!empty($osp)) { $orig = clone($osp); } else { $osp = new Old_school_prefs(); - $osp->user_id = $user->id; + $osp->user_id = $this->scoped->getID(); $osp->created = common_sql_now(); } @@ -113,34 +97,25 @@ class OldschoolsettingsAction extends SettingsAction $osp->stream_nicknames = $this->boolean('stream_nicknames'); $osp->modified = common_sql_now(); - if (!empty($orig)) { + if ($orig instanceof Old_school_prefs) { $osp->update($orig); } else { $osp->insert(); } // TRANS: Confirmation shown when user profile settings are saved. - $this->showForm(_('Settings saved.'), true); - - return; - } - - function showContent() - { - $user = common_current_user(); - $form = new OldSchoolForm($this, $user); - $form->show(); + return _('Settings saved.'); } } -class OldSchoolForm extends Form +class OldSchoolSettingsForm extends Form { var $user; - function __construct($out, $user) + function __construct(Action $out) { parent::__construct($out); - $this->user = $user; + $this->user = $out->getScoped()->getUser(); } /** diff --git a/actions/passwordsettings.php b/actions/passwordsettings.php index db36b612a2..cfdb6c7817 100644 --- a/actions/passwordsettings.php +++ b/actions/passwordsettings.php @@ -28,11 +28,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - - +if (!defined('STATUSNET')) { exit(1); } /** * Change password @@ -77,18 +73,8 @@ class PasswordsettingsAction extends SettingsAction $this->autofocus('oldpassword'); } - /** - * Content area of the page - * - * Shows a form for changing the password - * - * @return void - */ - function showContent() { - $user = common_current_user(); - $this->elementStart('form', array('method' => 'POST', 'id' => 'form_password', 'class' => 'form_settings', @@ -102,7 +88,7 @@ class PasswordsettingsAction extends SettingsAction $this->elementStart('ul', 'form_data'); // Users who logged in with OpenID won't have a pwd - if ($user->password) { + if ($this->scoped->hasPassword()) { $this->elementStart('li'); // TRANS: Field label on page where to change password. $this->password('oldpassword', _('Old password')); @@ -129,29 +115,8 @@ class PasswordsettingsAction extends SettingsAction $this->elementEnd('form'); } - /** - * Handle a post - * - * Validate input and save changes. Reload the form with a success - * or error message. - * - * @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; - } - - $user = common_current_user(); - assert(!is_null($user)); // should already be checked - // FIXME: scrub input $newpassword = $this->arg('newpassword'); @@ -161,49 +126,44 @@ class PasswordsettingsAction extends SettingsAction if (strlen($newpassword) < 6) { // TRANS: Form validation error on page where to change password. - $this->showForm(_('Password must be 6 or more characters.')); - return; + throw new ClientException(_('Password must be 6 or more characters.')); } else if (0 != strcmp($newpassword, $confirm)) { // TRANS: Form validation error on password change when password confirmation does not match. - $this->showForm(_('Passwords do not match.')); - return; + throw new ClientException(_('Passwords do not match.')); } - if ($user->password) { + $oldpassword = null; + if ($this->scoped->hasPassword()) { $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. - $this->showForm(_('Incorrect old password.')); - return; + throw new ClientException(_('Incorrect old password.')); } - }else{ - $oldpassword = null; } - $success = false; - if(Event::handle('StartChangePassword', array($user, $oldpassword, $newpassword))){ + if (Event::handle('StartChangePassword', array($this->scoped, $oldpassword, $newpassword))) { //no handler changed the password, so change the password internally + $user = $this->scoped->getUser(); $original = clone($user); - $user->password = common_munge_password($newpassword, $user->id); + $user->password = common_munge_password($newpassword, $this->scoped); $val = $user->validate(); if ($val !== true) { // TRANS: Form validation error on page where to change password. - $this->showForm(_('Error saving user; invalid.')); - return; + throw new ServerException(_('Error saving user; invalid.')); } if (!$user->update($original)) { // TRANS: Server error displayed on page where to change password when password change // 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. - $this->showForm(_('Password saved.'), true); + return _('Password saved.'); } } diff --git a/actions/profilesettings.php b/actions/profilesettings.php index 7d3143d4b1..5804f21ca5 100644 --- a/actions/profilesettings.php +++ b/actions/profilesettings.php @@ -82,7 +82,6 @@ class ProfilesettingsAction extends SettingsAction */ function showContent() { - $profile = $this->scoped; $user = $this->scoped->getUser(); $this->elementStart('form', array('method' => 'post', @@ -100,7 +99,7 @@ class ProfilesettingsAction extends SettingsAction $this->elementStart('li'); // TRANS: Field label in form for profile settings. $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. _('1-64 lowercase letters or numbers, no punctuation or spaces.'), null, false, // "name" (will be set to id), then "required" @@ -111,12 +110,12 @@ class ProfilesettingsAction extends SettingsAction $this->elementStart('li'); // TRANS: Field label in form for profile settings. $this->input('fullname', _('Full name'), - ($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname); + $this->trimmed('fullname') ?: $this->scoped->getFullname()); $this->elementEnd('li'); $this->elementStart('li'); // TRANS: Field label in form for profile settings. $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. _('URL of your homepage, blog, or profile on another site.')); $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: their biography. $this->textarea('bio', _('Bio'), - ($this->arg('bio')) ? $this->arg('bio') : $profile->bio, + $this->trimmed('bio') ?: $this->scoped->getDescription(), $bioInstr); $this->elementEnd('li'); $this->elementStart('li'); // TRANS: Field label in form for profile settings. $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. _('Where you are, like "City, State (or Region), Country".')); $this->elementEnd('li'); @@ -152,14 +151,14 @@ class ProfilesettingsAction extends SettingsAction // TRANS: Checkbox label in form for profile settings. $this->checkbox('sharelocation', _('Share my current location when posting notices'), ($this->arg('sharelocation')) ? - $this->arg('sharelocation') : $this->scoped->shareLocation()); + $this->boolean('sharelocation') : $this->scoped->shareLocation()); $this->elementEnd('li'); } Event::handle('EndProfileFormData', array($this)); $this->elementStart('li'); // TRANS: Field label in form for profile settings. $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. _('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated.')); $this->elementEnd('li'); @@ -228,17 +227,8 @@ class ProfilesettingsAction extends SettingsAction * * @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))) { // $nickname will only be set if this changenick value is true. @@ -246,15 +236,13 @@ class ProfilesettingsAction extends SettingsAction try { $nickname = Nickname::normalize($this->trimmed('nickname'), true); } catch (NicknameTakenException $e) { - // Abort only if the nickname is occupied by another local profile - if ($e->profile->id != $this->scoped->id) { - $this->showForm($e->getMessage()); - return; + // Abort only if the nickname is occupied by _another_ local user profile + if (!$this->scoped->sameAs($e->profile)) { + throw $e; } - $nickname = Nickname::normalize($this->trimmed('nickname')); // without in-use check this time - } catch (NicknameException $e) { - $this->showForm($e->getMessage()); - return; + // Since the variable wasn't set before the exception was thrown, let's run + // the normalize sequence again, but without in-use check this time. + $nickname = Nickname::normalize($this->trimmed('nickname')); } } @@ -273,33 +261,27 @@ class ProfilesettingsAction extends SettingsAction if (!is_null($homepage) && (strlen($homepage) > 0) && !common_valid_http_url($homepage)) { // TRANS: Validation error in form for profile settings. - $this->showForm(_('Homepage is not a valid URL.')); - return; - } else if (!is_null($fullname) && mb_strlen($fullname) > 255) { + throw new ClientException(_('Homepage is not a valid URL.')); + } else if (!is_null($fullname) && mb_strlen($fullname) > 191) { // TRANS: Validation error in form for profile settings. - $this->showForm(_('Full name is too long (maximum 255 characters).')); - return; + throw new ClientException(_('Full name is too long (maximum 191 characters).')); } else if (Profile::bioTooLong($bio)) { // TRANS: Validation error in form for profile settings. // TRANS: Plural form is used based on the maximum number of allowed // 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).', Profile::maxBio()), Profile::maxBio())); - return; - } else if (!is_null($location) && mb_strlen($location) > 255) { + } else if (!is_null($location) && mb_strlen($location) > 191) { // TRANS: Validation error in form for profile settings. - $this->showForm(_('Location is too long (maximum 255 characters).')); - return; + throw new ClientException(_('Location is too long (maximum 191 characters).')); } else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) { // TRANS: Validation error in form for profile settings. - $this->showForm(_('Timezone not selected.')); - return; + throw new ClientException(_('Timezone not selected.')); } else if (!is_null($language) && strlen($language) > 50) { // TRANS: Validation error in form for profile settings. - $this->showForm(_('Language is too long (maximum 50 characters).')); - return; + throw new ClientException(_('Language is too long (maximum 50 characters).')); } $tags = array(); @@ -315,15 +297,14 @@ class ProfilesettingsAction extends SettingsAction if (!common_valid_profile_tag($tag)) { // TRANS: Validation error in form for profile settings. // TRANS: %s is an invalid tag. - $this->showForm(sprintf(_('Invalid tag: "%s".'), $tag)); - return; + throw new ClientException(sprintf(_('Invalid tag: "%s".'), $tag)); } $tag_priv[$tag] = $private; } } - $user = common_current_user(); + $user = $this->scoped->getUser(); $user->query('BEGIN'); // $user->nickname is updated through Profile->update(); @@ -346,54 +327,53 @@ class ProfilesettingsAction extends SettingsAction $result = $user->update($original); if ($result === false) { common_log_db_error($user, 'UPDATE', __FILE__); + $user->query('ROLLBACK'); // TRANS: Server error thrown when user profile settings could not be updated to // 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 common_init_language(); } - $profile = $user->getProfile(); + $original = clone($this->scoped); - $orig_profile = clone($profile); - - if (common_config('profile', 'changenick') == true && $profile->nickname !== $nickname) { + if (common_config('profile', 'changenick') == true && $this->scoped->getNickname() !== $nickname) { assert(Nickname::normalize($nickname)===$nickname); - common_debug("Changing user nickname from '{$profile->nickname}' to '{$nickname}'."); - $profile->nickname = $nickname; - $profile->profileurl = common_profile_url($profile->nickname); + common_debug("Changing user nickname from '{$this->scoped->getNickname()}' to '{$nickname}'."); + $this->scoped->nickname = $nickname; + $this->scoped->profileurl = common_profile_url($this->scoped->getNickname()); } - $profile->fullname = $fullname; - $profile->homepage = $homepage; - $profile->bio = $bio; - $profile->location = $location; + $this->scoped->fullname = $fullname; + $this->scoped->homepage = $homepage; + $this->scoped->bio = $bio; + $this->scoped->location = $location; $loc = Location::fromName($location); if (empty($loc)) { - $profile->lat = null; - $profile->lon = null; - $profile->location_id = null; - $profile->location_ns = null; + $this->scoped->lat = null; + $this->scoped->lon = null; + $this->scoped->location_id = null; + $this->scoped->location_ns = null; } else { - $profile->lat = $loc->lat; - $profile->lon = $loc->lon; - $profile->location_id = $loc->location_id; - $profile->location_ns = $loc->location_ns; + $this->scoped->lat = $loc->lat; + $this->scoped->lon = $loc->lon; + $this->scoped->location_id = $loc->location_id; + $this->scoped->location_ns = $loc->location_ns; } if (common_config('location', 'share') == 'user') { $exists = false; - $prefs = User_location_prefs::getKV('user_id', $user->id); + $prefs = User_location_prefs::getKV('user_id', $this->scoped->getID()); if (empty($prefs)) { $prefs = new User_location_prefs(); - $prefs->user_id = $user->id; + $prefs->user_id = $this->scoped->getID(); $prefs->created = common_sql_now(); } else { $exists = true; @@ -410,42 +390,37 @@ class ProfilesettingsAction extends SettingsAction if ($result === false) { 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. - $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('New profile: ' . common_log_objstring($profile), __FILE__); + common_debug('Old profile: ' . common_log_objstring($original), __FILE__); + common_debug('New profile: ' . common_log_objstring($this->scoped), __FILE__); - $result = $profile->update($orig_profile); + $result = $this->scoped->update($original); 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. - $this->serverError(_('Could not save profile.')); + throw new ServerException(_('Could not save profile.')); } // Set the user tags - $result = $user->setSelfTags($tags, $tag_priv); - - if (!$result) { - // TRANS: Server error thrown when user profile settings tags could not be saved. - $this->serverError(_('Could not save tags.')); - } + $result = Profile_tag::setSelfTags($this->scoped, $tags, $tag_priv); $user->query('COMMIT'); Event::handle('EndProfileSaveForm', array($this)); // TRANS: Confirmation shown when user profile settings are saved. - $this->showForm(_('Settings saved.'), true); + return _('Settings saved.'); } } function showAside() { - $user = common_current_user(); - $this->elementStart('div', array('id' => 'aside_primary', 'class' => 'aside')); @@ -453,7 +428,7 @@ class ProfilesettingsAction extends SettingsAction 'class' => 'section')); $this->elementStart('ul'); if (Event::handle('StartProfileSettingsActions', array($this))) { - if ($user->hasRight(Right::BACKUPACCOUNT)) { + if ($this->scoped->hasRight(Right::BACKUPACCOUNT)) { $this->elementStart('li'); $this->element('a', array('href' => common_local_url('backupaccount')), @@ -461,7 +436,7 @@ class ProfilesettingsAction extends SettingsAction _('Backup account')); $this->elementEnd('li'); } - if ($user->hasRight(Right::DELETEACCOUNT)) { + if ($this->scoped->hasRight(Right::DELETEACCOUNT)) { $this->elementStart('li'); $this->element('a', array('href' => common_local_url('deleteaccount')), @@ -469,7 +444,7 @@ class ProfilesettingsAction extends SettingsAction _('Delete account')); $this->elementEnd('li'); } - if ($user->hasRight(Right::RESTOREACCOUNT)) { + if ($this->scoped->hasRight(Right::RESTOREACCOUNT)) { $this->elementStart('li'); $this->element('a', array('href' => common_local_url('restoreaccount')), diff --git a/actions/recoverpassword.php b/actions/recoverpassword.php index 060ba83510..1cbb73fd4a 100644 --- a/actions/recoverpassword.php +++ b/actions/recoverpassword.php @@ -325,7 +325,7 @@ class RecoverpasswordAction extends Action $original = clone($user); - $user->password = common_munge_password($newpassword, $user->id); + $user->password = common_munge_password($newpassword, $user->getProfile()); if (!$user->update($original)) { common_log_db_error($user, 'UPDATE', __FILE__); diff --git a/actions/shownotice.php b/actions/shownotice.php index 4a1adfd7de..23386868dd 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -222,25 +222,12 @@ class ShownoticeAction extends ManagedAction /** * Extra
content * - * We show the microid(s) for the author, if any. + * Facebook OpenGraph metadata. * * @return void */ 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 $avatarUrl = $this->profile->avatarUrl(AVATAR_PROFILE_SIZE); $this->element('meta', array('property' => 'og:image', diff --git a/actions/showstream.php b/actions/showstream.php index 51384eb487..890c1e711b 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -47,38 +47,6 @@ if (!defined('GNUSOCIAL')) { exit(1); } */ 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() { if (empty($this->tag)) { @@ -195,13 +163,6 @@ class ShowstreamAction extends NoticestreamAction '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 $this->element('link', array('rel' => 'microsummary', diff --git a/actions/smssettings.php b/actions/smssettings.php index ec8841281d..ca6a7d04ef 100644 --- a/actions/smssettings.php +++ b/actions/smssettings.php @@ -27,9 +27,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} +if (!defined('GNUSOCIAL')) { exit(1); } /** * Settings for SMS @@ -45,6 +43,14 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { 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 * @@ -86,14 +92,7 @@ class SmssettingsAction extends SettingsAction */ function showContent() { - if (!common_config('sms', 'enabled')) { - $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(); + $user = $this->scoped->getUser(); $this->elementStart('form', array('method' => 'post', 'id' => 'form_settings_sms', @@ -118,8 +117,8 @@ class SmssettingsAction extends SettingsAction // TRANS: Button label to remove a confirmed SMS address. $this->submit('remove', _m('BUTTON','Remove')); } else { - $confirm = $this->getConfirmation(); - if ($confirm) { + try { + $confirm = $this->getConfirmation(); $carrier = Sms_carrier::getKV($confirm->address_extra); $this->element('p', 'form_unconfirmed', $confirm->address . ' (' . $carrier->name . ')'); @@ -141,7 +140,7 @@ class SmssettingsAction extends SettingsAction $this->elementEnd('ul'); // TRANS: Button label to confirm SMS confirmation code in SMS settings. $this->submit('confirm', _m('BUTTON','Confirm')); - } else { + } catch (NoResultException $e) { $this->elementStart('ul', 'form_data'); $this->elementStart('li'); // TRANS: Field label for SMS phone number input in SMS settings form. @@ -216,60 +215,38 @@ class SmssettingsAction extends SettingsAction */ function getConfirmation() { - $user = common_current_user(); - $confirm = new Confirm_address(); - $confirm->user_id = $user->id; + $confirm->user_id = $this->scoped->getID(); $confirm->address_type = 'sms'; if ($confirm->find(true)) { 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 - * to do the actual task requested. - * - * All sub-functions reload the form with a message -- success or failure. - * - * @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('save')) { - $this->savePreferences(); + return $this->savePreferences(); } else if ($this->arg('add')) { - $this->addAddress(); + return $this->addAddress(); } else if ($this->arg('cancel')) { - $this->cancelConfirmation(); + return $this->cancelConfirmation(); } else if ($this->arg('remove')) { - $this->removeAddress(); + return $this->removeAddress(); } else if ($this->arg('removeincoming')) { - $this->removeIncoming(); + return $this->removeIncoming(); } else if ($this->arg('newincoming')) { - $this->newIncoming(); + return $this->newIncoming(); } else if ($this->arg('confirm')) { - $this->confirmCode(); - } else { - // TRANS: Message given submitting a form with an unknown action in SMS settings. - $this->showForm(_('Unexpected form submission.')); + return $this->confirmCode(); } + // 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() { - $smsnotify = $this->boolean('smsnotify'); - - $user = common_current_user(); - - assert(!is_null($user)); // should already be checked + $user = $this->scoped->getUser(); $user->query('BEGIN'); $original = clone($user); - $user->smsnotify = $smsnotify; + $user->smsnotify = $this->boolean('smsnotify'); $result = $user->update($original); if ($result === false) { common_log_db_error($user, 'UPDATE', __FILE__); // 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'); // 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 - if (!$sms) { + if (empty($sms)) { // TRANS: Message given saving SMS phone number without having provided one. - $this->showForm(_('No phone number.')); - return; + throw new ClientException(_('No phone number.')); } - if (!$carrier_id) { + if (empty($carrier_id)) { // TRANS: Message given saving SMS phone number without having selected a carrier. - $this->showForm(_('No carrier selected.')); - return; + throw new ClientException(_('No carrier selected.')); } $sms = common_canonical_sms($sms); - if ($user->sms == $sms) { + if ($user->sms === $sms) { // TRANS: Message given saving SMS phone number that is already set. - $this->showForm(_('That is already your phone number.')); - return; + throw new AlreadyFulfilledException(_('That is already your phone number.')); } else if ($this->smsExists($sms)) { // TRANS: Message given saving SMS phone number that is already set for another user. - $this->showForm(_('That phone number already belongs to another user.')); - return; + throw new ClientException(_('That phone number already belongs to another user.')); } $confirm = new Confirm_address(); @@ -353,7 +322,7 @@ class SmssettingsAction extends SettingsAction $confirm->address = $sms; $confirm->address_extra = $carrier_id; $confirm->address_type = 'sms'; - $confirm->user_id = $user->id; + $confirm->user_id = $this->scoped->getID(); $confirm->code = common_confirmation_code(40); $result = $confirm->insert(); @@ -371,11 +340,9 @@ class SmssettingsAction extends SettingsAction $carrier->toEmailAddress($sms)); // 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 '. 'on how to use it.'); - - $this->showForm($msg, true); } /** @@ -390,29 +357,27 @@ class SmssettingsAction extends SettingsAction $sms = $this->trimmed('sms'); $carrier = $this->trimmed('carrier'); - $confirm = $this->getConfirmation(); - - if (!$confirm) { + try { + $confirm = $this->getConfirmation(); + if ($confirm->address != $sms) { + // TRANS: Message given canceling SMS phone number confirmation for the wrong phone number. + throw new ClientException(_('That is the wrong confirmation number.')); + } + } catch (NoResultException $e) { // TRANS: Message given canceling SMS phone number confirmation that is not pending. - $this->showForm(_('No pending confirmation to cancel.')); - return; - } - if ($confirm->address != $sms) { - // TRANS: Message given canceling SMS phone number confirmation for the wrong phone number. - $this->showForm(_('That is the wrong confirmation number.')); - return; + throw new AlreadyFulfilledException(_('No pending confirmation to cancel.')); } $result = $confirm->delete(); - if (!$result) { + if ($result === false) { common_log_db_error($confirm, 'DELETE', __FILE__); // 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. - $this->showForm(_('SMS confirmation cancelled.'), true); + return _('SMS confirmation cancelled.'); } /** @@ -422,7 +387,7 @@ class SmssettingsAction extends SettingsAction */ function removeAddress() { - $user = common_current_user(); + $user = $this->scoped->getUser(); $sms = $this->arg('sms'); $carrier = $this->arg('carrier'); @@ -432,8 +397,7 @@ class SmssettingsAction extends SettingsAction if ($user->sms != $sms) { // TRANS: Message given trying to remove an SMS phone number that is not // TRANS: registered for the active user. - $this->showForm(_('That is not your phone number.')); - return; + throw new ClientException(_('That is not your phone number.')); } $original = clone($user); @@ -446,7 +410,7 @@ class SmssettingsAction extends SettingsAction $user->updateWithKeys($original); // 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) { - $user = common_current_user(); - $other = User::getKV('sms', $sms); - if (!$other) { + if (!$other instanceof User) { 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'); - if (!$code) { + if (empty($code)) { // TRANS: Message given saving SMS phone number confirmation code without having provided one. - $this->showForm(_('No code entered.')); - return; + throw new ClientException(_('No code entered.')); } - common_redirect(common_local_url('confirmaddress', - array('code' => $code)), - 303); + common_redirect(common_local_url('confirmaddress', array('code' => $code)), 303); } /** @@ -541,8 +500,7 @@ class SmssettingsAction extends SettingsAction if (!$user->incomingemail) { // 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.')); - return; + throw new ClientException(_('No incoming email address.')); } $orig = clone($user); @@ -553,7 +511,7 @@ class SmssettingsAction extends SettingsAction $user->updateWithKeys($orig); // 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() { - $user = common_current_user(); + $user = $this->scoped->getUser(); $orig = clone($user); @@ -575,6 +533,6 @@ class SmssettingsAction extends SettingsAction $user->updateWithKeys($orig); // TRANS: Confirmation text after updating SMS settings. - $this->showForm(_('New incoming email address added.'), true); + return _('New incoming email address added.'); } } diff --git a/actions/urlsettings.php b/actions/urlsettings.php index 69ad4c8690..c3e4ed50b2 100644 --- a/actions/urlsettings.php +++ b/actions/urlsettings.php @@ -28,9 +28,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} +if (!defined('GNUSOCIAL')) { exit(1); } /** * Miscellaneous settings actions @@ -83,7 +81,7 @@ class UrlsettingsAction extends SettingsAction */ function showContent() { - $user = common_current_user(); + $user = $this->scoped->getUser(); $this->elementStart('form', array('method' => 'post', 'id' => 'form_settings_other', @@ -154,31 +152,13 @@ class UrlsettingsAction extends SettingsAction $this->elementEnd('form'); } - /** - * Handle a post - * - * Saves the changes to url-shortening prefs and shows a success or failure - * message. - * - * @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; - } - $urlshorteningservice = $this->trimmed('urlshorteningservice'); if (!is_null($urlshorteningservice) && strlen($urlshorteningservice) > 50) { // TRANS: Form validation error for form "Other settings" in user profile. - $this->showForm(_('URL shortening service is too long (maximum 50 characters).')); - return; + throw new ClientException(_('URL shortening service is too long (maximum 50 characters).')); } $maxurllength = $this->trimmed('maxurllength'); @@ -195,9 +175,7 @@ class UrlsettingsAction extends SettingsAction throw new ClientException(_('Invalid number for maximum notice length.')); } - $user = common_current_user(); - - assert(!is_null($user)); // should already be checked + $user = $this->scoped->getUser(); $user->query('BEGIN'); @@ -209,14 +187,15 @@ class UrlsettingsAction extends SettingsAction if ($result === false) { 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. - $this->serverError(_('Could not update user.')); + throw new ServerException(_('Could not update user.')); } $prefs = User_urlshortener_prefs::getPrefs($user); $orig = null; - if (empty($prefs)) { + if (!$prefs instanceof User_urlshortener_prefs) { $prefs = new User_urlshortener_prefs(); $prefs->user_id = $user->id; @@ -229,13 +208,14 @@ class UrlsettingsAction extends SettingsAction $prefs->maxurllength = $maxurllength; $prefs->maxnoticelength = $maxnoticelength; - if (!empty($orig)) { + if ($orig instanceof User_urlshortener_prefs) { $result = $prefs->update($orig); } else { $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. throw new ServerException(_('Error saving user URL shortening preferences.')); } @@ -243,6 +223,6 @@ class UrlsettingsAction extends SettingsAction $user->query('COMMIT'); // TRANS: Confirmation message after saving preferences. - $this->showForm(_('Preferences saved.'), true); + return _('Preferences saved.'); } } diff --git a/actions/userrss.php b/actions/userrss.php index fd49a0e899..7bed1dd256 100644 --- a/actions/userrss.php +++ b/actions/userrss.php @@ -27,18 +27,20 @@ class UserrssAction extends TargetedRss10Action protected function doStreamPreparation() { + parent::doStreamPreparation(); + $this->tag = $this->trimmed('tag'); } protected function getNotices() { 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(); } // otherwise we fetch a normal user stream - $stream = $this->target->getNotices(0, $this->limit); + $stream = $this->getTarget()->getNotices(0, $this->limit); return $stream->fetchAll(); } diff --git a/classes/Foreign_link.php b/classes/Foreign_link.php index 6176ec43bc..b3757448ad 100644 --- a/classes/Foreign_link.php +++ b/classes/Foreign_link.php @@ -56,34 +56,37 @@ class Foreign_link extends Managed_DataObject static function getByUserID($user_id, $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->service = $service; $flink->user_id = $user_id; $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) { if (empty($foreign_id) || empty($service)) { - return null; - } else { - $flink = new Foreign_link(); - $flink->service = $service; - $flink->foreign_id = $foreign_id; - $flink->limit(1); - - $result = $flink->find(true); - - return empty($result) ? null : $flink; + throw new ServerException('Empty foreign_id or service for Foreign_link::getByForeignID'); } + + $flink = new Foreign_link(); + $flink->service = $service; + $flink->foreign_id = $foreign_id; + $flink->limit(1); + + if (!$flink->find(true)) { + throw new NoResultException($flink); + } + + return $flink; } function set_flags($noticesend, $noticerecv, $replysync, $friendsync) @@ -124,21 +127,21 @@ class Foreign_link extends Managed_DataObject $fuser->limit(1); - if ($fuser->find(true)) { - return $fuser; + if (!$fuser->find(true)) { + throw new NoResultException($fuser); } - return null; + return $fuser; } function getUser() { - return User::getKV($this->user_id); + return Profile::getByID($this->user_id)->getUser(); } 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 diff --git a/classes/Foreign_user.php b/classes/Foreign_user.php index c1739d318a..1f6c77851d 100644 --- a/classes/Foreign_user.php +++ b/classes/Foreign_user.php @@ -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->id = $id; $fuser->service = $service; - $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) { if (empty($nickname) || empty($service)) { - return null; - } else { - $fuser = new Foreign_user(); - $fuser->service = $service; - $fuser->nickname = $nickname; - $fuser->limit(1); - - $result = $fuser->find(true); - - return empty($result) ? null : $fuser; + throw new ServerException('Empty nickname or service for Foreign_user::getByNickname'); } + + $fuser = new Foreign_user(); + $fuser->service = $service; + $fuser->nickname = $nickname; + $fuser->limit(1); + + if (!$fuser->find(true)) { + throw new NoResultException($fuser); + } + + return $fuser; } } diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index c1f6f644db..41ce715210 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -74,7 +74,7 @@ class Memcached_DataObject extends Safe_DataObject { $obj = new $cls; - // php-compatible, for settype(), datatype + // PHP compatible datatype for settype() below $colType = $obj->columnType($keyCol); if (!in_array($colType, array('integer', 'int'))) { diff --git a/classes/Notice.php b/classes/Notice.php index ae722138b3..050eeaf90d 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -523,18 +523,13 @@ class Notice extends Managed_DataObject // Handle repeat case - if (isset($repeat_of)) { + if (!empty($options['repeat_of'])) { // Check for a private one - $repeat = Notice::getKV('id', $repeat_of); + $repeat = Notice::getByID($options['repeat_of']); - if (!($repeat instanceof Notice)) { - // 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) { + if ($profile->sameAs($repeat->getProfile())) { // TRANS: Client error displayed when trying to repeat an 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'])) { $conv = Conversation::getKV('uri', $options['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; } 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... - $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 { // 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)) { $orig = clone($notice); // $act->context->conversation will be null if it was not provided + $conv = Conversation::create($notice, $options['conversation']); $notice->conversation = $conv->id; $notice->update($orig); @@ -853,6 +850,22 @@ class Notice extends Managed_DataObject if (is_null($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) { @@ -898,10 +911,11 @@ class Notice extends Managed_DataObject throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString()); } - // If it's not part of a conversation, it's - // the beginning of a new conversation. + // If it's not part of a conversation, it's the beginning + // of a new one (or belongs to a previously unknown URI). if (empty($stored->conversation)) { // $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); $stored->conversation = $conv->id; } @@ -1656,32 +1670,22 @@ class Notice extends Managed_DataObject 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 */ function getReplies() { - if (isset($this->_replies[$this->id])) { - return $this->_replies[$this->id]; + if (!isset($this->_replies[$this->getID()])) { + $mentions = Reply::multiGet('notice_id', array($this->getID())); + $this->_replies[$this->getID()] = $mentions->fetchAll('profile_id'); } - - $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; + return $this->_replies[$this->getID()]; } function _setReplies($replies) { - $this->_replies[$this->id] = $replies; + $this->_replies[$this->getID()] = $replies; } /** diff --git a/classes/Profile.php b/classes/Profile.php index f628965a74..09f9ca71d1 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -138,6 +138,18 @@ class Profile extends Managed_DataObject 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() { // FIXME: More types... like peopletags and whatever @@ -865,6 +877,11 @@ class Profile extends Managed_DataObject 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->_deleteSubscriptions(); $this->_deleteTags(); @@ -1395,6 +1412,11 @@ class Profile extends Managed_DataObject return $this->fullname; } + public function getHomepage() + { + return $this->homepage; + } + public function getDescription() { return $this->bio; @@ -1623,4 +1645,9 @@ class Profile extends Managed_DataObject public function setPref($namespace, $topic, $data) { return Profile_prefs::setData($this, $namespace, $topic, $data); } + + public function getConnectedApps($offset=0, $limit=null) + { + return $this->getUser()->getConnectedApps($offset, $limit); + } } diff --git a/classes/Profile_tag.php b/classes/Profile_tag.php index 6d6f49bedc..8034e68ccf 100644 --- a/classes/Profile_tag.php +++ b/classes/Profile_tag.php @@ -2,22 +2,15 @@ /** * Table Definition for profile_tag */ -require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; 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 $tagger; // int(4) primary_key not_null public $tagged; // int(4) primary_key not_null public $tag; // varchar(64) primary_key not_null 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() { return array( @@ -52,6 +45,16 @@ class Profile_tag extends Managed_DataObject 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) { $profile_list = new Profile_list(); @@ -88,7 +91,7 @@ class Profile_tag extends Managed_DataObject return $profile_list; } - static function getTagsArray($tagger, $tagged, $auth_user_id=null) + static function getTagsArray($tagger, $tagged, Profile $scoped=null) { $ptag = new Profile_tag(); @@ -100,7 +103,7 @@ class Profile_tag extends Managed_DataObject 'and profile_tag.tagged = %d ', $tagger, $tagged); - if ($auth_user_id != $tagger) { + if (!$scoped instanceof Profile || $scoped->getID() !== $tagger) { $qry .= 'and profile_list.private = 0'; } @@ -115,10 +118,10 @@ class Profile_tag extends Managed_DataObject return $tags; } - static function setTags($tagger, $tagged, $newtags, $privacy=array()) { + static function setTags($tagger, $tagged, array $newtags, array $privacy=array()) { $newtags = array_unique($newtags); - $oldtags = self::getTagsArray($tagger, $tagged, $tagger); + $oldtags = self::getTagsArray($tagger, $tagged, Profile::getByID($tagger)); $ptag = new Profile_tag(); @@ -149,19 +152,18 @@ class Profile_tag extends Managed_DataObject 'tag' => $tag)); # if tag already exists, return it - if(!empty($ptag)) { + if ($ptag instanceof Profile_tag) { return $ptag; } - $tagger_profile = Profile::getKV('id', $tagger); - $tagged_profile = Profile::getKV('id', $tagged); + $tagger_profile = Profile::getByID($tagger); + $tagged_profile = Profile::getByID($tagged); if (Event::handle('StartTagProfile', array($tagger_profile, $tagged_profile, $tag))) { if (!$tagger_profile->canTag($tagged_profile)) { // TRANS: Client exception thrown trying to set a tag for a user that cannot be tagged. throw new ClientException(_('You cannot tag this user.')); - return false; } $tags = new Profile_list(); @@ -174,7 +176,6 @@ class Profile_tag extends Managed_DataObject 'which is the maximum allowed number of tags. ' . 'Try using or deleting some existing tags.'), common_config('peopletag', 'maxtags'))); - return false; } $plist = new Profile_list(); @@ -188,7 +189,6 @@ class Profile_tag extends Managed_DataObject 'which is the maximum allowed number. ' . 'Try unlisting others first.'), common_config('peopletag', 'maxpeople'), $tag)); - return false; } $newtag = new Profile_tag(); @@ -199,9 +199,9 @@ class Profile_tag extends Managed_DataObject $result = $newtag->insert(); - if (!$result) { common_log_db_error($newtag, 'INSERT', __FILE__); + $plist->query('ROLLBACK'); return false; } @@ -212,7 +212,6 @@ class Profile_tag extends Managed_DataObject $newtag->delete(); $profile_list->delete(); throw $e; - return false; } $profile_list->taggedCount(true); @@ -233,20 +232,17 @@ class Profile_tag extends Managed_DataObject if (Event::handle('StartUntagProfile', array($ptag))) { $orig = clone($ptag); $result = $ptag->delete(); - if (!$result) { + if ($result === false) { common_log_db_error($this, 'DELETE', __FILE__); return false; } Event::handle('EndUntagProfile', array($orig)); - if ($result) { - $profile_list = Profile_list::pkeyGet(array('tag' => $tag, 'tagger' => $tagger)); - if (!empty($profile_list)) { - $profile_list->taggedCount(true); - } - self::blowCaches($tagger, $tagged); - return true; + $profile_list = Profile_list::pkeyGet(array('tag' => $tag, 'tagger' => $tagger)); + if (!empty($profile_list)) { + $profile_list->taggedCount(true); } - return false; + self::blowCaches($tagger, $tagged); + return true; } } diff --git a/classes/Queue_item.php b/classes/Queue_item.php index 3a7d05adef..d41c53e0e0 100644 --- a/classes/Queue_item.php +++ b/classes/Queue_item.php @@ -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 * 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(); if ($transports) { @@ -52,6 +52,11 @@ class Queue_item extends Managed_DataObject $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->whereAdd('claimed is null'); diff --git a/classes/User.php b/classes/User.php index ec6ceccf12..e33c83e89c 100644 --- a/classes/User.php +++ b/classes/User.php @@ -42,7 +42,6 @@ class User extends Managed_DataObject public $emailnotifynudge; // tinyint(1) default_1 public $emailnotifymsg; // tinyint(1) default_1 public $emailnotifyattn; // tinyint(1) default_1 - public $emailmicroid; // tinyint(1) default_1 public $language; // varchar(50) public $timezone; // varchar(50) 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'), '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'), - 'emailmicroid' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'whether to publish email microid'), 'language' => array('type' => 'varchar', 'length' => 50, 'description' => 'preferred language'), 'timezone' => array('type' => 'varchar', 'length' => 50, 'description' => 'timezone'), '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->emailnotifymsg = 1; $user->emailnotifyattn = 1; - $user->emailmicroid = 1; $user->emailpost = 1; - $user->jabbermicroid = 1; $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 - $user->password = common_munge_password($password, $id); + $user->password = common_munge_password($password); } $result = $user->insert(); @@ -466,16 +462,6 @@ class User extends Managed_DataObject 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) { // Add a new block record @@ -612,8 +598,10 @@ class User extends Managed_DataObject } try { - $profile = $this->getProfile(); - $profile->delete(); + if (!$this->hasRole(Profile_role::DELETED)) { + $profile = $this->getProfile(); + $profile->delete(); + } } catch (UserNoProfileException $unp) { 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(); } + public function hasPassword() + { + return !empty($this->password); + } + public function delPref($namespace, $topic) { return $this->getProfile()->delPref($namespace, $topic); diff --git a/classes/User_im_prefs.php b/classes/User_im_prefs.php index 16fd030bb4..6a5fc7f3ab 100644 --- a/classes/User_im_prefs.php +++ b/classes/User_im_prefs.php @@ -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 $notify; // tinyint(1) public $replies; // tinyint(1) - public $microid; // tinyint(1) public $updatefrompresence; // tinyint(1) public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00 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)'), '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'), - '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.'), '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'), diff --git a/extlib/Auth/OpenID/URINorm.php b/extlib/Auth/OpenID/URINorm.php index c051b550aa..32e84588db 100644 --- a/extlib/Auth/OpenID/URINorm.php +++ b/extlib/Auth/OpenID/URINorm.php @@ -93,7 +93,17 @@ function Auth_OpenID_pct_encoded_replace_unreserved($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) diff --git a/extlib/Mf2/Parser.php b/extlib/Mf2/Parser.php index 27805f2324..b8a954f2c8 100644 --- a/extlib/Mf2/Parser.php +++ b/extlib/Mf2/Parser.php @@ -13,17 +13,17 @@ use stdClass; /** * Parse Microformats2 - * + * * Functional shortcut for the commonest cases of parsing microformats2 from HTML. - * + * * Example usage: - * + * * use Mf2; * $output = Mf2\parse('Barnaby Walters'); * echo json_encode($output, JSON_PRETTY_PRINT); - * + * * Produces: - * + * * { * "items": [ * { @@ -35,7 +35,7 @@ use stdClass; * ], * "rels": {} * } - * + * * @param string|DOMDocument $input The HTML string or DOMDocument object to parse * @param string $url The URL the input document was found at, for relative URL resolution * @param bool $convertClassic whether or not to convert classic microformats @@ -84,7 +84,7 @@ function fetch($url, $convertClassic = true, &$curlInfo=null) { /** * Unicode to HTML Entities * @param string $input String containing characters to convert into HTML entities - * @return string + * @return string */ function unicodeToHtmlEntities($input) { return mb_convert_encoding($input, 'HTML-ENTITIES', mb_detect_encoding($input)); @@ -92,10 +92,10 @@ function unicodeToHtmlEntities($input) { /** * Collapse Whitespace - * + * * Collapses any sequences of whitespace within a string into a single space * character. - * + * * @deprecated since v0.2.3 * @param string $str * @return string @@ -113,10 +113,10 @@ function unicodeTrim($str) { /** * Microformat Name From Class string - * - * Given the value of @class, get the relevant mf classnames (e.g. h-card, + * + * Given the value of @class, get the relevant mf classnames (e.g. h-card, * p-name). - * + * * @param string $class A space delimited list of classnames * @param string $prefix The prefix to look for * @return string|array The prefixed name of the first microfomats class found or false @@ -127,9 +127,9 @@ function mfNamesFromClass($class, $prefix='h-') { $matches = array(); foreach ($classes as $classname) { - $compare_classname = strtolower(' ' . $classname); - $compare_prefix = strtolower(' ' . $prefix); - if (stristr($compare_classname, $compare_prefix) !== false && ($compare_classname != $compare_prefix)) { + $compare_classname = ' ' . $classname; + $compare_prefix = ' ' . $prefix; + if (strstr($compare_classname, $compare_prefix) !== false && ($compare_classname != $compare_prefix)) { $matches[] = ($prefix === 'h-') ? $classname : substr($classname, strlen($prefix)); } } @@ -139,10 +139,10 @@ function mfNamesFromClass($class, $prefix='h-') { /** * Get Nested µf Property Name From Class - * - * Returns all the p-, u-, dt- or e- prefixed classnames it finds in a + * + * Returns all the p-, u-, dt- or e- prefixed classnames it finds in a * space-separated string. - * + * * @param string $class * @return array */ @@ -153,19 +153,24 @@ function nestedMfPropertyNamesFromClass($class) { $class = str_replace(array(' ', ' ', "\n"), ' ', $class); foreach (explode(' ', $class) as $classname) { foreach ($prefixes as $prefix) { - $compare_classname = strtolower(' ' . $classname); - if (stristr($compare_classname, $prefix) && ($compare_classname != $prefix)) { - $propertyNames = array_merge($propertyNames, mfNamesFromClass($classname, ltrim($prefix))); + // Check if $classname is a valid property classname for $prefix. + if (mb_substr($classname, 0, mb_strlen($prefix)) == $prefix && $classname != $prefix) { + $propertyName = mb_substr($classname, mb_strlen($prefix)); + $propertyNames[$propertyName][] = $prefix; } } } + + foreach ($propertyNames as $property => $prefixes) { + $propertyNames[$property] = array_unique($prefixes); + } return $propertyNames; } /** * Wraps mfNamesFromClass to handle an element as input (common) - * + * * @param DOMElement $e The element to get the classname for * @param string $prefix The prefix to look for * @return mixed See return value of mf2\Parser::mfNameFromClass() @@ -192,28 +197,27 @@ function convertTimeFormat($time) { $hh = $mm = $ss = ''; 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])) { return $time; - } - // else am/pm specified - else { + } else { + // Otherwise, am/pm is specified. $meridiem = strtolower(str_replace('.', '', $matches[4])); - // hours + // Hours. $hh = $matches[1]; - // add 12 to the pm hours + // Add 12 to hours if pm applies. if ($meridiem == 'pm' && ($hh < 12)) { $hh += 12; } $hh = str_pad($hh, 2, '0', STR_PAD_LEFT); - // minutes + // Minutes. $mm = (empty($matches[2]) ) ? '00' : $matches[2]; - // seconds, only if supplied + // Seconds, only if supplied. if (!empty($matches[3])) { $ss = $matches[3]; } @@ -229,11 +233,11 @@ function convertTimeFormat($time) { /** * Microformats2 Parser - * + * * A class which holds state for parsing microformats2 from HTML. - * + * * Example usage: - * + * * use Mf2; * $parser = new Mf2\Parser('Barnaby Walters
'); * $output = $parser->parse(); @@ -244,18 +248,18 @@ class Parser { /** @var DOMXPath object which can be used to query over any fragment*/ public $xpath; - + /** @var DOMDocument */ public $doc; - + /** @var SplObjectStorage */ protected $parsed; - + public $jsonMode; /** * Constructor - * + * * @param DOMDocument|string $input The data to parse. A string of HTML or a DOMDocument * @param string $url The URL of the parsed document, for relative URL resolution * @param boolean $jsonMode Whether or not to use a stdClass instance for an empty `rels` dictionary. This breaks PHP looping over rels, but allows the output to be correctly serialized as JSON. @@ -271,20 +275,20 @@ class Parser { $doc = new DOMDocument(); @$doc->loadHTML(''); } - + $this->xpath = new DOMXPath($doc); - + $baseurl = $url; foreach ($this->xpath->query('//base[@href]') as $base) { $baseElementUrl = $base->getAttribute('href'); - + if (parse_url($baseElementUrl, PHP_URL_SCHEME) === null) { /* The base element URL is relative to the document URL. * * :/ * * Perhaps the author was high? */ - + $baseurl = resolveUrl($url, $baseElementUrl); } else { $baseurl = $baseElementUrl; @@ -296,31 +300,31 @@ class Parser { foreach ($this->xpath->query('//template') as $templateEl) { $templateEl->parentNode->removeChild($templateEl); } - + $this->baseurl = $baseurl; $this->doc = $doc; $this->parsed = new SplObjectStorage(); $this->jsonMode = $jsonMode; } - + private function elementPrefixParsed(\DOMElement $e, $prefix) { if (!$this->parsed->contains($e)) $this->parsed->attach($e, array()); - + $prefixes = $this->parsed[$e]; $prefixes[] = $prefix; $this->parsed[$e] = $prefixes; } - + private function isElementParsed(\DOMElement $e, $prefix) { if (!$this->parsed->contains($e)) return false; - + $prefixes = $this->parsed[$e]; - + if (!in_array($prefix, $prefixes)) return false; - + return true; } @@ -352,72 +356,72 @@ class Parser { // TODO: figure out if this has problems with sms: and geo: URLs public function resolveUrl($url) { - // If the URL is seriously malformed it’s probably beyond the scope of this + // If the URL is seriously malformed it’s probably beyond the scope of this // parser to try to do anything with it. if (parse_url($url) === false) return $url; - + $scheme = parse_url($url, PHP_URL_SCHEME); - + if (empty($scheme) and !empty($this->baseurl)) { return resolveUrl($this->baseurl, $url); } else { return $url; } } - + // Parsing Functions - + /** - * Parse value-class/value-title on an element, joining with $separator if + * Parse value-class/value-title on an element, joining with $separator if * there are multiple. - * + * * @param \DOMElement $e * @param string $separator = '' if multiple value-title elements, join with this string * @return string|null the parsed value or null if value-class or -title aren’t in use */ public function parseValueClassTitle(\DOMElement $e, $separator = '') { $valueClassElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ")]', $e); - + if ($valueClassElements->length !== 0) { // Process value-class stuff $val = ''; foreach ($valueClassElements as $el) { $val .= $this->textContent($el); } - + return unicodeTrim($val); } - + $valueTitleElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value-title ")]', $e); - + if ($valueTitleElements->length !== 0) { // Process value-title stuff $val = ''; foreach ($valueTitleElements as $el) { $val .= $el->getAttribute('title'); } - + return unicodeTrim($val); } - + // No value-title or -class in this element return null; } - + /** * Given an element with class="p-*", get it’s value - * + * * @param DOMElement $p The element to parse * @return string The plaintext value of $p, dependant on type * @todo Make this adhere to value-class */ public function parseP(\DOMElement $p) { $classTitle = $this->parseValueClassTitle($p, ' '); - + if ($classTitle !== null) return $classTitle; - + if ($p->tagName == 'img' and $p->getAttribute('alt') !== '') { $pValue = $p->getAttribute('alt'); } elseif ($p->tagName == 'area' and $p->getAttribute('alt') !== '') { @@ -429,13 +433,13 @@ class Parser { } else { $pValue = unicodeTrim($this->textContent($p)); } - + return $pValue; } /** * Given an element with class="u-*", get the value of the URL - * + * * @param DOMElement $u The element to parse * @return string The plaintext value of $u, dependant on type * @todo make this adhere to value-class @@ -443,18 +447,18 @@ class Parser { public function parseU(\DOMElement $u) { if (($u->tagName == 'a' or $u->tagName == 'area') and $u->getAttribute('href') !== null) { $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'); } elseif ($u->tagName == 'object' and $u->getAttribute('data') !== null) { $uValue = $u->getAttribute('data'); } - + if (isset($uValue)) { return $this->resolveUrl($uValue); } - + $classTitle = $this->parseValueClassTitle($u); - + if ($classTitle !== null) { return $classTitle; } elseif ($u->tagName == 'abbr' and $u->getAttribute('title') !== null) { @@ -468,7 +472,7 @@ class Parser { /** * Given an element with class="dt-*", get the value of the datetime as a php date object - * + * * @param DOMElement $dt The element to parse * @param array $dates Array of dates processed so far * @return string The datetime string found @@ -477,11 +481,11 @@ class Parser { // Check for value-class pattern $valueClassChildren = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ") or contains(concat(" ", @class, " "), " value-title ")]', $dt); $dtValue = false; - + if ($valueClassChildren->length > 0) { // They’re using value-class $dateParts = array(); - + foreach ($valueClassChildren as $e) { if (strstr(' ' . $e->getAttribute('class') . ' ', ' value-title ')) { $title = $e->getAttribute('title'); @@ -591,16 +595,16 @@ class Parser { $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]; } } /** - * if $dtValue is only a time and there are recently parsed dates, - * form the full date-time using the most recnetly parsed dt- value + * if $dtValue is only a time and there are recently parsed dates, + * 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 = end($dates) . 'T' . unicodeTrim($dtValue, 'T'); } @@ -613,15 +617,15 @@ class Parser { * * @param DOMElement $e The element to parse * @return string $e’s innerHTML - * + * * @todo need to mark this element as e- parsed so it doesn’t get parsed as it’s parent’s e-* too */ public function parseE(\DOMElement $e) { $classTitle = $this->parseValueClassTitle($e); - + if ($classTitle !== null) return $classTitle; - + // Expand relative URLs within children of this element // TODO: as it is this is not relative to only children, make this .// and rerun tests $this->resolveChildUrls($e); @@ -630,7 +634,7 @@ class Parser { foreach ($e->childNodes as $node) { $html .= $node->C14N(); } - + return array( 'html' => $html, 'value' => unicodeTrim($this->textContent($e)) @@ -639,7 +643,7 @@ class Parser { /** * Recursively parse microformats - * + * * @param DOMElement $e The element to parse * @return array A representation of the values contained within microformat $e */ @@ -660,26 +664,39 @@ class Parser { foreach ($this->xpath->query('.//*[contains(concat(" ", @class)," h-")]', $e) as $subMF) { // Parse $result = $this->parseH($subMF); - + // If result was already parsed, skip it if (null === $result) 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); // Does this µf have any property names other than h-*? $properties = nestedMfPropertyNamesFromElement($subMF); - + if (!empty($properties)) { // Yes! It’s a nested property µf - foreach ($properties as $property) { - $return[$property][] = $result; + foreach ($properties as $property => $prefixes) { + // 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 { // No, it’s a child µf $children[] = $result; } - + // Make sure this sub-mf won’t get parsed as a µf or property // TODO: Determine if clearing this is required? $this->elementPrefixParsed($subMF, 'h'); @@ -689,19 +706,24 @@ class Parser { $this->elementPrefixParsed($subMF, 'e'); } + if($e->tagName == 'area') { + $coords = $e->getAttribute('coords'); + $shape = $e->getAttribute('shape'); + } + // Handle p-* foreach ($this->xpath->query('.//*[contains(concat(" ", @class) ," p-")]', $e) as $p) { if ($this->isElementParsed($p, 'p')) continue; $pValue = $this->parseP($p); - + // Add the value to the array for it’s p- properties foreach (mfNamesFromElement($p, 'p-') as $propName) { if (!empty($propName)) $return[$propName][] = $pValue; } - + // Make sure this sub-mf won’t get parsed as a top level mf $this->elementPrefixParsed($p, 'p'); } @@ -710,32 +732,32 @@ class Parser { foreach ($this->xpath->query('.//*[contains(concat(" ", @class)," u-")]', $e) as $u) { if ($this->isElementParsed($u, 'u')) continue; - + $uValue = $this->parseU($u); - + // Add the value to the array for it’s property types foreach (mfNamesFromElement($u, 'u-') as $propName) { $return[$propName][] = $uValue; } - + // Make sure this sub-mf won’t get parsed as a top level mf $this->elementPrefixParsed($u, 'u'); } - + // Handle dt-* foreach ($this->xpath->query('.//*[contains(concat(" ", @class), " dt-")]', $e) as $dt) { if ($this->isElementParsed($dt, 'dt')) continue; - + $dtValue = $this->parseDT($dt, $dates); - + if ($dtValue) { // Add the value to the array for dt- properties foreach (mfNamesFromElement($dt, 'dt-') as $propName) { $return[$propName][] = $dtValue; } } - + // Make sure this sub-mf won’t get parsed as a top level mf $this->elementPrefixParsed($dt, 'dt'); } @@ -762,22 +784,43 @@ class Parser { if (!array_key_exists('name', $return)) { try { // 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')); - + if ($e->tagName == 'abbr' and $e->hasAttribute('title')) throw new Exception($e->getAttribute('title')); - + // Look for nested img @alt 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')); + } } + // 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 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')); + } + } + + // 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); @@ -812,36 +855,58 @@ class Parser { // Check for u-url if (!array_key_exists('url', $return)) { // Look for img @src - if ($e->tagName == 'a') + if ($e->tagName == 'a' or $e->tagName == 'area') $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) { - $url = $em->getAttribute('href'); - break; + $emNames = mfNamesFromElement($em, 'h-'); + if (empty($emNames)) { + $url = $em->getAttribute('href'); + 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)) $return['url'][] = $this->resolveUrl($url); } // Make sure things are in alphabetical order sort($mfTypes); - + // Phew. Return the final result. $parsed = array( 'type' => $mfTypes, '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)); + } return $parsed; } - + /** * Parse Rels and Alternatives - * - * Returns [$rels, $alternatives]. If the $rels value is to be empty, i.e. there are no links on the page + * + * Returns [$rels, $alternatives]. If the $rels value is to be empty, i.e. there are no links on the page * with a rel value *not* containing `alternate`, then the type of $rels depends on $this->jsonMode. If set * to true, it will be a stdClass instance, optimising for JSON serialisation. Otherwise (the default case), * it will be an empty array. @@ -849,18 +914,18 @@ class Parser { public function parseRelsAndAlternates() { $rels = array(); $alternates = array(); - + // Iterate through all a, area and link elements with rel attributes foreach ($this->xpath->query('//*[@rel and @href]') as $hyperlink) { if ($hyperlink->getAttribute('rel') == '') continue; - + // Resolve the href $href = $this->resolveUrl($hyperlink->getAttribute('href')); - + // Split up the rel into space-separated values $linkRels = array_filter(explode(' ', $hyperlink->getAttribute('rel'))); - + // If alternate in rels, create alternate structure, append if (in_array('alternate', $linkRels)) { $alt = array( @@ -869,10 +934,19 @@ class Parser { ); if ($hyperlink->hasAttribute('media')) $alt['media'] = $hyperlink->getAttribute('media'); - + if ($hyperlink->hasAttribute('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; } else { foreach ($linkRels as $rel) { @@ -880,38 +954,38 @@ class Parser { } } } - + if (empty($rels) and $this->jsonMode) { $rels = new stdClass(); } - + return array($rels, $alternates); } - + /** * Kicks off the parsing routine - * + * * If `$htmlSafe` is set, any angle brackets in the results from non e-* properties * will be HTML-encoded, bringing all output to the same level of encoding. - * + * * If a DOMElement is set as the $context, only descendants of that element will * be parsed for microformats. - * + * * @param bool $htmlSafe whether or not to html-encode non e-* properties. Defaults to false * @param DOMElement $context optionally an element from which to parse microformats * @return array An array containing all the µfs found in the current document */ public function parse($convertClassic = true, DOMElement $context = null) { $mfs = array(); - + if ($convertClassic) { $this->convertLegacy(); } - + $mfElements = null === $context ? $this->xpath->query('//*[contains(concat(" ", @class), " h-")]') : $this->xpath->query('.//*[contains(concat(" ", @class), " h-")]', $context); - + // Parser microformats foreach ($mfElements as $node) { // For each microformat @@ -920,64 +994,64 @@ class Parser { // Add the value to the array for this property type $mfs[] = $result; } - + // Parse rels list($rels, $alternates) = $this->parseRelsAndAlternates(); - + $top = array( 'items' => array_values(array_filter($mfs)), 'rels' => $rels ); - + if (count($alternates)) $top['alternates'] = $alternates; - + return $top; } - + /** * Parse From ID - * + * * Given an ID, parse all microformats which are children of the element with * that ID. - * + * * Note that rel values are still document-wide. - * - * If an element with the ID is not found, an empty skeleton mf2 array structure + * + * If an element with the ID is not found, an empty skeleton mf2 array structure * will be returned. - * + * * @param string $id * @param bool $htmlSafe = false whether or not to HTML-encode angle brackets in non e-* properties * @return array */ public function parseFromId($id, $convertClassic=true) { $matches = $this->xpath->query("//*[@id='{$id}']"); - + if (empty($matches)) return array('items' => array(), 'rels' => array(), 'alternates' => array()); - + return $this->parse($convertClassic, $matches->item(0)); } /** * Convert Legacy Classnames - * + * * Adds microformats2 classnames into a document containing only legacy * semantic classnames. - * + * * @return Parser $this */ public function convertLegacy() { $doc = $this->doc; $xp = new DOMXPath($doc); - + // replace all roots foreach ($this->classicRootMap as $old => $new) { foreach ($xp->query('//*[contains(concat(" ", @class, " "), " ' . $old . ' ") and not(contains(concat(" ", @class, " "), " ' . $new . ' "))]') as $el) { $el->setAttribute('class', $el->getAttribute('class') . ' ' . $new); } } - + foreach ($this->classicPropertyMap as $oldRoot => $properties) { $newRoot = $this->classicRootMap[$oldRoot]; foreach ($properties as $old => $new) { @@ -986,16 +1060,16 @@ class Parser { } } } - + return $this; } - + /** * XPath Query - * + * * Runs an XPath query over the current document. Works in exactly the same * way as DOMXPath::query. - * + * * @param string $expression * @param DOMNode $context * @return DOMNodeList @@ -1003,7 +1077,7 @@ class Parser { public function query($expression, $context = null) { return $this->xpath->query($expression, $context); } - + /** * Classic Root Classname map */ @@ -1013,11 +1087,11 @@ class Parser { 'hentry' => 'h-entry', 'hrecipe' => 'h-recipe', 'hresume' => 'h-resume', - 'hevent' => 'h-event', + 'vevent' => 'h-event', 'hreview' => 'h-review', 'hproduct' => 'h-product' ); - + public $classicPropertyMap = array( 'vcard' => array( 'fn' => 'p-name', @@ -1084,7 +1158,7 @@ class Parser { 'skill' => 'p-skill', 'affiliation' => 'p-affiliation h-card', ), - 'hevent' => array( + 'vevent' => array( 'dtstart' => 'dt-start', 'dtend' => 'dt-end', 'duration' => 'dt-duration', @@ -1246,7 +1320,7 @@ function resolveUrl($baseURI, $referenceURI) { # 5.2.3 Merge Paths function mergePaths($base, $reference) { # If the base URI has a defined authority component and an empty - # path, + # path, if($base['authority'] && $base['path'] == null) { # then return a string consisting of "/" concatenated with the # reference's path; otherwise, diff --git a/lib/apignusocialoauthdatastore.php b/lib/apignusocialoauthdatastore.php index da412df8d2..6f4403dec7 100644 --- a/lib/apignusocialoauthdatastore.php +++ b/lib/apignusocialoauthdatastore.php @@ -26,20 +26,20 @@ require_once 'OAuth.php'; */ 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) { // Create an anon consumer and anon application if one // doesn't exist already - if ($consumerKey == 'anonymous') { + if ($consumer_key == 'anonymous') { common_debug("API OAuth - creating anonymous consumer"); $con = new Consumer(); - $con->consumer_key = $consumerKey; - $con->consumer_secret = $consumerKey; + $con->consumer_key = $consumer_key; + $con->consumer_secret = $consumer_key; $con->created = common_sql_now(); $result = $con->insert(); @@ -388,7 +388,7 @@ class ApiGNUsocialOAuthDataStore extends OAuthDataStore * * @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->consumer_key = $consumer->key; @@ -473,13 +473,13 @@ class ApiGNUsocialOAuthDataStore extends OAuthDataStore * @param type $token_key * @return OAuthToken */ - function lookup_token($consumer, $token_type, $token_key) + function lookup_token($consumer, $token_type, $token) { $t = new Token(); if (!is_null($consumer)) { $t->consumer_key = $consumer->key; } - $t->tok = $token_key; + $t->tok = $token; $t->type = ($token_type == 'access') ? 1 : 0; if ($t->find(true)) { return new OAuthToken($t->tok, $t->secret); diff --git a/lib/applicationlist.php b/lib/applicationlist.php index d0c9256df7..ab51a73096 100644 --- a/lib/applicationlist.php +++ b/lib/applicationlist.php @@ -27,13 +27,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR . '/lib/widget.php'; - -define('APPS_PER_PAGE', 20); +if (!defined('GNUSOCIAL')) { exit(1); } /** * Widget to show a list of OAuth applications @@ -52,16 +46,12 @@ class ApplicationList extends Widget /** Owner of this list */ var $owner = null; - /** Action object using us. */ - var $action = null; - - function __construct($application, $owner=null, $action=null) + function __construct($application, Profile $owner, Action $out=null) { - parent::__construct($action); + parent::__construct($out); $this->application = $application; $this->owner = $owner; - $this->action = $action; } function show() @@ -75,7 +65,7 @@ class ApplicationList extends Widget if($cnt > APPS_PER_PAGE) { break; } - $this->showapplication(); + $this->showApplication(); } $this->out->elementEnd('ul'); @@ -85,8 +75,6 @@ class ApplicationList extends Widget function showApplication() { - $user = common_current_user(); - $this->out->elementStart('li', array('class' => 'application h-entry', 'id' => 'oauthclient-' . $this->application->id)); @@ -119,140 +107,3 @@ class ApplicationList extends Widget return; } } - -/** - * Widget to show a list of connected OAuth clients - * - * @category Application - * @package StatusNet - * @author Zach Copley