Merge branch 'nightly'

This commit is contained in:
Mikael Nordfeldth 2015-09-06 01:54:00 +02:00
commit d448275713
216 changed files with 3186 additions and 5512 deletions

View File

@ -563,6 +563,11 @@ sslserver: if specified, this server will be used when creating HTTPS
sslpath: if this and the sslserver are specified, this path will be used sslpath: if this and the sslserver are specified, this path will be used
when creating HTTPS URLs. Otherwise, the attachments|path value when creating HTTPS URLs. Otherwise, the attachments|path value
will be used. will be used.
show_thumbs: show thumbnails in notice lists for uploaded images, and photos
and videos linked remotely that provide oEmbed info. Defaults to true.
show_html: show (filtered) text/html attachments (and oEmbed HTML etc.).
Doesn't affect AJAX calls. Defaults to false.
filename_base: for new files, choose one: 'upload', 'hash'. Defaults to hash.
group group
----- -----

View File

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

View File

@ -196,7 +196,9 @@ your server (like lighttpd or nginx).
file is well commented. file is well commented.
* For lighttpd, inspect the lighttpd.conf.example file and apply the * For lighttpd, inspect the lighttpd.conf.example file and apply the
appropriate changes in your virtualhost configuration for lighttpd. appropriate changes in your virtualhost configuration for lighttpd.
* For nginx and other webservers, we gladly accept contributions of * For nginx, inspect the nginx.conf.sample file and apply the appropriate
changes.
* For other webservers, we gladly accept contributions of
server configuration examples. server configuration examples.
2. Assuming your webserver is properly configured and have its settings 2. Assuming your webserver is properly configured and have its settings

View File

@ -39,8 +39,6 @@ if (!defined('GNUSOCIAL')) { exit(1); }
class AllAction extends ShowstreamAction class AllAction extends ShowstreamAction
{ {
var $notice;
public function getStream() public function getStream()
{ {
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) { if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
@ -54,7 +52,7 @@ class AllAction extends ShowstreamAction
function title() function title()
{ {
if (!empty($this->scoped) && $this->scoped->id == $this->target->id) { if (!empty($this->scoped) && $this->scoped->sameAs($this->target)) {
// TRANS: Title of a user's own start page. // TRANS: Title of a user's own start page.
return _('Home timeline'); return _('Home timeline');
} else { } else {
@ -71,44 +69,44 @@ class AllAction extends ShowstreamAction
common_local_url( common_local_url(
'ApiTimelineFriends', array( 'ApiTimelineFriends', array(
'format' => 'as', 'format' => 'as',
'id' => $this->target->nickname 'id' => $this->target->getNickname()
) )
), ),
// TRANS: %s is user nickname. // TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->nickname)), sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->getNickname())),
new Feed(Feed::RSS1, new Feed(Feed::RSS1,
common_local_url( common_local_url(
'allrss', array( 'allrss', array(
'nickname' => 'nickname' =>
$this->target->nickname) $this->target->getNickname())
), ),
// TRANS: %s is user nickname. // TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->target->nickname)), sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->target->getNickname())),
new Feed(Feed::RSS2, new Feed(Feed::RSS2,
common_local_url( common_local_url(
'ApiTimelineFriends', array( 'ApiTimelineFriends', array(
'format' => 'rss', 'format' => 'rss',
'id' => $this->target->nickname 'id' => $this->target->getNickname()
) )
), ),
// TRANS: %s is user nickname. // TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->nickname)), sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->getNickname())),
new Feed(Feed::ATOM, new Feed(Feed::ATOM,
common_local_url( common_local_url(
'ApiTimelineFriends', array( 'ApiTimelineFriends', array(
'format' => 'atom', 'format' => 'atom',
'id' => $this->target->nickname 'id' => $this->target->getNickname()
) )
), ),
// TRANS: %s is user nickname. // TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (Atom)'), $this->target->nickname)) sprintf(_('Feed for friends of %s (Atom)'), $this->target->getNickname()))
); );
} }
function showEmptyListMessage() function showEmptyListMessage()
{ {
// TRANS: Empty list message. %s is a user nickname. // TRANS: Empty list message. %s is a user nickname.
$message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->target->nickname) . ' '; $message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->target->getNickname()) . ' ';
if (common_logged_in()) { if (common_logged_in()) {
if ($this->target->id === $this->scoped->id) { if ($this->target->id === $this->scoped->id) {
@ -118,12 +116,12 @@ class AllAction extends ShowstreamAction
} else { } else {
// TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@". // TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@".
// TRANS: This message contains Markdown links. Keep "](" together. // TRANS: This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->target->nickname, $this->target->nickname, '@' . $this->target->nickname); $message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->target->getNickname(), $this->target->getNickname(), '@' . $this->target->getNickname());
} }
} else { } else {
// TRANS: Encouragement displayed on empty timeline user pages for anonymous users. // TRANS: Encouragement displayed on empty timeline user pages for anonymous users.
// TRANS: %s is a user nickname. This message contains Markdown links. Keep "](" together. // TRANS: %s is a user nickname. This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->nickname); $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->getNickname());
} }
$this->elementStart('div', 'guide'); $this->elementStart('div', 'guide');
@ -134,19 +132,10 @@ class AllAction extends ShowstreamAction
function showContent() function showContent()
{ {
if (Event::handle('StartShowAllContent', array($this))) { if (Event::handle('StartShowAllContent', array($this))) {
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
$profile = null;
$current_user = common_current_user();
if (!empty($current_user)) {
$profile = $current_user->getProfile();
}
if (!empty($current_user) && $current_user->streamModeOnly()) {
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE)); $nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
} else { } else {
$nl = new ThreadedNoticeList($this->notice, $this, $profile); $nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
} }
$cnt = $nl->show(); $cnt = $nl->show();
@ -157,7 +146,7 @@ class AllAction extends ShowstreamAction
$this->pagination( $this->pagination(
$this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'all', array('nickname' => $this->target->nickname) $this->page, 'all', array('nickname' => $this->target->getNickname())
); );
Event::handle('EndShowAllContent', array($this)); Event::handle('EndShowAllContent', array($this));

View File

@ -28,11 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/rssaction.php';
/** /**
* RSS feed for user and friends timeline. * RSS feed for user and friends timeline.
@ -46,52 +42,12 @@ require_once INSTALLDIR.'/lib/rssaction.php';
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */
class AllrssAction extends Rss10Action class AllrssAction extends TargetedRss10Action
{ {
var $user = null; protected function getNotices()
/**
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean false if user doesn't exist
*
*/
function prepare($args)
{ {
parent::prepare($args); $stream = new InboxNoticeStream($this->target);
$nickname = $this->trimmed('nickname'); return $stream->getNotices(0, $this->limit)->fetchAll();
$this->user = User::getKV('nickname', $nickname);
if (!$this->user) {
// TRANS: Client error when user not found for an rss related action.
$this->clientError(_('No such user.'));
} else {
$this->notices = $this->getNotices($this->limit);
return true;
}
}
/**
* Get notices
*
* @param integer $limit max number of notices to return
*
* @return array notices
*/
function getNotices($limit=0)
{
$stream = new InboxNoticeStream($this->user->getProfile());
$notice = $stream->getNotices(0, $limit, null, null);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
/** /**
@ -101,33 +57,17 @@ class AllrssAction extends Rss10Action
*/ */
function getChannel() function getChannel()
{ {
$user = $this->user;
$c = array('url' => common_local_url('allrss', $c = array('url' => common_local_url('allrss',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname. // TRANS: Message is used as link title. %s is a user nickname.
'title' => sprintf(_('%s and friends'), $user->nickname), 'title' => sprintf(_('%s and friends'), $this->target->getNickname()),
'link' => common_local_url('all', 'link' => common_local_url('all',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: Message is used as link description. %1$s is a username, %2$s is a site name. // TRANS: Message is used as link description. %1$s is a username, %2$s is a site name.
'description' => sprintf(_('Updates from %1$s and friends on %2$s!'), 'description' => sprintf(_('Updates from %1$s and friends on %2$s!'),
$user->nickname, common_config('site', 'name'))); $this->target->getNickname(), common_config('site', 'name')));
return $c; return $c;
} }
/**
* Get image.
*
* @return string user avatar URL or null
*/
function getImage()
{
$user = $this->user;
$profile = $user->getProfile();
if (!$profile) {
return null;
}
return $profile->avatarUrl(AVATAR_PROFILE_SIZE);
}
} }

View File

@ -96,21 +96,12 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$original = clone($profile); $original = clone($profile);
if (!empty($this->name)) { $profile->fullname = $this->name;
$profile->fullname = $this->name; $profile->homepage = $this->url;
} $profile->bio = $this->description;
$profile->location = $this->location;
if (!empty($this->url)) {
$profile->homepage = $this->url;
}
if (!empty($this->description)) {
$profile->bio = $this->description;
}
if (!empty($this->location)) { if (!empty($this->location)) {
$profile->location = $this->location;
$loc = Location::fromName($this->location); $loc = Location::fromName($this->location);
if (!empty($loc)) { if (!empty($loc)) {
@ -119,6 +110,12 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$profile->location_id = $loc->location_id; $profile->location_id = $loc->location_id;
$profile->location_ns = $loc->location_ns; $profile->location_ns = $loc->location_ns;
} }
} else {
// location is empty so reset the extrapolated information too
$profile->lat = '';
$profile->lon = '';
$profile->location_id = '';
$profile->location_ns = '';
} }
$result = $profile->update($original); $result = $profile->update($original);

View File

@ -51,10 +51,14 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
{ {
var $notices = null; var $notices = null;
protected function doPreparation() protected function prepare(array $args=array())
{ {
parent::prepare($args);
$this->tag = $this->arg('tag'); $this->tag = $this->arg('tag');
$this->notices = $this->getNotices(); $this->notices = $this->getNotices();
return true;
} }
/** /**

View File

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

View File

@ -28,80 +28,24 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
// @todo FIXME: documentation needed. // @todo FIXME: documentation needed.
class DeletenoticeAction extends Action class DeletenoticeAction extends FormAction
{ {
var $error = null; protected $notice = null;
var $user = null;
var $notice = null;
var $profile = null;
var $user_profile = null;
function prepare($args) protected function doPreparation()
{ {
parent::prepare($args); $this->notice = Notice::getByID($this->trimmed('notice'));
$this->user = common_current_user(); if (!$this->scoped->sameAs($this->notice->getProfile()) &&
!$this->scoped->hasRight(Right::DELETEOTHERSNOTICE)) {
if (!$this->user) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user.
common_user_error(_('Not logged in.'));
exit;
}
$notice_id = $this->trimmed('notice');
$this->notice = Notice::getKV($notice_id);
if (!$this->notice) {
// TRANS: Error message displayed trying to delete a non-existing notice.
common_user_error(_('No such notice.'));
exit;
}
$this->profile = $this->notice->getProfile();
$this->user_profile = $this->user->getProfile();
return true;
}
function handle($args)
{
parent::handle($args);
if ($this->notice->profile_id != $this->user_profile->id &&
!$this->user->hasRight(Right::DELETEOTHERSNOTICE)) {
// TRANS: Error message displayed trying to delete a notice that was not made by the current user. // TRANS: Error message displayed trying to delete a notice that was not made by the current user.
common_user_error(_('Cannot delete this notice.')); $this->clientError(_('Cannot delete this notice.'));
exit;
} }
// XXX: Ajax!
if ($_SERVER['REQUEST_METHOD'] == 'POST') { $this->formOpts['notice'] = $this->notice;
$this->deleteNotice();
} else if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$this->showForm();
}
}
/**
* Show the page notice
*
* Shows instructions for the page
*
* @return void
*/
function showPageNotice()
{
$instr = $this->getInstructions();
$output = common_markup_to_html($instr);
$this->elementStart('div', 'instructions');
$this->raw($output);
$this->elementEnd('div');
} }
function getInstructions() function getInstructions()
@ -117,84 +61,17 @@ class DeletenoticeAction extends Action
return _('Delete notice'); return _('Delete notice');
} }
/** protected function doPost()
* Wrapper for showing a page
*
* Stores an error and shows the page
*
* @param string $error Error, if any
*
* @return void
*/
function showForm($error = null)
{ {
$this->error = $error;
$this->showPage();
}
/**
* Insert delete notice form into the content
*
* @return void
*/
function showContent()
{
$this->elementStart('form', array('id' => 'form_notice_delete',
'class' => 'form_settings',
'method' => 'post',
'action' => common_local_url('deletenotice')));
$this->elementStart('fieldset');
// TRANS: Fieldset legend for the delete notice form.
$this->element('legend', null, _('Delete notice'));
$this->hidden('token', common_session_token());
$this->hidden('notice', $this->trimmed('notice'));
// TRANS: Message for the delete notice form.
$this->element('p', null, _('Are you sure you want to delete this notice?'));
$this->submit('form_action-no',
// TRANS: Button label on the delete notice form.
_m('BUTTON','No'),
'submit form_action-primary',
'no',
// TRANS: Submit button title for 'No' when deleting a notice.
_('Do not delete this notice.'));
$this->submit('form_action-yes',
// TRANS: Button label on the delete notice form.
_m('BUTTON','Yes'),
'submit form_action-secondary',
'yes',
// TRANS: Submit button title for 'Yes' when deleting a notice.
_('Delete this notice.'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
function deleteNotice()
{
// 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('yes')) { if ($this->arg('yes')) {
if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) { if (Event::handle('StartDeleteOwnNotice', array($this->scoped->getUser(), $this->notice))) {
$this->notice->delete(); $this->notice->delete();
Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice)); Event::handle('EndDeleteOwnNotice', array($this->scoped->getUser(), $this->notice));
} }
}
$url = common_get_returnto();
if ($url) {
common_set_returnto(null);
} else { } else {
$url = common_local_url('public'); common_redirect(common_get_returnto(), 303);
} }
common_redirect($url, 303); common_redirect(common_local_url('top'), 303);
} }
} }

View File

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

View File

@ -17,24 +17,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
define('LISTENER', 1); define('LISTENER', 1);
define('LISTENEE', -1); define('LISTENEE', -1);
define('BOTH', 0); define('BOTH', 0);
// @todo XXX: Documentation missing. // @todo XXX: Documentation missing.
class FoafAction extends Action class FoafAction extends ManagedAction
{ {
function isReadOnly($args) function isReadOnly($args)
{ {
return true; return true;
} }
function prepare($args) protected function doPreparation()
{ {
parent::prepare($args);
$nickname_arg = $this->arg('nickname'); $nickname_arg = $this->arg('nickname');
if (empty($nickname_arg)) { if (empty($nickname_arg)) {
@ -69,10 +67,8 @@ class FoafAction extends Action
return true; return true;
} }
function handle($args) public function showPage()
{ {
parent::handle($args);
header('Content-Type: application/rdf+xml'); header('Content-Type: application/rdf+xml');
$this->startXML(); $this->startXML();

View File

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

View File

@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/rssaction.php';
define('MEMBERS_PER_SECTION', 27); define('MEMBERS_PER_SECTION', 27);
@ -45,10 +41,10 @@ define('MEMBERS_PER_SECTION', 27);
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class groupRssAction extends Rss10Action class GroupRssAction extends TargetedRss10Action
{ {
/** group we're viewing. */ /** group we're viewing. */
var $group = null; protected $group = null;
/** /**
* Is this page read-only? * Is this page read-only?
@ -60,18 +56,8 @@ class groupRssAction extends Rss10Action
return true; return true;
} }
/** protected function doStreamPreparation()
* Prepare the action
*
* Reads and validates arguments and instantiates the attributes.
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{ {
parent::prepare($args);
$nickname_arg = $this->arg('nickname'); $nickname_arg = $this->arg('nickname');
$nickname = common_canonical_nickname($nickname_arg); $nickname = common_canonical_nickname($nickname_arg);
@ -90,52 +76,32 @@ class groupRssAction extends Rss10Action
$local = Local_group::getKV('nickname', $nickname); $local = Local_group::getKV('nickname', $nickname);
if (!$local) { if (!$local instanceof Local_group) {
// TRANS: Client error displayed when requesting a group RSS feed for group that does not exist. // TRANS: Client error displayed when requesting a group RSS feed for group that does not exist.
$this->clientError(_('No such group.'), 404); $this->clientError(_('No such group.'), 404);
} }
$this->group = User_group::getKV('id', $local->group_id); $this->group = $local->getGroup();
$this->target = $this->group->getProfile();
if (!$this->group) {
// TRANS: Client error displayed when requesting a group RSS feed for an object that is not a group.
$this->clientError(_('No such group.'), 404);
}
$this->notices = $this->getNotices($this->limit);
return true;
} }
function getNotices($limit=0) protected function getNotices()
{ {
$group = $this->group; $stream = $this->group->getNotices(0, $this->limit);
return $stream->fetchAll();
if (is_null($group)) {
return null;
}
$notices = array();
$notice = $group->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
function getChannel() function getChannel()
{ {
$group = $this->group;
$c = array('url' => common_local_url('grouprss', $c = array('url' => common_local_url('grouprss',
array('nickname' => array('nickname' =>
$group->nickname)), $this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname. // TRANS: Message is used as link title. %s is a user nickname.
'title' => sprintf(_('%s timeline'), $group->nickname), 'title' => sprintf(_('%s timeline'), $this->target->getNickname()),
'link' => common_local_url('showgroup', array('nickname' => $group->nickname)), 'link' => common_local_url('showgroup', array('nickname' => $this->target->getNickname())),
// TRANS: Message is used as link description. %1$s is a group name, %2$s is a site name. // TRANS: Message is used as link description. %1$s is a group name, %2$s is a site name.
'description' => sprintf(_('Updates from members of %1$s on %2$s!'), 'description' => sprintf(_('Updates from members of %1$s on %2$s!'),
$group->nickname, common_config('site', 'name'))); $this->target->getNickname(), common_config('site', 'name')));
return $c; return $c;
} }

View File

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

View File

@ -63,7 +63,7 @@ class LogoutAction extends ManagedAction
} }
Event::handle('EndLogout', array($this)); Event::handle('EndLogout', array($this));
common_redirect(common_local_url('startpage')); common_redirect(common_local_url('top'));
} }
// Accessed through the action on events // Accessed through the action on events

View File

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

View File

@ -28,11 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/rssaction.php';
/** /**
* RSS feed for notice search action class. * RSS feed for notice search action class.
@ -48,19 +44,7 @@ require_once INSTALLDIR.'/lib/rssaction.php';
*/ */
class NoticesearchrssAction extends Rss10Action class NoticesearchrssAction extends Rss10Action
{ {
function init() protected function getNotices()
{
return true;
}
function prepare($args)
{
parent::prepare($args);
$this->notices = $this->getNotices();
return true;
}
function getNotices($limit=0)
{ {
$q = $this->trimmed('q'); $q = $this->trimmed('q');
$notices = array(); $notices = array();
@ -70,8 +54,7 @@ class NoticesearchrssAction extends Rss10Action
$search_engine = $notice->getSearchEngine('notice'); $search_engine = $notice->getSearchEngine('notice');
$search_engine->set_sort_mode('chron'); $search_engine->set_sort_mode('chron');
if (!$limit) $limit = 20; $search_engine->limit(0, $this->limit, true);
$search_engine->limit(0, $limit, true);
if (false === $search_engine->query($q)) { if (false === $search_engine->query($q)) {
$cnt = 0; $cnt = 0;
} else { } else {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,11 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/rssaction.php';
/** /**
* RSS feed for public timeline. * RSS feed for public timeline.
@ -48,29 +44,6 @@ require_once INSTALLDIR.'/lib/rssaction.php';
*/ */
class PublicrssAction extends Rss10Action class PublicrssAction extends Rss10Action
{ {
/**
* Read arguments and initialize members
*
* @param array $args Arguments from $_REQUEST
* @return boolean success
*/
function prepare($args)
{
parent::prepare($args);
$this->notices = $this->getNotices($this->limit);
return true;
}
/**
* Initialization.
*
* @return boolean true
*/
function init()
{
return true;
}
/** /**
* Get notices * Get notices
* *
@ -78,15 +51,10 @@ class PublicrssAction extends Rss10Action
* *
* @return array notices * @return array notices
*/ */
function getNotices($limit=0) protected function getNotices()
{ {
$notices = array(); $stream = Notice::publicStream(0, $this->limit);
$notice = Notice::publicStream(0, ($limit == 0) ? 48 : $limit); return $stream->fetchAll();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
/** /**

View File

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

View File

@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/** /**
* Redirect to a given URL * Redirect to a given URL
@ -47,75 +43,27 @@ if (!defined('STATUSNET')) {
* @link http://status.net/ * @link http://status.net/
*/ */
class RedirecturlAction extends Action class RedirecturlAction extends ManagedAction
{ {
protected $id = null;
protected $file = null; protected $file = null;
/** protected function doPreparation()
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($argarray)
{ {
parent::prepare($argarray); $this->file = File::getByID($this->int('id'));
$this->id = $this->trimmed('id');
if (empty($this->id)) {
// TRANS: Client exception thrown when no ID parameter was provided.
throw new ClientException(_('No id parameter.'));
}
$this->file = File::getKV('id', $this->id);
if (empty($this->file)) {
// TRANS: Client exception thrown when an invalid ID parameter was provided for a file.
// TRANS: %d is the provided ID for which the file is not present (number).
throw new ClientException(sprintf(_('No such file "%d".'),
$this->id),
404);
}
return true; return true;
} }
/** public function showPage()
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handle($argarray=null)
{ {
common_redirect($this->file->url, 307); common_redirect($this->file->url, 307);
} }
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args) function isReadOnly($args)
{ {
return true; return true;
} }
/**
* Return last modified, if applicable.
*
* MAY override
*
* @return string last modified http header
*/
function lastModified() function lastModified()
{ {
// For comparison with If-Last-Modified // For comparison with If-Last-Modified
@ -133,9 +81,9 @@ class RedirecturlAction extends Action
*/ */
function etag() function etag()
{ {
return 'W/"' . implode(':', array($this->arg('action'), return 'W/"' . implode(':', array($this->getActionName(),
common_user_cache_hash(), common_user_cache_hash(),
common_language(), common_language(),
$this->file->id)) . '"'; $this->file->getID())) . '"';
} }
} }

View File

@ -40,9 +40,6 @@ if (!defined('GNUSOCIAL')) { exit(1); }
*/ */
class RepliesAction extends ShowstreamAction class RepliesAction extends ShowstreamAction
{ {
var $page = null;
var $notice;
public function getStream() public function getStream()
{ {
return new ReplyNoticeStream($this->target->getID(), $this->scoped); return new ReplyNoticeStream($this->target->getID(), $this->scoped);
@ -85,7 +82,7 @@ class RepliesAction extends ShowstreamAction
// TRANS: Link for feed with replies for a user. // TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
sprintf(_('Replies feed for %s (Activity Streams JSON)'), sprintf(_('Replies feed for %s (Activity Streams JSON)'),
$this->user->nickname)), $this->target->getNickname())),
new Feed(Feed::RSS1, new Feed(Feed::RSS1,
common_local_url('repliesrss', common_local_url('repliesrss',
array('nickname' => $this->target->getNickname())), array('nickname' => $this->target->getNickname())),

View File

@ -17,70 +17,34 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
require_once(INSTALLDIR.'/lib/rssaction.php');
// Formatting of RSS handled by Rss10Action // Formatting of RSS handled by Rss10Action
class RepliesrssAction extends Rss10Action class RepliesrssAction extends TargetedRss10Action
{ {
var $user = null; protected function getNotices()
function prepare($args)
{ {
parent::prepare($args); $stream = $this->target->getReplies(0, $this->limit);
$nickname = $this->trimmed('nickname'); return $stream->fetchAll();
$this->user = User::getKV('nickname', $nickname);
if (!$this->user) {
// TRANS: Client error displayed when providing a non-existing nickname in a RSS 1.0 action.
$this->clientError(_('No such user.'));
} else {
$this->notices = $this->getNotices($this->limit);
return true;
}
}
function getNotices($limit=0)
{
$user = $this->user;
$notice = $user->getReplies(0, ($limit == 0) ? 48 : $limit);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
function getChannel() function getChannel()
{ {
$user = $this->user;
$c = array('url' => common_local_url('repliesrss', $c = array('url' => common_local_url('repliesrss',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: RSS reply feed title. %s is a user nickname. // TRANS: RSS reply feed title. %s is a user nickname.
'title' => sprintf(_("Replies to %s"), $user->nickname), 'title' => sprintf(_("Replies to %s"), $this->target->getNickname()),
'link' => common_local_url('replies', 'link' => common_local_url('replies',
array('nickname' => array('nickname' => $this->target->getNickname())),
$user->nickname)),
// TRANS: RSS reply feed description. // TRANS: RSS reply feed description.
// TRANS: %1$s is a user nickname, %2$s is the StatusNet site name. // TRANS: %1$s is a user nickname, %2$s is the StatusNet site name.
'description' => sprintf(_('Replies to %1$s on %2$s.'), 'description' => sprintf(_('Replies to %1$s on %2$s.'),
$user->nickname, common_config('site', 'name'))); $this->target->getNickname(), common_config('site', 'name')));
return $c; return $c;
} }
function getImage()
{
$profile = $this->user->getProfile();
return $profile->avatarUrl(AVATAR_PROFILE_SIZE);
}
function isReadOnly($args) function isReadOnly($args)
{ {
return true; return true;

View File

@ -27,9 +27,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Prints out a static robots.txt * Prints out a static robots.txt
@ -40,19 +38,9 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */
class RobotstxtAction extends Action class RobotstxtAction extends ManagedAction
{ {
/** public function showPage()
* Handles requests
*
* Since this is a relatively static document, we
* don't do a prepare()
*
* @param array $args GET, POST, and URL params; unused.
*
* @return void
*/
function handle($args)
{ {
if (Event::handle('StartRobotsTxt', array($this))) { if (Event::handle('StartRobotsTxt', array($this))) {

View File

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

View File

@ -47,37 +47,6 @@ if (!defined('GNUSOCIAL')) { exit(1); }
*/ */
class ShowstreamAction extends NoticestreamAction class ShowstreamAction extends NoticestreamAction
{ {
var $notice;
protected function doPreparation()
{
// showstream requires a nickname
$nickname_arg = $this->arg('nickname');
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
if ($this->arg('page') && $this->arg('page') != 1) {
$args['page'] = $this->arg['page'];
}
common_redirect(common_local_url($this->getActionName(), $args), 301);
}
$this->user = User::getKV('nickname', $nickname);
if (!$this->user) {
$group = Local_group::getKV('nickname', $nickname);
if ($group instanceof Local_group) {
common_redirect($group->getProfile()->getUrl());
}
// TRANS: Client error displayed when calling a profile action without specifying a user.
$this->clientError(_('No such user.'), 404);
}
$this->target = $this->user->getProfile();
}
public function getStream() public function getStream()
{ {
if (empty($this->tag)) { if (empty($this->tag)) {
@ -194,13 +163,6 @@ class ShowstreamAction extends NoticestreamAction
'content' => $this->target->getDescription())); 'content' => $this->target->getDescription()));
} }
if ($this->target->isLocal() && $this->target->getUser()->emailmicroid && $this->target->getUser()->email && $this->target->getUrl()) {
$id = new Microid('mailto:'.$this->target->getUser()->email,
$this->selfUrl());
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
}
// See https://wiki.mozilla.org/Microsummaries // See https://wiki.mozilla.org/Microsummaries
$this->element('link', array('rel' => 'microsummary', $this->element('link', array('rel' => 'microsummary',
@ -289,7 +251,7 @@ class ShowstreamAction extends NoticestreamAction
{ {
parent::showSections(); parent::showSections();
if (!common_config('performance', 'high')) { if (!common_config('performance', 'high')) {
$cloud = new PersonalTagCloudSection($this, $this->user); $cloud = new PersonalTagCloudSection($this->target, $this);
$cloud->show(); $cloud->show();
} }
} }
@ -298,7 +260,7 @@ class ShowstreamAction extends NoticestreamAction
{ {
$options = parent::noticeFormOptions(); $options = parent::noticeFormOptions();
if (!$this->scoped instanceof Profile || $this->scoped->id != $this->target->id) { if (!$this->scoped instanceof Profile || !$this->scoped->sameAs($this->target)) {
$options['to_profile'] = $this->target; $options['to_profile'] = $this->target;
} }

View File

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

View File

@ -1,26 +0,0 @@
<?php
/**
* Startpage action. Decides what to show on the first page.
*/
if (!defined('GNUSOCIAL')) { exit(1); }
class StartpageAction extends ManagedAction
{
function isReadOnly($args)
{
return true;
}
function showPage()
{
if (common_config('singleuser', 'enabled')) {
$user = User::singleUser();
common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)), 303);
} elseif (common_config('public', 'localonly')) {
common_redirect(common_local_url('public'), 303);
} else {
common_redirect(common_local_url('networkpublic'), 303);
}
}
}

View File

@ -28,9 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* A list of the user's subscriptions * A list of the user's subscriptions
@ -60,7 +58,7 @@ class SubscriptionsAction extends GalleryAction
function showPageNotice() function showPageNotice()
{ {
if ($this->scoped instanceof Profile && $this->scoped->id === $this->target->id) { if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->getTarget())) {
$this->element('p', null, $this->element('p', null,
// TRANS: Page notice for page with an overview of all subscriptions // TRANS: Page notice for page with an overview of all subscriptions
// TRANS: of the logged in user's own profile. // TRANS: of the logged in user's own profile.

View File

@ -17,38 +17,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
require_once(INSTALLDIR.'/lib/rssaction.php');
// Formatting of RSS handled by Rss10Action // Formatting of RSS handled by Rss10Action
class TagrssAction extends Rss10Action class TagrssAction extends Rss10Action
{ {
var $tag; protected $tag;
function prepare($args) { protected function doStreamPreparation()
parent::prepare($args); {
$tag = common_canonical_tag($this->trimmed('tag')); $tag = common_canonical_tag($this->trimmed('tag'));
$this->tag = Notice_tag::getKV('tag', $tag); $this->tag = Notice_tag::getKV('tag', $tag);
if (!$this->tag) { if (!$this->tag instanceof Notice_tag) {
// TRANS: Client error when requesting a tag feed for a non-existing tag. // TRANS: Client error when requesting a tag feed for a non-existing tag.
$this->clientError(_('No such tag.')); $this->clientError(_('No such tag.'));
} else {
$this->notices = $this->getNotices($this->limit);
return true;
} }
} }
function getNotices($limit=0) protected function getNotices()
{ {
$tag = $this->tag; $stream = Notice_tag::getStream($this->tag->tag)->getNotices(0, $this->limit);
return $stream->fetchAll();
if (is_null($tag)) {
return null;
}
$notice = Notice_tag::getStream($tag->tag)->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
return $notice->fetchAll();
} }
function getChannel() function getChannel()

View File

@ -20,67 +20,29 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* @category Top
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @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);
}
/**
* An action to redirect to the top of the site
*
* @category Action * @category Action
* @package StatusNet * @package GNUsocial
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2010 StatusNet, Inc. * @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 * @copyright 2015 Free Software Foundation, Inc.
* @link http://status.net/ * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL 3.0
* @link https://gnu.io/social
*/ */
class TopAction extends Action if (!defined('GNUSOCIAL')) { exit(1); }
class TopAction extends ManagedAction
{ {
/** public function showPage()
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($argarray)
{
parent::prepare($argarray);
return true;
}
/**
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handle($argarray=null)
{ {
if (common_config('singleuser', 'enabled')) { if (common_config('singleuser', 'enabled')) {
$url = common_local_url('showstream', array('nickname' => User::singleUserNickname())); $user = User::singleUser();
common_redirect(common_local_url('showstream', array('nickname' => $user->getNickname())), 303);
} elseif (common_config('public', 'localonly')) {
common_redirect(common_local_url('public'), 303);
} else { } else {
$url = common_local_url('public'); common_redirect(common_local_url('networkpublic'), 303);
} }
// XXX: Permanent? I think so.
common_redirect($url, 301);
return;
} }
} }

View File

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

View File

@ -52,12 +52,12 @@ class UsergroupsAction extends GalleryAction
if ($this->page == 1) { if ($this->page == 1) {
// TRANS: Page title for first page of groups for a user. // TRANS: Page title for first page of groups for a user.
// TRANS: %s is a nickname. // TRANS: %s is a nickname.
return sprintf(_('%s groups'), $this->user->nickname); return sprintf(_('%s groups'), $this->getTarget()->getNickname());
} else { } else {
// TRANS: Page title for all but the first page of groups for a user. // TRANS: Page title for all but the first page of groups for a user.
// TRANS: %1$s is a nickname, %2$d is a page number. // TRANS: %1$s is a nickname, %2$d is a page number.
return sprintf(_('%1$s groups, page %2$d'), return sprintf(_('%1$s groups, page %2$d'),
$this->user->nickname, $this->getTarget()->getNickname(),
$this->page); $this->page);
} }
} }
@ -82,14 +82,14 @@ class UsergroupsAction extends GalleryAction
$offset = ($this->page-1) * GROUPS_PER_PAGE; $offset = ($this->page-1) * GROUPS_PER_PAGE;
$limit = GROUPS_PER_PAGE + 1; $limit = GROUPS_PER_PAGE + 1;
$groups = $this->user->getGroups($offset, $limit); $groups = $this->getTarget()->getGroups($offset, $limit);
if ($groups instanceof User_group) { if ($groups instanceof User_group) {
$gl = new GroupList($groups, $this->user, $this); $gl = new GroupList($groups, $this->getTarget(), $this);
$cnt = $gl->show(); $cnt = $gl->show();
$this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE, $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
$this->page, 'usergroups', $this->page, 'usergroups',
array('nickname' => $this->user->nickname)); array('nickname' => $this->getTarget()->getNickname()));
} else { } else {
$this->showEmptyListMessage(); $this->showEmptyListMessage();
} }
@ -102,11 +102,11 @@ class UsergroupsAction extends GalleryAction
{ {
// TRANS: Text on group page for a user that is not a member of any group. // TRANS: Text on group page for a user that is not a member of any group.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
$message = sprintf(_('%s is not a member of any group.'), $this->user->nickname) . ' '; $message = sprintf(_('%s is not a member of any group.'), $this->getTarget()->getNickname()) . ' ';
if (common_logged_in()) { if (common_logged_in()) {
$current_user = common_current_user(); $current_user = common_current_user();
if ($this->user->id === $current_user->id) { if ($this->scoped->sameAs($this->getTarget())) {
// TRANS: Text on group page for a user that is not a member of any group. This message contains // TRANS: Text on group page for a user that is not a member of any group. This message contains
// TRANS: a Markdown link in the form [link text](link) and a variable that should not be changed. // TRANS: a Markdown link in the form [link text](link) and a variable that should not be changed.
$message .= _('Try [searching for groups](%%action.groupsearch%%) and joining them.'); $message .= _('Try [searching for groups](%%action.groupsearch%%) and joining them.');
@ -119,7 +119,7 @@ class UsergroupsAction extends GalleryAction
function showProfileBlock() function showProfileBlock()
{ {
$block = new AccountProfileBlock($this, $this->profile); $block = new AccountProfileBlock($this, $this->getTarget());
$block->show(); $block->show();
} }
} }

View File

@ -17,100 +17,54 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
require_once(INSTALLDIR.'/lib/rssaction.php');
// Formatting of RSS handled by Rss10Action // Formatting of RSS handled by Rss10Action
class UserrssAction extends Rss10Action class UserrssAction extends TargetedRss10Action
{ {
var $tag = null; protected $tag = null;
function prepare($args) protected function doStreamPreparation()
{ {
parent::prepare($args); parent::doStreamPreparation();
$nickname = $this->trimmed('nickname');
$this->user = User::getKV('nickname', $nickname);
$this->tag = $this->trimmed('tag'); $this->tag = $this->trimmed('tag');
}
if (!$this->user) { protected function getNotices()
// TRANS: Client error displayed when user not found for an action. {
$this->clientError(_('No such user.'));
}
if (!empty($this->tag)) { if (!empty($this->tag)) {
$this->notices = $this->getTaggedNotices(); $stream = $this->getTarget()->getTaggedNotices($this->tag, 0, $this->limit);
} else { return $stream->fetchAll();
$this->notices = $this->getNotices();
} }
// otherwise we fetch a normal user stream
return true; $stream = $this->getTarget()->getNotices(0, $this->limit);
} return $stream->fetchAll();
function getTaggedNotices()
{
$notice = $this->user->getTaggedNotices(
$this->tag,
0,
($this->limit == 0) ? NOTICES_PER_PAGE : $this->limit,
0,
0
);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
}
function getNotices()
{
$notice = $this->user->getNotices(
0,
($this->limit == 0) ? NOTICES_PER_PAGE : $this->limit
);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
function getChannel() function getChannel()
{ {
$user = $this->user;
$profile = $user->getProfile();
$c = array('url' => common_local_url('userrss', $c = array('url' => common_local_url('userrss',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname. // TRANS: Message is used as link title. %s is a user nickname.
'title' => sprintf(_('%s timeline'), $user->nickname), 'title' => sprintf(_('%s timeline'), $this->target->getNickname()),
'link' => $profile->profileurl, 'link' => $this->target->getUrl(),
// TRANS: Message is used as link description. %1$s is a username, %2$s is a site name. // TRANS: Message is used as link description. %1$s is a username, %2$s is a site name.
'description' => sprintf(_('Updates from %1$s on %2$s!'), 'description' => sprintf(_('Updates from %1$s on %2$s!'),
$user->nickname, common_config('site', 'name'))); $this->target->getNickname(), common_config('site', 'name')));
return $c; return $c;
} }
function getImage()
{
$profile = $this->user->getProfile();
return $profile->avatarUrl(AVATAR_PROFILE_SIZE);
}
// override parent to add X-SUP-ID URL // override parent to add X-SUP-ID URL
function initRss($limit=0) function initRss()
{ {
$url = common_local_url('sup', null, null, $this->user->id); $url = common_local_url('sup', null, null, $this->target->getID());
header('X-SUP-ID: '.$url); header('X-SUP-ID: '.$url);
parent::initRss($limit); parent::initRss();
} }
function isReadOnly($args) function isReadOnly($args)

0
avatar/.gitignore vendored
View File

View File

View File

@ -110,8 +110,7 @@ class Conversation extends Managed_DataObject
{ {
$conv = new Conversation(); $conv = new Conversation();
$conv->id = $notice->conversation; $conv->id = $notice->conversation;
$conv->find(true); if (!$conv->find(true)) {
if (!$conv instanceof Conversation) {
common_debug('Conversation does not exist for notice ID: '.$notice->id); common_debug('Conversation does not exist for notice ID: '.$notice->id);
throw new NoResultException($conv); throw new NoResultException($conv);
} }

View File

@ -166,7 +166,7 @@ class File_redirection extends Managed_DataObject
* size (optional): byte size from Content-Length header * size (optional): byte size from Content-Length header
* time (optional): timestamp from Last-Modified header * time (optional): timestamp from Last-Modified header
*/ */
public function where($in_url, $discover=true) { static function where($in_url, $discover=true) {
// let's see if we know this... // let's see if we know this...
try { try {
$a = File::getByUrl($in_url); $a = File::getByUrl($in_url);
@ -176,7 +176,7 @@ class File_redirection extends Managed_DataObject
try { try {
$b = File_redirection::getByUrl($in_url); $b = File_redirection::getByUrl($in_url);
// this is a redirect to $b->file_id // this is a redirect to $b->file_id
$a = File::getKV('id', $b->file_id); $a = File::getByID($b->file_id);
return $a->url; return $a->url;
} catch (NoResultException $e) { } catch (NoResultException $e) {
// Oh well, let's keep going // Oh well, let's keep going
@ -186,10 +186,10 @@ class File_redirection extends Managed_DataObject
if ($discover) { if ($discover) {
$ret = File_redirection::lookupWhere($in_url); $ret = File_redirection::lookupWhere($in_url);
return $ret; return $ret;
} else {
// No manual dereferencing; leave the unknown URL as is.
return $in_url;
} }
// No manual dereferencing; leave the unknown URL as is.
return $in_url;
} }
/** /**
@ -206,7 +206,7 @@ class File_redirection extends Managed_DataObject
* @param User $user whose shortening options to use; defaults to the current web session user * @param User $user whose shortening options to use; defaults to the current web session user
* @return string * @return string
*/ */
function makeShort($long_url, $user=null) static function makeShort($long_url, $user=null)
{ {
$canon = File_redirection::_canonUrl($long_url); $canon = File_redirection::_canonUrl($long_url);
@ -214,11 +214,7 @@ class File_redirection extends Managed_DataObject
// Did we get one? Is it shorter? // Did we get one? Is it shorter?
if (!empty($short_url)) { return !empty($short_url) ? $short_url : $long_url;
return $short_url;
} else {
return $long_url;
}
} }
/** /**
@ -235,18 +231,14 @@ class File_redirection extends Managed_DataObject
* @return string * @return string
*/ */
function forceShort($long_url, $user) static function forceShort($long_url, $user)
{ {
$canon = File_redirection::_canonUrl($long_url); $canon = File_redirection::_canonUrl($long_url);
$short_url = File_redirection::_userMakeShort($canon, $user, true); $short_url = File_redirection::_userMakeShort($canon, $user, true);
// Did we get one? Is it shorter? // Did we get one? Is it shorter?
if (!empty($short_url)) { return !empty($short_url) ? $short_url : $long_url;
return $short_url;
} else {
return $long_url;
}
} }
static function _userMakeShort($long_url, User $user=null, $force = false) { static function _userMakeShort($long_url, User $user=null, $force = false) {

View File

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

View File

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

View File

@ -50,7 +50,7 @@ class Local_group extends Managed_DataObject
$group->find(true); $group->find(true);
if (!$group instanceof User_group) { if (!$group instanceof User_group) {
common_log(LOG_ERR, 'User_group does not exist for Local_group: '.$this->group_id); common_log(LOG_ERR, 'User_group does not exist for Local_group: '.$this->group_id);
throw new NoResultException($group); throw new NoSuchGroupException(array('id' => $this->group_id));
} }
return $group; return $group;
} }

View File

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

View File

@ -65,10 +65,6 @@ class Notice extends Managed_DataObject
public $is_local; // int(4) public $is_local; // int(4)
public $source; // varchar(32) public $source; // varchar(32)
public $conversation; // int(4) public $conversation; // int(4)
public $lat; // decimal(10,7)
public $lon; // decimal(10,7)
public $location_id; // int(4)
public $location_ns; // int(4)
public $repeat_of; // int(4) public $repeat_of; // int(4)
public $verb; // varchar(191) not 255 because utf8mb4 takes more space public $verb; // varchar(191) not 255 because utf8mb4 takes more space
public $object_type; // varchar(191) not 255 because utf8mb4 takes more space public $object_type; // varchar(191) not 255 because utf8mb4 takes more space
@ -93,10 +89,6 @@ class Notice extends Managed_DataObject
'is_local' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'notice was generated by a user'), 'is_local' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'notice was generated by a user'),
'source' => array('type' => 'varchar', 'length' => 32, 'description' => 'source of comment, like "web", "im", or "clientname"'), 'source' => array('type' => 'varchar', 'length' => 32, 'description' => 'source of comment, like "web", "im", or "clientname"'),
'conversation' => array('type' => 'int', 'description' => 'id of root notice in this conversation'), 'conversation' => array('type' => 'int', 'description' => 'id of root notice in this conversation'),
'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'), 'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'),
'object_type' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'), 'object_type' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'),
'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'), 'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
@ -196,6 +188,7 @@ class Notice extends Managed_DataObject
// Clear related records // Clear related records
$this->clearReplies(); $this->clearReplies();
$this->clearLocation();
$this->clearRepeats(); $this->clearRepeats();
$this->clearTags(); $this->clearTags();
$this->clearGroupInboxes(); $this->clearGroupInboxes();
@ -523,18 +516,13 @@ class Notice extends Managed_DataObject
// Handle repeat case // Handle repeat case
if (isset($repeat_of)) { if (!empty($options['repeat_of'])) {
// Check for a private one // Check for a private one
$repeat = Notice::getKV('id', $repeat_of); $repeat = Notice::getByID($options['repeat_of']);
if (!($repeat instanceof Notice)) { if ($profile->sameAs($repeat->getProfile())) {
// TRANS: Client exception thrown in notice when trying to repeat a missing or deleted notice.
throw new ClientException(_('Cannot repeat; original notice is missing or deleted.'));
}
if ($profile->id == $repeat->profile_id) {
// TRANS: Client error displayed when trying to repeat an own notice. // TRANS: Client error displayed when trying to repeat an own notice.
throw new ClientException(_('You cannot repeat your own notice.')); throw new ClientException(_('You cannot repeat your own notice.'));
} }
@ -610,12 +598,13 @@ class Notice extends Managed_DataObject
if (empty($notice->conversation) and !empty($options['conversation'])) { if (empty($notice->conversation) and !empty($options['conversation'])) {
$conv = Conversation::getKV('uri', $options['conversation']); $conv = Conversation::getKV('uri', $options['conversation']);
if ($conv instanceof Conversation) { if ($conv instanceof Conversation) {
common_debug('Conversation stitched together from (probably) reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').'); common_debug('Conversation stitched together from (probably) a reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
$notice->conversation = $conv->id; $notice->conversation = $conv->id;
} else { } else {
// Conversation URI was not found, so we must create it. But we can't create it // Conversation URI was not found, so we must create it. But we can't create it
// until we have a Notice ID because of the database layout... // until we have a Notice ID because of the database layout...
$notice->tmp_conv_uri = $options['conversation']; // $options['conversation'] will be used later after the $notice->insert();
common_debug('Conversation URI not found, so we have to create it after inserting this Notice: '.$options['conversation']);
} }
} else { } else {
// If we're not using the attached conversation URI let's remove it // If we're not using the attached conversation URI let's remove it
@ -625,14 +614,15 @@ class Notice extends Managed_DataObject
} }
} }
$notloc = new Notice_location();
if (!empty($lat) && !empty($lon)) { if (!empty($lat) && !empty($lon)) {
$notice->lat = $lat; $notloc->lat = $lat;
$notice->lon = $lon; $notloc->lon = $lon;
} }
if (!empty($location_ns) && !empty($location_id)) { if (!empty($location_ns) && !empty($location_id)) {
$notice->location_id = $location_id; $notloc->location_id = $location_id;
$notice->location_ns = $location_ns; $notloc->location_ns = $location_ns;
} }
if (!empty($rendered)) { if (!empty($rendered)) {
@ -671,12 +661,19 @@ class Notice extends Managed_DataObject
// XXX: some of these functions write to the DB // XXX: some of these functions write to the DB
try { try {
$notice->insert(); // throws exception on failure $notice->insert(); // throws exception on failure, if successful we have an ->id
if (($notloc->lat && $notloc->lon) || ($notloc->location_id && $notloc->location_ns)) {
$notloc->notice_id = $notice->getID();
$notloc->insert(); // store the notice location if it had any information
}
// If it's not part of a conversation, it's // If it's not part of a conversation, it's
// the beginning of a new conversation. // the beginning of a new conversation.
if (empty($notice->conversation)) { if (empty($notice->conversation)) {
$orig = clone($notice); $orig = clone($notice);
// $act->context->conversation will be null if it was not provided // $act->context->conversation will be null if it was not provided
$conv = Conversation::create($notice, $options['conversation']); $conv = Conversation::create($notice, $options['conversation']);
$notice->conversation = $conv->id; $notice->conversation = $conv->id;
$notice->update($orig); $notice->update($orig);
@ -853,17 +850,28 @@ class Notice extends Managed_DataObject
if (is_null($scope)) { if (is_null($scope)) {
$scope = $reply->scope; $scope = $reply->scope;
} }
} else {
// If we don't know the reply, we might know the conversation!
// This will happen if a known remote user replies to an
// unknown remote user - within a known conversation.
if (empty($stored->conversation) and !empty($act->context->conversation)) {
$conv = Conversation::getKV('uri', $act->context->conversation);
if ($conv instanceof Conversation) {
common_debug('Conversation stitched together from (probably) a reply activity to unknown remote user. Activity creation time ('.$stored->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
$stored->conversation = $conv->id;
} else {
// Conversation URI was not found, so we must create it. But we can't create it
// until we have a Notice ID because of the database layout...
// $options['conversation'] will be used later after the $stored->insert();
common_debug('Conversation URI from activity context not found, so we have to create it after inserting this Notice: '.$act->context->conversation);
}
}
} }
$notloc = null;
if ($act->context instanceof ActivityContext) { if ($act->context instanceof ActivityContext) {
$location = $act->context->location; if ($act->context->location instanceof Location) {
if ($location) { $notloc = Notice_location::fromLocation($act->context->location);
$stored->lat = $location->lat;
$stored->lon = $location->lon;
if ($location->location_id) {
$stored->location_ns = $location->location_ns;
$stored->location_id = $location->location_id;
}
} }
} else { } else {
$act->context = new ActivityContext(); $act->context = new ActivityContext();
@ -890,6 +898,12 @@ class Notice extends Managed_DataObject
try { try {
$stored->insert(); // throws exception on error $stored->insert(); // throws exception on error
if ($notloc instanceof Notice_location) {
$notloc->notice_id = $stored->getID();
$notloc->insert();
}
$orig = clone($stored); // for updating later in this try clause $orig = clone($stored); // for updating later in this try clause
$object = null; $object = null;
@ -898,10 +912,11 @@ class Notice extends Managed_DataObject
throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString()); throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString());
} }
// If it's not part of a conversation, it's // If it's not part of a conversation, it's the beginning
// the beginning of a new conversation. // of a new one (or belongs to a previously unknown URI).
if (empty($stored->conversation)) { if (empty($stored->conversation)) {
// $act->context->conversation will be null if it was not provided // $act->context->conversation will be null if it was not provided
common_debug('Creating a new conversation for stored notice ID='.$stored->getID().' with URI: '.$act->context->conversation);
$conv = Conversation::create($stored, $act->context->conversation); $conv = Conversation::create($stored, $act->context->conversation);
$stored->conversation = $conv->id; $stored->conversation = $conv->id;
} }
@ -1203,17 +1218,15 @@ class Notice extends Managed_DataObject
$this->_attachments[$this->id] = $attachments; $this->_attachments[$this->id] = $attachments;
} }
function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0) static function publicStream($offset=0, $limit=20, $since_id=null, $max_id=null)
{ {
$stream = new PublicNoticeStream(); $stream = new PublicNoticeStream();
return $stream->getNotices($offset, $limit, $since_id, $max_id); return $stream->getNotices($offset, $limit, $since_id, $max_id);
} }
static function conversationStream($id, $offset=0, $limit=20, $since_id=null, $max_id=null)
function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0)
{ {
$stream = new ConversationNoticeStream($id); $stream = new ConversationNoticeStream($id);
return $stream->getNotices($offset, $limit, $since_id, $max_id); return $stream->getNotices($offset, $limit, $since_id, $max_id);
} }
@ -1225,18 +1238,17 @@ class Notice extends Managed_DataObject
*/ */
function hasConversation() function hasConversation()
{ {
if (!empty($this->conversation)) { if (empty($this->conversation)) {
$conversation = Notice::conversationStream( // this notice is not part of a conversation apparently
$this->conversation, // FIXME: all notices should have a conversation value, right?
1, return false;
1
);
if ($conversation->N > 0) {
return true;
}
} }
return false;
$stream = new ConversationNoticeStream($this->conversation);
$notice = $stream->getNotices(/*offset*/ 1, /*limit*/ 1);
// if our "offset 1, limit 1" query got a result, return true else false
return $notice->N > 0;
} }
/** /**
@ -1271,8 +1283,7 @@ class Notice extends Managed_DataObject
$root = new Notice; $root = new Notice;
$root->conversation = $this->conversation; $root->conversation = $this->conversation;
$root->orderBy('notice.created ASC'); $root->orderBy('notice.created ASC');
$root->find(); $root->find(true); // true means "fetch first result"
$root->fetch();
$root->free(); $root->free();
return $root; return $root;
} }
@ -1660,32 +1671,22 @@ class Notice extends Managed_DataObject
protected $_replies = array(); protected $_replies = array();
/** /**
* Pull the complete list of @-reply targets for this notice. * Pull the complete list of @-mentioned profile IDs for this notice.
* *
* @return array of integer profile ids * @return array of integer profile ids
*/ */
function getReplies() function getReplies()
{ {
if (isset($this->_replies[$this->id])) { if (!isset($this->_replies[$this->getID()])) {
return $this->_replies[$this->id]; $mentions = Reply::multiGet('notice_id', array($this->getID()));
$this->_replies[$this->getID()] = $mentions->fetchAll('profile_id');
} }
return $this->_replies[$this->getID()];
$replyMap = Reply::listGet('notice_id', array($this->id));
$ids = array();
foreach ($replyMap[$this->id] as $reply) {
$ids[] = $reply->profile_id;
}
$this->_replies[$this->id] = $ids;
return $ids;
} }
function _setReplies($replies) function _setReplies($replies)
{ {
$this->_replies[$this->id] = $replies; $this->_replies[$this->getID()] = $replies;
} }
/** /**
@ -1844,7 +1845,11 @@ class Notice extends Managed_DataObject
// This is not a reply to something // This is not a reply to something
} }
$ctx->location = $this->getLocation(); try {
$ctx->location = Notice_location::locFromStored($this);
} catch (ServerException $e) {
$ctx->location = null;
}
$conv = null; $conv = null;
@ -2090,23 +2095,6 @@ class Notice extends Managed_DataObject
return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit)); return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
} }
function getLocation()
{
$location = null;
if (!empty($this->location_id) && !empty($this->location_ns)) {
$location = Location::fromId($this->location_id, $this->location_ns);
}
if (is_null($location)) { // no ID, or Location::fromId() failed
if (!empty($this->lat) && !empty($this->lon)) {
$location = Location::fromLatLon($this->lat, $this->lon);
}
}
return $location;
}
/** /**
* Convenience function for posting a repeat of an existing message. * Convenience function for posting a repeat of an existing message.
* *
@ -2193,7 +2181,7 @@ class Notice extends Managed_DataObject
return $notice->fetchAll('id'); return $notice->fetchAll('id');
} }
function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null) static function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null)
{ {
$options = array(); $options = array();
@ -2277,6 +2265,16 @@ class Notice extends Managed_DataObject
$reply->free(); $reply->free();
} }
function clearLocation()
{
$loc = new Notice_location();
$loc->notice_id = $this->id;
if ($loc->find()) {
$loc->delete();
}
}
function clearFiles() function clearFiles()
{ {
$f2p = new File_to_post(); $f2p = new File_to_post();
@ -2885,4 +2883,51 @@ class Notice extends Managed_DataObject
$notice->_setReplies($ids); $notice->_setReplies($ids);
} }
} }
static public function beforeSchemaUpdate()
{
$table = strtolower(get_called_class());
$schema = Schema::get();
$schemadef = $schema->getTableDef($table);
// 2015-09-04 We move Notice location data to Notice_location
// First we see if we have to do this at all
if (!isset($schemadef['fields']['lat'])
&& !isset($schemadef['fields']['lon'])
&& !isset($schemadef['fields']['location_id'])
&& !isset($schemadef['fields']['location_ns'])) {
// We have already removed the location fields, so no need to migrate.
return;
}
// Then we make sure the Notice_location table is created!
$schema->ensureTable('notice_location', Notice_location::schemaDef());
// Then we continue on our road to migration!
echo "\nFound old $table table, moving location data to 'notice_location' table... (this will probably take a LONG time, but can be aborted and continued)";
$notice = new Notice();
$notice->query(sprintf('SELECT id, lat, lon, location_id, location_ns FROM %1$s ' .
'WHERE lat IS NOT NULL ' .
'OR lon IS NOT NULL ' .
'OR location_id IS NOT NULL ' .
'OR location_ns IS NOT NULL',
$schema->quoteIdentifier($table)));
print "\nFound {$notice->N} notices with location data, inserting";
while ($notice->fetch()) {
$notloc = Notice_location::getKV('notice_id', $notice->id);
if ($notloc instanceof Notice_location) {
print "-";
continue;
}
$notloc = new Notice_location();
$notloc->notice_id = $notice->id;
$notloc->lat= $notice->lat;
$notloc->lon= $notice->lon;
$notloc->location_id= $notice->location_id;
$notloc->location_ns= $notice->location_ns;
$notloc->insert();
print ".";
}
print "\n";
}
} }

View File

@ -0,0 +1,75 @@
<?php
/**
* Table Definition for notice_location
*/
class Notice_location extends Managed_DataObject
{
public $__table = 'notice_location'; // table name
public $notice_id; // int(4) primary_key not_null
public $lat; // decimal(10,7)
public $lon; // decimal(10,7)
public $location_id; // int(4)
public $location_ns; // int(4)
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
public static function schemaDef()
{
return array(
'fields' => array(
'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice that is the reply'),
'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
'primary key' => array('notice_id'),
'foreign keys' => array(
'notice_location_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
),
'indexes' => array(
'notice_location_location_id_idx' => array('location_id'),
),
);
}
static function locFromStored(Notice $stored)
{
$loc = new Notice_location();
$loc->notice_id = $stored->getID();
if (!$loc->find(true)) {
throw new NoResultException($loc);
}
return $loc->asLocation();
}
static function fromLocation(Location $location)
{
$notloc = new Notice_location();
$notloc->lat = $location->lat;
$notloc->lon = $location->lon;
$notloc->location_ns = $location->location_ns;
$notloc->location_id = $location->location_id;
return $notloc;
}
public function asLocation()
{
$location = null;
if (!empty($this->location_id) && !empty($this->location_ns)) {
$location = Location::fromId($this->location_id, $this->location_ns);
}
if (is_null($location)) { // no ID, or Location::fromId() failed
$location = Location::fromLatLon($this->lat, $this->lon);
}
if (is_null($location)) {
throw new ServerException('Location could not be looked up from existing data.');
}
return $location;
}
}

View File

@ -138,6 +138,18 @@ class Profile extends Managed_DataObject
return true; return true;
} }
// Returns false if the user has no password (which will always
// be the case for remote users). This can be the case for OpenID
// logins or other mechanisms which don't store a password hash.
public function hasPassword()
{
try {
return !empty($this->getUser()->hasPassword());
} catch (NoSuchUserException $e) {
return false;
}
}
public function getObjectType() public function getObjectType()
{ {
// FIXME: More types... like peopletags and whatever // FIXME: More types... like peopletags and whatever
@ -242,6 +254,11 @@ class Profile extends Managed_DataObject
return null; return null;
} }
function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{
return Reply::stream($this->getID(), $offset, $limit, $since_id, $before_id);
}
function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0) function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{ {
$stream = new TaggedProfileNoticeStream($this, $tag); $stream = new TaggedProfileNoticeStream($this, $tag);
@ -860,6 +877,11 @@ class Profile extends Managed_DataObject
function delete($useWhere=false) function delete($useWhere=false)
{ {
// just in case it hadn't been done before... (usually set before adding deluser to queue handling!)
if (!$this->hasRole(Profile_role::DELETED)) {
$this->grantRole(Profile_role::DELETED);
}
$this->_deleteNotices(); $this->_deleteNotices();
$this->_deleteSubscriptions(); $this->_deleteSubscriptions();
$this->_deleteTags(); $this->_deleteTags();
@ -1390,6 +1412,11 @@ class Profile extends Managed_DataObject
return $this->fullname; return $this->fullname;
} }
public function getHomepage()
{
return $this->homepage;
}
public function getDescription() public function getDescription()
{ {
return $this->bio; return $this->bio;
@ -1566,6 +1593,11 @@ class Profile extends Managed_DataObject
return $this; return $this;
} }
public function sameAs(Profile $other)
{
return $this->getID() === $other->getID();
}
/** /**
* This will perform shortenLinks with the connected User object. * This will perform shortenLinks with the connected User object.
* *
@ -1613,4 +1645,9 @@ class Profile extends Managed_DataObject
public function setPref($namespace, $topic, $data) { public function setPref($namespace, $topic, $data) {
return Profile_prefs::setData($this, $namespace, $topic, $data); return Profile_prefs::setData($this, $namespace, $topic, $data);
} }
public function getConnectedApps($offset=0, $limit=null)
{
return $this->getUser()->getConnectedApps($offset, $limit);
}
} }

View File

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

View File

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

View File

@ -55,10 +55,9 @@ class Reply extends Managed_DataObject
return $result; return $result;
} }
function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0) static function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{ {
$stream = new ReplyNoticeStream($user_id); $stream = new ReplyNoticeStream($user_id);
return $stream->getNotices($offset, $limit, $since_id, $max_id); return $stream->getNotices($offset, $limit, $since_id, $max_id);
} }
} }

View File

@ -47,10 +47,10 @@ class Subscription_queue extends Managed_DataObject
return $rq; return $rq;
} }
public function exists(Profile $subscriber, Profile $other) static function exists(Profile $subscriber, Profile $other)
{ {
$sub = Subscription_queue::pkeyGet(array('subscriber' => $subscriber->id, $sub = Subscription_queue::pkeyGet(array('subscriber' => $subscriber->getID(),
'subscribed' => $other->id)); 'subscribed' => $other->getID()));
return ($sub instanceof Subscription_queue); return ($sub instanceof Subscription_queue);
} }

View File

@ -42,7 +42,6 @@ class User extends Managed_DataObject
public $emailnotifynudge; // tinyint(1) default_1 public $emailnotifynudge; // tinyint(1) default_1
public $emailnotifymsg; // tinyint(1) default_1 public $emailnotifymsg; // tinyint(1) default_1
public $emailnotifyattn; // tinyint(1) default_1 public $emailnotifyattn; // tinyint(1) default_1
public $emailmicroid; // tinyint(1) default_1
public $language; // varchar(50) public $language; // varchar(50)
public $timezone; // varchar(50) public $timezone; // varchar(50)
public $emailpost; // tinyint(1) default_1 public $emailpost; // tinyint(1) default_1
@ -77,7 +76,6 @@ class User extends Managed_DataObject
'emailnotifynudge' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of nudges'), 'emailnotifynudge' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of nudges'),
'emailnotifymsg' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of direct messages'), 'emailnotifymsg' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of direct messages'),
'emailnotifyattn' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of @-replies'), 'emailnotifyattn' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of @-replies'),
'emailmicroid' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'whether to publish email microid'),
'language' => array('type' => 'varchar', 'length' => 50, 'description' => 'preferred language'), 'language' => array('type' => 'varchar', 'length' => 50, 'description' => 'preferred language'),
'timezone' => array('type' => 'varchar', 'length' => 50, 'description' => 'timezone'), 'timezone' => array('type' => 'varchar', 'length' => 50, 'description' => 'timezone'),
'emailpost' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Post by email'), 'emailpost' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Post by email'),
@ -132,6 +130,11 @@ class User extends Managed_DataObject
return $this->_profile[$this->id]; return $this->_profile[$this->id];
} }
public function sameAs(Profile $other)
{
return $this->getProfile()->sameAs($other);
}
public function getUri() public function getUri()
{ {
return $this->uri; return $this->uri;
@ -142,6 +145,16 @@ class User extends Managed_DataObject
return $this->getProfile()->getNickname(); return $this->getProfile()->getNickname();
} }
static function getByNickname($nickname)
{
$user = User::getKV('nickname', $nickname);
if (!$user instanceof User) {
throw new NoSuchUserException(array('nickname' => $nickname));
}
return $user;
}
function isSubscribed(Profile $other) function isSubscribed(Profile $other)
{ {
return $this->getProfile()->isSubscribed($other); return $this->getProfile()->isSubscribed($other);
@ -261,9 +274,7 @@ class User extends Managed_DataObject
$user->emailnotifynudge = 1; $user->emailnotifynudge = 1;
$user->emailnotifymsg = 1; $user->emailnotifymsg = 1;
$user->emailnotifyattn = 1; $user->emailnotifyattn = 1;
$user->emailmicroid = 1;
$user->emailpost = 1; $user->emailpost = 1;
$user->jabbermicroid = 1;
$user->created = common_sql_now(); $user->created = common_sql_now();
@ -288,7 +299,7 @@ class User extends Managed_DataObject
} }
if (!empty($password)) { // may not have a password for OpenID users if (!empty($password)) { // may not have a password for OpenID users
$user->password = common_munge_password($password, $id); $user->password = common_munge_password($password);
} }
$result = $user->insert(); $result = $user->insert();
@ -439,7 +450,7 @@ class User extends Managed_DataObject
function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{ {
return Reply::stream($this->id, $offset, $limit, $since_id, $before_id); return $this->getProfile()->getReplies($offset, $limit, $since_id, $before_id);
} }
function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) {
@ -451,16 +462,6 @@ class User extends Managed_DataObject
return $this->getProfile()->getNotices($offset, $limit, $since_id, $before_id); return $this->getProfile()->getNotices($offset, $limit, $since_id, $before_id);
} }
function getSelfTags()
{
return Profile_tag::getTagsArray($this->id, $this->id, $this->id);
}
function setSelfTags($newtags, $privacy)
{
return Profile_tag::setTags($this->id, $this->id, $newtags, $privacy);
}
function block(Profile $other) function block(Profile $other)
{ {
// Add a new block record // Add a new block record
@ -597,8 +598,10 @@ class User extends Managed_DataObject
} }
try { try {
$profile = $this->getProfile(); if (!$this->hasRole(Profile_role::DELETED)) {
$profile->delete(); $profile = $this->getProfile();
$profile->delete();
}
} catch (UserNoProfileException $unp) { } catch (UserNoProfileException $unp) {
common_log(LOG_INFO, "User {$this->nickname} has no profile; continuing deletion."); common_log(LOG_INFO, "User {$this->nickname} has no profile; continuing deletion.");
} }
@ -1004,6 +1007,11 @@ class User extends Managed_DataObject
return $this->getProfile()->isPrivateStream(); return $this->getProfile()->isPrivateStream();
} }
public function hasPassword()
{
return !empty($this->password);
}
public function delPref($namespace, $topic) public function delPref($namespace, $topic)
{ {
return $this->getProfile()->delPref($namespace, $topic); return $this->getProfile()->delPref($namespace, $topic);

View File

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

View File

@ -39,6 +39,7 @@ $classes = array('Schema_version',
'Subscription_queue', 'Subscription_queue',
'Oauth_token_association', 'Oauth_token_association',
'Notice', 'Notice',
'Notice_location',
'Notice_source', 'Notice_source',
'Reply', 'Reply',
'Consumer', 'Consumer',

View File

@ -52,7 +52,7 @@
* @author Sean Coates <sean@php.net> * @author Sean Coates <sean@php.net>
* @copyright 2003-2006 PEAR <pear-group@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License * @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id: mimeDecode.php,v 1.48 2006/12/03 13:43:33 cipri Exp $ * @version CVS: $Id: mimeDecode.php 305875 2010-12-01 07:17:10Z alan_k $
* @link http://pear.php.net/package/Mail_mime * @link http://pear.php.net/package/Mail_mime
*/ */
@ -147,6 +147,15 @@ class Mail_mimeDecode extends PEAR
*/ */
var $_decode_headers; var $_decode_headers;
/**
* Flag to determine whether to include attached messages
* as body in the returned object. Depends on $_include_bodies
*
* @var boolean
* @access private
*/
var $_rfc822_bodies;
/** /**
* Constructor. * Constructor.
* *
@ -165,6 +174,7 @@ class Mail_mimeDecode extends PEAR
$this->_body = $body; $this->_body = $body;
$this->_decode_bodies = false; $this->_decode_bodies = false;
$this->_include_bodies = true; $this->_include_bodies = true;
$this->_rfc822_bodies = false;
} }
/** /**
@ -187,7 +197,7 @@ class Mail_mimeDecode extends PEAR
function decode($params = null) function decode($params = null)
{ {
// determine if this method has been called statically // determine if this method has been called statically
$isStatic = !(isset($this) && get_class($this) == __CLASS__); $isStatic = empty($this) || !is_a($this, __CLASS__);
// Have we been called statically? // Have we been called statically?
// If so, create an object and pass details to that. // If so, create an object and pass details to that.
@ -208,6 +218,8 @@ class Mail_mimeDecode extends PEAR
$params['decode_bodies'] : false; $params['decode_bodies'] : false;
$this->_decode_headers = isset($params['decode_headers']) ? $this->_decode_headers = isset($params['decode_headers']) ?
$params['decode_headers'] : false; $params['decode_headers'] : false;
$this->_rfc822_bodies = isset($params['rfc_822bodies']) ?
$params['rfc_822bodies'] : false;
$structure = $this->_decode($this->_header, $this->_body); $structure = $this->_decode($this->_header, $this->_body);
if ($structure === false) { if ($structure === false) {
@ -235,6 +247,7 @@ class Mail_mimeDecode extends PEAR
$headers = $this->_parseHeaders($headers); $headers = $this->_parseHeaders($headers);
foreach ($headers as $value) { foreach ($headers as $value) {
$value['value'] = $this->_decode_headers ? $this->_decodeHeader($value['value']) : $value['value'];
if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) { if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
$return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]); $return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]);
$return->headers[strtolower($value['name'])][] = $value['value']; $return->headers[strtolower($value['name'])][] = $value['value'];
@ -247,8 +260,8 @@ class Mail_mimeDecode extends PEAR
} }
} }
reset($headers);
while (list($key, $value) = each($headers)) { foreach ($headers as $key => $value) {
$headers[$key]['name'] = strtolower($headers[$key]['name']); $headers[$key]['name'] = strtolower($headers[$key]['name']);
switch ($headers[$key]['name']) { switch ($headers[$key]['name']) {
@ -261,7 +274,7 @@ class Mail_mimeDecode extends PEAR
} }
if (isset($content_type['other'])) { if (isset($content_type['other'])) {
while (list($p_name, $p_value) = each($content_type['other'])) { foreach($content_type['other'] as $p_name => $p_value) {
$return->ctype_parameters[$p_name] = $p_value; $return->ctype_parameters[$p_name] = $p_value;
} }
} }
@ -271,7 +284,7 @@ class Mail_mimeDecode extends PEAR
$content_disposition = $this->_parseHeaderValue($headers[$key]['value']); $content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
$return->disposition = $content_disposition['value']; $return->disposition = $content_disposition['value'];
if (isset($content_disposition['other'])) { if (isset($content_disposition['other'])) {
while (list($p_name, $p_value) = each($content_disposition['other'])) { foreach($content_disposition['other'] as $p_name => $p_value) {
$return->d_parameters[$p_name] = $p_value; $return->d_parameters[$p_name] = $p_value;
} }
} }
@ -303,6 +316,7 @@ class Mail_mimeDecode extends PEAR
case 'multipart/alternative': case 'multipart/alternative':
case 'multipart/related': case 'multipart/related':
case 'multipart/mixed': case 'multipart/mixed':
case 'application/vnd.wap.multipart.related':
if(!isset($content_type['other']['boundary'])){ if(!isset($content_type['other']['boundary'])){
$this->_error = 'No boundary found for ' . $content_type['value'] . ' part'; $this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
return false; return false;
@ -321,7 +335,11 @@ class Mail_mimeDecode extends PEAR
break; break;
case 'message/rfc822': case 'message/rfc822':
$obj = &new Mail_mimeDecode($body); if ($this->_rfc822_bodies) {
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
$return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body);
}
$obj = new Mail_mimeDecode($body);
$return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies, $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
'decode_bodies' => $this->_decode_bodies, 'decode_bodies' => $this->_decode_bodies,
'decode_headers' => $this->_decode_headers)); 'decode_headers' => $this->_decode_headers));
@ -401,6 +419,11 @@ class Mail_mimeDecode extends PEAR
if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) { if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
return array($match[1], $match[2]); return array($match[1], $match[2]);
} }
// bug #17325 - empty bodies are allowed. - we just check that at least one line
// of headers exist..
if (count(explode("\n",$input))) {
return array($input, '');
}
$this->_error = 'Could not split header and body'; $this->_error = 'Could not split header and body';
return false; return false;
} }
@ -419,7 +442,12 @@ class Mail_mimeDecode extends PEAR
if ($input !== '') { if ($input !== '') {
// Unfold the input // Unfold the input
$input = preg_replace("/\r?\n/", "\r\n", $input); $input = preg_replace("/\r?\n/", "\r\n", $input);
//#7065 - wrapping.. with encoded stuff.. - probably not needed,
// wrapping space should only get removed if the trailing item on previous line is a
// encoded character
$input = preg_replace("/=\r\n(\t| )+/", '=', $input);
$input = preg_replace("/\r\n(\t| )+/", ' ', $input); $input = preg_replace("/\r\n(\t| )+/", ' ', $input);
$headers = explode("\r\n", trim($input)); $headers = explode("\r\n", trim($input));
foreach ($headers as $value) { foreach ($headers as $value) {
@ -430,7 +458,7 @@ class Mail_mimeDecode extends PEAR
$return[] = array( $return[] = array(
'name' => $hdr_name, 'name' => $hdr_name,
'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value 'value' => $hdr_value
); );
} }
} else { } else {
@ -454,41 +482,161 @@ class Mail_mimeDecode extends PEAR
function _parseHeaderValue($input) function _parseHeaderValue($input)
{ {
if (($pos = strpos($input, ';')) !== false) { if (($pos = strpos($input, ';')) === false) {
$input = $this->_decode_headers ? $this->_decodeHeader($input) : $input;
$return['value'] = trim(substr($input, 0, $pos));
$input = trim(substr($input, $pos+1));
if (strlen($input) > 0) {
// This splits on a semi-colon, if there's no preceeding backslash
// Now works with quoted values; had to glue the \; breaks in PHP
// the regex is already bordering on incomprehensible
$splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
preg_match_all($splitRegex, $input, $matches);
$parameters = array();
for ($i=0; $i<count($matches[0]); $i++) {
$param = $matches[0][$i];
while (substr($param, -2) == '\;') {
$param .= $matches[0][++$i];
}
$parameters[] = $param;
}
for ($i = 0; $i < count($parameters); $i++) {
$param_name = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
$param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
if ($param_value[0] == '"') {
$param_value = substr($param_value, 1, -1);
}
$return['other'][$param_name] = $param_value;
$return['other'][strtolower($param_name)] = $param_value;
}
}
} else {
$return['value'] = trim($input); $return['value'] = trim($input);
return $return;
} }
$value = substr($input, 0, $pos);
$value = $this->_decode_headers ? $this->_decodeHeader($value) : $value;
$return['value'] = trim($value);
$input = trim(substr($input, $pos+1));
if (!strlen($input) > 0) {
return $return;
}
// at this point input contains xxxx=".....";zzzz="...."
// since we are dealing with quoted strings, we need to handle this properly..
$i = 0;
$l = strlen($input);
$key = '';
$val = false; // our string - including quotes..
$q = false; // in quote..
$lq = ''; // last quote..
while ($i < $l) {
$c = $input[$i];
//var_dump(array('i'=>$i,'c'=>$c,'q'=>$q, 'lq'=>$lq, 'key'=>$key, 'val' =>$val));
$escaped = false;
if ($c == '\\') {
$i++;
if ($i == $l-1) { // end of string.
break;
}
$escaped = true;
$c = $input[$i];
}
// state - in key..
if ($val === false) {
if (!$escaped && $c == '=') {
$val = '';
$key = trim($key);
$i++;
continue;
}
if (!$escaped && $c == ';') {
if ($key) { // a key without a value..
$key= trim($key);
$return['other'][$key] = '';
$return['other'][strtolower($key)] = '';
}
$key = '';
}
$key .= $c;
$i++;
continue;
}
// state - in value.. (as $val is set..)
if ($q === false) {
// not in quote yet.
if ((!strlen($val) || $lq !== false) && $c == ' ' || $c == "\t") {
$i++;
continue; // skip leading spaces after '=' or after '"'
}
if (!$escaped && ($c == '"' || $c == "'")) {
// start quoted area..
$q = $c;
// in theory should not happen raw text in value part..
// but we will handle it as a merged part of the string..
$val = !strlen(trim($val)) ? '' : trim($val);
$i++;
continue;
}
// got end....
if (!$escaped && $c == ';') {
$val = trim($val);
$added = false;
if (preg_match('/\*[0-9]+$/', $key)) {
// this is the extended aaa*0=...;aaa*1=.... code
// it assumes the pieces arrive in order, and are valid...
$key = preg_replace('/\*[0-9]+$/', '', $key);
if (isset($return['other'][$key])) {
$return['other'][$key] .= $val;
if (strtolower($key) != $key) {
$return['other'][strtolower($key)] .= $val;
}
$added = true;
}
// continue and use standard setters..
}
if (!$added) {
$return['other'][$key] = $val;
$return['other'][strtolower($key)] = $val;
}
$val = false;
$key = '';
$lq = false;
$i++;
continue;
}
$val .= $c;
$i++;
continue;
}
// state - in quote..
if (!$escaped && $c == $q) { // potential exit state..
// end of quoted string..
$lq = $q;
$q = false;
$i++;
continue;
}
// normal char inside of quoted string..
$val.= $c;
$i++;
}
// do we have anything left..
if (strlen(trim($key)) || $val !== false) {
$val = trim($val);
$added = false;
if ($val !== false && preg_match('/\*[0-9]+$/', $key)) {
// no dupes due to our crazy regexp.
$key = preg_replace('/\*[0-9]+$/', '', $key);
if (isset($return['other'][$key])) {
$return['other'][$key] .= $val;
if (strtolower($key) != $key) {
$return['other'][strtolower($key)] .= $val;
}
$added = true;
}
// continue and use standard setters..
}
if (!$added) {
$return['other'][$key] = $val;
$return['other'][strtolower($key)] = $val;
}
}
// decode values.
foreach($return['other'] as $key =>$val) {
$return['other'][$key] = $this->_decode_headers ? $this->_decodeHeader($val) : $val;
}
//print_r($return);
return $return; return $return;
} }
@ -510,13 +658,19 @@ class Mail_mimeDecode extends PEAR
if ($boundary == $bs_check) { if ($boundary == $bs_check) {
$boundary = $bs_possible; $boundary = $bs_possible;
} }
$tmp = preg_split("/--".preg_quote($boundary, '/')."((?=\s)|--)/", $input);
$tmp = explode('--' . $boundary, $input); $len = count($tmp) -1;
for ($i = 1; $i < $len; $i++) {
for ($i = 1; $i < count($tmp) - 1; $i++) { if (strlen(trim($tmp[$i]))) {
$parts[] = $tmp[$i]; $parts[] = $tmp[$i];
}
}
// add the last part on if it does not end with the 'closing indicator'
if (!empty($tmp[$len]) && strlen(trim($tmp[$len])) && $tmp[$len][0] != '-') {
$parts[] = $tmp[$len];
} }
return $parts; return $parts;
} }
@ -719,7 +873,7 @@ class Mail_mimeDecode extends PEAR
case "to": case "to":
case "cc": case "cc":
case "bcc": case "bcc":
$to = ",".$item['value']; $to .= ",".$item['value'];
default: default:
break; break;
} }

View File

@ -13,17 +13,17 @@ use stdClass;
/** /**
* Parse Microformats2 * Parse Microformats2
* *
* Functional shortcut for the commonest cases of parsing microformats2 from HTML. * Functional shortcut for the commonest cases of parsing microformats2 from HTML.
* *
* Example usage: * Example usage:
* *
* use Mf2; * use Mf2;
* $output = Mf2\parse('<span class="h-card">Barnaby Walters</span>'); * $output = Mf2\parse('<span class="h-card">Barnaby Walters</span>');
* echo json_encode($output, JSON_PRETTY_PRINT); * echo json_encode($output, JSON_PRETTY_PRINT);
* *
* Produces: * Produces:
* *
* { * {
* "items": [ * "items": [
* { * {
@ -35,7 +35,7 @@ use stdClass;
* ], * ],
* "rels": {} * "rels": {}
* } * }
* *
* @param string|DOMDocument $input The HTML string or DOMDocument object to parse * @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 string $url The URL the input document was found at, for relative URL resolution
* @param bool $convertClassic whether or not to convert classic microformats * @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 * Unicode to HTML Entities
* @param string $input String containing characters to convert into HTML entities * @param string $input String containing characters to convert into HTML entities
* @return string * @return string
*/ */
function unicodeToHtmlEntities($input) { function unicodeToHtmlEntities($input) {
return mb_convert_encoding($input, 'HTML-ENTITIES', mb_detect_encoding($input)); return mb_convert_encoding($input, 'HTML-ENTITIES', mb_detect_encoding($input));
@ -92,10 +92,10 @@ function unicodeToHtmlEntities($input) {
/** /**
* Collapse Whitespace * Collapse Whitespace
* *
* Collapses any sequences of whitespace within a string into a single space * Collapses any sequences of whitespace within a string into a single space
* character. * character.
* *
* @deprecated since v0.2.3 * @deprecated since v0.2.3
* @param string $str * @param string $str
* @return string * @return string
@ -113,10 +113,10 @@ function unicodeTrim($str) {
/** /**
* Microformat Name From Class string * 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). * p-name).
* *
* @param string $class A space delimited list of classnames * @param string $class A space delimited list of classnames
* @param string $prefix The prefix to look for * @param string $prefix The prefix to look for
* @return string|array The prefixed name of the first microfomats class found or false * @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(); $matches = array();
foreach ($classes as $classname) { foreach ($classes as $classname) {
$compare_classname = strtolower(' ' . $classname); $compare_classname = ' ' . $classname;
$compare_prefix = strtolower(' ' . $prefix); $compare_prefix = ' ' . $prefix;
if (stristr($compare_classname, $compare_prefix) !== false && ($compare_classname != $compare_prefix)) { if (strstr($compare_classname, $compare_prefix) !== false && ($compare_classname != $compare_prefix)) {
$matches[] = ($prefix === 'h-') ? $classname : substr($classname, strlen($prefix)); $matches[] = ($prefix === 'h-') ? $classname : substr($classname, strlen($prefix));
} }
} }
@ -139,10 +139,10 @@ function mfNamesFromClass($class, $prefix='h-') {
/** /**
* Get Nested µf Property Name From Class * 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. * space-separated string.
* *
* @param string $class * @param string $class
* @return array * @return array
*/ */
@ -153,19 +153,24 @@ function nestedMfPropertyNamesFromClass($class) {
$class = str_replace(array(' ', ' ', "\n"), ' ', $class); $class = str_replace(array(' ', ' ', "\n"), ' ', $class);
foreach (explode(' ', $class) as $classname) { foreach (explode(' ', $class) as $classname) {
foreach ($prefixes as $prefix) { foreach ($prefixes as $prefix) {
$compare_classname = strtolower(' ' . $classname); // Check if $classname is a valid property classname for $prefix.
if (stristr($compare_classname, $prefix) && ($compare_classname != $prefix)) { if (mb_substr($classname, 0, mb_strlen($prefix)) == $prefix && $classname != $prefix) {
$propertyNames = array_merge($propertyNames, mfNamesFromClass($classname, ltrim($prefix))); $propertyName = mb_substr($classname, mb_strlen($prefix));
$propertyNames[$propertyName][] = $prefix;
} }
} }
} }
foreach ($propertyNames as $property => $prefixes) {
$propertyNames[$property] = array_unique($prefixes);
}
return $propertyNames; return $propertyNames;
} }
/** /**
* Wraps mfNamesFromClass to handle an element as input (common) * Wraps mfNamesFromClass to handle an element as input (common)
* *
* @param DOMElement $e The element to get the classname for * @param DOMElement $e The element to get the classname for
* @param string $prefix The prefix to look for * @param string $prefix The prefix to look for
* @return mixed See return value of mf2\Parser::mfNameFromClass() * @return mixed See return value of mf2\Parser::mfNameFromClass()
@ -192,28 +197,27 @@ function convertTimeFormat($time) {
$hh = $mm = $ss = ''; $hh = $mm = $ss = '';
preg_match('/(\d{1,2}):?(\d{2})?:?(\d{2})?(a\.?m\.?|p\.?m\.?)?/i', $time, $matches); preg_match('/(\d{1,2}):?(\d{2})?:?(\d{2})?(a\.?m\.?|p\.?m\.?)?/i', $time, $matches);
// if no am/pm specified // If no am/pm is specified:
if (empty($matches[4])) { if (empty($matches[4])) {
return $time; return $time;
} } else {
// else am/pm specified // Otherwise, am/pm is specified.
else {
$meridiem = strtolower(str_replace('.', '', $matches[4])); $meridiem = strtolower(str_replace('.', '', $matches[4]));
// hours // Hours.
$hh = $matches[1]; $hh = $matches[1];
// add 12 to the pm hours // Add 12 to hours if pm applies.
if ($meridiem == 'pm' && ($hh < 12)) { if ($meridiem == 'pm' && ($hh < 12)) {
$hh += 12; $hh += 12;
} }
$hh = str_pad($hh, 2, '0', STR_PAD_LEFT); $hh = str_pad($hh, 2, '0', STR_PAD_LEFT);
// minutes // Minutes.
$mm = (empty($matches[2]) ) ? '00' : $matches[2]; $mm = (empty($matches[2]) ) ? '00' : $matches[2];
// seconds, only if supplied // Seconds, only if supplied.
if (!empty($matches[3])) { if (!empty($matches[3])) {
$ss = $matches[3]; $ss = $matches[3];
} }
@ -229,11 +233,11 @@ function convertTimeFormat($time) {
/** /**
* Microformats2 Parser * Microformats2 Parser
* *
* A class which holds state for parsing microformats2 from HTML. * A class which holds state for parsing microformats2 from HTML.
* *
* Example usage: * Example usage:
* *
* use Mf2; * use Mf2;
* $parser = new Mf2\Parser('<p class="h-card">Barnaby Walters</p>'); * $parser = new Mf2\Parser('<p class="h-card">Barnaby Walters</p>');
* $output = $parser->parse(); * $output = $parser->parse();
@ -244,18 +248,18 @@ class Parser {
/** @var DOMXPath object which can be used to query over any fragment*/ /** @var DOMXPath object which can be used to query over any fragment*/
public $xpath; public $xpath;
/** @var DOMDocument */ /** @var DOMDocument */
public $doc; public $doc;
/** @var SplObjectStorage */ /** @var SplObjectStorage */
protected $parsed; protected $parsed;
public $jsonMode; public $jsonMode;
/** /**
* Constructor * Constructor
* *
* @param DOMDocument|string $input The data to parse. A string of HTML or a DOMDocument * @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 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. * @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 = new DOMDocument();
@$doc->loadHTML(''); @$doc->loadHTML('');
} }
$this->xpath = new DOMXPath($doc); $this->xpath = new DOMXPath($doc);
$baseurl = $url; $baseurl = $url;
foreach ($this->xpath->query('//base[@href]') as $base) { foreach ($this->xpath->query('//base[@href]') as $base) {
$baseElementUrl = $base->getAttribute('href'); $baseElementUrl = $base->getAttribute('href');
if (parse_url($baseElementUrl, PHP_URL_SCHEME) === null) { if (parse_url($baseElementUrl, PHP_URL_SCHEME) === null) {
/* The base element URL is relative to the document URL. /* The base element URL is relative to the document URL.
* *
* :/ * :/
* *
* Perhaps the author was high? */ * Perhaps the author was high? */
$baseurl = resolveUrl($url, $baseElementUrl); $baseurl = resolveUrl($url, $baseElementUrl);
} else { } else {
$baseurl = $baseElementUrl; $baseurl = $baseElementUrl;
@ -296,31 +300,31 @@ class Parser {
foreach ($this->xpath->query('//template') as $templateEl) { foreach ($this->xpath->query('//template') as $templateEl) {
$templateEl->parentNode->removeChild($templateEl); $templateEl->parentNode->removeChild($templateEl);
} }
$this->baseurl = $baseurl; $this->baseurl = $baseurl;
$this->doc = $doc; $this->doc = $doc;
$this->parsed = new SplObjectStorage(); $this->parsed = new SplObjectStorage();
$this->jsonMode = $jsonMode; $this->jsonMode = $jsonMode;
} }
private function elementPrefixParsed(\DOMElement $e, $prefix) { private function elementPrefixParsed(\DOMElement $e, $prefix) {
if (!$this->parsed->contains($e)) if (!$this->parsed->contains($e))
$this->parsed->attach($e, array()); $this->parsed->attach($e, array());
$prefixes = $this->parsed[$e]; $prefixes = $this->parsed[$e];
$prefixes[] = $prefix; $prefixes[] = $prefix;
$this->parsed[$e] = $prefixes; $this->parsed[$e] = $prefixes;
} }
private function isElementParsed(\DOMElement $e, $prefix) { private function isElementParsed(\DOMElement $e, $prefix) {
if (!$this->parsed->contains($e)) if (!$this->parsed->contains($e))
return false; return false;
$prefixes = $this->parsed[$e]; $prefixes = $this->parsed[$e];
if (!in_array($prefix, $prefixes)) if (!in_array($prefix, $prefixes))
return false; return false;
return true; return true;
} }
@ -352,72 +356,72 @@ class Parser {
// TODO: figure out if this has problems with sms: and geo: URLs // TODO: figure out if this has problems with sms: and geo: URLs
public function resolveUrl($url) { public function resolveUrl($url) {
// If the URL is seriously malformed its probably beyond the scope of this // If the URL is seriously malformed its probably beyond the scope of this
// parser to try to do anything with it. // parser to try to do anything with it.
if (parse_url($url) === false) if (parse_url($url) === false)
return $url; return $url;
$scheme = parse_url($url, PHP_URL_SCHEME); $scheme = parse_url($url, PHP_URL_SCHEME);
if (empty($scheme) and !empty($this->baseurl)) { if (empty($scheme) and !empty($this->baseurl)) {
return resolveUrl($this->baseurl, $url); return resolveUrl($this->baseurl, $url);
} else { } else {
return $url; return $url;
} }
} }
// Parsing Functions // 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. * there are multiple.
* *
* @param \DOMElement $e * @param \DOMElement $e
* @param string $separator = '' if multiple value-title elements, join with this string * @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 arent in use * @return string|null the parsed value or null if value-class or -title arent in use
*/ */
public function parseValueClassTitle(\DOMElement $e, $separator = '') { public function parseValueClassTitle(\DOMElement $e, $separator = '') {
$valueClassElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ")]', $e); $valueClassElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ")]', $e);
if ($valueClassElements->length !== 0) { if ($valueClassElements->length !== 0) {
// Process value-class stuff // Process value-class stuff
$val = ''; $val = '';
foreach ($valueClassElements as $el) { foreach ($valueClassElements as $el) {
$val .= $this->textContent($el); $val .= $this->textContent($el);
} }
return unicodeTrim($val); return unicodeTrim($val);
} }
$valueTitleElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value-title ")]', $e); $valueTitleElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value-title ")]', $e);
if ($valueTitleElements->length !== 0) { if ($valueTitleElements->length !== 0) {
// Process value-title stuff // Process value-title stuff
$val = ''; $val = '';
foreach ($valueTitleElements as $el) { foreach ($valueTitleElements as $el) {
$val .= $el->getAttribute('title'); $val .= $el->getAttribute('title');
} }
return unicodeTrim($val); return unicodeTrim($val);
} }
// No value-title or -class in this element // No value-title or -class in this element
return null; return null;
} }
/** /**
* Given an element with class="p-*", get its value * Given an element with class="p-*", get its value
* *
* @param DOMElement $p The element to parse * @param DOMElement $p The element to parse
* @return string The plaintext value of $p, dependant on type * @return string The plaintext value of $p, dependant on type
* @todo Make this adhere to value-class * @todo Make this adhere to value-class
*/ */
public function parseP(\DOMElement $p) { public function parseP(\DOMElement $p) {
$classTitle = $this->parseValueClassTitle($p, ' '); $classTitle = $this->parseValueClassTitle($p, ' ');
if ($classTitle !== null) if ($classTitle !== null)
return $classTitle; return $classTitle;
if ($p->tagName == 'img' and $p->getAttribute('alt') !== '') { if ($p->tagName == 'img' and $p->getAttribute('alt') !== '') {
$pValue = $p->getAttribute('alt'); $pValue = $p->getAttribute('alt');
} elseif ($p->tagName == 'area' and $p->getAttribute('alt') !== '') { } elseif ($p->tagName == 'area' and $p->getAttribute('alt') !== '') {
@ -429,13 +433,13 @@ class Parser {
} else { } else {
$pValue = unicodeTrim($this->textContent($p)); $pValue = unicodeTrim($this->textContent($p));
} }
return $pValue; return $pValue;
} }
/** /**
* Given an element with class="u-*", get the value of the URL * Given an element with class="u-*", get the value of the URL
* *
* @param DOMElement $u The element to parse * @param DOMElement $u The element to parse
* @return string The plaintext value of $u, dependant on type * @return string The plaintext value of $u, dependant on type
* @todo make this adhere to value-class * @todo make this adhere to value-class
@ -443,18 +447,18 @@ class Parser {
public function parseU(\DOMElement $u) { public function parseU(\DOMElement $u) {
if (($u->tagName == 'a' or $u->tagName == 'area') and $u->getAttribute('href') !== null) { if (($u->tagName == 'a' or $u->tagName == 'area') and $u->getAttribute('href') !== null) {
$uValue = $u->getAttribute('href'); $uValue = $u->getAttribute('href');
} elseif ($u->tagName == 'img' and $u->getAttribute('src') !== null) { } elseif (in_array($u->tagName, array('img', 'audio', 'video', 'source')) and $u->getAttribute('src') !== null) {
$uValue = $u->getAttribute('src'); $uValue = $u->getAttribute('src');
} elseif ($u->tagName == 'object' and $u->getAttribute('data') !== null) { } elseif ($u->tagName == 'object' and $u->getAttribute('data') !== null) {
$uValue = $u->getAttribute('data'); $uValue = $u->getAttribute('data');
} }
if (isset($uValue)) { if (isset($uValue)) {
return $this->resolveUrl($uValue); return $this->resolveUrl($uValue);
} }
$classTitle = $this->parseValueClassTitle($u); $classTitle = $this->parseValueClassTitle($u);
if ($classTitle !== null) { if ($classTitle !== null) {
return $classTitle; return $classTitle;
} elseif ($u->tagName == 'abbr' and $u->getAttribute('title') !== null) { } 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 * 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 DOMElement $dt The element to parse
* @param array $dates Array of dates processed so far * @param array $dates Array of dates processed so far
* @return string The datetime string found * @return string The datetime string found
@ -477,11 +481,11 @@ class Parser {
// Check for value-class pattern // Check for value-class pattern
$valueClassChildren = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ") or contains(concat(" ", @class, " "), " value-title ")]', $dt); $valueClassChildren = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ") or contains(concat(" ", @class, " "), " value-title ")]', $dt);
$dtValue = false; $dtValue = false;
if ($valueClassChildren->length > 0) { if ($valueClassChildren->length > 0) {
// Theyre using value-class // Theyre using value-class
$dateParts = array(); $dateParts = array();
foreach ($valueClassChildren as $e) { foreach ($valueClassChildren as $e) {
if (strstr(' ' . $e->getAttribute('class') . ' ', ' value-title ')) { if (strstr(' ' . $e->getAttribute('class') . ' ', ' value-title ')) {
$title = $e->getAttribute('title'); $title = $e->getAttribute('title');
@ -591,16 +595,16 @@ class Parser {
$dtValue = $dt->nodeValue; $dtValue = $dt->nodeValue;
} }
if ( preg_match('/(\d{4}-\d{2}-\d{2})/', $dtValue, $matches) ) { if (preg_match('/(\d{4}-\d{2}-\d{2})/', $dtValue, $matches)) {
$dates[] = $matches[0]; $dates[] = $matches[0];
} }
} }
/** /**
* if $dtValue is only a time and there are recently parsed dates, * if $dtValue is only a time and there are recently parsed dates,
* form the full date-time using the most recnetly parsed dt- value * form the full date-time using the most recently parsed dt- value
*/ */
if ( (preg_match('/^\d{1,2}:\d{1,2}(Z?[+|-]\d{2}:?\d{2})?/', $dtValue) or preg_match('/^\d{1,2}[a|p]m/', $dtValue)) && !empty($dates) ) { if ((preg_match('/^\d{1,2}:\d{1,2}(Z?[+|-]\d{2}:?\d{2})?/', $dtValue) or preg_match('/^\d{1,2}[a|p]m/', $dtValue)) && !empty($dates)) {
$dtValue = convertTimeFormat($dtValue); $dtValue = convertTimeFormat($dtValue);
$dtValue = end($dates) . 'T' . unicodeTrim($dtValue, 'T'); $dtValue = end($dates) . 'T' . unicodeTrim($dtValue, 'T');
} }
@ -613,15 +617,15 @@ class Parser {
* *
* @param DOMElement $e The element to parse * @param DOMElement $e The element to parse
* @return string $es innerHTML * @return string $es innerHTML
* *
* @todo need to mark this element as e- parsed so it doesnt get parsed as its parents e-* too * @todo need to mark this element as e- parsed so it doesnt get parsed as its parents e-* too
*/ */
public function parseE(\DOMElement $e) { public function parseE(\DOMElement $e) {
$classTitle = $this->parseValueClassTitle($e); $classTitle = $this->parseValueClassTitle($e);
if ($classTitle !== null) if ($classTitle !== null)
return $classTitle; return $classTitle;
// Expand relative URLs within children of this element // Expand relative URLs within children of this element
// TODO: as it is this is not relative to only children, make this .// and rerun tests // TODO: as it is this is not relative to only children, make this .// and rerun tests
$this->resolveChildUrls($e); $this->resolveChildUrls($e);
@ -630,7 +634,7 @@ class Parser {
foreach ($e->childNodes as $node) { foreach ($e->childNodes as $node) {
$html .= $node->C14N(); $html .= $node->C14N();
} }
return array( return array(
'html' => $html, 'html' => $html,
'value' => unicodeTrim($this->textContent($e)) 'value' => unicodeTrim($this->textContent($e))
@ -639,7 +643,7 @@ class Parser {
/** /**
* Recursively parse microformats * Recursively parse microformats
* *
* @param DOMElement $e The element to parse * @param DOMElement $e The element to parse
* @return array A representation of the values contained within microformat $e * @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) { foreach ($this->xpath->query('.//*[contains(concat(" ", @class)," h-")]', $e) as $subMF) {
// Parse // Parse
$result = $this->parseH($subMF); $result = $this->parseH($subMF);
// If result was already parsed, skip it // If result was already parsed, skip it
if (null === $result) if (null === $result)
continue; continue;
// In most cases, the value attribute of the nested microformat should be the p- parsed value of the elemnt.
// The only times this is different is when the microformat is nested under certain prefixes, which are handled below.
$result['value'] = $this->parseP($subMF); $result['value'] = $this->parseP($subMF);
// Does this µf have any property names other than h-*? // Does this µf have any property names other than h-*?
$properties = nestedMfPropertyNamesFromElement($subMF); $properties = nestedMfPropertyNamesFromElement($subMF);
if (!empty($properties)) { if (!empty($properties)) {
// Yes! Its a nested property µf // Yes! Its a nested property µf
foreach ($properties as $property) { foreach ($properties as $property => $prefixes) {
$return[$property][] = $result; // Note: handling microformat nesting under multiple conflicting prefixes is not currently specified by the mf2 parsing spec.
$prefixSpecificResult = $result;
if (in_array('p-', $prefixes)) {
$prefixSpecificResult['value'] = $prefixSpecificResult['properties']['name'][0];
} elseif (in_array('e-', $prefixes)) {
$eParsedResult = $this->parseE($subMF);
$prefixSpecificResult['html'] = $eParsedResult['html'];
$prefixSpecificResult['value'] = $eParsedResult['value'];
} elseif (in_array('u-', $prefixes)) {
$prefixSpecificResult['value'] = $this->parseU($subMF);
}
$return[$property][] = $prefixSpecificResult;
} }
} else { } else {
// No, its a child µf // No, its a child µf
$children[] = $result; $children[] = $result;
} }
// Make sure this sub-mf wont get parsed as a µf or property // Make sure this sub-mf wont get parsed as a µf or property
// TODO: Determine if clearing this is required? // TODO: Determine if clearing this is required?
$this->elementPrefixParsed($subMF, 'h'); $this->elementPrefixParsed($subMF, 'h');
@ -689,19 +706,24 @@ class Parser {
$this->elementPrefixParsed($subMF, 'e'); $this->elementPrefixParsed($subMF, 'e');
} }
if($e->tagName == 'area') {
$coords = $e->getAttribute('coords');
$shape = $e->getAttribute('shape');
}
// Handle p-* // Handle p-*
foreach ($this->xpath->query('.//*[contains(concat(" ", @class) ," p-")]', $e) as $p) { foreach ($this->xpath->query('.//*[contains(concat(" ", @class) ," p-")]', $e) as $p) {
if ($this->isElementParsed($p, 'p')) if ($this->isElementParsed($p, 'p'))
continue; continue;
$pValue = $this->parseP($p); $pValue = $this->parseP($p);
// Add the value to the array for its p- properties // Add the value to the array for its p- properties
foreach (mfNamesFromElement($p, 'p-') as $propName) { foreach (mfNamesFromElement($p, 'p-') as $propName) {
if (!empty($propName)) if (!empty($propName))
$return[$propName][] = $pValue; $return[$propName][] = $pValue;
} }
// Make sure this sub-mf wont get parsed as a top level mf // Make sure this sub-mf wont get parsed as a top level mf
$this->elementPrefixParsed($p, 'p'); $this->elementPrefixParsed($p, 'p');
} }
@ -710,32 +732,32 @@ class Parser {
foreach ($this->xpath->query('.//*[contains(concat(" ", @class)," u-")]', $e) as $u) { foreach ($this->xpath->query('.//*[contains(concat(" ", @class)," u-")]', $e) as $u) {
if ($this->isElementParsed($u, 'u')) if ($this->isElementParsed($u, 'u'))
continue; continue;
$uValue = $this->parseU($u); $uValue = $this->parseU($u);
// Add the value to the array for its property types // Add the value to the array for its property types
foreach (mfNamesFromElement($u, 'u-') as $propName) { foreach (mfNamesFromElement($u, 'u-') as $propName) {
$return[$propName][] = $uValue; $return[$propName][] = $uValue;
} }
// Make sure this sub-mf wont get parsed as a top level mf // Make sure this sub-mf wont get parsed as a top level mf
$this->elementPrefixParsed($u, 'u'); $this->elementPrefixParsed($u, 'u');
} }
// Handle dt-* // Handle dt-*
foreach ($this->xpath->query('.//*[contains(concat(" ", @class), " dt-")]', $e) as $dt) { foreach ($this->xpath->query('.//*[contains(concat(" ", @class), " dt-")]', $e) as $dt) {
if ($this->isElementParsed($dt, 'dt')) if ($this->isElementParsed($dt, 'dt'))
continue; continue;
$dtValue = $this->parseDT($dt, $dates); $dtValue = $this->parseDT($dt, $dates);
if ($dtValue) { if ($dtValue) {
// Add the value to the array for dt- properties // Add the value to the array for dt- properties
foreach (mfNamesFromElement($dt, 'dt-') as $propName) { foreach (mfNamesFromElement($dt, 'dt-') as $propName) {
$return[$propName][] = $dtValue; $return[$propName][] = $dtValue;
} }
} }
// Make sure this sub-mf wont get parsed as a top level mf // Make sure this sub-mf wont get parsed as a top level mf
$this->elementPrefixParsed($dt, 'dt'); $this->elementPrefixParsed($dt, 'dt');
} }
@ -762,22 +784,43 @@ class Parser {
if (!array_key_exists('name', $return)) { if (!array_key_exists('name', $return)) {
try { try {
// Look for img @alt // Look for img @alt
if ($e->tagName == 'img' and $e->getAttribute('alt') != '') if (($e->tagName == 'img' or $e->tagName == 'area') and $e->getAttribute('alt') != '')
throw new Exception($e->getAttribute('alt')); throw new Exception($e->getAttribute('alt'));
if ($e->tagName == 'abbr' and $e->hasAttribute('title')) if ($e->tagName == 'abbr' and $e->hasAttribute('title'))
throw new Exception($e->getAttribute('title')); throw new Exception($e->getAttribute('title'));
// Look for nested img @alt // Look for nested img @alt
foreach ($this->xpath->query('./img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) { foreach ($this->xpath->query('./img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
if ($em->getAttribute('alt') != '') $emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt')); throw new Exception($em->getAttribute('alt'));
}
} }
// Look for nested area @alt
foreach ($this->xpath->query('./area[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
$emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt'));
}
}
// Look for double nested img @alt // Look for double nested img @alt
foreach ($this->xpath->query('./*[count(preceding-sibling::*)+count(following-sibling::*)=0]/img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) { foreach ($this->xpath->query('./*[count(preceding-sibling::*)+count(following-sibling::*)=0]/img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
if ($em->getAttribute('alt') != '') $emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt')); throw new Exception($em->getAttribute('alt'));
}
}
// Look for double nested img @alt
foreach ($this->xpath->query('./*[count(preceding-sibling::*)+count(following-sibling::*)=0]/area[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) {
$emNames = mfNamesFromElement($em, 'h-');
if (empty($emNames) && $em->getAttribute('alt') != '') {
throw new Exception($em->getAttribute('alt'));
}
} }
throw new Exception($e->nodeValue); throw new Exception($e->nodeValue);
@ -812,36 +855,58 @@ class Parser {
// Check for u-url // Check for u-url
if (!array_key_exists('url', $return)) { if (!array_key_exists('url', $return)) {
// Look for img @src // Look for img @src
if ($e->tagName == 'a') if ($e->tagName == 'a' or $e->tagName == 'area')
$url = $e->getAttribute('href'); $url = $e->getAttribute('href');
// Look for nested img @src // Look for nested a @href
foreach ($this->xpath->query('./a[count(preceding-sibling::a)+count(following-sibling::a)=0]', $e) as $em) { foreach ($this->xpath->query('./a[count(preceding-sibling::a)+count(following-sibling::a)=0]', $e) as $em) {
$url = $em->getAttribute('href'); $emNames = mfNamesFromElement($em, 'h-');
break; 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)) if (!empty($url))
$return['url'][] = $this->resolveUrl($url); $return['url'][] = $this->resolveUrl($url);
} }
// Make sure things are in alphabetical order // Make sure things are in alphabetical order
sort($mfTypes); sort($mfTypes);
// Phew. Return the final result. // Phew. Return the final result.
$parsed = array( $parsed = array(
'type' => $mfTypes, 'type' => $mfTypes,
'properties' => $return 'properties' => $return
); );
if (!empty($children))
if (!empty($shape)) {
$parsed['shape'] = $shape;
}
if (!empty($coords)) {
$parsed['coords'] = $coords;
}
if (!empty($children)) {
$parsed['children'] = array_values(array_filter($children)); $parsed['children'] = array_values(array_filter($children));
}
return $parsed; return $parsed;
} }
/** /**
* Parse Rels and Alternatives * 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 * 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), * to true, it will be a stdClass instance, optimising for JSON serialisation. Otherwise (the default case),
* it will be an empty array. * it will be an empty array.
@ -849,18 +914,18 @@ class Parser {
public function parseRelsAndAlternates() { public function parseRelsAndAlternates() {
$rels = array(); $rels = array();
$alternates = array(); $alternates = array();
// Iterate through all a, area and link elements with rel attributes // Iterate through all a, area and link elements with rel attributes
foreach ($this->xpath->query('//*[@rel and @href]') as $hyperlink) { foreach ($this->xpath->query('//*[@rel and @href]') as $hyperlink) {
if ($hyperlink->getAttribute('rel') == '') if ($hyperlink->getAttribute('rel') == '')
continue; continue;
// Resolve the href // Resolve the href
$href = $this->resolveUrl($hyperlink->getAttribute('href')); $href = $this->resolveUrl($hyperlink->getAttribute('href'));
// Split up the rel into space-separated values // Split up the rel into space-separated values
$linkRels = array_filter(explode(' ', $hyperlink->getAttribute('rel'))); $linkRels = array_filter(explode(' ', $hyperlink->getAttribute('rel')));
// If alternate in rels, create alternate structure, append // If alternate in rels, create alternate structure, append
if (in_array('alternate', $linkRels)) { if (in_array('alternate', $linkRels)) {
$alt = array( $alt = array(
@ -869,10 +934,19 @@ class Parser {
); );
if ($hyperlink->hasAttribute('media')) if ($hyperlink->hasAttribute('media'))
$alt['media'] = $hyperlink->getAttribute('media'); $alt['media'] = $hyperlink->getAttribute('media');
if ($hyperlink->hasAttribute('hreflang')) if ($hyperlink->hasAttribute('hreflang'))
$alt['hreflang'] = $hyperlink->getAttribute('hreflang'); $alt['hreflang'] = $hyperlink->getAttribute('hreflang');
if ($hyperlink->hasAttribute('title'))
$alt['title'] = $hyperlink->getAttribute('title');
if ($hyperlink->hasAttribute('type'))
$alt['type'] = $hyperlink->getAttribute('type');
if ($hyperlink->nodeValue)
$alt['text'] = $hyperlink->nodeValue;
$alternates[] = $alt; $alternates[] = $alt;
} else { } else {
foreach ($linkRels as $rel) { foreach ($linkRels as $rel) {
@ -880,38 +954,38 @@ class Parser {
} }
} }
} }
if (empty($rels) and $this->jsonMode) { if (empty($rels) and $this->jsonMode) {
$rels = new stdClass(); $rels = new stdClass();
} }
return array($rels, $alternates); return array($rels, $alternates);
} }
/** /**
* Kicks off the parsing routine * Kicks off the parsing routine
* *
* If `$htmlSafe` is set, any angle brackets in the results from non e-* properties * 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. * 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 * If a DOMElement is set as the $context, only descendants of that element will
* be parsed for microformats. * be parsed for microformats.
* *
* @param bool $htmlSafe whether or not to html-encode non e-* properties. Defaults to false * @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 * @param DOMElement $context optionally an element from which to parse microformats
* @return array An array containing all the µfs found in the current document * @return array An array containing all the µfs found in the current document
*/ */
public function parse($convertClassic = true, DOMElement $context = null) { public function parse($convertClassic = true, DOMElement $context = null) {
$mfs = array(); $mfs = array();
if ($convertClassic) { if ($convertClassic) {
$this->convertLegacy(); $this->convertLegacy();
} }
$mfElements = null === $context $mfElements = null === $context
? $this->xpath->query('//*[contains(concat(" ", @class), " h-")]') ? $this->xpath->query('//*[contains(concat(" ", @class), " h-")]')
: $this->xpath->query('.//*[contains(concat(" ", @class), " h-")]', $context); : $this->xpath->query('.//*[contains(concat(" ", @class), " h-")]', $context);
// Parser microformats // Parser microformats
foreach ($mfElements as $node) { foreach ($mfElements as $node) {
// For each microformat // For each microformat
@ -920,64 +994,64 @@ class Parser {
// Add the value to the array for this property type // Add the value to the array for this property type
$mfs[] = $result; $mfs[] = $result;
} }
// Parse rels // Parse rels
list($rels, $alternates) = $this->parseRelsAndAlternates(); list($rels, $alternates) = $this->parseRelsAndAlternates();
$top = array( $top = array(
'items' => array_values(array_filter($mfs)), 'items' => array_values(array_filter($mfs)),
'rels' => $rels 'rels' => $rels
); );
if (count($alternates)) if (count($alternates))
$top['alternates'] = $alternates; $top['alternates'] = $alternates;
return $top; return $top;
} }
/** /**
* Parse From ID * Parse From ID
* *
* Given an ID, parse all microformats which are children of the element with * Given an ID, parse all microformats which are children of the element with
* that ID. * that ID.
* *
* Note that rel values are still document-wide. * 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. * will be returned.
* *
* @param string $id * @param string $id
* @param bool $htmlSafe = false whether or not to HTML-encode angle brackets in non e-* properties * @param bool $htmlSafe = false whether or not to HTML-encode angle brackets in non e-* properties
* @return array * @return array
*/ */
public function parseFromId($id, $convertClassic=true) { public function parseFromId($id, $convertClassic=true) {
$matches = $this->xpath->query("//*[@id='{$id}']"); $matches = $this->xpath->query("//*[@id='{$id}']");
if (empty($matches)) if (empty($matches))
return array('items' => array(), 'rels' => array(), 'alternates' => array()); return array('items' => array(), 'rels' => array(), 'alternates' => array());
return $this->parse($convertClassic, $matches->item(0)); return $this->parse($convertClassic, $matches->item(0));
} }
/** /**
* Convert Legacy Classnames * Convert Legacy Classnames
* *
* Adds microformats2 classnames into a document containing only legacy * Adds microformats2 classnames into a document containing only legacy
* semantic classnames. * semantic classnames.
* *
* @return Parser $this * @return Parser $this
*/ */
public function convertLegacy() { public function convertLegacy() {
$doc = $this->doc; $doc = $this->doc;
$xp = new DOMXPath($doc); $xp = new DOMXPath($doc);
// replace all roots // replace all roots
foreach ($this->classicRootMap as $old => $new) { foreach ($this->classicRootMap as $old => $new) {
foreach ($xp->query('//*[contains(concat(" ", @class, " "), " ' . $old . ' ") and not(contains(concat(" ", @class, " "), " ' . $new . ' "))]') as $el) { foreach ($xp->query('//*[contains(concat(" ", @class, " "), " ' . $old . ' ") and not(contains(concat(" ", @class, " "), " ' . $new . ' "))]') as $el) {
$el->setAttribute('class', $el->getAttribute('class') . ' ' . $new); $el->setAttribute('class', $el->getAttribute('class') . ' ' . $new);
} }
} }
foreach ($this->classicPropertyMap as $oldRoot => $properties) { foreach ($this->classicPropertyMap as $oldRoot => $properties) {
$newRoot = $this->classicRootMap[$oldRoot]; $newRoot = $this->classicRootMap[$oldRoot];
foreach ($properties as $old => $new) { foreach ($properties as $old => $new) {
@ -986,16 +1060,16 @@ class Parser {
} }
} }
} }
return $this; return $this;
} }
/** /**
* XPath Query * XPath Query
* *
* Runs an XPath query over the current document. Works in exactly the same * Runs an XPath query over the current document. Works in exactly the same
* way as DOMXPath::query. * way as DOMXPath::query.
* *
* @param string $expression * @param string $expression
* @param DOMNode $context * @param DOMNode $context
* @return DOMNodeList * @return DOMNodeList
@ -1003,7 +1077,7 @@ class Parser {
public function query($expression, $context = null) { public function query($expression, $context = null) {
return $this->xpath->query($expression, $context); return $this->xpath->query($expression, $context);
} }
/** /**
* Classic Root Classname map * Classic Root Classname map
*/ */
@ -1013,11 +1087,11 @@ class Parser {
'hentry' => 'h-entry', 'hentry' => 'h-entry',
'hrecipe' => 'h-recipe', 'hrecipe' => 'h-recipe',
'hresume' => 'h-resume', 'hresume' => 'h-resume',
'hevent' => 'h-event', 'vevent' => 'h-event',
'hreview' => 'h-review', 'hreview' => 'h-review',
'hproduct' => 'h-product' 'hproduct' => 'h-product'
); );
public $classicPropertyMap = array( public $classicPropertyMap = array(
'vcard' => array( 'vcard' => array(
'fn' => 'p-name', 'fn' => 'p-name',
@ -1084,7 +1158,7 @@ class Parser {
'skill' => 'p-skill', 'skill' => 'p-skill',
'affiliation' => 'p-affiliation h-card', 'affiliation' => 'p-affiliation h-card',
), ),
'hevent' => array( 'vevent' => array(
'dtstart' => 'dt-start', 'dtstart' => 'dt-start',
'dtend' => 'dt-end', 'dtend' => 'dt-end',
'duration' => 'dt-duration', 'duration' => 'dt-duration',
@ -1246,7 +1320,7 @@ function resolveUrl($baseURI, $referenceURI) {
# 5.2.3 Merge Paths # 5.2.3 Merge Paths
function mergePaths($base, $reference) { function mergePaths($base, $reference) {
# If the base URI has a defined authority component and an empty # If the base URI has a defined authority component and an empty
# path, # path,
if($base['authority'] && $base['path'] == null) { if($base['authority'] && $base['path'] == null) {
# then return a string consisting of "/" concatenated with the # then return a string consisting of "/" concatenated with the
# reference's path; otherwise, # reference's path; otherwise,

View File

@ -1,14 +0,0 @@
<?php
if ( !function_exists('sys_get_temp_dir')) {
function sys_get_temp_dir() {
if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
$tempfile=tempnam(uniqid(rand(),TRUE),'');
if (file_exists($tempfile)) {
unlink($tempfile);
}
return realpath(dirname($tempfile));
}
}
?>

0
file/.gitignore vendored
View File

View File

@ -1081,12 +1081,12 @@ var SN = { // StatusNet
label.attr('title', label.text()); label.attr('title', label.text());
check.change(function () { check.change(function () {
if (check.prop('checked') === true || $.cookie(SN.C.S.NoticeDataGeoCookie) === null) { if (check.prop('checked') === true || $.cookie(SN.C.S.NoticeDataGeoCookie) === undefined) {
label label
.attr('title', NoticeDataGeo_text.ShareDisable) .attr('title', NoticeDataGeo_text.ShareDisable)
.addClass('checked'); .addClass('checked');
if ($.cookie(SN.C.S.NoticeDataGeoCookie) === null || $.cookie(SN.C.S.NoticeDataGeoCookie) == 'disabled') { if ($.cookie(SN.C.S.NoticeDataGeoCookie) === undefined || $.cookie(SN.C.S.NoticeDataGeoCookie) == 'disabled') {
if (navigator.geolocation) { if (navigator.geolocation) {
SN.U.NoticeGeoStatus(form, 'Requesting location from browser...'); SN.U.NoticeGeoStatus(form, 'Requesting location from browser...');
navigator.geolocation.getCurrentPosition( navigator.geolocation.getCurrentPosition(
@ -1297,7 +1297,7 @@ var SN = { // StatusNet
* @fixme what is this? * @fixme what is this?
*/ */
Delete: function () { Delete: function () {
$.cookie(SN.C.S.StatusNetInstance, null); $.removeCookie(SN.C.S.StatusNetInstance);
} }
}, },

View File

@ -205,7 +205,7 @@ class Action extends HTMLOutputter // lawsuit
* *
* @return nothing * @return nothing
*/ */
function showPage() public function showPage()
{ {
if (GNUsocial::isAjax()) { if (GNUsocial::isAjax()) {
self::showAjax(); self::showAjax();

View File

@ -281,19 +281,20 @@ class ActivityUtils
static function validateUri($uri) static function validateUri($uri)
{ {
// Check mailto: URIs first // Check mailto: URIs first
$validate = new Validate();
if (preg_match('/^mailto:(.*)$/', $uri, $match)) { if (preg_match('/^mailto:(.*)$/', $uri, $match)) {
return Validate::email($match[1], common_config('email', 'check_domain')); return $validate->email($match[1], common_config('email', 'check_domain'));
} }
if (Validate::uri($uri)) { if ($validate->uri($uri)) {
return true; return true;
} }
// Possibly an upstream bug; tag: URIs aren't validated properly // Possibly an upstream bug; tag: URIs aren't validated properly
// unless you explicitly ask for them. All other schemes are accepted // unless you explicitly ask for them. All other schemes are accepted
// for basic URI validation without asking. // for basic URI validation without asking.
if (Validate::uri($uri, array('allowed_scheme' => array('tag')))) { if ($validate->uri($uri, array('allowed_scheme' => array('tag')))) {
return true; return true;
} }

View File

@ -248,7 +248,7 @@ class ApiAction extends Action
$twitter_user['friends_count'] = $profile->subscriptionCount(); $twitter_user['friends_count'] = $profile->subscriptionCount();
$twitter_user['created_at'] = $this->dateTwitter($profile->created); $twitter_user['created_at'] = self::dateTwitter($profile->created);
$timezone = 'UTC'; $timezone = 'UTC';
@ -322,7 +322,7 @@ class ApiAction extends Action
$twitter_status = array(); $twitter_status = array();
$twitter_status['text'] = $notice->content; $twitter_status['text'] = $notice->content;
$twitter_status['truncated'] = false; # Not possible on StatusNet $twitter_status['truncated'] = false; # Not possible on StatusNet
$twitter_status['created_at'] = $this->dateTwitter($notice->created); $twitter_status['created_at'] = self::dateTwitter($notice->created);
try { try {
// We could just do $notice->reply_to but maybe the future holds a // We could just do $notice->reply_to but maybe the future holds a
// different story for parenting. // different story for parenting.
@ -366,12 +366,13 @@ class ApiAction extends Action
$twitter_status['in_reply_to_screen_name'] = $twitter_status['in_reply_to_screen_name'] =
($replier_profile) ? $replier_profile->nickname : null; ($replier_profile) ? $replier_profile->nickname : null;
if (isset($notice->lat) && isset($notice->lon)) { try {
$notloc = Notice_location::locFromStored($notice);
// This is the format that GeoJSON expects stuff to be in // This is the format that GeoJSON expects stuff to be in
$twitter_status['geo'] = array('type' => 'Point', $twitter_status['geo'] = array('type' => 'Point',
'coordinates' => array((float) $notice->lat, 'coordinates' => array((float) $notloc->lat,
(float) $notice->lon)); (float) $notloc->lon));
} else { } catch (ServerException $e) {
$twitter_status['geo'] = null; $twitter_status['geo'] = null;
} }
@ -440,8 +441,8 @@ class ApiAction extends Action
$twitter_group['homepage'] = $group->homepage; $twitter_group['homepage'] = $group->homepage;
$twitter_group['description'] = $group->description; $twitter_group['description'] = $group->description;
$twitter_group['location'] = $group->location; $twitter_group['location'] = $group->location;
$twitter_group['created'] = $this->dateTwitter($group->created); $twitter_group['created'] = self::dateTwitter($group->created);
$twitter_group['modified'] = $this->dateTwitter($group->modified); $twitter_group['modified'] = self::dateTwitter($group->modified);
return $twitter_group; return $twitter_group;
} }
@ -547,13 +548,14 @@ class ApiAction extends Action
$entry['pubDate'] = common_date_rfc2822($notice->created); $entry['pubDate'] = common_date_rfc2822($notice->created);
$entry['guid'] = $entry['link']; $entry['guid'] = $entry['link'];
if (isset($notice->lat) && isset($notice->lon)) { try {
$notloc = Notice_location::locFromStored($notice);
// This is the format that GeoJSON expects stuff to be in. // This is the format that GeoJSON expects stuff to be in.
// showGeoRSS() below uses it for XML output, so we reuse it // showGeoRSS() below uses it for XML output, so we reuse it
$entry['geo'] = array('type' => 'Point', $entry['geo'] = array('type' => 'Point',
'coordinates' => array((float) $notice->lat, 'coordinates' => array((float) $notloc->lat,
(float) $notice->lon)); (float) $notloc->lon));
} else { } catch (ServerException $e) {
$entry['geo'] = null; $entry['geo'] = null;
} }
@ -997,7 +999,13 @@ class ApiAction extends Action
$statuses = array(); $statuses = array();
if (is_array($notice)) { if (is_array($notice)) {
$notice = new ArrayWrapper($notice); //FIXME: make everything calling showJsonTimeline use only Notice objects
common_debug('ArrayWrapper avoidance in progress! Beep boop, make showJsonTimeline only receive Notice objects!');
$ids = array();
foreach ($notice as $n) {
$ids[] = $n->getID();
}
$notice = Notice::multiGet('id', $ids);
} }
while ($notice->fetch()) { while ($notice->fetch()) {
@ -1196,7 +1204,7 @@ class ApiAction extends Action
$this->endDocument('xml'); $this->endDocument('xml');
} }
function dateTwitter($dt) static function dateTwitter($dt)
{ {
$dateStr = date('d F Y H:i:s', strtotime($dt)); $dateStr = date('d F Y H:i:s', strtotime($dt));
$d = new DateTime($dateStr, new DateTimeZone('UTC')); $d = new DateTime($dateStr, new DateTimeZone('UTC'));

View File

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

View File

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

View File

@ -113,10 +113,12 @@ class AtomNoticeFeed extends Atom10Feed
foreach ($notices as $notice) { foreach ($notices as $notice) {
$this->addEntryFromNotice($notice); $this->addEntryFromNotice($notice);
} }
} else { } elseif ($notices instanceof Notice) {
while ($notices->fetch()) { while ($notices->fetch()) {
$this->addEntryFromNotice($notices); $this->addEntryFromNotice($notices);
} }
} else {
throw new ServerException('addEntryFromNotices got neither an array nor a Notice object');
} }
} }
@ -125,7 +127,7 @@ class AtomNoticeFeed extends Atom10Feed
* *
* @param Notice $notice a Notice to add * @param Notice $notice a Notice to add
*/ */
function addEntryFromNotice($notice) function addEntryFromNotice(Notice $notice)
{ {
try { try {
$source = $this->showSource(); $source = $this->showSource();

View File

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

163
lib/connectedappslist.php Normal file
View File

@ -0,0 +1,163 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Widget to show a list of OAuth applications
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Application
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Widget to show a list of connected OAuth clients
*
* @category Application
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ConnectedAppsList extends Widget
{
/** Current connected application query */
var $connection = null;
/** Owner of this list */
var $owner = null;
function __construct($connection, Profile $owner, Action $out=null)
{
parent::__construct($out);
common_debug("ConnectedAppsList constructor");
$this->connection = $connection;
$this->owner = $owner;
}
/* Override this in subclasses. */
function showOwnerControls()
{
return;
}
function show()
{
$this->out->elementStart('ul', 'applications');
$cnt = 0;
while ($this->connection->fetch()) {
$cnt++;
if($cnt > APPS_PER_PAGE) {
break;
}
$this->showConnection();
}
$this->out->elementEnd('ul');
return $cnt;
}
function showConnection()
{
$app = Oauth_application::getKV('id', $this->connection->application_id);
$this->out->elementStart('li', array('class' => 'application h-entry',
'id' => 'oauthclient-' . $app->id));
$this->out->elementStart('a', array('href' => $app->source_url,
'class' => 'h-card p-name'));
if (!empty($app->icon)) {
$this->out->element('img', array('src' => $app->icon,
'class' => 'avatar u-photo'));
}
if ($app->name != 'anonymous') {
$this->out->text($app->name);
} else {
// TRANS: Name for an anonymous application in application list.
$this->out->element('span', 'p-name', _('Unknown application'));
}
$this->out->elementEnd('a');
if ($app->name != 'anonymous') {
// @todo FIXME: i18n trouble.
// TRANS: Message has a leading space and a trailing space. Used in application list.
// TRANS: Before this message the application name is put, behind it the organisation that manages it.
$this->out->raw(_(' by '));
$this->out->element('a', array('href' => $app->homepage,
'class' => 'h-card'),
$app->organization);
}
// TRANS: Application access type
$readWriteText = _('read-write');
// TRANS: Application access type
$readOnlyText = _('read-only');
$access = ($this->connection->access_type & Oauth_application::$writeAccess)
? $readWriteText : $readOnlyText;
$modifiedDate = common_date_string($this->connection->modified);
// TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only")
$txt = sprintf(_('Approved %1$s - "%2$s" access.'), $modifiedDate, $access);
// @todo FIXME: i18n trouble.
$this->out->raw(" - $txt");
if (!empty($app->description)) {
$this->out->element(
'p', array('class' => 'application_description'),
$app->description
);
}
$this->out->element(
'p', array(
'class' => 'access_token'),
// TRANS: Access token in the application list.
// TRANS: %s are the first 7 characters of the access token.
sprintf(_('Access token starting with: %s'), substr($this->connection->token, 0, 7))
);
$this->out->elementStart(
'form',
array(
'id' => 'form_revoke_app',
'class' => 'form_revoke_app',
'method' => 'POST',
'action' => common_local_url('oauthconnectionssettings')
)
);
$this->out->elementStart('fieldset');
$this->out->hidden('oauth_token', $this->connection->token);
$this->out->hidden('token', common_session_token());
// TRANS: Button label in application list to revoke access to user data.
$this->out->submit('revoke', _m('BUTTON','Revoke'));
$this->out->elementEnd('fieldset');
$this->out->elementEnd('form');
$this->out->elementEnd('li');
}
}

View File

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

View File

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

67
lib/deletenoticeform.php Normal file
View File

@ -0,0 +1,67 @@
<?php
if (!defined('GNUSOCIAL')) { exit(1); }
class DeletenoticeForm extends Form
{
protected $notice = null;
function __construct(HTMLOutputter $out=null, array $formOpts=array())
{
if (!array_key_exists('notice', $formOpts) || !$formOpts['notice'] instanceof Notice) {
throw new ServerException('No notice provided to DeletenoticeForm');
}
parent::__construct($out);
$this->notice = $formOpts['notice'];
}
function id()
{
return 'form_notice_delete-' . $this->notice->getID();
}
function formClass()
{
return 'form_settings';
}
function action()
{
return common_local_url('deletenotice', array('notice' => $this->notice->getID()));
}
function formLegend()
{
$this->out->element('legend', null, _('Delete notice'));
}
function formData()
{
$this->out->element('p', null, _('Are you sure you want to delete this notice?'));
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
$this->out->submit('form_action-no',
// TRANS: Button label on the delete notice form.
_m('BUTTON','No'),
'submit form_action-primary',
'no',
// TRANS: Submit button title for 'No' when deleting a notice.
_('Do not delete this notice.'));
$this->out->submit('form_action-yes',
// TRANS: Button label on the delete notice form.
_m('BUTTON','Yes'),
'submit form_action-secondary',
'yes',
// TRANS: Submit button title for 'Yes' when deleting a notice.
_('Delete this notice.'));
}
}

View File

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

View File

@ -50,7 +50,7 @@ class FormAction extends ManagedAction
protected function prepare(array $args=array()) { protected function prepare(array $args=array()) {
parent::prepare($args); parent::prepare($args);
$this->form = $this->form ?: $this->action; $this->form = $this->form ?: ucfirst($this->action);
$this->args['form'] = $this->form; $this->args['form'] = $this->form;
$this->type = !is_null($this->type) ? $this->type : $this->trimmed('type'); $this->type = !is_null($this->type) ? $this->type : $this->trimmed('type');

View File

@ -23,7 +23,7 @@ define('GNUSOCIAL_ENGINE', 'GNU social');
define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/'); define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/');
define('GNUSOCIAL_BASE_VERSION', '1.2.0'); define('GNUSOCIAL_BASE_VERSION', '1.2.0');
define('GNUSOCIAL_LIFECYCLE', 'alpha1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release' define('GNUSOCIAL_LIFECYCLE', 'alpha2'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE); define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
@ -37,6 +37,7 @@ define('NOTICES_PER_PAGE', 20);
define('PROFILES_PER_PAGE', 20); define('PROFILES_PER_PAGE', 20);
define('MESSAGES_PER_PAGE', 20); define('MESSAGES_PER_PAGE', 20);
define('GROUPS_PER_PAGE', 20); define('GROUPS_PER_PAGE', 20);
define('APPS_PER_PAGE', 20);
define('GROUPS_PER_MINILIST', 8); define('GROUPS_PER_MINILIST', 8);
define('PROFILES_PER_MINILIST', 8); define('PROFILES_PER_MINILIST', 8);
@ -136,9 +137,18 @@ spl_autoload_register('GNUsocial_class_autoload');
* and is available here: http://www.php-fig.org/psr/psr-0/ * and is available here: http://www.php-fig.org/psr/psr-0/
*/ */
spl_autoload_register(function($class){ spl_autoload_register(function($class){
$file = INSTALLDIR.'/extlib/'.preg_replace('{\\\\|_(?!.*\\\\)}', DIRECTORY_SEPARATOR, ltrim($class, '\\')).'.php'; $class_path = preg_replace('{\\\\|_(?!.*\\\\)}', DIRECTORY_SEPARATOR, ltrim($class, '\\')).'.php';
$file = INSTALLDIR.'/extlib/'.$class_path;
if (file_exists($file)) { if (file_exists($file)) {
require_once $file; require_once $file;
return;
}
# Try if the system has this external library
$file = '/usr/share/php/'.$class_path;
if (file_exists($file)) {
require_once $file;
return;
} }
}); });

View File

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

View File

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

View File

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

View File

@ -622,7 +622,7 @@ abstract class Installer
$this->updateStatus("GNU social has been installed at $link"); $this->updateStatus("GNU social has been installed at $link");
$this->updateStatus( $this->updateStatus(
'<strong>DONE!</strong> You can visit your <a href="'.htmlspecialchars($link).'">new GNU social site</a> (log in as "'.htmlspecialchars($this->adminNick).'"). If this is your first GNU social install, make your experience the best possible by visiting our resource site to join the <a href="https://gnu.io/social/resources/">mailing list or IRC.</a>. <a href="'.htmlspecialchars($link).'/doc/faq/">FAQ is found here</a>.' '<strong>DONE!</strong> You can visit your <a href="'.htmlspecialchars($link).'">new GNU social site</a> (log in as "'.htmlspecialchars($this->adminNick).'"). If this is your first GNU social install, make your experience the best possible by visiting our resource site to join the <a href="https://gnu.io/social/resources/">mailing list or IRC</a>. <a href="'.htmlspecialchars($link).'/doc/faq">FAQ is found here</a>.'
); );
return true; return true;

View File

@ -47,11 +47,13 @@ require_once 'Mail.php';
function mail_backend() function mail_backend()
{ {
static $backend = null; static $backend = null;
global $_PEAR;
if (!$backend) { if (!$backend) {
$backend = Mail::factory(common_config('mail', 'backend'), $mail = new Mail();
$backend = $mail->factory(common_config('mail', 'backend'),
common_config('mail', 'params') ?: array()); common_config('mail', 'params') ?: array());
if (PEAR::isError($backend)) { if ($_PEAR->isError($backend)) {
common_server_error($backend->getMessage(), 500); common_server_error($backend->getMessage(), 500);
} }
} }

View File

@ -1,97 +0,0 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Microid class
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category ID
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* A class for microids
*
* @category ID
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @see http://microid.org/
*/
class Microid
{
/** Agent part of the ID. */
var $agent = null;
/** Resource part of the ID. */
var $resource = null;
/**
* Constructor
*
* @param string $agent Agent of the ID
* @param string $resource Resource part
*/
function __construct($agent, $resource)
{
$this->agent = $agent;
$this->resource = $resource;
}
/**
* Generate a MicroID string
*
* @return string MicroID for agent and resource
*/
function toString()
{
$agent_proto = $this->_getProto($this->agent);
$resource_proto = $this->_getProto($this->resource);
return $agent_proto.'+'.$resource_proto.':sha1:'.
sha1(sha1($this->agent).sha1($this->resource));
}
/**
* Utility for getting the protocol part of a URI
*
* @param string $uri URI to parse
*
* @return string scheme part of the URI
*/
function _getProto($uri)
{
$colon = strpos($uri, ':');
return substr($uri, 0, $colon);
}
}

View File

@ -368,18 +368,19 @@ class NoticeListItem extends Widget
*/ */
function showNoticeLocation() function showNoticeLocation()
{ {
$id = $this->notice->id; return;
try {
$location = $this->notice->getLocation(); $location = Notice_location::locFromStored($this->notice);
} catch (NoResultException $e) {
if (empty($location)) { return;
} catch (ServerException $e) {
return; return;
} }
$name = $location->getName(); $name = $location->getName();
$lat = $this->notice->lat; $lat = $location->lat;
$lon = $this->notice->lon; $lon = $location->lon;
$latlon = (!empty($lat) && !empty($lon)) ? $lat.';'.$lon : ''; $latlon = (!empty($lat) && !empty($lon)) ? $lat.';'.$lon : '';
if (empty($name)) { if (empty($name)) {

View File

@ -4,6 +4,7 @@ if (!defined('GNUSOCIAL')) { exit(1); }
abstract class NoticestreamAction extends ProfileAction abstract class NoticestreamAction extends ProfileAction
{ {
protected $notice = null; // holds the stream result
protected function prepare(array $args=array()) { protected function prepare(array $args=array()) {
parent::prepare($args); parent::prepare($args);

View File

@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Personal tag cloud section * Personal tag cloud section
@ -42,12 +40,12 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
*/ */
class PersonalTagCloudSection extends TagCloudSection class PersonalTagCloudSection extends TagCloudSection
{ {
var $user = null; protected $profile = null;
function __construct($out=null, $user=null) function __construct(Profile $profile, HTMLOutputter $out=null)
{ {
parent::__construct($out); parent::__construct($out);
$this->user = $user; $this->profile = $profile;
} }
function title() function title()
@ -80,7 +78,7 @@ class PersonalTagCloudSection extends TagCloudSection
$tag = Memcached_DataObject::cachedQuery('Notice_tag', $tag = Memcached_DataObject::cachedQuery('Notice_tag',
sprintf($qry, sprintf($qry,
$this->user->id), $this->profile->getID()),
3600); 3600);
return $tag; return $tag;
} }

View File

@ -48,6 +48,36 @@ abstract class ProfileAction extends ManagedAction
protected $target = null; // Profile that we're showing protected $target = null; // Profile that we're showing
protected function doPreparation()
{
// showstream requires a nickname
$nickname_arg = $this->trimmed('nickname');
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
if ($this->arg('page') && $this->arg('page') != 1) {
$args['page'] = $this->arg['page'];
}
common_redirect(common_local_url($this->getActionName(), $args), 301);
}
try {
$user = User::getByNickname($nickname);
} catch (NoSuchUserException $e) {
$group = Local_group::getKV('nickname', $nickname);
if ($group instanceof Local_group) {
common_redirect($group->getProfile()->getUrl());
}
// No user nor group found, throw the NoSuchUserException again
throw $e;
}
$this->target = $user->getProfile();
}
protected function prepare(array $args=array()) protected function prepare(array $args=array())
{ {
// this will call ->doPreparation() which child classes use to set $this->target // this will call ->doPreparation() which child classes use to set $this->target
@ -58,9 +88,6 @@ abstract class ProfileAction extends ManagedAction
throw new ClientException(_('This profile has been silenced by site moderators'), 403); throw new ClientException(_('This profile has been silenced by site moderators'), 403);
} }
// backwards compatibility until all actions are fixed to use $this->target
$this->profile = $this->target;
$this->tag = $this->trimmed('tag'); $this->tag = $this->trimmed('tag');
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
common_set_returnto($this->selfUrl()); common_set_returnto($this->selfUrl());
@ -68,13 +95,11 @@ abstract class ProfileAction extends ManagedAction
return true; return true;
} }
protected function profileActionPreparation()
{
// Nothing to do by default.
}
public function getTarget() public function getTarget()
{ {
if (!$this->target instanceof Profile) {
throw new ServerException('No target profile in ProfileAction class');
}
return $this->target; return $this->target;
} }

View File

@ -32,18 +32,26 @@ if (!defined('GNUSOCIAL')) { exit(1); }
class ProfileListItem extends Widget class ProfileListItem extends Widget
{ {
/** Current profile. */ /** Current profile. */
protected $target = null;
var $profile = null; var $profile = null;
/** Action object using us. */ /** Action object using us. */
var $action = null; var $action = null;
function __construct($profile, $action) // FIXME: Directory plugin sends a User_group here, but should send a Profile and handle User_group specifics itself
function __construct($target, HTMLOutputter $action)
{ {
parent::__construct($action); parent::__construct($action);
$this->profile = $profile; $this->target = $target;
$this->profile = $this->target;
$this->action = $action; $this->action = $action;
} }
function getTarget()
{
return $this->target;
}
function show() function show()
{ {
if (Event::handle('StartProfileListItem', array($this))) { if (Event::handle('StartProfileListItem', array($this))) {

View File

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

View File

@ -108,6 +108,11 @@ class Router
if (Event::handle('StartInitializeRouter', array(&$m))) { if (Event::handle('StartInitializeRouter', array(&$m))) {
// top of the menu hierarchy, sometimes "Home"
$m->connect('', array('action' => 'top'));
// public endpoints
$m->connect('robots.txt', array('action' => 'robotstxt')); $m->connect('robots.txt', array('action' => 'robotstxt'));
$m->connect('opensearch/people', array('action' => 'opensearch', $m->connect('opensearch/people', array('action' => 'opensearch',
@ -156,13 +161,13 @@ class Router
'deleteaccount', 'deleteaccount',
'restoreaccount', 'restoreaccount',
'top', 'top',
'public',
); );
foreach ($main as $a) { foreach ($main as $a) {
$m->connect('main/'.$a, array('action' => $a)); $m->connect('main/'.$a, array('action' => $a));
} }
$m->connect('main/public', array('action' => 'public'));
$m->connect('main/all', array('action' => 'networkpublic')); $m->connect('main/all', array('action' => 'networkpublic'));
$m->connect('main/tagprofile/:id', array('action' => 'tagprofile'), $m->connect('main/tagprofile/:id', array('action' => 'tagprofile'),
@ -239,12 +244,10 @@ class Router
array('action' => 'shownotice'), array('action' => 'shownotice'),
array('notice' => '[0-9]+')); array('notice' => '[0-9]+'));
$m->connect('notice/delete/:notice', $m->connect('notice/:notice/delete',
array('action' => 'deletenotice'), array('action' => 'deletenotice'),
array('notice' => '[0-9]+')); array('notice' => '[0-9]+'));
$m->connect('notice/delete', array('action' => 'deletenotice'));
// conversation // conversation
$m->connect('conversation/:id', $m->connect('conversation/:id',
@ -875,9 +878,6 @@ class Router
array('action' => 'rsd', array('action' => 'rsd',
'nickname' => $nickname)); 'nickname' => $nickname));
$m->connect('',
array('action' => 'startpage'));
// peopletags // peopletags
$m->connect('peopletags', $m->connect('peopletags',
@ -930,9 +930,6 @@ class Router
} }
} }
$m->connect('', array('action' => 'startpage'));
$m->connect('main/public', array('action' => 'public'));
$m->connect('main/all', array('action' => 'networkpublic'));
$m->connect('rss', array('action' => 'publicrss')); $m->connect('rss', array('action' => 'publicrss'));
$m->connect('featuredrss', array('action' => 'featuredrss')); $m->connect('featuredrss', array('action' => 'featuredrss'));
$m->connect('featured/', array('action' => 'featured')); $m->connect('featured/', array('action' => 'featured'));
@ -950,6 +947,13 @@ class Router
array('action' => 'subqueue'), array('action' => 'subqueue'),
array('nickname' => Nickname::DISPLAY_FMT)); array('nickname' => Nickname::DISPLAY_FMT));
// some targeted RSS 1.0 actions (extends TargetedRss10Action)
foreach (array('all', 'replies') as $a) {
$m->connect(':nickname/'.$a.'/rss',
array('action' => $a.'rss'),
array('nickname' => Nickname::DISPLAY_FMT));
}
// people tags // people tags
$m->connect(':nickname/peopletags', $m->connect(':nickname/peopletags',
@ -1017,12 +1021,6 @@ class Router
array('nickname' => Nickname::DISPLAY_FMT)); array('nickname' => Nickname::DISPLAY_FMT));
} }
foreach (array('all', 'replies') as $a) {
$m->connect(':nickname/'.$a.'/rss',
array('action' => $a.'rss'),
array('nickname' => Nickname::DISPLAY_FMT));
}
$m->connect(':nickname/avatar', $m->connect(':nickname/avatar',
array('action' => 'avatarbynickname'), array('action' => 'avatarbynickname'),
array('nickname' => Nickname::DISPLAY_FMT)); array('nickname' => Nickname::DISPLAY_FMT));

View File

@ -28,11 +28,11 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
define('DEFAULT_RSS_LIMIT', 48); define('DEFAULT_RSS_LIMIT', 48);
class Rss10Action extends Action class Rss10Action extends ManagedAction
{ {
// This will contain the details of each feed item's author and be used to generate SIOC data. // This will contain the details of each feed item's author and be used to generate SIOC data.
@ -41,47 +41,16 @@ class Rss10Action extends Action
var $notices = null; var $notices = null;
var $tags_already_output = array(); var $tags_already_output = array();
/** public function isReadOnly($args)
* Constructor
*
* Just wraps the Action constructor.
*
* @param string $output URI to output to, default = stdout
* @param boolean $indent Whether to indent output, default true
*
* @see Action::__construct
*/
function __construct($output='php://output', $indent=null)
{
parent::__construct($output, $indent);
}
/**
* Do we need to write to the database?
*
* @return boolean true
*/
function isReadonly()
{ {
return true; return true;
} }
/** protected function doPreparation()
* Read arguments and initialize members
*
* @param array $args Arguments from $_REQUEST
* @return boolean success
*/
function prepare($args)
{ {
parent::prepare($args); $this->limit = $this->int('limit');
$this->limit = (int) $this->trimmed('limit'); if (empty($this->limit)) {
if ($this->limit == 0) {
$this->limit = DEFAULT_RSS_LIMIT; $this->limit = DEFAULT_RSS_LIMIT;
} }
@ -93,7 +62,7 @@ class Rss10Action extends Action
// If the user hits cancel -- bam! // If the user hits cancel -- bam!
$this->show_basic_auth_error(); $this->show_basic_auth_error();
return; // the above calls 'exit'
} else { } else {
$nickname = $_SERVER['PHP_AUTH_USER']; $nickname = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW']; $password = $_SERVER['PHP_AUTH_PW'];
@ -104,27 +73,19 @@ class Rss10Action extends Action
common_log(LOG_WARNING, "Failed RSS auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip."); common_log(LOG_WARNING, "Failed RSS auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
$this->show_basic_auth_error(); $this->show_basic_auth_error();
return; // the above calls 'exit'
} }
} }
} }
return true; $this->doStreamPreparation();
$this->notices = $this->getNotices($this->limit);
} }
/** protected function doStreamPreparation()
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
function handle($args)
{ {
// Parent handling, including cache check // for example if we need to set $this->target or something
parent::handle($args);
$this->showRss();
} }
function show_basic_auth_error() function show_basic_auth_error()
@ -137,6 +98,7 @@ class Rss10Action extends Action
$this->element('request', null, $_SERVER['REQUEST_URI']); $this->element('request', null, $_SERVER['REQUEST_URI']);
$this->elementEnd('hash'); $this->elementEnd('hash');
$this->endXML(); $this->endXML();
exit;
} }
/** /**
@ -145,7 +107,7 @@ class Rss10Action extends Action
* @return array an array of Notice objects sorted in reverse chron * @return array an array of Notice objects sorted in reverse chron
*/ */
function getNotices() protected function getNotices()
{ {
return array(); return array();
} }
@ -170,7 +132,7 @@ class Rss10Action extends Action
return null; return null;
} }
function showRss() function showPage()
{ {
$this->initRss(); $this->initRss();
$this->showChannel(); $this->showChannel();
@ -254,15 +216,19 @@ class Rss10Action extends Action
$this->element('dc:creator', null, ($profile->fullname) ? $profile->fullname : $profile->nickname); $this->element('dc:creator', null, ($profile->fullname) ? $profile->fullname : $profile->nickname);
$this->element('foaf:maker', array('rdf:resource' => $creator_uri)); $this->element('foaf:maker', array('rdf:resource' => $creator_uri));
$this->element('sioc:has_creator', array('rdf:resource' => $creator_uri.'#acct')); $this->element('sioc:has_creator', array('rdf:resource' => $creator_uri.'#acct'));
$location = $notice->getLocation(); try {
if ($location && isset($location->lat) && isset($location->lon)) { $location = Notice_location::locFromStored($notice);
$location_uri = $location->getRdfURL(); if (isset($location->lat) && isset($location->lon)) {
$attrs = array('geo:lat' => $location->lat, $location_uri = $location->getRdfURL();
'geo:long' => $location->lon); $attrs = array('geo:lat' => $location->lat,
if (strlen($location_uri)) { 'geo:long' => $location->lon);
$attrs['rdf:resource'] = $location_uri; if (strlen($location_uri)) {
$attrs['rdf:resource'] = $location_uri;
}
$this->element('statusnet:origin', $attrs);
} }
$this->element('statusnet:origin', $attrs); } catch (ServerException $e) {
// No result, so no location data
} }
$this->element('statusnet:postIcon', array('rdf:resource' => $profile->avatarUrl())); $this->element('statusnet:postIcon', array('rdf:resource' => $profile->avatarUrl()));
$this->element('cc:licence', array('rdf:resource' => common_config('license', 'url'))); $this->element('cc:licence', array('rdf:resource' => common_config('license', 'url')));

View File

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

View File

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

View File

@ -115,11 +115,10 @@ class TagCloudSection extends Section
function tagUrl($tag) function tagUrl($tag)
{ {
if ('showstream' === $this->out->trimmed('action')) { if ($this->out instanceof ShowstreamAction) {
return common_local_url('showstream', array('nickname' => $this->out->profile->nickname, 'tag' => $tag)); return common_local_url('showstream', array('nickname' => $this->out->getTarget()->getNickname(), 'tag' => $tag));
} else {
return common_local_url('tag', array('tag' => $tag));
} }
return common_local_url('tag', array('tag' => $tag));
} }
function divId() function divId()

View File

@ -0,0 +1,51 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Base class for RSS 1.0 feed actions
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Mail
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Earle Martin <earle@downlode.org>
* @copyright 2008-9 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
class TargetedRss10Action extends Rss10Action
{
protected $target = null;
protected function doStreamPreparation()
{
$this->target = User::getByNickname($this->trimmed('nickname'))->getProfile();
}
public function getTarget()
{
return $this->target;
}
function getImage()
{
return $this->target->avatarUrl(AVATAR_PROFILE_SIZE);
}
}

View File

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

View File

@ -210,7 +210,7 @@ function common_language()
/** /**
* Salted, hashed passwords are stored in the DB. * Salted, hashed passwords are stored in the DB.
*/ */
function common_munge_password($password, $id, Profile $profile=null) function common_munge_password($password, Profile $profile=null)
{ {
$hashed = null; $hashed = null;
@ -245,8 +245,7 @@ function common_check_user($nickname, $password)
} }
if ($user instanceof User && !empty($password)) { if ($user instanceof User && !empty($password)) {
if (0 == strcmp(common_munge_password($password, $user->id), if (0 == strcmp(common_munge_password($password, $user->getProfile()), $user->password)) {
$user->password)) {
//internal checking passed //internal checking passed
$authenticatedUser = $user; $authenticatedUser = $user;
} }
@ -710,26 +709,22 @@ function common_find_mentions($text, Notice $notice)
// Is it a reply? // Is it a reply?
if ($notice instanceof Notice) { try {
try { $origNotice = $notice->getParent();
$origNotice = $notice->getParent(); $origAuthor = $origNotice->getProfile();
$origAuthor = $origNotice->getProfile();
$ids = $origNotice->getReplies(); $ids = $origNotice->getReplies();
foreach ($ids as $id) { foreach ($ids as $id) {
$repliedTo = Profile::getKV('id', $id); try {
if ($repliedTo instanceof Profile) { $repliedTo = Profile::getByID($id);
$origMentions[$repliedTo->nickname] = $repliedTo; $origMentions[$repliedTo->getNickname()] = $repliedTo;
} } catch (NoResultException $e) {
// continue foreach
} }
} catch (NoProfileException $e) {
common_log(LOG_WARNING, sprintf('Notice %d author profile id %d does not exist', $origNotice->id, $origNotice->profile_id));
} catch (NoParentNoticeException $e) {
// This notice is not in reply to anything
} catch (Exception $e) {
common_log(LOG_WARNING, __METHOD__ . ' got exception ' . get_class($e) . ' : ' . $e->getMessage());
} }
} catch (NoParentNoticeException $e) {
// It wasn't a reply to anything, so we can't harvest nickname-relations.
} }
$matches = common_find_mentions_raw($text); $matches = common_find_mentions_raw($text);
@ -2434,3 +2429,12 @@ function common_strip_html($html, $trim=true, $save_whitespace=false)
$text = html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8'); $text = html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8');
return $trim ? trim($text) : $text; return $trim ? trim($text) : $text;
} }
function html_sprintf()
{
$args = func_get_args();
for ($i=1; $i<count($args); $i++) {
$args[$i] = htmlspecialchars($args[$i]);
}
return call_user_func_array('sprintf', $args);
}

View File

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

53
nginx.conf.sample Normal file
View File

@ -0,0 +1,53 @@
server {
# Ports
listen 80;
# Uncomment the following line
# to enable HTTPS
#listen 443 ssl;
# Server name
# Change "example.org" to your domain name
server_name example.org;
# SSL
# Uncomment and change the paths to setup
# your SSL key/cert. See https://cipherli.st/
# for more information
#ssl_certificate /path/to/ssl.cert;
#ssl_certificate_key /path/to/ssl.key;
# Logs
# Uncomment and change the paths to setup
# logging
#access_log /path/to/access.log;
#error_log /path/to/error.log;
# Root
# Change the path below to where you installed
# GNU social
root /path/to/gnusocial/root;
# Index
index index.php;
# PHP
location ~ \.php {
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
# Remove the "fastcgi_pass" line above and uncomment
# the one below to use TCP sockets instead of Unix sockets
#fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}
# Location
location / {
try_files $uri $uri/ @gnusocial;
}
# Fancy URLs
location @gnusocial {
rewrite ^(.*)$ /index.php?p=$1 last;
}
}

View File

@ -72,7 +72,7 @@ class ActivityPlugin extends Plugin
// TRANS: Text for "started following" item in activity plugin. // TRANS: Text for "started following" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name, // TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a profile URL, %4$s is a profile name. // TRANS: %3$s is a profile URL, %4$s is a profile name.
$rendered = sprintf(_m('<a href="%1$s">%2$s</a> started following <a href="%3$s">%4$s</a>.'), $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> started following <a href="%3$s">%4$s</a>.'),
$profile->getUrl(), $profile->getUrl(),
$profile->getBestName(), $profile->getBestName(),
$other->getUrl(), $other->getUrl(),
@ -110,7 +110,7 @@ class ActivityPlugin extends Plugin
// TRANS: Text for "stopped following" item in activity plugin. // TRANS: Text for "stopped following" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name, // TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a profile URL, %4$s is a profile name. // TRANS: %3$s is a profile URL, %4$s is a profile name.
$rendered = sprintf(_m('<a href="%1$s">%2$s</a> stopped following <a href="%3$s">%4$s</a>.'), $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> stopped following <a href="%3$s">%4$s</a>.'),
$profile->getUrl(), $profile->getUrl(),
$profile->getBestName(), $profile->getBestName(),
$other->getUrl(), $other->getUrl(),
@ -155,7 +155,7 @@ class ActivityPlugin extends Plugin
// TRANS: Text for "stopped liking" item in activity plugin. // TRANS: Text for "stopped liking" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name, // TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a notice URL, %4$s is an author name. // TRANS: %3$s is a notice URL, %4$s is an author name.
$rendered = sprintf(_m('<a href="%1$s">%2$s</a> stopped liking <a href="%3$s">%4$s\'s update</a>.'), $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> stopped liking <a href="%3$s">%4$s\'s update</a>.'),
$profile->getUrl(), $profile->getUrl(),
$profile->getBestName(), $profile->getBestName(),
$notice->getUrl(), $notice->getUrl(),
@ -200,7 +200,7 @@ class ActivityPlugin extends Plugin
// TRANS: Text for "joined group" item in activity plugin. // TRANS: Text for "joined group" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name, // TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a group URL, %4$s is a group name. // TRANS: %3$s is a group URL, %4$s is a group name.
$rendered = sprintf(_m('<a href="%1$s">%2$s</a> joined the group <a href="%3$s">%4$s</a>.'), $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> joined the group <a href="%3$s">%4$s</a>.'),
$profile->getUrl(), $profile->getUrl(),
$profile->getBestName(), $profile->getBestName(),
$group->homeUrl(), $group->homeUrl(),
@ -241,7 +241,7 @@ class ActivityPlugin extends Plugin
// TRANS: Text for "left group" item in activity plugin. // TRANS: Text for "left group" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name, // TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a group URL, %4$s is a group name. // TRANS: %3$s is a group URL, %4$s is a group name.
$rendered = sprintf(_m('<a href="%1$s">%2$s</a> left the group <a href="%3$s">%4$s</a>.'), $rendered = html_sprintf(_m('<a href="%1$s">%2$s</a> left the group <a href="%3$s">%4$s</a>.'),
$profile->getUrl(), $profile->getUrl(),
$profile->getBestName(), $profile->getBestName(),
$group->homeUrl(), $group->homeUrl(),

View File

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

View File

@ -28,11 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/rssaction.php';
/** /**
* RSS feed for user bookmarks action class. * RSS feed for user bookmarks action class.
@ -48,54 +44,12 @@ require_once INSTALLDIR.'/lib/rssaction.php';
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */
class BookmarksrssAction extends Rss10Action class BookmarksrssAction extends TargetedRss10Action
{ {
/** The user whose bookmarks to display */ protected function getNotices()
var $user = null;
/**
* Find the user to display by supplied nickname
*
* @param array $args Arguments from $_REQUEST
*
* @return boolean success
*/
function prepare($args)
{
parent::prepare($args);
$nickname = $this->trimmed('nickname');
$this->user = User::getKV('nickname', $nickname);
if (!$this->user) {
// TRANS: Client error displayed when trying to get the RSS feed with bookmarks of a user that does not exist.
$this->clientError(_('No such user.'));
} else {
$this->notices = $this->getNotices($this->limit);
return true;
}
}
/**
* Get notices
*
* @param integer $limit max number of notices to return
*
* @return array notices
*/
function getNotices($limit=0)
{ {
$user = $this->user; $stream = new BookmarksNoticeStream($this->target->getID(), true);
return $stream->getNotices(0, $this->limit)->fetchAll();
$notice = new BookmarksNoticeStream($this->user->id, true);
$notice = $notice->getNotices(0, NOTICES_PER_PAGE);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
/** /**
@ -105,31 +59,19 @@ class BookmarksrssAction extends Rss10Action
*/ */
function getChannel() function getChannel()
{ {
$user = $this->user;
$c = array('url' => common_local_url('bookmarksrss', $c = array('url' => common_local_url('bookmarksrss',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: Title of RSS feed with bookmarks of a user. // TRANS: Title of RSS feed with bookmarks of a user.
// TRANS: %s is a user's nickname. // TRANS: %s is a user's nickname.
'title' => sprintf(_("%s's bookmarks"), $user->nickname), 'title' => sprintf(_("%s's bookmarks"), $this->target->getNickname()),
'link' => common_local_url('bookmarks', 'link' => common_local_url('bookmarks',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: Desciption of RSS feed with bookmarks of a user. // TRANS: Desciption of RSS feed with bookmarks of a user.
// TRANS: %1$s is a user's nickname, %2$s is the name of the StatusNet site. // TRANS: %1$s is a user's nickname, %2$s is the name of the StatusNet site.
'description' => sprintf(_('Bookmarks posted by %1$s on %2$s!'), 'description' => sprintf(_('Bookmarks posted by %1$s on %2$s!'),
$user->nickname, common_config('site', 'name'))); $this->target->getNickname(), common_config('site', 'name')));
return $c; return $c;
} }
/**
* Get image.
*
* @return void
*/
function getImage()
{
return null;
}
} }

Some files were not shown because too many files have changed in this diff Show More