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

View File

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

View File

@ -84,8 +84,10 @@ class GroupsNav extends MoreMenu
function seeAllItem() { function seeAllItem() {
return array('usergroups', return array('usergroups',
array('nickname' => $this->user->nickname), array('nickname' => $this->user->nickname),
// TRANS: Link description for seeing all groups.
_('See all'), _('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'; return 'lists';
} }
function getItems() function getItems()
{ {
$items = array(); $items = array();
while ($this->lists->fetch()) { while ($this->lists->fetch()) {
$mode = $this->lists->private ? 'private' : 'public'; $mode = $this->lists->private ? 'private' : 'public';
$items[] = array('showprofiletag', $items[] = array('showprofiletag',
@ -84,7 +84,9 @@ class ListsNav extends MoreMenu
{ {
return array('peopletagsbyuser', return array('peopletagsbyuser',
array('nickname' => $this->profile->nickname), array('nickname' => $this->profile->nickname),
// TRANS: Link description for seeing all lists.
_('See all'), _('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. * Copyright (C) 2011, StatusNet, Inc.
* *
* A menu with a More... button to show more elements * A menu with a More... button to show more elements
* *
* PHP version 5 * PHP version 5
* *
* This program is free software: you can redistribute it and/or modify * 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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class MoreMenu extends Menu class MoreMenu extends Menu
{ {
const SOFT_MAX = 5; const SOFT_MAX = 5;
const HARD_MAX = 15; const HARD_MAX = 15;
/** /**
* Show a menu with a limited number of elements * Show a menu with a limited number of elements
* *
@ -57,7 +56,6 @@ class MoreMenu extends Menu
* *
* @return * @return
*/ */
function show() function show()
{ {
$items = $this->getItems(); $items = $this->getItems();
@ -72,7 +70,6 @@ class MoreMenu extends Menu
} }
if (Event::handle('StartNav', array($this, &$tag, &$items))) { if (Event::handle('StartNav', array($this, &$tag, &$items))) {
$this->out->elementStart('ul', $attrs); $this->out->elementStart('ul', $attrs);
$total = count($items); $total = count($items);
@ -89,10 +86,10 @@ class MoreMenu extends Menu
} }
if ($total > self::SOFT_MAX + 1) { if ($total > self::SOFT_MAX + 1) {
$this->out->elementStart('li', array('class' => 'more_link')); $this->out->elementStart('li', array('class' => 'more_link'));
$this->out->element('a', array('href' => '#', $this->out->element('a', array('href' => '#',
'onclick' => 'SN.U.showMoreMenuItems("'.$menuID.'"); return false;'), 'onclick' => 'SN.U.showMoreMenuItems("'.$menuID.'"); return false;'),
// TRANS: Link description to show more items in a list.
_('More ▼')); _('More ▼'));
$this->out->elementEnd('li'); $this->out->elementEnd('li');
@ -112,7 +109,7 @@ class MoreMenu extends Menu
} }
} }
} }
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
Event::handle('EndNav', array($this, $tag, $items)); Event::handle('EndNav', array($this, $tag, $items));
@ -123,5 +120,4 @@ class MoreMenu extends Menu
{ {
return null; return null;
} }
} }

View File

@ -120,13 +120,15 @@ class BlogPlugin extends MicroAppPlugin
'author' => 'Evan Prodromou', 'author' => 'Evan Prodromou',
'homepage' => 'http://status.net/wiki/Plugin:Blog', 'homepage' => 'http://status.net/wiki/Plugin:Blog',
'rawdescription' => 'rawdescription' =>
// TRANS: Plugin description.
_m('Let users write and share long-form texts.')); _m('Let users write and share long-form texts.'));
return true; return true;
} }
function appTitle() function appTitle()
{ {
return _m('Blog'); // TRANS: Blog application title.
return _m('TITLE','Blog');
} }
function tag() function tag()
@ -149,7 +151,7 @@ class BlogPlugin extends MicroAppPlugin
$entryObj = $activity->objects[0]; $entryObj = $activity->objects[0];
if ($entryObj->type != Blog_entry::TYPE) { 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.')); throw new ClientException(_m('Wrong type for object.'));
} }
@ -175,7 +177,8 @@ class BlogPlugin extends MicroAppPlugin
$entry = Blog_entry::fromNotice($notice); $entry = Blog_entry::fromNotice($notice);
if (empty($entry)) { 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)); $notice->id));
} }
@ -204,7 +207,7 @@ class BlogPlugin extends MicroAppPlugin
if ($notice->object_type == Blog_entry::TYPE) { if ($notice->object_type == Blog_entry::TYPE) {
return new BlogEntryListItem($nli); return new BlogEntryListItem($nli);
} }
return null; 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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class BlogEntryForm extends Form class BlogEntryForm extends Form
{ {
/** /**
@ -96,13 +95,13 @@ class BlogEntryForm extends Form
_m('Title of the blog entry.'), _m('Title of the blog entry.'),
'title'); 'title');
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->out->textarea('blog-entry-content', $this->out->textarea('blog-entry-content',
// TRANS: Field label on event form. // TRANS: Field label on blog entry form.
_m('LABEL','Text'), _m('LABEL','Text'),
null, null,
// TRANS: Field title on event form. // TRANS: Field title on blog entry form.
_m('Text of the blog entry.'), _m('Text of the blog entry.'),
'content'); 'content');
$this->unli(); $this->unli();
@ -124,8 +123,8 @@ class BlogEntryForm extends Form
*/ */
function formActions() function formActions()
{ {
// TRANS: Button text to save an event..
$this->out->submit('blog-entry-submit', $this->out->submit('blog-entry-submit',
// TRANS: Button text to save a blog entry.
_m('BUTTON', 'Save'), _m('BUTTON', 'Save'),
'submit', 'submit',
'submit'); 'submit');

View File

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

View File

@ -32,7 +32,6 @@ if (!defined('STATUSNET')) {
* Callback handler to populate end time dropdown * Callback handler to populate end time dropdown
*/ */
class TimelistAction extends Action { class TimelistAction extends Action {
private $start; private $start;
private $duration; private $duration;
@ -63,13 +62,14 @@ class TimelistAction extends Action {
if (!common_logged_in()) { if (!common_logged_in()) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user. // 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; return;
} }
if (!empty($this->start)) { if (!empty($this->start)) {
$times = EventTimeList::getTimes($this->start, $this->duration); $times = EventTimeList::getTimes($this->start, $this->duration);
} else { } else {
// TRANS: Client error when submitting a form with unexpected information.
$this->clientError(_m('Unexpected form submission.')); $this->clientError(_m('Unexpected form submission.'));
return; return;
} }
@ -78,6 +78,7 @@ class TimelistAction extends Action {
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
print json_encode($times); print json_encode($times);
} else { } else {
// TRANS: Client error displayed when using an action in a non-AJAX way.
$this->clientError(_m('This action is AJAX only.')); $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); $omb01 = Remote_profile::staticGet('id', $other_id);
if (!empty($omb01)) { if (!empty($omb01)) {
// TRANS: Client error displayed trying to subscribe to an OMB 0.1 remote profile.
throw new ClientException( throw new ClientException(
_m( // TRANS: Client error displayed trying to subscribe to an OMB 0.1 remote profile.
'You cannot subscribe to an OMB 0.1 ' _m('You cannot subscribe to an OMB 0.1 '
. 'remote profile with this action.' . 'remote profile with this action.'
) )
); );
return false; return false;
} }
} }
/** /**
@ -257,10 +255,9 @@ class OMBPlugin extends Plugin
$omb01 = Remote_profile::staticGet('id', $tagged_profile->id); $omb01 = Remote_profile::staticGet('id', $tagged_profile->id);
if (!empty($omb01)) { if (!empty($omb01)) {
// TRANS: Client error displayed when trying to add an OMB 0.1 remote profile to a list.
$this->clientError( $this->clientError(
_m( // TRANS: Client error displayed when trying to add an OMB 0.1 remote profile to a list.
'You cannot list an OMB 0.1 ' _m('You cannot list an OMB 0.1 '
.'remote profile with this action.') .'remote profile with this action.')
); );
} }
@ -282,10 +279,9 @@ class OMBPlugin extends Plugin
$omb01 = Remote_profile::staticGet('id', $ptag->tagged); $omb01 = Remote_profile::staticGet('id', $ptag->tagged);
if (!empty($omb01)) { if (!empty($omb01)) {
// TRANS: Client error displayed when trying to (un)list an OMB 0.1 remote profile.
$this->clientError( $this->clientError(
_m( // TRANS: Client error displayed when trying to (un)list an OMB 0.1 remote profile.
'You cannot (un)list an OMB 0.1 ' _m('You cannot (un)list an OMB 0.1 '
. 'remote profile with this action.') . 'remote profile with this action.')
); );
return false; return false;
@ -317,8 +313,8 @@ class OMBPlugin extends Plugin
if (!$result) { if (!$result) {
common_log_db_error($token, 'DELETE', __FILE__); 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( 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.') _m('Could not delete subscription OMB token.')
); );
} }
@ -394,12 +390,10 @@ class OMBPlugin extends Plugin
'version' => STATUSNET_VERSION, 'version' => STATUSNET_VERSION,
'author' => 'Zach Copley', 'author' => 'Zach Copley',
'homepage' => 'http://status.net/wiki/Plugin:Sample', 'homepage' => 'http://status.net/wiki/Plugin:Sample',
'rawdescription' =>
// TRANS: Plugin description. // 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; 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 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 to timeout. You may wish to enable queuing and handle OMB communication
offline. See the "queues and daemons" section of the main StatusNet README. offline. See the "queues and daemons" section of the main StatusNet README.

View File

@ -25,7 +25,6 @@ if (!defined('STATUSNET')) {
* @package OStatusPlugin * @package OStatusPlugin
* @maintainer Brion Vibber <brion@status.net> * @maintainer Brion Vibber <brion@status.net>
*/ */
class Ostatus_profile extends Managed_DataObject class Ostatus_profile extends Managed_DataObject
{ {
public $__table = 'ostatus_profile'; public $__table = 'ostatus_profile';
@ -141,7 +140,7 @@ class Ostatus_profile extends Managed_DataObject
* *
* Assumes that 'activity' namespace has been previously defined. * 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' * @param string $element one of 'actor', 'subject', 'object', 'target'
* @return string * @return string
@ -301,7 +300,7 @@ class Ostatus_profile extends Managed_DataObject
$actor->getURI(), $actor->getURI(),
common_date_iso8601(time())); common_date_iso8601(time()));
// @fixme consolidate all these NS settings somewhere // @todo FIXME: Consolidate all these NS settings somewhere.
$attributes = array('xmlns' => Activity::ATOM, $attributes = array('xmlns' => Activity::ATOM,
'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/',
'xmlns:thr' => 'http://purl.org/syndication/thread/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) { if ($feed->localName == 'feed' && $feed->namespaceURI == Activity::ATOM) {
$this->processAtomFeed($feed, $source); $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); $this->processRssFeed($feed, $source);
} else { } else {
// TRANS: Exception. // TRANS: Exception.
@ -466,7 +465,6 @@ class Ostatus_profile extends Managed_DataObject
* *
* @return Notice Notice representing the new (or existing) activity * @return Notice Notice representing the new (or existing) activity
*/ */
public function processEntry($entry, $feed, $source) public function processEntry($entry, $feed, $source)
{ {
$activity = new Activity($entry, $feed); $activity = new Activity($entry, $feed);
@ -509,7 +507,7 @@ class Ostatus_profile extends Managed_DataObject
Event::handle('EndHandleFeedEntry', array($activity)); Event::handle('EndHandleFeedEntry', array($activity));
Event::handle('EndHandleFeedEntryWithProfile', array($activity, $this, $notice)); Event::handle('EndHandleFeedEntryWithProfile', array($activity, $this, $notice));
} }
return $notice; return $notice;
} }
@ -525,13 +523,15 @@ class Ostatus_profile extends Managed_DataObject
} }
if (count($activity->objects) != 1) { 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]; $shared = $activity->objects[0];
if (!($shared instanceof Activity)) { 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); $other = Ostatus_profile::ensureActivityObjectProfile($shared->actor);
@ -539,10 +539,12 @@ class Ostatus_profile extends Managed_DataObject
// Save the item (or check for a dupe) // Save the item (or check for a dupe)
$sharedNotice = $other->processActivity($shared, $method); $sharedNotice = $other->processActivity($shared, $method);
if (empty($sharedNotice)) { if (empty($sharedNotice)) {
$sharedId = ($shared->id) ? $shared->id : $shared->objects[0]->id; $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)); $sharedId));
} }
@ -578,7 +580,7 @@ class Ostatus_profile extends Managed_DataObject
} else if (!empty($activity->title)) { } else if (!empty($activity->title)) {
$sourceContent = $activity->title; $sourceContent = $activity->title;
} else { } else {
// @fixme fetch from $sourceUrl? // @todo FIXME: Fetch from $sourceUrl?
// TRANS: Client exception. %s is a source URI. // TRANS: Client exception. %s is a source URI.
throw new ClientException(sprintf(_m('No content for notice %s.'),$sourceUri)); throw new ClientException(sprintf(_m('No content for notice %s.'),$sourceUri));
} }
@ -652,7 +654,7 @@ class Ostatus_profile extends Managed_DataObject
$options['replies'] = $replies; $options['replies'] = $replies;
// Maintain direct reply associations // Maintain direct reply associations
// @fixme what about conversation ID? // @todo FIXME: What about conversation ID?
if (!empty($activity->context->replyToID)) { if (!empty($activity->context->replyToID)) {
$orig = Notice::staticGet('uri', $orig = Notice::staticGet('uri',
$activity->context->replyToID); $activity->context->replyToID);
@ -688,7 +690,7 @@ class Ostatus_profile extends Managed_DataObject
// Atom enclosures -> attachment URLs // Atom enclosures -> attachment URLs
foreach ($activity->enclosures as $href) { foreach ($activity->enclosures as $href) {
// @fixme save these locally or....? // @todo FIXME: Save these locally or....?
$options['urls'][] = $href; $options['urls'][] = $href;
} }
@ -705,7 +707,7 @@ class Ostatus_profile extends Managed_DataObject
* @param Activity $activity * @param Activity $activity
* @param string $method 'push' or 'salmon' * @param string $method 'push' or 'salmon'
* @return mixed saved Notice or false * @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) public function processPost($activity, $method)
{ {
@ -750,7 +752,7 @@ class Ostatus_profile extends Managed_DataObject
} else if (!empty($note->title)) { } else if (!empty($note->title)) {
$sourceContent = $note->title; $sourceContent = $note->title;
} else { } else {
// @fixme fetch from $sourceUrl? // @todo FIXME: Fetch from $sourceUrl?
// TRANS: Client exception. %s is a source URI. // TRANS: Client exception. %s is a source URI.
throw new ClientException(sprintf(_m('No content for notice %s.'),$sourceUri)); throw new ClientException(sprintf(_m('No content for notice %s.'),$sourceUri));
} }
@ -822,7 +824,7 @@ class Ostatus_profile extends Managed_DataObject
$options['replies'] = $replies; $options['replies'] = $replies;
// Maintain direct reply associations // Maintain direct reply associations
// @fixme what about conversation ID? // @todo FIXME: What about conversation ID?
if (!empty($activity->context->replyToID)) { if (!empty($activity->context->replyToID)) {
$orig = Notice::staticGet('uri', $orig = Notice::staticGet('uri',
$activity->context->replyToID); $activity->context->replyToID);
@ -858,7 +860,7 @@ class Ostatus_profile extends Managed_DataObject
// Atom enclosures -> attachment URLs // Atom enclosures -> attachment URLs
foreach ($activity->enclosures as $href) { foreach ($activity->enclosures as $href) {
// @fixme save these locally or....? // @todo FIXME: Save these locally or....?
$options['urls'][] = $href; $options['urls'][] = $href;
} }
@ -907,7 +909,7 @@ class Ostatus_profile extends Managed_DataObject
// Is the recipient a local user? // Is the recipient a local user?
$user = User::staticGet('uri', $recipient); $user = User::staticGet('uri', $recipient);
if ($user) { if ($user) {
// @fixme sender verification, spam etc? // @todo FIXME: Sender verification, spam etc?
$replies[] = $recipient; $replies[] = $recipient;
continue; continue;
} }
@ -936,7 +938,7 @@ class Ostatus_profile extends Managed_DataObject
$oprofile = Ostatus_profile::ensureProfileURI($recipient); $oprofile = Ostatus_profile::ensureProfileURI($recipient);
if ($oprofile->isGroup()) { if ($oprofile->isGroup()) {
// Deliver to local members of this remote group. // Deliver to local members of this remote group.
// @fixme sender verification? // @todo FIXME: Sender verification?
$groups[] = $oprofile->group_id; $groups[] = $oprofile->group_id;
} else { } else {
// may be canonicalized or something // 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 DOMElement $feedEl root element of a loaded Atom feed
* @param array $hints additional discovery information passed from higher levels * @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 * @return Ostatus_profile
* @throws Exception * @throws Exception
*/ */
@ -1155,7 +1157,7 @@ class Ostatus_profile extends Managed_DataObject
* *
* @param DOMElement $feedEl root element of a loaded RSS feed * @param DOMElement $feedEl root element of a loaded RSS feed
* @param array $hints additional discovery information passed from higher levels * @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 * @return Ostatus_profile
* @throws Exception * @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. // with different <author> or <dc:creator> elements, and... I dunno.
// Do something about that. // Do something about that.
@ -1219,7 +1221,7 @@ class Ostatus_profile extends Managed_DataObject
$this->uri)); $this->uri));
} }
// @fixme this should be better encapsulated // @todo FIXME: This should be better encapsulated
// ripped from oauthstore.php (for old OMB client) // ripped from oauthstore.php (for old OMB client)
$temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
try { try {
@ -1233,7 +1235,7 @@ class Ostatus_profile extends Managed_DataObject
} else { } else {
$id = $this->profile_id; $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); $imagefile = new ImageFile($id, $temp_filename);
$filename = Avatar::filename($id, $filename = Avatar::filename($id,
image_type_to_extension($imagefile->type), image_type_to_extension($imagefile->type),
@ -1244,7 +1246,7 @@ class Ostatus_profile extends Managed_DataObject
unlink($temp_filename); unlink($temp_filename);
throw $e; 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) // keep from accidentally saving images from command-line (queues)
// that can't be read from web server, which causes hard-to-notice // that can't be read from web server, which causes hard-to-notice
// problems later on: // problems later on:
@ -1253,7 +1255,7 @@ class Ostatus_profile extends Managed_DataObject
chmod(Avatar::path($filename), 0644); chmod(Avatar::path($filename), 0644);
$profile = $this->localProfile(); $profile = $this->localProfile();
if (!empty($profile)) { if (!empty($profile)) {
$profile->setOriginal($filename); $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(); $oprofile->profile_id = $profile->insert();
if (!$oprofile->profile_id) { if (!$oprofile->profile_id) {
// TRANS: Server exception. // TRANS: Server exception.
throw new ServerException(_m('Cannot save local profile.')); throw new ServerException(_m('Cannot save local profile.'));
} }
} else if ($object->type == ActivityObject::GROUP) { } 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 // @todo tags from categories
if ($profile->id) { if ($profile->id) {
@ -1887,7 +1889,7 @@ class Ostatus_profile extends Managed_DataObject
$xrd = $disco->lookup($addr); $xrd = $disco->lookup($addr);
} catch (Exception $e) { } catch (Exception $e) {
// Save negative cache entry so we don't waste time looking it up again. // 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); self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), null);
// TRANS: Exception. // TRANS: Exception.
throw new Exception(_m('Not a valid webfinger address.')); throw new Exception(_m('Not a valid webfinger address.'));
@ -1930,14 +1932,14 @@ class Ostatus_profile extends Managed_DataObject
return $oprofile; return $oprofile;
} catch (OStatusShadowException $e) { } catch (OStatusShadowException $e) {
// We've ended up with a remote reference to a local user or group. // 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 // go back and refer to it the regular way
throw $e; throw $e;
} catch (Exception $e) { } catch (Exception $e) {
common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage()); common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage());
// keep looking // 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 // may give us a corrupt entry using the webfinger URI, which
// will obscure the correct page-keyed profile later on. // 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 * When it's time to initialize the plugin, calculate and
* pass the URLs we need. * pass the URLs we need.
*/ */
function onInitializePlugin() function onInitializePlugin()
{ {
// FIXME: need to find a better way to pass this pattern in // FIXME: need to find a better way to pass this pattern in
@ -59,14 +58,14 @@ class RealtimePlugin extends Plugin
array('notice' => '0000000000')); array('notice' => '0000000000'));
return true; return true;
} }
function onCheckSchema() function onCheckSchema()
{ {
$schema = Schema::get(); $schema = Schema::get();
$schema->ensureTable('realtime_channel', Realtime_channel::schemaDef()); $schema->ensureTable('realtime_channel', Realtime_channel::schemaDef());
return true; return true;
} }
function onAutoload($cls) function onAutoload($cls)
{ {
$dir = dirname(__FILE__); $dir = dirname(__FILE__);
@ -238,31 +237,31 @@ class RealtimePlugin extends Plugin
$json = $this->noticeAsJson($notice); $json = $this->noticeAsJson($notice);
$this->_connect(); $this->_connect();
// XXX: We should probably fan-out here and do a // XXX: We should probably fan-out here and do a
// new queue item for each path // new queue item for each path
foreach ($paths as $path) { foreach ($paths as $path) {
list($action, $arg1, $arg2) = $path; list($action, $arg1, $arg2) = $path;
$channels = Realtime_channel::getAllChannels($action, $arg1, $arg2); $channels = Realtime_channel::getAllChannels($action, $arg1, $arg2);
foreach ($channels as $channel) { foreach ($channels as $channel) {
// XXX: We should probably fan-out here and do a // XXX: We should probably fan-out here and do a
// new queue item for each user/path combo // new queue item for each user/path combo
if (is_null($channel->user_id)) { if (is_null($channel->user_id)) {
$profile = null; $profile = null;
} else { } else {
$profile = Profile::staticGet('id', $channel->user_id); $profile = Profile::staticGet('id', $channel->user_id);
} }
if ($notice->inScope($profile)) { if ($notice->inScope($profile)) {
$timeline = $this->_pathToChannel(array($channel->channel_key)); $timeline = $this->_pathToChannel(array($channel->channel_key));
$this->_publish($timeline, $json); $this->_publish($timeline, $json);
} }
} }
} }
$this->_disconnect(); $this->_disconnect();
@ -367,9 +366,8 @@ class RealtimePlugin extends Plugin
$convurl = $conv->uri; $convurl = $conv->uri;
if(empty($convurl)) { if(empty($convurl)) {
$msg = sprintf( $msg = sprintf( "Could not find Conversation ID %d to make 'in context'"
"Couldn't find Conversation ID %d to make 'in context'" . "link for Notice ID %d.",
. "link for Notice ID %d",
$notice->conversation, $notice->conversation,
$notice->id $notice->id
); );
@ -455,26 +453,26 @@ class RealtimePlugin extends Plugin
function _getChannel($action) function _getChannel($action)
{ {
$timeline = null; $timeline = null;
$arg1 = null; $arg1 = null;
$arg2 = null; $arg2 = null;
$action_name = $action->trimmed('action'); $action_name = $action->trimmed('action');
// FIXME: lists // FIXME: lists
// FIXME: search (!) // FIXME: search (!)
// FIXME: profile + tag // FIXME: profile + tag
switch ($action_name) { switch ($action_name) {
case 'public': case 'public':
// no arguments // no arguments
break; break;
case 'tag': case 'tag':
$tag = $action->trimmed('tag'); $tag = $action->trimmed('tag');
if (empty($tag)) { if (empty($tag)) {
$arg1 = $tag; $arg1 = $tag;
} else { } else {
$this->log(LOG_NOTICE, "Unexpected 'tag' action without tag argument"); $this->log(LOG_NOTICE, "Unexpected 'tag' action without tag argument");
return null; return null;
} }
break; break;
case 'showstream': case 'showstream':
@ -485,29 +483,29 @@ class RealtimePlugin extends Plugin
if (!empty($nickname)) { if (!empty($nickname)) {
$arg1 = $nickname; $arg1 = $nickname;
} else { } else {
$this->log(LOG_NOTICE, "Unexpected $action_name action without nickname argument."); $this->log(LOG_NOTICE, "Unexpected $action_name action without nickname argument.");
return null; return null;
} }
break; break;
default: default:
return null; return null;
} }
$user = common_current_user(); $user = common_current_user();
$user_id = (!empty($user)) ? $user->id : null; $user_id = (!empty($user)) ? $user->id : null;
$channel = Realtime_channel::getChannel($user_id, $channel = Realtime_channel::getChannel($user_id,
$action_name, $action_name,
$arg1, $arg1,
$arg2); $arg2);
return $channel; return $channel;
} }
function onStartReadWriteTables(&$alwaysRW, &$rwdb) function onStartReadWriteTables(&$alwaysRW, &$rwdb)
{ {
$alwaysRW[] = 'realtime_channel'; $alwaysRW[] = 'realtime_channel';
return true; return true;
} }
} }

View File

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

View File

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

View File

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