From 2e6a32c7bee6a47ddfe6b80f5769359f6d0979d4 Mon Sep 17 00:00:00 2001 From: Mikael Nordfeldth Date: Tue, 1 Jul 2014 23:25:58 +0200 Subject: [PATCH] Most of the activityobject-saving for Favorite implemented --- lib/activityhandlerplugin.php | 43 +++++++- plugins/Favorite/FavoritePlugin.php | 47 +++++++- plugins/Favorite/classes/Fave.php | 159 ++++++++++++++++++++++------ 3 files changed, 213 insertions(+), 36 deletions(-) diff --git a/lib/activityhandlerplugin.php b/lib/activityhandlerplugin.php index c8a46ae5ef..d972d17552 100644 --- a/lib/activityhandlerplugin.php +++ b/lib/activityhandlerplugin.php @@ -110,7 +110,8 @@ abstract class ActivityHandlerPlugin extends Plugin * and any additional data structures you require. * * This function is deprecated and in the future, Notice::saveActivity - * should be called from onStartHandleFeedEntryWithProfile in this class. + * should be called from onStartHandleFeedEntryWithProfile in this class + * (which instead turns to saveObjectFromActivity). * * @param Activity $activity * @param Profile $actor @@ -121,7 +122,8 @@ abstract class ActivityHandlerPlugin extends Plugin public function saveNoticeFromActivity(Activity $activity, Profile $actor, array $options=array()) { // Any plugin which has not implemented saveObjectFromActivity _must_ - // override this function (which will be deleted when all plugins are migrated). + // override this function until they are migrated (this function will + // be deleted when all plugins are migrated to saveObjectFromActivity). if (isset($this->oldSaveNew)) { throw new ServerException('A function has been called for new saveActivity functionality, but is still set with an oldSaveNew configuration'); @@ -130,6 +132,37 @@ abstract class ActivityHandlerPlugin extends Plugin return Notice::saveActivity($activity, $actor, $options); } + /** + * Given a parsed ActivityStreams activity, your plugin gets + * to figure out itself how to store the additional data into + * the database, besides the base data stored by the core. + * + * This will handle just about all events where an activity + * object gets saved, whether it is via AtomPub, OStatus + * (PuSH and Salmon transports), or ActivityStreams-based + * backup/restore of account data. + * + * You should be able to accept as input the output from an + * asActivity() call on the stored object. Where applicable, + * try to use existing ActivityStreams structures and object + * types, and be liberal in accepting input from what might + * be other compatible apps. + * + * All micro-app classes must override this method. + * + * @fixme are there any standard options? + * + * @param Activity $activity + * @param Profile $actor + * @param array $options=array() + * + * @return Notice the resulting notice + */ + public function saveObjectFromActivity(Activity $activity, Notice $stored, array $options=array()) + { + throw new ServerException('This function should be abstract when all plugins have migrated to saveObjectFromActivity'); + } + /* * This usually gets called from Notice::saveActivity after a Notice object has been created, * so it contains a proper id and a uri for the object to be saved. @@ -252,7 +285,11 @@ abstract class ActivityHandlerPlugin extends Plugin return true; } - $object = $this->activityObjectFromNotice($notice); + try { + $object = $this->activityObjectFromNotice($notice); + } catch (NoResultException $e) { + $object = null; // because getKV returns null on failure + } return false; } diff --git a/plugins/Favorite/FavoritePlugin.php b/plugins/Favorite/FavoritePlugin.php index 6248b332c4..18aa32738b 100644 --- a/plugins/Favorite/FavoritePlugin.php +++ b/plugins/Favorite/FavoritePlugin.php @@ -151,12 +151,53 @@ class FavoritePlugin extends ActivityHandlerPlugin { } + // FIXME: Set this to abstract public when all plugins have migrated! + public function saveObjectFromActivity(Activity $activity, Notice $stored, array $options=array()) + { + assert($this->isMyActivity($act)); + $actor = $stored->getProfile(); + $uris = array(); + if (count($act->objects) != 1) { + // TRANS: Exception thrown when a favor activity has anything but 1 object + throw new ClientException(_('Favoring activites can only be done one at a time')); + } + + $obj = $act->objects[0]; + $type = isset($obj->type) ? ActivityUtils::resolveUri($obj->type, true) : ActivityObject::NOTE; + $uris = $obj->getIdentifiers(); + try { + $local = ActivityUtils::findLocalObject($uris, $type); + } catch (Exception $e) { + // TODO: if it's not available locally, we should import the favorited object! + common_debug('Could not find favored notice locally: '.var_export($uris,true)); + return null; + } + + if (!empty($act->time)) { + // This should reasonably mean that it was created on our instance. + $options['created'] = common_sql_date($act->time); + } + + $options['uri'] = !empty($act->id) ? $act->id : $act->selfLink; + $object = Fave::saveNew($actor, $local, $type, $options); + return $object; + } + + public function activityObjectFromNotice(Notice $notice) { + $fave = Fave::fromStored($notice); + return $fave->asActivityObject(); } public function deleteRelated(Notice $notice) { + try { + $fave = Fave::fromStored($notice); + $fave->delete(); + } catch (NoResultException $e) { + // Cool, no problem. We wanted to get rid of it anyway. + } } // API stuff @@ -196,15 +237,15 @@ class FavoritePlugin extends ActivityHandlerPlugin { parent::onNoticeDeleteRelated($notice); - // The below algorithm is because we have faves not stored as - // proper activities in Notice from legacy versions of SN/GNU social + // The below algorithm is because we want to delete fave + // activities on any notice which _has_ faves, and not as + // in the parent function only ones that _are_ faves. $fave = new Fave(); $fave->notice_id = $notice->id; if ($fave->find()) { while ($fave->fetch()) { - Fave::blowCacheForProfileId($fave->user_id); $fave->delete(); } } diff --git a/plugins/Favorite/classes/Fave.php b/plugins/Favorite/classes/Fave.php index 089cafc884..0801ef8717 100644 --- a/plugins/Favorite/classes/Fave.php +++ b/plugins/Favorite/classes/Fave.php @@ -2,22 +2,16 @@ /** * Table Definition for fave */ -require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; class Fave extends Managed_DataObject { - ###START_AUTOCODE - /* the code below is auto generated do not remove the above tag */ - public $__table = 'fave'; // table name public $notice_id; // int(4) primary_key not_null public $user_id; // int(4) primary_key not_null public $uri; // varchar(255) + public $created; // datetime multiple_key not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP - /* the code above is auto generated do not remove the tag below */ - ###END_AUTOCODE - public static function schemaDef() { return array( @@ -25,6 +19,7 @@ class Fave extends Managed_DataObject 'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice that is the favorite'), 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who likes this notice'), 'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'), + 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), ), 'primary key' => array('notice_id', 'user_id'), @@ -61,11 +56,15 @@ class Fave extends Managed_DataObject $fave->user_id = $profile->id; $fave->notice_id = $notice->id; + $fave->created = common_sql_now(); $fave->modified = common_sql_now(); - $fave->uri = self::newURI($fave->user_id, - $fave->notice_id, - $fave->modified); - if (!$fave->insert()) { + $fave->uri = self::newURI($profile, + $notice, + $fave->created); + + try { + $fave->insert(); + } catch (ServerException $e) { common_log_db_error($fave, 'INSERT', __FILE__); return false; } @@ -79,7 +78,17 @@ class Fave extends Managed_DataObject return $fave; } - function delete($useWhere=false) + // exception throwing takeover! + public function insert() { + if (!parent::insert()) { + throw new ServerException(sprintf(_m('Could not store new object of type %s'), get_called_class())); + } + self::blowCacheForProfileId($this->user_id); + self::blowCacheForNoticeId($this->notice_id); + return $this; + } + + public function delete($useWhere=false) { $profile = Profile::getKV('id', $this->user_id); $notice = Notice::getKV('id', $this->notice_id); @@ -215,24 +224,6 @@ class Fave extends Managed_DataObject return $cnt; } - function getURI() - { - if (!empty($this->uri)) { - return $this->uri; - } else { - return self::newURI($this->user_id, $this->notice_id, $this->modified); - } - } - - static function newURI($profile_id, $notice_id, $modified) - { - return TagURI::mint('favor:%d:%d:%s', - $profile_id, - $notice_id, - common_date_iso8601($modified)); - } - - static protected $_faves = array(); /** @@ -276,4 +267,112 @@ class Fave extends Managed_DataObject $cache->delete(Cache::key('fave:list-ids:notice_id:'.$notice_id)); } } + + // Remember that we want the _activity_ notice here, not faves applied + // to the supplied Notice (as with byNotice)! + static public function fromStored(Notice $stored) { + $class = get_called_class(); + $object = new $class; + $object->uri = $stored->uri; + if (!$object->find(true)) { + throw new NoResultException($object); + } + return $object; + } + + static public function verbToTitle($verb) { + return ucfirst($verb); + } + + static public function object_type() + { + return 'activity'; + } + + public function asActivityObject(Profile $scoped=null) { + $actobj = new ActivityObject(); + $actobj->id = $this->getUri(); + $actobj->type = ActivityUtils::resolveUri(ActivityObject::ACTIVITY); + $actobj->actor = $this->getActorObject(); + $actobj->objects = array(clone($actobj->target)); + $actobj->title = Stored_ActivityVerb::verbToTitle($this->verb); + //$actobj->verb = $this->verb; + //$actobj->target = $this->getTargetObject(); + return $actobj; + } + + static public function parseActivityObject(ActivityObject $actobj, Notice $stored) { + $actor = $stored->getProfile(); + $object = new Fave(); + $object->user_id = $actor->id; + $object->notice_id = $stored->id; + $object->object_uri = $stored->uri; + $object->created = $stored->created; + return $object; + } + + static function saveActivityObject(ActivityObject $actobj, Notice $stored) { + $object = self::parseActivityObject($actobj, $stored); + $object->insert(); // exception throwing! + return $object; + } + + + public function getTarget() { + // throws exception on failure + return ActivityUtils::findLocalObject(array($this->uri), $this->type); + } + + public function getTargetObject() { + return $this->getTarget()->asActivityObject(); + } + + protected $_stored = array(); + + public function getStored() { + if (!isset($this->_stored[$this->uri])) { + $stored = new Notice(); + $stored->uri = $this->uri; + if (!$stored->find(true)) { + throw new NoResultException($stored); + } + $this->_stored[$this->uri] = $stored; + } + return $this->_stored[$this->uri]; + } + + public function getActor() { + $profile = new Profile(); + $profile->id = $this->user_id; + if (!$profile->find(true)) { + throw new NoResultException($profile); + } + return $profile; + } + + public function getActorObject() { + return $this->getActor()->asActivityObject(); + } + + public function getURI() + { + if (!empty($this->uri)) { + return $this->uri; + } + + // We (should've in this case) created it ourselves, so we tag it ourselves + return self::newURI($this->getActor(), $this->getTarget(), $this->created); + } + + static function newURI(Profile $actor, Managed_DataObject $target, $created=null) + { + if (is_null($created)) { + $created = common_sql_now(); + } + return TagURI::mint(strtolower(get_called_class()).':%d:%s:%d:%s', + $actor->id, + ActivityUtils::resolveUri(self::object_type(), true), + $target->id, + common_date_iso8601($created)); + } } -- 2.39.2