* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* @package OStatusPlugin
- * @maintainer Brion Vibber <brion@status.net>
+ * @author Brion Vibber <brion@status.net>
+ * @maintainer Mikael Nordfeldth <mmn@hethane.se>
*/
class Ostatus_profile extends Managed_DataObject
{
{
return array(
'fields' => array(
- 'uri' => array('type' => 'varchar', 'length' => 255, 'not null' => true),
+ 'uri' => array('type' => 'varchar', 'length' => 191, 'not null' => true),
'profile_id' => array('type' => 'integer'),
'group_id' => array('type' => 'integer'),
'peopletag_id' => array('type' => 'integer'),
- 'feeduri' => array('type' => 'varchar', 'length' => 255),
- 'salmonuri' => array('type' => 'varchar', 'length' => 255),
+ 'feeduri' => array('type' => 'varchar', 'length' => 191),
+ 'salmonuri' => array('type' => 'varchar', 'length' => 191),
'avatar' => array('type' => 'text'),
'created' => array('type' => 'datetime', 'not null' => true),
'modified' => array('type' => 'datetime', 'not null' => true),
return $this->uri;
}
+ public function fromProfile(Profile $profile)
+ {
+ $oprofile = Ostatus_profile::getKV('profile_id', $profile->id);
+ if (!$oprofile instanceof Ostatus_profile) {
+ throw new Exception('No Ostatus_profile for Profile ID: '.$profile->id);
+ }
+ }
+
/**
* Fetch the locally stored profile for this feed
* @return Profile
*/
public function localProfile()
{
+ if ($this->isGroup()) {
+ return $this->localGroup()->getProfile();
+ }
+
$profile = Profile::getKV('id', $this->profile_id);
- if ($profile instanceof Profile) {
- return $profile;
+ if (!$profile instanceof Profile) {
+ throw new NoProfileException($this->profile_id);
}
- throw new NoProfileException($this->profile_id);
+ return $profile;
}
/**
*/
public function localGroup()
{
- if ($this->group_id) {
- return User_group::getKV('id', $this->group_id);
+ $group = User_group::getKV('id', $this->group_id);
+
+ if (!$group instanceof User_group) {
+ throw new NoSuchGroupException(array('id'=>$this->group_id));
}
- return null;
+
+ return $group;
}
/**
$xml = $entry->getString();
common_log(LOG_INFO, "Posting to Salmon endpoint $this->salmonuri: $xml");
- Salmon::post($this->salmonuri, $xml, $actor->getUser());
+ Salmon::post($this->salmonuri, $xml, $actor);
}
/**
public function notifyActivity($entry, Profile $actor)
{
if ($this->salmonuri) {
- return Salmon::post($this->salmonuri, $this->notifyPrepXml($entry), $actor->getUser());
+ return Salmon::post($this->salmonuri, $this->notifyPrepXml($entry), $actor, $this->localProfile());
}
common_debug(__CLASS__.' error: No salmonuri for Ostatus_profile uri: '.$this->uri);
if ($this->salmonuri) {
$data = array('salmonuri' => $this->salmonuri,
'entry' => $this->notifyPrepXml($entry),
- 'actor' => $actor->id);
+ 'actor' => $actor->getID(),
+ 'target' => $this->localProfile()->getID());
$qm = QueueManager::get();
return $qm->enqueue($data, 'salmon');
return;
}
- for ($i = 0; $i < $entries->length; $i++) {
- $entry = $entries->item($i);
- $this->processEntry($entry, $feed, $source);
- }
+ $this->processEntries($entries, $feed, $source);
}
public function processRssFeed(DOMElement $rss, $source)
$items = $channel->getElementsByTagName('item');
- for ($i = 0; $i < $items->length; $i++) {
- $item = $items->item($i);
- $this->processEntry($item, $channel, $source);
+ $this->processEntries($items, $channel, $source);
+ }
+
+ public function processEntries(DOMNodeList $entries, DOMElement $feed, $source)
+ {
+ for ($i = 0; $i < $entries->length; $i++) {
+ $entry = $entries->item($i);
+ try {
+ $this->processEntry($entry, $feed, $source);
+ } catch (AlreadyFulfilledException $e) {
+ common_debug('We already had this entry: '.$e->getMessage());
+ }
}
}
*
* @return Notice Notice representing the new (or existing) activity
*/
- public function processEntry($entry, $feed, $source)
+ public function processEntry(DOMElement $entry, DOMElement $feed, $source)
{
$activity = new Activity($entry, $feed);
return $this->processActivity($activity, $source);
}
// TODO: Make this throw an exception
- public function processActivity($activity, $source)
+ public function processActivity(Activity $activity, $source)
{
$notice = null;
if (Event::handle('StartHandleFeedEntryWithProfile', array($activity, $this->localProfile(), &$notice)) &&
Event::handle('StartHandleFeedEntry', array($activity))) {
- switch ($activity->verb) {
- case ActivityVerb::POST:
- // @todo process all activity objects
- switch ($activity->objects[0]->type) {
- case ActivityObject::ARTICLE:
- case ActivityObject::BLOGENTRY:
- case ActivityObject::NOTE:
- case ActivityObject::STATUS:
- case ActivityObject::COMMENT:
- case null:
- $notice = $this->processPost($activity, $source);
- break;
- default:
- // TRANS: Client exception.
- throw new ClientException(_m('Cannot handle that kind of post.'));
- }
- break;
- case ActivityVerb::SHARE:
- $notice = $this->processShare($activity, $source);
- break;
- default:
- common_log(LOG_INFO, "Ignoring activity with unrecognized verb $activity->verb");
- }
+ common_log(LOG_INFO, "Ignoring activity with unrecognized verb $activity->verb");
Event::handle('EndHandleFeedEntry', array($activity));
Event::handle('EndHandleFeedEntryWithProfile', array($activity, $this, $notice));
return $notice;
}
- public function processShare($activity, $method)
- {
- $notice = null;
-
- try {
- $profile = ActivityUtils::checkAuthorship($activity, $this->localProfile());
- } catch (ServerException $e) {
- return null;
- }
-
- // The id URI will be used as a unique identifier for the notice,
- // protecting against duplicate saves. It isn't required to be a URL;
- // tag: URIs for instance are found in Google Buzz feeds.
- $dupe = Notice::getKV('uri', $activity->id);
- if ($dupe instanceof Notice) {
- common_log(LOG_INFO, "OStatus: ignoring duplicate post: {$activity->id}");
- return $dupe;
- }
-
- if (count($activity->objects) != 1) {
- // TRANS: Client exception thrown when trying to share multiple activities at once.
- throw new ClientException(_m('Can only handle share activities with exactly one object.'));
- }
-
- $shared = $activity->objects[0];
-
- if (!$shared instanceof Activity) {
- // TRANS: Client exception thrown when trying to share a non-activity object.
- throw new ClientException(_m('Can only handle shared activities.'));
- }
-
- $sharedId = $shared->id;
- if (!empty($shared->objects[0]->id)) {
- // Because StatusNet since commit 8cc4660 sets $shared->id to a TagURI which
- // fucks up federation, because the URI is no longer recognised by the origin.
- // So we set it to the object ID if it exists, otherwise we trust $shared->id
- $sharedId = $shared->objects[0]->id;
- }
- if (empty($sharedId)) {
- throw new ClientException(_m('Shared activity does not have an id'));
- }
-
- // First check if we have the shared activity. This has to be done first, because
- // we can't use these functions to "ensureActivityObjectProfile" of a local user,
- // who might be the creator of the shared activity in question.
- $sharedNotice = Notice::getKV('uri', $sharedId);
- if (!$sharedNotice instanceof Notice) {
- // If no locally stored notice is found, process it!
- // TODO: Remember to check Deleted_notice!
- // TODO: If a post is shared that we can't retrieve - what to do?
- try {
- $other = self::ensureActivityObjectProfile($shared->actor);
- $sharedNotice = $other->processActivity($shared, $method);
- if (!$sharedNotice instanceof Notice) {
- // And if we apparently can't get the shared notice, we'll abort the whole thing.
- // TRANS: Client exception thrown when saving an activity share fails.
- // TRANS: %s is a share ID.
- throw new ClientException(sprintf(_m('Failed to save activity %s.'), $sharedId));
- }
- } catch (FeedSubException $e) {
- // Remote feed could not be found or verified, should we
- // transform this into an "RT @user Blah, blah, blah..."?
- common_log(LOG_INFO, __METHOD__ . ' got a ' . get_class($e) . ': ' . $e->getMessage());
- return null;
- }
- }
-
- // We'll want to save a web link to the original notice, if provided.
-
- $sourceUrl = null;
- if ($activity->link) {
- $sourceUrl = $activity->link;
- } else if ($activity->link) {
- $sourceUrl = $activity->link;
- } else if (preg_match('!^https?://!', $activity->id)) {
- $sourceUrl = $activity->id;
- }
-
- // Use summary as fallback for content
-
- if (!empty($activity->content)) {
- $sourceContent = $activity->content;
- } else if (!empty($activity->summary)) {
- $sourceContent = $activity->summary;
- } else if (!empty($activity->title)) {
- $sourceContent = $activity->title;
- } else {
- // @todo FIXME: Fetch from $sourceUrl?
- // TRANS: Client exception. %s is a source URI.
- throw new ClientException(sprintf(_m('No content for notice %s.'), $activity->id));
- }
-
- // Get (safe!) HTML and text versions of the content
-
- $rendered = $this->purify($sourceContent);
- $content = common_strip_html($rendered);
-
- $shortened = common_shorten_links($content);
-
- // If it's too long, try using the summary, and make the
- // HTML an attachment.
-
- $attachment = null;
-
- if (Notice::contentTooLong($shortened)) {
- $attachment = $this->saveHTMLFile($activity->title, $rendered);
- $summary = common_strip_html($activity->summary);
- if (empty($summary)) {
- $summary = $content;
- }
- $shortSummary = common_shorten_links($summary);
- if (Notice::contentTooLong($shortSummary)) {
- $url = common_shorten_url($sourceUrl);
- $shortSummary = substr($shortSummary,
- 0,
- Notice::maxContent() - (mb_strlen($url) + 2));
- $content = $shortSummary . ' ' . $url;
-
- // We mark up the attachment link specially for the HTML output
- // so we can fold-out the full version inline.
-
- // @todo FIXME i18n: This tooltip will be saved with the site's default language
- // TRANS: Shown when a notice is longer than supported and/or when attachments are present. At runtime
- // TRANS: this will usually be replaced with localised text from StatusNet core messages.
- $showMoreText = _m('Show more');
- $attachUrl = common_local_url('attachment',
- array('attachment' => $attachment->id));
- $rendered = common_render_text($shortSummary) .
- '<a href="' . htmlspecialchars($attachUrl) .'"'.
- ' class="attachment more"' .
- ' title="'. htmlspecialchars($showMoreText) . '">' .
- '…' .
- '</a>';
- }
- }
-
- $options = array('is_local' => Notice::REMOTE,
- 'url' => $sourceUrl,
- 'uri' => $activity->id,
- 'rendered' => $rendered,
- 'replies' => array(),
- 'groups' => array(),
- 'peopletags' => array(),
- 'tags' => array(),
- 'urls' => array(),
- 'repeat_of' => $sharedNotice->id,
- 'scope' => $sharedNotice->scope);
-
- // Check for optional attributes...
-
- if (!empty($activity->time)) {
- $options['created'] = common_sql_date($activity->time);
- }
-
- if ($activity->context) {
- // TODO: context->attention
- list($options['groups'], $options['replies'])
- = self::filterAttention($profile, $activity->context->attention);
-
- // Maintain direct reply associations
- // @todo FIXME: What about conversation ID?
- if (!empty($activity->context->replyToID)) {
- $orig = Notice::getKV('uri',
- $activity->context->replyToID);
- if ($orig instanceof Notice) {
- $options['reply_to'] = $orig->id;
- }
- }
-
- $location = $activity->context->location;
- if ($location) {
- $options['lat'] = $location->lat;
- $options['lon'] = $location->lon;
- if ($location->location_id) {
- $options['location_ns'] = $location->location_ns;
- $options['location_id'] = $location->location_id;
- }
- }
- }
-
- if ($this->isPeopletag()) {
- $options['peopletags'][] = $this->localPeopletag();
- }
-
- // Atom categories <-> hashtags
- foreach ($activity->categories as $cat) {
- if ($cat->term) {
- $term = common_canonical_tag($cat->term);
- if ($term) {
- $options['tags'][] = $term;
- }
- }
- }
-
- // Atom enclosures -> attachment URLs
- foreach ($activity->enclosures as $href) {
- // @todo FIXME: Save these locally or....?
- $options['urls'][] = $href;
- }
-
- $notice = Notice::saveNew($profile->id,
- $content,
- 'ostatus',
- $options);
-
- return $notice;
- }
-
/**
* Process an incoming post activity from this remote feed.
* @param Activity $activity
* @param string $method 'push' or 'salmon'
* @return mixed saved Notice or false
- * @todo FIXME: Break up this function, it's getting nasty long
*/
public function processPost($activity, $method)
{
- $notice = null;
-
- $profile = $this->checkAuthorship($activity, $this->localProfile());
-
- // It's not always an ActivityObject::NOTE, but... let's just say it is.
-
- $note = $activity->objects[0];
-
- // The id URI will be used as a unique identifier for the notice,
- // protecting against duplicate saves. It isn't required to be a URL;
- // tag: URIs for instance are found in Google Buzz feeds.
- $sourceUri = $note->id;
- $dupe = Notice::getKV('uri', $sourceUri);
- if ($dupe instanceof Notice) {
- common_log(LOG_INFO, "OStatus: ignoring duplicate post: $sourceUri");
- return $dupe;
- }
-
- // We'll also want to save a web link to the original notice, if provided.
- $sourceUrl = null;
- if ($note->link) {
- $sourceUrl = $note->link;
- } else if ($activity->link) {
- $sourceUrl = $activity->link;
- } else if (preg_match('!^https?://!', $note->id)) {
- $sourceUrl = $note->id;
- }
-
- // Use summary as fallback for content
-
- if (!empty($note->content)) {
- $sourceContent = $note->content;
- } else if (!empty($note->summary)) {
- $sourceContent = $note->summary;
- } else if (!empty($note->title)) {
- $sourceContent = $note->title;
- } else {
- // @todo FIXME: Fetch from $sourceUrl?
- // TRANS: Client exception. %s is a source URI.
- throw new ClientException(sprintf(_m('No content for notice %s.'),$sourceUri));
- }
-
- // Get (safe!) HTML and text versions of the content
-
- $rendered = $this->purify($sourceContent);
- $content = common_strip_html($rendered);
-
- $shortened = common_shorten_links($content);
-
- // If it's too long, try using the summary, and make the
- // HTML an attachment.
-
- $attachment = null;
-
- if (Notice::contentTooLong($shortened)) {
- $attachment = $this->saveHTMLFile($note->title, $rendered);
- $summary = common_strip_html($note->summary);
- if (empty($summary)) {
- $summary = $content;
- }
- $shortSummary = common_shorten_links($summary);
- if (Notice::contentTooLong($shortSummary)) {
- $url = common_shorten_url($sourceUrl);
- $shortSummary = substr($shortSummary,
- 0,
- Notice::maxContent() - (mb_strlen($url) + 2));
- $content = $shortSummary . ' ' . $url;
-
- // We mark up the attachment link specially for the HTML output
- // so we can fold-out the full version inline.
-
- // @todo FIXME i18n: This tooltip will be saved with the site's default language
- // TRANS: Shown when a notice is longer than supported and/or when attachments are present. At runtime
- // TRANS: this will usually be replaced with localised text from StatusNet core messages.
- $showMoreText = _m('Show more');
- $attachUrl = common_local_url('attachment',
- array('attachment' => $attachment->id));
- $rendered = common_render_text($shortSummary) .
- '<a href="' . htmlspecialchars($attachUrl) .'"'.
- ' class="attachment more"' .
- ' title="'. htmlspecialchars($showMoreText) . '">' .
- '…' .
- '</a>';
- }
- }
+ $actor = ActivityUtils::checkAuthorship($activity, $this->localProfile());
- $options = array('is_local' => Notice::REMOTE,
- 'url' => $sourceUrl,
- 'uri' => $sourceUri,
- 'rendered' => $rendered,
- 'replies' => array(),
- 'groups' => array(),
- 'peopletags' => array(),
- 'tags' => array(),
- 'urls' => array());
-
- // Check for optional attributes...
-
- if (!empty($activity->time)) {
- $options['created'] = common_sql_date($activity->time);
- }
-
- if ($activity->context) {
- // TODO: context->attention
- list($options['groups'], $options['replies'])
- = self::filterAttention($profile, $activity->context->attention);
-
- // Maintain direct reply associations
- // @todo FIXME: What about conversation ID?
- if (!empty($activity->context->replyToID)) {
- $orig = Notice::getKV('uri', $activity->context->replyToID);
- if ($orig instanceof Notice) {
- $options['reply_to'] = $orig->id;
- }
- }
-
- $location = $activity->context->location;
- if ($location) {
- $options['lat'] = $location->lat;
- $options['lon'] = $location->lon;
- if ($location->location_id) {
- $options['location_ns'] = $location->location_ns;
- $options['location_id'] = $location->location_id;
- }
- }
- }
-
- if ($this->isPeopletag()) {
- $options['peopletags'][] = $this->localPeopletag();
- }
-
- // Atom categories <-> hashtags
- foreach ($activity->categories as $cat) {
- if ($cat->term) {
- $term = common_canonical_tag($cat->term);
- if ($term) {
- $options['tags'][] = $term;
- }
- }
- }
-
- // Atom enclosures -> attachment URLs
- foreach ($activity->enclosures as $href) {
- // @todo FIXME: Save these locally or....?
- $options['urls'][] = $href;
- }
+ $options = array('is_local' => Notice::REMOTE);
try {
- $saved = Notice::saveNew($profile->id,
- $content,
- 'ostatus',
- $options);
- if ($saved instanceof Notice) {
- Ostatus_source::saveNew($saved, $this, $method);
- if (!empty($attachment)) {
- File_to_post::processNew($attachment->id, $saved->id);
- }
- }
+ $stored = Notice::saveActivity($activity, $actor, $options);
+ Ostatus_source::saveNew($stored, $this, $method);
} catch (Exception $e) {
common_log(LOG_ERR, "OStatus save of remote message $sourceUri failed: " . $e->getMessage());
throw $e;
}
- common_log(LOG_INFO, "OStatus saved remote message $sourceUri as notice id $saved->id");
- return $saved;
- }
-
- /**
- * Clean up HTML
- */
- protected function purify($html)
- {
- require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
- $config = array('safe' => 1,
- 'deny_attribute' => 'id,style,on*');
- return htmLawed($html, $config);
+ return $stored;
}
/**
* @throws Exception on various error conditions
* @throws OStatusShadowException if this reference would obscure a local user/group
*/
- public static function ensureProfileURL($profile_url, $hints=array())
+ public static function ensureProfileURL($profile_url, array $hints=array())
{
$oprofile = self::getFromProfileURL($profile_url);
}
}
- // Try to get some hCard data
+ if (in_array(
+ preg_replace('/\s*;.*$/', '', $response->getHeader('Content-Type')),
+ array('application/rss+xml', 'application/atom+xml', 'application/xml', 'text/xml'))
+ ) {
+ $hints['feedurl'] = $response->getUrl();
+ } else {
+ // Try to get some hCard data
- $body = $response->getBody();
+ $body = $response->getBody();
- $hcardHints = DiscoveryHints::hcardHints($body, $finalUrl);
+ $hcardHints = DiscoveryHints::hcardHints($body, $finalUrl);
- if (!empty($hcardHints)) {
- $hints = array_merge($hints, $hcardHints);
+ if (!empty($hcardHints)) {
+ $hints = array_merge($hints, $hcardHints);
+ }
}
// Check if they've got an LRDD header
* @return Ostatus_profile
* @throws Exception
*/
- public static function ensureFeedURL($feed_url, $hints=array())
+ public static function ensureFeedURL($feed_url, array $hints=array())
{
+ $oprofile = Ostatus_profile::getKV('feeduri', $feed_url);
+ if ($oprofile instanceof Ostatus_profile) {
+ return $oprofile;
+ }
+
$discover = new FeedDiscovery();
$feeduri = $discover->discoverFromFeedURL($feed_url);
?: $discover->getAtomLink(Salmon::NS_REPLIES);
$hints['salmon'] = $salmonuri;
- if (!$huburi && !common_config('feedsub', 'fallback_hub')) {
+ if (!$huburi && !common_config('feedsub', 'fallback_hub') && !common_config('feedsub', 'nohub')) {
// We can only deal with folks with a PuSH hub
+ // unless we have something similar available locally.
throw new FeedSubNoHubException();
}
* @return Ostatus_profile
* @throws Exception
*/
- public static function ensureAtomFeed($feedEl, $hints)
+ public static function ensureAtomFeed(DOMElement $feedEl, array $hints)
{
$author = ActivityUtils::getFeedAuthor($feedEl);
* @return Ostatus_profile
* @throws Exception
*/
- public static function ensureRssChannel($feedEl, $hints)
+ public static function ensureRssChannel(DOMElement $feedEl, array $hints)
{
// Special-case for Posterous. They have some nice metadata in their
// posterous:author elements. We should use them instead of the channel.
}
}
+ $obj = ActivityUtils::getFeedAuthor($feedEl);
+
// @todo FIXME: We should check whether this feed has elements
// with different <author> or <dc:creator> elements, and... I dunno.
// Do something about that.
- $obj = ActivityObject::fromRssChannel($feedEl);
+ if(empty($obj)) { $obj = ActivityObject::fromRssChannel($feedEl); }
return self::ensureActivityObjectProfile($obj, $hints);
}
* Download and update given avatar image
*
* @param string $url
+ * @return Avatar The Avatar we have on disk. (seldom used)
* @throws Exception in various failure cases
*/
- protected function updateAvatar($url)
+ public function updateAvatar($url, $force=false)
{
- if ($url == $this->avatar) {
- // We've already got this one.
- return;
+ try {
+ // If avatar URL differs: update. If URLs were identical but we're forced: update.
+ if ($url == $this->avatar && !$force) {
+ // If there's no locally stored avatar, throw an exception and continue fetching below.
+ $avatar = Avatar::getUploaded($this->localProfile()) instanceof Avatar;
+ return $avatar;
+ }
+ } catch (NoAvatarException $e) {
+ // No avatar available, let's fetch it.
}
+
if (!common_valid_http_url($url)) {
// TRANS: Server exception. %s is a URL.
throw new ServerException(sprintf(_m('Invalid avatar URL %s.'), $url));
}
- if ($this->isGroup()) {
- // FIXME: throw exception for localGroup
- $self = $this->localGroup();
- } else {
- // this throws an exception already
- $self = $this->localProfile();
- }
- if (!$self) {
- throw new ServerException(sprintf(
- // TRANS: Server exception. %s is a URI.
- _m('Tried to update avatar for unsaved remote profile %s.'),
- $this->getUri()));
- }
+ $self = $this->localProfile();
// @todo FIXME: This should be better encapsulated
// ripped from oauthstore.php (for old OMB client)
$temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
try {
- if (!copy($url, $temp_filename)) {
- // TRANS: Server exception. %s is a URL.
- throw new ServerException(sprintf(_m('Unable to fetch avatar from %s.'), $url));
+ $imgData = HTTPClient::quickGet($url);
+ // Make sure it's at least an image file. ImageFile can do the rest.
+ if (false === getimagesizefromstring($imgData)) {
+ throw new UnsupportedMediaException(_('Downloaded group avatar was not an image.'));
}
+ file_put_contents($temp_filename, $imgData);
+ unset($imgData); // No need to carry this in memory.
if ($this->isGroup()) {
$id = $this->group_id;
} else {
$id = $this->profile_id;
}
- // @todo FIXME: Should we be using different ids?
- $imagefile = new ImageFile($id, $temp_filename);
+ $imagefile = new ImageFile(null, $temp_filename);
$filename = Avatar::filename($id,
image_type_to_extension($imagefile->type),
null,
$orig = clone($this);
$this->avatar = $url;
$this->update($orig);
+
+ return Avatar::getUploaded($self);
}
/**
* @param array $hints
* @return mixed URL string or false
*/
- public static function getActivityObjectAvatar($object, $hints=array())
+ public static function getActivityObjectAvatar(ActivityObject $object, array $hints=array())
{
if ($object->avatarLinks) {
$best = false;
* @param DOMElement $feed
* @return string
*/
- protected static function getAvatar($actor, $feed)
+ protected static function getAvatar(ActivityObject $actor, DOMElement $feed)
{
$url = '';
$icon = '';
* @return Ostatus_profile
* @throws Exception
*/
- public static function ensureActorProfile($activity, $hints=array())
+ public static function ensureActorProfile(Activity $activity, array $hints=array())
{
return self::ensureActivityObjectProfile($activity->actor, $hints);
}
* @return Ostatus_profile
* @throws Exception
*/
- public static function ensureActivityObjectProfile($object, $hints=array())
+ public static function ensureActivityObjectProfile(ActivityObject $object, array $hints=array())
{
$profile = self::getActivityObjectProfile($object);
if ($profile instanceof Ostatus_profile) {
* @return mixed matching Ostatus_profile or false if none known
* @throws ServerException if feed info invalid
*/
- public static function getActorProfile($activity)
+ public static function getActorProfile(Activity $activity)
{
return self::getActivityObjectProfile($activity->actor);
}
* @return mixed matching Ostatus_profile or false if none known
* @throws ServerException if feed info invalid
*/
- protected static function getActivityObjectProfile($object)
+ protected static function getActivityObjectProfile(ActivityObject $object)
{
$uri = self::getActivityObjectProfileURI($object);
return Ostatus_profile::getKV('uri', $uri);
* @return string
* @throws ServerException if feed info invalid
*/
- protected static function getActivityObjectProfileURI($object)
+ protected static function getActivityObjectProfileURI(ActivityObject $object)
{
if ($object->id) {
if (ActivityUtils::validateUri($object->id)) {
*
* @return Ostatus_profile
*/
- protected static function createActivityObjectProfile($object, $hints=array())
+ protected static function createActivityObjectProfile(ActivityObject $object, array $hints=array())
{
$homeuri = $object->id;
$discover = false;
$huburi = $discover->getHubLink();
}
- if (!$huburi && !common_config('feedsub', 'fallback_hub')) {
+ if (!$huburi && !common_config('feedsub', 'fallback_hub') && !common_config('feedsub', 'nohub')) {
// We can only deal with folks with a PuSH hub
throw new FeedSubNoHubException();
}
* @param ActivityObject $object
* @param array $hints
*/
- public function updateFromActivityObject($object, $hints=array())
+ public function updateFromActivityObject(ActivityObject $object, array $hints=array())
{
if ($this->isGroup()) {
$group = $this->localGroup();
}
}
- public static function updateProfile($profile, $object, $hints=array())
+ public static function updateProfile(Profile $profile, ActivityObject $object, array $hints=array())
{
$orig = clone($profile);
}
}
- protected static function updateGroup(User_group $group, $object, $hints=array())
+ protected static function updateGroup(User_group $group, ActivityObject $object, array $hints=array())
{
$orig = clone($group);
}
}
- protected static function updatePeopletag($tag, $object, $hints=array()) {
+ protected static function updatePeopletag($tag, ActivityObject $object, array $hints=array()) {
$orig = clone($tag);
$tag->tag = $object->title;
}
}
- protected static function getActivityObjectHomepage($object, $hints=array())
+ protected static function getActivityObjectHomepage(ActivityObject $object, array $hints=array())
{
$homepage = null;
$poco = $object->poco;
return $homepage;
}
- protected static function getActivityObjectLocation($object, $hints=array())
+ protected static function getActivityObjectLocation(ActivityObject $object, array $hints=array())
{
$location = null;
}
if (!empty($location)) {
- if (mb_strlen($location) > 255) {
- $location = mb_substr($note, 0, 255 - 3) . ' … ';
+ if (mb_strlen($location) > 191) { // not 255 because utf8mb4 takes more space
+ $location = mb_substr($note, 0, 191 - 3) . ' … ';
}
}
return $location;
}
- protected static function getActivityObjectBio($object, $hints=array())
+ protected static function getActivityObjectBio(ActivityObject $object, array $hints=array())
{
$bio = null;
return $bio;
}
- public static function getActivityObjectNickname($object, $hints=array())
+ public static function getActivityObjectNickname(ActivityObject $object, array $hints=array())
{
if ($object->poco) {
if (!empty($object->poco->preferredUsername)) {
throw new Exception(_m('Not a valid webfinger address.'));
}
- $hints = array('webfinger' => $addr);
-
- $dhints = DiscoveryHints::fromXRD($xrd);
-
- $hints = array_merge($hints, $dhints);
+ $hints = array_merge(array('webfinger' => $addr),
+ DiscoveryHints::fromXRD($xrd));
// If there's an Hcard, let's grab its info
if (array_key_exists('hcard', $hints)) {
'text/html');
$filepath = File::path($filename);
+ $fileurl = File::url($filename);
file_put_contents($filepath, $final);
$file = new File;
$file->filename = $filename;
- $file->url = File::url($filename);
+ $file->urlhash = File::hashurl($fileurl);
+ $file->url = $fileurl;
$file->size = filesize($filepath);
$file->date = time();
$file->mimetype = 'text/html';
throw new ServerException(sprintf(_m('Unrecognized URI protocol for profile: %1$s (%2$s).'),
$protocol,
$uri));
- break;
}
} else {
// TRANS: Server exception. %s is a URI.
return $oprofile->localProfile();
}
+
+ public function updateUriKeys($profile_uri, array $hints=array())
+ {
+ $orig = clone($this);
+
+ common_debug('URIFIX These identities both say they are each other: "'.$orig->uri.'" and "'.$profile_uri.'"');
+ $this->uri = $profile_uri;
+
+ if (array_key_exists('feedurl', $hints)) {
+ if (!empty($this->feeduri)) {
+ common_debug('URIFIX Changing FeedSub ['.$feedsub->id.'] feeduri "'.$feedsub->uri.'" to "'.$hints['feedurl']);
+ $feedsub = FeedSub::getKV('uri', $this->feeduri);
+ $feedorig = clone($feedsub);
+ $feedsub->uri = $hints['feedurl'];
+ $feedsub->updateWithKeys($feedorig);
+ } else {
+ common_debug('URIFIX Old Ostatus_profile did not have feedurl set, ensuring feed: '.$hints['feedurl']);
+ FeedSub::ensureFeed($hints['feedurl']);
+ }
+ $this->feeduri = $hints['feedurl'];
+ }
+ if (array_key_exists('salmon', $hints)) {
+ common_debug('URIFIX Changing Ostatus_profile salmonuri from "'.$this->salmonuri.'" to "'.$hints['salmon'].'"');
+ $this->salmonuri = $hints['salmon'];
+ }
+
+ common_debug('URIFIX Updating Ostatus_profile URI for '.$orig->uri.' to '.$this->uri);
+ $this->updateWithKeys($orig, 'uri'); // 'uri' is the primary key column
+
+ common_debug('URIFIX Subscribing/renewing feedsub for Ostatus_profile '.$this->uri);
+ $this->subscribe();
+ }
}
/**