* 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
* @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.
*
*
* @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;
}
*
* @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);
*
* @return boolean is read only action?
*/
-
function isReadOnly($args)
{
if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
*
* @return string etag http header
*/
-
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
)
. '"';
}
-
+
return null;
}
*
* @return boolean true if delete, else false
*/
-
function requiresAuth()
{
if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
return true;
}
}
-}
\ No newline at end of file
+}
}
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;
- }
-
+ $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;
- }
- }
-
+ $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]);
- }
- }
-
- 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';
- }
- }
-
+ $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';
+ }
+ }
+
/**
- * @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)
{
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();
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;
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;
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.'));
}
-
+
}
{
return 'lists';
}
-
+
function getItems()
{
$items = array();
-
+
while ($this->lists->fetch()) {
$mode = $this->lists->private ? 'private' : 'public';
$items[] = array('showprofiletag',
{
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.'));
}
}
* 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
* @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
*
*
* @return
*/
-
function show()
{
$items = $this->getItems();
}
if (Event::handle('StartNav', array($this, &$tag, &$items))) {
-
$this->out->elementStart('ul', $attrs);
$total = count($items);
}
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');
}
}
}
-
+
$this->out->elementEnd('ul');
Event::handle('EndNav', array($this, $tag, $items));
{
return null;
}
-
}
'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()
$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.'));
}
$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));
}
if ($notice->object_type == Blog_entry::TYPE) {
return new BlogEntryListItem($nli);
}
-
+
return null;
}
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
-
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();
*/
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');
* Copyright (C) 2011, StatusNet, Inc.
*
* Show a blog entry
- *
+ *
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* @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');
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;
}
}
$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.'));
}
* Callback handler to populate end time dropdown
*/
class TimelistAction extends Action {
-
private $start;
private $duration;
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;
}
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.'));
}
}
$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;
}
-
}
/**
$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.')
);
}
$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;
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.')
);
}
'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;
}
}
-
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.
-
* @package OStatusPlugin
* @maintainer Brion Vibber <brion@status.net>
*/
-
class Ostatus_profile extends Managed_DataObject
{
public $__table = 'ostatus_profile';
*
* 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
$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',
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.
*
* @return Notice Notice representing the new (or existing) activity
*/
-
public function processEntry($entry, $feed, $source)
{
$activity = new Activity($entry, $feed);
Event::handle('EndHandleFeedEntry', array($activity));
Event::handle('EndHandleFeedEntryWithProfile', array($activity, $this, $notice));
}
-
+
return $notice;
}
}
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);
// 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));
}
} 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));
}
$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);
// Atom enclosures -> attachment URLs
foreach ($activity->enclosures as $href) {
- // @fixme save these locally or....?
+ // @todo FIXME: Save these locally or....?
$options['urls'][] = $href;
}
* @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)
{
} 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));
}
$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);
// Atom enclosures -> attachment URLs
foreach ($activity->enclosures as $href) {
- // @fixme save these locally or....?
+ // @todo FIXME: Save these locally or....?
$options['urls'][] = $href;
}
// 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;
}
$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
*
* @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
*/
*
* @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
*/
}
}
- // @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.
$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 {
} 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),
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:
chmod(Avatar::path($filename), 0644);
$profile = $this->localProfile();
-
+
if (!empty($profile)) {
$profile->setOriginal($filename);
}
}
/**
- * @fixme validate stuff somewhere
+ * @todo FIXME: Validate stuff somewhere.
*/
/**
$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) {
}
}
- // @fixme tags/categories
+ // @todo FIXME: tags/categories
// @todo tags from categories
if ($profile->id) {
$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.'));
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.
}
* 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
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__);
$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();
$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
);
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':
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;
}
}
* 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
/**
* 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.
*
*
* @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
/**
* @param mixed $v Value to lookup
*
* @return Realtime_channel object found, or null for no hits
- *
*/
function staticGet($k, $v=null)
{
* @param array $kv array of key-value mappings
*
* @return Realtime_channel object found, or null for no hits
- *
*/
function pkeyGet($kv)
{
'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')),
),
);
}
-
+
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()
* 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
* @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;
*
* @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.'));
}
*
* @return void
*/
-
function handle($argarray=null)
{
$this->channel->decrement();
*
* @return boolean is read only action?
*/
-
function isReadOnly($args)
{
return false;
* @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;
*
* @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.'));
}
*
* @return void
*/
-
function handle($argarray=null)
{
$this->channel->touch();
*
* @return boolean is read only action?
*/
-
function isReadOnly($args)
{
return false;
$.ajax({
type: 'POST',
url: RealtimeUpdate._keepaliveurl});
-
+
}, 15 * 60 * 1000 ); // every 15 min; timeout in 30 min
RealtimeUpdate.initPlayPause();