Merge branch '1.0.x' of gitorious.org:statusnet/mainline into 1.0.x

This commit is contained in:
Samantha Doherty 2011-08-19 13:02:32 -04:00
commit 1b7d1d9a4a
18 changed files with 400 additions and 411 deletions

View File

@ -4,7 +4,7 @@
* Copyright (C) 2011, StatusNet, Inc.
*
* Show a stream of notices in a particular conversation
*
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
@ -46,12 +46,11 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class ApiconversationAction extends ApiAuthAction
{
protected $conversation = null;
protected $notices = null;
protected $conversation = null;
protected $notices = null;
/**
* For initializing members of the class.
*
@ -59,35 +58,36 @@ class ApiconversationAction extends ApiAuthAction
*
* @return boolean true
*/
function prepare($argarray)
{
parent::prepare($argarray);
$convId = $this->trimmed('id');
if (empty($convId)) {
throw new ClientException(_m('no conversation id'));
// TRANS: Client exception thrown when no conversation ID is given.
throw new ClientException(_('No conversation ID.'));
}
$this->conversation = Conversation::staticGet('id', $convId);
if (empty($this->conversation)) {
throw new ClientException(sprintf(_m('No conversation with id %d'), $convId),
404);
// TRANS: Client exception thrown when referring to a non-existing conversation ID (%d).
throw new ClientException(sprintf(_('No conversation with ID %d.'), $convId),
404);
}
$profile = Profile::current();
$stream = new ConversationNoticeStream($convId, $profile);
$notice = $stream->getNotices(($this->page-1) * $this->count,
$this->count,
$this->since_id,
$this->max_id);
$this->notices = $notice->fetchAll();
return true;
}
@ -98,17 +98,16 @@ class ApiconversationAction extends ApiAuthAction
*
* @return void
*/
function handle($argarray=null)
{
$sitename = common_config('site', 'name');
// TRANS: Timeline title for user and friends. %s is a user nickname.
$title = _("Conversation");
$title = _m('TITLE', 'Conversation');
$id = common_local_url('apiconversation', array('id' => $this->conversation->id, 'format' => $this->format));
$link = common_local_url('conversation', array('id' => $this->conversation->id));
$self = $id;
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
@ -168,7 +167,6 @@ class ApiconversationAction extends ApiAuthAction
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
@ -202,7 +200,6 @@ class ApiconversationAction extends ApiAuthAction
*
* @return string etag http header
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
@ -220,7 +217,7 @@ class ApiconversationAction extends ApiAuthAction
)
. '"';
}
return null;
}
@ -229,7 +226,6 @@ class ApiconversationAction extends ApiAuthAction
*
* @return boolean true if delete, else false
*/
function requiresAuth()
{
if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
@ -239,4 +235,4 @@ class ApiconversationAction extends ApiAuthAction
return true;
}
}
}
}

View File

@ -63,156 +63,158 @@ class Memcached_DataObject extends Safe_DataObject
}
return $i;
}
/**
* Get multiple items from the database by key
*
*
* @param string $cls Class to fetch
* @param string $keyCol name of column for key
* @param array $keyVals key values to fetch
* @param boolean $skipNulls return only non-null results?
*
*
* @return array Array of objects, in order
*/
function multiGet($cls, $keyCol, $keyVals, $skipNulls=true)
{
$result = self::pivotGet($cls, $keyCol, $keyVals);
$values = array_values($result);
if ($skipNulls) {
$tmp = array();
foreach ($values as $value) {
if (!empty($value)) {
$tmp[] = $value;
}
}
$values = $tmp;
}
return new ArrayWrapper($values);
$result = self::pivotGet($cls, $keyCol, $keyVals);
$values = array_values($result);
if ($skipNulls) {
$tmp = array();
foreach ($values as $value) {
if (!empty($value)) {
$tmp[] = $value;
}
}
$values = $tmp;
}
return new ArrayWrapper($values);
}
/**
* Get multiple items from the database by key
*
*
* @param string $cls Class to fetch
* @param string $keyCol name of column for key
* @param array $keyVals key values to fetch
* @param boolean $otherCols Other columns to hold fixed
*
*
* @return array Array mapping $keyVals to objects, or null if not found
*/
static function pivotGet($cls, $keyCol, $keyVals, $otherCols = array())
{
$result = array_fill_keys($keyVals, null);
$toFetch = array();
foreach ($keyVals as $keyVal) {
$kv = array_merge($otherCols, array($keyCol => $keyVal));
$i = self::multicache($cls, $kv);
if ($i !== false) {
$result[$keyVal] = $i;
} else if (!empty($keyVal)) {
$toFetch[] = $keyVal;
}
}
if (count($toFetch) > 0) {
$result = array_fill_keys($keyVals, null);
$toFetch = array();
foreach ($keyVals as $keyVal) {
$kv = array_merge($otherCols, array($keyCol => $keyVal));
$i = self::multicache($cls, $kv);
if ($i !== false) {
$result[$keyVal] = $i;
} else if (!empty($keyVal)) {
$toFetch[] = $keyVal;
}
}
if (count($toFetch) > 0) {
$i = DB_DataObject::factory($cls);
if (empty($i)) {
throw new Exception(_('Cannot instantiate class ' . $cls));
// TRANS: Exception thrown when a class (%s) could not be instantiated.
throw new Exception(sprintf(_('Cannot instantiate class %s.'),$cls));
}
foreach ($otherCols as $otherKeyCol => $otherKeyVal) {
$i->$otherKeyCol = $otherKeyVal;
}
$i->whereAddIn($keyCol, $toFetch, $i->columnType($keyCol));
if ($i->find()) {
while ($i->fetch()) {
$copy = clone($i);
$copy->encache();
$result[$i->$keyCol] = $copy;
}
}
// Save state of DB misses
foreach ($toFetch as $keyVal) {
if (empty($result[$keyVal])) {
$kv = array_merge($otherCols, array($keyCol => $keyVal));
// save the fact that no such row exists
$c = self::memcache();
if (!empty($c)) {
$ck = self::multicacheKey($cls, $kv);
$c->set($ck, null);
}
}
}
}
return $result;
}
function listGet($cls, $keyCol, $keyVals)
{
$result = array_fill_keys($keyVals, array());
$toFetch = array();
foreach ($keyVals as $keyVal) {
$l = self::cacheGet(sprintf("%s:list:%s:%s", $cls, $keyCol, $keyVal));
if ($l !== false) {
$result[$keyVal] = $l;
} else {
$toFetch[] = $keyVal;
}
}
if (count($toFetch) > 0) {
$i = DB_DataObject::factory($cls);
if (empty($i)) {
throw new Exception(_('Cannot instantiate class ' . $cls));
}
$i->whereAddIn($keyCol, $toFetch, $i->columnType($keyCol));
if ($i->find()) {
while ($i->fetch()) {
$copy = clone($i);
$copy->encache();
$result[$i->$keyCol][] = $copy;
}
}
foreach ($toFetch as $keyVal)
{
self::cacheSet(sprintf("%s:list:%s:%s", $cls, $keyCol, $keyVal),
$result[$keyVal]);
}
$i->whereAddIn($keyCol, $toFetch, $i->columnType($keyCol));
if ($i->find()) {
while ($i->fetch()) {
$copy = clone($i);
$copy->encache();
$result[$i->$keyCol] = $copy;
}
}
// Save state of DB misses
foreach ($toFetch as $keyVal) {
if (empty($result[$keyVal])) {
$kv = array_merge($otherCols, array($keyCol => $keyVal));
// save the fact that no such row exists
$c = self::memcache();
if (!empty($c)) {
$ck = self::multicacheKey($cls, $kv);
$c->set($ck, null);
}
}
}
}
return $result;
}
function listGet($cls, $keyCol, $keyVals)
{
$result = array_fill_keys($keyVals, array());
$toFetch = array();
foreach ($keyVals as $keyVal) {
$l = self::cacheGet(sprintf("%s:list:%s:%s", $cls, $keyCol, $keyVal));
if ($l !== false) {
$result[$keyVal] = $l;
} else {
$toFetch[] = $keyVal;
}
}
if (count($toFetch) > 0) {
$i = DB_DataObject::factory($cls);
if (empty($i)) {
// TRANS: Exception thrown when a class (%s) could not be instantiated.
throw new Exception(sprintf(_('Cannot instantiate class %s.'),$cls));
}
$i->whereAddIn($keyCol, $toFetch, $i->columnType($keyCol));
if ($i->find()) {
while ($i->fetch()) {
$copy = clone($i);
$copy->encache();
$result[$i->$keyCol][] = $copy;
}
}
foreach ($toFetch as $keyVal)
{
self::cacheSet(sprintf("%s:list:%s:%s", $cls, $keyCol, $keyVal),
$result[$keyVal]);
}
}
return $result;
}
function columnType($columnName)
{
$keys = $this->table();
if (!array_key_exists($columnName, $keys)) {
throw new Exception('Unknown key column ' . $columnName . ' in ' . join(',', array_keys($keys)));
}
$def = $keys[$columnName];
if ($def & DB_DATAOBJECT_INT) {
return 'integer';
} else {
return 'string';
}
return $result;
}
function columnType($columnName)
{
$keys = $this->table();
if (!array_key_exists($columnName, $keys)) {
throw new Exception('Unknown key column ' . $columnName . ' in ' . join(',', array_keys($keys)));
}
$def = $keys[$columnName];
if ($def & DB_DATAOBJECT_INT) {
return 'integer';
} else {
return 'string';
}
}
/**
* @fixme Should this return false on lookup fail to match staticGet?
* @todo FIXME: Should this return false on lookup fail to match staticGet?
*/
function pkeyGet($cls, $kv)
{
@ -225,13 +227,13 @@ class Memcached_DataObject extends Safe_DataObject
return false;
}
foreach ($kv as $k => $v) {
if (is_null($v)) {
// XXX: possible SQL injection...? Don't
// pass keys from the browser, eh.
$i->whereAdd("$k is null");
} else {
$i->$k = $v;
}
if (is_null($v)) {
// XXX: possible SQL injection...? Don't
// pass keys from the browser, eh.
$i->whereAdd("$k is null");
} else {
$i->$k = $v;
}
}
if ($i->find(true)) {
$i->encache();
@ -562,7 +564,7 @@ class Memcached_DataObject extends Safe_DataObject
continue;
}
if (in_array($func, $ignoreStatic)) {
continue; // @fixme this shouldn't be needed?
continue; // @todo FIXME: This shouldn't be needed?
}
$here = get_class($frame['object']) . '->' . $func;
break;
@ -705,7 +707,7 @@ class Memcached_DataObject extends Safe_DataObject
if (!$dsn) {
// TRANS: Exception thrown when database name or Data Source Name could not be found.
throw new Exception(_("No database name or DSN found anywhere."));
throw new Exception(_('No database name or DSN found anywhere.'));
}
return $dsn;

View File

@ -84,8 +84,10 @@ class GroupsNav extends MoreMenu
function seeAllItem() {
return array('usergroups',
array('nickname' => $this->user->nickname),
// TRANS: Link description for seeing all groups.
_('See all'),
_('See all groups you belong to'));
// TRANS: Link title for seeing all groups.
_('See all groups you belong to.'));
}
}

View File

@ -58,11 +58,11 @@ class ListsNav extends MoreMenu
{
return 'lists';
}
function getItems()
{
$items = array();
while ($this->lists->fetch()) {
$mode = $this->lists->private ? 'private' : 'public';
$items[] = array('showprofiletag',
@ -84,7 +84,9 @@ class ListsNav extends MoreMenu
{
return array('peopletagsbyuser',
array('nickname' => $this->profile->nickname),
// TRANS: Link description for seeing all lists.
_('See all'),
_('See all lists you have created'));
// TRANS: Link title for seeing all lists.
_('See all lists you have created.'));
}
}

View File

@ -4,7 +4,7 @@
* Copyright (C) 2011, StatusNet, Inc.
*
* A menu with a More... button to show more elements
*
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
@ -44,12 +44,11 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class MoreMenu extends Menu
{
const SOFT_MAX = 5;
const HARD_MAX = 15;
/**
* Show a menu with a limited number of elements
*
@ -57,7 +56,6 @@ class MoreMenu extends Menu
*
* @return
*/
function show()
{
$items = $this->getItems();
@ -72,7 +70,6 @@ class MoreMenu extends Menu
}
if (Event::handle('StartNav', array($this, &$tag, &$items))) {
$this->out->elementStart('ul', $attrs);
$total = count($items);
@ -89,10 +86,10 @@ class MoreMenu extends Menu
}
if ($total > self::SOFT_MAX + 1) {
$this->out->elementStart('li', array('class' => 'more_link'));
$this->out->element('a', array('href' => '#',
'onclick' => 'SN.U.showMoreMenuItems("'.$menuID.'"); return false;'),
// TRANS: Link description to show more items in a list.
_('More ▼'));
$this->out->elementEnd('li');
@ -112,7 +109,7 @@ class MoreMenu extends Menu
}
}
}
$this->out->elementEnd('ul');
Event::handle('EndNav', array($this, $tag, $items));
@ -123,5 +120,4 @@ class MoreMenu extends Menu
{
return null;
}
}

View File

@ -120,13 +120,15 @@ class BlogPlugin extends MicroAppPlugin
'author' => 'Evan Prodromou',
'homepage' => 'http://status.net/wiki/Plugin:Blog',
'rawdescription' =>
// TRANS: Plugin description.
_m('Let users write and share long-form texts.'));
return true;
}
function appTitle()
{
return _m('Blog');
// TRANS: Blog application title.
return _m('TITLE','Blog');
}
function tag()
@ -149,7 +151,7 @@ class BlogPlugin extends MicroAppPlugin
$entryObj = $activity->objects[0];
if ($entryObj->type != Blog_entry::TYPE) {
// TRANS: Exception thrown when blog plugin comes across a non-event type object.
// TRANS: Exception thrown when blog plugin comes across a non-blog entry type object.
throw new ClientException(_m('Wrong type for object.'));
}
@ -175,7 +177,8 @@ class BlogPlugin extends MicroAppPlugin
$entry = Blog_entry::fromNotice($notice);
if (empty($entry)) {
throw new ClientException(sprintf(_('No blog entry for notice %s'),
// TRANS: Exception thrown when requesting a non-existing blog entry for notice.
throw new ClientException(sprintf(_m('No blog entry for notice %s.'),
$notice->id));
}
@ -204,7 +207,7 @@ class BlogPlugin extends MicroAppPlugin
if ($notice->object_type == Blog_entry::TYPE) {
return new BlogEntryListItem($nli);
}
return null;
}

View File

@ -44,7 +44,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class BlogEntryForm extends Form
{
/**
@ -96,13 +95,13 @@ class BlogEntryForm extends Form
_m('Title of the blog entry.'),
'title');
$this->unli();
$this->li();
$this->out->textarea('blog-entry-content',
// TRANS: Field label on event form.
// TRANS: Field label on blog entry form.
_m('LABEL','Text'),
null,
// TRANS: Field title on event form.
// TRANS: Field title on blog entry form.
_m('Text of the blog entry.'),
'content');
$this->unli();
@ -124,8 +123,8 @@ class BlogEntryForm extends Form
*/
function formActions()
{
// TRANS: Button text to save an event..
$this->out->submit('blog-entry-submit',
// TRANS: Button text to save a blog entry.
_m('BUTTON', 'Save'),
'submit',
'submit');

View File

@ -4,7 +4,7 @@
* Copyright (C) 2011, StatusNet, Inc.
*
* Show a blog entry
*
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
@ -44,12 +44,11 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class ShowblogentryAction extends ShownoticeAction
{
protected $id;
protected $entry;
function getNotice()
{
$this->id = $this->trimmed('id');
@ -81,6 +80,7 @@ class ShowblogentryAction extends ShownoticeAction
function title()
{
// XXX: check for double-encoding
// TRANS: Title for a blog entry without a title.
return (empty($this->entry->title)) ? _m('Untitled') : $this->entry->title;
}
}

View File

@ -78,14 +78,14 @@ class NewrsvpAction extends Action
$eventId = $this->trimmed('event');
if (empty($eventId)) {
// TRANS: Client exception thrown when requesting a non-exsting event.
// TRANS: Client exception thrown when referring to a non-existing event.
throw new ClientException(_m('No such event.'));
}
$this->event = Happening::staticGet('id', $eventId);
if (empty($this->event)) {
// TRANS: Client exception thrown when requesting a non-exsting event.
// TRANS: Client exception thrown when referring to a non-existing event.
throw new ClientException(_m('No such event.'));
}

View File

@ -32,7 +32,6 @@ if (!defined('STATUSNET')) {
* Callback handler to populate end time dropdown
*/
class TimelistAction extends Action {
private $start;
private $duration;
@ -63,13 +62,14 @@ class TimelistAction extends Action {
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.'));
$this->clientError(_m('Not logged in.'));
return;
}
if (!empty($this->start)) {
$times = EventTimeList::getTimes($this->start, $this->duration);
} else {
// TRANS: Client error when submitting a form with unexpected information.
$this->clientError(_m('Unexpected form submission.'));
return;
}
@ -78,6 +78,7 @@ class TimelistAction extends Action {
header('Content-Type: application/json; charset=utf-8');
print json_encode($times);
} else {
// TRANS: Client error displayed when using an action in a non-AJAX way.
$this->clientError(_m('This action is AJAX only.'));
}
}

View File

@ -228,16 +228,14 @@ class OMBPlugin extends Plugin
$omb01 = Remote_profile::staticGet('id', $other_id);
if (!empty($omb01)) {
// TRANS: Client error displayed trying to subscribe to an OMB 0.1 remote profile.
throw new ClientException(
_m(
'You cannot subscribe to an OMB 0.1 '
// TRANS: Client error displayed trying to subscribe to an OMB 0.1 remote profile.
_m('You cannot subscribe to an OMB 0.1 '
. 'remote profile with this action.'
)
);
return false;
}
}
/**
@ -257,10 +255,9 @@ class OMBPlugin extends Plugin
$omb01 = Remote_profile::staticGet('id', $tagged_profile->id);
if (!empty($omb01)) {
// TRANS: Client error displayed when trying to add an OMB 0.1 remote profile to a list.
$this->clientError(
_m(
'You cannot list an OMB 0.1 '
// TRANS: Client error displayed when trying to add an OMB 0.1 remote profile to a list.
_m('You cannot list an OMB 0.1 '
.'remote profile with this action.')
);
}
@ -282,10 +279,9 @@ class OMBPlugin extends Plugin
$omb01 = Remote_profile::staticGet('id', $ptag->tagged);
if (!empty($omb01)) {
// TRANS: Client error displayed when trying to (un)list an OMB 0.1 remote profile.
$this->clientError(
_m(
'You cannot (un)list an OMB 0.1 '
// TRANS: Client error displayed when trying to (un)list an OMB 0.1 remote profile.
_m('You cannot (un)list an OMB 0.1 '
. 'remote profile with this action.')
);
return false;
@ -317,8 +313,8 @@ class OMBPlugin extends Plugin
if (!$result) {
common_log_db_error($token, 'DELETE', __FILE__);
// TRANS: Exception thrown when the OMB token for a subscription could not deleted on the server.
throw new Exception(
// TRANS: Exception thrown when the OMB token for a subscription could not deleted on the server.
_m('Could not delete subscription OMB token.')
);
}
@ -394,12 +390,10 @@ class OMBPlugin extends Plugin
'version' => STATUSNET_VERSION,
'author' => 'Zach Copley',
'homepage' => 'http://status.net/wiki/Plugin:Sample',
'rawdescription' =>
// TRANS: Plugin description.
_m('A sample plugin to show basics of development for new hackers.')
'rawdescription' => _m('A sample plugin to show basics of development for new hackers.')
);
return true;
}
}

View File

@ -28,4 +28,3 @@ Note: once you have a sizable number of users, sending OMB messages whenever
someone posts a message can really slow down your site; it may cause posting
to timeout. You may wish to enable queuing and handle OMB communication
offline. See the "queues and daemons" section of the main StatusNet README.

View File

@ -25,7 +25,6 @@ if (!defined('STATUSNET')) {
* @package OStatusPlugin
* @maintainer Brion Vibber <brion@status.net>
*/
class Ostatus_profile extends Managed_DataObject
{
public $__table = 'ostatus_profile';
@ -141,7 +140,7 @@ class Ostatus_profile extends Managed_DataObject
*
* Assumes that 'activity' namespace has been previously defined.
*
* @fixme replace with wrappers on asActivityObject when it's got everything.
* @todo FIXME: Replace with wrappers on asActivityObject when it's got everything.
*
* @param string $element one of 'actor', 'subject', 'object', 'target'
* @return string
@ -301,7 +300,7 @@ class Ostatus_profile extends Managed_DataObject
$actor->getURI(),
common_date_iso8601(time()));
// @fixme consolidate all these NS settings somewhere
// @todo FIXME: Consolidate all these NS settings somewhere.
$attributes = array('xmlns' => Activity::ATOM,
'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/',
'xmlns:thr' => 'http://purl.org/syndication/thread/1.0',
@ -414,7 +413,7 @@ class Ostatus_profile extends Managed_DataObject
if ($feed->localName == 'feed' && $feed->namespaceURI == Activity::ATOM) {
$this->processAtomFeed($feed, $source);
} else if ($feed->localName == 'rss') { // @fixme check namespace
} else if ($feed->localName == 'rss') { // @todo FIXME: Check namespace.
$this->processRssFeed($feed, $source);
} else {
// TRANS: Exception.
@ -466,7 +465,6 @@ class Ostatus_profile extends Managed_DataObject
*
* @return Notice Notice representing the new (or existing) activity
*/
public function processEntry($entry, $feed, $source)
{
$activity = new Activity($entry, $feed);
@ -509,7 +507,7 @@ class Ostatus_profile extends Managed_DataObject
Event::handle('EndHandleFeedEntry', array($activity));
Event::handle('EndHandleFeedEntryWithProfile', array($activity, $this, $notice));
}
return $notice;
}
@ -525,13 +523,15 @@ class Ostatus_profile extends Managed_DataObject
}
if (count($activity->objects) != 1) {
throw new ClientException(_m("Can only handle share activities with exactly one object."));
// TRANS: Client exception thrown when trying to share multiple activities at once.
throw new ClientException(_m('Can only handle share activities with exactly one object.'));
}
$shared = $activity->objects[0];
if (!($shared instanceof Activity)) {
throw new ClientException(_m("Can only handle shared activities."));
// TRANS: Client exception thrown when trying to share a non-activity object.
throw new ClientException(_m('Can only handle shared activities.'));
}
$other = Ostatus_profile::ensureActivityObjectProfile($shared->actor);
@ -539,10 +539,12 @@ class Ostatus_profile extends Managed_DataObject
// Save the item (or check for a dupe)
$sharedNotice = $other->processActivity($shared, $method);
if (empty($sharedNotice)) {
$sharedId = ($shared->id) ? $shared->id : $shared->objects[0]->id;
throw new ClientException(sprintf(_m("Failed to save activity %s."),
// TRANS: Client exception thrown when saving an activity share fails.
// TRANS: %s is a share ID.
throw new ClientException(sprintf(_m('Failed to save activity %s.'),
$sharedId));
}
@ -578,7 +580,7 @@ class Ostatus_profile extends Managed_DataObject
} else if (!empty($activity->title)) {
$sourceContent = $activity->title;
} else {
// @fixme fetch from $sourceUrl?
// @todo FIXME: Fetch from $sourceUrl?
// TRANS: Client exception. %s is a source URI.
throw new ClientException(sprintf(_m('No content for notice %s.'),$sourceUri));
}
@ -652,7 +654,7 @@ class Ostatus_profile extends Managed_DataObject
$options['replies'] = $replies;
// Maintain direct reply associations
// @fixme what about conversation ID?
// @todo FIXME: What about conversation ID?
if (!empty($activity->context->replyToID)) {
$orig = Notice::staticGet('uri',
$activity->context->replyToID);
@ -688,7 +690,7 @@ class Ostatus_profile extends Managed_DataObject
// Atom enclosures -> attachment URLs
foreach ($activity->enclosures as $href) {
// @fixme save these locally or....?
// @todo FIXME: Save these locally or....?
$options['urls'][] = $href;
}
@ -705,7 +707,7 @@ class Ostatus_profile extends Managed_DataObject
* @param Activity $activity
* @param string $method 'push' or 'salmon'
* @return mixed saved Notice or false
* @fixme break up this function, it's getting nasty long
* @todo FIXME: Break up this function, it's getting nasty long
*/
public function processPost($activity, $method)
{
@ -750,7 +752,7 @@ class Ostatus_profile extends Managed_DataObject
} else if (!empty($note->title)) {
$sourceContent = $note->title;
} else {
// @fixme fetch from $sourceUrl?
// @todo FIXME: Fetch from $sourceUrl?
// TRANS: Client exception. %s is a source URI.
throw new ClientException(sprintf(_m('No content for notice %s.'),$sourceUri));
}
@ -822,7 +824,7 @@ class Ostatus_profile extends Managed_DataObject
$options['replies'] = $replies;
// Maintain direct reply associations
// @fixme what about conversation ID?
// @todo FIXME: What about conversation ID?
if (!empty($activity->context->replyToID)) {
$orig = Notice::staticGet('uri',
$activity->context->replyToID);
@ -858,7 +860,7 @@ class Ostatus_profile extends Managed_DataObject
// Atom enclosures -> attachment URLs
foreach ($activity->enclosures as $href) {
// @fixme save these locally or....?
// @todo FIXME: Save these locally or....?
$options['urls'][] = $href;
}
@ -907,7 +909,7 @@ class Ostatus_profile extends Managed_DataObject
// Is the recipient a local user?
$user = User::staticGet('uri', $recipient);
if ($user) {
// @fixme sender verification, spam etc?
// @todo FIXME: Sender verification, spam etc?
$replies[] = $recipient;
continue;
}
@ -936,7 +938,7 @@ class Ostatus_profile extends Managed_DataObject
$oprofile = Ostatus_profile::ensureProfileURI($recipient);
if ($oprofile->isGroup()) {
// Deliver to local members of this remote group.
// @fixme sender verification?
// @todo FIXME: Sender verification?
$groups[] = $oprofile->group_id;
} else {
// may be canonicalized or something
@ -1129,7 +1131,7 @@ class Ostatus_profile extends Managed_DataObject
*
* @param DOMElement $feedEl root element of a loaded Atom feed
* @param array $hints additional discovery information passed from higher levels
* @fixme should this be marked public?
* @todo FIXME: Should this be marked public?
* @return Ostatus_profile
* @throws Exception
*/
@ -1155,7 +1157,7 @@ class Ostatus_profile extends Managed_DataObject
*
* @param DOMElement $feedEl root element of a loaded RSS feed
* @param array $hints additional discovery information passed from higher levels
* @fixme should this be marked public?
* @todo FIXME: Should this be marked public?
* @return Ostatus_profile
* @throws Exception
*/
@ -1181,7 +1183,7 @@ class Ostatus_profile extends Managed_DataObject
}
}
// @fixme we should check whether this feed has elements
// @todo FIXME: We should check whether this feed has elements
// with different <author> or <dc:creator> elements, and... I dunno.
// Do something about that.
@ -1219,7 +1221,7 @@ class Ostatus_profile extends Managed_DataObject
$this->uri));
}
// @fixme this should be better encapsulated
// @todo FIXME: This should be better encapsulated
// ripped from oauthstore.php (for old OMB client)
$temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
try {
@ -1233,7 +1235,7 @@ class Ostatus_profile extends Managed_DataObject
} else {
$id = $this->profile_id;
}
// @fixme should we be using different ids?
// @todo FIXME: Should we be using different ids?
$imagefile = new ImageFile($id, $temp_filename);
$filename = Avatar::filename($id,
image_type_to_extension($imagefile->type),
@ -1244,7 +1246,7 @@ class Ostatus_profile extends Managed_DataObject
unlink($temp_filename);
throw $e;
}
// @fixme hardcoded chmod is lame, but seems to be necessary to
// @todo FIXME: Hardcoded chmod is lame, but seems to be necessary to
// keep from accidentally saving images from command-line (queues)
// that can't be read from web server, which causes hard-to-notice
// problems later on:
@ -1253,7 +1255,7 @@ class Ostatus_profile extends Managed_DataObject
chmod(Avatar::path($filename), 0644);
$profile = $this->localProfile();
if (!empty($profile)) {
$profile->setOriginal($filename);
}
@ -1426,7 +1428,7 @@ class Ostatus_profile extends Managed_DataObject
}
/**
* @fixme validate stuff somewhere
* @todo FIXME: Validate stuff somewhere.
*/
/**
@ -1519,7 +1521,7 @@ class Ostatus_profile extends Managed_DataObject
$oprofile->profile_id = $profile->insert();
if (!$oprofile->profile_id) {
// TRANS: Server exception.
// TRANS: Server exception.
throw new ServerException(_m('Cannot save local profile.'));
}
} else if ($object->type == ActivityObject::GROUP) {
@ -1652,7 +1654,7 @@ class Ostatus_profile extends Managed_DataObject
}
}
// @fixme tags/categories
// @todo FIXME: tags/categories
// @todo tags from categories
if ($profile->id) {
@ -1887,7 +1889,7 @@ class Ostatus_profile extends Managed_DataObject
$xrd = $disco->lookup($addr);
} catch (Exception $e) {
// Save negative cache entry so we don't waste time looking it up again.
// @fixme distinguish temporary failures?
// @todo FIXME: Distinguish temporary failures?
self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), null);
// TRANS: Exception.
throw new Exception(_m('Not a valid webfinger address.'));
@ -1930,14 +1932,14 @@ class Ostatus_profile extends Managed_DataObject
return $oprofile;
} catch (OStatusShadowException $e) {
// We've ended up with a remote reference to a local user or group.
// @fixme ideally we should be able to say who it was so we can
// @todo FIXME: Ideally we should be able to say who it was so we can
// go back and refer to it the regular way
throw $e;
} catch (Exception $e) {
common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage());
// keep looking
//
// @fixme this means an error discovering from profile page
// @todo FIXME: This means an error discovering from profile page
// may give us a corrupt entry using the webfinger URI, which
// will obscure the correct page-keyed profile later on.
}

View File

@ -51,7 +51,6 @@ class RealtimePlugin extends Plugin
* When it's time to initialize the plugin, calculate and
* pass the URLs we need.
*/
function onInitializePlugin()
{
// FIXME: need to find a better way to pass this pattern in
@ -59,14 +58,14 @@ class RealtimePlugin extends Plugin
array('notice' => '0000000000'));
return true;
}
function onCheckSchema()
{
$schema = Schema::get();
$schema->ensureTable('realtime_channel', Realtime_channel::schemaDef());
return true;
}
function onAutoload($cls)
{
$dir = dirname(__FILE__);
@ -238,31 +237,31 @@ class RealtimePlugin extends Plugin
$json = $this->noticeAsJson($notice);
$this->_connect();
// XXX: We should probably fan-out here and do a
// new queue item for each path
foreach ($paths as $path) {
list($action, $arg1, $arg2) = $path;
$channels = Realtime_channel::getAllChannels($action, $arg1, $arg2);
foreach ($channels as $channel) {
// XXX: We should probably fan-out here and do a
// new queue item for each user/path combo
if (is_null($channel->user_id)) {
$profile = null;
} else {
$profile = Profile::staticGet('id', $channel->user_id);
}
if ($notice->inScope($profile)) {
$timeline = $this->_pathToChannel(array($channel->channel_key));
$this->_publish($timeline, $json);
}
}
list($action, $arg1, $arg2) = $path;
$channels = Realtime_channel::getAllChannels($action, $arg1, $arg2);
foreach ($channels as $channel) {
// XXX: We should probably fan-out here and do a
// new queue item for each user/path combo
if (is_null($channel->user_id)) {
$profile = null;
} else {
$profile = Profile::staticGet('id', $channel->user_id);
}
if ($notice->inScope($profile)) {
$timeline = $this->_pathToChannel(array($channel->channel_key));
$this->_publish($timeline, $json);
}
}
}
$this->_disconnect();
@ -367,9 +366,8 @@ class RealtimePlugin extends Plugin
$convurl = $conv->uri;
if(empty($convurl)) {
$msg = sprintf(
"Couldn't find Conversation ID %d to make 'in context'"
. "link for Notice ID %d",
$msg = sprintf( "Could not find Conversation ID %d to make 'in context'"
. "link for Notice ID %d.",
$notice->conversation,
$notice->id
);
@ -455,26 +453,26 @@ class RealtimePlugin extends Plugin
function _getChannel($action)
{
$timeline = null;
$arg1 = null;
$arg2 = null;
$arg1 = null;
$arg2 = null;
$action_name = $action->trimmed('action');
// FIXME: lists
// FIXME: search (!)
// FIXME: profile + tag
// FIXME: lists
// FIXME: search (!)
// FIXME: profile + tag
switch ($action_name) {
case 'public':
// no arguments
// no arguments
break;
case 'tag':
$tag = $action->trimmed('tag');
if (empty($tag)) {
$arg1 = $tag;
} else {
$this->log(LOG_NOTICE, "Unexpected 'tag' action without tag argument");
return null;
$this->log(LOG_NOTICE, "Unexpected 'tag' action without tag argument");
return null;
}
break;
case 'showstream':
@ -485,29 +483,29 @@ class RealtimePlugin extends Plugin
if (!empty($nickname)) {
$arg1 = $nickname;
} else {
$this->log(LOG_NOTICE, "Unexpected $action_name action without nickname argument.");
return null;
$this->log(LOG_NOTICE, "Unexpected $action_name action without nickname argument.");
return null;
}
break;
default:
return null;
}
$user = common_current_user();
$user_id = (!empty($user)) ? $user->id : null;
$channel = Realtime_channel::getChannel($user_id,
$action_name,
$arg1,
$arg2);
$user = common_current_user();
$user_id = (!empty($user)) ? $user->id : null;
$channel = Realtime_channel::getChannel($user_id,
$action_name,
$arg1,
$arg2);
return $channel;
}
function onStartReadWriteTables(&$alwaysRW, &$rwdb)
{
$alwaysRW[] = 'realtime_channel';
return true;
$alwaysRW[] = 'realtime_channel';
return true;
}
}

View File

@ -4,7 +4,7 @@
* Copyright (C) 2011, StatusNet, Inc.
*
* A channel for real-time browser data
*
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
@ -34,7 +34,7 @@ if (!defined('STATUSNET')) {
/**
* A channel for real-time browser data
*
*
* For each user currently browsing the site, we want to know which page they're on
* so we can send real-time updates to their browser.
*
@ -46,20 +46,19 @@ if (!defined('STATUSNET')) {
*
* @see DB_DataObject
*/
class Realtime_channel extends Managed_DataObject
{
const TIMEOUT = 1800; // 30 minutes
const TIMEOUT = 1800; // 30 minutes
public $__table = 'realtime_channel'; // table name
public $user_id; // int -> user.id, can be null
public $action; // string
public $arg1; // argument
public $arg2; // argument, usually null
public $channel_key; // 128-bit shared secret key
public $audience; // listener count
public $created; // created date
public $created; // created date
public $modified; // modified date
/**
@ -71,7 +70,6 @@ class Realtime_channel extends Managed_DataObject
* @param mixed $v Value to lookup
*
* @return Realtime_channel object found, or null for no hits
*
*/
function staticGet($k, $v=null)
{
@ -84,7 +82,6 @@ class Realtime_channel extends Managed_DataObject
* @param array $kv array of key-value mappings
*
* @return Realtime_channel object found, or null for no hits
*
*/
function pkeyGet($kv)
{
@ -100,34 +97,34 @@ class Realtime_channel extends Managed_DataObject
'description' => 'A channel of realtime notice data',
'fields' => array(
'user_id' => array('type' => 'int',
'not null' => false,
'description' => 'user viewing page; can be null'),
'not null' => false,
'description' => 'user viewing page; can be null'),
'action' => array('type' => 'varchar',
'length' => 255,
'not null' => true,
'description' => 'page being viewed'),
'arg1' => array('type' => 'varchar',
'length' => 255,
'not null' => false,
'description' => 'page argument, like username or tag'),
'arg2' => array('type' => 'varchar',
'length' => 255,
'not null' => false,
'description' => 'second page argument, like tag for showstream'),
'channel_key' => array('type' => 'varchar',
'length' => 32,
'not null' => true,
'description' => 'shared secret key for this channel'),
'audience' => array('type' => 'integer',
'length' => 255,
'not null' => true,
'description' => 'page being viewed'),
'arg1' => array('type' => 'varchar',
'length' => 255,
'not null' => false,
'description' => 'page argument, like username or tag'),
'arg2' => array('type' => 'varchar',
'length' => 255,
'not null' => false,
'description' => 'second page argument, like tag for showstream'),
'channel_key' => array('type' => 'varchar',
'length' => 32,
'not null' => true,
'description' => 'shared secret key for this channel'),
'audience' => array('type' => 'integer',
'not null' => true,
'default' => 0,
'description' => 'reference count'),
'created' => array('type' => 'datetime',
'not null' => true,
'description' => 'date this record was created'),
'not null' => true,
'description' => 'date this record was created'),
'modified' => array('type' => 'datetime',
'not null' => true,
'description' => 'date this record was modified'),
'not null' => true,
'description' => 'date this record was modified'),
),
'primary key' => array('channel_key'),
'unique keys' => array('realtime_channel_user_page_idx' => array('user_id', 'action', 'arg1', 'arg2')),
@ -140,107 +137,107 @@ class Realtime_channel extends Managed_DataObject
),
);
}
static function saveNew($user_id, $action, $arg1, $arg2)
{
$channel = new Realtime_channel();
$channel->user_id = $user_id;
$channel->action = $action;
$channel->arg1 = $arg1;
$channel->arg2 = $arg2;
$channel->audience = 1;
$channel->channel_key = common_good_rand(16); // 128-bit key, 32 hex chars
$channel->created = common_sql_now();
$channel->modified = $channel->created;
$channel->insert();
return $channel;
$channel = new Realtime_channel();
$channel->user_id = $user_id;
$channel->action = $action;
$channel->arg1 = $arg1;
$channel->arg2 = $arg2;
$channel->audience = 1;
$channel->channel_key = common_good_rand(16); // 128-bit key, 32 hex chars
$channel->created = common_sql_now();
$channel->modified = $channel->created;
$channel->insert();
return $channel;
}
static function getChannel($user_id, $action, $arg1, $arg2)
{
$channel = self::fetchChannel($user_id, $action, $arg1, $arg2);
// Ignore (and delete!) old channels
if (!empty($channel)) {
$modTime = strtotime($channel->modified);
if ((time() - $modTime) > self::TIMEOUT) {
$channel->delete();
$channel = null;
}
}
if (empty($channel)) {
$channel = self::saveNew($user_id, $action, $arg1, $arg2);
}
return $channel;
$channel = self::fetchChannel($user_id, $action, $arg1, $arg2);
// Ignore (and delete!) old channels
if (!empty($channel)) {
$modTime = strtotime($channel->modified);
if ((time() - $modTime) > self::TIMEOUT) {
$channel->delete();
$channel = null;
}
}
if (empty($channel)) {
$channel = self::saveNew($user_id, $action, $arg1, $arg2);
}
return $channel;
}
static function getAllChannels($action, $arg1, $arg2)
{
$channel = new Realtime_channel();
$channel->action = $action;
if (is_null($arg1)) {
$channel->whereAdd('arg1 is null');
} else {
$channel->arg1 = $arg1;
}
if (is_null($arg2)) {
$channel->whereAdd('arg2 is null');
} else {
$channel->arg2 = $arg2;
}
$channel->whereAdd('modified > "' . common_sql_date(time() - self::TIMEOUT) . '"');
$channels = array();
if ($channel->find()) {
$channels = $channel->fetchAll();
}
return $channels;
$channel = new Realtime_channel();
$channel->action = $action;
if (is_null($arg1)) {
$channel->whereAdd('arg1 is null');
} else {
$channel->arg1 = $arg1;
}
if (is_null($arg2)) {
$channel->whereAdd('arg2 is null');
} else {
$channel->arg2 = $arg2;
}
$channel->whereAdd('modified > "' . common_sql_date(time() - self::TIMEOUT) . '"');
$channels = array();
if ($channel->find()) {
$channels = $channel->fetchAll();
}
return $channels;
}
static function fetchChannel($user_id, $action, $arg1, $arg2)
{
$channel = new Realtime_channel();
if (is_null($user_id)) {
$channel->whereAdd('user_id is null');
} else {
$channel->user_id = $user_id;
}
$channel->action = $action;
if (is_null($arg1)) {
$channel->whereAdd('arg1 is null');
} else {
$channel->arg1 = $arg1;
}
if (is_null($arg2)) {
$channel->whereAdd('arg2 is null');
} else {
$channel->arg2 = $arg2;
}
if ($channel->find(true)) {
{
$channel = new Realtime_channel();
if (is_null($user_id)) {
$channel->whereAdd('user_id is null');
} else {
$channel->user_id = $user_id;
}
$channel->action = $action;
if (is_null($arg1)) {
$channel->whereAdd('arg1 is null');
} else {
$channel->arg1 = $arg1;
}
if (is_null($arg2)) {
$channel->whereAdd('arg2 is null');
} else {
$channel->arg2 = $arg2;
}
if ($channel->find(true)) {
$channel->increment();
return $channel;
} else {
return null;
}
return $channel;
} else {
return null;
}
}
function increment()

View File

@ -4,7 +4,7 @@
* Copyright (C) 2011, StatusNet, Inc.
*
* action to close a channel
*
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
@ -44,7 +44,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class ClosechannelAction extends Action
{
protected $channelKey = null;
@ -57,24 +56,26 @@ class ClosechannelAction extends Action
*
* @return boolean true
*/
function prepare($argarray)
{
parent::prepare($argarray);
if (!$this->isPost()) {
// TRANS: Client exception. Do not translate POST.
throw new ClientException(_m('You have to POST it.'));
}
$this->channelKey = $this->trimmed('channelkey');
if (empty($this->channelKey)) {
// TRANS: Client exception thrown when the channel key argument is missing.
throw new ClientException(_m('No channel key argument.'));
}
$this->channel = Realtime_channel::staticGet('channel_key', $this->channelKey);
if (empty($this->channel)) {
// TRANS: Client exception thrown when referring to a non-existing channel.
throw new ClientException(_m('No such channel.'));
}
@ -88,7 +89,6 @@ class ClosechannelAction extends Action
*
* @return void
*/
function handle($argarray=null)
{
$this->channel->decrement();
@ -107,7 +107,6 @@ class ClosechannelAction extends Action
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return false;

View File

@ -44,7 +44,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class KeepalivechannelAction extends Action
{
protected $channelKey = null;
@ -57,24 +56,26 @@ class KeepalivechannelAction extends Action
*
* @return boolean true
*/
function prepare($argarray)
{
parent::prepare($argarray);
if (!$this->isPost()) {
// TRANS: Client exception. Do not translate POST.
throw new ClientException(_m('You have to POST it.'));
}
$this->channelKey = $this->trimmed('channelkey');
if (empty($this->channelKey)) {
// TRANS: Client exception thrown when the channel key argument is missing.
throw new ClientException(_m('No channel key argument.'));
}
$this->channel = Realtime_channel::staticGet('channel_key', $this->channelKey);
if (empty($this->channel)) {
// TRANS: Client exception thrown when referring to a non-existing channel.
throw new ClientException(_m('No such channel.'));
}
@ -88,7 +89,6 @@ class KeepalivechannelAction extends Action
*
* @return void
*/
function handle($argarray=null)
{
$this->channel->touch();
@ -107,7 +107,6 @@ class KeepalivechannelAction extends Action
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return false;

View File

@ -412,7 +412,7 @@ RealtimeUpdate = {
$.ajax({
type: 'POST',
url: RealtimeUpdate._keepaliveurl});
}, 15 * 60 * 1000 ); // every 15 min; timeout in 30 min
RealtimeUpdate.initPlayPause();