* @license GNU Affero General Public License http://www.gnu.org/licenses/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Table Definition for notice
*/
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
/* We keep 200 notices, the max number of notices available per API request,
* in the memcached cache. */
const FOLLOWER_SCOPE = 8;
protected $_profile = -1;
-
+
public function getProfile()
{
if ($this->_profile === -1) {
$this->_setProfile(Profile::getKV('id', $this->profile_id));
}
- if (!$this->_profile instanceof Profile) {
- throw new NoProfileException($this->profile_id);
- }
return $this->_profile;
}
- function _setProfile(Profile $profile)
+ public function _setProfile(Profile $profile=null)
{
+ if (!$profile instanceof Profile) {
+ throw new NoProfileException($this->profile_id);
+ }
$this->_profile = $profile;
}
$this->clearTags();
$this->clearGroupInboxes();
$this->clearFiles();
+ $this->clearAttentions();
- // NOTE: we don't clear inboxes
// NOTE: we don't clear queue items
}
return $this->uri;
}
+ /*
+ * @param $root boolean If true, link to just the conversation root.
+ *
+ * @return URL to conversation
+ */
+ public function getConversationUrl($anchor=true)
+ {
+ return Conversation::getUrlFromNotice($this, $anchor);
+ }
+
+ /*
+ * Get the local representation URL of this notice.
+ */
+ public function getLocalUrl()
+ {
+ return common_local_url('shownotice', array('notice' => $this->id), null, null, false);
+ }
+
+ /*
+ * Get the original representation URL of this notice.
+ */
public function getUrl()
{
// The risk is we start having empty urls and non-http uris...
- return $this->url ?: $this->uri;
+ // and we can't really handle any other protocol right now.
+ switch (true) {
+ case common_valid_http_url($this->url): // should we allow non-http/https URLs?
+ return $this->url;
+ case $this->isLocal():
+ // let's generate a valid link to our locally available notice on demand
+ return common_local_url('shownotice', array('notice' => $this->id), null, null, false);
+ case common_valid_http_url($this->uri):
+ return $this->uri;
+ default:
+ common_debug('No URL available for notice: id='.$this->id);
+ throw new InvalidUrlException($this->url);
+ }
+ }
+
+ public function get_object_type($canonical=false) {
+ return $canonical
+ ? ActivityObject::canonicalType($this->object_type)
+ : $this->object_type;
+ }
+
+ public static function getByUri($uri)
+ {
+ $notice = new Notice();
+ $notice->uri = $uri;
+ if (!$notice->find(true)) {
+ throw new NoResultException($notice);
+ }
+ return $notice;
}
/**
* int 'location_ns' geoname namespace to interpret location_id
* int 'reply_to'; notice ID this is a reply to
* int 'repeat_of'; notice ID this is a repeat of
- * string 'uri' unique ID for notice; defaults to local notice URL
+ * string 'uri' unique ID for notice; a unique tag uri (can be url or anything too)
* string 'url' permalink to notice; defaults to local notice URL
* string 'rendered' rendered HTML version of content
* array 'replies' list of profile URIs for reply delivery in
$notice->created = common_sql_now();
}
+ if (!$notice->isLocal()) {
+ // Only do these checks for non-local notices. Local notices will generate these values later.
+ if (!common_valid_http_url($url)) {
+ common_debug('Bad notice URL: ['.$url.'], URI: ['.$uri.']. Cannot link back to original! This is normal for shared notices etc.');
+ }
+ if (empty($uri)) {
+ throw new ServerException('No URI for remote notice. Cannot accept that.');
+ }
+ }
+
$notice->content = $final;
$notice->source = $source;
$changed = false;
- if (empty($uri)) {
- $notice->uri = common_notice_uri($notice);
+ // 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;
}
// the beginning of a new conversation.
if (empty($notice->conversation)) {
- $conv = Conversation::create();
+ $conv = Conversation::create($notice);
$notice->conversation = $conv->id;
$changed = true;
}
if ($changed) {
- if (!$notice->update($orig)) {
+ 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.'));
}
}
- if (is_null($groups)) {
- $groups = $this->getGroups();
- }
-
if (is_null($recipients)) {
$recipients = $this->getReplies();
}
- $users = $this->getSubscribedUsers();
- $ptags = $this->getProfileTags();
-
- // FIXME: kind of ignoring 'transitional'...
- // we'll probably stop supporting inboxless mode
- // in 0.9.x
-
$ni = array();
// Give plugins a chance to add folks in at start...
if (Event::handle('StartNoticeWhoGets', array($this, &$ni))) {
+ $users = $this->getSubscribedUsers();
foreach ($users as $id) {
$ni[$id] = NOTICE_INBOX_SOURCE_SUB;
}
+ if (is_null($groups)) {
+ $groups = $this->getGroups();
+ }
foreach ($groups as $group) {
$users = $group->getUserMembers();
foreach ($users as $id) {
}
}
- foreach ($ptags as $ptag) {
- $users = $ptag->getUserSubscribers();
- foreach ($users as $id) {
- if (!array_key_exists($id, $ni)) {
- $ni[$id] = NOTICE_INBOX_SOURCE_PROFILE_TAG;
- }
+ $ptAtts = $this->getAttentionsFromProfileTags();
+ foreach ($ptAtts as $key=>$val) {
+ if (!array_key_exists($key, $ni)) {
+ $ni[$key] = $val;
}
}
return $ni;
}
- /**
- * Adds this notice to the inboxes of each local user who should receive
- * it, based on author subscriptions, group memberships, and @-replies.
- *
- * Warning: running a second time currently will make items appear
- * multiple times in users' inboxes.
- *
- * @fixme make more robust against errors
- * @fixme break up massive deliveries to smaller background tasks
- *
- * @param array $groups optional list of Group objects;
- * if left empty, will be loaded from group_inbox records
- * @param array $recipient optional list of reply profile ids
- * if left empty, will be loaded from reply records
- */
- function addToInboxes(array $groups=null, array $recipients=null)
- {
- $ni = $this->whoGets($groups, $recipients);
-
- $ids = array_keys($ni);
-
- // We remove the author (if they're a local user),
- // since we'll have already done this in distribute()
-
- $i = array_search($this->profile_id, $ids);
-
- if ($i !== false) {
- unset($ids[$i]);
- }
-
- // Bulk insert
-
- Inbox::bulkInsert($this->id, $ids);
-
- return;
- }
-
function getSubscribedUsers()
{
$user = new User();
return $ptags;
}
+ public function getAttentionsFromProfileTags()
+ {
+ $ni = array();
+ $ptags = $this->getProfileTags();
+ foreach ($ptags as $ptag) {
+ $users = $ptag->getUserSubscribers();
+ foreach ($users as $id) {
+ $ni[$id] = NOTICE_INBOX_SOURCE_PROFILE_TAG;
+ }
+ }
+ return $ni;
+ }
+
/**
* Record this notice to the given group inboxes for delivery.
* Overrides the regular parsing of !group markup.
$act->id = $this->uri;
$act->time = strtotime($this->created);
- $act->link = $this->bestUrl();
+ $act->link = $this->getUrl();
$act->content = common_xml_safe_str($this->rendered);
$profile = $this->getProfile();
try {
$reply = $this->getParent();
- $ctx->replyToID = $reply->uri;
- $ctx->replyToUrl = $reply->bestUrl();
+ $ctx->replyToID = $reply->getUri();
+ $ctx->replyToUrl = $reply->getUrl();
} catch (Exception $e) {
// This is not a reply to something
}
return $noun->asString('activity:' . $element);
}
- function bestUrl()
- {
- if (!empty($this->url)) {
- return $this->url;
- } else if (!empty($this->uri) && preg_match('/^https?:/', $this->uri)) {
- return $this->uri;
- } else {
- return common_local_url('shownotice',
- array('notice' => $this->id));
- }
- }
-
-
/**
* Determine which notice, if any, a new notice is in reply to.
*
return $options;
}
+ function clearAttentions()
+ {
+ $att = new Attention();
+ $att->notice_id = $this->getID();
+
+ if ($att->find()) {
+ while ($att->fetch()) {
+ // Can't do delete() on the object directly since it won't remove all of it
+ $other = clone($att);
+ $other->delete();
+ }
+ }
+ }
+
function clearReplies()
{
$replyNotice = new Notice();
// have to wait
Event::handle('StartNoticeDistribute', array($this));
- $user = User::getKV('id', $this->profile_id);
- if ($user instanceof User) {
- Inbox::insertNotice($user->id, $this->id);
- }
-
- if (common_config('queue', 'inboxes')) {
- // If there's a failure, we want to _force_
- // distribution at this point.
+ // If there's a failure, we want to _force_
+ // distribution at this point.
+ try {
+ $qm = QueueManager::get();
+ $qm->enqueue($this, 'distrib');
+ } catch (Exception $e) {
+ // If the exception isn't transient, this
+ // may throw more exceptions as DQH does
+ // its own enqueueing. So, we ignore them!
try {
- $qm = QueueManager::get();
- $qm->enqueue($this, 'distrib');
+ $handler = new DistribQueueHandler();
+ $handler->handle($this);
} catch (Exception $e) {
- // If the exception isn't transient, this
- // may throw more exceptions as DQH does
- // its own enqueueing. So, we ignore them!
- try {
- $handler = new DistribQueueHandler();
- $handler->handle($this);
- } catch (Exception $e) {
- common_log(LOG_ERR, "emergency redistribution resulted in " . $e->getMessage());
- }
- // Re-throw so somebody smarter can handle it.
- throw $e;
+ common_log(LOG_ERR, "emergency redistribution resulted in " . $e->getMessage());
}
- } else {
- $handler = new DistribQueueHandler();
- $handler->handle($this);
+ // Re-throw so somebody smarter can handle it.
+ throw $e;
}
}
{
$map = self::getProfiles($notices);
- foreach ($notices as $notice) {
- if (array_key_exists($notice->profile_id, $map)) {
- $notice->_setProfile($map[$notice->profile_id]);
- }
+ foreach ($notices as $entry=>$notice) {
+ try {
+ if (array_key_exists($notice->profile_id, $map)) {
+ $notice->_setProfile($map[$notice->profile_id]);
+ }
+ } catch (NoProfileException $e) {
+ common_log(LOG_WARNING, "Failed to fill profile in Notice with non-existing entry for profile_id: {$e->id}");
+ unset($notices[$entry]);
+ }
}
return array_values($map);