summaryrefslogtreecommitdiff
path: root/classes
diff options
context:
space:
mode:
authorZach Copley <zach@status.net>2010-03-05 03:56:04 +0000
committerZach Copley <zach@status.net>2010-03-05 03:56:04 +0000
commitb8001ea1079c4c8895fd2323e1e4a47c0d71fde3 (patch)
treeb732b239bf790c78c90f4c7d3be7044ce9f7da45 /classes
parente3c4b0c85d3fbae9604b22d3666fe36a3c1c7551 (diff)
parent064c45890f896f2af8a0231449fa5337bb79c509 (diff)
Merge branch '0.9.x' of git@gitorious.org:statusnet/mainline into 0.9.x
Diffstat (limited to 'classes')
-rw-r--r--classes/Fave.php50
-rw-r--r--classes/File.php13
-rw-r--r--classes/Inbox.php8
-rw-r--r--classes/Local_group.php46
-rw-r--r--classes/Memcached_DataObject.php6
-rw-r--r--classes/Notice.php383
-rw-r--r--classes/Notice_inbox.php4
-rw-r--r--classes/Notice_tag.php6
-rw-r--r--classes/Profile.php121
-rw-r--r--classes/Profile_role.php17
-rw-r--r--classes/Reply.php10
-rw-r--r--classes/Subscription.php176
-rw-r--r--classes/User.php86
-rw-r--r--classes/User_group.php176
-rw-r--r--classes/statusnet.ini34
15 files changed, 820 insertions, 316 deletions
diff --git a/classes/Fave.php b/classes/Fave.php
index 8113c8e16..a04f15e9c 100644
--- a/classes/Fave.php
+++ b/classes/Fave.php
@@ -21,17 +21,47 @@ class Fave extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
- static function addNew($user, $notice) {
- $fave = new Fave();
- $fave->user_id = $user->id;
- $fave->notice_id = $notice->id;
- if (!$fave->insert()) {
- common_log_db_error($fave, 'INSERT', __FILE__);
- return false;
+ static function addNew($profile, $notice) {
+
+ $fave = null;
+
+ if (Event::handle('StartFavorNotice', array($profile, $notice, &$fave))) {
+
+ $fave = new Fave();
+
+ $fave->user_id = $profile->id;
+ $fave->notice_id = $notice->id;
+
+ if (!$fave->insert()) {
+ common_log_db_error($fave, 'INSERT', __FILE__);
+ return false;
+ }
+
+ Event::handle('EndFavorNotice', array($profile, $notice));
}
+
return $fave;
}
+ function delete()
+ {
+ $profile = Profile::staticGet('id', $this->user_id);
+ $notice = Notice::staticGet('id', $this->notice_id);
+
+ $result = null;
+
+ if (Event::handle('StartDisfavorNotice', array($profile, $notice, &$result))) {
+
+ $result = parent::delete();
+
+ if ($result) {
+ Event::handle('EndDisfavorNotice', array($profile, $notice));
+ }
+ }
+
+ return $result;
+ }
+
function pkeyGet($kv)
{
return Memcached_DataObject::pkeyGet('Fave', $kv);
@@ -47,7 +77,7 @@ class Fave extends Memcached_DataObject
return $ids;
}
- function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id, $since)
+ function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id)
{
$fav = new Fave();
$qry = null;
@@ -70,10 +100,6 @@ class Fave extends Memcached_DataObject
$qry .= 'AND notice_id <= ' . $max_id . ' ';
}
- if (!is_null($since)) {
- $qry .= 'AND modified > \'' . date('Y-m-d H:i:s', $since) . '\' ';
- }
-
// NOTE: we sort by fave time, not by notice time!
$qry .= 'ORDER BY modified DESC ';
diff --git a/classes/File.php b/classes/File.php
index 189e04ce0..4ecd3b959 100644
--- a/classes/File.php
+++ b/classes/File.php
@@ -169,7 +169,11 @@ class File extends Memcached_DataObject
{
require_once 'MIME/Type/Extension.php';
$mte = new MIME_Type_Extension();
- $ext = $mte->getExtension($mimetype);
+ try {
+ $ext = $mte->getExtension($mimetype);
+ } catch ( Exception $e) {
+ $ext = strtolower(preg_replace('/\W/', '', $mimetype));
+ }
$nickname = $profile->nickname;
$datestamp = strftime('%Y%m%dT%H%M%S', time());
$random = strtolower(common_confirmation_code(32));
@@ -286,5 +290,12 @@ class File extends Memcached_DataObject
}
return $enclosure;
}
+
+ // quick back-compat hack, since there's still code using this
+ function isEnclosure()
+ {
+ $enclosure = $this->getEnclosure();
+ return !empty($enclosure);
+ }
}
diff --git a/classes/Inbox.php b/classes/Inbox.php
index be62611a1..014ba3d82 100644
--- a/classes/Inbox.php
+++ b/classes/Inbox.php
@@ -137,7 +137,7 @@ class Inbox extends Memcached_DataObject
}
}
- function stream($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false)
+ function stream($user_id, $offset, $limit, $since_id, $max_id, $own=false)
{
$inbox = Inbox::staticGet('user_id', $user_id);
@@ -195,15 +195,15 @@ class Inbox extends Memcached_DataObject
* @param int $limit
* @param mixed $since_id return only notices after but not including this id
* @param mixed $max_id return only notices up to and including this id
- * @param mixed $since obsolete/ignored
* @param mixed $own ignored?
* @return array of Notice objects
*
* @todo consider repacking the inbox when this happens?
+ * @fixme reimplement $own if we need it?
*/
- function streamNotices($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false)
+ function streamNotices($user_id, $offset, $limit, $since_id, $max_id, $own=false)
{
- $ids = self::stream($user_id, $offset, self::MAX_NOTICES, $since_id, $max_id, $since, $own);
+ $ids = self::stream($user_id, $offset, self::MAX_NOTICES, $since_id, $max_id, $own);
// Do a bulk lookup for the first $limit items
// Fast path when nothing's deleted.
diff --git a/classes/Local_group.php b/classes/Local_group.php
new file mode 100644
index 000000000..42312ec63
--- /dev/null
+++ b/classes/Local_group.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Table Definition for local_group
+ */
+
+class Local_group extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'local_group'; // table name
+ public $group_id; // int(4) primary_key not_null
+ public $nickname; // varchar(64) unique_key
+ public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
+ public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Local_group',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function sequenceKey()
+ {
+ return array(false, false, false);
+ }
+
+ function setNickname($nickname)
+ {
+ $this->decache();
+ $qry = 'UPDATE local_group set nickname = "'.$nickname.'" where group_id = ' . $this->group_id;
+
+ $result = $this->query($qry);
+
+ if ($result) {
+ $this->nickname = $nickname;
+ $this->fixupTimestamps();
+ $this->encache();
+ } else {
+ common_log_db_error($local, 'UPDATE', __FILE__);
+ throw new ServerException(_('Could not update local group.'));
+ }
+
+ return $result;
+ }
+}
diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php
index 40576dc71..bc4c3a000 100644
--- a/classes/Memcached_DataObject.php
+++ b/classes/Memcached_DataObject.php
@@ -501,7 +501,11 @@ class Memcached_DataObject extends Safe_DataObject
function raiseError($message, $type = null, $behaviour = null)
{
- throw new ServerException("DB_DataObject error [$type]: $message");
+ $id = get_class($this);
+ if ($this->id) {
+ $id .= ':' . $this->id;
+ }
+ throw new ServerException("[$id] DB_DataObject error [$type]: $message");
}
static function cacheGet($keyPart)
diff --git a/classes/Notice.php b/classes/Notice.php
index b0edb6de6..4c7e6ab4b 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -121,6 +121,9 @@ class Notice extends Memcached_DataObject
$result = parent::delete();
}
+ /**
+ * Extract #hashtags from this notice's content and save them to the database.
+ */
function saveTags()
{
/* extract all #hastags */
@@ -129,14 +132,22 @@ class Notice extends Memcached_DataObject
return true;
}
+ /* Add them to the database */
+ return $this->saveKnownTags($match[1]);
+ }
+
+ /**
+ * Record the given set of hash tags in the db for this notice.
+ * Given tag strings will be normalized and checked for dupes.
+ */
+ function saveKnownTags($hashtags)
+ {
//turn each into their canonical tag
//this is needed to remove dupes before saving e.g. #hash.tag = #hashtag
- $hashtags = array();
- for($i=0; $i<count($match[1]); $i++) {
- $hashtags[] = common_canonical_tag($match[1][$i]);
+ for($i=0; $i<count($hashtags); $i++) {
+ $hashtags[$i] = common_canonical_tag($hashtags[$i]);
}
- /* Add them to the database */
foreach(array_unique($hashtags) as $hashtag) {
/* elide characters we don't want in the tag */
$this->saveTag($hashtag);
@@ -145,6 +156,10 @@ class Notice extends Memcached_DataObject
return true;
}
+ /**
+ * Record a single hash tag as associated with this notice.
+ * Tag format and uniqueness must be validated by caller.
+ */
function saveTag($hashtag)
{
$tag = new Notice_tag();
@@ -187,13 +202,25 @@ 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
+ * array 'tags' list of hashtag strings to save with the notice
+ * in place of extracting # tags from content
+ * array 'urls' list of attached/referred URLs to save with the
+ * notice in place of extracting links from content
+ * @fixme tag override
*
* @return Notice
* @throws ClientException
*/
static function saveNew($profile_id, $content, $source, $options=null) {
$defaults = array('uri' => null,
+ 'url' => null,
'reply_to' => null,
'repeat_of' => null);
@@ -256,9 +283,10 @@ class Notice extends Memcached_DataObject
}
$notice->content = $final;
- $notice->rendered = common_render_content($final, $notice);
+
$notice->source = $source;
$notice->uri = $uri;
+ $notice->url = $url;
// Handle repeat case
@@ -283,6 +311,12 @@ class Notice extends Memcached_DataObject
$notice->location_ns = $location_ns;
}
+ if (!empty($rendered)) {
+ $notice->rendered = $rendered;
+ } else {
+ $notice->rendered = common_render_content($final, $notice);
+ }
+
if (Event::handle('StartNoticeSave', array(&$notice))) {
// XXX: some of these functions write to the DB
@@ -325,8 +359,36 @@ 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();
+ // Save per-notice metadata...
+
+ if (isset($replies)) {
+ $notice->saveKnownReplies($replies);
+ } else {
+ $notice->saveReplies();
+ }
+
+ if (isset($groups)) {
+ $notice->saveKnownGroups($groups);
+ } else {
+ $notice->saveGroups();
+ }
+
+ if (isset($tags)) {
+ $notice->saveKnownTags($tags);
+ } else {
+ $notice->saveTags();
+ }
+
+ if (isset($urls)) {
+ $notice->saveKnownUrls($urls);
+ } else {
+ $notice->saveUrls();
+ }
+
+ // Prepare inbox delivery, may be queued to background.
$notice->distribute();
return $notice;
@@ -370,6 +432,25 @@ class Notice extends Memcached_DataObject
common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this->id);
}
+ /**
+ * Save the given URLs as related links/attachments to the db
+ *
+ * follow redirects and save all available file information
+ * (mimetype, date, size, oembed, etc.)
+ *
+ * @return void
+ */
+ function saveKnownUrls($urls)
+ {
+ // @fixme validation?
+ foreach ($urls as $url) {
+ File::processNew($url, $this->id);
+ }
+ }
+
+ /**
+ * @private callback
+ */
function saveUrl($data) {
list($url, $notice_id) = $data;
File::processNew($url, $notice_id);
@@ -502,17 +583,17 @@ class Notice extends Memcached_DataObject
}
}
- function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array('Notice', '_publicStreamDirect'),
array(),
'public',
- $offset, $limit, $since_id, $max_id, $since);
+ $offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
}
- function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $max_id=0)
{
$notice = new Notice();
@@ -541,10 +622,6 @@ class Notice extends Memcached_DataObject
$notice->whereAdd('id <= ' . $max_id);
}
- if (!is_null($since)) {
- $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
- }
-
$ids = array();
if ($notice->find()) {
@@ -559,17 +636,17 @@ class Notice extends Memcached_DataObject
return $ids;
}
- function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array('Notice', '_conversationStreamDirect'),
array($id),
'notice:conversation_ids:'.$id,
- $offset, $limit, $since_id, $max_id, $since);
+ $offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
}
- function _conversationStreamDirect($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function _conversationStreamDirect($id, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
$notice = new Notice();
@@ -592,10 +669,6 @@ class Notice extends Memcached_DataObject
$notice->whereAdd('id <= ' . $max_id);
}
- if (!is_null($since)) {
- $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
- }
-
$ids = array();
if ($notice->find()) {
@@ -677,11 +750,39 @@ 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);
- Inbox::bulkInsert($this->id, array_keys($ni));
+ $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;
}
@@ -714,6 +815,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()
@@ -740,7 +877,7 @@ class Notice extends Memcached_DataObject
foreach (array_unique($match[1]) as $nickname) {
/* XXX: remote groups. */
- $group = User_group::getForNickname($nickname);
+ $group = User_group::getForNickname($nickname, $profile);
if (empty($group)) {
continue;
@@ -797,8 +934,51 @@ 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();
+
+ self::blow('reply:stream:%d', $user->id);
+ }
+ }
+
+ 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
@@ -807,76 +987,47 @@ 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();
+ $sender = Profile::staticGet($this->profile_id);
- 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]);
- }
+ // @todo ideally this parser information would only
+ // be calculated once.
- $sender = Profile::staticGet($this->profile_id);
+ $mentions = common_find_mentions($this->content, $this);
$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;
}
}
}
@@ -917,9 +1068,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()
{
@@ -942,7 +1094,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;
+ }
}
}
@@ -951,7 +1106,7 @@ class Notice extends Memcached_DataObject
return $groups;
}
- function asAtomEntry($namespace=false, $source=false)
+ function asAtomEntry($namespace=false, $source=false, $author=true)
{
$profile = $this->getProfile();
@@ -962,6 +1117,8 @@ 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:media' => 'http://purl.org/syndication/atommedia',
+ 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
'xmlns:ostatus' => 'http://ostatus.org/schema/1.0');
} else {
$attrs = array();
@@ -993,12 +1150,14 @@ class Notice extends Memcached_DataObject
}
$xs->element('title', null, $this->content);
- $xs->element('summary', null, $this->content);
- $xs->raw($profile->asAtomAuthor());
- $xs->raw($profile->asActivityActor());
+ if ($author) {
+ $xs->raw($profile->asAtomAuthor());
+ $xs->raw($profile->asActivityActor());
+ }
$xs->element('link', array('rel' => 'alternate',
+ 'type' => 'text/html',
'href' => $this->bestUrl()));
$xs->element('id', null, $this->uri);
@@ -1045,6 +1204,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)) {
@@ -1090,6 +1260,21 @@ class Notice extends Memcached_DataObject
return $xs->getString();
}
+ /**
+ * Returns an XML string fragment with a reference to a notice as an
+ * Activity Streams noun object with the given element type.
+ *
+ * Assumes that 'activity' namespace has been previously defined.
+ *
+ * @param string $element one of 'subject', 'object', 'target'
+ * @return string
+ */
+ function asActivityNoun($element)
+ {
+ $noun = ActivityObject::fromNotice($this);
+ return $noun->asString('activity:' . $element);
+ }
+
function bestUrl()
{
if (!empty($this->url)) {
@@ -1102,16 +1287,16 @@ class Notice extends Memcached_DataObject
}
}
- function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+ function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
$cache = common_memcache();
if (empty($cache) ||
- $since_id != 0 || $max_id != 0 || (!is_null($since) && $since > 0) ||
+ $since_id != 0 || $max_id != 0 ||
is_null($limit) ||
($offset + $limit) > NOTICE_CACHE_WINDOW) {
return call_user_func_array($fn, array_merge($args, array($offset, $limit, $since_id,
- $max_id, $since)));
+ $max_id)));
}
$idkey = common_cache_key($cachekey);
@@ -1487,6 +1672,14 @@ class Notice extends Memcached_DataObject
function distribute()
{
+ // We always insert for the author so they don't
+ // have to wait
+
+ $user = User::staticGet('id', $this->profile_id);
+ if (!empty($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.
diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php
index c27dcdfd6..47ed6b22d 100644
--- a/classes/Notice_inbox.php
+++ b/classes/Notice_inbox.php
@@ -49,12 +49,12 @@ class Notice_inbox extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
- function stream($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false)
+ function stream($user_id, $offset, $limit, $since_id, $max_id, $own=false)
{
throw new Exception('Notice_inbox no longer used; use Inbox');
}
- function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id, $since)
+ function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id)
{
throw new Exception('Notice_inbox no longer used; use Inbox');
}
diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php
index 4fd76e8ea..a5d0716a7 100644
--- a/classes/Notice_tag.php
+++ b/classes/Notice_tag.php
@@ -46,7 +46,7 @@ class Notice_tag extends Memcached_DataObject
return Notice::getStreamByIds($ids);
}
- function _streamDirect($tag, $offset, $limit, $since_id, $max_id, $since)
+ function _streamDirect($tag, $offset, $limit, $since_id, $max_id)
{
$nt = new Notice_tag();
@@ -63,10 +63,6 @@ class Notice_tag extends Memcached_DataObject
$nt->whereAdd('notice_id < ' . $max_id);
}
- if (!is_null($since)) {
- $nt->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
- }
-
$nt->orderBy('notice_id DESC');
if (!is_null($offset)) {
diff --git a/classes/Profile.php b/classes/Profile.php
index 494c697e4..0322c9358 100644
--- a/classes/Profile.php
+++ b/classes/Profile.php
@@ -163,27 +163,27 @@ class Profile extends Memcached_DataObject
return null;
}
- function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null)
+ function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array($this, '_streamTaggedDirect'),
array($tag),
'profile:notice_ids_tagged:' . $this->id . ':' . $tag,
- $offset, $limit, $since_id, $max_id, $since);
+ $offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
}
- function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null)
+ function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
// XXX: I'm not sure this is going to be any faster. It probably isn't.
$ids = Notice::stream(array($this, '_streamDirect'),
array(),
'profile:notice_ids:' . $this->id,
- $offset, $limit, $since_id, $max_id, $since);
+ $offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
}
- function _streamTaggedDirect($tag, $offset, $limit, $since_id, $max_id, $since)
+ function _streamTaggedDirect($tag, $offset, $limit, $since_id, $max_id)
{
// XXX It would be nice to do this without a join
@@ -202,10 +202,6 @@ class Profile extends Memcached_DataObject
$query .= " and id < $max_id";
}
- if (!is_null($since)) {
- $query .= " and created > '" . date('Y-m-d H:i:s', $since) . "'";
- }
-
$query .= ' order by id DESC';
if (!is_null($offset)) {
@@ -223,7 +219,7 @@ class Profile extends Memcached_DataObject
return $ids;
}
- function _streamDirect($offset, $limit, $since_id, $max_id, $since = null)
+ function _streamDirect($offset, $limit, $since_id, $max_id)
{
$notice = new Notice();
@@ -240,10 +236,6 @@ class Profile extends Memcached_DataObject
$notice->whereAdd('id <= ' . $max_id);
}
- if (!is_null($since)) {
- $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
- }
-
$notice->orderBy('id DESC');
if (!is_null($offset)) {
@@ -290,6 +282,32 @@ class Profile extends Memcached_DataObject
}
}
+ function getGroups($offset=0, $limit=null)
+ {
+ $qry =
+ 'SELECT user_group.* ' .
+ 'FROM user_group JOIN group_member '.
+ 'ON user_group.id = group_member.group_id ' .
+ 'WHERE group_member.profile_id = %d ' .
+ 'ORDER BY group_member.created DESC ';
+
+ if ($offset>0 && !is_null($limit)) {
+ if ($offset) {
+ if (common_config('db','type') == 'pgsql') {
+ $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+ } else {
+ $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+ }
+ }
+ }
+
+ $groups = new User_group();
+
+ $cnt = $groups->query(sprintf($qry, $this->id));
+
+ return $groups;
+ }
+
function avatarUrl($size=AVATAR_PROFILE_SIZE)
{
$avatar = $this->getAvatar($size);
@@ -725,6 +743,10 @@ class Profile extends Memcached_DataObject
case Right::CONFIGURESITE:
$result = $this->hasRole(Profile_role::ADMINISTRATOR);
break;
+ case Right::GRANTROLE:
+ case Right::REVOKEROLE:
+ $result = $this->hasRole(Profile_role::OWNER);
+ break;
case Right::NEWNOTICE:
case Right::NEWMESSAGE:
case Right::SUBSCRIBE:
@@ -792,44 +814,17 @@ class Profile extends Memcached_DataObject
* Returns an XML string fragment with profile information as an
* Activity Streams noun object with the given element type.
*
- * Assumes that 'activity' namespace has been previously defined.
+ * Assumes that 'activity', 'georss', and 'poco' namespace has been
+ * previously defined.
*
* @param string $element one of 'actor', 'subject', 'object', 'target'
+ *
* @return string
*/
function asActivityNoun($element)
{
- $xs = new XMLStringer(true);
-
- $xs->elementStart('activity:' . $element);
- $xs->element(
- 'activity:object-type',
- null,
- 'http://activitystrea.ms/schema/1.0/person'
- );
- $xs->element(
- 'id',
- null,
- $this->getUri()
- );
- $xs->element('title', null, $this->getBestName());
-
- $avatar = $this->getAvatar(AVATAR_PROFILE_SIZE);
-
- $xs->element(
- 'link', array(
- 'type' => empty($avatar) ? 'image/png' : $avatar->mediatype,
- 'rel' => 'avatar',
- 'href' => empty($avatar)
- ? Avatar::defaultImage(AVATAR_PROFILE_SIZE)
- : $avatar->displayUrl()
- ),
- ''
- );
-
- $xs->elementEnd('activity:' . $element);
-
- return $xs->getString();
+ $noun = ActivityObject::fromProfile($this);
+ return $noun->asString('activity:' . $element);
}
/**
@@ -841,31 +836,37 @@ class Profile extends Memcached_DataObject
{
$uri = null;
- // check for a local user first
- $user = User::staticGet('id', $this->id);
+ // give plugins a chance to set the URI
+ if (Event::handle('StartGetProfileUri', array($this, &$uri))) {
- if (!empty($user)) {
- $uri = common_local_url(
- 'userbyid',
- array('id' => $user->id)
- );
- } else {
-
- // give plugins a chance to set the URI
- if (Event::handle('StartGetProfileUri', array($this, &$uri))) {
+ // check for a local user first
+ $user = User::staticGet('id', $this->id);
+ if (!empty($user)) {
+ $uri = $user->uri;
+ } else {
// return OMB profile if any
$remote = Remote_profile::staticGet('id', $this->id);
-
if (!empty($remote)) {
$uri = $remote->uri;
}
-
- Event::handle('EndGetProfileUri', array($this, &$uri));
}
+ Event::handle('EndGetProfileUri', array($this, &$uri));
}
return $uri;
}
+ function hasBlocked($other)
+ {
+ $block = Profile_block::get($this->id, $other->id);
+
+ if (empty($block)) {
+ $result = false;
+ } else {
+ $result = true;
+ }
+
+ return $result;
+ }
}
diff --git a/classes/Profile_role.php b/classes/Profile_role.php
index bf2c453ed..d0a0b31f0 100644
--- a/classes/Profile_role.php
+++ b/classes/Profile_role.php
@@ -53,4 +53,21 @@ class Profile_role extends Memcached_DataObject
const ADMINISTRATOR = 'administrator';
const SANDBOXED = 'sandboxed';
const SILENCED = 'silenced';
+
+ public static function isValid($role)
+ {
+ // @fixme could probably pull this from class constants
+ $known = array(self::OWNER,
+ self::MODERATOR,
+ self::ADMINISTRATOR,
+ self::SANDBOXED,
+ self::SILENCED);
+ return in_array($role, $known);
+ }
+
+ public static function isSettable($role)
+ {
+ $allowedRoles = array('administrator', 'moderator');
+ return self::isValid($role) && in_array($role, $allowedRoles);
+ }
}
diff --git a/classes/Reply.php b/classes/Reply.php
index 49b1e05e5..659e04c92 100644
--- a/classes/Reply.php
+++ b/classes/Reply.php
@@ -22,16 +22,16 @@ class Reply extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
- function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null)
+ function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$ids = Notice::stream(array('Reply', '_streamDirect'),
array($user_id),
'reply:stream:' . $user_id,
- $offset, $limit, $since_id, $max_id, $since);
+ $offset, $limit, $since_id, $max_id);
return $ids;
}
- function _streamDirect($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null)
+ function _streamDirect($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
$reply = new Reply();
$reply->profile_id = $user_id;
@@ -44,10 +44,6 @@ class Reply extends Memcached_DataObject
$reply->whereAdd('notice_id < ' . $max_id);
}
- if (!is_null($since)) {
- $reply->whereAdd('modified > \'' . date('Y-m-d H:i:s', $since) . '\'');
- }
-
$reply->orderBy('notice_id DESC');
if (!is_null($offset)) {
diff --git a/classes/Subscription.php b/classes/Subscription.php
index faf1331cd..9cef2df1a 100644
--- a/classes/Subscription.php
+++ b/classes/Subscription.php
@@ -24,7 +24,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-class Subscription extends Memcached_DataObject
+class Subscription extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
@@ -34,8 +34,8 @@ class Subscription extends Memcached_DataObject
public $subscribed; // int(4) primary_key not_null
public $jabber; // tinyint(1) default_1
public $sms; // tinyint(1) default_1
- public $token; // varchar(255)
- public $secret; // varchar(255)
+ public $token; // varchar(255)
+ public $secret; // varchar(255)
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@@ -45,9 +45,177 @@ class Subscription extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
-
+
function pkeyGet($kv)
{
return Memcached_DataObject::pkeyGet('Subscription', $kv);
}
+
+ /**
+ * Make a new subscription
+ *
+ * @param Profile $subscriber party to receive new notices
+ * @param Profile $other party sending notices; publisher
+ *
+ * @return Subscription new subscription
+ */
+
+ static function start($subscriber, $other)
+ {
+ if (!$subscriber->hasRight(Right::SUBSCRIBE)) {
+ throw new Exception(_('You have been banned from subscribing.'));
+ }
+
+ if (self::exists($subscriber, $other)) {
+ throw new Exception(_('Already subscribed!'));
+ }
+
+ if ($other->hasBlocked($subscriber)) {
+ throw new Exception(_('User has blocked you.'));
+ }
+
+ if (Event::handle('StartSubscribe', array($subscriber, $other))) {
+
+ $sub = new Subscription();
+
+ $sub->subscriber = $subscriber->id;
+ $sub->subscribed = $other->id;
+ $sub->created = common_sql_now();
+
+ $result = $sub->insert();
+
+ if (!$result) {
+ common_log_db_error($sub, 'INSERT', __FILE__);
+ throw new Exception(_('Could not save subscription.'));
+ }
+
+ $sub->notify();
+
+ self::blow('user:notices_with_friends:%d', $subscriber->id);
+
+ $subscriber->blowSubscriptionsCount();
+ $other->blowSubscribersCount();
+
+ $otherUser = User::staticGet('id', $other->id);
+
+ if (!empty($otherUser) &&
+ $otherUser->autosubscribe &&
+ !self::exists($other, $subscriber) &&
+ !$subscriber->hasBlocked($other)) {
+
+ $auto = new Subscription();
+
+ $auto->subscriber = $subscriber->id;
+ $auto->subscribed = $other->id;
+ $auto->created = common_sql_now();
+
+ $result = $auto->insert();
+
+ if (!$result) {
+ common_log_db_error($auto, 'INSERT', __FILE__);
+ throw new Exception(_('Could not save subscription.'));
+ }
+
+ $auto->notify();
+ }
+
+ Event::handle('EndSubscribe', array($subscriber, $other));
+ }
+
+ return true;
+ }
+
+ function notify()
+ {
+ # XXX: add other notifications (Jabber, SMS) here
+ # XXX: queue this and handle it offline
+ # XXX: Whatever happens, do it in Twitter-like API, too
+
+ $this->notifyEmail();
+ }
+
+ function notifyEmail()
+ {
+ $subscribedUser = User::staticGet('id', $this->subscribed);
+
+ if (!empty($subscribedUser)) {
+
+ $subscriber = Profile::staticGet('id', $this->subscriber);
+
+ mail_subscribe_notify_profile($subscribedUser, $subscriber);
+ }
+ }
+
+ /**
+ * Cancel a subscription
+ *
+ */
+
+ function cancel($subscriber, $other)
+ {
+ if (!self::exists($subscriber, $other)) {
+ throw new Exception(_('Not subscribed!'));
+ }
+
+ // Don't allow deleting self subs
+
+ if ($subscriber->id == $other->id) {
+ throw new Exception(_('Couldn\'t delete self-subscription.'));
+ }
+
+ if (Event::handle('StartUnsubscribe', array($subscriber, $other))) {
+
+ $sub = Subscription::pkeyGet(array('subscriber' => $subscriber->id,
+ 'subscribed' => $other->id));
+
+ // note we checked for existence above
+
+ assert(!empty($sub));
+
+ // @todo: move this block to EndSubscribe handler for
+ // OMB plugin when it exists.
+
+ if (!empty($sub->token)) {
+
+ $token = new Token();
+
+ $token->tok = $sub->token;
+
+ if ($token->find(true)) {
+
+ $result = $token->delete();
+
+ if (!$result) {
+ common_log_db_error($token, 'DELETE', __FILE__);
+ throw new Exception(_('Couldn\'t delete subscription OMB token.'));
+ }
+ } else {
+ common_log(LOG_ERR, "Couldn't find credentials with token {$token->tok}");
+ }
+ }
+
+ $result = $sub->delete();
+
+ if (!$result) {
+ common_log_db_error($sub, 'DELETE', __FILE__);
+ throw new Exception(_('Couldn\'t delete subscription.'));
+ }
+
+ self::blow('user:notices_with_friends:%d', $subscriber->id);
+
+ $subscriber->blowSubscriptionsCount();
+ $other->blowSubscribersCount();
+
+ Event::handle('EndUnsubscribe', array($subscriber, $other));
+ }
+
+ return;
+ }
+
+ function exists($subscriber, $other)
+ {
+ $sub = Subscription::pkeyGet(array('subscriber' => $subscriber->id,
+ 'subscribed' => $other->id));
+ return (empty($sub)) ? false : true;
+ }
}
diff --git a/classes/User.php b/classes/User.php
index 72c3f39e9..fade0f35d 100644
--- a/classes/User.php
+++ b/classes/User.php
@@ -80,11 +80,7 @@ class User extends Memcached_DataObject
function isSubscribed($other)
{
- assert(!is_null($other));
- // XXX: cache results of this query
- $sub = Subscription::pkeyGet(array('subscriber' => $this->id,
- 'subscribed' => $other->id));
- return (is_null($sub)) ? false : true;
+ return Subscription::exists($this->getProfile(), $other);
}
// 'update' won't write key columns, so we have to do it ourselves.
@@ -167,17 +163,8 @@ class User extends Memcached_DataObject
function hasBlocked($other)
{
-
- $block = Profile_block::get($this->id, $other->id);
-
- if (is_null($block)) {
- $result = false;
- } else {
- $result = true;
- $block->free();
- }
-
- return $result;
+ $profile = $this->getProfile();
+ return $profile->hasBlocked($other);
}
/**
@@ -219,6 +206,7 @@ class User extends Memcached_DataObject
if(! User::allowed_nickname($nickname)){
common_log(LOG_WARNING, sprintf("Attempted to register a nickname that is not allowed: %s", $profile->nickname),
__FILE__);
+ return false;
}
$profile->profileurl = common_profile_url($nickname);
@@ -469,28 +457,28 @@ class User extends Memcached_DataObject
return $user;
}
- function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+ function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{
- $ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id, $since);
+ $ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id);
return Notice::getStreamByIds($ids);
}
- function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) {
+ function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) {
$profile = $this->getProfile();
if (!$profile) {
return null;
} else {
- return $profile->getTaggedNotices($tag, $offset, $limit, $since_id, $before_id, $since);
+ return $profile->getTaggedNotices($tag, $offset, $limit, $since_id, $before_id);
}
}
- function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+ function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{
$profile = $this->getProfile();
if (!$profile) {
return null;
} else {
- return $profile->getNotices($offset, $limit, $since_id, $before_id, $since);
+ return $profile->getNotices($offset, $limit, $since_id, $before_id);
}
}
@@ -500,24 +488,24 @@ class User extends Memcached_DataObject
return Notice::getStreamByIds($ids);
}
- function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+ function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{
- return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, $since, false);
+ return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, false);
}
- function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+ function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{
- return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, $since, true);
+ return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, true);
}
- function friendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+ function friendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{
- return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, $since, false);
+ return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, false);
}
- function ownFriendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+ function ownFriendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
{
- return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, $since, true);
+ return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, true);
}
function blowFavesCache()
@@ -625,28 +613,8 @@ class User extends Memcached_DataObject
function getGroups($offset=0, $limit=null)
{
- $qry =
- 'SELECT user_group.* ' .
- 'FROM user_group JOIN group_member '.
- 'ON user_group.id = group_member.group_id ' .
- 'WHERE group_member.profile_id = %d ' .
- 'ORDER BY group_member.created DESC ';
-
- if ($offset>0 && !is_null($limit)) {
- if ($offset) {
- if (common_config('db','type') == 'pgsql') {
- $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
- } else {
- $qry .= ' LIMIT ' . $offset . ', ' . $limit;
- }
- }
- }
-
- $groups = new User_group();
-
- $cnt = $groups->query(sprintf($qry, $this->id));
-
- return $groups;
+ $profile = $this->getProfile();
+ return $profile->getGroups($offset, $limit);
}
function getSubscriptions($offset=0, $limit=null)
@@ -802,7 +770,7 @@ class User extends Memcached_DataObject
return Notice::getStreamByIds($ids);
}
- function _repeatedByMeDirect($offset, $limit, $since_id, $max_id, $since)
+ function _repeatedByMeDirect($offset, $limit, $since_id, $max_id)
{
$notice = new Notice();
@@ -826,10 +794,6 @@ class User extends Memcached_DataObject
$notice->whereAdd('id <= ' . $max_id);
}
- if (!is_null($since)) {
- $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
- }
-
$ids = array();
if ($notice->find()) {
@@ -849,12 +813,12 @@ class User extends Memcached_DataObject
$ids = Notice::stream(array($this, '_repeatsOfMeDirect'),
array(),
'user:repeats_of_me:'.$this->id,
- $offset, $limit, $since_id, $max_id, null);
+ $offset, $limit, $since_id, $max_id);
return Notice::getStreamByIds($ids);
}
- function _repeatsOfMeDirect($offset, $limit, $since_id, $max_id, $since)
+ function _repeatsOfMeDirect($offset, $limit, $since_id, $max_id)
{
$qry =
'SELECT DISTINCT original.id AS id ' .
@@ -869,10 +833,6 @@ class User extends Memcached_DataObject
$qry .= 'AND original.id <= ' . $max_id . ' ';
}
- if (!is_null($since)) {
- $qry .= 'AND original.modified > \'' . date('Y-m-d H:i:s', $since) . '\' ';
- }
-
// NOTE: we sort by fave time, not by notice time!
$qry .= 'ORDER BY original.id DESC ';
diff --git a/classes/User_group.php b/classes/User_group.php
index 379e6b721..7be55163a 100644
--- a/classes/User_group.php
+++ b/classes/User_group.php
@@ -10,21 +10,23 @@ class User_group extends Memcached_DataObject
public $__table = 'user_group'; // table name
public $id; // int(4) primary_key not_null
- public $nickname; // varchar(64) unique_key
+ public $nickname; // varchar(64)
public $fullname; // varchar(255)
public $homepage; // varchar(255)
- public $description; // text()
+ public $description; // text
public $location; // varchar(255)
public $original_logo; // varchar(255)
public $homepage_logo; // varchar(255)
public $stream_logo; // varchar(255)
public $mini_logo; // varchar(255)
public $design_id; // int(4)
- public $created; // datetime() not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
+ public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
+ public $uri; // varchar(255) unique_key
+ public $mainpage; // varchar(255)
/* Static get */
- function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User_group',$k,$v); }
+ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('User_group',$k,$v); }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
@@ -39,14 +41,44 @@ class User_group extends Memcached_DataObject
function homeUrl()
{
- return common_local_url('showgroup',
- array('nickname' => $this->nickname));
+ $url = null;
+ if (Event::handle('StartUserGroupHomeUrl', array($this, &$url))) {
+ // normally stored in mainpage, but older ones may be null
+ if (!empty($this->mainpage)) {
+ $url = $this->mainpage;
+ } else {
+ $url = common_local_url('showgroup',
+ array('nickname' => $this->nickname));
+ }
+ }
+ Event::handle('EndUserGroupHomeUrl', array($this, &$url));
+ return $url;
+ }
+
+ function getUri()
+ {
+ $uri = null;
+ if (Event::handle('StartUserGroupGetUri', array($this, &$uri))) {
+ if (!empty($this->uri)) {
+ $uri = $this->uri;
+ } else {
+ $uri = common_local_url('groupbyid',
+ array('id' => $this->id));
+ }
+ }
+ Event::handle('EndUserGroupGetUri', array($this, &$uri));
+ return $uri;
}
function permalink()
{
- return common_local_url('groupbyid',
- array('id' => $this->id));
+ $url = null;
+ if (Event::handle('StartUserGroupPermalink', array($this, &$url))) {
+ $url = common_local_url('groupbyid',
+ array('id' => $this->id));
+ }
+ Event::handle('EndUserGroupPermalink', array($this, &$url));
+ return $url;
}
function getNotices($offset, $limit, $since_id=null, $max_id=null)
@@ -59,7 +91,7 @@ class User_group extends Memcached_DataObject
return Notice::getStreamByIds($ids);
}
- function _streamDirect($offset, $limit, $since_id, $max_id, $since)
+ function _streamDirect($offset, $limit, $since_id, $max_id)
{
$inbox = new Group_inbox();
@@ -76,10 +108,6 @@ class User_group extends Memcached_DataObject
$inbox->whereAdd('notice_id <= ' . $max_id);
}
- if (!is_null($since)) {
- $inbox->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
- }
-
$inbox->orderBy('notice_id DESC');
if (!is_null($offset)) {
@@ -251,12 +279,26 @@ class User_group extends Memcached_DataObject
return true;
}
- static function getForNickname($nickname)
+ static function getForNickname($nickname, $profile=null)
{
$nickname = common_canonical_nickname($nickname);
- $group = User_group::staticGet('nickname', $nickname);
+
+ // Are there any matching remote groups this profile's in?
+ if ($profile) {
+ $group = $profile->getGroups();
+ while ($group->fetch()) {
+ if ($group->nickname == $nickname) {
+ // @fixme is this the best way?
+ return clone($group);
+ }
+ }
+ }
+
+ // If not, check local groups.
+
+ $group = Local_group::staticGet('nickname', $nickname);
if (!empty($group)) {
- return $group;
+ return User_group::staticGet('id', $group->group_id);
}
$alias = Group_alias::staticGet('alias', $nickname);
if (!empty($alias)) {
@@ -367,25 +409,41 @@ class User_group extends Memcached_DataObject
return $xs->getString();
}
+ /**
+ * Returns an XML string fragment with group information as an
+ * Activity Streams <activity:subject> element.
+ *
+ * Assumes that 'activity' namespace has been previously defined.
+ *
+ * @return string
+ */
function asActivitySubject()
{
- $xs = new XMLStringer(true);
+ return $this->asActivityNoun('subject');
+ }
- $xs->elementStart('activity:subject');
- $xs->element('activity:object', null, 'http://activitystrea.ms/schema/1.0/group');
- $xs->element('id', null, $this->permalink());
- $xs->element('title', null, $this->getBestName());
- $xs->element(
- 'link', array(
- 'rel' => 'avatar',
- 'href' => empty($this->homepage_logo)
- ? User_group::defaultLogo(AVATAR_PROFILE_SIZE)
- : $this->homepage_logo
- )
- );
- $xs->elementEnd('activity:subject');
+ /**
+ * Returns an XML string fragment with group information as an
+ * Activity Streams noun object with the given element type.
+ *
+ * Assumes that 'activity', 'georss', and 'poco' namespace has been
+ * previously defined.
+ *
+ * @param string $element one of 'actor', 'subject', 'object', 'target'
+ *
+ * @return string
+ */
+ function asActivityNoun($element)
+ {
+ $noun = ActivityObject::fromGroup($this);
+ return $noun->asString('activity:' . $element);
+ }
- return $xs->getString();
+ function getAvatar()
+ {
+ return empty($this->homepage_logo)
+ ? User_group::defaultLogo(AVATAR_PROFILE_SIZE)
+ : $this->homepage_logo;
}
static function register($fields) {
@@ -397,34 +455,42 @@ class User_group extends Memcached_DataObject
$group = new User_group();
$group->query('BEGIN');
+
+ if (empty($uri)) {
+ // fill in later...
+ $uri = null;
+ }
$group->nickname = $nickname;
$group->fullname = $fullname;
$group->homepage = $homepage;
$group->description = $description;
$group->location = $location;
+ $group->uri = $uri;
+ $group->mainpage = $mainpage;
$group->created = common_sql_now();
$result = $group->insert();
if (!$result) {
common_log_db_error($group, 'INSERT', __FILE__);
- $this->serverError(
- _('Could not create group.'),
- 500,
- $this->format
- );
- return;
+ throw new ServerException(_('Could not create group.'));
+ }
+
+ if (!isset($uri) || empty($uri)) {
+ $orig = clone($group);
+ $group->uri = common_local_url('groupbyid', array('id' => $group->id));
+ $result = $group->update($orig);
+ if (!$result) {
+ common_log_db_error($group, 'UPDATE', __FILE__);
+ throw new ServerException(_('Could not set group URI.'));
+ }
}
+
$result = $group->setAliases($aliases);
if (!$result) {
- $this->serverError(
- _('Could not create aliases.'),
- 500,
- $this->format
- );
- return;
+ throw new ServerException(_('Could not create aliases.'));
}
$member = new Group_member();
@@ -438,12 +504,22 @@ class User_group extends Memcached_DataObject
if (!$result) {
common_log_db_error($member, 'INSERT', __FILE__);
- $this->serverError(
- _('Could not set group membership.'),
- 500,
- $this->format
- );
- return;
+ throw new ServerException(_('Could not set group membership.'));
+ }
+
+ if ($local) {
+ $local_group = new Local_group();
+
+ $local_group->group_id = $group->id;
+ $local_group->nickname = $nickname;
+ $local_group->created = common_sql_now();
+
+ $result = $local_group->insert();
+
+ if (!$result) {
+ common_log_db_error($local_group, 'INSERT', __FILE__);
+ throw new ServerException(_('Could not save local group info.'));
+ }
}
$group->query('COMMIT');
diff --git a/classes/statusnet.ini b/classes/statusnet.ini
index 81c1b68b2..3fb8ee208 100644
--- a/classes/statusnet.ini
+++ b/classes/statusnet.ini
@@ -245,13 +245,6 @@ modified = 384
group_id = K
profile_id = K
-[invitation]
-code = 130
-user_id = 129
-address = 130
-address_type = 130
-created = 142
-
[inbox]
user_id = 129
notice_ids = 66
@@ -259,9 +252,26 @@ notice_ids = 66
[inbox__keys]
user_id = K
+[invitation]
+code = 130
+user_id = 129
+address = 130
+address_type = 130
+created = 142
+
[invitation__keys]
code = K
+[local_group]
+group_id = 129
+nickname = 2
+created = 142
+modified = 384
+
+[local_group__keys]
+group_id = K
+nickname = U
+
[location_namespace]
id = 129
description = 2
@@ -369,7 +379,7 @@ icon = 130
source_url = 2
organization = 2
homepage = 2
-callback_url = 130
+callback_url = 2
type = 17
access_type = 17
created = 142
@@ -440,13 +450,13 @@ tag = K
[queue_item]
id = 129
-frame = 66
+frame = 194
transport = 130
created = 142
claimed = 14
[queue_item__keys]
-id = K
+id = N
[related_group]
group_id = 129
@@ -593,10 +603,11 @@ mini_logo = 2
design_id = 1
created = 142
modified = 384
+uri = 2
+mainpage = 2
[user_group__keys]
id = N
-nickname = U
[user_openid]
canonical = 130
@@ -627,4 +638,3 @@ modified = 384
[user_location_prefs__keys]
user_id = K
-