Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly
This commit is contained in:
commit
c00b2ecc4b
2
INSTALL
2
INSTALL
|
@ -26,7 +26,7 @@ PHP modules
|
|||
The following software packages are *required* for this software to
|
||||
run correctly.
|
||||
|
||||
- PHP 5.4+ For newer versions, some functions that are used may be
|
||||
- PHP 5.5+ For newer versions, some functions that are used may be
|
||||
disabled by default, such as the pcntl_* family. See the
|
||||
section on 'Queues and daemons' for more information.
|
||||
- MariaDB 5+ GNU Social uses, by default, a MariaDB server for data
|
||||
|
|
|
@ -128,7 +128,7 @@ class ConversationAction extends ManagedAction
|
|||
'format' => 'atom')),
|
||||
// TRANS: Title for link to notice feed.
|
||||
// TRANS: %s is a user nickname.
|
||||
_('Conversation feed (Activity Streams JSON)')));
|
||||
_('Conversation feed (Atom)')));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -410,6 +410,7 @@ class EmailsettingsAction extends SettingsAction
|
|||
$this->serverError(_('Could not insert confirmation code.'));
|
||||
}
|
||||
|
||||
common_debug('Sending confirmation address for user '.$user->id.' to email '.$email);
|
||||
mail_confirm_address($user, $confirm->code, $user->nickname, $email);
|
||||
|
||||
Event::handle('EndAddEmailAddress', array($user, $email));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
class NetworkpublicAction extends PublicAction
|
||||
class NetworkpublicAction extends SitestreamAction
|
||||
{
|
||||
protected function streamPrepare()
|
||||
{
|
||||
|
@ -28,13 +28,6 @@ class NetworkpublicAction extends PublicAction
|
|||
}
|
||||
}
|
||||
|
||||
function extraHead()
|
||||
{
|
||||
// the PublicAction has some XRDS stuff that might be unique to the non-network public feed
|
||||
// FIXME: Solve this with a call that doesn't rely on parent:: and is unique for each class.
|
||||
ManagedAction::extraHead();
|
||||
}
|
||||
|
||||
function showSections()
|
||||
{
|
||||
// Show invite button, as long as site isn't closed, and
|
||||
|
|
|
@ -29,10 +29,6 @@
|
|||
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
// Farther than any human will go
|
||||
|
||||
define('MAX_PUBLIC_PAGE', 100);
|
||||
|
||||
/**
|
||||
* Action for displaying the public stream
|
||||
*
|
||||
|
@ -43,54 +39,9 @@ define('MAX_PUBLIC_PAGE', 100);
|
|||
* @link http://status.net/
|
||||
*
|
||||
* @see PublicrssAction
|
||||
* @see PublicxrdsAction
|
||||
*/
|
||||
class PublicAction extends ManagedAction
|
||||
class PublicAction extends SitestreamAction
|
||||
{
|
||||
/**
|
||||
* page of the stream we're on; default = 1
|
||||
*/
|
||||
|
||||
var $page = null;
|
||||
var $notice;
|
||||
|
||||
protected $stream = null;
|
||||
|
||||
function isReadOnly($args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function doPreparation()
|
||||
{
|
||||
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
|
||||
|
||||
if ($this->page > MAX_PUBLIC_PAGE) {
|
||||
// TRANS: Client error displayed when requesting a public timeline page beyond the page limit.
|
||||
// TRANS: %s is the page limit.
|
||||
$this->clientError(sprintf(_('Beyond the page limit (%s).'), MAX_PUBLIC_PAGE));
|
||||
}
|
||||
|
||||
common_set_returnto($this->selfUrl());
|
||||
|
||||
$this->streamPrepare();
|
||||
|
||||
$this->notice = $this->stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
|
||||
NOTICES_PER_PAGE + 1);
|
||||
|
||||
if (!$this->notice) {
|
||||
// TRANS: Server error displayed when a public timeline cannot be retrieved.
|
||||
$this->serverError(_('Could not retrieve public timeline.'));
|
||||
}
|
||||
|
||||
if ($this->page > 1 && $this->notice->N == 0){
|
||||
// TRANS: Client error when page not found (404).
|
||||
$this->clientError(_('No such page.'), 404);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function streamPrepare()
|
||||
{
|
||||
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
|
||||
|
@ -117,100 +68,6 @@ class PublicAction extends ManagedAction
|
|||
}
|
||||
}
|
||||
|
||||
function extraHead()
|
||||
{
|
||||
parent::extraHead();
|
||||
$this->element('meta', array('http-equiv' => 'X-XRDS-Location',
|
||||
'content' => common_local_url('publicxrds')));
|
||||
|
||||
$rsd = common_local_url('rsd');
|
||||
|
||||
// RSD, http://tales.phrasewise.com/rfc/rsd
|
||||
|
||||
$this->element('link', array('rel' => 'EditURI',
|
||||
'type' => 'application/rsd+xml',
|
||||
'href' => $rsd));
|
||||
|
||||
if ($this->page != 1) {
|
||||
$this->element('link', array('rel' => 'canonical',
|
||||
'href' => common_local_url('public')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output <head> elements for RSS and Atom feeds
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function getFeeds()
|
||||
{
|
||||
return array(new Feed(Feed::JSON,
|
||||
common_local_url('ApiTimelinePublic',
|
||||
array('format' => 'as')),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (Activity Streams JSON)')),
|
||||
new Feed(Feed::RSS1, common_local_url('publicrss'),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (RSS 1.0)')),
|
||||
new Feed(Feed::RSS2,
|
||||
common_local_url('ApiTimelinePublic',
|
||||
array('format' => 'rss')),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (RSS 2.0)')),
|
||||
new Feed(Feed::ATOM,
|
||||
common_local_url('ApiTimelinePublic',
|
||||
array('format' => 'atom')),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (Atom)')));
|
||||
}
|
||||
|
||||
function showEmptyList()
|
||||
{
|
||||
// TRANS: Text displayed for public feed when there are no public notices.
|
||||
$message = _('This is the public timeline for %%site.name%% but no one has posted anything yet.') . ' ';
|
||||
|
||||
if (common_logged_in()) {
|
||||
// TRANS: Additional text displayed for public feed when there are no public notices for a logged in user.
|
||||
$message .= _('Be the first to post!');
|
||||
}
|
||||
else {
|
||||
if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
|
||||
// TRANS: Additional text displayed for public feed when there are no public notices for a not logged in user.
|
||||
$message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
|
||||
}
|
||||
}
|
||||
|
||||
$this->elementStart('div', 'guide');
|
||||
$this->raw(common_markup_to_html($message));
|
||||
$this->elementEnd('div');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the content area
|
||||
*
|
||||
* Shows a list of the notices in the public stream, with some pagination
|
||||
* controls.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showContent()
|
||||
{
|
||||
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
|
||||
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
|
||||
} else {
|
||||
$nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
|
||||
}
|
||||
|
||||
$cnt = $nl->show();
|
||||
|
||||
if ($cnt == 0) {
|
||||
$this->showEmptyList();
|
||||
}
|
||||
|
||||
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
|
||||
$this->page, $this->action);
|
||||
}
|
||||
|
||||
function showSections()
|
||||
{
|
||||
// Show invite button, as long as site isn't closed, and
|
||||
|
@ -239,23 +96,30 @@ class PublicAction extends ManagedAction
|
|||
$feat->show();
|
||||
}
|
||||
|
||||
function showAnonymousMessage()
|
||||
/**
|
||||
* Output <head> elements for RSS and Atom feeds
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function getFeeds()
|
||||
{
|
||||
if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
|
||||
// TRANS: Message for not logged in users at an invite-only site trying to view the public feed of notices.
|
||||
// TRANS: This message contains Markdown links. Please mind the formatting.
|
||||
$m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
|
||||
'based on the Free Software [StatusNet](http://status.net/) tool. ' .
|
||||
'[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ' .
|
||||
'([Read more](%%doc.help%%))');
|
||||
} else {
|
||||
// TRANS: Message for not logged in users at a closed site trying to view the public feed of notices.
|
||||
// TRANS: This message contains Markdown links. Please mind the formatting.
|
||||
$m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
|
||||
'based on the Free Software [StatusNet](http://status.net/) tool.');
|
||||
}
|
||||
$this->elementStart('div', array('id' => 'anon_notice'));
|
||||
$this->raw(common_markup_to_html($m));
|
||||
$this->elementEnd('div');
|
||||
return array(new Feed(Feed::JSON,
|
||||
common_local_url('ApiTimelinePublic',
|
||||
array('format' => 'as')),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (Activity Streams JSON)')),
|
||||
new Feed(Feed::RSS1, common_local_url('publicrss'),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (RSS 1.0)')),
|
||||
new Feed(Feed::RSS2,
|
||||
common_local_url('ApiTimelinePublic',
|
||||
array('format' => 'rss')),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (RSS 2.0)')),
|
||||
new Feed(Feed::ATOM,
|
||||
common_local_url('ApiTimelinePublic',
|
||||
array('format' => 'atom')),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (Atom)')));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -272,10 +272,16 @@ class RecoverpasswordAction extends Action
|
|||
try {
|
||||
User::recoverPassword($nore);
|
||||
$this->mode = 'sent';
|
||||
// TRANS: User notification after an e-mail with instructions was sent from the password recovery form.
|
||||
$this->msg = _('Instructions for recovering your password ' .
|
||||
'have been sent to the email address registered to your ' .
|
||||
'account.');
|
||||
if (common_is_email($nore) && common_config('site', 'fakeaddressrecovery')) {
|
||||
// TRANS: User notification when recovering password by giving email address,
|
||||
// regardless if the mail was sent or not (to hide registered email status).
|
||||
$this->msg = _('If the email address you provided was found in the database, a recovery mail with instructions has been sent there.');
|
||||
} else {
|
||||
// TRANS: User notification after an e-mail with instructions was sent from the password recovery form.
|
||||
$this->msg = _('Instructions for recovering your password ' .
|
||||
'have been sent to the email address registered to your ' .
|
||||
'account.');
|
||||
}
|
||||
$this->success = true;
|
||||
} catch (Exception $e) {
|
||||
$this->success = false;
|
||||
|
|
|
@ -116,14 +116,14 @@ class File extends Managed_DataObject
|
|||
*
|
||||
* @fixme refactor this mess, it's gotten pretty scary.
|
||||
* @param string $given_url the URL we're looking at
|
||||
* @param int $notice_id (optional)
|
||||
* @param Notice $notice (optional)
|
||||
* @param bool $followRedirects defaults to true
|
||||
*
|
||||
* @return mixed File on success, -1 on some errors
|
||||
*
|
||||
* @throws ServerException on failure
|
||||
*/
|
||||
public static function processNew($given_url, $notice_id=null, $followRedirects=true) {
|
||||
public static function processNew($given_url, Notice $notice=null, $followRedirects=true) {
|
||||
if (empty($given_url)) {
|
||||
throw new ServerException('No given URL to process');
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ class File extends Managed_DataObject
|
|||
//
|
||||
// Seen in the wild with clojure.org, which redirects through
|
||||
// wikispaces for auth and appends session data in the URL params.
|
||||
$file = self::processNew($redir_url, $notice_id, /*followRedirects*/false);
|
||||
$file = self::processNew($redir_url, $notice, /*followRedirects*/false);
|
||||
File_redirection::saveNew($redir_data, $file->id, $given_url);
|
||||
}
|
||||
|
||||
|
@ -193,8 +193,8 @@ class File extends Managed_DataObject
|
|||
}
|
||||
}
|
||||
|
||||
if (!empty($notice_id)) {
|
||||
File_to_post::processNew($file->id, $notice_id);
|
||||
if ($notice instanceof Notice) {
|
||||
File_to_post::processNew($file, $notice);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
@ -249,6 +249,15 @@ class File extends Managed_DataObject
|
|||
return true;
|
||||
}
|
||||
|
||||
public function getFilename()
|
||||
{
|
||||
if (!self::validFilename($this->filename)) {
|
||||
// TRANS: Client exception thrown if a file upload does not have a valid name.
|
||||
throw new ClientException(_("Invalid filename."));
|
||||
}
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
// where should the file go?
|
||||
|
||||
static function filename(Profile $profile, $origname, $mimetype)
|
||||
|
@ -501,9 +510,9 @@ class File extends Managed_DataObject
|
|||
|
||||
function blowCache($last=false)
|
||||
{
|
||||
self::blow('file:notice-ids:%s', $this->urlhash);
|
||||
self::blow('file:notice-ids:%s', $this->id);
|
||||
if ($last) {
|
||||
self::blow('file:notice-ids:%s;last', $this->urlhash);
|
||||
self::blow('file:notice-ids:%s;last', $this->id);
|
||||
}
|
||||
self::blow('file:notice-count:%d', $this->id);
|
||||
}
|
||||
|
@ -610,12 +619,45 @@ class File extends Managed_DataObject
|
|||
return;
|
||||
}
|
||||
echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
|
||||
|
||||
$file = new File();
|
||||
$file->query(sprintf('SELECT id, LEFT(url, 191) AS shortenedurl, COUNT(*) AS c FROM %1$s WHERE LENGTH(url)>191 GROUP BY shortenedurl HAVING c > 1', $schema->quoteIdentifier($table)));
|
||||
print "\nFound {$file->N} URLs with too long entries in file table\n";
|
||||
while ($file->fetch()) {
|
||||
// We've got a URL that is too long for our future file table
|
||||
// so we'll cut it. We could save the original URL, but there is
|
||||
// no guarantee it is complete anyway since the previous max was 255 chars.
|
||||
$dupfile = new File();
|
||||
// First we find file entries that would be duplicates of this when shortened
|
||||
// ... and we'll just throw the dupes out the window for now! It's already so borken.
|
||||
$dupfile->query(sprintf('SELECT * FROM file WHERE LEFT(url, 191) = "%1$s"', $file->shortenedurl));
|
||||
// Leave one of the URLs in the database by using ->find(true) (fetches first entry)
|
||||
if ($dupfile->find(true)) {
|
||||
print "\nShortening url entry for $table id: {$file->id} [";
|
||||
$orig = clone($dupfile);
|
||||
$dupfile->url = $file->shortenedurl; // make sure it's only 191 chars from now on
|
||||
$dupfile->update($orig);
|
||||
print "\nDeleting duplicate entries of too long URL on $table id: {$file->id} [";
|
||||
// only start deleting with this fetch.
|
||||
while($dupfile->fetch()) {
|
||||
print ".";
|
||||
$dupfile->delete();
|
||||
}
|
||||
print "]\n";
|
||||
} else {
|
||||
print "\nWarning! URL suddenly disappeared from database: {$file->url}\n";
|
||||
}
|
||||
}
|
||||
echo "...and now all the non-duplicates which are longer than 191 characters...\n";
|
||||
$file->query('UPDATE file SET url=LEFT(url, 191) WHERE LENGTH(url)>191');
|
||||
|
||||
echo "\n...now running hacky pre-schemaupdate change for $table:";
|
||||
// We have to create a urlhash that is _not_ the primary key,
|
||||
// transfer data and THEN run checkSchema
|
||||
$schemadef['fields']['urlhash'] = array (
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => true,
|
||||
'not null' => false, // this is because when adding column, all entries will _be_ NULL!
|
||||
'description' => 'sha256 of destination URL (url field)',
|
||||
);
|
||||
$schemadef['fields']['url'] = array (
|
||||
|
|
|
@ -59,12 +59,7 @@ class File_redirection extends Managed_DataObject
|
|||
|
||||
static public function getByUrl($url)
|
||||
{
|
||||
$file = new File_redirection();
|
||||
$file->urlhash = File::hashurl($url);
|
||||
if (!$file->find(true)) {
|
||||
throw new NoResultException($file);
|
||||
}
|
||||
return $file;
|
||||
return self::getByPK(array('urlhash' => File::hashurl($url)));
|
||||
}
|
||||
|
||||
static function _commonHttp($url, $redirs) {
|
||||
|
@ -261,7 +256,7 @@ class File_redirection extends Managed_DataObject
|
|||
// store it
|
||||
$file = File::getKV('url', $long_url);
|
||||
if ($file instanceof File) {
|
||||
$file_id = $file->id;
|
||||
$file_id = $file->getID();
|
||||
} else {
|
||||
// Check if the target URL is itself a redirect...
|
||||
$redir_data = File_redirection::where($long_url);
|
||||
|
@ -269,7 +264,7 @@ class File_redirection extends Managed_DataObject
|
|||
// We haven't seen the target URL before.
|
||||
// Save file and embedding data about it!
|
||||
$file = File::saveNew($redir_data, $long_url);
|
||||
$file_id = $file->id;
|
||||
$file_id = $file->getID();
|
||||
} else if (is_string($redir_data)) {
|
||||
// The file is a known redirect target.
|
||||
$file = File::getKV('url', $redir_data);
|
||||
|
@ -281,7 +276,7 @@ class File_redirection extends Managed_DataObject
|
|||
// SSL sites with cert issues.
|
||||
return null;
|
||||
}
|
||||
$file_id = $file->id;
|
||||
$file_id = $file->getID();
|
||||
}
|
||||
}
|
||||
$file_redir = File_redirection::getKV('url', $short_url);
|
||||
|
|
|
@ -82,9 +82,9 @@ class File_thumbnail extends Managed_DataObject
|
|||
* Fetch an entry by using a File's id
|
||||
*/
|
||||
static function byFile(File $file) {
|
||||
$file_thumbnail = self::getKV('file_id', $file->id);
|
||||
$file_thumbnail = self::getKV('file_id', $file->getID());
|
||||
if (!$file_thumbnail instanceof File_thumbnail) {
|
||||
throw new ServerException(sprintf('No File_thumbnail entry for File id==%u', $file->id));
|
||||
throw new ServerException(sprintf('No File_thumbnail entry for File id==%u', $file->getID()));
|
||||
}
|
||||
return $file_thumbnail;
|
||||
}
|
||||
|
@ -167,11 +167,6 @@ class File_thumbnail extends Managed_DataObject
|
|||
|
||||
public function getFile()
|
||||
{
|
||||
$file = new File();
|
||||
$file->id = $this->file_id;
|
||||
if (!$file->find(true)) {
|
||||
throw new NoResultException($file);
|
||||
}
|
||||
return $file;
|
||||
return File::getByID($this->file_id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
||||
|
||||
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Table Definition for file_to_post
|
||||
|
@ -58,39 +56,59 @@ class File_to_post extends Managed_DataObject
|
|||
);
|
||||
}
|
||||
|
||||
function processNew($file_id, $notice_id) {
|
||||
function processNew(File $file, Notice $notice) {
|
||||
static $seen = array();
|
||||
if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) {
|
||||
|
||||
$f2p = File_to_post::pkeyGet(array('post_id' => $notice_id,
|
||||
'file_id' => $file_id));
|
||||
if (empty($f2p)) {
|
||||
$file_id = $file->getID();
|
||||
$notice_id = $notice->getID();
|
||||
if (!array_key_exists($notice_id, $seen)) {
|
||||
$seen[$notice_id] = array();
|
||||
}
|
||||
|
||||
if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) {
|
||||
try {
|
||||
$f2p = File_to_post::getByPK(array('post_id' => $notice_id,
|
||||
'file_id' => $file_id));
|
||||
} catch (NoResultException $e) {
|
||||
$f2p = new File_to_post;
|
||||
$f2p->file_id = $file_id;
|
||||
$f2p->post_id = $notice_id;
|
||||
$f2p->insert();
|
||||
|
||||
$f = File::getKV($file_id);
|
||||
|
||||
if (!empty($f)) {
|
||||
$f->blowCache();
|
||||
}
|
||||
$file->blowCache();
|
||||
}
|
||||
|
||||
if (empty($seen[$notice_id])) {
|
||||
$seen[$notice_id] = array($file_id);
|
||||
} else {
|
||||
$seen[$notice_id][] = $file_id;
|
||||
}
|
||||
$seen[$notice_id][] = $file_id;
|
||||
}
|
||||
}
|
||||
|
||||
static function getNoticeIDsByFile(File $file)
|
||||
{
|
||||
$f2p = new File_to_post();
|
||||
|
||||
$f2p->selectAdd();
|
||||
$f2p->selectAdd('post_id');
|
||||
|
||||
$f2p->file_id = $file->getID();
|
||||
|
||||
$ids = array();
|
||||
|
||||
if (!$f2p->find()) {
|
||||
throw new NoResultException($f2p);
|
||||
}
|
||||
|
||||
return $f2p->fetchAll('post_id');
|
||||
}
|
||||
|
||||
function delete($useWhere=false)
|
||||
{
|
||||
$f = File::getKV('id', $this->file_id);
|
||||
if ($f instanceof File) {
|
||||
try {
|
||||
$f = File::getByID($this->file_id);
|
||||
$f->blowCache();
|
||||
} catch (NoResultException $e) {
|
||||
// ...alright, that's weird, but no File to delete anyway.
|
||||
}
|
||||
|
||||
return parent::delete($useWhere);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,11 @@ abstract class Managed_DataObject extends Memcached_DataObject
|
|||
return parent::pkeyGetClass(get_called_class(), $kv);
|
||||
}
|
||||
|
||||
static function pkeyCols()
|
||||
{
|
||||
return parent::pkeyColsClass(get_called_class());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple items from the database by key
|
||||
*
|
||||
|
@ -304,6 +309,53 @@ abstract class Managed_DataObject extends Memcached_DataObject
|
|||
return common_database_tablename($this->tableName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object by looking at the primary key column(s).
|
||||
*
|
||||
* Will require all primary key columns to be defined in an associative array
|
||||
* and ignore any keys which are not part of the primary key.
|
||||
*
|
||||
* Will NOT accept NULL values as part of primary key.
|
||||
*
|
||||
* @param array $vals Must match all primary key columns for the dataobject.
|
||||
*
|
||||
* @return Managed_DataObject of the get_called_class() type
|
||||
* @throws NoResultException if no object with that primary key
|
||||
*/
|
||||
static function getByPK(array $vals)
|
||||
{
|
||||
$classname = get_called_class();
|
||||
|
||||
$pkey = static::pkeyCols();
|
||||
if (is_null($pkey)) {
|
||||
throw new ServerException("Failed to get primary key columns for class '{$classname}'");
|
||||
}
|
||||
|
||||
$object = new $classname();
|
||||
foreach ($pkey as $col) {
|
||||
if (!array_key_exists($col, $vals)) {
|
||||
throw new ServerException("Missing primary key column '{$col}'");
|
||||
} elseif (is_null($vals[$col])) {
|
||||
throw new ServerException("NULL values not allowed in getByPK for column '{$col}'");
|
||||
}
|
||||
$object->$col = $vals[$col];
|
||||
}
|
||||
if (!$object->find(true)) {
|
||||
throw new NoResultException($object);
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
static function getByID($id)
|
||||
{
|
||||
if (empty($id)) {
|
||||
throw new ServerException('Empty ID on lookup');
|
||||
}
|
||||
// getByPK throws exception if id is null
|
||||
// or if the class does not have a single 'id' column as primary key
|
||||
return static::getByPK(array('id' => $id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ID, checked that it is set and reasonably valid
|
||||
*
|
||||
|
|
|
@ -34,7 +34,7 @@ class Memcached_DataObject extends Safe_DataObject
|
|||
{
|
||||
if (is_null($v)) {
|
||||
$v = $k;
|
||||
$keys = self::pkeyCols($cls);
|
||||
$keys = static::pkeyCols();
|
||||
if (count($keys) > 1) {
|
||||
// FIXME: maybe call pkeyGetClass() ourselves?
|
||||
throw new Exception('Use pkeyGetClass() for compound primary keys');
|
||||
|
@ -246,7 +246,7 @@ class Memcached_DataObject extends Safe_DataObject
|
|||
return $query;
|
||||
}
|
||||
|
||||
static function pkeyCols($cls)
|
||||
static function pkeyColsClass($cls)
|
||||
{
|
||||
$i = new $cls;
|
||||
$types = $i->keyTypes();
|
||||
|
@ -279,7 +279,7 @@ class Memcached_DataObject extends Safe_DataObject
|
|||
$pkeyMap = array_fill_keys($keyVals, array());
|
||||
$result = array_fill_keys($keyVals, array());
|
||||
|
||||
$pkeyCols = self::pkeyCols($cls);
|
||||
$pkeyCols = static::pkeyCols();
|
||||
|
||||
$toFetch = array();
|
||||
$allPkeys = array();
|
||||
|
|
|
@ -84,7 +84,7 @@ class Notice extends Managed_DataObject
|
|||
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
|
||||
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'who made the update'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
|
||||
'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8_general_ci'),
|
||||
'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8mb4_general_ci'),
|
||||
'rendered' => array('type' => 'text', 'description' => 'HTML version of the content'),
|
||||
'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL of any attachment (image, video, bookmark, whatever)'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
|
@ -313,16 +313,6 @@ class Notice extends Managed_DataObject
|
|||
return $notice;
|
||||
}
|
||||
|
||||
public static function getById($id)
|
||||
{
|
||||
$notice = new Notice();
|
||||
$notice->id = $id;
|
||||
if (!$notice->find(true)) {
|
||||
throw new NoResultException($notice);
|
||||
}
|
||||
return $notice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract #hashtags from this notice's content and save them to the database.
|
||||
*/
|
||||
|
@ -1109,7 +1099,7 @@ class Notice extends Managed_DataObject
|
|||
*/
|
||||
function saveUrls() {
|
||||
if (common_config('attachments', 'process_links')) {
|
||||
common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this->id);
|
||||
common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1126,11 +1116,7 @@ class Notice extends Managed_DataObject
|
|||
if (common_config('attachments', 'process_links')) {
|
||||
// @fixme validation?
|
||||
foreach (array_unique($urls) as $url) {
|
||||
try {
|
||||
File::processNew($url, $this->id);
|
||||
} catch (ServerException $e) {
|
||||
// Could not save URL. Log it?
|
||||
}
|
||||
$this->saveUrl($url, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1138,9 +1124,9 @@ class Notice extends Managed_DataObject
|
|||
/**
|
||||
* @private callback
|
||||
*/
|
||||
function saveUrl($url, $notice_id) {
|
||||
function saveUrl($url, Notice $notice) {
|
||||
try {
|
||||
File::processNew($url, $notice_id);
|
||||
File::processNew($url, $notice);
|
||||
} catch (ServerException $e) {
|
||||
// Could not save URL. Log it?
|
||||
}
|
||||
|
@ -1311,7 +1297,7 @@ class Notice extends Managed_DataObject
|
|||
$last = $parent;
|
||||
continue;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
} catch (NoParentNoticeException $e) {
|
||||
// Latest notice has no parent
|
||||
}
|
||||
// No parent, or parent out of scope
|
||||
|
@ -1617,7 +1603,7 @@ class Notice extends Managed_DataObject
|
|||
$this->saveReply($parentauthor->id);
|
||||
$replied[$parentauthor->id] = 1;
|
||||
self::blow('reply:stream:%d', $parentauthor->id);
|
||||
} catch (Exception $e) {
|
||||
} catch (NoParentNoticeException $e) {
|
||||
// Not a reply, since it has no parent!
|
||||
}
|
||||
|
||||
|
@ -1634,8 +1620,7 @@ class Notice extends Managed_DataObject
|
|||
foreach ($mention['mentioned'] as $mentioned) {
|
||||
|
||||
// skip if they're already covered
|
||||
|
||||
if (!empty($replied[$mentioned->id])) {
|
||||
if (array_key_exists($mentioned->id, $replied)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1852,8 +1837,8 @@ class Notice extends Managed_DataObject
|
|||
try {
|
||||
$reply = $this->getParent();
|
||||
$ctx->replyToID = $reply->getUri();
|
||||
$ctx->replyToUrl = $reply->getUrl();
|
||||
} catch (Exception $e) {
|
||||
$ctx->replyToUrl = $reply->getUrl(true); // true for fallback to local URL, less messy
|
||||
} catch (NoParentNoticeException $e) {
|
||||
// This is not a reply to something
|
||||
}
|
||||
|
||||
|
@ -2763,13 +2748,10 @@ class Notice extends Managed_DataObject
|
|||
|
||||
public function getParent()
|
||||
{
|
||||
$parent = Notice::getKV('id', $this->reply_to);
|
||||
|
||||
if (!$parent instanceof Notice) {
|
||||
throw new ServerException('Notice has no parent');
|
||||
if (empty($this->reply_to)) {
|
||||
throw new NoParentNoticeException($this);
|
||||
}
|
||||
|
||||
return $parent;
|
||||
return self::getByID($this->reply_to);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,12 +48,12 @@ class Profile extends Managed_DataObject
|
|||
'description' => 'local and remote users have profiles',
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
|
||||
'nickname' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username', 'collate' => 'utf8_general_ci'),
|
||||
'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name', 'collate' => 'utf8_general_ci'),
|
||||
'nickname' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username', 'collate' => 'utf8mb4_general_ci'),
|
||||
'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name', 'collate' => 'utf8mb4_general_ci'),
|
||||
'profileurl' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
|
||||
'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'identifying URL', 'collate' => 'utf8_general_ci'),
|
||||
'bio' => array('type' => 'text', 'description' => 'descriptive biography', 'collate' => 'utf8_general_ci'),
|
||||
'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'physical location', 'collate' => 'utf8_general_ci'),
|
||||
'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'identifying URL', 'collate' => 'utf8mb4_general_ci'),
|
||||
'bio' => array('type' => 'text', 'description' => 'descriptive biography', 'collate' => 'utf8mb4_general_ci'),
|
||||
'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'physical location', 'collate' => 'utf8mb4_general_ci'),
|
||||
'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'),
|
||||
|
@ -880,6 +880,11 @@ class Profile extends Managed_DataObject
|
|||
$inst->delete();
|
||||
}
|
||||
|
||||
$localuser = User::getKV('id', $this->id);
|
||||
if ($localuser instanceof User) {
|
||||
$localuser->delete();
|
||||
}
|
||||
|
||||
return parent::delete($useWhere);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,11 +62,11 @@ class Profile_prefs extends Managed_DataObject
|
|||
{
|
||||
if (empty($topic)) {
|
||||
$prefs = new Profile_prefs();
|
||||
$prefs->profile_id = $profile->id;
|
||||
$prefs->profile_id = $profile->getID();
|
||||
$prefs->namespace = $namespace;
|
||||
$prefs->find();
|
||||
} else {
|
||||
$prefs = self::pivotGet('profile_id', $profile->id, array('namespace'=>$namespace, 'topic'=>$topic));
|
||||
$prefs = self::pivotGet('profile_id', $profile->getID(), array('namespace'=>$namespace, 'topic'=>$topic));
|
||||
}
|
||||
|
||||
if (empty($prefs->N)) {
|
||||
|
@ -85,7 +85,7 @@ class Profile_prefs extends Managed_DataObject
|
|||
static function getAll(Profile $profile)
|
||||
{
|
||||
try {
|
||||
$prefs = self::listFind('profile_id', $profile->id);
|
||||
$prefs = self::listFind('profile_id', $profile->getID());
|
||||
} catch (NoResultException $e) {
|
||||
return array();
|
||||
}
|
||||
|
@ -101,15 +101,9 @@ class Profile_prefs extends Managed_DataObject
|
|||
}
|
||||
|
||||
static function getTopic(Profile $profile, $namespace, $topic) {
|
||||
$pref = new Profile_prefs;
|
||||
$pref->profile_id = $profile->id;
|
||||
$pref->namespace = $namespace;
|
||||
$pref->topic = $topic;
|
||||
|
||||
if (!$pref->find(true)) {
|
||||
throw new NoResultException($pref);
|
||||
}
|
||||
return $pref;
|
||||
return Profile_prefs::getByPK(array('profile_id' => $profile->getID(),
|
||||
'namespace' => $namespace,
|
||||
'topic' => $topic));
|
||||
}
|
||||
|
||||
static function getData(Profile $profile, $namespace, $topic, $def=null) {
|
||||
|
@ -164,7 +158,7 @@ class Profile_prefs extends Managed_DataObject
|
|||
}
|
||||
|
||||
$pref = new Profile_prefs();
|
||||
$pref->profile_id = $profile->id;
|
||||
$pref->profile_id = $profile->getID();
|
||||
$pref->namespace = $namespace;
|
||||
$pref->topic = $topic;
|
||||
$pref->data = $data;
|
||||
|
|
|
@ -63,7 +63,7 @@ class Queue_item extends Managed_DataObject
|
|||
// XXX: potential race condition
|
||||
// can we force it to only update if claimed is still null
|
||||
// (or old)?
|
||||
common_log(LOG_INFO, 'claiming queue item id = ' . $qi->id .
|
||||
common_log(LOG_INFO, 'claiming queue item id = ' . $qi->getID() .
|
||||
' for transport ' . $qi->transport);
|
||||
$orig = clone($qi);
|
||||
$qi->claimed = common_sql_now();
|
||||
|
@ -85,7 +85,7 @@ class Queue_item extends Managed_DataObject
|
|||
function releaseClaim()
|
||||
{
|
||||
// DB_DataObject doesn't let us save nulls right now
|
||||
$sql = sprintf("UPDATE queue_item SET claimed=NULL WHERE id=%d", $this->id);
|
||||
$sql = sprintf("UPDATE queue_item SET claimed=NULL WHERE id=%d", $this->getID());
|
||||
$this->query($sql);
|
||||
|
||||
$this->claimed = null;
|
||||
|
|
|
@ -853,57 +853,59 @@ class User extends Managed_DataObject
|
|||
|
||||
static function recoverPassword($nore)
|
||||
{
|
||||
$user = User::getKV('email', common_canonical_email($nore));
|
||||
// $confirm_email will be used as a fallback if our user doesn't have a confirmed email
|
||||
$confirm_email = null;
|
||||
|
||||
if (!$user) {
|
||||
try {
|
||||
$user = User::getKV('nickname', common_canonical_nickname($nore));
|
||||
} catch (NicknameException $e) {
|
||||
// invalid
|
||||
if (common_is_email($nore)) {
|
||||
$user = User::getKV('email', common_canonical_email($nore));
|
||||
|
||||
// See if it's an unconfirmed email address
|
||||
if (!$user instanceof User) {
|
||||
// Warning: it may actually be legit to have multiple folks
|
||||
// who have claimed, but not yet confirmed, the same address.
|
||||
// We'll only send to the first one that comes up.
|
||||
$confirm_email = new Confirm_address();
|
||||
$confirm_email->address = common_canonical_email($nore);
|
||||
$confirm_email->address_type = 'email';
|
||||
if ($confirm_email->find(true)) {
|
||||
$user = User::getKV('id', $confirm_email->user_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if it's an unconfirmed email address
|
||||
|
||||
if (!$user) {
|
||||
// Warning: it may actually be legit to have multiple folks
|
||||
// who have claimed, but not yet confirmed, the same address.
|
||||
// We'll only send to the first one that comes up.
|
||||
$confirm_email = new Confirm_address();
|
||||
$confirm_email->address = common_canonical_email($nore);
|
||||
$confirm_email->address_type = 'email';
|
||||
$confirm_email->find();
|
||||
if ($confirm_email->fetch()) {
|
||||
$user = User::getKV($confirm_email->user_id);
|
||||
} else {
|
||||
$confirm_email = null;
|
||||
// No luck finding anyone by that email address.
|
||||
if (!$user instanceof User) {
|
||||
if (common_config('site', 'fakeaddressrecovery')) {
|
||||
// Return without actually doing anything! We fake address recovery
|
||||
// to avoid revealing which email addresses are registered with the site.
|
||||
return;
|
||||
}
|
||||
// TRANS: Information on password recovery form if no known e-mail address was specified.
|
||||
throw new ClientException(_('No user with that email address exists here.'));
|
||||
}
|
||||
} else {
|
||||
$confirm_email = null;
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
// TRANS: Information on password recovery form if no known username or e-mail address was specified.
|
||||
throw new ClientException(_('No user with that email address or username.'));
|
||||
return;
|
||||
// This might throw a NicknameException on bad nicknames
|
||||
$user = User::getKV('nickname', common_canonical_nickname($nore));
|
||||
if (!$user instanceof User) {
|
||||
// TRANS: Information on password recovery form if no known username was specified.
|
||||
throw new ClientException(_('No user with that nickname exists here.'));
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get an unconfirmed email address if they used a user name
|
||||
|
||||
if (!$user->email && !$confirm_email) {
|
||||
if (empty($user->email) && $confirm_email === null) {
|
||||
$confirm_email = new Confirm_address();
|
||||
$confirm_email->user_id = $user->id;
|
||||
$confirm_email->address_type = 'email';
|
||||
$confirm_email->find();
|
||||
if (!$confirm_email->fetch()) {
|
||||
// Nothing found, so let's reset it to null
|
||||
$confirm_email = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$user->email && !$confirm_email) {
|
||||
if (empty($user->email) && !$confirm_email instanceof Confirm_address) {
|
||||
// TRANS: Client error displayed on password recovery form if a user does not have a registered e-mail address.
|
||||
throw new ClientException(_('No registered email address for that user.'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Success! We have a valid user and a confirmed or unconfirmed email address
|
||||
|
@ -912,13 +914,12 @@ class User extends Managed_DataObject
|
|||
$confirm->code = common_confirmation_code(128);
|
||||
$confirm->address_type = 'recover';
|
||||
$confirm->user_id = $user->id;
|
||||
$confirm->address = (!empty($user->email)) ? $user->email : $confirm_email->address;
|
||||
$confirm->address = $user->email ?: $confirm_email->address;
|
||||
|
||||
if (!$confirm->insert()) {
|
||||
common_log_db_error($confirm, 'INSERT', __FILE__);
|
||||
// TRANS: Server error displayed if e-mail address confirmation fails in the database on the password recovery form.
|
||||
throw new ServerException(_('Error saving address confirmation.'));
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo FIXME: needs i18n.
|
||||
|
|
13
js/extlib/jquery.js
vendored
13
js/extlib/jquery.js
vendored
|
@ -1,5 +1,5 @@
|
|||
/*!
|
||||
* jQuery JavaScript Library v2.1.3
|
||||
* jQuery JavaScript Library v2.1.4
|
||||
* http://jquery.com/
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
|
@ -9,7 +9,7 @@
|
|||
* Released under the MIT license
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Date: 2014-12-18T15:11Z
|
||||
* Date: 2015-04-28T16:01Z
|
||||
*/
|
||||
|
||||
(function( global, factory ) {
|
||||
|
@ -67,7 +67,7 @@ var
|
|||
// Use the correct document accordingly with window argument (sandbox)
|
||||
document = window.document,
|
||||
|
||||
version = "2.1.3",
|
||||
version = "2.1.4",
|
||||
|
||||
// Define a local copy of jQuery
|
||||
jQuery = function( selector, context ) {
|
||||
|
@ -531,7 +531,12 @@ jQuery.each("Boolean Number String Function Array Date RegExp Object Error".spli
|
|||
});
|
||||
|
||||
function isArraylike( obj ) {
|
||||
var length = obj.length,
|
||||
|
||||
// Support: iOS 8.2 (not reproducible in simulator)
|
||||
// `in` check used to prevent JIT error (gh-2145)
|
||||
// hasOwn isn't used here due to false negatives
|
||||
// regarding Nodelist length in IE
|
||||
var length = "length" in obj && obj.length,
|
||||
type = jQuery.type( obj );
|
||||
|
||||
if ( type === "function" || jQuery.isWindow( obj ) ) {
|
||||
|
|
|
@ -328,7 +328,7 @@ class ApiAction extends Action
|
|||
// different story for parenting.
|
||||
$parent = $notice->getParent();
|
||||
$in_reply_to = $parent->id;
|
||||
} catch (Exception $e) {
|
||||
} catch (NoParentNoticeException $e) {
|
||||
$in_reply_to = null;
|
||||
}
|
||||
$twitter_status['in_reply_to_status_id'] = $in_reply_to;
|
||||
|
|
|
@ -36,6 +36,7 @@ class CommandInterpreter
|
|||
// StatusNet
|
||||
|
||||
$cmd = strtolower($cmd);
|
||||
$result = false;
|
||||
|
||||
if (Event::handle('StartInterpretCommand', array($cmd, $arg, $user, &$result))) {
|
||||
switch($cmd) {
|
||||
|
@ -297,8 +298,6 @@ class CommandInterpreter
|
|||
$result = new TrackingCommand($user);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$result = false;
|
||||
}
|
||||
|
||||
Event::handle('EndInterpretCommand', array($cmd, $arg, $user, &$result));
|
||||
|
|
|
@ -48,6 +48,7 @@ $default =
|
|||
'languages' => get_all_languages(),
|
||||
'email' =>
|
||||
array_key_exists('SERVER_ADMIN', $_SERVER) ? $_SERVER['SERVER_ADMIN'] : null,
|
||||
'fakeaddressrecovery' => true,
|
||||
'broughtby' => null,
|
||||
'timezone' => 'UTC',
|
||||
'broughtbyurl' => null,
|
||||
|
|
|
@ -23,7 +23,7 @@ define('GNUSOCIAL_ENGINE', 'GNU social');
|
|||
define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/');
|
||||
|
||||
define('GNUSOCIAL_BASE_VERSION', '1.2.0');
|
||||
define('GNUSOCIAL_LIFECYCLE', 'dev'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
|
||||
define('GNUSOCIAL_LIFECYCLE', 'alpha1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
|
||||
|
||||
define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
|
||||
|
||||
|
@ -38,6 +38,9 @@ define('PROFILES_PER_PAGE', 20);
|
|||
define('MESSAGES_PER_PAGE', 20);
|
||||
define('GROUPS_PER_PAGE', 20);
|
||||
|
||||
define('GROUPS_PER_MINILIST', 8);
|
||||
define('PROFILES_PER_MINILIST', 8);
|
||||
|
||||
define('FOREIGN_NOTICE_SEND', 1);
|
||||
define('FOREIGN_NOTICE_RECV', 2);
|
||||
define('FOREIGN_NOTICE_SEND_REPLY', 4);
|
||||
|
|
|
@ -33,8 +33,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||
|
||||
require_once INSTALLDIR.'/lib/grouplist.php';
|
||||
|
||||
define('GROUPS_PER_MINILIST', 8);
|
||||
|
||||
/**
|
||||
* Widget to show a list of groups, good for sidebar
|
||||
*
|
||||
|
|
|
@ -103,7 +103,7 @@ class GNUsocial_HTTPResponse extends HTTP_Request2_Response
|
|||
*
|
||||
* This extends the PEAR HTTP_Request2 package:
|
||||
* - sends StatusNet-specific User-Agent header
|
||||
* - 'follow_redirects' config option, defaulting off
|
||||
* - 'follow_redirects' config option, defaulting on
|
||||
* - 'max_redirs' config option, defaulting to 10
|
||||
* - extended response class adds getRedirectCount() and getUrl() methods
|
||||
* - get() and post() convenience functions return body content directly
|
||||
|
@ -205,12 +205,28 @@ class HTTPClient extends HTTP_Request2
|
|||
/**
|
||||
* Convenience function to run a HEAD request.
|
||||
*
|
||||
* NOTE: Will probably turn into a GET request if you let it follow redirects!
|
||||
* That option is only there to be flexible and may be removed in the future!
|
||||
*
|
||||
* @return GNUsocial_HTTPResponse
|
||||
* @throws HTTP_Request2_Exception
|
||||
*/
|
||||
public function head($url, $headers=array())
|
||||
public function head($url, $headers=array(), $follow_redirects=false)
|
||||
{
|
||||
return $this->doRequest($url, self::METHOD_HEAD, $headers);
|
||||
// Save the configured value for follow_redirects
|
||||
$old_follow = $this->config['follow_redirects'];
|
||||
try {
|
||||
// Temporarily (possibly) override the follow_redirects setting
|
||||
$this->config['follow_redirects'] = $follow_redirects;
|
||||
return $this->doRequest($url, self::METHOD_HEAD, $headers);
|
||||
} catch (Exception $e) {
|
||||
// Let the exception go on its merry way.
|
||||
throw $e;
|
||||
} finally {
|
||||
// reset to the old value
|
||||
$this->config['follow_redirects'] = $old_follow;
|
||||
}
|
||||
//we've either returned or thrown exception here
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -380,7 +380,7 @@ abstract class ImPlugin extends Plugin
|
|||
$parent = $notice->getParent();
|
||||
$orig_profile = $parent->getProfile();
|
||||
$nicknames = sprintf('%1$s => %2$s', $profile->nickname, $orig_profile->nickname);
|
||||
} catch (Exception $e) {
|
||||
} catch (NoParentNoticeException $e) {
|
||||
$nicknames = $profile->nickname;
|
||||
}
|
||||
|
||||
|
@ -402,9 +402,8 @@ abstract class ImPlugin extends Plugin
|
|||
$chan = new IMChannel($this);
|
||||
$cmd->execute($chan);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -61,7 +61,7 @@ class MediaFile
|
|||
|
||||
public function attachToNotice(Notice $notice)
|
||||
{
|
||||
File_to_post::processNew($this->fileRecord->id, $notice->id);
|
||||
File_to_post::processNew($this->fileRecord, $notice);
|
||||
}
|
||||
|
||||
public function getPath()
|
||||
|
|
41
lib/noparentnoticeexception.php
Normal file
41
lib/noparentnoticeexception.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet, the distributed open-source microblogging tool
|
||||
*
|
||||
* Class for an exception when a database lookup returns no results
|
||||
*
|
||||
* 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 Exception
|
||||
* @package GNUsocial
|
||||
* @author Mikael Nordfeldth <mmn@hethane.se>
|
||||
* @copyright 2013 Free Software Foundation, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
class NoParentNoticeException extends ServerException
|
||||
{
|
||||
public $notice; // The notice which has no parent
|
||||
|
||||
public function __construct(Notice $notice)
|
||||
{
|
||||
$this->notice = $notice;
|
||||
parent::__construct(sprintf(_('No parent for notice with ID "%s".'), $this->notice->id));
|
||||
}
|
||||
}
|
|
@ -29,8 +29,6 @@
|
|||
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
define('PROFILES_PER_MINILIST', 8);
|
||||
|
||||
/**
|
||||
* Widget to show a list of profiles, good for sidebar
|
||||
*
|
||||
|
|
|
@ -64,6 +64,13 @@ class PublicGroupNav extends Menu
|
|||
// TRANS: Menu item title in search group navigation panel.
|
||||
_('Public timeline'), $this->actionName == 'public', 'nav_timeline_public');
|
||||
}
|
||||
if (!common_config('public', 'localonly') || $this->action->getScoped() instanceof Profile) {
|
||||
// Allow network wide view if you're logged in
|
||||
// TRANS: Menu item in search group navigation panel.
|
||||
$this->out->menuItem(common_local_url('networkpublic'), _m('MENU','Network'),
|
||||
// TRANS: Menu item title in search group navigation panel.
|
||||
_('Network public timeline'), $this->actionName == 'networkpublic', 'nav_timeline_networkpublic');
|
||||
}
|
||||
|
||||
// TRANS: Menu item in search group navigation panel.
|
||||
$this->out->menuItem(common_local_url('groups'), _m('MENU','Groups'),
|
||||
|
|
|
@ -535,6 +535,7 @@ class Schema
|
|||
$res = $this->conn->query($sql);
|
||||
|
||||
if ($_PEAR->isError($res)) {
|
||||
common_debug('PEAR exception on query: '.$sql);
|
||||
PEAR_ErrorToPEAR_Exception($res);
|
||||
}
|
||||
}
|
||||
|
|
182
lib/sitestreamaction.php
Normal file
182
lib/sitestreamaction.php
Normal file
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
// Farther than any human will go
|
||||
|
||||
define('MAX_PUBLIC_PAGE', 100);
|
||||
|
||||
class SitestreamAction extends ManagedAction
|
||||
{
|
||||
/**
|
||||
* page of the stream we're on; default = 1
|
||||
*/
|
||||
|
||||
var $page = null;
|
||||
var $notice;
|
||||
|
||||
protected $stream = null;
|
||||
|
||||
function isReadOnly($args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function doPreparation()
|
||||
{
|
||||
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
|
||||
|
||||
if ($this->page > MAX_PUBLIC_PAGE) {
|
||||
// TRANS: Client error displayed when requesting a public timeline page beyond the page limit.
|
||||
// TRANS: %s is the page limit.
|
||||
$this->clientError(sprintf(_('Beyond the page limit (%s).'), MAX_PUBLIC_PAGE));
|
||||
}
|
||||
|
||||
common_set_returnto($this->selfUrl());
|
||||
|
||||
$this->streamPrepare();
|
||||
|
||||
$this->notice = $this->stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
|
||||
NOTICES_PER_PAGE + 1);
|
||||
|
||||
if (!$this->notice) {
|
||||
// TRANS: Server error displayed when a public timeline cannot be retrieved.
|
||||
$this->serverError(_('Could not retrieve public timeline.'));
|
||||
}
|
||||
|
||||
if ($this->page > 1 && $this->notice->N == 0){
|
||||
// TRANS: Client error when page not found (404).
|
||||
$this->clientError(_('No such page.'), 404);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Title of the page
|
||||
*
|
||||
* @return page title, including page number if over 1
|
||||
*/
|
||||
function title()
|
||||
{
|
||||
if ($this->page > 1) {
|
||||
// TRANS: Title for all public timeline pages but the first.
|
||||
// TRANS: %d is the page number.
|
||||
return sprintf(_('Public timeline, page %d'), $this->page);
|
||||
} else {
|
||||
// TRANS: Title for the first public timeline page.
|
||||
return _('Public timeline');
|
||||
}
|
||||
}
|
||||
|
||||
function extraHead()
|
||||
{
|
||||
parent::extraHead();
|
||||
$rsd = common_local_url('rsd');
|
||||
|
||||
// RSD, http://tales.phrasewise.com/rfc/rsd
|
||||
|
||||
$this->element('link', array('rel' => 'EditURI',
|
||||
'type' => 'application/rsd+xml',
|
||||
'href' => $rsd));
|
||||
|
||||
if ($this->page != 1) {
|
||||
$this->element('link', array('rel' => 'canonical',
|
||||
'href' => common_local_url('public')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output <head> elements for RSS and Atom feeds
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function getFeeds()
|
||||
{
|
||||
return array(new Feed(Feed::JSON,
|
||||
common_local_url('ApiTimelinePublic',
|
||||
array('format' => 'as')),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (Activity Streams JSON)')),
|
||||
new Feed(Feed::RSS1, common_local_url('publicrss'),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (RSS 1.0)')),
|
||||
new Feed(Feed::RSS2,
|
||||
common_local_url('ApiTimelinePublic',
|
||||
array('format' => 'rss')),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (RSS 2.0)')),
|
||||
new Feed(Feed::ATOM,
|
||||
common_local_url('ApiTimelinePublic',
|
||||
array('format' => 'atom')),
|
||||
// TRANS: Link description for public timeline feed.
|
||||
_('Public Timeline Feed (Atom)')));
|
||||
}
|
||||
|
||||
function showEmptyList()
|
||||
{
|
||||
// TRANS: Text displayed for public feed when there are no public notices.
|
||||
$message = _('This is the public timeline for %%site.name%% but no one has posted anything yet.') . ' ';
|
||||
|
||||
if (common_logged_in()) {
|
||||
// TRANS: Additional text displayed for public feed when there are no public notices for a logged in user.
|
||||
$message .= _('Be the first to post!');
|
||||
}
|
||||
else {
|
||||
if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
|
||||
// TRANS: Additional text displayed for public feed when there are no public notices for a not logged in user.
|
||||
$message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
|
||||
}
|
||||
}
|
||||
|
||||
$this->elementStart('div', 'guide');
|
||||
$this->raw(common_markup_to_html($message));
|
||||
$this->elementEnd('div');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the content area
|
||||
*
|
||||
* Shows a list of the notices in the public stream, with some pagination
|
||||
* controls.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showContent()
|
||||
{
|
||||
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
|
||||
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
|
||||
} else {
|
||||
$nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
|
||||
}
|
||||
|
||||
$cnt = $nl->show();
|
||||
|
||||
if ($cnt == 0) {
|
||||
$this->showEmptyList();
|
||||
}
|
||||
|
||||
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
|
||||
$this->page, $this->action);
|
||||
}
|
||||
|
||||
function showAnonymousMessage()
|
||||
{
|
||||
if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
|
||||
// TRANS: Message for not logged in users at an invite-only site trying to view the public feed of notices.
|
||||
// TRANS: This message contains Markdown links. Please mind the formatting.
|
||||
$m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
|
||||
'based on the Free Software [StatusNet](http://status.net/) tool. ' .
|
||||
'[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ' .
|
||||
'([Read more](%%doc.help%%))');
|
||||
} else {
|
||||
// TRANS: Message for not logged in users at a closed site trying to view the public feed of notices.
|
||||
// TRANS: This message contains Markdown links. Please mind the formatting.
|
||||
$m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
|
||||
'based on the Free Software [StatusNet](http://status.net/) tool.');
|
||||
}
|
||||
$this->elementStart('div', array('id' => 'anon_notice'));
|
||||
$this->raw(common_markup_to_html($m));
|
||||
$this->elementEnd('div');
|
||||
}
|
||||
}
|
17
lib/util.php
17
lib/util.php
|
@ -628,7 +628,7 @@ function common_render_content($text, Notice $notice)
|
|||
* @param Notice $notice in-progress or complete Notice object for context
|
||||
* @return string partially-rendered HTML
|
||||
*/
|
||||
function common_linkify_mentions($text, $notice)
|
||||
function common_linkify_mentions($text, Notice $notice)
|
||||
{
|
||||
$mentions = common_find_mentions($text, $notice);
|
||||
|
||||
|
@ -655,7 +655,7 @@ function common_linkify_mentions($text, $notice)
|
|||
return $text;
|
||||
}
|
||||
|
||||
function common_linkify_mention($mention)
|
||||
function common_linkify_mention(array $mention)
|
||||
{
|
||||
$output = null;
|
||||
|
||||
|
@ -695,13 +695,10 @@ function common_linkify_mention($mention)
|
|||
*
|
||||
* @access private
|
||||
*/
|
||||
function common_find_mentions($text, $notice)
|
||||
function common_find_mentions($text, Notice $notice)
|
||||
{
|
||||
try {
|
||||
$sender = Profile::getKV('id', $notice->profile_id);
|
||||
} catch (NoProfileException $e) {
|
||||
return array();
|
||||
}
|
||||
// The getProfile call throws NoProfileException on failure
|
||||
$sender = $notice->getProfile();
|
||||
|
||||
$mentions = array();
|
||||
|
||||
|
@ -728,8 +725,8 @@ function common_find_mentions($text, $notice)
|
|||
}
|
||||
} catch (NoProfileException $e) {
|
||||
common_log(LOG_WARNING, sprintf('Notice %d author profile id %d does not exist', $origNotice->id, $origNotice->profile_id));
|
||||
} catch (ServerException $e) {
|
||||
// Probably just no parent. Should get a specific NoParentException
|
||||
} 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());
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class ActivityverbAction extends ManagedAction
|
|||
throw new ServerException('A verb has not been specified.');
|
||||
}
|
||||
|
||||
$this->notice = Notice::getById($this->trimmed('id'));
|
||||
$this->notice = Notice::getByID($this->trimmed('id'));
|
||||
|
||||
if (!$this->notice->inScope($this->scoped)) {
|
||||
// TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
|
||||
|
|
|
@ -9,6 +9,13 @@ class AntiBrutePlugin extends Plugin {
|
|||
|
||||
const FAILED_LOGIN_IP_SECTION = 'failed_login_ip';
|
||||
|
||||
public function initialize()
|
||||
{
|
||||
// This probably needs some work. For example with IPv6 you can easily generate new IPs...
|
||||
$client_ip = common_client_ip();
|
||||
$this->client_ip = $client_ip[0] ?: $client_ip[1]; // [0] is proxy, [1] should be the real IP
|
||||
}
|
||||
|
||||
public function onStartCheckPassword($nickname, $password, &$authenticatedUser)
|
||||
{
|
||||
if (common_is_email($nickname)) {
|
||||
|
@ -22,9 +29,6 @@ class AntiBrutePlugin extends Plugin {
|
|||
return true;
|
||||
}
|
||||
|
||||
// This probably needs some work. For example with IPv6 you can easily generate new IPs...
|
||||
$client_ip = common_client_ip();
|
||||
$this->client_ip = $client_ip[0] ?: $client_ip[1]; // [0] is proxy, [1] should be the real IP
|
||||
$this->failed_attempts = (int)$this->unauthed_user->getPref(self::FAILED_LOGIN_IP_SECTION, $this->client_ip);
|
||||
switch (true) {
|
||||
case $this->failed_attempts >= 5:
|
||||
|
|
|
@ -691,8 +691,8 @@ class Ostatus_profile extends Managed_DataObject
|
|||
$options);
|
||||
if ($saved instanceof Notice) {
|
||||
Ostatus_source::saveNew($saved, $this, $method);
|
||||
if (!empty($attachment)) {
|
||||
File_to_post::processNew($attachment->id, $saved->id);
|
||||
if ($attachment instanceof File) {
|
||||
File_to_post::processNew($attachment, $saved);
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
|
|
|
@ -154,7 +154,7 @@ class OpenIDPlugin extends Plugin
|
|||
*
|
||||
* @return boolean hook return
|
||||
*/
|
||||
function onEndPublicXRDS($action, &$xrdsOutputter)
|
||||
function onEndPublicXRDS(Action $action, &$xrdsOutputter)
|
||||
{
|
||||
$xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
|
||||
'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
|
||||
|
@ -174,37 +174,6 @@ class OpenIDPlugin extends Plugin
|
|||
$xrdsOutputter->elementEnd('XRD');
|
||||
}
|
||||
|
||||
/**
|
||||
* User XRDS output hook
|
||||
*
|
||||
* Puts the bits of code needed to discover OpenID endpoints.
|
||||
*
|
||||
* @param Action $action Action being executed
|
||||
* @param XMLOutputter &$xrdsOutputter Output channel
|
||||
*
|
||||
* @return boolean hook return
|
||||
*/
|
||||
function onEndUserXRDS($action, &$xrdsOutputter)
|
||||
{
|
||||
$xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
|
||||
'xml:id' => 'openid',
|
||||
'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
|
||||
'version' => '2.0'));
|
||||
$xrdsOutputter->element('Type', null, 'xri://$xrds*simple');
|
||||
|
||||
//consumer
|
||||
$xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/return_to',
|
||||
common_local_url('finishopenidlogin'));
|
||||
|
||||
//provider
|
||||
$xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/signon',
|
||||
common_local_url('openidserver'),
|
||||
null,
|
||||
null,
|
||||
common_profile_url($action->user->nickname));
|
||||
$xrdsOutputter->elementEnd('XRD');
|
||||
}
|
||||
|
||||
/**
|
||||
* If we're in OpenID-only mode, hide all the main menu except OpenID login.
|
||||
*
|
||||
|
@ -415,7 +384,7 @@ class OpenIDPlugin extends Plugin
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
function onEndShowHeadElements($action)
|
||||
function onEndShowHeadElements(Action $action)
|
||||
{
|
||||
if ($action instanceof ShowstreamAction) {
|
||||
$action->element('link', array('rel' => 'openid2.provider',
|
||||
|
@ -427,6 +396,11 @@ class OpenIDPlugin extends Plugin
|
|||
$action->element('link', array('rel' => 'openid.delegate',
|
||||
'href' => $action->profile->profileurl));
|
||||
}
|
||||
|
||||
if ($action instanceof SitestreamAction) {
|
||||
$action->element('meta', array('http-equiv' => 'X-XRDS-Location',
|
||||
'content' => common_local_url('publicxrds')));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,12 +30,9 @@
|
|||
* 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.'/plugins/OpenID/openid.php';
|
||||
require_once INSTALLDIR.'/lib/xrdsoutputter.php';
|
||||
require_once __DIR__.'/../openid.php';
|
||||
|
||||
/**
|
||||
* Public XRDS
|
||||
|
@ -48,8 +45,6 @@ require_once INSTALLDIR.'/lib/xrdsoutputter.php';
|
|||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*
|
||||
* @todo factor out similarities with XrdsAction
|
||||
*/
|
||||
class PublicxrdsAction extends Action
|
||||
{
|
||||
|
@ -70,9 +65,9 @@ class PublicxrdsAction extends Action
|
|||
*
|
||||
* @return nothing
|
||||
*/
|
||||
function handle($args)
|
||||
protected function handle()
|
||||
{
|
||||
parent::handle($args);
|
||||
parent::handle();
|
||||
$xrdsOutputter = new XRDSOutputter();
|
||||
$xrdsOutputter->startXRDS();
|
||||
Event::handle('StartPublicXRDS', array($this,&$xrdsOutputter));
|
||||
|
@ -80,4 +75,3 @@ class PublicxrdsAction extends Action
|
|||
$xrdsOutputter->endXRDS();
|
||||
}
|
||||
}
|
||||
|
|
@ -28,11 +28,7 @@
|
|||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/xmloutputter.php';
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Low-level generator for XRDS XML
|
|
@ -161,7 +161,7 @@ class SharePlugin extends ActivityVerbHandlerPlugin
|
|||
public function extendActivity(Notice $stored, Activity $act, Profile $scoped=null)
|
||||
{
|
||||
// TODO: How to handle repeats of deleted notices?
|
||||
$target = Notice::getById($stored->repeat_of);
|
||||
$target = Notice::getByID($stored->repeat_of);
|
||||
// TRANS: A repeat activity's title. %1$s is repeater's nickname
|
||||
// and %2$s is the repeated user's nickname.
|
||||
$act->title = sprintf(_('%1$s repeated a notice by %2$s'),
|
||||
|
|
|
@ -564,13 +564,13 @@ class TwitterImport
|
|||
* @param Notice $notice
|
||||
* @param object $status
|
||||
*/
|
||||
function saveStatusAttachments($notice, $status)
|
||||
function saveStatusAttachments(Notice $notice, $status)
|
||||
{
|
||||
if (common_config('attachments', 'process_links')) {
|
||||
if (!empty($status->entities) && !empty($status->entities->urls)) {
|
||||
foreach ($status->entities->urls as $url) {
|
||||
try {
|
||||
File::processNew($url->url, $notice->id);
|
||||
File::processNew($url->url, $notice);
|
||||
} catch (ServerException $e) {
|
||||
// Could not process attached URL
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@ if (!defined('GNUSOCIAL')) { exit(1); }
|
|||
|
||||
class WebFingerPlugin extends Plugin
|
||||
{
|
||||
const OAUTH_ACCESS_TOKEN_REL = 'http://apinamespace.org/oauth/access_token';
|
||||
const OAUTH_REQUEST_TOKEN_REL = 'http://apinamespace.org/oauth/request_token';
|
||||
const OAUTH_AUTHORIZE_REL = 'http://apinamespace.org/oauth/authorize';
|
||||
|
||||
public $http_alias = false;
|
||||
|
||||
public function initialize()
|
||||
|
@ -127,6 +131,11 @@ class WebFingerPlugin extends Plugin
|
|||
$type,
|
||||
true); // isTemplate
|
||||
}
|
||||
|
||||
// OAuth connections
|
||||
$links[] = new XML_XRD_Element_link(self::OAUTH_ACCESS_TOKEN_REL, common_local_url('ApiOAuthAccessToken'));
|
||||
$links[] = new XML_XRD_Element_link(self::OAUTH_REQUEST_TOKEN_REL, common_local_url('ApiOAuthRequestToken'));
|
||||
$links[] = new XML_XRD_Element_link(self::OAUTH_AUTHORIZE_REL, common_local_url('ApiOAuthAuthorize'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -354,7 +354,7 @@ class XmppPlugin extends ImPlugin
|
|||
$xs->text(": ");
|
||||
} catch (InvalidUrlException $e) {
|
||||
$xs->text(sprintf(' => %s', $orig_profile->nickname));
|
||||
} catch (Exception $e) {
|
||||
} catch (NoParentNoticeException $e) {
|
||||
$xs->text(": ");
|
||||
}
|
||||
if (!empty($notice->rendered)) {
|
||||
|
|
|
@ -113,7 +113,7 @@ function readline_emulation($prompt)
|
|||
|
||||
function console_help()
|
||||
{
|
||||
print "Welcome to StatusNet's interactive PHP console!\n";
|
||||
print "Welcome to GNU social's interactive PHP console!\n";
|
||||
print "Type some PHP code and it'll execute...\n";
|
||||
print "\n";
|
||||
print "Hint: return a value of any type to output it via var_export():\n";
|
||||
|
@ -128,8 +128,8 @@ function console_help()
|
|||
}
|
||||
|
||||
if (CONSOLE_INTERACTIVE) {
|
||||
print "StatusNet interactive PHP console... type ctrl+D or enter 'exit' to exit.\n";
|
||||
$prompt = common_config('site', 'name') . '> ';
|
||||
print "GNU social interactive PHP console... type ctrl+D or enter 'exit' to exit.\n";
|
||||
$prompt = common_slugify(common_config('site', 'name')) . '> ';
|
||||
} else {
|
||||
$prompt = '';
|
||||
}
|
||||
|
|
78
scripts/nukefile.php
Executable file
78
scripts/nukefile.php
Executable file
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
* StatusNet - a distributed open-source microblogging tool
|
||||
* Copyright (C) 2008, 2009, StatusNet, Inc.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
|
||||
|
||||
$shortoptions = 'i::yv';
|
||||
$longoptions = array('id=', 'yes', 'verbose');
|
||||
|
||||
$helptext = <<<END_OF_HELP
|
||||
nukefile.php [options]
|
||||
deletes a file and related notices from the database
|
||||
|
||||
-i --id ID of the file
|
||||
-v --verbose Be verbose (print the contents of the notices deleted).
|
||||
|
||||
END_OF_HELP;
|
||||
|
||||
require_once INSTALLDIR.'/scripts/commandline.inc';
|
||||
|
||||
if (have_option('i', 'id')) {
|
||||
$id = get_option_value('i', 'id');
|
||||
$file = File::getKV('id', $id);
|
||||
if (!$file instanceof File) {
|
||||
print "Can't find file with ID $id\n";
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
print "You must provide a file ID.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$verbose = have_option('v', 'verbose');
|
||||
|
||||
if (!have_option('y', 'yes')) {
|
||||
try {
|
||||
$filename = $file->getFilename();
|
||||
} catch (Exception $e) {
|
||||
$filename = '(remote file or no filename)';
|
||||
}
|
||||
print "About to PERMANENTLY delete file ($filename) ({$file->id}). Are you sure? [y/N] ";
|
||||
$response = fgets(STDIN);
|
||||
if (strtolower(trim($response)) != 'y') {
|
||||
print "Aborting.\n";
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
print "Finding notices...\n";
|
||||
try {
|
||||
$ids = File_to_post::getNoticeIDsByFile($file);
|
||||
$notice = Notice::multiGet('id', $ids);
|
||||
while ($notice->fetch()) {
|
||||
print "Deleting notice {$notice->id}".($verbose ? ": $notice->content\n" : "\n");
|
||||
$notice->delete();
|
||||
}
|
||||
} catch (NoResultException $e) {
|
||||
print "No notices found with this File attached.\n";
|
||||
}
|
||||
print "Deleting File object together with possibly locally stored copy.\n";
|
||||
$file->delete();
|
||||
print "DONE.\n";
|
Loading…
Reference in New Issue
Block a user