summaryrefslogtreecommitdiff
path: root/classes/Notice.php
diff options
context:
space:
mode:
authorBrion Vibber <brion@pobox.com>2010-02-24 15:47:51 -0800
committerBrion Vibber <brion@pobox.com>2010-02-24 15:47:51 -0800
commit59be4b8cae992476628b23c2976d335e4f704c89 (patch)
treea1e94f2b11a5e640bf55f6977576fdca237f323f /classes/Notice.php
parent2d9d444b05e29105082d3a443b8b71de6498b7e9 (diff)
parente04f55630473f5f8b17554d14cfd047b93af8609 (diff)
Merge branch 'testing' of gitorious.org:statusnet/mainline into 0.9.x
Diffstat (limited to 'classes/Notice.php')
-rw-r--r--classes/Notice.php253
1 files changed, 166 insertions, 87 deletions
diff --git a/classes/Notice.php b/classes/Notice.php
index 7e524cacd..e8d5c45cb 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -187,7 +187,14 @@ class Notice extends Memcached_DataObject
* 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' permalink to notice; defaults to local notice URL
+ * string 'uri' unique ID for notice; defaults to local notice URL
+ * 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
+ * place of extracting @-replies from content.
+ * array 'groups' list of group IDs to deliver to, in place of
+ * extracting ! tags from content
+ * @fixme tag override
*
* @return Notice
* @throws ClientException
@@ -333,8 +340,21 @@ class Notice extends Memcached_DataObject
# Clear the cache for subscribed users, so they'll update at next request
# XXX: someone clever could prepend instead of clearing the cache
+
$notice->blowOnInsert();
+ if (isset($replies)) {
+ $notice->saveKnownReplies($replies);
+ } else {
+ $notice->saveReplies();
+ }
+
+ if (isset($groups)) {
+ $notice->saveKnownGroups($groups);
+ } else {
+ $notice->saveGroups();
+ }
+
$notice->distribute();
return $notice;
@@ -685,7 +705,22 @@ class Notice extends Memcached_DataObject
return $ni;
}
- function addToInboxes($groups, $recipients)
+ /**
+ * 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($groups=null, $recipients=null)
{
$ni = $this->whoGets($groups, $recipients);
@@ -735,6 +770,42 @@ class Notice extends Memcached_DataObject
}
/**
+ * Record this notice to the given group inboxes for delivery.
+ * Overrides the regular parsing of !group markup.
+ *
+ * @param string $group_ids
+ * @fixme might prefer URIs as identifiers, as for replies?
+ * best with generalizations on user_group to support
+ * remote groups better.
+ */
+ function saveKnownGroups($group_ids)
+ {
+ if (!is_array($group_ids)) {
+ throw new ServerException("Bad type provided to saveKnownGroups");
+ }
+
+ $groups = array();
+ foreach ($group_ids as $id) {
+ $group = User_group::staticGet('id', $id);
+ if ($group) {
+ common_log(LOG_ERR, "Local delivery to group id $id, $group->nickname");
+ $result = $this->addToGroupInbox($group);
+ if (!$result) {
+ common_log_db_error($gi, 'INSERT', __FILE__);
+ }
+
+ // @fixme should we save the tags here or not?
+ $groups[] = clone($group);
+ } else {
+ common_log(LOG_ERR, "Local delivery to group id $id skipped, doesn't exist");
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * Parse !group delivery and record targets into group_inbox.
* @return array of Group objects
*/
function saveGroups()
@@ -818,8 +889,49 @@ class Notice extends Memcached_DataObject
}
/**
+ * Save reply records indicating that this notice needs to be
+ * delivered to the local users with the given URIs.
+ *
+ * Since this is expected to be used when saving foreign-sourced
+ * messages, we won't deliver to any remote targets as that's the
+ * source service's responsibility.
+ *
+ * @fixme Unlike saveReplies() there's no mail notification here.
+ * Move that to distrib queue handler?
+ *
+ * @param array of unique identifier URIs for recipients
+ */
+ function saveKnownReplies($uris)
+ {
+ foreach ($uris as $uri) {
+
+ $user = User::staticGet('uri', $uri);
+
+ if (!empty($user)) {
+
+ $reply = new Reply();
+
+ $reply->notice_id = $this->id;
+ $reply->profile_id = $user->id;
+
+ $id = $reply->insert();
+ }
+ }
+
+ return;
+ }
+
+ /**
+ * Pull @-replies from this message's content in StatusNet markup format
+ * and save reply records indicating that this message needs to be
+ * delivered to those users.
+ *
+ * Side effect: local recipients get e-mail notifications here.
+ * @fixme move mail notifications to distrib?
+ *
* @return array of integer profile IDs
*/
+
function saveReplies()
{
// Don't save reply data for repeats
@@ -828,76 +940,44 @@ class Notice extends Memcached_DataObject
return array();
}
- // Alternative reply format
- $tname = false;
- if (preg_match('/^T ([A-Z0-9]{1,64}) /', $this->content, $match)) {
- $tname = $match[1];
- }
- // extract all @messages
- $cnt = preg_match_all('/(?:^|\s)@([a-z0-9]{1,64})/', $this->content, $match);
-
- $names = array();
-
- if ($cnt || $tname) {
- // XXX: is there another way to make an array copy?
- $names = ($tname) ? array_unique(array_merge(array(strtolower($tname)), $match[1])) : array_unique($match[1]);
- }
-
$sender = Profile::staticGet($this->profile_id);
+ $mentions = common_find_mentions($this->profile_id, $this->content);
+
$replied = array();
// store replied only for first @ (what user/notice what the reply directed,
// we assume first @ is it)
- for ($i=0; $i<count($names); $i++) {
- $nickname = $names[$i];
- $recipient = common_relative_profile($sender, $nickname, $this->created);
- if (empty($recipient)) {
- continue;
- }
- // Don't save replies from blocked profile to local user
- $recipient_user = User::staticGet('id', $recipient->id);
- if (!empty($recipient_user) && $recipient_user->hasBlocked($sender)) {
- continue;
- }
- $reply = new Reply();
- $reply->notice_id = $this->id;
- $reply->profile_id = $recipient->id;
- $id = $reply->insert();
- if (!$id) {
- $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
- common_log(LOG_ERR, 'DB error inserting reply: ' . $last_error->message);
- common_server_error(sprintf(_('DB error inserting reply: %s'), $last_error->message));
- return array();
- } else {
- $replied[$recipient->id] = 1;
- }
- }
+ foreach ($mentions as $mention) {
- // Hash format replies, too
- $cnt = preg_match_all('/(?:^|\s)@#([a-z0-9]{1,64})/', $this->content, $match);
- if ($cnt) {
- foreach ($match[1] as $tag) {
- $tagged = Profile_tag::getTagged($sender->id, $tag);
- foreach ($tagged as $t) {
- if (!$replied[$t->id]) {
- // Don't save replies from blocked profile to local user
- $t_user = User::staticGet('id', $t->id);
- if ($t_user && $t_user->hasBlocked($sender)) {
- continue;
- }
- $reply = new Reply();
- $reply->notice_id = $this->id;
- $reply->profile_id = $t->id;
- $id = $reply->insert();
- if (!$id) {
- common_log_db_error($reply, 'INSERT', __FILE__);
- return array();
- } else {
- $replied[$recipient->id] = 1;
- }
- }
+ foreach ($mention['mentioned'] as $mentioned) {
+
+ // skip if they're already covered
+
+ if (!empty($replied[$mentioned->id])) {
+ continue;
+ }
+
+ // Don't save replies from blocked profile to local user
+
+ $mentioned_user = User::staticGet('id', $mentioned->id);
+ if (!empty($mentioned_user) && $mentioned_user->hasBlocked($sender)) {
+ continue;
+ }
+
+ $reply = new Reply();
+
+ $reply->notice_id = $this->id;
+ $reply->profile_id = $mentioned->id;
+
+ $id = $reply->insert();
+
+ if (!$id) {
+ common_log_db_error($reply, 'INSERT', __FILE__);
+ throw new ServerException("Couldn't save reply for {$this->id}, {$mentioned->id}");
+ } else {
+ $replied[$mentioned->id] = 1;
}
}
}
@@ -938,9 +1018,10 @@ class Notice extends Memcached_DataObject
}
/**
- * Same calculation as saveGroups but without the saving
- * @fixme merge the functions
- * @return array of Group_inbox objects
+ * Pull list of groups this notice needs to be delivered to,
+ * as previously recorded by saveGroups() or saveKnownGroups().
+ *
+ * @return array of Group objects
*/
function getGroups()
{
@@ -963,7 +1044,10 @@ class Notice extends Memcached_DataObject
if ($gi->find()) {
while ($gi->fetch()) {
- $groups[] = clone($gi);
+ $group = User_group::staticGet('id', $gi->group_id);
+ if ($group) {
+ $groups[] = $group;
+ }
}
}
@@ -983,6 +1067,7 @@ class Notice extends Memcached_DataObject
'xmlns:thr' => 'http://purl.org/syndication/thread/1.0',
'xmlns:georss' => 'http://www.georss.org/georss',
'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/',
+ 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
'xmlns:ostatus' => 'http://ostatus.org/schema/1.0');
} else {
$attrs = array();
@@ -1067,6 +1152,17 @@ class Notice extends Memcached_DataObject
}
}
+ $groups = $this->getGroups();
+
+ foreach ($groups as $group) {
+ $xs->element(
+ 'link', array(
+ 'rel' => 'ostatus:attention',
+ 'href' => $group->permalink()
+ )
+ );
+ }
+
if (!empty($this->repeat_of)) {
$repeat = Notice::staticGet('id', $this->repeat_of);
if (!empty($repeat)) {
@@ -1123,25 +1219,8 @@ class Notice extends Memcached_DataObject
*/
function asActivityNoun($element)
{
- $xs = new XMLStringer(true);
-
- $xs->elementStart('activity:' . $element);
- $xs->element('activity:object-type',
- null,
- 'http://activitystrea.ms/schema/1.0/note');
- $xs->element('id',
- null,
- $this->uri);
- $xs->element('content',
- array('type' => 'text/html'),
- $this->rendered);
- $xs->element('link',
- array('type' => 'text/html',
- 'rel' => 'alternate',
- 'href' => $this->bestUrl()));
- $xs->elementEnd('activity:' . $element);
-
- return $xs->getString();
+ $noun = ActivityObject::fromNotice($this);
+ return $noun->asString('activity:' . $element);
}
function bestUrl()