* @author Robin Millette <millette@controlyourself.ca>
* @author Sarven Capadisli <csarven@controlyourself.ca>
* @author Tom Adams <tom@holizz.com>
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license GNU Affero General Public License http://www.gnu.org/licenses/
*/
// For auditing purposes, save a record that the notice
// was deleted.
- $deleted = new Deleted_notice();
+ // @fixme we have some cases where things get re-run and so the
+ // insert fails.
+ $deleted = Deleted_notice::staticGet('id', $this->id);
+ if (!$deleted) {
+ $deleted = new Deleted_notice();
- $deleted->id = $this->id;
- $deleted->profile_id = $this->profile_id;
- $deleted->uri = $this->uri;
- $deleted->created = $this->created;
- $deleted->deleted = common_sql_now();
+ $deleted->id = $this->id;
+ $deleted->profile_id = $this->profile_id;
+ $deleted->uri = $this->uri;
+ $deleted->created = $this->created;
+ $deleted->deleted = common_sql_now();
- $deleted->insert();
+ $deleted->insert();
+ }
// Clear related records
// NOTE: we don't clear queue items
$result = parent::delete();
+
+ $this->blowOnDelete();
+ return $result;
}
+ /**
+ * Extract #hashtags from this notice's content and save them to the database.
+ */
function saveTags()
{
/* extract all #hastags */
return true;
}
+ /* Add them to the database */
+ return $this->saveKnownTags($match[1]);
+ }
+
+ /**
+ * Record the given set of hash tags in the db for this notice.
+ * Given tag strings will be normalized and checked for dupes.
+ */
+ function saveKnownTags($hashtags)
+ {
//turn each into their canonical tag
//this is needed to remove dupes before saving e.g. #hash.tag = #hashtag
- $hashtags = array();
- for($i=0; $i<count($match[1]); $i++) {
- $hashtags[] = common_canonical_tag($match[1][$i]);
+ for($i=0; $i<count($hashtags); $i++) {
+ /* elide characters we don't want in the tag */
+ $hashtags[$i] = common_canonical_tag($hashtags[$i]);
}
- /* Add them to the database */
foreach(array_unique($hashtags) as $hashtag) {
- /* elide characters we don't want in the tag */
$this->saveTag($hashtag);
self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, $hashtag);
}
return true;
}
+ /**
+ * Record a single hash tag as associated with this notice.
+ * Tag format and uniqueness must be validated by caller.
+ */
function saveTag($hashtag)
{
$tag = new Notice_tag();
$id = $tag->insert();
if (!$id) {
- throw new ServerException(sprintf(_('DB error inserting hashtag: %s'),
+ // TRANS: Server exception. %s are the error details.
+ throw new ServerException(sprintf(_('Database error inserting hashtag: %s'),
$last_error->message));
return;
}
* int 'location_ns' geoname namespace to interpret location_id
* int 'reply_to'; notice ID this is a reply to
* int 'repeat_of'; notice ID this is a repeat of
- * string 'uri' permalink to notice; defaults to local notice URL
+ * string 'uri' unique ID for notice; defaults to local notice URL
+ * string 'url' permalink to notice; defaults to local notice URL
+ * string 'rendered' rendered HTML version of content
+ * array 'replies' list of profile URIs for reply delivery in
+ * place of extracting @-replies from content.
+ * array 'groups' list of group IDs to deliver to, in place of
+ * extracting ! tags from content
+ * array 'tags' list of hashtag strings to save with the notice
+ * in place of extracting # tags from content
+ * array 'urls' list of attached/referred URLs to save with the
+ * notice in place of extracting links from content
+ * @fixme tag override
*
* @return Notice
* @throws ClientException
*/
static function saveNew($profile_id, $content, $source, $options=null) {
$defaults = array('uri' => null,
+ 'url' => null,
'reply_to' => null,
'repeat_of' => null);
}
$notice->content = $final;
- $notice->rendered = common_render_content($final, $notice);
+
$notice->source = $source;
$notice->uri = $uri;
+ $notice->url = $url;
// Handle repeat case
$notice->location_ns = $location_ns;
}
+ if (!empty($rendered)) {
+ $notice->rendered = $rendered;
+ } else {
+ $notice->rendered = common_render_content($final, $notice);
+ }
+
if (Event::handle('StartNoticeSave', array(&$notice))) {
// XXX: some of these functions write to the DB
// the beginning of a new conversation.
if (empty($notice->conversation)) {
- $notice->conversation = $notice->id;
+ $conv = Conversation::create();
+ $notice->conversation = $conv->id;
$changed = true;
}
# Clear the cache for subscribed users, so they'll update at next request
# XXX: someone clever could prepend instead of clearing the cache
+
$notice->blowOnInsert();
+ // Save per-notice metadata...
+
+ if (isset($replies)) {
+ $notice->saveKnownReplies($replies);
+ } else {
+ $notice->saveReplies();
+ }
+
+ if (isset($tags)) {
+ $notice->saveKnownTags($tags);
+ } else {
+ $notice->saveTags();
+ }
+
+ // Note: groups may save tags, so must be run after tags are saved
+ // to avoid errors on duplicates.
+ if (isset($groups)) {
+ $notice->saveKnownGroups($groups);
+ } else {
+ $notice->saveGroups();
+ }
+
+ if (isset($urls)) {
+ $notice->saveKnownUrls($urls);
+ } else {
+ $notice->saveUrls();
+ }
+
+ // Prepare inbox delivery, may be queued to background.
$notice->distribute();
return $notice;
}
- function blowOnInsert()
+ function blowOnInsert($conversation = false)
{
self::blow('profile:notice_ids:%d', $this->profile_id);
self::blow('public');
- if ($this->conversation != $this->id) {
- self::blow('notice:conversation_ids:%d', $this->conversation);
- }
+ // XXX: Before we were blowing the casche only if the notice id
+ // was not the root of the conversation. What to do now?
+
+ self::blow('notice:conversation_ids:%d', $this->conversation);
if (!empty($this->repeat_of)) {
self::blow('notice:repeats:%d', $this->repeat_of);
}
$profile = Profile::staticGet($this->profile_id);
- $profile->blowNoticeCount();
+ if (!empty($profile)) {
+ $profile->blowNoticeCount();
+ }
+ }
+
+ /**
+ * Clear cache entries related to this notice at delete time.
+ * Necessary to avoid breaking paging on public, profile timelines.
+ */
+ function blowOnDelete()
+ {
+ $this->blowOnInsert();
+
+ self::blow('profile:notice_ids:%d;last', $this->profile_id);
+ self::blow('public;last');
}
/** save all urls in the notice to the db
common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this->id);
}
+ /**
+ * Save the given URLs as related links/attachments to the db
+ *
+ * follow redirects and save all available file information
+ * (mimetype, date, size, oembed, etc.)
+ *
+ * @return void
+ */
+ function saveKnownUrls($urls)
+ {
+ // @fixme validation?
+ foreach ($urls as $url) {
+ File::processNew($url, $this->id);
+ }
+ }
+
+ /**
+ * @private callback
+ */
function saveUrl($data) {
list($url, $notice_id) = $data;
File::processNew($url, $notice_id);
}
}
- function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array('Notice', '_publicStreamDirect'),
array(),
'public',
- $offset, $limit, $since_id, $max_id, $since);
-
+ $offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
}
- function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $max_id=0)
{
$notice = new Notice();
$notice->whereAdd('id <= ' . $max_id);
}
- if (!is_null($since)) {
- $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
- }
-
$ids = array();
if ($notice->find()) {
return $ids;
}
- function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array('Notice', '_conversationStreamDirect'),
array($id),
'notice:conversation_ids:'.$id,
- $offset, $limit, $since_id, $max_id, $since);
+ $offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
}
- function _conversationStreamDirect($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function _conversationStreamDirect($id, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
$notice = new Notice();
$notice->whereAdd('id <= ' . $max_id);
}
- if (!is_null($since)) {
- $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
- }
-
$ids = array();
if ($notice->find()) {
return $ids;
}
+ /**
+ * Is this notice part of an active conversation?
+ *
+ * @return boolean true if other messages exist in the same
+ * conversation, false if this is the only one
+ */
+ function hasConversation()
+ {
+ if (!empty($this->conversation)) {
+ $conversation = Notice::conversationStream(
+ $this->conversation,
+ 1,
+ 1
+ );
+ if ($conversation->N > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* @param $groups array of Group *objects*
* @param $recipients array of profile *ids*
return $ni;
}
- function addToInboxes($groups, $recipients)
+ /**
+ * Adds this notice to the inboxes of each local user who should receive
+ * it, based on author subscriptions, group memberships, and @-replies.
+ *
+ * Warning: running a second time currently will make items appear
+ * multiple times in users' inboxes.
+ *
+ * @fixme make more robust against errors
+ * @fixme break up massive deliveries to smaller background tasks
+ *
+ * @param array $groups optional list of Group objects;
+ * if left empty, will be loaded from group_inbox records
+ * @param array $recipient optional list of reply profile ids
+ * if left empty, will be loaded from reply records
+ */
+ function addToInboxes($groups=null, $recipients=null)
{
$ni = $this->whoGets($groups, $recipients);
- Inbox::bulkInsert($this->id, array_keys($ni));
+ $ids = array_keys($ni);
+
+ // We remove the author (if they're a local user),
+ // since we'll have already done this in distribute()
+
+ $i = array_search($this->profile_id, $ids);
+
+ if ($i !== false) {
+ unset($ids[$i]);
+ }
+
+ // Bulk insert
+
+ Inbox::bulkInsert($this->id, $ids);
return;
}
}
/**
+ * Record this notice to the given group inboxes for delivery.
+ * Overrides the regular parsing of !group markup.
+ *
+ * @param string $group_ids
+ * @fixme might prefer URIs as identifiers, as for replies?
+ * best with generalizations on user_group to support
+ * remote groups better.
+ */
+ function saveKnownGroups($group_ids)
+ {
+ if (!is_array($group_ids)) {
+ throw new ServerException("Bad type provided to saveKnownGroups");
+ }
+
+ $groups = array();
+ foreach ($group_ids as $id) {
+ $group = User_group::staticGet('id', $id);
+ if ($group) {
+ common_log(LOG_ERR, "Local delivery to group id $id, $group->nickname");
+ $result = $this->addToGroupInbox($group);
+ if (!$result) {
+ common_log_db_error($gi, 'INSERT', __FILE__);
+ }
+
+ // @fixme should we save the tags here or not?
+ $groups[] = clone($group);
+ } else {
+ common_log(LOG_ERR, "Local delivery to group id $id skipped, doesn't exist");
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * Parse !group delivery and record targets into group_inbox.
* @return array of Group objects
*/
function saveGroups()
foreach (array_unique($match[1]) as $nickname) {
/* XXX: remote groups. */
- $group = User_group::getForNickname($nickname);
+ $group = User_group::getForNickname($nickname, $profile);
if (empty($group)) {
continue;
}
/**
+ * Save reply records indicating that this notice needs to be
+ * delivered to the local users with the given URIs.
+ *
+ * Since this is expected to be used when saving foreign-sourced
+ * messages, we won't deliver to any remote targets as that's the
+ * source service's responsibility.
+ *
+ * Mail notifications etc will be handled later.
+ *
+ * @param array of unique identifier URIs for recipients
+ */
+ function saveKnownReplies($uris)
+ {
+ if (empty($uris)) {
+ return;
+ }
+ $sender = Profile::staticGet($this->profile_id);
+
+ foreach ($uris as $uri) {
+
+ $user = User::staticGet('uri', $uri);
+
+ if (!empty($user)) {
+ if ($user->hasBlocked($sender)) {
+ continue;
+ }
+
+ $reply = new Reply();
+
+ $reply->notice_id = $this->id;
+ $reply->profile_id = $user->id;
+
+ $id = $reply->insert();
+ }
+ }
+
+ return;
+ }
+
+ /**
+ * Pull @-replies from this message's content in StatusNet markup format
+ * and save reply records indicating that this message needs to be
+ * delivered to those users.
+ *
+ * Mail notifications to local profiles will be sent later.
+ *
* @return array of integer profile IDs
*/
+
function saveReplies()
{
// Don't save reply data for repeats
return array();
}
- // Alternative reply format
- $tname = false;
- if (preg_match('/^T ([A-Z0-9]{1,64}) /', $this->content, $match)) {
- $tname = $match[1];
- }
- // extract all @messages
- $cnt = preg_match_all('/(?:^|\s)@([a-z0-9]{1,64})/', $this->content, $match);
-
- $names = array();
+ $sender = Profile::staticGet($this->profile_id);
- if ($cnt || $tname) {
- // XXX: is there another way to make an array copy?
- $names = ($tname) ? array_unique(array_merge(array(strtolower($tname)), $match[1])) : array_unique($match[1]);
- }
+ // @todo ideally this parser information would only
+ // be calculated once.
- $sender = Profile::staticGet($this->profile_id);
+ $mentions = common_find_mentions($this->content, $this);
$replied = array();
// store replied only for first @ (what user/notice what the reply directed,
// we assume first @ is it)
- for ($i=0; $i<count($names); $i++) {
- $nickname = $names[$i];
- $recipient = common_relative_profile($sender, $nickname, $this->created);
- if (empty($recipient)) {
- continue;
- }
- // Don't save replies from blocked profile to local user
- $recipient_user = User::staticGet('id', $recipient->id);
- if (!empty($recipient_user) && $recipient_user->hasBlocked($sender)) {
- continue;
- }
- $reply = new Reply();
- $reply->notice_id = $this->id;
- $reply->profile_id = $recipient->id;
- $id = $reply->insert();
- if (!$id) {
- $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
- common_log(LOG_ERR, 'DB error inserting reply: ' . $last_error->message);
- common_server_error(sprintf(_('DB error inserting reply: %s'), $last_error->message));
- return array();
- } else {
- $replied[$recipient->id] = 1;
- }
- }
+ foreach ($mentions as $mention) {
- // Hash format replies, too
- $cnt = preg_match_all('/(?:^|\s)@#([a-z0-9]{1,64})/', $this->content, $match);
- if ($cnt) {
- foreach ($match[1] as $tag) {
- $tagged = Profile_tag::getTagged($sender->id, $tag);
- foreach ($tagged as $t) {
- if (!$replied[$t->id]) {
- // Don't save replies from blocked profile to local user
- $t_user = User::staticGet('id', $t->id);
- if ($t_user && $t_user->hasBlocked($sender)) {
- continue;
- }
- $reply = new Reply();
- $reply->notice_id = $this->id;
- $reply->profile_id = $t->id;
- $id = $reply->insert();
- if (!$id) {
- common_log_db_error($reply, 'INSERT', __FILE__);
- return array();
- } else {
- $replied[$recipient->id] = 1;
- }
- }
+ foreach ($mention['mentioned'] as $mentioned) {
+
+ // skip if they're already covered
+
+ if (!empty($replied[$mentioned->id])) {
+ continue;
}
- }
- }
- $recipientIds = array_keys($replied);
+ // Don't save replies from blocked profile to local user
- foreach ($recipientIds as $recipientId) {
- $user = User::staticGet('id', $recipientId);
- if (!empty($user)) {
- self::blow('reply:stream:%d', $reply->profile_id);
- mail_notify_attn($user, $this);
+ $mentioned_user = User::staticGet('id', $mentioned->id);
+ if (!empty($mentioned_user) && $mentioned_user->hasBlocked($sender)) {
+ continue;
+ }
+
+ $reply = new Reply();
+
+ $reply->notice_id = $this->id;
+ $reply->profile_id = $mentioned->id;
+
+ $id = $reply->insert();
+
+ if (!$id) {
+ common_log_db_error($reply, 'INSERT', __FILE__);
+ throw new ServerException("Couldn't save reply for {$this->id}, {$mentioned->id}");
+ } else {
+ $replied[$mentioned->id] = 1;
+ self::blow('reply:stream:%d', $mentioned->id);
+ }
}
}
+ $recipientIds = array_keys($replied);
+
return $recipientIds;
}
+ /**
+ * Pull the complete list of @-reply targets for this notice.
+ *
+ * @return array of integer profile ids
+ */
function getReplies()
{
// XXX: cache me
}
/**
- * Same calculation as saveGroups but without the saving
- * @fixme merge the functions
- * @return array of Group_inbox objects
+ * Send e-mail notifications to local @-reply targets.
+ *
+ * Replies must already have been saved; this is expected to be run
+ * from the distrib queue handler.
+ */
+ function sendReplyNotifications()
+ {
+ // Don't send reply notifications for repeats
+
+ if (!empty($this->repeat_of)) {
+ return array();
+ }
+
+ $recipientIds = $this->getReplies();
+
+ foreach ($recipientIds as $recipientId) {
+ $user = User::staticGet('id', $recipientId);
+ if (!empty($user)) {
+ mail_notify_attn($user, $this);
+ }
+ }
+ }
+
+ /**
+ * Pull list of groups this notice needs to be delivered to,
+ * as previously recorded by saveGroups() or saveKnownGroups().
+ *
+ * @return array of Group objects
*/
function getGroups()
{
if ($gi->find()) {
while ($gi->fetch()) {
- $groups[] = clone($gi);
+ $group = User_group::staticGet('id', $gi->group_id);
+ if ($group) {
+ $groups[] = $group;
+ }
}
}
return $groups;
}
- function asAtomEntry($namespace=false, $source=false)
+ function asAtomEntry($namespace=false, $source=false, $author=true, $cur=null)
{
$profile = $this->getProfile();
'xmlns:thr' => 'http://purl.org/syndication/thread/1.0',
'xmlns:georss' => 'http://www.georss.org/georss',
'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/',
- 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0');
+ 'xmlns:media' => 'http://purl.org/syndication/atommedia',
+ 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
+ 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0',
+ 'xmlns:statusnet' => 'http://status.net/schema/api/1/');
} else {
$attrs = array();
}
if ($source) {
$xs->elementStart('source');
+ $xs->element('id', null, $profile->profileurl);
$xs->element('title', null, $profile->nickname . " - " . common_config('site', 'name'));
$xs->element('link', array('href' => $profile->profileurl));
$user = User::staticGet('id', $profile->id);
}
$xs->element('icon', null, $profile->avatarUrl(AVATAR_PROFILE_SIZE));
+ $xs->element('updated', null, common_date_w3dtf($this->created));
}
if ($source) {
$xs->elementEnd('source');
}
- $xs->element('title', null, $this->content);
- $xs->element('summary', null, $this->content);
+ $xs->element('title', null, common_xml_safe_str($this->content));
- $xs->raw($profile->asAtomAuthor());
- $xs->raw($profile->asActivityActor());
+ if ($author) {
+ $xs->raw($profile->asAtomAuthor($cur));
+ $xs->raw($profile->asActivityActor());
+ }
$xs->element('link', array('rel' => 'alternate',
+ 'type' => 'text/html',
'href' => $this->bestUrl()));
$xs->element('id', null, $this->uri);
$xs->element('published', null, common_date_w3dtf($this->created));
$xs->element('updated', null, common_date_w3dtf($this->created));
+ $source = null;
+
+ $ns = $this->getSource();
+
+ if ($ns) {
+ if (!empty($ns->name) && !empty($ns->url)) {
+ $source = '<a href="'
+ . htmlspecialchars($ns->url)
+ . '" rel="nofollow">'
+ . htmlspecialchars($ns->name)
+ . '</a>';
+ } else {
+ $source = $ns->code;
+ }
+ }
+
+ $noticeInfoAttr = array(
+ 'local_id' => $this->id, // local notice ID (useful to clients for ordering)
+ 'source' => $source, // the client name (source attribution)
+ );
+
+ $ns = $this->getSource();
+ if ($ns) {
+ if (!empty($ns->url)) {
+ $noticeInfoAttr['source_link'] = $ns->url;
+ }
+ }
+
+ if (!empty($cur)) {
+ $noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false";
+ $profile = $cur->getProfile();
+ $noticeInfoAttr['repeated'] = ($profile->hasRepeated($this->id)) ? "true" : "false";
+ }
+
+ if (!empty($this->repeat_of)) {
+ $noticeInfoAttr['repeat_of'] = $this->repeat_of;
+ }
+
+ $xs->element('statusnet:notice_info', $noticeInfoAttr, null);
+
if ($this->reply_to) {
$reply_notice = Notice::staticGet('id', $this->reply_to);
if (!empty($reply_notice)) {
}
}
- if (!empty($this->conversation)
- && $this->conversation != $this->notice->id) {
- $xs->element(
- 'link', array(
- 'rel' => 'ostatus:conversation',
- 'href' => common_local_url(
- 'conversation',
- array('id' => $this->conversation)
- )
+ if (!empty($this->conversation)) {
+
+ $conv = Conversation::staticGet('id', $this->conversation);
+
+ if (!empty($conv)) {
+ $xs->element(
+ 'link', array(
+ 'rel' => 'ostatus:conversation',
+ 'href' => $conv->uri
)
);
+ }
}
$reply_ids = $this->getReplies();
foreach ($reply_ids as $id) {
$profile = Profile::staticGet('id', $id);
- if (!empty($profile)) {
+ if (!empty($profile)) {
$xs->element(
'link', array(
'rel' => 'ostatus:attention',
- 'href' => $profile->getAcctUri()
+ 'href' => $profile->getUri()
)
);
}
}
+ $groups = $this->getGroups();
+
+ foreach ($groups as $group) {
+ $xs->element(
+ 'link', array(
+ 'rel' => 'ostatus:attention',
+ 'href' => $group->permalink()
+ )
+ );
+ }
+
if (!empty($this->repeat_of)) {
$repeat = Notice::staticGet('id', $this->repeat_of);
if (!empty($repeat)) {
}
}
- $xs->element('content', array('type' => 'html'), $this->rendered);
+ $xs->element(
+ 'content',
+ array('type' => 'html'),
+ common_xml_safe_str($this->rendered)
+ );
$tag = new Notice_tag();
$tag->notice_id = $this->id;
return $xs->getString();
}
+ /**
+ * Returns an XML string fragment with a reference to a notice as an
+ * Activity Streams noun object with the given element type.
+ *
+ * Assumes that 'activity' namespace has been previously defined.
+ *
+ * @param string $element one of 'subject', 'object', 'target'
+ * @return string
+ */
+ function asActivityNoun($element)
+ {
+ $noun = ActivityObject::fromNotice($this);
+ return $noun->asString('activity:' . $element);
+ }
+
function bestUrl()
{
if (!empty($this->url)) {
}
}
- function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
$cache = common_memcache();
if (empty($cache) ||
- $since_id != 0 || $max_id != 0 || (!is_null($since) && $since > 0) ||
+ $since_id != 0 || $max_id != 0 ||
is_null($limit) ||
($offset + $limit) > NOTICE_CACHE_WINDOW) {
return call_user_func_array($fn, array_merge($args, array($offset, $limit, $since_id,
- $max_id, $since)));
+ $max_id)));
}
$idkey = common_cache_key($cachekey);
{
$author = Profile::staticGet('id', $this->profile_id);
+ // TRANS: Message used to repeat a notice. RT is the abbreviation of 'retweet'.
+ // TRANS: %1$s is the repeated user's name, %2$s is the repeated notice.
$content = sprintf(_('RT @%1$s %2$s'),
$author->nickname,
$this->content);
function distribute()
{
+ // We always insert for the author so they don't
+ // have to wait
+
+ $user = User::staticGet('id', $this->profile_id);
+ if (!empty($user)) {
+ Inbox::insertNotice($user->id, $this->id);
+ }
+
if (common_config('queue', 'inboxes')) {
// If there's a failure, we want to _force_
// distribution at this point.
return $result;
}
+
+ /**
+ * Get the source of the notice
+ *
+ * @return Notice_source $ns A notice source object. 'code' is the only attribute
+ * guaranteed to be populated.
+ */
+ function getSource()
+ {
+ $ns = new Notice_source();
+ if (!empty($this->source)) {
+ switch ($this->source) {
+ case 'web':
+ case 'xmpp':
+ case 'mail':
+ case 'omb':
+ case 'system':
+ case 'api':
+ $ns->code = $this->source;
+ break;
+ default:
+ $ns = Notice_source::staticGet($this->source);
+ if (!$ns) {
+ $ns = new Notice_source();
+ $ns->code = $this->source;
+ $app = Oauth_application::staticGet('name', $this->source);
+ if ($app) {
+ $ns->name = $app->name;
+ $ns->url = $app->source_url;
+ }
+ }
+ break;
+ }
+ }
+ return $ns;
+ }
+
+ /**
+ * Determine whether the notice was locally created
+ *
+ * @return boolean locality
+ */
+
+ public function isLocal()
+ {
+ return ($this->is_local == Notice::LOCAL_PUBLIC ||
+ $this->is_local == Notice::LOCAL_NONPUBLIC);
+ }
+
}