/** * Initialization. * * @param array $args Web and URL arguments * * @return boolean false if id not passed in */ protected function prepare(array $args = array()) { parent::prepare($args); $convId = $this->int('id'); $this->conv = Conversation::getKV('id', $convId); if (!$this->conv instanceof Conversation) { throw new ClientException('Could not find specified conversation'); } return true; }
protected function prepare(array $args = array()) { parent::prepare($args); $convId = $this->trimmed('id'); if (empty($convId)) { // TRANS: Client exception thrown when no conversation ID is given. throw new ClientException(_('No conversation ID.')); } $this->conversation = Conversation::getKV('id', $convId); if (empty($this->conversation)) { // TRANS: Client exception thrown when referring to a non-existing conversation ID (%d). throw new ClientException(sprintf(_('No conversation with ID %d.'), $convId), 404); } $stream = new ConversationNoticeStream($convId, $this->scoped); $notice = $stream->getNotices(($this->page - 1) * $this->count, $this->count, $this->since_id, $this->max_id); $this->notices = $notice->fetchAll(); return true; }
/** * Convert a notice into an activity for export. * * @param Profile $scoped The currently logged in/scoped profile * * @return Activity activity object representing this Notice. */ function asActivity(Profile $scoped = null) { $act = self::cacheGet(Cache::codeKey('notice:as-activity:' . $this->id)); if ($act instanceof Activity) { return $act; } $act = new Activity(); if (Event::handle('StartNoticeAsActivity', array($this, $act, $scoped))) { $act->id = $this->uri; $act->time = strtotime($this->created); try { $act->link = $this->getUrl(); } catch (InvalidUrlException $e) { // The notice is probably a share or similar, which don't // have a representational URL of their own. } $act->content = common_xml_safe_str($this->getRendered()); $profile = $this->getProfile(); $act->actor = $profile->asActivityObject(); $act->actor->extra[] = $profile->profileInfo($scoped); $act->verb = $this->verb; if (!$this->repeat_of) { $act->objects[] = $this->asActivityObject(); } // XXX: should this be handled by default processing for object entry? // Categories $tags = $this->getTags(); foreach ($tags as $tag) { $cat = new AtomCategory(); $cat->term = $tag; $act->categories[] = $cat; } // Enclosures // XXX: use Atom Media and/or File activity objects instead $attachments = $this->attachments(); foreach ($attachments as $attachment) { // Include local attachments in Activity if (!empty($attachment->filename)) { $act->enclosures[] = $attachment->getEnclosure(); } } $ctx = new ActivityContext(); try { $reply = $this->getParent(); $ctx->replyToID = $reply->getUri(); $ctx->replyToUrl = $reply->getUrl(true); // true for fallback to local URL, less messy } catch (NoParentNoticeException $e) { // This is not a reply to something } catch (NoResultException $e) { // Parent notice was probably deleted } try { $ctx->location = Notice_location::locFromStored($this); } catch (ServerException $e) { $ctx->location = null; } $conv = null; if (!empty($this->conversation)) { $conv = Conversation::getKV('id', $this->conversation); if ($conv instanceof Conversation) { $ctx->conversation = $conv->uri; } } // This covers the legacy getReplies and getGroups too which get their data // from entries stored via Notice::saveNew (which we want to move away from)... foreach ($this->getAttentionProfiles() as $target) { // User and group profiles which get the attention of this notice $ctx->attention[$target->getUri()] = $target->getObjectType(); } switch ($this->scope) { case Notice::PUBLIC_SCOPE: $ctx->attention[ActivityContext::ATTN_PUBLIC] = ActivityObject::COLLECTION; break; case Notice::FOLLOWER_SCOPE: $surl = common_local_url("subscribers", array('nickname' => $profile->nickname)); $ctx->attention[$surl] = ActivityObject::COLLECTION; break; } $act->context = $ctx; $source = $this->getSource(); if ($source instanceof Notice_source) { $act->generator = ActivityObject::fromNoticeSource($source); } // Source $atom_feed = $profile->getAtomFeed(); if (!empty($atom_feed)) { $act->source = new ActivitySource(); // XXX: we should store the actual feed ID $act->source->id = $atom_feed; // XXX: we should store the actual feed title $act->source->title = $profile->getBestName(); $act->source->links['alternate'] = $profile->profileurl; $act->source->links['self'] = $atom_feed; $act->source->icon = $profile->avatarUrl(AVATAR_PROFILE_SIZE); $notice = $profile->getCurrentNotice(); if ($notice instanceof Notice) { $act->source->updated = self::utcDate($notice->created); } $user = User::getKV('id', $profile->id); if ($user instanceof User) { $act->source->links['license'] = common_config('license', 'url'); } } if ($this->isLocal()) { $act->selfLink = common_local_url('ApiStatusesShow', array('id' => $this->id, 'format' => 'atom')); $act->editLink = $act->selfLink; } Event::handle('EndNoticeAsActivity', array($this, $act, $scoped)); } self::cacheSet(Cache::codeKey('notice:as-activity:' . $this->id), $act); return $act; }
public function testConversationLink() { $orig = $this->_fakeNotice($this->targetUser1); $text = "@" . $this->targetUser1->nickname . " reply text " . common_random_hexstr(4); $reply = Notice::saveNew($this->author1->id, $text, 'test', array('uri' => null, 'reply_to' => $orig->id)); $conv = Conversation::getKV('id', $reply->conversation); $entry = $reply->asAtomEntry(); $element = $this->_entryToElement($entry, true); $this->assertEquals($conv->getUri(), ActivityUtils::getLink($element, 'ostatus:conversation')); }
function linkback_save($source, $target, $response, $notice_or_user) { $dupe = linkback_is_dupe('uri', $response->getEffectiveUrl()); if (!$dupe) { $dupe = linkback_is_dupe('url', $response->getEffectiveUrl()); } if (!$dupe) { $dupe = linkback_is_dupe('uri', $source); } if (!$dupe) { $dupe = linkback_is_dupe('url', $source); } $mf2 = new Mf2\Parser($response->getBody(), $response->getEffectiveUrl()); $mf2 = $mf2->parse(); $entry = linkback_find_entry($mf2, $target); if (!$entry) { preg_match('/<title>([^<]+)', $response->getBody(), $match); $entry = array('content' => array('html' => $response->getBody()), 'name' => $match[1] ? htmlspecialchars_decode($match[1]) : $source); } if (!$entry['url']) { $entry['url'] = array($response->getEffectiveUrl()); } if (!$dupe) { $dupe = linkback_is_dupe('uri', $entry['url'][0]); } if (!$dupe) { $dupe = linkback_is_dupe('url', $entry['url'][0]); } $entry['type'] = linkback_entry_type($entry, $mf2, $target); list($profile, $author) = linkback_profile($entry, $mf2, $response, $target); list($content, $options) = linkback_notice($source, $notice_or_user, $entry, $author, $mf2); if ($dupe) { $orig = clone $dupe; try { // Ignore duplicate save error try { $dupe->saveKnownReplies($options['replies']); } catch (ServerException $ex) { } try { $dupe->saveKnownTags($options['tags']); } catch (ServerException $ex) { } try { $dupe->saveKnownUrls($options['urls']); } catch (ServerException $ex) { } if ($options['reply_to']) { $dupe->reply_to = $options['reply_to']; } if ($options['repeat_of']) { $dupe->repeat_of = $options['repeat_of']; } if ($dupe->reply_to != $orig->reply_to || $dupe->repeat_of != $orig->repeat_of) { $parent = Notice::getKV('id', $dupe->repost_of ? $dupe->repost_of : $dupe->reply_to); if ($parent instanceof Notice) { // If we changed the reply_to or repeat_of we might live in a new conversation now $dupe->conversation = $parent->conversation; } } if ($dupe->update($orig)) { $saved = $dupe; } if ($dupe->conversation != $orig->conversation && Conversation::noticeCount($orig->conversation) < 1) { // Delete empty conversation $emptyConversation = Conversation::getKV('id', $orig->conversation); $emptyConversation->delete(); } } catch (Exception $e) { common_log(LOG_ERR, "Linkback update of remote message {$source} failed: " . $e->getMessage()); return false; } common_log(LOG_INFO, "Linkback updated remote message {$source} as notice id {$saved->id}"); } else { if ($entry['type'] == 'like' || $entry['type'] == 'reply' && $entry['rsvp']) { $act = new Activity(); $act->type = ActivityObject::ACTIVITY; $act->time = $options['created'] ? strtotime($options['created']) : time(); $act->title = $entry["name"] ? $entry["name"][0] : _m("Favor"); $act->actor = $profile->asActivityObject(); $act->target = $notice_or_user->asActivityObject(); $act->objects = array(clone $act->target); // TRANS: Message that is the "content" of a favorite (%1$s is the actor's nickname, %2$ is the favorited // notice's nickname and %3$s is the content of the favorited notice.) $act->content = sprintf(_('%1$s favorited something by %2$s: %3$s'), $profile->getNickname(), $notice_or_user->getProfile()->getNickname(), $notice_or_user->getRendered()); if ($entry['rsvp']) { $act->content = $options['rendered']; } $act->verb = ActivityVerb::FAVORITE; if (strtolower($entry['rsvp'][0]) == 'yes') { $act->verb = 'http://activitystrea.ms/schema/1.0/rsvp-yes'; } else { if (strtolower($entry['rsvp'][0]) == 'no') { $act->verb = 'http://activitystrea.ms/schema/1.0/rsvp-no'; } else { if (strtolower($entry['rsvp'][0]) == 'maybe') { $act->verb = 'http://activitystrea.ms/schema/1.0/rsvp-maybe'; } } } $act->id = $source; $act->link = $entry['url'][0]; $options['source'] = 'linkback'; $options['mentions'] = $options['replies']; unset($options['reply_to']); unset($options['repeat_of']); try { $saved = Notice::saveActivity($act, $profile, $options); } catch (Exception $e) { common_log(LOG_ERR, "Linkback save of remote message {$source} failed: " . $e->getMessage()); return false; } common_log(LOG_INFO, "Linkback saved remote message {$source} as notice id {$saved->id}"); } else { // Fallback is to make a notice manually try { $saved = Notice::saveNew($profile->id, $content, 'linkback', $options); } catch (Exception $e) { common_log(LOG_ERR, "Linkback save of remote message {$source} failed: " . $e->getMessage()); return false; } common_log(LOG_INFO, "Linkback saved remote message {$source} as notice id {$saved->id}"); } } return $saved->getLocalUrl(); }