summaryrefslogtreecommitdiff
path: root/classes
diff options
context:
space:
mode:
Diffstat (limited to 'classes')
-rw-r--r--classes/Design.php7
-rw-r--r--classes/Fave.php2
-rw-r--r--classes/File.php14
-rw-r--r--classes/File_oembed.php55
-rw-r--r--classes/File_thumbnail.php6
-rw-r--r--classes/Notice.php169
-rw-r--r--classes/Notice_inbox.php41
-rw-r--r--classes/Profile.php6
-rw-r--r--classes/User_group.php48
9 files changed, 266 insertions, 82 deletions
diff --git a/classes/Design.php b/classes/Design.php
index dc1712aff..19c9e0292 100644
--- a/classes/Design.php
+++ b/classes/Design.php
@@ -107,7 +107,7 @@ class Design extends Memcached_DataObject
static function toWebColor($color)
{
- if (is_null($color)) {
+ if ($color == null) {
return null;
}
@@ -204,7 +204,10 @@ class Design extends Memcached_DataObject
'disposition');
foreach ($attrs as $attr) {
- $siteDesign->$attr = common_config('design', $attr);
+ $val = common_config('design', $attr);
+ if ($val !== false) {
+ $siteDesign->$attr = $val;
+ }
}
}
diff --git a/classes/Fave.php b/classes/Fave.php
index d1e3b01b3..11e876ff1 100644
--- a/classes/Fave.php
+++ b/classes/Fave.php
@@ -79,7 +79,7 @@ class Fave extends Memcached_DataObject
$qry .= 'ORDER BY modified DESC ';
if (!is_null($offset)) {
- $qry .= "LIMIT $offset, $limit";
+ $qry .= "LIMIT $limit OFFSET $offset";
}
$fav->query($qry);
diff --git a/classes/File.php b/classes/File.php
index 56d9f9827..959301eda 100644
--- a/classes/File.php
+++ b/classes/File.php
@@ -79,9 +79,8 @@ class File extends Memcached_DataObject
if (isset($redir_data['type'])
&& ('text/html' === substr($redir_data['type'], 0, 9))
- && ($oembed_data = File_oembed::_getOembed($given_url))
- && isset($oembed_data['json'])) {
- File_oembed::saveNew($oembed_data['json'], $file_id);
+ && ($oembed_data = File_oembed::_getOembed($given_url))) {
+ File_oembed::saveNew($oembed_data, $file_id);
}
return $x;
}
@@ -94,7 +93,6 @@ class File extends Memcached_DataObject
if (empty($file)) {
$file_redir = File_redirection::staticGet('url', $given_url);
if (empty($file_redir)) {
- common_debug("processNew() '$given_url' not a known redirect.\n");
$redir_data = File_redirection::where($given_url);
$redir_url = $redir_data['url'];
if ($redir_url === $given_url) {
@@ -115,7 +113,9 @@ class File extends Memcached_DataObject
if (empty($x)) {
$x = File::staticGet($file_id);
- if (empty($x)) die('Impossible!');
+ if (empty($x)) {
+ throw new ServerException("Robin thinks something is impossible.");
+ }
}
File_to_post::processNew($file_id, $notice_id);
@@ -123,6 +123,7 @@ class File extends Memcached_DataObject
}
function isRespectsQuota($user,$fileSize) {
+
if ($fileSize > common_config('attachments', 'file_quota')) {
return sprintf(_('No file may be larger than %d bytes ' .
'and the file you sent was %d bytes. Try to upload a smaller version.'),
@@ -136,8 +137,7 @@ class File extends Memcached_DataObject
if ($total > common_config('attachments', 'user_quota')) {
return sprintf(_('A file this large would exceed your user quota of %d bytes.'), common_config('attachments', 'user_quota'));
}
-
- $query .= ' month(modified) = month(now()) and year(modified) = year(now())';
+ $query .= ' AND EXTRACT(month FROM file.modified) = EXTRACT(month FROM now()) and EXTRACT(year FROM file.modified) = EXTRACT(year FROM now())';
$this->query($query);
$this->fetch();
$total = $this->total + $fileSize;
diff --git a/classes/File_oembed.php b/classes/File_oembed.php
index 69230e4a4..bbf112729 100644
--- a/classes/File_oembed.php
+++ b/classes/File_oembed.php
@@ -56,33 +56,46 @@ class File_oembed extends Memcached_DataObject
return array(false, false, false);
}
- function _getOembed($url, $maxwidth = 500, $maxheight = 400, $format = 'json') {
- $cmd = common_config('oohembed', 'endpoint') . '?url=' . urlencode($url);
- if (is_int($maxwidth)) $cmd .= "&maxwidth=$maxwidth";
- if (is_int($maxheight)) $cmd .= "&maxheight=$maxheight";
- if (is_string($format)) $cmd .= "&format=$format";
- $oe = @file_get_contents($cmd);
- if (false === $oe) return false;
- return array($format => (('json' === $format) ? json_decode($oe, true) : $oe));
+ function _getOembed($url, $maxwidth = 500, $maxheight = 400) {
+ require_once INSTALLDIR.'/extlib/Services/oEmbed.php';
+ $parameters = array(
+ 'maxwidth'=>$maxwidth,
+ 'maxheight'=>$maxheight,
+ );
+ try{
+ $oEmbed = new Services_oEmbed($url);
+ $object = $oEmbed->getObject($parameters);
+ return $object;
+ }catch(Exception $e){
+ try{
+ $oEmbed = new Services_oEmbed($url, array(
+ Services_oEmbed::OPTION_API => common_config('oohembed', 'endpoint')
+ ));
+ $object = $oEmbed->getObject($parameters);
+ return $object;
+ }catch(Exception $ex){
+ return false;
+ }
+ }
}
function saveNew($data, $file_id) {
$file_oembed = new File_oembed;
$file_oembed->file_id = $file_id;
- $file_oembed->version = $data['version'];
- $file_oembed->type = $data['type'];
- if (!empty($data['provider_name'])) $file_oembed->provider = $data['provider_name'];
- if (!isset($file_oembed->provider) && !empty($data['provide'])) $file_oembed->provider = $data['provider'];
- if (!empty($data['provide_url'])) $file_oembed->provider_url = $data['provider_url'];
- if (!empty($data['width'])) $file_oembed->width = intval($data['width']);
- if (!empty($data['height'])) $file_oembed->height = intval($data['height']);
- if (!empty($data['html'])) $file_oembed->html = $data['html'];
- if (!empty($data['title'])) $file_oembed->title = $data['title'];
- if (!empty($data['author_name'])) $file_oembed->author_name = $data['author_name'];
- if (!empty($data['author_url'])) $file_oembed->author_url = $data['author_url'];
- if (!empty($data['url'])) $file_oembed->url = $data['url'];
+ $file_oembed->version = $data->version;
+ $file_oembed->type = $data->type;
+ if (!empty($data->provider_name)) $file_oembed->provider = $data->provider_name;
+ if (!empty($data->provider)) $file_oembed->provider = $data->provider;
+ if (!empty($data->provide_url)) $file_oembed->provider_url = $data->provider_url;
+ if (!empty($data->width)) $file_oembed->width = intval($data->width);
+ if (!empty($data->height)) $file_oembed->height = intval($data->height);
+ if (!empty($data->html)) $file_oembed->html = $data->html;
+ if (!empty($data->title)) $file_oembed->title = $data->title;
+ if (!empty($data->author_name)) $file_oembed->author_name = $data->author_name;
+ if (!empty($data->author_url)) $file_oembed->author_url = $data->author_url;
+ if (!empty($data->url)) $file_oembed->url = $data->url;
$file_oembed->insert();
- if (!empty($data['thumbnail_url'])) {
+ if (!empty($data->thumbnail_url)) {
File_thumbnail::saveNew($data, $file_id);
}
}
diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php
index 44b92a2fa..0b09c6af8 100644
--- a/classes/File_thumbnail.php
+++ b/classes/File_thumbnail.php
@@ -51,9 +51,9 @@ class File_thumbnail extends Memcached_DataObject
function saveNew($data, $file_id) {
$tn = new File_thumbnail;
$tn->file_id = $file_id;
- $tn->url = $data['thumbnail_url'];
- $tn->width = intval($data['thumbnail_width']);
- $tn->height = intval($data['thumbnail_height']);
+ $tn->url = $data->thumbnail_url;
+ $tn->width = intval($data->thumbnail_width);
+ $tn->height = intval($data->thumbnail_height);
$tn->insert();
}
}
diff --git a/classes/Notice.php b/classes/Notice.php
index 4b9a866b0..ebd5e1efd 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -98,13 +98,20 @@ class Notice extends Memcached_DataObject
function saveTags()
{
/* extract all #hastags */
- $count = preg_match_all('/(?:^|\s)#([A-Za-z0-9_\-\.]{1,64})/', strtolower($this->content), $match);
+ $count = preg_match_all('/(?:^|\s)#([\pL\pN_\-\.]{1,64})/', strtolower($this->content), $match);
if (!$count) {
return true;
}
+ //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]);
+ }
+
/* Add them to the database */
- foreach(array_unique($match[1]) as $hashtag) {
+ foreach(array_unique($hashtags) as $hashtag) {
/* elide characters we don't want in the tag */
$this->saveTag($hashtag);
}
@@ -113,8 +120,6 @@ class Notice extends Memcached_DataObject
function saveTag($hashtag)
{
- $hashtag = common_canonical_tag($hashtag);
-
$tag = new Notice_tag();
$tag->notice_id = $this->id;
$tag->tag = $hashtag;
@@ -177,29 +182,30 @@ class Notice extends Memcached_DataObject
$notice->is_local = $is_local;
}
- $notice->query('BEGIN');
-
- $notice->reply_to = $reply_to;
if (!empty($created)) {
$notice->created = $created;
} else {
$notice->created = common_sql_now();
}
+
$notice->content = $final;
$notice->rendered = common_render_content($final, $notice);
$notice->source = $source;
$notice->uri = $uri;
- if (!empty($reply_to)) {
- $reply_notice = Notice::staticGet('id', $reply_to);
- if (!empty($reply_notice)) {
- $notice->reply_to = $reply_to;
- $notice->conversation = $reply_notice->conversation;
- }
+ $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final);
+
+ if (!empty($notice->reply_to)) {
+ $reply = Notice::staticGet('id', $notice->reply_to);
+ $notice->conversation = $reply->conversation;
}
if (Event::handle('StartNoticeSave', array(&$notice))) {
+ // XXX: some of these functions write to the DB
+
+ $notice->query('BEGIN');
+
$id = $notice->insert();
if (!$id) {
@@ -207,18 +213,33 @@ class Notice extends Memcached_DataObject
return _('Problem saving notice.');
}
- # Update the URI after the notice is in the database
- if (!$uri) {
- $orig = clone($notice);
+ // Update ID-dependent columns: URI, conversation
+
+ $orig = clone($notice);
+
+ $changed = false;
+
+ if (empty($uri)) {
$notice->uri = common_notice_uri($notice);
+ $changed = true;
+ }
+ // If it's not part of a conversation, it's
+ // the beginning of a new conversation.
+
+ if (empty($notice->conversation)) {
+ $notice->conversation = $notice->id;
+ $changed = true;
+ }
+
+ if ($changed) {
if (!$notice->update($orig)) {
common_log_db_error($notice, 'UPDATE', __FILE__);
return _('Problem saving notice.');
}
}
- # XXX: do we need to change this for remote users?
+ // XXX: do we need to change this for remote users?
$notice->saveReplies();
$notice->saveTags();
@@ -226,8 +247,13 @@ class Notice extends Memcached_DataObject
$notice->addToInboxes();
$notice->saveUrls();
+
+ // FIXME: why do we have to re-render the content?
+ // Remove this if it's not necessary.
+
$orig2 = clone($notice);
- $notice->rendered = common_render_content($final, $notice);
+
+ $notice->rendered = common_render_content($final, $notice);
if (!$notice->update($orig2)) {
common_log_db_error($notice, 'UPDATE', __FILE__);
return _('Problem saving notice.');
@@ -284,9 +310,9 @@ class Notice extends Memcached_DataObject
$notice->profile_id = $profile_id;
$notice->content = $content;
if (common_config('db','type') == 'pgsql')
- $notice->whereAdd('extract(epoch from now() - created) < ' . common_config('site', 'dupelimit'));
+ $notice->whereAdd('extract(epoch from now() - created) < ' . common_config('site', 'dupelimit'));
else
- $notice->whereAdd('now() - created < ' . common_config('site', 'dupelimit'));
+ $notice->whereAdd('now() - created < ' . common_config('site', 'dupelimit'));
$cnt = $notice->count();
return ($cnt == 0);
@@ -874,8 +900,11 @@ class Notice extends Memcached_DataObject
if ($cnt > 0) {
$qry .= ', ';
}
- $qry .= '('.$id.', '.$this->id.', '.$source.', "'.$this->created.'") ';
+ $qry .= '('.$id.', '.$this->id.', '.$source.", '".$this->created. "') ";
$cnt++;
+ if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) {
+ Notice_inbox::gc($id);
+ }
if ($cnt >= MAX_BOXCARS) {
$inbox = new Notice_inbox();
$inbox->query($qry);
@@ -897,10 +926,14 @@ class Notice extends Memcached_DataObject
{
$user = new User();
+ if(common_config('db','quote_identifiers'))
+ $user_table = '"user"';
+ else $user_table = 'user';
+
$qry =
'SELECT id ' .
- 'FROM user JOIN subscription '.
- 'ON user.id = subscription.subscriber ' .
+ 'FROM '. $user_table .' JOIN subscription '.
+ 'ON '. $user_table .'.id = subscription.subscriber ' .
'WHERE subscription.subscribed = %d ';
$user->query(sprintf($qry, $this->profile_id));
@@ -1018,16 +1051,6 @@ class Notice extends Memcached_DataObject
if (!$recipient) {
continue;
}
- if ($i == 0 && ($recipient->id != $sender->id) && !$this->reply_to) { // Don't save reply to self
- $reply_for = $recipient;
- $recipient_notice = $reply_for->getCurrentNotice();
- if ($recipient_notice) {
- $orig = clone($this);
- $this->reply_to = $recipient_notice->id;
- $this->conversation = $recipient_notice->conversation;
- $this->update($orig);
- }
- }
// Don't save replies from blocked profile to local user
$recipient_user = User::staticGet('id', $recipient->id);
if ($recipient_user && $recipient_user->hasBlocked($sender)) {
@@ -1074,14 +1097,6 @@ class Notice extends Memcached_DataObject
}
}
- // If it's not a reply, make it the root of a new conversation
-
- if (empty($this->conversation)) {
- $orig = clone($this);
- $this->conversation = $this->id;
- $this->update($orig);
- }
-
foreach (array_keys($replied) as $recipient) {
$user = User::staticGet('id', $recipient);
if ($user) {
@@ -1253,4 +1268,76 @@ class Notice extends Memcached_DataObject
return $ids;
}
+
+ /**
+ * Determine which notice, if any, a new notice is in reply to.
+ *
+ * For conversation tracking, we try to see where this notice fits
+ * in the tree. Rough algorithm is:
+ *
+ * if (reply_to is set and valid) {
+ * return reply_to;
+ * } else if ((source not API or Web) and (content starts with "T NAME" or "@name ")) {
+ * return ID of last notice by initial @name in content;
+ * }
+ *
+ * Note that all @nickname instances will still be used to save "reply" records,
+ * so the notice shows up in the mentioned users' "replies" tab.
+ *
+ * @param integer $reply_to ID passed in by Web or API
+ * @param integer $profile_id ID of author
+ * @param string $source Source tag, like 'web' or 'gwibber'
+ * @param string $content Final notice content
+ *
+ * @return integer ID of replied-to notice, or null for not a reply.
+ */
+
+ static function getReplyTo($reply_to, $profile_id, $source, $content)
+ {
+ static $lb = array('xmpp', 'mail', 'sms', 'omb');
+
+ // If $reply_to is specified, we check that it exists, and then
+ // return it if it does
+
+ if (!empty($reply_to)) {
+ $reply_notice = Notice::staticGet('id', $reply_to);
+ if (!empty($reply_notice)) {
+ return $reply_to;
+ }
+ }
+
+ // If it's not a "low bandwidth" source (one where you can't set
+ // a reply_to argument), we return. This is mostly web and API
+ // clients.
+
+ if (!in_array($source, $lb)) {
+ return null;
+ }
+
+ // Is there an initial @ or T?
+
+ if (preg_match('/^T ([A-Z0-9]{1,64}) /', $content, $match) ||
+ preg_match('/^@([a-z0-9]{1,64})\s+/', $content, $match)) {
+ $nickname = common_canonical_nickname($match[1]);
+ } else {
+ return null;
+ }
+
+ // Figure out who that is.
+
+ $sender = Profile::staticGet('id', $profile_id);
+ $recipient = common_relative_profile($sender, $nickname, common_sql_now());
+
+ if (empty($recipient)) {
+ return null;
+ }
+
+ // Get their last notice
+
+ $last = $recipient->getCurrentNotice();
+
+ if (!empty($last)) {
+ return $last->id;
+ }
+ }
}
diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php
index 940381f84..2af34b1a4 100644
--- a/classes/Notice_inbox.php
+++ b/classes/Notice_inbox.php
@@ -24,6 +24,10 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
// We keep 5 pages of inbox notices in memcache, +1 for pagination check
define('INBOX_CACHE_WINDOW', 101);
+define('NOTICE_INBOX_GC_BOXCAR', 128);
+define('NOTICE_INBOX_GC_MAX', 12800);
+define('NOTICE_INBOX_LIMIT', 1000);
+define('NOTICE_INBOX_SOFT_LIMIT', 1000);
define('NOTICE_INBOX_SOURCE_SUB', 1);
define('NOTICE_INBOX_SOURCE_GROUP', 2);
@@ -100,4 +104,41 @@ class Notice_inbox extends Memcached_DataObject
{
return Memcached_DataObject::pkeyGet('Notice_inbox', $kv);
}
+
+ static function gc($user_id)
+ {
+ $entry = new Notice_inbox();
+ $entry->user_id = $user_id;
+ $entry->orderBy('created DESC');
+ $entry->limit(NOTICE_INBOX_LIMIT - 1, NOTICE_INBOX_GC_MAX);
+
+ $total = $entry->find();
+
+ if ($total > 0) {
+ $notices = array();
+ $cnt = 0;
+ while ($entry->fetch()) {
+ $notices[] = $entry->notice_id;
+ $cnt++;
+ if ($cnt >= NOTICE_INBOX_GC_BOXCAR) {
+ self::deleteMatching($user_id, $notices);
+ $notices = array();
+ $cnt = 0;
+ }
+ }
+
+ if ($cnt > 0) {
+ self::deleteMatching($user_id, $notices);
+ $notices = array();
+ }
+ }
+ }
+
+ static function deleteMatching($user_id, $notices)
+ {
+ $entry = new Notice_inbox();
+ return $entry->query('DELETE FROM notice_inbox '.
+ 'WHERE user_id = ' . $user_id . ' ' .
+ 'AND notice_id in ('.implode(',', $notices).')');
+ }
}
diff --git a/classes/Profile.php b/classes/Profile.php
index 224b61bd2..f926b2cef 100644
--- a/classes/Profile.php
+++ b/classes/Profile.php
@@ -199,7 +199,7 @@ class Profile extends Memcached_DataObject
$query .= ' order by id DESC';
if (!is_null($offset)) {
- $query .= " limit $offset, $limit";
+ $query .= " LIMIT $limit OFFSET $offset";
}
$notice->query($query);
@@ -360,7 +360,6 @@ class Profile extends Memcached_DataObject
$c->set(common_cache_key('profile:subscription_count:'.$this->id), $cnt);
}
- common_debug("subscriptionCount == $cnt");
return $cnt;
}
@@ -385,7 +384,6 @@ class Profile extends Memcached_DataObject
$c->set(common_cache_key('profile:subscriber_count:'.$this->id), $cnt);
}
- common_debug("subscriberCount == $cnt");
return $cnt;
}
@@ -407,7 +405,6 @@ class Profile extends Memcached_DataObject
$c->set(common_cache_key('profile:fave_count:'.$this->id), $cnt);
}
- common_debug("faveCount == $cnt");
return $cnt;
}
@@ -430,7 +427,6 @@ class Profile extends Memcached_DataObject
$c->set(common_cache_key('profile:notice_count:'.$this->id), $cnt);
}
- common_debug("noticeCount == $cnt");
return $cnt;
}
diff --git a/classes/User_group.php b/classes/User_group.php
index 27b444705..ea19cbb97 100644
--- a/classes/User_group.php
+++ b/classes/User_group.php
@@ -275,11 +275,14 @@ class User_group extends Memcached_DataObject
// XXX: cache this
$user = new User();
+ if(common_config('db','quote_identifiers'))
+ $user_table = '"user"';
+ else $user_table = 'user';
$qry =
'SELECT id ' .
- 'FROM user JOIN group_member '.
- 'ON user.id = group_member.profile_id ' .
+ 'FROM '. $user_table .' JOIN group_member '.
+ 'ON '. $user_table .'.id = group_member.profile_id ' .
'WHERE group_member.group_id = %d ';
$user->query(sprintf($qry, $this->id));
@@ -294,4 +297,45 @@ class User_group extends Memcached_DataObject
return $ids;
}
+
+ function asAtomEntry($namespace=false, $source=false)
+ {
+ $xs = new XMLStringer(true);
+
+ if ($namespace) {
+ $attrs = array('xmlns' => 'http://www.w3.org/2005/Atom',
+ 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0');
+ } else {
+ $attrs = array();
+ }
+
+ $xs->elementStart('entry', $attrs);
+
+ if ($source) {
+ $xs->elementStart('source');
+ $xs->element('title', null, $profile->nickname . " - " . common_config('site', 'name'));
+ $xs->element('link', array('href' => $this->permalink()));
+ }
+
+ if ($source) {
+ $xs->elementEnd('source');
+ }
+
+ $xs->element('title', null, $this->nickname);
+ $xs->element('summary', null, $this->description);
+
+ $xs->element('link', array('rel' => 'alternate',
+ 'href' => $this->permalink()));
+
+ $xs->element('id', null, $this->permalink());
+
+ $xs->element('published', null, common_date_w3dtf($this->created));
+ $xs->element('updated', null, common_date_w3dtf($this->modified));
+
+ $xs->element('content', array('type' => 'html'), $this->description);
+
+ $xs->elementEnd('entry');
+
+ return $xs->getString();
+ }
}