diff options
Diffstat (limited to 'classes')
-rw-r--r-- | classes/Consumer.php | 21 | ||||
-rw-r--r-- | classes/File.php | 7 | ||||
-rw-r--r-- | classes/File_oembed.php | 8 | ||||
-rw-r--r-- | classes/Group_member.php | 37 | ||||
-rw-r--r-- | classes/Inbox.php | 180 | ||||
-rw-r--r-- | classes/Login_token.php | 27 | ||||
-rw-r--r-- | classes/Memcached_DataObject.php | 188 | ||||
-rw-r--r-- | classes/Notice.php | 140 | ||||
-rw-r--r-- | classes/Notice_inbox.php | 132 | ||||
-rw-r--r-- | classes/Oauth_application.php | 140 | ||||
-rw-r--r-- | classes/Oauth_application_user.php | 44 | ||||
-rw-r--r-- | classes/Profile.php | 25 | ||||
-rw-r--r-- | classes/Queue_item.php | 9 | ||||
-rw-r--r-- | classes/Status_network.php | 48 | ||||
-rw-r--r-- | classes/Token.php | 6 | ||||
-rw-r--r-- | classes/User.php | 133 | ||||
-rw-r--r-- | classes/statusnet.ini | 41 |
17 files changed, 802 insertions, 384 deletions
diff --git a/classes/Consumer.php b/classes/Consumer.php index d5b7b7e33..ad64a8491 100644 --- a/classes/Consumer.php +++ b/classes/Consumer.php @@ -4,16 +4,17 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Consumer extends Memcached_DataObject +class Consumer extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ public $__table = 'consumer'; // table name public $consumer_key; // varchar(255) primary_key not_null + public $consumer_secret; // varchar(255) not_null public $seed; // char(32) not_null - public $created; // datetime() not_null - public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + public $created; // datetime not_null + public $modified; // timestamp not_null default_CURRENT_TIMESTAMP /* Static get */ function staticGet($k,$v=null) @@ -21,4 +22,18 @@ class Consumer extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + static function generateNew() + { + $cons = new Consumer(); + $rand = common_good_rand(16); + + $cons->seed = $rand; + $cons->consumer_key = md5(time() + $rand); + $cons->consumer_secret = md5(md5(time() + time() + $rand)); + $cons->created = common_sql_now(); + + return $cons; + } + } diff --git a/classes/File.php b/classes/File.php index 6173f31d6..c527c4ffe 100644 --- a/classes/File.php +++ b/classes/File.php @@ -80,7 +80,14 @@ class File extends Memcached_DataObject if (isset($redir_data['type']) && (('text/html' === substr($redir_data['type'], 0, 9) || 'application/xhtml+xml' === substr($redir_data['type'], 0, 21))) && ($oembed_data = File_oembed::_getOembed($given_url))) { + + $fo = File_oembed::staticGet('file_id', $file_id); + + if (empty($fo)) { File_oembed::saveNew($oembed_data, $file_id); + } else { + common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file $file_id", __FILE__); + } } return $x; } diff --git a/classes/File_oembed.php b/classes/File_oembed.php index e41ccfd09..11f160718 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -115,7 +115,13 @@ class File_oembed extends Memcached_DataObject } $file_oembed->insert(); if (!empty($data->thumbnail_url)) { - File_thumbnail::saveNew($data, $file_id); + $ft = File_thumbnail::staticGet('file_id', $file_id); + if (!empty($ft)) { + common_log(LOG_WARNING, "Strangely, a File_thumbnail object exists for new file $file_id", + __FILE__); + } else { + File_thumbnail::saveNew($data, $file_id); + } } } } diff --git a/classes/Group_member.php b/classes/Group_member.php index 069b2c7a1..7b1760f76 100644 --- a/classes/Group_member.php +++ b/classes/Group_member.php @@ -25,4 +25,41 @@ class Group_member extends Memcached_DataObject { return Memcached_DataObject::pkeyGet('Group_member', $kv); } + + static function join($group_id, $profile_id) + { + $member = new Group_member(); + + $member->group_id = $group_id; + $member->profile_id = $profile_id; + $member->created = common_sql_now(); + + $result = $member->insert(); + + if (!$result) { + common_log_db_error($member, 'INSERT', __FILE__); + throw new Exception(_("Group join failed.")); + } + + return true; + } + + static function leave($group_id, $profile_id) + { + $member = Group_member::pkeyGet(array('group_id' => $group_id, + 'profile_id' => $profile_id)); + + if (empty($member)) { + throw new Exception(_("Not part of group.")); + } + + $result = $member->delete(); + + if (!$result) { + common_log_db_error($member, 'INSERT', __FILE__); + throw new Exception(_("Group leave failed.")); + } + + return true; + } } diff --git a/classes/Inbox.php b/classes/Inbox.php new file mode 100644 index 000000000..086dba1c9 --- /dev/null +++ b/classes/Inbox.php @@ -0,0 +1,180 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Data class for user location preferences + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Data + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2009 StatusNet Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +class Inbox extends Memcached_DataObject +{ + const BOXCAR = 128; + + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'inbox'; // table name + public $user_id; // int(4) primary_key not_null + public $notice_ids; // blob + + /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Inbox',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + function sequenceKey() + { + return array(false, false, false); + } + + /** + * Create a new inbox from existing Notice_inbox stuff + */ + + static function initialize($user_id) + { + $inbox = Inbox::fromNoticeInbox($user_id); + + unset($inbox->fake); + + $result = $inbox->insert(); + + if (!$result) { + common_log_db_error($inbox, 'INSERT', __FILE__); + return null; + } + + return $inbox; + } + + static function fromNoticeInbox($user_id) + { + $ids = array(); + + $ni = new Notice_inbox(); + + $ni->user_id = $user_id; + $ni->selectAdd(); + $ni->selectAdd('notice_id'); + $ni->orderBy('notice_id DESC'); + $ni->limit(0, 1024); + + if ($ni->find()) { + while($ni->fetch()) { + $ids[] = $ni->notice_id; + } + } + + $ni->free(); + unset($ni); + + $inbox = new Inbox(); + + $inbox->user_id = $user_id; + $inbox->notice_ids = call_user_func_array('pack', array_merge(array('N*'), $ids)); + $inbox->fake = true; + + return $inbox; + } + + static function insertNotice($user_id, $notice_id) + { + $inbox = DB_DataObject::staticGet('inbox', 'user_id', $user_id); + + if (empty($inbox)) { + $inbox = Inbox::initialize($user_id); + } + + if (empty($inbox)) { + return false; + } + + $result = $inbox->query(sprintf('UPDATE inbox '. + 'set notice_ids = concat(cast(0x%08x as binary(4)), '. + 'substr(notice_ids, 1, 4092)) '. + 'WHERE user_id = %d', + $notice_id, $user_id)); + + if ($result) { + $c = self::memcache(); + + if (!empty($c)) { + $c->delete(self::cacheKey('inbox', 'user_id', $user_id)); + } + } + + return $result; + } + + static function bulkInsert($notice_id, $user_ids) + { + foreach ($user_ids as $user_id) + { + Inbox::insertNotice($user_id, $notice_id); + } + } + + function stream($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false) + { + $inbox = Inbox::staticGet('user_id', $user_id); + + if (empty($inbox)) { + $inbox = Inbox::fromNoticeInbox($user_id); + if (empty($inbox)) { + return array(); + } else { + $inbox->encache(); + } + } + + $ids = unpack('N*', $inbox->notice_ids); + + if (!empty($since_id)) { + $newids = array(); + foreach ($ids as $id) { + if ($id > $since_id) { + $newids[] = $id; + } + } + $ids = $newids; + } + + if (!empty($max_id)) { + $newids = array(); + foreach ($ids as $id) { + if ($id <= $max_id) { + $newids[] = $id; + } + } + $ids = $newids; + } + + $ids = array_slice($ids, $offset, $limit); + + return $ids; + } +} diff --git a/classes/Login_token.php b/classes/Login_token.php index 746cd7f22..51dc61262 100644 --- a/classes/Login_token.php +++ b/classes/Login_token.php @@ -40,6 +40,8 @@ class Login_token extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + const TIMEOUT = 120; // seconds after which to timeout the token + /* DB_DataObject calculates the sequence key(s) by taking the first key returned by the keys() function. In this case, the keys() function returns user_id as the first key. user_id is not a sequence, but @@ -52,4 +54,29 @@ class Login_token extends Memcached_DataObject { return array(false,false); } + + function makeNew($user) + { + $login_token = Login_token::staticGet('user_id', $user->id); + + if (!empty($login_token)) { + $login_token->delete(); + } + + $login_token = new Login_token(); + + $login_token->user_id = $user->id; + $login_token->token = common_good_rand(16); + $login_token->created = common_sql_now(); + + $result = $login_token->insert(); + + if (!$result) { + common_log_db_error($login_token, 'INSERT', __FILE__); + throw new Exception(sprintf(_('Could not create login token for %s'), + $user->nickname)); + } + + return $login_token; + } } diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 400b05f97..4ecab9db6 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -19,6 +19,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + class Memcached_DataObject extends DB_DataObject { /** @@ -66,6 +68,7 @@ class Memcached_DataObject extends DB_DataObject // Clear this out so we don't accidentally break global // state in *this* process. $this->_DB_resultid = null; + // We don't have any local DBO refs, so clear these out. $this->_link_loaded = false; } @@ -90,7 +93,9 @@ class Memcached_DataObject extends DB_DataObject unset($i); } $i = Memcached_DataObject::getcached($cls, $k, $v); - if ($i === false) { // false == cache miss + if ($i) { + return $i; + } else { $i = DB_DataObject::factory($cls); if (empty($i)) { $i = false; @@ -98,34 +103,22 @@ class Memcached_DataObject extends DB_DataObject } $result = $i->get($k, $v); if ($result) { - // Hit! $i->encache(); + return $i; } else { - // save the fact that no such row exists - $c = self::memcache(); - if (!empty($c)) { - $ck = self::cachekey($cls, $k, $v); - $c->set($ck, null); - } $i = false; + return $i; } } - return $i; } - /** - * @fixme Should this return false on lookup fail to match staticGet? - */ - function pkeyGet($cls, $kv) + function &pkeyGet($cls, $kv) { $i = Memcached_DataObject::multicache($cls, $kv); - if ($i !== false) { // false == cache miss + if ($i) { return $i; } else { - $i = DB_DataObject::factory($cls); - if (empty($i)) { - return false; - } + $i = new $cls(); foreach ($kv as $k => $v) { $i->$k = $v; } @@ -133,11 +126,6 @@ class Memcached_DataObject extends DB_DataObject $i->encache(); } else { $i = null; - $c = self::memcache(); - if (!empty($c)) { - $ck = self::multicacheKey($cls, $kv); - $c->set($ck, null); - } } return $i; } @@ -146,9 +134,6 @@ class Memcached_DataObject extends DB_DataObject function insert() { $result = parent::insert(); - if ($result) { - $this->encache(); // in case of cached negative lookups - } return $result; } @@ -188,23 +173,21 @@ class Memcached_DataObject extends DB_DataObject if (!$c) { return false; } else { - return $c->get(Memcached_DataObject::cacheKey($cls, $k, $v)); + $obj = $c->get(Memcached_DataObject::cacheKey($cls, $k, $v)); + if (0 == strcasecmp($cls, 'User')) { + // Special case for User + if (is_object($obj) && is_object($obj->id)) { + common_log(LOG_ERR, "User " . $obj->nickname . " was cached with User as ID; deleting"); + $c->delete(Memcached_DataObject::cacheKey($cls, $k, $v)); + return false; + } + } + return $obj; } } function keyTypes() { - // ini-based classes return number-indexed arrays. handbuilt - // classes return column => keytype. Make this uniform. - - $keys = $this->keys(); - - $keyskeys = array_keys($keys); - - if (is_string($keyskeys[0])) { - return $keys; - } - global $_DB_DATAOBJECT; if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) { $this->databaseStructure(); @@ -216,90 +199,73 @@ class Memcached_DataObject extends DB_DataObject function encache() { $c = $this->memcache(); - if (!$c) { return false; - } - - $keys = $this->_allCacheKeys(); - - foreach ($keys as $key) { - $c->set($key, $this); + } else if ($this->tableName() == 'user' && is_object($this->id)) { + // Special case for User bug + $e = new Exception(); + common_log(LOG_ERR, __METHOD__ . ' caching user with User object as ID ' . + str_replace("\n", " ", $e->getTraceAsString())); + return false; + } else { + $pkey = array(); + $pval = array(); + $types = $this->keyTypes(); + ksort($types); + foreach ($types as $key => $type) { + if ($type == 'K') { + $pkey[] = $key; + $pval[] = $this->$key; + } else { + $c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this); + } + } + # XXX: should work for both compound and scalar pkeys + $pvals = implode(',', $pval); + $pkeys = implode(',', $pkey); + $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this); } } function decache() { $c = $this->memcache(); - if (!$c) { return false; - } - - $keys = $this->_allCacheKeys(); - - foreach ($keys as $key) { - $c->delete($key, $this); - } - } - - function _allCacheKeys() - { - $ckeys = array(); - - $types = $this->keyTypes(); - ksort($types); - - $pkey = array(); - $pval = array(); - - foreach ($types as $key => $type) { - - assert(!empty($key)); - - if ($type == 'U') { - if (empty($this->$key)) { - continue; + } else { + $pkey = array(); + $pval = array(); + $types = $this->keyTypes(); + ksort($types); + foreach ($types as $key => $type) { + if ($type == 'K') { + $pkey[] = $key; + $pval[] = $this->$key; + } else { + $c->delete($this->cacheKey($this->tableName(), $key, $this->$key)); } - $ckeys[] = $this->cacheKey($this->tableName(), $key, $this->$key); - } else if ($type == 'K' || $type == 'N') { - $pkey[] = $key; - $pval[] = $this->$key; - } else { - throw new Exception("Unknown key type $key => $type for " . $this->tableName()); } + # should work for both compound and scalar pkeys + # XXX: comma works for now but may not be safe separator for future keys + $pvals = implode(',', $pval); + $pkeys = implode(',', $pkey); + $c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals)); } - - assert(count($pkey) > 0); - - // XXX: should work for both compound and scalar pkeys - $pvals = implode(',', $pval); - $pkeys = implode(',', $pkey); - - $ckeys[] = $this->cacheKey($this->tableName(), $pkeys, $pvals); - - return $ckeys; } function multicache($cls, $kv) { ksort($kv); - $c = self::memcache(); + $c = Memcached_DataObject::memcache(); if (!$c) { return false; } else { - return $c->get(self::multicacheKey($cls, $kv)); + $pkeys = implode(',', array_keys($kv)); + $pvals = implode(',', array_values($kv)); + return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals)); } } - static function multicacheKey($cls, $kv) - { - ksort($kv); - $pkeys = implode(',', array_keys($kv)); - $pvals = implode(',', array_values($kv)); - return self::cacheKey($cls, $pkeys, $pvals); - } - function getSearchEngine($table) { require_once INSTALLDIR.'/lib/search_engines.php'; @@ -334,8 +300,7 @@ class Memcached_DataObject extends DB_DataObject $key_part = common_keyize($cls).':'.md5($qry); $ckey = common_cache_key($key_part); $stored = $c->get($ckey); - - if ($stored !== false) { + if ($stored) { return new ArrayWrapper($stored); } @@ -366,6 +331,29 @@ class Memcached_DataObject extends DB_DataObject $exists = false; } + // @fixme horrible evil hack! + // + // In multisite configuration we don't want to keep around a separate + // connection for every database; we could end up with thousands of + // connections open per thread. In an ideal world we might keep + // a connection per server and select different databases, but that'd + // be reliant on having the same db username/pass as well. + // + // MySQL connections are cheap enough we're going to try just + // closing out the old connection and reopening when we encounter + // a new DSN. + // + // WARNING WARNING if we end up actually using multiple DBs at a time + // we'll need some fancier logic here. + if (!$exists && !empty($_DB_DATAOBJECT['CONNECTIONS'])) { + foreach ($_DB_DATAOBJECT['CONNECTIONS'] as $index => $conn) { + if (!empty($conn)) { + $conn->disconnect(); + } + unset($_DB_DATAOBJECT['CONNECTIONS'][$index]); + } + } + $result = parent::_connect(); if ($result && !$exists) { diff --git a/classes/Notice.php b/classes/Notice.php index 9bda47827..38b10db04 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -125,8 +125,7 @@ class Notice extends Memcached_DataObject 'Fave', 'Notice_tag', 'Group_inbox', - 'Queue_item', - 'Notice_inbox'); + 'Queue_item'); foreach ($related as $cls) { $inst = new $cls(); @@ -276,7 +275,6 @@ class Notice extends Memcached_DataObject if (isset($repeat_of)) { $notice->repeat_of = $repeat_of; - $notice->reply_to = $repeat_of; } else { $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final); } @@ -300,8 +298,6 @@ class Notice extends Memcached_DataObject // XXX: some of these functions write to the DB - $notice->query('BEGIN'); - $id = $notice->insert(); if (!$id) { @@ -339,11 +335,13 @@ class Notice extends Memcached_DataObject $notice->saveTags(); - $notice->addToInboxes(); + $groups = $notice->saveGroups(); - $notice->saveUrls(); + $recipients = $notice->saveReplies(); - $notice->query('COMMIT'); + $notice->addToInboxes($groups, $recipients); + + $notice->saveUrls(); Event::handle('EndNoticeSave', array($notice)); } @@ -503,20 +501,6 @@ class Notice extends Memcached_DataObject $original->free(); unset($original); } - - $ni = new Notice_inbox(); - - $ni->notice_id = $this->id; - - if ($ni->find()) { - while ($ni->fetch()) { - $tmk = common_cache_key('user:repeated_to_me:'.$ni->user_id); - $cache->delete($tmk); - } - } - - $ni->free(); - unset($ni); } } } @@ -842,11 +826,28 @@ class Notice extends Memcached_DataObject return $ids; } - function addToInboxes() + /** + * @param $groups array of Group *objects* + * @param $recipients array of profile *ids* + */ + function whoGets($groups=null, $recipients=null) { - // XXX: loads constants + $c = self::memcache(); + + if (!empty($c)) { + $ni = $c->get(common_cache_key('notice:who_gets:'.$this->id)); + if ($ni !== false) { + return $ni; + } + } + + if (is_null($groups)) { + $groups = $this->getGroups(); + } - $inbox = new Notice_inbox(); + if (is_null($recipients)) { + $recipients = $this->getReplies(); + } $users = $this->getSubscribedUsers(); @@ -860,7 +861,6 @@ class Notice extends Memcached_DataObject $ni[$id] = NOTICE_INBOX_SOURCE_SUB; } - $groups = $this->saveGroups(); $profile = $this->getProfile(); foreach ($groups as $group) { @@ -875,8 +875,6 @@ class Notice extends Memcached_DataObject } } - $recipients = $this->saveReplies(); - foreach ($recipients as $recipient) { if (!array_key_exists($recipient, $ni)) { @@ -887,7 +885,19 @@ class Notice extends Memcached_DataObject } } - Notice_inbox::bulkInsert($this->id, $this->created, $ni); + if (!empty($c)) { + // XXX: pack this data better + $c->set(common_cache_key('notice:who_gets:'.$this->id), $ni); + } + + return $ni; + } + + function addToInboxes($groups, $recipients) + { + $ni = $this->whoGets($groups, $recipients); + + Inbox::bulkInsert($this->id, array_keys($ni)); return; } @@ -919,8 +929,17 @@ class Notice extends Memcached_DataObject return $ids; } + /** + * @return array of Group objects + */ function saveGroups() { + // Don't save groups for repeats + + if (!empty($this->repeat_of)) { + return array(); + } + $groups = array(); /* extract all !group */ @@ -991,6 +1010,12 @@ class Notice extends Memcached_DataObject */ function saveReplies() { + // Don't save reply data for repeats + + if (!empty($this->repeat_of)) { + return array(); + } + // Alternative reply format $tname = false; if (preg_match('/^T ([A-Z0-9]{1,64}) /', $this->content, $match)) { @@ -1077,6 +1102,63 @@ class Notice extends Memcached_DataObject return $recipientIds; } + function getReplies() + { + // XXX: cache me + + $ids = array(); + + $reply = new Reply(); + $reply->selectAdd(); + $reply->selectAdd('profile_id'); + $reply->notice_id = $this->id; + + if ($reply->find()) { + while($reply->fetch()) { + $ids[] = $reply->profile_id; + } + } + + $reply->free(); + + return $ids; + } + + /** + * Same calculation as saveGroups but without the saving + * @fixme merge the functions + * @return array of Group objects + */ + function getGroups() + { + // Don't save groups for repeats + + if (!empty($this->repeat_of)) { + return array(); + } + + // XXX: cache me + + $groups = array(); + + $gi = new Group_inbox(); + + $gi->selectAdd(); + $gi->selectAdd('group_id'); + + $gi->notice_id = $this->id; + + if ($gi->find()) { + while ($gi->fetch()) { + $groups[] = clone($gi); + } + } + + $gi->free(); + + return $groups; + } + function asAtomEntry($namespace=false, $source=false) { $profile = $this->getProfile(); diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php index e350e6e2f..c27dcdfd6 100644 --- a/classes/Notice_inbox.php +++ b/classes/Notice_inbox.php @@ -1,7 +1,7 @@ <?php /* * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2008, 2009, StatusNet, Inc. + * Copyright (C) 2008-2010, StatusNet, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -17,7 +17,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +if (!defined('STATUSNET')) { + exit(1); +} require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; @@ -29,12 +31,6 @@ 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); -define('NOTICE_INBOX_SOURCE_REPLY', 3); -define('NOTICE_INBOX_SOURCE_FORWARD', 4); -define('NOTICE_INBOX_SOURCE_GATEWAY', -1); - class Notice_inbox extends Memcached_DataObject { ###START_AUTOCODE @@ -55,139 +51,31 @@ class Notice_inbox extends Memcached_DataObject function stream($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false) { - return Notice::stream(array('Notice_inbox', '_streamDirect'), - array($user_id, $own), - ($own) ? 'notice_inbox:by_user:'.$user_id : - 'notice_inbox:by_user_own:'.$user_id, - $offset, $limit, $since_id, $max_id, $since); + throw new Exception('Notice_inbox no longer used; use Inbox'); } function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id, $since) { - $inbox = new Notice_inbox(); - - $inbox->user_id = $user_id; - - if (!$own) { - $inbox->whereAdd('source != ' . NOTICE_INBOX_SOURCE_GATEWAY); - } - - if ($since_id != 0) { - $inbox->whereAdd('notice_id > ' . $since_id); - } - - if ($max_id != 0) { - $inbox->whereAdd('notice_id <= ' . $max_id); - } - - if (!is_null($since)) { - $inbox->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\''); - } - - $inbox->orderBy('created DESC'); - - if (!is_null($offset)) { - $inbox->limit($offset, $limit); - } - - $ids = array(); - - if ($inbox->find()) { - while ($inbox->fetch()) { - $ids[] = $inbox->notice_id; - } - } - - return $ids; + throw new Exception('Notice_inbox no longer used; use Inbox'); } - function pkeyGet($kv) + function &pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Notice_inbox', $kv); } - /** - * Trim inbox for a given user to latest NOTICE_INBOX_LIMIT items - * (up to NOTICE_INBOX_GC_MAX will be deleted). - * - * @param int $user_id - * @return int count of notices dropped from the inbox, if any - */ 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(); - } - } - - return $total; + throw new Exception('Notice_inbox no longer used; use Inbox'); } 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).')'); + throw new Exception('Notice_inbox no longer used; use Inbox'); } static function bulkInsert($notice_id, $created, $ni) { - $cnt = 0; - - $qryhdr = 'INSERT INTO notice_inbox (user_id, notice_id, source, created) VALUES '; - $qry = $qryhdr; - - foreach ($ni as $id => $source) { - if ($cnt > 0) { - $qry .= ', '; - } - $qry .= '('.$id.', '.$notice_id.', '.$source.", '".$created. "') "; - $cnt++; - if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) { - // FIXME: Causes lag in replicated servers - // Notice_inbox::gc($id); - } - if ($cnt >= MAX_BOXCARS) { - $inbox = new Notice_inbox(); - $result = $inbox->query($qry); - if (PEAR::isError($result)) { - common_log_db_error($inbox, $qry); - } - $qry = $qryhdr; - $cnt = 0; - } - } - - if ($cnt > 0) { - $inbox = new Notice_inbox(); - $result = $inbox->query($qry); - if (PEAR::isError($result)) { - common_log_db_error($inbox, $qry); - } - } - - return; + throw new Exception('Notice_inbox no longer used; use Inbox'); } } diff --git a/classes/Oauth_application.php b/classes/Oauth_application.php new file mode 100644 index 000000000..a6b539087 --- /dev/null +++ b/classes/Oauth_application.php @@ -0,0 +1,140 @@ +<?php +/** + * Table Definition for oauth_application + */ +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +class Oauth_application extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'oauth_application'; // table name + public $id; // int(4) primary_key not_null + public $owner; // int(4) not_null + public $consumer_key; // varchar(255) not_null + public $name; // varchar(255) not_null + public $description; // varchar(255) + public $icon; // varchar(255) not_null + public $source_url; // varchar(255) + public $organization; // varchar(255) + public $homepage; // varchar(255) + public $callback_url; // varchar(255) not_null + public $type; // tinyint(1) + public $access_type; // tinyint(1) + public $created; // datetime not_null + public $modified; // timestamp not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=NULL) { + return Memcached_DataObject::staticGet('Oauth_application',$k,$v); + } + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + // Bit flags + public static $readAccess = 1; + public static $writeAccess = 2; + + public static $browser = 1; + public static $desktop = 2; + + function getConsumer() + { + return Consumer::staticGet('consumer_key', $this->consumer_key); + } + + static function maxDesc() + { + $desclimit = common_config('application', 'desclimit'); + // null => use global limit (distinct from 0!) + if (is_null($desclimit)) { + $desclimit = common_config('site', 'textlimit'); + } + return $desclimit; + } + + static function descriptionTooLong($desc) + { + $desclimit = self::maxDesc(); + return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit)); + } + + function setAccessFlags($read, $write) + { + if ($read) { + $this->access_type |= self::$readAccess; + } else { + $this->access_type &= ~self::$readAccess; + } + + if ($write) { + $this->access_type |= self::$writeAccess; + } else { + $this->access_type &= ~self::$writeAccess; + } + } + + function setOriginal($filename) + { + $imagefile = new ImageFile($this->id, Avatar::path($filename)); + + // XXX: Do we want to have a bunch of different size icons? homepage, stream, mini? + // or just one and control size via CSS? --Zach + + $orig = clone($this); + $this->icon = Avatar::url($filename); + common_debug(common_log_objstring($this)); + return $this->update($orig); + } + + static function getByConsumerKey($key) + { + if (empty($key)) { + return null; + } + + $app = new Oauth_application(); + $app->consumer_key = $key; + $app->limit(1); + $result = $app->find(true); + + return empty($result) ? null : $app; + } + + /** + * Handle an image upload + * + * Does all the magic for handling an image upload, and crops the + * image by default. + * + * @return void + */ + + function uploadLogo() + { + if ($_FILES['app_icon']['error'] == + UPLOAD_ERR_OK) { + + try { + $imagefile = ImageFile::fromUpload('app_icon'); + } catch (Exception $e) { + common_debug("damn that sucks"); + $this->showForm($e->getMessage()); + return; + } + + $filename = Avatar::filename($this->id, + image_type_to_extension($imagefile->type), + null, + 'oauth-app-icon-'.common_timestamp()); + + $filepath = Avatar::path($filename); + + move_uploaded_file($imagefile->filepath, $filepath); + + $this->setOriginal($filename); + } + } + +} diff --git a/classes/Oauth_application_user.php b/classes/Oauth_application_user.php new file mode 100644 index 000000000..57986281f --- /dev/null +++ b/classes/Oauth_application_user.php @@ -0,0 +1,44 @@ +<?php +/** + * Table Definition for oauth_application_user + */ +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +class Oauth_application_user extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'oauth_application_user'; // table name + public $profile_id; // int(4) primary_key not_null + public $application_id; // int(4) primary_key not_null + public $access_type; // tinyint(1) + public $token; // varchar(255) + public $created; // datetime not_null + public $modified; // timestamp not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=NULL) { + return Memcached_DataObject::staticGet('Oauth_application_user',$k,$v); + } + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + static function getByKeys($user, $app) + { + if (empty($user) || empty($app)) { + return null; + } + + $oau = new Oauth_application_user(); + + $oau->profile_id = $user->id; + $oau->application_id = $app->id; + $oau->limit(1); + + $result = $oau->find(true); + + return empty($result) ? null : $oau; + } + +} diff --git a/classes/Profile.php b/classes/Profile.php index 25d908dbf..1076fb2cb 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -352,6 +352,31 @@ class Profile extends Memcached_DataObject return $profile; } + function getApplications($offset = 0, $limit = null) + { + $qry = + 'SELECT a.* ' . + 'FROM oauth_application_user u, oauth_application a ' . + 'WHERE u.profile_id = %d ' . + 'AND a.id = u.application_id ' . + 'AND u.access_type > 0 ' . + 'ORDER BY u.created DESC '; + + if ($offset > 0) { + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + } + + $application = new Oauth_application(); + + $cnt = $application->query(sprintf($qry, $this->id)); + + return $application; + } + function subscriptionCount() { $c = common_memcache(); diff --git a/classes/Queue_item.php b/classes/Queue_item.php index 9c673540d..cf805a606 100644 --- a/classes/Queue_item.php +++ b/classes/Queue_item.php @@ -25,10 +25,12 @@ class Queue_item extends Memcached_DataObject function sequenceKey() { return array(false, false); } - static function top($transport) { + static function top($transport=null) { $qi = new Queue_item(); - $qi->transport = $transport; + if ($transport) { + $qi->transport = $transport; + } $qi->orderBy('created'); $qi->whereAdd('claimed is null'); @@ -40,7 +42,8 @@ class Queue_item extends Memcached_DataObject # XXX: potential race condition # can we force it to only update if claimed is still null # (or old)? - common_log(LOG_INFO, 'claiming queue item = ' . $qi->notice_id . ' for transport ' . $transport); + common_log(LOG_INFO, 'claiming queue item = ' . $qi->notice_id . + ' for transport ' . $qi->transport); $orig = clone($qi); $qi->claimed = common_sql_now(); $result = $qi->update($orig); diff --git a/classes/Status_network.php b/classes/Status_network.php index b3117640d..ef8e1ed43 100644 --- a/classes/Status_network.php +++ b/classes/Status_network.php @@ -49,6 +49,13 @@ class Status_network extends DB_DataObject static $cache = null; static $base = null; + /** + * @param string $dbhost + * @param string $dbuser + * @param string $dbpass + * @param string $dbname + * @param array $servers memcached servers to use for caching config info + */ static function setupDB($dbhost, $dbuser, $dbpass, $dbname, $servers) { global $config; @@ -60,12 +67,17 @@ class Status_network extends DB_DataObject if (class_exists('Memcache')) { self::$cache = new Memcache(); + // Can't close persistent connections, making forking painful. + // + // @fixme only do this in *parent* CLI processes. + // single-process and child-processes *should* use persistent. + $persist = php_sapi_name() != 'cli'; if (is_array($servers)) { foreach($servers as $server) { - self::$cache->addServer($server); + self::$cache->addServer($server, 11211, $persist); } } else { - self::$cache->addServer($servers); + self::$cache->addServer($servers, 11211, $persist); } } @@ -89,7 +101,7 @@ class Status_network extends DB_DataObject if (empty($sn)) { $sn = self::staticGet($k, $v); if (!empty($sn)) { - self::$cache->set($ck, $sn); + self::$cache->set($ck, clone($sn)); } } @@ -121,6 +133,11 @@ class Status_network extends DB_DataObject return parent::delete(); } + /** + * @param string $servername hostname + * @param string $pathname URL base path + * @param string $wildcard hostname suffix to match wildcard config + */ static function setupSite($servername, $pathname, $wildcard) { global $config; @@ -150,9 +167,19 @@ class Status_network extends DB_DataObject } if (!empty($sn)) { - if (!empty($sn->hostname) && 0 != strcasecmp($sn->hostname, $servername)) { - $sn->redirectToHostname(); + + // Redirect to the right URL + + if (!empty($sn->hostname) && + empty($_SERVER['HTTPS']) && + 0 != strcasecmp($sn->hostname, $servername)) { + $sn->redirectTo('http://'.$sn->hostname.$_SERVER['REQUEST_URI']); + } else if (!empty($_SERVER['HTTPS']) && + 0 != strcasecmp($sn->hostname, $servername) && + 0 != strcasecmp($sn->nickname.'.'.$wildcard, $servername)) { + $sn->redirectTo('https://'.$sn->nickname.'.'.$wildcard.$_SERVER['REQUEST_URI']); } + $dbhost = (empty($sn->dbhost)) ? 'localhost' : $sn->dbhost; $dbuser = (empty($sn->dbuser)) ? $sn->nickname : $sn->dbuser; $dbpass = $sn->dbpass; @@ -160,7 +187,11 @@ class Status_network extends DB_DataObject $config['db']['database'] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname"; - $config['site']['name'] = $sn->sitename; + $config['site']['name'] = $sn->sitename; + + if (!empty($sn->hostname)) { + $config['site']['server'] = $sn->hostname; + } if (!empty($sn->theme)) { $config['site']['theme'] = $sn->theme; @@ -179,11 +210,8 @@ class Status_network extends DB_DataObject // (C) 2006 by Heiko Richler http://www.richler.de/ // LGPL - function redirectToHostname() + function redirectTo($destination) { - $destination = 'http://'.$this->hostname; - $destination .= $_SERVER['REQUEST_URI']; - $old = 'http'. (($_SERVER['HTTPS'] == 'on') ? 'S' : ''). '://'. diff --git a/classes/Token.php b/classes/Token.php index 1fabd72f1..a129d1fd1 100644 --- a/classes/Token.php +++ b/classes/Token.php @@ -4,7 +4,7 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Token extends Memcached_DataObject +class Token extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -14,7 +14,9 @@ class Token extends Memcached_DataObject public $tok; // char(32) primary_key not_null public $secret; // char(32) not_null public $type; // tinyint(1) not_null - public $state; // tinyint(1) + public $state; // tinyint(1) + public $verifier; // varchar(255) + public $verified_callback; // varchar(255) public $created; // datetime() not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP diff --git a/classes/User.php b/classes/User.php index 34151778c..d6b52be01 100644 --- a/classes/User.php +++ b/classes/User.php @@ -291,6 +291,20 @@ class User extends Memcached_DataObject return false; } + // Everyone gets an inbox + + $inbox = new Inbox(); + + $inbox->user_id = $user->id; + $inbox->notice_ids = ''; + + $result = $inbox->insert(); + + if (!$result) { + common_log_db_error($inbox, 'INSERT', __FILE__); + return false; + } + // Everyone is subscribed to themself $subscription = new Subscription(); @@ -482,89 +496,30 @@ class User extends Memcached_DataObject function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) { - $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false); - + $ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false); return Notice::getStreamByIds($ids); } function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) { - $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true); - + $ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true); return Notice::getStreamByIds($ids); } function friendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) { - $ids = Notice::stream(array($this, '_friendsTimelineDirect'), - array(false), - 'user:friends_timeline:'.$this->id, - $offset, $limit, $since_id, $before_id, $since); + $ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false); return Notice::getStreamByIds($ids); } function ownFriendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) { - $ids = Notice::stream(array($this, '_friendsTimelineDirect'), - array(true), - 'user:friends_timeline_own:'.$this->id, - $offset, $limit, $since_id, $before_id, $since); + $ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true); return Notice::getStreamByIds($ids); } - function _friendsTimelineDirect($own, $offset, $limit, $since_id, $max_id, $since) - { - $qry = - 'SELECT notice.id AS id ' . - 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' . - 'WHERE notice_inbox.user_id = ' . $this->id . ' ' . - 'AND notice.repeat_of IS NULL '; - - if (!$own) { - // XXX: autoload notice inbox for constant - $inbox = new Notice_inbox(); - - $qry .= 'AND notice_inbox.source != ' . NOTICE_INBOX_SOURCE_GATEWAY . ' '; - } - - if ($since_id != 0) { - $qry .= 'AND notice.id > ' . $since_id . ' '; - } - - if ($max_id != 0) { - $qry .= 'AND notice.id <= ' . $max_id . ' '; - } - - if (!is_null($since)) { - $qry .= 'AND notice.modified > \'' . date('Y-m-d H:i:s', $since) . '\' '; - } - - // NOTE: we sort by fave time, not by notice time! - - $qry .= 'ORDER BY notice_id DESC '; - - if (!is_null($offset)) { - $qry .= "LIMIT $limit OFFSET $offset"; - } - - $ids = array(); - - $notice = new Notice(); - - $notice->query($qry); - - while ($notice->fetch()) { - $ids[] = $notice->id; - } - - $notice->free(); - $notice = NULL; - - return $ids; - } - function blowFavesCache() { $cache = common_memcache(); @@ -777,7 +732,6 @@ class User extends Memcached_DataObject 'Remember_me', 'Foreign_link', 'Invitation', - 'Notice_inbox', ); Event::handle('UserDeleteRelated', array($this, &$related)); @@ -945,56 +899,7 @@ class User extends Memcached_DataObject function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null) { - $ids = Notice::stream(array($this, '_repeatedToMeDirect'), - array(), - 'user:repeated_to_me:'.$this->id, - $offset, $limit, $since_id, $max_id, null); - - return Notice::getStreamByIds($ids); - } - - function _repeatedToMeDirect($offset, $limit, $since_id, $max_id, $since) - { - $qry = - 'SELECT notice.id AS id ' . - 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' . - 'WHERE notice_inbox.user_id = ' . $this->id . ' ' . - 'AND notice.repeat_of IS NOT NULL '; - - if ($since_id != 0) { - $qry .= 'AND notice.id > ' . $since_id . ' '; - } - - if ($max_id != 0) { - $qry .= 'AND notice.id <= ' . $max_id . ' '; - } - - if (!is_null($since)) { - $qry .= 'AND notice.modified > \'' . date('Y-m-d H:i:s', $since) . '\' '; - } - - // NOTE: we sort by fave time, not by notice time! - - $qry .= 'ORDER BY notice.id DESC '; - - if (!is_null($offset)) { - $qry .= "LIMIT $limit OFFSET $offset"; - } - - $ids = array(); - - $notice = new Notice(); - - $notice->query($qry); - - while ($notice->fetch()) { - $ids[] = $notice->id; - } - - $notice->free(); - $notice = NULL; - - return $ids; + throw new Exception("Not implemented since inbox change."); } function shareLocation() diff --git a/classes/statusnet.ini b/classes/statusnet.ini index 0db2c5d6e..44088cf6b 100644 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -39,6 +39,7 @@ code = K [consumer] consumer_key = 130 +consumer_secret = 130 seed = 130 created = 142 modified = 384 @@ -241,6 +242,13 @@ address = 130 address_type = 130 created = 142 +[inbox] +user_id = 129 +notice_ids = 66 + +[inbox__keys] +user_id = K + [invitation__keys] code = K @@ -341,6 +349,37 @@ created = 142 tag = K notice_id = K +[oauth_application] +id = 129 +owner = 129 +consumer_key = 130 +name = 130 +description = 2 +icon = 130 +source_url = 2 +organization = 2 +homepage = 2 +callback_url = 130 +type = 17 +access_type = 17 +created = 142 +modified = 384 + +[oauth_application__keys] +id = N + +[oauth_application_user] +profile_id = 129 +application_id = 129 +access_type = 17 +token = 2 +created = 142 +modified = 384 + +[oauth_application_user__keys] +profile_id = K +application_id = K + [profile] id = 129 nickname = 130 @@ -477,6 +516,8 @@ tok = 130 secret = 130 type = 145 state = 17 +verifier = 2 +verified_callback = 2 created = 142 modified = 384 |