FavorAction now uses Notice::saveActivity

This commit is contained in:
Mikael Nordfeldth 2014-07-05 23:39:17 +02:00
parent aa33b7f21c
commit fffacaa27c
9 changed files with 166 additions and 187 deletions

View File

@ -647,54 +647,21 @@ class Notice extends Managed_DataObject
// XXX: some of these functions write to the DB // XXX: some of these functions write to the DB
$id = $notice->insert(); try {
$notice->insert(); // throws exception on failure
if (!$id) { } catch (Exception $e) {
common_log_db_error($notice, 'INSERT', __FILE__); // Let's test if we managed initial insert, which would imply
// TRANS: Server exception thrown when a notice cannot be saved. // failing on some update-part (check 'insert()'). Delete if
throw new ServerException(_('Problem saving notice.')); // something had been stored to the database.
} if (!empty($notice->id)) {
$notice->delete();
// Update ID-dependent columns: URI, conversation
$orig = clone($notice);
$changed = false;
// We can only get here if it's a local notice, since remote notices
// should've bailed out earlier due to lacking a URI.
if (empty($notice->uri)) {
$notice->uri = sprintf('%s%s=%d:%s=%s',
TagURI::mint(),
'noticeId', $notice->id,
'objectType', $notice->get_object_type(true));
$changed = true;
}
// If it's not part of a conversation, it's
// the beginning of a new conversation.
if (empty($notice->conversation)) {
$conv = Conversation::create($notice);
$notice->conversation = $conv->id;
$changed = true;
}
if ($changed) {
if ($notice->update($orig) === false) {
common_log_db_error($notice, 'UPDATE', __FILE__);
// TRANS: Server exception thrown when a notice cannot be updated.
throw new ServerException(_('Problem saving notice.'));
} }
} }
} }
// Clear the cache for subscribed users, so they'll update at next request // Clear the cache for subscribed users, so they'll update at next request
// XXX: someone clever could prepend instead of clearing the cache // XXX: someone clever could prepend instead of clearing the cache
$notice->blowOnInsert();
// Save per-notice metadata... // Save per-notice metadata...
if (isset($replies)) { if (isset($replies)) {
@ -905,6 +872,9 @@ class Notice extends Managed_DataObject
throw $e; throw $e;
} }
} }
if (!$stored instanceof Notice) {
throw new ServerException('StartNoticeSave did not give back a Notice');
}
// Save per-notice metadata... // Save per-notice metadata...
$mentions = array(); $mentions = array();
@ -1798,7 +1768,7 @@ class Notice extends Managed_DataObject
$act->objects[] = $repeated->asActivity($cur); $act->objects[] = $repeated->asActivity($cur);
} }
} else { } else {
$act->objects[] = ActivityObject::fromNotice($this); $act->objects[] = $this->asActivityObject();
} }
// XXX: should this be handled by default processing for object entry? // XXX: should this be handled by default processing for object entry?
@ -2005,10 +1975,29 @@ class Notice extends Managed_DataObject
function asActivityNoun($element) function asActivityNoun($element)
{ {
$noun = ActivityObject::fromNotice($this); $noun = $this->asActivityObject();
return $noun->asString('activity:' . $element); return $noun->asString('activity:' . $element);
} }
public function asActivityObject()
{
$object = new ActivityObject();
if (Event::handle('StartActivityObjectFromNotice', array($this, &$object))) {
$object->type = $this->object_type ?: ActivityObject::NOTE;
$object->id = $this->getUri();
$object->title = sprintf('New %1$s by %2$s', $object->type, $this->getProfile()->getNickname());
$object->content = $this->rendered;
$object->link = $this->getUrl();
$object->extra[] = array('status_net', array('notice_id' => $this->id));
Event::handle('EndActivityObjectFromNotice', array($this, &$object));
}
return $object;
}
/** /**
* Determine which notice, if any, a new notice is in reply to. * Determine which notice, if any, a new notice is in reply to.
* *
@ -2335,20 +2324,55 @@ class Notice extends Managed_DataObject
{ {
$result = parent::insert(); $result = parent::insert();
if ($result !== false) { if ($result === false) {
// Profile::hasRepeated() abuses pkeyGet(), so we common_log_db_error($this, 'INSERT', __FILE__);
// have to clear manually // TRANS: Server exception thrown when a stored object entry cannot be saved.
if (!empty($this->repeat_of)) { throw new ServerException('Could not save Notice');
$c = self::memcache(); }
if (!empty($c)) {
$ck = self::multicacheKey('Notice', // Profile::hasRepeated() abuses pkeyGet(), so we
array('profile_id' => $this->profile_id, // have to clear manually
'repeat_of' => $this->repeat_of)); if (!empty($this->repeat_of)) {
$c->delete($ck); $c = self::memcache();
} if (!empty($c)) {
$ck = self::multicacheKey('Notice',
array('profile_id' => $this->profile_id,
'repeat_of' => $this->repeat_of));
$c->delete($ck);
} }
} }
// Update possibly ID-dependent columns: URI, conversation
// (now that INSERT has added the notice's local id)
$orig = clone($this);
$changed = false;
// We can only get here if it's a local notice, since remote notices
// should've bailed out earlier due to lacking a URI.
if (empty($this->uri)) {
$this->uri = sprintf('%s%s=%d:%s=%s',
TagURI::mint(),
'noticeId', $this->id,
'objectType', $this->get_object_type(true));
$changed = true;
}
// If it's not part of a conversation, it's
// the beginning of a new conversation.
if (empty($this->conversation)) {
$conv = Conversation::create($this);
$this->conversation = $conv->id;
$changed = true;
}
if ($changed && $this->update($orig) === false) {
common_log_db_error($notice, 'UPDATE', __FILE__);
// TRANS: Server exception thrown when a notice cannot be updated.
throw new ServerException(_('Problem saving notice.'));
}
$this->blowOnInsert();
return $result; return $result;
} }

View File

@ -431,32 +431,6 @@ class ActivityObject
} }
} }
static function fromNotice(Notice $notice)
{
$object = new ActivityObject();
if (Event::handle('StartActivityObjectFromNotice', array($notice, &$object))) {
$object->type = (empty($notice->object_type)) ? ActivityObject::NOTE : $notice->object_type;
$object->id = $notice->uri;
$object->title = 'New ' . self::canonicalType($object->type) . ' by ';
try {
$object->title .= $notice->getProfile()->getAcctUri();
} catch (ProfileNoAcctUriException $e) {
$object->title .= $e->profile->nickname;
}
$object->content = $notice->rendered;
$object->link = $notice->getUrl();
$object->extra[] = array('status_net', array('notice_id' => $notice->id));
Event::handle('EndActivityObjectFromNotice', array($notice, &$object));
}
return $object;
}
static function fromGroup(User_group $group) static function fromGroup(User_group $group)
{ {
$object = new ActivityObject(); $object = new ActivityObject();

View File

@ -433,4 +433,14 @@ class ActivityUtils
return $profile; return $profile;
} }
static public function typeToTitle($type)
{
return ucfirst(self::resolveUri($type, true));
}
static public function verbToTitle($verb)
{
return ucfirst(self::resolveUri($verb, true));
}
} }

View File

@ -142,50 +142,6 @@ class ActivityPlugin extends Plugin
return true; return true;
} }
function onEndFavorNotice($profile, $notice)
{
// Only do this if config is enabled
if(!$this->StartLike) return true;
if (!$profile->isLocal()) {
return true;
}
$author = $notice->getProfile();
$fave = Fave::pkeyGet(array('user_id' => $profile->id,
'notice_id' => $notice->id));
// TRANS: Text for "liked" item in activity plugin.
// TRANS: %1$s is a profile URL, %2$s is a profile name,
// TRANS: %3$s is a notice URL, %4$s is an author name.
$rendered = sprintf(_m('<a href="%1$s">%2$s</a> liked <a href="%3$s">%4$s\'s update</a>.'),
$profile->getUrl(),
$profile->getBestName(),
$notice->getUrl(),
$author->getBestName());
// TRANS: Text for "liked" item in activity plugin.
// TRANS: %1$s is a profile name, %2$s is a profile URL,
// TRANS: %3$s is an author name, %4$s is a notice URL.
$content = sprintf(_m('%1$s (%2$s) liked %3$s\'s status (%4$s).'),
$profile->getBestName(),
$profile->getUrl(),
$author->getBestName(),
$notice->getUrl());
$notice = Notice::saveNew($profile->id,
$content,
ActivityPlugin::SOURCE,
array('rendered' => $rendered,
'urls' => array(),
'replies' => array($author->getUri()),
'uri' => $fave->getURI(),
'verb' => ActivityVerb::FAVORITE,
'object_type' => (($notice->verb == ActivityVerb::POST) ?
$notice->object_type : ActivityObject::ACTIVITY)));
return true;
}
function onEndDisfavorNotice($profile, $notice) function onEndDisfavorNotice($profile, $notice)
{ {
// Only do this if config is enabled // Only do this if config is enabled

View File

@ -63,7 +63,7 @@ class FavoritePlugin extends ActivityHandlerPlugin
' modified = "%s" '. ' modified = "%s" '.
'WHERE user_id = %d '. 'WHERE user_id = %d '.
'AND notice_id = %d', 'AND notice_id = %d',
Fave::newURI($fave->user_id, $fave->notice_id, $fave->modified), Fave::newUri($fave->user_id, $fave->notice_id, $fave->modified),
common_sql_date(strtotime($fave->modified)), common_sql_date(strtotime($fave->modified)),
$fave->user_id, $fave->user_id,
$fave->notice_id)); $fave->notice_id));
@ -171,7 +171,6 @@ class FavoritePlugin extends ActivityHandlerPlugin
// more explicit catch-statement. // more explicit catch-statement.
return null; return null;
} }
common_debug(get_called_class().' returning '.get_class($object).' object with uri: '.$object->uri);
return $object; return $object;
} }

View File

@ -133,7 +133,7 @@ class AtompubfavoritefeedAction extends ApiAuthAction
$feed->setUpdated('now'); $feed->setUpdated('now');
$feed->addAuthor($this->_profile->getBestName(), $feed->addAuthor($this->_profile->getBestName(),
$this->_profile->getURI()); $this->_profile->getUri());
// TRANS: Title for Atom favorites feed. // TRANS: Title for Atom favorites feed.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.

View File

@ -48,40 +48,61 @@ class FavorAction extends FormAction
{ {
protected $needPost = true; protected $needPost = true;
protected $object = null;
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->target = Notice::getKV($this->trimmed('notice'));
if (!$this->target instanceof Notice) {
throw new ServerException(_m('No such notice.'));
}
if (!$this->target->inScope($this->scoped)) {
// TRANS: Client error displayed when trying to reply to a notice a the target has no access to.
// TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
throw new ClientException(sprintf(_m('%1$s has no right to reply to notice %2$d.'), $this->scoped->getNickname(), $this->target->id), 403);
}
return true;
}
protected function handlePost() protected function handlePost()
{ {
$id = $this->trimmed('notice'); parent::handlePost();
$notice = Notice::getKV($id);
if (!($notice instanceof Notice)) { if (Fave::existsForProfile($this->target, $this->scoped)) {
$this->serverError(_('Notice not found'));
}
if (Fave::existsForProfile($notice, $this->scoped)) {
// TRANS: Client error displayed when trying to mark a notice as favorite that already is a favorite. // TRANS: Client error displayed when trying to mark a notice as favorite that already is a favorite.
throw new AlreadyFulfilledException(_('This notice is already a favorite!')); throw new AlreadyFulfilledException(_('You have already favorited this!'));
} }
$fave = Fave::addNew($this->scoped, $notice);
if (!$fave instanceof Fave) { $now = common_sql_now();
// TRANS: Server error displayed when trying to mark a notice as favorite fails in the database.
$this->serverError(_('Could not create favorite.')); $act = new Activity();
} $act->id = Fave::newUri($this->scoped, $this->target, $now);
$this->notify($notice, $this->scoped->getUser()); $act->type = Fave::getObjectType();
$act->actor = $this->scoped->asActivityObject();
$act->target = $this->target->asActivityObject();
$act->objects = array(clone($act->target));
$act->verb = ActivityVerb::FAVORITE;
$act->title = ActivityUtils::verbToTitle($act->verb);
$act->time = strtotime($now);
$stored = Notice::saveActivity($act, $this->scoped,
array('uri'=>$act->id));
$this->notify($stored, $this->scoped->getUser());
Fave::blowCacheForProfileId($this->scoped->id); Fave::blowCacheForProfileId($this->scoped->id);
if (StatusNet::isAjax()) {
$this->startHTML('text/xml;charset=utf-8'); return _('Favorited the notice');
$this->elementStart('head'); }
// TRANS: Page title for page on which favorite notices can be unfavourited.
$this->element('title', null, _('Disfavor favorite.')); protected function showContent()
$this->elementEnd('head'); {
$this->elementStart('body'); if ($this->target instanceof Notice) {
$disfavor = new DisFavorForm($this, $notice); $disfavor = new DisfavorForm($this, $this->target);
$disfavor->show(); $disfavor->show();
$this->elementEnd('body');
$this->endHTML();
exit;
} }
common_redirect(common_local_url('showfavorites',
array('nickname' => $this->scoped->nickname)),
303);
} }
/** /**

View File

@ -58,7 +58,7 @@ class Fave extends Managed_DataObject
$fave->notice_id = $notice->id; $fave->notice_id = $notice->id;
$fave->created = common_sql_now(); $fave->created = common_sql_now();
$fave->modified = common_sql_now(); $fave->modified = common_sql_now();
$fave->uri = self::newURI($profile, $fave->uri = self::newUri($profile,
$notice, $notice,
$fave->created); $fave->created);
@ -128,17 +128,8 @@ class Fave extends Managed_DataObject
function asActivity() function asActivity()
{ {
$notice = Notice::getKV('id', $this->notice_id); $notice = $this->getTarget();
$profile = $this->getActor();
if (!$notice) {
throw new Exception("Fave for non-existent notice: " . $this->notice_id);
}
$profile = Profile::getKV('id', $this->user_id);
if (!$profile) {
throw new Exception("Fave by non-existent profile: " . $this->user_id);
}
$act = new Activity(); $act = new Activity();
@ -146,7 +137,7 @@ class Fave extends Managed_DataObject
// FIXME: rationalize this with URL below // FIXME: rationalize this with URL below
$act->id = $this->getURI(); $act->id = $this->getUri();
$act->time = strtotime($this->modified); $act->time = strtotime($this->modified);
// TRANS: Activity title when marking a notice as favorite. // TRANS: Activity title when marking a notice as favorite.
@ -158,11 +149,13 @@ class Fave extends Managed_DataObject
$notice->getUrl()); $notice->getUrl());
$act->actor = $profile->asActivityObject(); $act->actor = $profile->asActivityObject();
$act->objects[] = ActivityObject::fromNotice($notice); // $act->target = $notice->asActivityObject();
// $act->objects = array(clone($act->target));
$act->objects[] = $notice->asActivityObject();
$url = common_local_url('AtomPubShowFavorite', $url = common_local_url('AtomPubShowFavorite',
array('profile' => $this->user_id, array('profile' => $profile->id,
'notice' => $this->notice_id)); 'notice' => $notice->id));
$act->selfLink = $url; $act->selfLink = $url;
$act->editLink = $url; $act->editLink = $url;
@ -283,11 +276,6 @@ class Fave extends Managed_DataObject
return $object; return $object;
} }
static public function verbToTitle($verb)
{
return ucfirst($verb);
}
static public function getObjectType() static public function getObjectType()
{ {
return 'activity'; return 'activity';
@ -297,22 +285,22 @@ class Fave extends Managed_DataObject
{ {
$actobj = new ActivityObject(); $actobj = new ActivityObject();
$actobj->id = $this->getUri(); $actobj->id = $this->getUri();
$actobj->type = ActivityUtils::resolveUri($this->getObjectType()); $actobj->type = ActivityUtils::resolveUri(self::getObjectType());
$actobj->actor = $this->getActorObject(); $actobj->actor = $this->getActorObject();
$actobj->target = $this->getTarget()->asActivityObject(); $actobj->target = $this->getTarget()->asActivityObject();
$actobj->objects = array(clone($actobj->target)); $actobj->objects = array(clone($actobj->target));
$actobj->title = Stored_ActivityVerb::verbToTitle($this->verb);
$actobj->verb = ActivityVerb::FAVORITE; $actobj->verb = ActivityVerb::FAVORITE;
$actobj->title = ActivityUtils::verbToTitle($actobj->verb);
return $actobj; return $actobj;
} }
/**
* @param ActivityObject $actobj The _favored_ notice (which we're "in-reply-to")
* @param Notice $stored The _activity_ notice, i.e. the favor itself.
*/
static public function parseActivityObject(ActivityObject $actobj, Notice $stored) static public function parseActivityObject(ActivityObject $actobj, Notice $stored)
{ {
// The ActivityObject we get here is the _favored_ notice (kind of what we're "in-reply-to") $local = ActivityUtils::findLocalObject($actobj->getIdentifiers());
// The Notice we get is the _activity_ stored in our Notice table
$type = isset($actobj->type) ? ActivityUtils::resolveUri($actobj->type, true) : ActivityObject::NOTE;
$local = ActivityUtils::findLocalObject($actobj->getIdentifiers(), $type);
if (!$local instanceof Notice) { if (!$local instanceof Notice) {
// $local always returns something, but this was not what we expected. Something is wrong. // $local always returns something, but this was not what we expected. Something is wrong.
throw new Exception('Something other than a Notice was returned from findLocalObject'); throw new Exception('Something other than a Notice was returned from findLocalObject');
@ -332,6 +320,7 @@ class Fave extends Managed_DataObject
{ {
$object = self::parseActivityObject($actobj, $stored); $object = self::parseActivityObject($actobj, $stored);
$object->insert(); // exception throwing! $object->insert(); // exception throwing!
Event::handle('EndFavorNotice', array($stored->getProfile(), $object->getTarget()));
return $object; return $object;
} }
@ -339,7 +328,13 @@ class Fave extends Managed_DataObject
public function getTarget() public function getTarget()
{ {
// throws exception on failure // throws exception on failure
return ActivityUtils::findLocalObject(array($this->uri)); $target = new Notice();
$target->id = $this->notice_id;
if (!$target->find(true)) {
throw new NoResultException($target);
}
return $target;
} }
public function getTargetObject() public function getTargetObject()
@ -377,17 +372,17 @@ class Fave extends Managed_DataObject
return $this->getActor()->asActivityObject(); return $this->getActor()->asActivityObject();
} }
public function getURI() public function getUri()
{ {
if (!empty($this->uri)) { if (!empty($this->uri)) {
return $this->uri; return $this->uri;
} }
// We (should've in this case) created it ourselves, so we tag it ourselves // We (should've in this case) created it ourselves, so we tag it ourselves
return self::newURI($this->getActor(), $this->getTarget(), $this->created); return self::newUri($this->getActor(), $this->getTarget(), $this->created);
} }
static function newURI(Profile $actor, Managed_DataObject $target, $created=null) static function newUri(Profile $actor, Managed_DataObject $target, $created=null)
{ {
if (is_null($created)) { if (is_null($created)) {
$created = common_sql_now(); $created = common_sql_now();

View File

@ -1044,7 +1044,7 @@ class OStatusPlugin extends Plugin
$notice->getUrl()); $notice->getUrl());
$act->actor = $profile->asActivityObject(); $act->actor = $profile->asActivityObject();
$act->object = ActivityObject::fromNotice($notice); $act->object = $notice->asActivityObject();
$oprofile->notifyActivity($act, $profile); $oprofile->notifyActivity($act, $profile);