Commit 6d8e01ad authored by Evan Prodromou's avatar Evan Prodromou

Merge branch 'master' of gitorious.org:statusnet/mainline

parents 8a2144ae bbfd6eff
...@@ -85,18 +85,27 @@ public sites upgrade to the new version immediately. ...@@ -85,18 +85,27 @@ public sites upgrade to the new version immediately.
Notable changes this version: Notable changes this version:
- Installer no longer fails with a PHP fatal error when trying to set up the
subscription to update@status.net
- Fixed email notifications for @-replies that come in via OStatus - Fixed email notifications for @-replies that come in via OStatus
- OStatus related Fixes to the cloudy theme - OStatus related Fixes to the cloudy theme
- Pass geo locations over Twitter bridge (will only be used if enabled on the Twitter side) - Pass geo locations over Twitter bridge (will only be used if enabled on the
- scripts/showplugins.php - script to dump the list of activated plugins and their settings Twitter side)
- scripts/fixup_blocks.php - script to finds any stray subscriptions in violation of blocks, and removes them - scripts/showplugins.php - script to dump the list of activated plugins and
- Allow blocking someone who's not currently subscribed to you (prevents seeing @-replies from them, or them subbing to you in future) their settings
- Default 2-second timeout on Geonames web service lookups - scripts/fixup_blocks.php - script to finds any stray subscriptions in
violation of blocks, and removes them
- Allow blocking someone who's not currently subscribed to you (prevents
seeing @-replies from them, or them subbing to you in future)
- Default 2-second timeout on Geonames web service lookups
- Improved localization for plugins - Improved localization for plugins
- New anti-spam measures: added nofollow rels to group members list, subscribers list - New anti-spam measures: added nofollow rels to group members list,
- Shared cache key option for Geonames plugin (lets multi-instance sites share their cached geoname lookups) subscribers list
- Shared cache key option for Geonames plugin (lets multi-instance sites
share their cached geoname lookups)
- Stability fixes to the TwitterStatusFetcher - Stability fixes to the TwitterStatusFetcher
- If user allows location sharing but turned off browser location use profile location - If user allows location sharing but turned off browser location use profile
location
- Improved group listing via the API - Improved group listing via the API
- Improved FOAF output - Improved FOAF output
- Several other bugfixes - Several other bugfixes
......
...@@ -52,7 +52,6 @@ require_once INSTALLDIR . '/lib/apiauth.php'; ...@@ -52,7 +52,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
class ApiDirectMessageNewAction extends ApiAuthAction class ApiDirectMessageNewAction extends ApiAuthAction
{ {
var $source = null;
var $other = null; var $other = null;
var $content = null; var $content = null;
...@@ -76,13 +75,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction ...@@ -76,13 +75,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction
return; return;
} }
$this->source = $this->trimmed('source'); // Not supported by Twitter.
$reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
if (empty($this->source) || in_array($this->source, $reserved_sources)) {
$source = 'api';
}
$this->content = $this->trimmed('text'); $this->content = $this->trimmed('text');
$this->user = $this->auth_user; $this->user = $this->auth_user;
......
...@@ -79,7 +79,7 @@ class ApiStatusesRetweetAction extends ApiAuthAction ...@@ -79,7 +79,7 @@ class ApiStatusesRetweetAction extends ApiAuthAction
$this->user = $this->auth_user; $this->user = $this->auth_user;
if ($this->user->id == $notice->profile_id) { if ($this->user->id == $this->original->profile_id) {
$this->clientError(_('Cannot repeat your own notice.'), $this->clientError(_('Cannot repeat your own notice.'),
400, $this->format); 400, $this->format);
return false; return false;
......
...@@ -64,8 +64,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction ...@@ -64,8 +64,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
var $lat = null; var $lat = null;
var $lon = null; var $lon = null;
static $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
/** /**
* Take arguments for running * Take arguments for running
* *
...@@ -80,19 +78,9 @@ class ApiStatusesUpdateAction extends ApiAuthAction ...@@ -80,19 +78,9 @@ class ApiStatusesUpdateAction extends ApiAuthAction
parent::prepare($args); parent::prepare($args);
$this->status = $this->trimmed('status'); $this->status = $this->trimmed('status');
$this->source = $this->trimmed('source');
$this->lat = $this->trimmed('lat'); $this->lat = $this->trimmed('lat');
$this->lon = $this->trimmed('long'); $this->lon = $this->trimmed('long');
// try to set the source attr from OAuth app
if (empty($this->source)) {
$this->source = $this->oauth_source;
}
if (empty($this->source) || in_array($this->source, self::$reserved_sources)) {
$this->source = 'api';
}
$this->in_reply_to_status_id $this->in_reply_to_status_id
= intval($this->trimmed('in_reply_to_status_id')); = intval($this->trimmed('in_reply_to_status_id'));
......
...@@ -185,17 +185,23 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction ...@@ -185,17 +185,23 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction
{ {
$notices = array(); $notices = array();
common_debug("since id = " . $this->since_id . " max id = " . $this->max_id);
if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) { if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) {
$notice = $this->user->favoriteNotices( $notice = $this->user->favoriteNotices(
true,
($this->page-1) * $this->count, ($this->page-1) * $this->count,
$this->count, $this->count,
true $this->since_id,
$this->max_id
); );
} else { } else {
$notice = $this->user->favoriteNotices( $notice = $this->user->favoriteNotices(
false,
($this->page-1) * $this->count, ($this->page-1) * $this->count,
$this->count, $this->count,
false $this->since_id,
$this->max_id
); );
} }
......
...@@ -87,13 +87,15 @@ class BlockAction extends ProfileFormAction ...@@ -87,13 +87,15 @@ class BlockAction extends ProfileFormAction
{ {
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) { if ($this->arg('no')) {
$this->returnToArgs(); $this->returnToPrevious();
} elseif ($this->arg('yes')) { } elseif ($this->arg('yes')) {
$this->handlePost(); $this->handlePost();
$this->returnToArgs(); $this->returnToPrevious();
} else { } else {
$this->showPage(); $this->showPage();
} }
} else {
$this->showPage();
} }
} }
...@@ -118,6 +120,12 @@ class BlockAction extends ProfileFormAction ...@@ -118,6 +120,12 @@ class BlockAction extends ProfileFormAction
*/ */
function areYouSureForm() function areYouSureForm()
{ {
// @fixme if we ajaxify the confirmation form, skip the preview on ajax hits
$profile = new ArrayWrapper(array($this->profile));
$preview = new ProfileList($profile, $this);
$preview->show();
$id = $this->profile->id; $id = $this->profile->id;
$this->elementStart('form', array('id' => 'block-' . $id, $this->elementStart('form', array('id' => 'block-' . $id,
'method' => 'post', 'method' => 'post',
...@@ -175,4 +183,38 @@ class BlockAction extends ProfileFormAction ...@@ -175,4 +183,38 @@ class BlockAction extends ProfileFormAction
$this->autofocus('form_action-yes'); $this->autofocus('form_action-yes');
} }
/**
* Override for form session token checks; on our first hit we're just
* requesting confirmation, which doesn't need a token. We need to be
* able to take regular GET requests from email!
*
* @throws ClientException if token is bad on POST request or if we have
* confirmation parameters which could trigger something.
*/
function checkSessionToken()
{
if ($_SERVER['REQUEST_METHOD'] == 'POST' ||
$this->arg('yes') ||
$this->arg('no')) {
return parent::checkSessionToken();
}
}
/**
* If we reached this form without returnto arguments, return to the
* current user's subscription list.
*
* @return string URL
*/
function defaultReturnTo()
{
$user = common_current_user();
if ($user) {
return common_local_url('subscribers',
array('nickname' => $user->nickname));
} else {
return common_local_url('public');
}
}
} }
...@@ -92,10 +92,10 @@ class DeleteuserAction extends ProfileFormAction ...@@ -92,10 +92,10 @@ class DeleteuserAction extends ProfileFormAction
{ {
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) { if ($this->arg('no')) {
$this->returnToArgs(); $this->returnToPrevious();
} elseif ($this->arg('yes')) { } elseif ($this->arg('yes')) {
$this->handlePost(); $this->handlePost();
$this->returnToArgs(); $this->returnToPrevious();
} else { } else {
$this->showPage(); $this->showPage();
} }
......
...@@ -89,7 +89,7 @@ class FavoritesrssAction extends Rss10Action ...@@ -89,7 +89,7 @@ class FavoritesrssAction extends Rss10Action
function getNotices($limit=0) function getNotices($limit=0)
{ {
$user = $this->user; $user = $this->user;
$notice = $user->favoriteNotices(0, $limit); $notice = $user->favoriteNotices(false, 0, $limit);
$notices = array(); $notices = array();
while ($notice->fetch()) { while ($notice->fetch()) {
$notices[] = clone($notice); $notices[] = clone($notice);
......
...@@ -117,7 +117,7 @@ class GroupblockAction extends RedirectingAction ...@@ -117,7 +117,7 @@ class GroupblockAction extends RedirectingAction
parent::handle($args); parent::handle($args);
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) { if ($this->arg('no')) {
$this->returnToArgs(); $this->returnToPrevious();
} elseif ($this->arg('yes')) { } elseif ($this->arg('yes')) {
$this->blockProfile(); $this->blockProfile();
} elseif ($this->arg('blockto')) { } elseif ($this->arg('blockto')) {
...@@ -195,7 +195,7 @@ class GroupblockAction extends RedirectingAction ...@@ -195,7 +195,7 @@ class GroupblockAction extends RedirectingAction
return false; return false;
} }
$this->returnToArgs(); $this->returnToPrevious();
} }
/** /**
......
...@@ -62,6 +62,28 @@ class LoginAction extends Action ...@@ -62,6 +62,28 @@ class LoginAction extends Action
return false; return false;
} }
/**
* Prepare page to run
*
*
* @param $args
* @return string title
*/
function prepare($args)
{
parent::prepare($args);
// @todo this check should really be in index.php for all sensitive actions
$ssl = common_config('site', 'ssl');
if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) {
common_redirect(common_local_url('login'));
// exit
}
return true;
}
/** /**
* Handle input, produce output * Handle input, produce output
* *
......
...@@ -74,6 +74,13 @@ class RegisterAction extends Action ...@@ -74,6 +74,13 @@ class RegisterAction extends Action
parent::prepare($args); parent::prepare($args);
$this->code = $this->trimmed('code'); $this->code = $this->trimmed('code');
// @todo this check should really be in index.php for all sensitive actions
$ssl = common_config('site', 'ssl');
if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) {
common_redirect(common_local_url('register'));
// exit
}
if (empty($this->code)) { if (empty($this->code)) {
common_ensure_session(); common_ensure_session();
if (array_key_exists('invitecode', $_SESSION)) { if (array_key_exists('invitecode', $_SESSION)) {
...@@ -491,6 +498,45 @@ class RegisterAction extends Action ...@@ -491,6 +498,45 @@ class RegisterAction extends Action
$this->elementStart('li'); $this->elementStart('li');
$this->element('input', $attrs); $this->element('input', $attrs);
$this->elementStart('label', array('class' => 'checkbox', 'for' => 'license')); $this->elementStart('label', array('class' => 'checkbox', 'for' => 'license'));
$this->raw($this->licenseCheckbox());
$this->elementEnd('label');
$this->elementEnd('li');
}
$this->elementEnd('ul');
$this->submit('submit', _('Register'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
function licenseCheckbox()
{
$out = '';
switch (common_config('license', 'type')) {
case 'private':
// TRANS: Copyright checkbox label in registration dialog, for private sites.
$out .= htmlspecialchars(sprintf(
_('I understand that content and data of %1$s are private and confidential.'),
common_config('site', 'name')));
// fall through
case 'allrightsreserved':
if ($out != '') {
$out .= ' ';
}
if (common_config('license', 'owner')) {
// TRANS: Copyright checkbox label in registration dialog, for all rights reserved with a specified copyright owner.
$out .= htmlspecialchars(sprintf(
_('My text and files are copyright by %1$s.'),
common_config('license', 'owner')));
} else {
// TRANS: Copyright checkbox label in registration dialog, for all rights reserved with ownership left to contributors.
$out .= htmlspecialchars(_('My text and files remain under my own copyright.'));
}
// TRANS: Copyright checkbox label in registration dialog, for all rights reserved.
$out .= ' ' . _('All rights reserved.');
break;
case 'cc': // fall through
default:
// TRANS: Copyright checkbox label in registration dialog, for Creative Commons-style licenses.
$message = _('My text and files are available under %s ' . $message = _('My text and files are available under %s ' .
'except this private data: password, ' . 'except this private data: password, ' .
'email address, IM address, and phone number.'); 'email address, IM address, and phone number.');
...@@ -499,14 +545,9 @@ class RegisterAction extends Action ...@@ -499,14 +545,9 @@ class RegisterAction extends Action
'">' . '">' .
htmlspecialchars(common_config('license', 'title')) . htmlspecialchars(common_config('license', 'title')) .
'</a>'; '</a>';
$this->raw(sprintf(htmlspecialchars($message), $link)); $out .= sprintf(htmlspecialchars($message), $link);
$this->elementEnd('label');
$this->elementEnd('li');
} }
$this->elementEnd('ul'); return $out;
$this->submit('submit', _('Register'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
} }
/** /**
......
...@@ -121,11 +121,11 @@ class ShowfavoritesAction extends OwnerDesignAction ...@@ -121,11 +121,11 @@ class ShowfavoritesAction extends OwnerDesignAction
// Show imported/gateway notices as well as local if // Show imported/gateway notices as well as local if
// the user is looking at his own favorites // the user is looking at his own favorites
$this->notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE, $this->notice = $this->user->favoriteNotices(true, ($this->page-1)*NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1, true); NOTICES_PER_PAGE + 1);
} else { } else {
$this->notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE, $this->notice = $this->user->favoriteNotices(false, ($this->page-1)*NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1, false); NOTICES_PER_PAGE + 1);
} }
if (empty($this->notice)) { if (empty($this->notice)) {
......
...@@ -342,10 +342,24 @@ class TwitapisearchatomAction extends ApiAction ...@@ -342,10 +342,24 @@ class TwitapisearchatomAction extends ApiAction
'rel' => 'related', 'rel' => 'related',
'href' => $profile->avatarUrl())); 'href' => $profile->avatarUrl()));
// TODO: Here is where we'd put in a link to an atom feed for threads // @todo: Here is where we'd put in a link to an atom feed for threads
$source = null;
$ns = $notice->getSource();
if ($ns) {
if (!empty($ns->name) && !empty($ns->url)) {
$source = '<a href="'
. htmlspecialchars($ns->url)
. '" rel="nofollow">'
. htmlspecialchars($ns->name)
. '</a>';
} else {
$source = $ns->code;
}
}
$this->element("twitter:source", null, $this->element("twitter:source", null, $source);
htmlentities($this->sourceLink($notice->source)));
$this->elementStart('author'); $this->elementStart('author');
......
...@@ -75,13 +75,13 @@ class Fave extends Memcached_DataObject ...@@ -75,13 +75,13 @@ class Fave extends Memcached_DataObject
return Memcached_DataObject::pkeyGet('Fave', $kv); return Memcached_DataObject::pkeyGet('Fave', $kv);
} }
function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false) function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false, $since_id=0, $max_id=0)
{ {
$ids = Notice::stream(array('Fave', '_streamDirect'), $ids = Notice::stream(array('Fave', '_streamDirect'),
array($user_id, $own), array($user_id, $own),
($own) ? 'fave:ids_by_user_own:'.$user_id : ($own) ? 'fave:ids_by_user_own:'.$user_id :
'fave:ids_by_user:'.$user_id, 'fave:ids_by_user:'.$user_id,
$offset, $limit); $offset, $limit, $since_id, $max_id);
return $ids; return $ids;
} }
......
...@@ -703,7 +703,7 @@ class Notice extends Memcached_DataObject ...@@ -703,7 +703,7 @@ class Notice extends Memcached_DataObject
/** /**
* Is this notice part of an active conversation? * Is this notice part of an active conversation?
* *
* @return boolean true if other messages exist in the same * @return boolean true if other messages exist in the same
* conversation, false if this is the only one * conversation, false if this is the only one
*/ */
...@@ -1171,7 +1171,7 @@ class Notice extends Memcached_DataObject ...@@ -1171,7 +1171,7 @@ class Notice extends Memcached_DataObject
return $groups; return $groups;
} }
function asAtomEntry($namespace=false, $source=false, $author=true) function asAtomEntry($namespace=false, $source=false, $author=true, $cur=null)
{ {
$profile = $this->getProfile(); $profile = $this->getProfile();
...@@ -1184,7 +1184,8 @@ class Notice extends Memcached_DataObject ...@@ -1184,7 +1184,8 @@ class Notice extends Memcached_DataObject
'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/',
'xmlns:media' => 'http://purl.org/syndication/atommedia', 'xmlns:media' => 'http://purl.org/syndication/atommedia',
'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
'xmlns:ostatus' => 'http://ostatus.org/schema/1.0'); 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0',
'xmlns:statusnet' => 'http://status.net/ont/');
} else { } else {
$attrs = array(); $attrs = array();
} }
...@@ -1232,6 +1233,24 @@ class Notice extends Memcached_DataObject ...@@ -1232,6 +1233,24 @@ class Notice extends Memcached_DataObject
$xs->element('published', null, common_date_w3dtf($this->created)); $xs->element('published', null, common_date_w3dtf($this->created));
$xs->element('updated', null, common_date_w3dtf($this->created)); $xs->element('updated', null, common_date_w3dtf($this->created));
$noticeInfoAttr = array(
'local_id' => $this->id, // local notice ID (useful to clients for ordering)
'source' => $this->source // the client name (source attribution)
);
$ns = $this->getSource();
if ($ns) {
if (!empty($ns->url)) {
$noticeInfoAttr['source_link'] = $ns->url;
}
}
if (!empty($cur)) {
$noticeInfoAttr['favorited'] = ($cur->hasFave($this)) ? 'true' : 'false';
}
$xs->element('statusnet:notice_info', $noticeInfoAttr, null);
if ($this->reply_to) { if ($this->reply_to) {
$reply_notice = Notice::staticGet('id', $this->reply_to); $reply_notice = Notice::staticGet('id', $this->reply_to);
if (!empty($reply_notice)) { if (!empty($reply_notice)) {
...@@ -1796,4 +1815,41 @@ class Notice extends Memcached_DataObject ...@@ -1796,4 +1815,41 @@ class Notice extends Memcached_DataObject
return $result; return $result;
} }
/**
* Get the source of the notice
*
* @return Notice_source $ns A notice source object. 'code' is the only attribute
* guaranteed to be populated.
*/
function getSource()
{
$ns = new Notice_source();
if (!empty($this->source)) {
switch ($this->source) {
case 'web':
case 'xmpp':
case 'mail':
case 'omb':
case 'system':
case 'api':
$ns->code = $this->source;
break;
default:
$ns = Notice_source::staticGet($this->source);
if (!$ns) {
$ns = new Notice_source();
$ns->code = $this->source;
$app = Oauth_application::staticGet('name', $this->source);
if ($app) {
$ns->name = $app->name;
$ns->url = $app->source_url;
}
}
break;
}
}
return $ns;
}
} }
...@@ -464,9 +464,9 @@ class User extends Memcached_DataObject ...@@ -464,9 +464,9 @@ class User extends Memcached_DataObject
return $profile->getNotices($offset, $limit, $since_id, $before_id);