'reply_to' => array('type' => 'int', 'description' => 'notice replied to (usually a guess)'),
'is_local' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'notice was generated by a user'),
'source' => array('type' => 'varchar', 'length' => 32, 'description' => 'source of comment, like "web", "im", or "clientname"'),
- 'conversation' => array('type' => 'int', 'description' => 'id of root notice in this conversation'),
+ 'conversation' => array('type' => 'int', 'description' => 'the local numerical conversation id'),
'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'),
'object_type' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams object type', 'default' => null),
'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
public function getRendered()
{
- if (is_null($this->rendered) || $this->rendered === '') {
+ // we test $this->id because if it's not inserted yet, we can't update the field
+ if (!empty($this->id) && (is_null($this->rendered) || $this->rendered === '')) {
// update to include rendered content on-the-fly, so we don't have to have a fix-up script in upgrade.php
common_debug('Rendering notice '.$this->getID().' as it had no rendered HTML content.');
$orig = clone($this);
return $this->rendered;
}
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ public function getVerb($make_relative=false)
+ {
+ return ActivityUtils::resolveUri($this->verb, $make_relative);
+ }
+
+ public function isVerb(array $verbs)
+ {
+ return ActivityUtils::compareVerbs($this->getVerb(), $verbs);
+ }
+
/*
* Get the original representation URL of this notice.
*
}
public function getObjectType($canonical=false) {
+ if (is_null($this->object_type) || $this->object_type==='') {
+ throw new NoObjectTypeException($this);
+ }
return ActivityUtils::resolveUri($this->object_type, $canonical);
}
+ public function isObjectType(array $types)
+ {
+ try {
+ return ActivityUtils::compareTypes($this->getObjectType(), $types);
+ } catch (NoObjectTypeException $e) {
+ return false;
+ }
+ }
+
public static function getByUri($uri)
{
$notice = new Notice();
$options['uri'] = $act->id;
$options['url'] = $act->link;
} else {
- $actobj = count($act->objects)==1 ? $act->objects[0] : null;
+ $actobj = count($act->objects)===1 ? $act->objects[0] : null;
if (!is_null($actobj) && !empty($actobj->id)) {
$options['uri'] = $actobj->id;
if ($actobj->link) {
$stored->url = $url;
$stored->verb = $act->verb;
- // Notice content. We trust local users to provide HTML we like, but of course not remote users.
- // FIXME: What about local users importing feeds? Mirror functions must filter out bad HTML first...
$content = $act->content ?: $act->summary;
if (is_null($content) && !is_null($actobj)) {
$content = $actobj->content ?: $actobj->summary;
}
- $stored->rendered = $actor->isLocal() ? $content : common_purify($content);
- // yeah, just don't use getRendered() here since it's not inserted yet ;)
- $stored->content = common_strip_html($stored->rendered);
+ // Strip out any bad HTML
+ $stored->rendered = common_purify($content);
+ $stored->content = common_strip_html($stored->getRendered(), true, true);
+ if (trim($stored->content) === '') {
+ // TRANS: Error message when the plain text content of a notice has zero length.
+ throw new ClientException(_('Empty notice content, will not save this.'));
+ }
// Maybe a missing act-time should be fatal if the actor is not local?
if (!empty($act->time)) {
$object = null;
Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
if (empty($object)) {
- throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->getUri() . ': '.$act->asString());
+ throw new NoticeSaveException('Unsuccessful call to StoreActivityObject '._ve($stored->getUri()) . ': '._ve($act->asString()));
}
// If something changed in the Notice during StoreActivityObject
// reasonably handle notifications themselves.
if (ActivityUtils::compareVerbs($stored->verb, array(ActivityVerb::POST))) {
- $stored->saveAttentions($act->context->attention);
-
if (!empty($tags)) {
$stored->saveKnownTags($tags);
} else {
// Note: groups may save tags, so must be run after tags are saved
// to avoid errors on duplicates.
- // Note: groups should always be set.
+ $stored->saveAttentions($act->context->attention);
if (!empty($urls)) {
$stored->saveKnownUrls($urls);
continue;
}
- $this->saveAttention($target);
+ try {
+ $this->saveAttention($target);
+ } catch (AlreadyFulfilledException $e) {
+ common_debug('Attention already exists: '.var_export($e->getMessage(),true));
+ } catch (Exception $e) {
+ common_log(LOG_ERR, "Could not save notice id=={$this->getID()} attention for profile id=={$target->getID()}: {$e->getMessage()}");
+ }
}
}
+ /**
+ * Saves an attention for a profile (user or group) which means
+ * it shows up in their home feed and such.
+ */
function saveAttention(Profile $target, $reason=null)
{
if ($target->isGroup()) {
- // FIXME: Make sure we check that users are in the groups they send to!
+ // FIXME: Make sure we check (for both local and remote) users are in the groups they send to!
+
+ // legacy notification method, will still be in use for quite a while I think
+ $this->addToGroupInbox($target->getGroup());
} else {
if ($target->hasBlocked($this->getProfile())) {
common_log(LOG_INFO, "Not saving reply to profile {$target->id} ($uri) from sender {$sender->id} because of a block.");
}
if ($target->isLocal()) {
- // is local user
- $this->saveReply($target->getID()); // since we still have the Reply table which some apparently use!
+ // legacy notification method, will still be in use for quite a while I think
+ $this->saveReply($target->getID());
}
- try {
- $att = Attention::saveNew($this, $target, $reason);
- } catch (AlreadyFulfilledException $e) {
- common_debug('Could not save Attention: '.$e->getMessage());
- } catch (Exception $e) {
- common_log(LOG_ERR, 'Could not save Attention: '.$e->getMessage());
- }
+ $att = Attention::saveNew($this, $target, $reason);
self::blow('reply:stream:%d', $target->getID());
return true;
}
}
- $reply_ids = $this->getReplies();
-
- foreach ($reply_ids as $id) {
- $rprofile = Profile::getKV('id', $id);
- if ($rprofile instanceof Profile) {
- $ctx->attention[$rprofile->getUri()] = ActivityObject::PERSON;
- }
- }
-
- $groups = $this->getGroups();
-
- foreach ($groups as $group) {
- $ctx->attention[$group->getUri()] = ActivityObject::GROUP;
+ // 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) {
Event::handle('EndActivityObjectFromNotice', array($this, &$object));
}
+ if (!$object instanceof ActivityObject) {
+ common_log(LOG_ERR, 'Notice asActivityObject created something else for uri=='._ve($this->getUri()).': '._ve($object));
+ throw new ServerException('Notice asActivityObject created something else.');
+ }
+
return $object;
}
*/
public static function getAsTimestamp($id)
{
- if (!$id) {
- return false;
+ if (empty($id)) {
+ throw new EmptyIdException('Notice');
}
- $notice = Notice::getKV('id', $id);
- if ($notice) {
- return $notice->created;
+ $timestamp = null;
+ if (Event::handle('GetNoticeSqlTimestamp', array($id, &$timestamp))) {
+ // getByID throws exception if $id isn't found
+ $notice = Notice::getByID($id);
+ $timestamp = $notice->created;
}
- $deleted = Deleted_notice::getKV('id', $id);
- if ($deleted) {
- return $deleted->created;
+ if (empty($timestamp)) {
+ throw new ServerException('No timestamp found for Notice with id=='._ve($id));
}
-
- return false;
+ return $timestamp;
}
/**
*/
public static function whereSinceId($id, $idField='id', $createdField='created')
{
- $since = Notice::getAsTimestamp($id);
- if ($since) {
- return sprintf("($createdField = '%s' and $idField > %d) or ($createdField > '%s')", $since, $id, $since);
+ try {
+ $since = Notice::getAsTimestamp($id);
+ } catch (Exception $e) {
+ return false;
}
- return false;
+ return sprintf("($createdField = '%s' and $idField > %d) or ($createdField > '%s')", $since, $id, $since);
}
/**
*/
public static function whereMaxId($id, $idField='id', $createdField='created')
{
- $max = Notice::getAsTimestamp($id);
- if ($max) {
- return sprintf("($createdField < '%s') or ($createdField = '%s' and $idField <= %d)", $max, $max, $id);
+ try {
+ $max = Notice::getAsTimestamp($id);
+ } catch (Exception $e) {
+ return false;
}
- return false;
+ return sprintf("($createdField < '%s') or ($createdField = '%s' and $idField <= %d)", $max, $max, $id);
}
/**