From ad5dd9030bf919dba95e1ca066acef71a2d3e6bd Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 13 Apr 2009 19:19:26 -0700 Subject: Add option to Twitter settings for importing Friends Timeline --- classes/Foreign_link.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'classes') diff --git a/classes/Foreign_link.php b/classes/Foreign_link.php index afc0e2180..5d9c82a85 100644 --- a/classes/Foreign_link.php +++ b/classes/Foreign_link.php @@ -57,13 +57,19 @@ class Foreign_link extends Memcached_DataObject return null; } - function set_flags($noticesync, $replysync, $friendsync) + function set_flags($noticesend, $noticerecv, $replysync, $friendsync) { - if ($noticesync) { + if ($noticesend) { $this->noticesync |= FOREIGN_NOTICE_SEND; } else { $this->noticesync &= ~FOREIGN_NOTICE_SEND; } + + if ($noticerecv) { + $this->noticesync |= FOREIGN_NOTICE_RECV; + } else { + $this->noticesync &= ~FOREIGN_NOTICE_RECV; + } if ($replysync) { $this->noticesync |= FOREIGN_NOTICE_SEND_REPLY; -- cgit v1.2.3-54-g00ecf From 3081b2e31708520e6ed83041ce3ba04329d3ec90 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 16 Apr 2009 08:44:48 -0400 Subject: start of querybyid --- classes/User.php | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) (limited to 'classes') diff --git a/classes/User.php b/classes/User.php index 3b9b5cd83..098381f73 100644 --- a/classes/User.php +++ b/classes/User.php @@ -444,21 +444,42 @@ class User extends Memcached_DataObject 'SELECT notice.* ' . 'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' . 'WHERE subscription.subscriber = %d '; - $order = null; + return Notice::getStream(sprintf($qry, $this->id), + 'user:notices_with_friends:' . $this->id, + $offset, $limit, $since_id, $before_id, + $order, $since); } else if ($enabled === true || ($enabled == 'transitional' && $this->inboxed == 1)) { - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' . - 'WHERE notice_inbox.user_id = %d '; - // NOTE: we override ORDER - $order = null; + $cache = common_memcache(); + + if (!empty($cache)) { + + # Get the notices out of the cache + + $notices = $cache->get(common_cache_key($cachekey)); + + # On a cache hit, return a DB-object-like wrapper + + if ($notices !== false) { + $wrapper = new ArrayWrapper(array_slice($notices, $offset, $limit)); + return $wrapper; + } + } + + $inbox = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since); + + $ids = array(); + + while ($inbox->fetch()) { + $ids[] = $inbox->notice_id; + } + + $inbox->free(); + unset($inbox); + + return Notice::getStreamByIds($ids, 'user:notices_with_friends:' . $this->id); } - return Notice::getStream(sprintf($qry, $this->id), - 'user:notices_with_friends:' . $this->id, - $offset, $limit, $since_id, $before_id, - $order, $since); } function blowFavesCache() -- cgit v1.2.3-54-g00ecf From 640628de2d593933e810b4785dfe38923b979713 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 23 Apr 2009 05:03:19 -0400 Subject: A queuehandler for blowing caches offline We add a queuehandler for blowing the memcached caches off-line. This should speed up the processing of new notices. --- classes/Notice.php | 25 ++++++++++++++++- lib/util.php | 14 ++++++++-- scripts/memcachedqueuehandler.php | 58 +++++++++++++++++++++++++++++++++++++++ scripts/startdaemons.sh | 3 +- scripts/stopdaemons.sh | 2 +- 5 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 scripts/memcachedqueuehandler.php (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index 5fa0d79a1..fbfeb9489 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -207,7 +207,11 @@ class Notice extends Memcached_DataObject # XXX: someone clever could prepend instead of clearing the cache if (common_config('memcached', 'enabled')) { - $notice->blowCaches(); + if (common_config('queues', 'enabled')) { + $notice->blowAuthorCaches(); + } else { + $notice->blowCaches(); + } } return $notice; @@ -271,6 +275,25 @@ class Notice extends Memcached_DataObject $this->blowGroupCache($blowLast); } + function blowAuthorCaches($blowLast=false) + { + // Clear the user's cache + $cache = common_memcache(); + if ($cache) { + $user = User::staticGet($this->profile_id); + if (!empty($user)) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); + if ($blowLast) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last')); + } + } + $user->free(); + unset($user); + } + $this->blowNoticeCache($blowLast); + $this->blowPublicCache($blowLast); + } + function blowGroupCache($blowLast=false) { $cache = common_memcache(); diff --git a/lib/util.php b/lib/util.php index e0eda0114..12797891c 100644 --- a/lib/util.php +++ b/lib/util.php @@ -879,7 +879,17 @@ function common_broadcast_notice($notice, $remote=false) function common_enqueue_notice($notice) { - foreach (array('jabber', 'omb', 'sms', 'public', 'twitter', 'facebook', 'ping') as $transport) { + $transports = array('omb', 'sms', 'twitter', 'facebook', 'ping'); + + if (common_config('xmpp', 'enabled')) { + $transports = array_merge($transports, array('jabber', 'public')); + } + + if (common_config('memcached', 'enabled')) { + $transports[] = 'memcached'; + } + + foreach ($transports as $transport) { $qi = new Queue_item(); $qi->notice_id = $notice->id; $qi->transport = $transport; @@ -1332,7 +1342,7 @@ function common_compatible_license($from, $to) */ function common_database_tablename($tablename) { - + if(common_config('db','quote_identifiers')) { $tablename = '"'. $tablename .'"'; } diff --git a/scripts/memcachedqueuehandler.php b/scripts/memcachedqueuehandler.php new file mode 100644 index 000000000..3fcebcfc3 --- /dev/null +++ b/scripts/memcachedqueuehandler.php @@ -0,0 +1,58 @@ +#!/usr/bin/env php +. + */ + +// Abort if called from a web server + +if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { + print "This script must be run from the command line\n"; + exit(); +} + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('LACONICA', true); + +require_once(INSTALLDIR . '/lib/common.php'); + +set_error_handler('common_error_handler'); + +class MemcachedQueueHandler extends QueueHandler +{ + function transport() + { + return 'memcached'; + } + + function handle_notice($notice) + { + // XXX: fork here + common_log(LOG_INFO, "Blowing memcached for $notice->id\n"; + $notice->blowCaches(); + return true; + } +} + +ini_set("max_execution_time", "0"); +ini_set("max_input_time", "0"); +set_time_limit(0); +mb_internal_encoding('UTF-8'); + +$handler = new MemcachedQueueHandler($resource); + +$handler->runOnce(); diff --git a/scripts/startdaemons.sh b/scripts/startdaemons.sh index c3729761d..08de6d954 100755 --- a/scripts/startdaemons.sh +++ b/scripts/startdaemons.sh @@ -24,7 +24,8 @@ DIR=`dirname $0` for f in xmppdaemon.php jabberqueuehandler.php publicqueuehandler.php \ xmppconfirmhandler.php smsqueuehandler.php ombqueuehandler.php \ - twitterqueuehandler.php facebookqueuehandler.php pingqueuehandler.php; do + twitterqueuehandler.php facebookqueuehandler.php pingqueuehandler.php \ + memcachedqueuehandler.php; do echo -n "Starting $f..."; php $DIR/$f diff --git a/scripts/stopdaemons.sh b/scripts/stopdaemons.sh index 2bb8f9ecb..e5a181cd1 100755 --- a/scripts/stopdaemons.sh +++ b/scripts/stopdaemons.sh @@ -24,7 +24,7 @@ SDIR=`dirname $0` DIR=`php $SDIR/getpiddir.php` for f in jabberhandler ombhandler publichandler smshandler pinghandler \ - xmppconfirmhandler xmppdaemon twitterhandler facebookhandler ; do + xmppconfirmhandler xmppdaemon twitterhandler facebookhandler memcachedhandler; do FILES="$DIR/$f.*.pid" for ff in "$FILES" ; do -- cgit v1.2.3-54-g00ecf From aee45ea91dcec4736d8b9befe17e030b873d9226 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 23 Apr 2009 05:08:48 -0400 Subject: Add an inbox queue handler Handle distributing a notice to multiple inboxes in a queue handler rather than in the Web action. --- classes/Notice.php | 5 +++- lib/util.php | 4 +++ scripts/inboxqueuehandler.php | 57 +++++++++++++++++++++++++++++++++++++++++++ scripts/startdaemons.sh | 2 +- scripts/stopdaemons.sh | 3 ++- 5 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 scripts/inboxqueuehandler.php (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index fbfeb9489..ff00f2a94 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -197,7 +197,10 @@ class Notice extends Memcached_DataObject $notice->saveTags(); $notice->saveGroups(); - $notice->addToInboxes(); + if (!common_config('queues', 'enabled')) { + $notice->addToInboxes(); + } + $notice->query('COMMIT'); Event::handle('EndNoticeSave', array($notice)); diff --git a/lib/util.php b/lib/util.php index 12797891c..9b6d2941a 100644 --- a/lib/util.php +++ b/lib/util.php @@ -889,6 +889,10 @@ function common_enqueue_notice($notice) $transports[] = 'memcached'; } + if (common_config('queues', 'enabled')) { + $transports[] = 'inbox'; + } + foreach ($transports as $transport) { $qi = new Queue_item(); $qi->notice_id = $notice->id; diff --git a/scripts/inboxqueuehandler.php b/scripts/inboxqueuehandler.php new file mode 100644 index 000000000..16e334b83 --- /dev/null +++ b/scripts/inboxqueuehandler.php @@ -0,0 +1,57 @@ +#!/usr/bin/env php +. + */ + +// Abort if called from a web server + +if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { + print "This script must be run from the command line\n"; + exit(); +} + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('LACONICA', true); + +require_once(INSTALLDIR . '/lib/common.php'); + +set_error_handler('common_error_handler'); + +class InboxQueueHandler extends QueueHandler +{ + function transport() + { + return 'inbox'; + } + + function handle_notice($notice) + { + common_log(LOG_INFO, "Distributing notice to inboxes for $notice->id"); + $notice->addToInboxes(); + return true; + } +} + +ini_set("max_execution_time", "0"); +ini_set("max_input_time", "0"); +set_time_limit(0); +mb_internal_encoding('UTF-8'); + +$handler = new InboxQueueHandler($resource); + +$handler->runOnce(); diff --git a/scripts/startdaemons.sh b/scripts/startdaemons.sh index 08de6d954..66f9ed4e0 100755 --- a/scripts/startdaemons.sh +++ b/scripts/startdaemons.sh @@ -25,7 +25,7 @@ DIR=`dirname $0` for f in xmppdaemon.php jabberqueuehandler.php publicqueuehandler.php \ xmppconfirmhandler.php smsqueuehandler.php ombqueuehandler.php \ twitterqueuehandler.php facebookqueuehandler.php pingqueuehandler.php \ - memcachedqueuehandler.php; do + memcachedqueuehandler.php inboxqueuehandler.php; do echo -n "Starting $f..."; php $DIR/$f diff --git a/scripts/stopdaemons.sh b/scripts/stopdaemons.sh index e5a181cd1..196991de0 100755 --- a/scripts/stopdaemons.sh +++ b/scripts/stopdaemons.sh @@ -24,7 +24,8 @@ SDIR=`dirname $0` DIR=`php $SDIR/getpiddir.php` for f in jabberhandler ombhandler publichandler smshandler pinghandler \ - xmppconfirmhandler xmppdaemon twitterhandler facebookhandler memcachedhandler; do + xmppconfirmhandler xmppdaemon twitterhandler facebookhandler \ + memcachedhandler inboxhandler; do FILES="$DIR/$f.*.pid" for ff in "$FILES" ; do -- cgit v1.2.3-54-g00ecf From 31b04220bd9fbadbfac01b60276a9c14896b6a45 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 23 Apr 2009 09:34:56 +0000 Subject: incorrect config setting for queues --- classes/Notice.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index ff00f2a94..ebed0b8af 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -197,7 +197,7 @@ class Notice extends Memcached_DataObject $notice->saveTags(); $notice->saveGroups(); - if (!common_config('queues', 'enabled')) { + if (!common_config('queue', 'enabled')) { $notice->addToInboxes(); } @@ -210,7 +210,7 @@ class Notice extends Memcached_DataObject # XXX: someone clever could prepend instead of clearing the cache if (common_config('memcached', 'enabled')) { - if (common_config('queues', 'enabled')) { + if (common_config('queue', 'enabled')) { $notice->blowAuthorCaches(); } else { $notice->blowCaches(); -- cgit v1.2.3-54-g00ecf From a9df5eab100bce97da3e028851d224ea2e9fff80 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 23 Apr 2009 10:38:51 +0000 Subject: insert into user's inbox at Web time --- classes/Notice.php | 76 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 25 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index ebed0b8af..27b98de1c 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -197,7 +197,9 @@ class Notice extends Memcached_DataObject $notice->saveTags(); $notice->saveGroups(); - if (!common_config('queue', 'enabled')) { + if (common_config('queue', 'enabled')) { + $notice->addToAuthorInbox(); + } else { $notice->addToInboxes(); } @@ -282,16 +284,8 @@ class Notice extends Memcached_DataObject { // Clear the user's cache $cache = common_memcache(); - if ($cache) { - $user = User::staticGet($this->profile_id); - if (!empty($user)) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); - if ($blowLast) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last')); - } - } - $user->free(); - unset($user); + if (!empty($cache)) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $this->profile_id)); } $this->blowNoticeCache($blowLast); $this->blowPublicCache($blowLast); @@ -665,6 +659,33 @@ class Notice extends Memcached_DataObject return; } + function addToAuthorInbox() + { + $enabled = common_config('inboxes', 'enabled'); + + if ($enabled === true || $enabled === 'transitional') { + $user = User::staticGet('id', $this->profile_id); + if (empty($user)) { + return; + } + $inbox = new Notice_inbox(); + $UT = common_config('db','type')=='pgsql'?'"user"':'user'; + $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created) ' . + "SELECT $UT.id, " . $this->id . ", '" . $this->created . "' " . + "FROM $UT " . + "WHERE $UT.id = " . $this->profile_id . ' ' . + 'AND NOT EXISTS (SELECT user_id, notice_id ' . + 'FROM notice_inbox ' . + "WHERE user_id = " . $this->profile_id . ' '. + 'AND notice_id = ' . $this->id . ' )'; + if ($enabled === 'transitional') { + $qry .= " AND $UT.inboxed = 1"; + } + $inbox->query($qry); + } + return; + } + function saveGroups() { $enabled = common_config('inboxes', 'enabled'); @@ -717,24 +738,29 @@ class Notice extends Memcached_DataObject // FIXME: do this in an offline daemon - $inbox = new Notice_inbox(); - $UT = common_config('db','type')=='pgsql'?'"user"':'user'; - $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created, source) ' . - "SELECT $UT.id, " . $this->id . ", '" . $this->created . "', 2 " . - "FROM $UT JOIN group_member ON $UT.id = group_member.profile_id " . - 'WHERE group_member.group_id = ' . $group->id . ' ' . - 'AND NOT EXISTS (SELECT user_id, notice_id ' . - 'FROM notice_inbox ' . - "WHERE user_id = $UT.id " . - 'AND notice_id = ' . $this->id . ' )'; - if ($enabled === 'transitional') { - $qry .= " AND $UT.inboxed = 1"; - } - $result = $inbox->query($qry); + $this->addToGroupInboxes($group); } } } + function addToGroupInboxes($group) + { + $inbox = new Notice_inbox(); + $UT = common_config('db','type')=='pgsql'?'"user"':'user'; + $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created, source) ' . + "SELECT $UT.id, " . $this->id . ", '" . $this->created . "', 2 " . + "FROM $UT JOIN group_member ON $UT.id = group_member.profile_id " . + 'WHERE group_member.group_id = ' . $group->id . ' ' . + 'AND NOT EXISTS (SELECT user_id, notice_id ' . + 'FROM notice_inbox ' . + "WHERE user_id = $UT.id " . + 'AND notice_id = ' . $this->id . ' )'; + if ($enabled === 'transitional') { + $qry .= " AND $UT.inboxed = 1"; + } + $result = $inbox->query($qry); + } + function saveReplies() { // Alternative reply format -- cgit v1.2.3-54-g00ecf From 84072aa5cf6124d59a06a7f0a7945c00ee2836da Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 26 Apr 2009 12:13:49 -0400 Subject: run 'set names' after each connection to deal with UTF8 correctly --- classes/Memcached_DataObject.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'classes') diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 5f71f716b..877bbf2e0 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -227,4 +227,20 @@ class Memcached_DataObject extends DB_DataObject $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry); return new ArrayWrapper($cached); } + + // We overload so that 'SET NAMES "utf8"' is called for + // each connection + + function _connect() + { + global $_DB_DATAOBJECT; + $exists = !empty($this->_database_dsn_md5) && + isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]); + $result = parent::_connect(); + if (!$exists) { + $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; + $DB->query('SET NAMES "utf8"'); + } + return $result; + } } -- cgit v1.2.3-54-g00ecf From 068f6801cc59488bfc50ef399a2a4d22b1b7e9c2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 26 Apr 2009 12:27:32 -0400 Subject: Revert "run 'set names' after each connection to deal with UTF8 correctly" This reverts commit 84072aa5cf6124d59a06a7f0a7945c00ee2836da. This commit caused grievous harm to old notices on identi.ca. Reverting until we figure out how to convert the old notices. --- classes/Memcached_DataObject.php | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'classes') diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 877bbf2e0..5f71f716b 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -227,20 +227,4 @@ class Memcached_DataObject extends DB_DataObject $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry); return new ArrayWrapper($cached); } - - // We overload so that 'SET NAMES "utf8"' is called for - // each connection - - function _connect() - { - global $_DB_DATAOBJECT; - $exists = !empty($this->_database_dsn_md5) && - isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]); - $result = parent::_connect(); - if (!$exists) { - $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; - $DB->query('SET NAMES "utf8"'); - } - return $result; - } } -- cgit v1.2.3-54-g00ecf From c7105c2af1ebe3cddd477265c6fea59a61d0c7e5 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 28 Apr 2009 13:07:05 -0400 Subject: Change to avoid a join in notice inbox The join in notice_inbox is causing temp-table sorting on identi.ca, so I'm trying a finer-tuned approach. --- classes/Notice.php | 31 ++++++++++++--- classes/Notice_inbox.php | 101 ++++++++++++++++++++++++++++++++++++++++++++++- classes/User.php | 29 +------------- 3 files changed, 127 insertions(+), 34 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index 27b98de1c..b087c94bc 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -215,6 +215,7 @@ class Notice extends Memcached_DataObject if (common_config('queue', 'enabled')) { $notice->blowAuthorCaches(); } else { + common_debug("Blowing caches for new notice."); $notice->blowCaches(); } } @@ -285,7 +286,7 @@ class Notice extends Memcached_DataObject // Clear the user's cache $cache = common_memcache(); if (!empty($cache)) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $this->profile_id)); + $cache->delete(common_cache_key('notice_inbox:by_user:'.$this->profile_id)); } $this->blowNoticeCache($blowLast); $this->blowPublicCache($blowLast); @@ -307,9 +308,9 @@ class Notice extends Memcached_DataObject $member->group_id = $group_inbox->group_id; if ($member->find()) { while ($member->fetch()) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $member->profile_id)); + $cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id)); if ($blowLast) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $member->profile_id . ';last')); + $cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id . ';last')); } } } @@ -352,9 +353,9 @@ class Notice extends Memcached_DataObject 'WHERE subscription.subscribed = ' . $this->profile_id); while ($user->fetch()) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); + $cache->delete(common_cache_key('notice_inbox:by_user:'.$user_id)); if ($blowLast) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last')); + $cache->delete(common_cache_key('notice_inbox:by_user:'.$user_id.';last')); } } $user->free(); @@ -613,6 +614,26 @@ class Notice extends Memcached_DataObject return $wrapper; } + function getStreamByIds($ids) + { + $cache = common_memcache(); + + if (!empty($cache)) { + $notices = array(); + foreach ($ids as $id) { + $notices[] = Notice::staticGet('id', $id); + } + return new ArrayWrapper($notices); + } else { + $notice = new Notice(); + $notice->whereAdd('id in (' . implode(', ', $ids) . ')'); + $notice->orderBy('id DESC'); + + $notice->find(); + return $notice; + } + } + function publicStream($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null) { diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php index 81ddb4538..162da74fe 100644 --- a/classes/Notice_inbox.php +++ b/classes/Notice_inbox.php @@ -1,7 +1,7 @@ INBOX_CACHE_WINDOW) { + common_debug('Doing direct DB hit for notice_inbox since the params are screwy.'); + return Notice_inbox::_streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since); + } + + $idkey = common_cache_key('notice_inbox:by_user:'.$user_id); + + $idstr = $cache->get($idkey); + + if (!empty($idstr)) { + // Cache hit! Woohoo! + common_debug('Cache hit for notice_inbox.'); + $window = explode(',', $idstr); + $ids = array_slice($window, $offset, $limit); + return $ids; + } + + $laststr = common_cache_key($idkey.';last'); + + if (!empty($laststr)) { + common_debug('Cache hit for notice_inbox on last item.'); + + $window = explode(',', $laststr); + $last_id = $window[0]; + $new_ids = Notice_inbox::_streamDirect($user_id, 0, INBOX_CACHE_WINDOW, + $last_id, null, null); + + $new_window = array_merge($new_ids, $window); + + $new_windowstr = implode(',', $new_window); + + $result = $cache->set($idkey, $new_windowstr); + $result = $cache->set($idkey . ';last', $new_windowstr); + + $ids = array_slice($new_window, $offset, $limit); + + return $ids; + } + + $window = Notice_inbox::_streamDirect($user_id, 0, INBOX_CACHE_WINDOW, + null, null, null); + + $windowstr = implode(',', $new_window); + + $result = $cache->set($idkey, $windowstr); + $result = $cache->set($idkey . ';last', $windowstr); + + $ids = array_slice($window, $offset, $limit); + + return $ids; + } + + function _streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since) + { + $inbox = new Notice_inbox(); + + $inbox->user_id = $user_id; + + if ($since_id != 0) { + $inbox->whereAdd('notice_id > ' . $since_id); + } + + if ($before_id != 0) { + $inbox->whereAdd('notice_id < ' . $before_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)) { + $inbox->limit($offset, $limit); + } + + $ids = array(); + + if ($inbox->find()) { + while ($inbox->fetch()) { + $ids[] = $inbox->notice_id; + } + } + + return $ids; + } } diff --git a/classes/User.php b/classes/User.php index 098381f73..ce7ea1464 100644 --- a/classes/User.php +++ b/classes/User.php @@ -451,34 +451,9 @@ class User extends Memcached_DataObject } else if ($enabled === true || ($enabled == 'transitional' && $this->inboxed == 1)) { - $cache = common_memcache(); + $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since); - if (!empty($cache)) { - - # Get the notices out of the cache - - $notices = $cache->get(common_cache_key($cachekey)); - - # On a cache hit, return a DB-object-like wrapper - - if ($notices !== false) { - $wrapper = new ArrayWrapper(array_slice($notices, $offset, $limit)); - return $wrapper; - } - } - - $inbox = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since); - - $ids = array(); - - while ($inbox->fetch()) { - $ids[] = $inbox->notice_id; - } - - $inbox->free(); - unset($inbox); - - return Notice::getStreamByIds($ids, 'user:notices_with_friends:' . $this->id); + return Notice::getStreamByIds($ids); } } -- cgit v1.2.3-54-g00ecf From fe53e780be5db4ceb2831a1d69faec6130a10deb Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 28 Apr 2009 13:31:56 -0400 Subject: Remove some debug comments in query-by-id --- classes/Notice.php | 5 ++--- classes/Notice_inbox.php | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index b087c94bc..49d0939c1 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -215,7 +215,6 @@ class Notice extends Memcached_DataObject if (common_config('queue', 'enabled')) { $notice->blowAuthorCaches(); } else { - common_debug("Blowing caches for new notice."); $notice->blowCaches(); } } @@ -353,9 +352,9 @@ class Notice extends Memcached_DataObject 'WHERE subscription.subscribed = ' . $this->profile_id); while ($user->fetch()) { - $cache->delete(common_cache_key('notice_inbox:by_user:'.$user_id)); + $cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id)); if ($blowLast) { - $cache->delete(common_cache_key('notice_inbox:by_user:'.$user_id.';last')); + $cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id.';last')); } } $user->free(); diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php index 162da74fe..f32137038 100644 --- a/classes/Notice_inbox.php +++ b/classes/Notice_inbox.php @@ -50,7 +50,6 @@ class Notice_inbox extends Memcached_DataObject if (empty($cache) || $since_id != 0 || $before_id != 0 || !is_null($since) || ($offset + $limit) > INBOX_CACHE_WINDOW) { - common_debug('Doing direct DB hit for notice_inbox since the params are screwy.'); return Notice_inbox::_streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since); } @@ -60,7 +59,6 @@ class Notice_inbox extends Memcached_DataObject if (!empty($idstr)) { // Cache hit! Woohoo! - common_debug('Cache hit for notice_inbox.'); $window = explode(',', $idstr); $ids = array_slice($window, $offset, $limit); return $ids; @@ -69,8 +67,6 @@ class Notice_inbox extends Memcached_DataObject $laststr = common_cache_key($idkey.';last'); if (!empty($laststr)) { - common_debug('Cache hit for notice_inbox on last item.'); - $window = explode(',', $laststr); $last_id = $window[0]; $new_ids = Notice_inbox::_streamDirect($user_id, 0, INBOX_CACHE_WINDOW, -- cgit v1.2.3-54-g00ecf From f798d1ea430d25c2f4a60179c65b39b1257a5340 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 28 Apr 2009 17:08:20 -0700 Subject: Added dirty dates to Foreign_link --- classes/Foreign_link.php | 2 ++ classes/laconica.ini | 2 ++ db/laconica.sql | 2 ++ 3 files changed, 6 insertions(+) (limited to 'classes') diff --git a/classes/Foreign_link.php b/classes/Foreign_link.php index 5d9c82a85..606560951 100644 --- a/classes/Foreign_link.php +++ b/classes/Foreign_link.php @@ -17,6 +17,8 @@ class Foreign_link extends Memcached_DataObject public $noticesync; // tinyint(1) not_null default_1 public $friendsync; // tinyint(1) not_null default_2 public $profilesync; // tinyint(1) not_null default_1 + public $last_noticesync; // datetime() + public $last_friendsync; // datetime() public $created; // datetime() not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP diff --git a/classes/laconica.ini b/classes/laconica.ini index dd424bbdd..5a905a4bb 100755 --- a/classes/laconica.ini +++ b/classes/laconica.ini @@ -55,6 +55,8 @@ credentials = 2 noticesync = 145 friendsync = 145 profilesync = 145 +last_noticesync = 14 +last_friendsync = 14 created = 142 modified = 384 diff --git a/db/laconica.sql b/db/laconica.sql index 83d610f0d..d9e21a7b5 100644 --- a/db/laconica.sql +++ b/db/laconica.sql @@ -291,6 +291,8 @@ create table foreign_link ( noticesync tinyint not null default 1 comment 'notice synchronization, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies', friendsync tinyint not null default 2 comment 'friend synchronization, bit 1 = sync outgoing, bit 2 = sync incoming', profilesync tinyint not null default 1 comment 'profile synchronization, bit 1 = sync outgoing, bit 2 = sync incoming', + last_noticesync datetime default null comment 'last time notices were imported', + last_friendsync datetime default null comment 'last time friends were imported', created datetime not null comment 'date this record was created', modified timestamp comment 'date this record was modified', -- cgit v1.2.3-54-g00ecf From 10ef8a2f7112a83188e9702d480abd3c6062c26c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 29 Apr 2009 11:27:45 -0400 Subject: Move algorithm for caching to Notice class Moved the algorithm for notice stream caching to the Notice class. --- classes/Notice.php | 55 +++++++++++++++++++++++++++++++++++++++++++++++ classes/Notice_inbox.php | 56 +++++------------------------------------------- 2 files changed, 60 insertions(+), 51 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index 49d0939c1..faabb1e14 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -967,4 +967,59 @@ class Notice extends Memcached_DataObject array('notice' => $this->id)); } } + + function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $since=null) + { + $cache = common_memcache(); + + if (empty($cache) || + $since_id != 0 || $before_id != 0 || !is_null($since) || + ($offset + $limit) > NOTICE_CACHE_WINDOW) { + return call_user_func_array($fn, array_merge($args, array($offset, $limit, $since_id, + $before_id, $since))); + } + + $idkey = common_cache_key($cachekey); + + $idstr = $cache->get($idkey); + + if (!empty($idstr)) { + // Cache hit! Woohoo! + $window = explode(',', $idstr); + $ids = array_slice($window, $offset, $limit); + return $ids; + } + + $laststr = common_cache_key($idkey.';last'); + + if (!empty($laststr)) { + $window = explode(',', $laststr); + $last_id = $window[0]; + $new_ids = call_user_func_array($fn, array_merge($args, array(0, NOTICE_CACHE_WINDOW, + $last_id, 0, null))); + + $new_window = array_merge($new_ids, $window); + + $new_windowstr = implode(',', $new_window); + + $result = $cache->set($idkey, $new_windowstr); + $result = $cache->set($idkey . ';last', $new_windowstr); + + $ids = array_slice($new_window, $offset, $limit); + + return $ids; + } + + $window = call_user_func_array($fn, array_merge($args, array(0, NOTICE_CACHE_WINDOW, + 0, 0, null))); + + $windowstr = implode(',', $new_window); + + $result = $cache->set($idkey, $windowstr); + $result = $cache->set($idkey . ';last', $windowstr); + + $ids = array_slice($window, $offset, $limit); + + return $ids; + } } diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php index f32137038..dec14b0d1 100644 --- a/classes/Notice_inbox.php +++ b/classes/Notice_inbox.php @@ -43,58 +43,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=0, $limit=20, $since_id=0, $before_id=0, $since=null) + function stream($user_id, $offset, $limit, $since_id, $before_id, $since) { - $cache = common_memcache(); - - if (empty($cache) || - $since_id != 0 || $before_id != 0 || !is_null($since) || - ($offset + $limit) > INBOX_CACHE_WINDOW) { - return Notice_inbox::_streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since); - } - - $idkey = common_cache_key('notice_inbox:by_user:'.$user_id); - - $idstr = $cache->get($idkey); - - if (!empty($idstr)) { - // Cache hit! Woohoo! - $window = explode(',', $idstr); - $ids = array_slice($window, $offset, $limit); - return $ids; - } - - $laststr = common_cache_key($idkey.';last'); - - if (!empty($laststr)) { - $window = explode(',', $laststr); - $last_id = $window[0]; - $new_ids = Notice_inbox::_streamDirect($user_id, 0, INBOX_CACHE_WINDOW, - $last_id, null, null); - - $new_window = array_merge($new_ids, $window); - - $new_windowstr = implode(',', $new_window); - - $result = $cache->set($idkey, $new_windowstr); - $result = $cache->set($idkey . ';last', $new_windowstr); - - $ids = array_slice($new_window, $offset, $limit); - - return $ids; - } - - $window = Notice_inbox::_streamDirect($user_id, 0, INBOX_CACHE_WINDOW, - null, null, null); - - $windowstr = implode(',', $new_window); - - $result = $cache->set($idkey, $windowstr); - $result = $cache->set($idkey . ';last', $windowstr); - - $ids = array_slice($window, $offset, $limit); - - return $ids; + return Notice::stream(array('Notice_inbox', '_streamDirect'), + array($user_id), + 'notice_inbox:by_user:'.$user_id, + $offset, $limit, $since_id, $before_id, $since); } function _streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since) -- cgit v1.2.3-54-g00ecf From a4d959b8a2254d173bdf45f418f6add9f6f62cda Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 29 Apr 2009 12:05:31 -0400 Subject: Public stream uses IDs method Public stream now uses IDs method --- classes/Notice.php | 55 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 11 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index faabb1e14..6a7522bd3 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -635,25 +635,58 @@ class Notice extends Memcached_DataObject function publicStream($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null) { + $ids = Notice::stream(array('Notice', '_publicStreamDirect'), + array(), + 'public', + $offset, $limit, $since_id, $before_id, $since); - $parts = array(); + return Notice::getStreamByIds($ids); + } + + function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null) + { + $notice = new Notice(); + + $notice->selectAdd(); // clears it + $notice->selectAdd('id'); - $qry = 'SELECT * FROM notice '; + $notice->orderBy('id DESC'); + + if (!is_null($offset)) { + $notice->limit($offset, $limit); + } if (common_config('public', 'localonly')) { - $parts[] = 'is_local = 1'; + $notice->whereAdd('is_local = 1'); } else { # -1 == blacklisted - $parts[] = 'is_local != -1'; + $notice->whereAdd('is_local != -1'); } - if ($parts) { - $qry .= ' WHERE ' . implode(' AND ', $parts); + if ($since_id != 0) { + $notice->whereAdd('id > ' . $since_id); } - return Notice::getStream($qry, - 'public', - $offset, $limit, $since_id, $before_id, null, $since); + if ($before_id != 0) { + $notice->whereAdd('id < ' . $before_id); + } + + if (!is_null($since)) { + $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\''); + } + + $ids = array(); + + if ($notice->find()) { + while ($notice->fetch()) { + $ids[] = $notice->id; + } + } + + $notice->free(); + $notice = NULL; + + return $ids; } function addToInboxes() @@ -990,7 +1023,7 @@ class Notice extends Memcached_DataObject return $ids; } - $laststr = common_cache_key($idkey.';last'); + $laststr = $cache->get($idkey.';last'); if (!empty($laststr)) { $window = explode(',', $laststr); @@ -1013,7 +1046,7 @@ class Notice extends Memcached_DataObject $window = call_user_func_array($fn, array_merge($args, array(0, NOTICE_CACHE_WINDOW, 0, 0, null))); - $windowstr = implode(',', $new_window); + $windowstr = implode(',', $window); $result = $cache->set($idkey, $windowstr); $result = $cache->set($idkey . ';last', $windowstr); -- cgit v1.2.3-54-g00ecf From 1e8ea1eb460b163176c4d7d1e7dffa500024ef91 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 29 Apr 2009 16:09:03 -0400 Subject: Make the tag stream use ID mechanism --- classes/Notice.php | 5 +---- classes/Notice_tag.php | 61 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 52 insertions(+), 14 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index 6a7522bd3..2bb466155 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -328,10 +328,7 @@ class Notice extends Memcached_DataObject $tag->notice_id = $this->id; if ($tag->find()) { while ($tag->fetch()) { - $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag)); - if ($blowLast) { - $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag . ';last')); - } + $tag->blowCache($blowLast); } } $tag->free(); diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php index f2247299a..e5b772243 100644 --- a/classes/Notice_tag.php +++ b/classes/Notice_tag.php @@ -37,21 +37,62 @@ class Notice_tag extends Memcached_DataObject ###END_AUTOCODE static function getStream($tag, $offset=0, $limit=20) { - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN notice_tag ON notice.id = notice_tag.notice_id ' . - "WHERE notice_tag.tag = '%s' "; - - return Notice::getStream(sprintf($qry, $tag), - 'notice_tag:notice_stream:' . common_keyize($tag), - $offset, $limit); + + $ids = Notice::stream(array('Notice_tag', '_streamDirect'), + array($tag), + 'notice_tag:notice_ids:' . common_keyize($tag), + $offset, $limit); + + return Notice::getStreamByIds($ids); + } + + function _streamDirect($tag, $offset, $limit, $since_id, $before_id, $since) + { + $nt = new Notice_tag(); + + $nt->tag = $tag; + + $nt->selectAdd(); + $nt->selectAdd('notice_id'); + + if ($since_id != 0) { + $nt->whereAdd('notice_id > ' . $since_id); + } + + if ($before_id != 0) { + $nt->whereAdd('notice_id < ' . $before_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)) { + $nt->limit($offset, $limit); + } + + $ids = array(); + + if ($nt->find()) { + while ($nt->fetch()) { + $ids[] = $nt->notice_id; + } + } + + return $ids; } - function blowCache() + function blowCache($blowLast=false) { $cache = common_memcache(); if ($cache) { - $cache->delete(common_cache_key('notice_tag:notice_stream:' . $this->tag)); + $idkey = common_cache_key('notice_tag:notice_ids:' . common_keyize($this->tag)); + $cache->delete($idkey); + if ($blowLast) { + $cache->delete($idkey.';last'); + } } } -- cgit v1.2.3-54-g00ecf From aee641ee1e311fb0af0f9f6d75ca7fae2c7d8477 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 29 Apr 2009 20:45:33 -0400 Subject: make replies use new query format --- classes/Notice.php | 4 ++-- classes/Reply.php | 47 +++++++++++++++++++++++++++++++++++++++++++++-- classes/User.php | 10 +++------- 3 files changed, 50 insertions(+), 11 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index 2bb466155..808631f4d 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -380,9 +380,9 @@ class Notice extends Memcached_DataObject $reply->notice_id = $this->id; if ($reply->find()) { while ($reply->fetch()) { - $cache->delete(common_cache_key('user:replies:'.$reply->profile_id)); + $cache->delete(common_cache_key('reply:stream:'.$reply->profile_id)); if ($blowLast) { - $cache->delete(common_cache_key('user:replies:'.$reply->profile_id.';last')); + $cache->delete(common_cache_key('reply:stream:'.$reply->profile_id.';last')); } } } diff --git a/classes/Reply.php b/classes/Reply.php index af86aaf87..4439053b4 100644 --- a/classes/Reply.php +++ b/classes/Reply.php @@ -4,7 +4,7 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Reply extends Memcached_DataObject +class Reply extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -13,7 +13,7 @@ class Reply extends Memcached_DataObject public $notice_id; // int(4) primary_key not_null public $profile_id; // int(4) primary_key not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP - public $replied_id; // int(4) + public $replied_id; // int(4) /* Static get */ function staticGet($k,$v=null) @@ -21,4 +21,47 @@ 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, $before_id=0, $since=null) + { + $ids = Notice::stream(array('Reply', '_streamDirect'), + array($user_id), + 'reply:stream:' . $user_id, + $offset, $limit, $since_id, $before_id, $since); + return $ids; + } + + function _streamDirect($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { + $reply = new Reply(); + $reply->profile_id = $user_id; + + if ($since_id != 0) { + $reply->whereAdd('notice_id > ' . $since_id); + } + + if ($before_id != 0) { + $reply->whereAdd('notice_id < ' . $before_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)) { + $reply->limit($offset, $limit); + } + + $ids = array(); + + if ($reply->find()) { + while ($reply->fetch()) { + $ids[] = $reply->notice_id; + } + } + + return $ids; + } } diff --git a/classes/User.php b/classes/User.php index ce7ea1464..b76e45c33 100644 --- a/classes/User.php +++ b/classes/User.php @@ -401,13 +401,9 @@ class User extends Memcached_DataObject function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) { - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN reply ON notice.id = reply.notice_id ' . - 'WHERE reply.profile_id = %d '; - return Notice::getStream(sprintf($qry, $this->id), - 'user:replies:'.$this->id, - $offset, $limit, $since_id, $before_id, null, $since); + $ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id, $since); + common_debug("Ids = " . implode(',', $ids)); + return Notice::getStreamByIds($ids); } function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) -- cgit v1.2.3-54-g00ecf From 3328ec545c36fc2408cdb9d048effe24feafe218 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 1 May 2009 11:27:57 -0700 Subject: make profile notice getting use ids --- classes/Notice.php | 6 +++--- classes/Profile.php | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 11 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index 808631f4d..eceed325b 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -363,10 +363,10 @@ class Notice extends Memcached_DataObject { if ($this->is_local) { $cache = common_memcache(); - if ($cache) { - $cache->delete(common_cache_key('profile:notices:'.$this->profile_id)); + if (!empty($cache)) { + $cache->delete(common_cache_key('profile:notice_ids:'.$this->profile_id)); if ($blowLast) { - $cache->delete(common_cache_key('profile:notices:'.$this->profile_id.';last')); + $cache->delete(common_cache_key('profile:notice_ids:'.$this->profile_id.';last')); } } } diff --git a/classes/Profile.php b/classes/Profile.php index f3bfe299c..ae5641d79 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -155,14 +155,51 @@ class Profile extends Memcached_DataObject function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { - $qry = - 'SELECT * ' . - 'FROM notice ' . - 'WHERE profile_id = %d '; - - return Notice::getStream(sprintf($qry, $this->id), - 'profile:notices:'.$this->id, - $offset, $limit, $since_id, $before_id); + // 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, $before_id); + + return Notice::getStreamByIds($ids); + } + + function _streamDirect($offset, $limit, $since_id, $before_id, $since) + { + $notice = new Notice(); + + $notice->profile_id = $this->id; + + $notice->selectAdd(); + $notice->selectAdd('id'); + + if ($since_id != 0) { + $notice->whereAdd('id > ' . $since_id); + } + + if ($before_id != 0) { + $notice->whereAdd('id < ' . $before_id); + } + + if (!is_null($since)) { + $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\''); + } + + $notice->orderBy('id DESC'); + + if (!is_null($offset)) { + $notice->limit($offset, $limit); + } + + $ids = array(); + + if ($notice->find()) { + while ($notice->fetch()) { + $ids[] = $notice->id; + } + } + + return $ids; } function isMember($group) -- cgit v1.2.3-54-g00ecf From 021b520a11d3449a1476182e1ad117582999d364 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 1 May 2009 11:38:50 -0700 Subject: Make user group stream use IDs --- classes/Notice.php | 4 ++-- classes/User_group.php | 53 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 10 deletions(-) (limited to 'classes') diff --git a/classes/Notice.php b/classes/Notice.php index eceed325b..c036e6e9e 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -299,9 +299,9 @@ class Notice extends Memcached_DataObject $group_inbox->notice_id = $this->id; if ($group_inbox->find()) { while ($group_inbox->fetch()) { - $cache->delete(common_cache_key('group:notices:'.$group_inbox->group_id)); + $cache->delete(common_cache_key('user_group:notice_ids:' . $group_inbox->group_id)); if ($blowLast) { - $cache->delete(common_cache_key('group:notices:'.$group_inbox->group_id.';last')); + $cache->delete(common_cache_key('user_group:notice_ids:' . $group_inbox->group_id.';last')); } $member = new Group_member(); $member->group_id = $group_inbox->group_id; diff --git a/classes/User_group.php b/classes/User_group.php index d152f9d56..7cc31e702 100755 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -50,13 +50,50 @@ class User_group extends Memcached_DataObject function getNotices($offset, $limit) { - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN group_inbox ON notice.id = group_inbox.notice_id ' . - 'WHERE group_inbox.group_id = %d '; - return Notice::getStream(sprintf($qry, $this->id), - 'group:notices:'.$this->id, - $offset, $limit); + $ids = Notice::stream(array($this, '_streamDirect'), + array(), + 'user_group:notice_ids:' . $this->id, + $offset, $limit); + + return Notice::getStreamByIds($ids); + } + + function _streamDirect($offset, $limit, $since_id, $before_id, $since) + { + $inbox = new Group_inbox(); + + $inbox->group_id = $this->id; + + $inbox->selectAdd(); + $inbox->selectAdd('notice_id'); + + if ($since_id != 0) { + $inbox->whereAdd('notice_id > ' . $since_id); + } + + if ($before_id != 0) { + $inbox->whereAdd('notice_id < ' . $before_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)) { + $inbox->limit($offset, $limit); + } + + $ids = array(); + + if ($inbox->find()) { + while ($inbox->fetch()) { + $ids[] = $inbox->notice_id; + } + } + + return $ids; } function allowedNickname($nickname) @@ -91,7 +128,7 @@ class User_group extends Memcached_DataObject function setOriginal($filename) { $imagefile = new ImageFile($this->id, Avatar::path($filename)); - + $orig = clone($this); $this->original_logo = Avatar::url($filename); $this->homepage_logo = Avatar::url($imagefile->resize(AVATAR_PROFILE_SIZE)); -- cgit v1.2.3-54-g00ecf From 5314d9b2cfaef3f2fd0ead262e18d1776fd99c8d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 1 May 2009 12:01:28 -0700 Subject: make faves work with ids --- classes/Fave.php | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-- classes/Notice.php | 4 ++-- classes/User.php | 38 +++++++++++++++++--------------------- 3 files changed, 70 insertions(+), 25 deletions(-) (limited to 'classes') diff --git a/classes/Fave.php b/classes/Fave.php index 24df5938c..915b4572f 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -4,7 +4,7 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Fave extends Memcached_DataObject +class Fave extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -31,9 +31,58 @@ class Fave extends Memcached_DataObject } return $fave; } - + function &pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Fave', $kv); } + + function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE) + { + $ids = Notice::stream(array('Fave', '_streamDirect'), + array($user_id), + 'fave:ids_by_user:'.$user_id, + $offset, $limit); + return $ids; + } + + function _streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since) + { + $fav = new Fave(); + + $fav->user_id = $user_id; + + $fav->selectAdd(); + $fav->selectAdd('notice_id'); + + if ($since_id != 0) { + $fav->whereAdd('notice_id > ' . $since_id); + } + + if ($before_id != 0) { + $fav->whereAdd('notice_id < ' . $before_id); + } + + if (!is_null($since)) { + $fav->whereAdd('modified > \'' . date('Y-m-d H:i:s', $since) . '\''); + } + + // NOTE: we sort by fave time, not by notice time! + + $fav->orderBy('modified DESC'); + + if (!is_null($offset)) { + $fav->limit($offset, $limit); + } + + $ids = array(); + + if ($fav->find()) { + while ($fav->fetch()) { + $ids[] = $fav->notice_id; + } + } + + return $ids; + } } diff --git a/classes/Notice.php b/classes/Notice.php index c036e6e9e..771a4e715 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -412,9 +412,9 @@ class Notice extends Memcached_DataObject $fave->notice_id = $this->id; if ($fave->find()) { while ($fave->fetch()) { - $cache->delete(common_cache_key('user:faves:'.$fave->user_id)); + $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id)); if ($blowLast) { - $cache->delete(common_cache_key('user:faves:'.$fave->user_id.';last')); + $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id.';last')); } } } diff --git a/classes/User.php b/classes/User.php index b76e45c33..b5ac7b220 100644 --- a/classes/User.php +++ b/classes/User.php @@ -349,30 +349,31 @@ class User extends Memcached_DataObject $cache = common_memcache(); // XXX: Kind of a hack. + if ($cache) { // This is the stream of favorite notices, in rev chron // order. This forces it into cache. - $faves = $this->favoriteNotices(0, NOTICE_CACHE_WINDOW); - $cnt = 0; - while ($faves->fetch()) { - if ($faves->id < $notice->id) { - // If we passed it, it's not a fave - return false; - } else if ($faves->id == $notice->id) { - // If it matches a cached notice, then it's a fave - return true; - } - $cnt++; + + $ids = Fave::stream($this->id, 0, NOTICE_CACHE_WINDOW); + + // If it's in the list, then it's a fave + + if (in_array($notice->id, $ids)) { + return true; } + // If we're not past the end of the cache window, // then the cache has all available faves, so this one // is not a fave. - if ($cnt < NOTICE_CACHE_WINDOW) { + + if (count($ids) < NOTICE_CACHE_WINDOW) { return false; } + // Otherwise, cache doesn't have all faves; // fall through to the default } + $fave = Fave::pkeyGet(array('user_id' => $this->id, 'notice_id' => $notice->id)); return ((is_null($fave)) ? false : true); @@ -418,13 +419,8 @@ class User extends Memcached_DataObject function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE) { - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . - 'WHERE fave.user_id = %d '; - return Notice::getStream(sprintf($qry, $this->id), - 'user:faves:'.$this->id, - $offset, $limit); + $ids = Fave::stream($this->id, $offset, $limit); + return Notice::getStreamByIds($ids); } function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) @@ -459,8 +455,8 @@ class User extends Memcached_DataObject if ($cache) { // Faves don't happen chronologically, so we need to blow // ;last cache, too - $cache->delete(common_cache_key('user:faves:'.$this->id)); - $cache->delete(common_cache_key('user:faves:'.$this->id).';last'); + $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id)); + $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id.';last')); } } -- cgit v1.2.3-54-g00ecf From d010d811ba812ccc8fbb1a49fcba7a30226ea779 Mon Sep 17 00:00:00 2001 From: Robin Millette Date: Mon, 11 May 2009 13:45:00 -0400 Subject: db stuff for URLs: redirections, oembed, etc. --- actions/newnotice.php | 33 ++++++++++++ classes/File.php | 47 +++++++++++++++++ classes/File_oembed.php | 53 +++++++++++++++++++ classes/File_redirection.php | 45 ++++++++++++++++ classes/File_thumbnail.php | 45 ++++++++++++++++ classes/File_to_post.php | 43 +++++++++++++++ classes/laconica.ini | 123 +++++++++++++++++++++++++++++++++++++++++++ classes/laconica.links.ini | 71 +++++++++++++++++++++++++ lib/util.php | 8 ++- 9 files changed, 466 insertions(+), 2 deletions(-) create mode 100644 classes/File.php create mode 100644 classes/File_oembed.php create mode 100644 classes/File_redirection.php create mode 100644 classes/File_thumbnail.php create mode 100644 classes/File_to_post.php mode change 100755 => 100644 classes/laconica.ini (limited to 'classes') diff --git a/actions/newnotice.php b/actions/newnotice.php index cbd04c58b..8b03abc62 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -166,6 +166,8 @@ class NewnoticeAction extends Action return; } + $this->saveUrls($notice); + common_broadcast_notice($notice); if ($this->boolean('ajax')) { @@ -191,6 +193,37 @@ class NewnoticeAction extends Action } } + /** save all urls in the notice to the db + * + * follow redirects and save all available file information + * (mimetype, date, size, oembed, etc.) + * + * @param class $notice Notice to pull URLs from + * + * @return void + */ + function saveUrls($notice) { + common_debug("Saving all URLs"); + common_replace_urls_callback($notice->content, array($this, 'saveUrl'), $notice->id); + } + + function saveUrl($data) { + list($url, $notice_id) = $data; + common_debug("Saving $url for $notice_id"); + $file = File::staticGet('url', $url); + if (empty($file)) { + common_debug('unknown file/url'); + $file = new File; + $file->url = $url; + $file->insert(); + } + common_debug('File: ' . print_r($file, true)); + $f2p = new File_to_post; + $f2p->file_id = $file->id; + $f2p->post_id = $notice_id; + $f2p->insert(); + } + /** * Show an Ajax-y error message * diff --git a/classes/File.php b/classes/File.php new file mode 100644 index 000000000..8dd017b79 --- /dev/null +++ b/classes/File.php @@ -0,0 +1,47 @@ +. + */ + +if (!defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +/** + * Table Definition for file + */ + +class File extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'file'; // table name + public $id; // int(11) not_null primary_key group_by + public $url; // varchar(255) unique_key + public $mimetype; // varchar(50) + public $size; // int(11) group_by + public $title; // varchar(255) + public $date; // int(11) group_by + public $protected; // int(1) group_by + + /* Static get */ + function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/File_oembed.php b/classes/File_oembed.php new file mode 100644 index 000000000..33dd8200c --- /dev/null +++ b/classes/File_oembed.php @@ -0,0 +1,53 @@ +. + */ + +if (!defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +/** + * Table Definition for file_oembed + */ + +class File_oembed extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'file_oembed'; // table name + public $id; // int(11) not_null primary_key group_by + public $file_id; // int(11) unique_key group_by + public $version; // varchar(20) + public $type; // varchar(20) + public $provider; // varchar(50) + public $provider_url; // varchar(255) + public $width; // int(11) group_by + public $height; // int(11) group_by + public $html; // blob(65535) blob + public $title; // varchar(255) + public $author_name; // varchar(50) + public $author_url; // varchar(255) + public $url; // varchar(255) + + /* Static get */ + function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_oembed',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/File_redirection.php b/classes/File_redirection.php new file mode 100644 index 000000000..e2d1e69c3 --- /dev/null +++ b/classes/File_redirection.php @@ -0,0 +1,45 @@ +. + */ + +if (!defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +/** + * Table Definition for file_redirection + */ + +class File_redirection extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'file_redirection'; // table name + public $id; // int(11) not_null primary_key group_by + public $url; // varchar(255) unique_key + public $file_id; // int(11) group_by + public $redirections; // int(11) group_by + public $httpcode; // int(11) group_by + + /* Static get */ + function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_redirection',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php new file mode 100644 index 000000000..7b906a07c --- /dev/null +++ b/classes/File_thumbnail.php @@ -0,0 +1,45 @@ +. + */ + +if (!defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +/** + * Table Definition for file_thumbnail + */ + +class File_thumbnail extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'file_thumbnail'; // table name + public $id; // int(11) not_null primary_key group_by + public $file_id; // int(11) unique_key group_by + public $url; // varchar(255) unique_key + public $width; // int(11) group_by + public $height; // int(11) group_by + + /* Static get */ + function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_thumbnail',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/File_to_post.php b/classes/File_to_post.php new file mode 100644 index 000000000..bd0528d98 --- /dev/null +++ b/classes/File_to_post.php @@ -0,0 +1,43 @@ +. + */ + +if (!defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +/** + * Table Definition for file_to_post + */ + +class File_to_post extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'file_to_post'; // table name + public $id; // int(11) not_null primary_key group_by + public $file_id; // int(11) multiple_key group_by + public $post_id; // int(11) group_by + + /* Static get */ + function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_to_post',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/laconica.ini b/classes/laconica.ini old mode 100755 new mode 100644 index 5a905a4bb..00a1b8936 --- a/classes/laconica.ini +++ b/classes/laconica.ini @@ -1,4 +1,67 @@ +[file] +id = 129 +url = 2 +mimetype = 2 +size = 1 +title = 2 +date = 1 +protected = 17 + +[file__keys] +id = K +url = U + +[file_oembed] +id = 129 +file_id = 1 +version = 2 +type = 2 +provider = 2 +provider_url = 2 +width = 1 +height = 1 +html = 66 +title = 2 +author_name = 2 +author_url = 2 +url = 2 + +[file_oembed__keys] +id = K +file_id = U + +[file_redirection] +id = 129 +url = 2 +file_id = 1 +redirections = 1 +httpcode = 1 + +[file_redirection__keys] +id = K +url = U + +[file_thumbnail] +id = 129 +file_id = 1 +url = 2 +width = 1 +height = 1 + +[file_thumbnail__keys] +id = K +file_id = U +url = U + +[file_to_post] +id = 129 +file_id = 1 +post_id = 1 + +[file_to_post__keys] +id = K + [avatar] profile_id = 129 original = 17 @@ -393,3 +456,63 @@ modified = 384 [user_openid__keys] canonical = K display = U + +[file] +id = 129 +url = 2 +mimetype = 2 +size = 1 +title = 2 +date = 1 +protected = 1 + +[file__keys] +id = N + +[file_oembed] +id = 129 +file_id = 129 +version = 2 +type = 2 +provider = 2 +provider_url = 2 +width = 1 +height = 1 +html = 34 +title = 2 +author_name = 2 +author_url = 2 +url = 2 + +[file_oembed__keys] +id = N + +[file_redirection] +id = 129 +url = 2 +file_id = 129 +redirections = 1 +httpcode = 1 + +[file_redirection__keys] +id = N + +[file_thumbnail] +id = 129 +file_id = 129 +url = 2 +width = 1 +height = 1 + +[file_thumbnail__keys] +id = N + +[file_to_post] +id = 129 +file_id = 129 +post_id = 129 + +[file_to_post__keys] +id = N + + diff --git a/classes/laconica.links.ini b/classes/laconica.links.ini index 173b18726..bc52ce578 100644 --- a/classes/laconica.links.ini +++ b/classes/laconica.links.ini @@ -41,3 +41,74 @@ subscribed = profile:id [fave] notice_id = notice:id user_id = user:id + +[file_oembed] +file_id = file:id + +[file_redirection] +file_id = file:id + +[file_thumbnail] +file_id = file:id + +[file_to_post] +file_id = file:id +post_id = post:id + +[avatar] +profile_id = profile:id + +[user] +id = profile:id +carrier = sms_carrier:id + +[remote_profile] +id = profile:id + +[notice] +profile_id = profile:id +reply_to = notice:id + +[reply] +notice_id = notice:id +profile_id = profile:id + +[token] +consumer_key = consumer:consumer_key + +[nonce] +consumer_key,token = token:consumer_key,token + +[user_openid] +user_id = user:id + +[confirm_address] +user_id = user:id + +[remember_me] +user_id = user:id + +[queue_item] +notice_id = notice:id + +[subscription] +subscriber = profile:id +subscribed = profile:id + +[fave] +notice_id = notice:id +user_id = user:id + +[file_oembed] +file_id = file:id + +[file_redirection] +file_id = file:id + +[file_thumbnail] +file_id = file:id + +[file_to_post] +file_id = file:id +post_id = post:id + diff --git a/lib/util.php b/lib/util.php index 198185338..c4a63a441 100644 --- a/lib/util.php +++ b/lib/util.php @@ -395,7 +395,7 @@ function common_render_text($text) return $r; } -function common_replace_urls_callback($text, $callback) { +function common_replace_urls_callback($text, $callback, $notice_id = null) { // Start off with a regex $regex = '#'. '(?:'. @@ -466,7 +466,11 @@ function common_replace_urls_callback($text, $callback) { $url = (mb_strpos($orig_url, htmlspecialchars($url)) === FALSE) ? $url:htmlspecialchars($url); // Call user specified func - $modified_url = call_user_func($callback, $url); + if (isset($notice_id)) { + $modified_url = call_user_func($callback, array($url, $notice_id)); + } else { + $modified_url = call_user_func($callback, $url); + } // Replace it! $start = mb_strpos($text, $url, $offset); -- cgit v1.2.3-54-g00ecf From 3b7ee5a5f9a2cd3066aff8a7a12d09878be3b06c Mon Sep 17 00:00:00 2001 From: Robin Millette Date: Wed, 13 May 2009 14:27:32 -0400 Subject: rewrote short url stuff to handle new file/url classes (redirections, oembed, mimetypes, etc.) --- actions/newnotice.php | 18 +--- classes/File.php | 65 ++++++++++++ classes/File_oembed.php | 43 ++++++++ classes/File_redirection.php | 231 +++++++++++++++++++++++++++++++++++++++++++ classes/File_to_post.php | 17 ++++ classes/Notice.php | 6 +- classes/laconica.ini | 64 ------------ classes/laconica.links.ini | 59 +---------- lib/Shorturl_api.php | 3 +- lib/util.php | 112 ++++----------------- 10 files changed, 382 insertions(+), 236 deletions(-) (limited to 'classes') diff --git a/actions/newnotice.php b/actions/newnotice.php index 8b03abc62..ae0ff9636 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -158,7 +158,8 @@ class NewnoticeAction extends Action $replyto = 'false'; } - $notice = Notice::saveNew($user->id, $content, 'web', 1, +// $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1, + $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1, ($replyto == 'false') ? null : $replyto); if (is_string($notice)) { @@ -203,25 +204,12 @@ class NewnoticeAction extends Action * @return void */ function saveUrls($notice) { - common_debug("Saving all URLs"); common_replace_urls_callback($notice->content, array($this, 'saveUrl'), $notice->id); } function saveUrl($data) { list($url, $notice_id) = $data; - common_debug("Saving $url for $notice_id"); - $file = File::staticGet('url', $url); - if (empty($file)) { - common_debug('unknown file/url'); - $file = new File; - $file->url = $url; - $file->insert(); - } - common_debug('File: ' . print_r($file, true)); - $f2p = new File_to_post; - $f2p->file_id = $file->id; - $f2p->post_id = $notice_id; - $f2p->insert(); + $zzz = File::processNew($url, $notice_id); } /** diff --git a/classes/File.php b/classes/File.php index 8dd017b79..2ddc5deb8 100644 --- a/classes/File.php +++ b/classes/File.php @@ -20,6 +20,11 @@ if (!defined('LACONICA')) { exit(1); } require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; +require_once INSTALLDIR.'/classes/File_redirection.php'; +require_once INSTALLDIR.'/classes/File_oembed.php'; +require_once INSTALLDIR.'/classes/File_thumbnail.php'; +require_once INSTALLDIR.'/classes/File_to_post.php'; +//require_once INSTALLDIR.'/classes/File_redirection.php'; /** * Table Definition for file @@ -44,4 +49,64 @@ class File extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + function isProtected($url) { + return 'http://www.facebook.com/login.php' === $url; + } + + function saveNew($redir_data, $given_url) { + $x = new File; + $x->url = $given_url; + if (!empty($redir_data['protected'])) $x->protected = $redir_data['protected']; + if (!empty($redir_data['title'])) $x->title = $redir_data['title']; + if (!empty($redir_data['type'])) $x->mimetype = $redir_data['type']; + if (!empty($redir_data['size'])) $x->size = intval($redir_data['size']); + if (isset($redir_data['time']) && $redir_data['time'] > 0) $x->date = intval($redir_data['time']); + $file_id = $x->insert(); + + 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); + } + return $x; + } + + function processNew($given_url, $notice_id) { + if (empty($given_url)) return -1; // error, no url to process + $given_url = File_redirection::_canonUrl($given_url); + if (empty($given_url)) return -1; // error, no url to process + $file = File::staticGet('url', $given_url); + if (empty($file->id)) { + $file_redir = File_redirection::staticGet('url', $given_url); + if (empty($file_redir->id)) { + $redir_data = File_redirection::where($given_url); + $redir_url = $redir_data['url']; + if ($redir_url === $given_url) { + $x = File::saveNew($redir_data, $given_url); + $file_id = $x->id; + + } else { + $x = File::processNew($redir_url, $notice_id); + $file_id = $x->id; + File_redirection::saveNew($redir_data, $file_id, $given_url); + } + } else { + $file_id = $file_redir->file_id; + } + } else { + $file_id = $file->id; + $x = $file; + } + + if (empty($x)) { + $x = File::staticGet($file_id); + if (empty($x)) die('Impossible!'); + } + + File_to_post::processNew($file_id, $notice_id); + return $x; + } } diff --git a/classes/File_oembed.php b/classes/File_oembed.php index 33dd8200c..2846f49db 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -50,4 +50,47 @@ class File_oembed extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + + function _getOembed($url, $maxwidth = 500, $maxheight = 400, $format = 'json') { + $cmd = 'http://oohembed.com/oohembed/?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 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->insert(); + + if (!empty($data['thumbnail_url'])) { + $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->insert(); + } + + + + } } + + diff --git a/classes/File_redirection.php b/classes/File_redirection.php index e2d1e69c3..a71d1c083 100644 --- a/classes/File_redirection.php +++ b/classes/File_redirection.php @@ -20,6 +20,11 @@ if (!defined('LACONICA')) { exit(1); } require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; +require_once INSTALLDIR.'/classes/File.php'; +require_once INSTALLDIR.'/classes/File_oembed.php'; + +define('USER_AGENT', 'Laconica user agent / file probe'); + /** * Table Definition for file_redirection @@ -42,4 +47,230 @@ class File_redirection extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + + + function _commonCurl($url, $redirs) { + $curlh = curl_init(); + curl_setopt($curlh, CURLOPT_URL, $url); + curl_setopt($curlh, CURLOPT_AUTOREFERER, true); // # setup referer header when folowing redirects + curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 10); // # seconds to wait + curl_setopt($curlh, CURLOPT_MAXREDIRS, $redirs); // # max number of http redirections to follow + curl_setopt($curlh, CURLOPT_USERAGENT, USER_AGENT); + curl_setopt($curlh, CURLOPT_FOLLOWLOCATION, true); // Follow redirects + curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curlh, CURLOPT_FILETIME, true); + curl_setopt($curlh, CURLOPT_HEADER, true); // Include header in output + return $curlh; + } + + function _redirectWhere_imp($short_url, $redirs = 10, $protected = false) { + if ($redirs < 0) return false; + + // let's see if we know this... + $a = File::staticGet('url', $short_url); + if (empty($a->id)) { + $b = File_redirection::staticGet('url', $short_url); + if (empty($b->id)) { + // we'll have to figure it out + } else { + // this is a redirect to $b->file_id + $a = File::staticGet($b->file_id); + $url = $a->url; + } + } else { + // this is a direct link to $a->url + $url = $a->url; + } + if (isset($url)) { + return $url; + } + + + + $curlh = File_redirection::_commonCurl($short_url, $redirs); + // Don't include body in output + curl_setopt($curlh, CURLOPT_NOBODY, true); + curl_exec($curlh); + $info = curl_getinfo($curlh); + curl_close($curlh); + + if (405 == $info['http_code']) { + $curlh = File_redirection::_commonCurl($short_url, $redirs); + curl_exec($curlh); + $info = curl_getinfo($curlh); + curl_close($curlh); + } + + if (!empty($info['redirect_count']) && File::isProtected($info['url'])) { + return File_redirection::_redirectWhere_imp($short_url, $info['redirect_count'] - 1, true); + } + + $ret = array('code' => $info['http_code'] + , 'redirects' => $info['redirect_count'] + , 'url' => $info['url']); + + if (!empty($info['content_type'])) $ret['type'] = $info['content_type']; + if ($protected) $ret['protected'] = true; + if (!empty($info['download_content_length'])) $ret['size'] = $info['download_content_length']; + if (isset($info['filetime']) && ($info['filetime'] > 0)) $ret['time'] = $info['filetime']; + return $ret; + } + + function where($in_url) { + $ret = File_redirection::_redirectWhere_imp($in_url); + return $ret; + } + + function makeShort($long_url) { + $long_url = File_redirection::_canonUrl($long_url); + // do we already know this long_url and have a short redirection for it? + $file = new File; + $file_redir = new File_redirection; + $file->url = $long_url; + $file->joinAdd($file_redir); + $file->selectAdd('length(file_redirection.url) as len'); + $file->limit(1); + $file->orderBy('len'); + $file->find(true); + if (!empty($file->id)) { + return $file->url; + } + + // if yet unknown, we must find a short url according to user settings + $short_url = File_redirection::_userMakeShort($long_url, common_current_user()); + return $short_url; + } + + function _userMakeShort($long_url, $user) { + if (empty($user)) { + // common current user does not find a user when called from the XMPP daemon + // therefore we'll set one here fix, so that XMPP given URLs may be shortened + $user->urlshorteningservice = 'ur1.ca'; + } + $curlh = curl_init(); + curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait + curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica'); + curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true); + + switch($user->urlshorteningservice) { + case 'ur1.ca': + require_once INSTALLDIR.'/lib/Shorturl_api.php'; + $short_url_service = new LilUrl; + $short_url = $short_url_service->shorten($long_url); + break; + + case '2tu.us': + $short_url_service = new TightUrl; + require_once INSTALLDIR.'/lib/Shorturl_api.php'; + $short_url = $short_url_service->shorten($long_url); + break; + + case 'ptiturl.com': + require_once INSTALLDIR.'/lib/Shorturl_api.php'; + $short_url_service = new PtitUrl; + $short_url = $short_url_service->shorten($long_url); + break; + + case 'bit.ly': + curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url)); + $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl; + break; + + case 'is.gd': + curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url)); + $short_url = curl_exec($curlh); + break; + case 'snipr.com': + curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url)); + $short_url = curl_exec($curlh); + break; + case 'metamark.net': + curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url)); + $short_url = curl_exec($curlh); + break; + case 'tinyurl.com': + curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url)); + $short_url = curl_exec($curlh); + break; + default: + $short_url = false; + } + + curl_close($curlh); + + if ($short_url) { + $short_url = (string)$short_url; +if(1) { + // store it + $file = File::staticGet('url', $long_url); + if (empty($file)) { + $redir_data = File_redirection::where($long_url); + $file = File::saveNew($redir_data, $long_url); + $file_id = $file->id; + if (!empty($redir_data['oembed']['json'])) { + File_oembed::saveNew($redir_data['oembed']['json'], $file_id); + } + } else { + $file_id = $file->id; + } + $file_redir = File_redirection::staticGet('url', $short_url); + if (empty($file_redir)) { + $file_redir = new File_redirection; + $file_redir->url = $short_url; + $file_redir->file_id = $file_id; + $file_redir->insert(); + } } + return $short_url; + } + return $long_url; + } + + function _canonUrl($in_url, $default_scheme = 'http://') { + if (empty($in_url)) return false; + $out_url = $in_url; + $p = parse_url($out_url); + if (empty($p['host']) || empty($p['scheme'])) { + list($scheme) = explode(':', $in_url, 2); + switch ($scheme) { + case 'fax': + case 'tel': + $out_url = str_replace('.-()', '', $out_url); + break; + + case 'mailto': + case 'aim': + case 'jabber': + case 'xmpp': + // don't touch anything + break; + + default: + $out_url = $default_scheme . ltrim($out_url, '/'); + $p = parse_url($out_url); + if (empty($p['scheme'])) return false; + break; + } + } + + if (('ftp' == $p['scheme']) || ('http' == $p['scheme']) || ('https' == $p['scheme'])) { + if (empty($p['host'])) return false; + if (empty($p['path'])) { + $out_url .= '/'; + } + } + + return $out_url; + } + + function saveNew($data, $file_id, $url) { + $file_redir = new File_redirection; + $file_redir->url = $url; + $file_redir->file_id = $file_id; + $file_redir->redirections = intval($data['redirects']); + $file_redir->httpcode = intval($data['code']); + $file_redir->insert(); + } +} + diff --git a/classes/File_to_post.php b/classes/File_to_post.php index bd0528d98..00ddebe6b 100644 --- a/classes/File_to_post.php +++ b/classes/File_to_post.php @@ -40,4 +40,21 @@ class File_to_post extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + function processNew($file_id, $notice_id) { + static $seen = array(); + if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) { + $f2p = new File_to_post; + $f2p->file_id = $file_id; + $f2p->post_id = $notice_id; + $f2p->insert(); + if (empty($seen[$notice_id])) { + $seen[$notice_id] = array($file_id); + } else { + $seen[$notice_id][] = $file_id; + } + } + + } } + diff --git a/classes/Notice.php b/classes/Notice.php index 382d160ab..c2fa2d19e 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -124,7 +124,7 @@ class Notice extends Memcached_DataObject $profile = Profile::staticGet($profile_id); - $final = common_shorten_links($content); +// $final = common_shorten_links($content); if (!$profile) { common_log(LOG_ERR, 'Problem saving notice. Unknown user.'); @@ -167,8 +167,8 @@ class Notice extends Memcached_DataObject $notice->reply_to = $reply_to; $notice->created = common_sql_now(); - $notice->content = $final; - $notice->rendered = common_render_content($final, $notice); + $notice->content = $content; + $notice->rendered = common_render_content($content, $notice); $notice->source = $source; $notice->uri = $uri; diff --git a/classes/laconica.ini b/classes/laconica.ini index 00a1b8936..316923af0 100644 --- a/classes/laconica.ini +++ b/classes/laconica.ini @@ -1,67 +1,3 @@ - -[file] -id = 129 -url = 2 -mimetype = 2 -size = 1 -title = 2 -date = 1 -protected = 17 - -[file__keys] -id = K -url = U - -[file_oembed] -id = 129 -file_id = 1 -version = 2 -type = 2 -provider = 2 -provider_url = 2 -width = 1 -height = 1 -html = 66 -title = 2 -author_name = 2 -author_url = 2 -url = 2 - -[file_oembed__keys] -id = K -file_id = U - -[file_redirection] -id = 129 -url = 2 -file_id = 1 -redirections = 1 -httpcode = 1 - -[file_redirection__keys] -id = K -url = U - -[file_thumbnail] -id = 129 -file_id = 1 -url = 2 -width = 1 -height = 1 - -[file_thumbnail__keys] -id = K -file_id = U -url = U - -[file_to_post] -id = 129 -file_id = 1 -post_id = 1 - -[file_to_post__keys] -id = K - [avatar] profile_id = 129 original = 17 diff --git a/classes/laconica.links.ini b/classes/laconica.links.ini index bc52ce578..95c63f3c0 100644 --- a/classes/laconica.links.ini +++ b/classes/laconica.links.ini @@ -53,62 +53,5 @@ file_id = file:id [file_to_post] file_id = file:id -post_id = post:id - -[avatar] -profile_id = profile:id - -[user] -id = profile:id -carrier = sms_carrier:id - -[remote_profile] -id = profile:id - -[notice] -profile_id = profile:id -reply_to = notice:id - -[reply] -notice_id = notice:id -profile_id = profile:id - -[token] -consumer_key = consumer:consumer_key - -[nonce] -consumer_key,token = token:consumer_key,token - -[user_openid] -user_id = user:id - -[confirm_address] -user_id = user:id - -[remember_me] -user_id = user:id - -[queue_item] -notice_id = notice:id - -[subscription] -subscriber = profile:id -subscribed = profile:id - -[fave] -notice_id = notice:id -user_id = user:id - -[file_oembed] -file_id = file:id - -[file_redirection] -file_id = file:id - -[file_thumbnail] -file_id = file:id - -[file_to_post] -file_id = file:id -post_id = post:id +post_id = notice:id diff --git a/lib/Shorturl_api.php b/lib/Shorturl_api.php index fe106cb83..924aa93a8 100644 --- a/lib/Shorturl_api.php +++ b/lib/Shorturl_api.php @@ -22,6 +22,7 @@ if (!defined('LACONICA')) { exit(1); } class ShortUrlApi { protected $service_url; + protected $long_limit = 27; function __construct($service_url) { @@ -39,7 +40,7 @@ class ShortUrlApi } private function is_long($url) { - return strlen($url) >= 30; + return strlen($url) >= $this->long_limit; } protected function http_post($data) { diff --git a/lib/util.php b/lib/util.php index c4a63a441..25c0fb0a1 100644 --- a/lib/util.php +++ b/lib/util.php @@ -466,10 +466,10 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) { $url = (mb_strpos($orig_url, htmlspecialchars($url)) === FALSE) ? $url:htmlspecialchars($url); // Call user specified func - if (isset($notice_id)) { - $modified_url = call_user_func($callback, array($url, $notice_id)); - } else { + if (empty($notice_id)) { $modified_url = call_user_func($callback, $url); + } else { + $modified_url = call_user_func($callback, array($url, $notice_id)); } // Replace it! @@ -485,107 +485,29 @@ function common_linkify($url) { // It comes in special'd, so we unspecial it before passing to the stringifying // functions $url = htmlspecialchars_decode($url); - $display = $url; - $url = (!preg_match('#^([a-z]+://|(mailto|aim|tel):)#i', $url)) ? 'http://'.$url : $url; - - $attrs = array('href' => $url, 'rel' => 'external'); + $display = File_redirection::_canonUrl($url); + $longurl_data = File_redirection::where($url); + if (is_array($longurl_data)) { + $longurl = $longurl_data['url']; + } elseif (is_string($longurl_data)) { + $longurl = $longurl_data; + } else { + die('impossible to linkify'); + } - if ($longurl = common_longurl($url)) { + $attrs = array('href' => $longurl, 'rel' => 'external'); +if(0){ + if ($longurl !== $url) { $attrs['title'] = $longurl; } - - return XMLStringer::estring('a', $attrs, $display); -} - -function common_longurl($short_url) -{ - $long_url = common_shorten_link($short_url, true); - if ($long_url === $short_url) return false; - return $long_url; } - -function common_longurl2($uri) -{ - $uri_e = urlencode($uri); - $longurl = unserialize(file_get_contents("http://api.longurl.org/v1/expand?format=php&url=$uri_e")); - if (empty($longurl['long_url']) || $uri === $longurl['long_url']) return false; - return stripslashes($longurl['long_url']); + return XMLStringer::estring('a', $attrs, $display); } function common_shorten_links($text) { if (mb_strlen($text) <= 140) return $text; - static $cache = array(); - if (isset($cache[$text])) return $cache[$text]; - // \s = not a horizontal whitespace character (since PHP 5.2.4) - return $cache[$text] = common_replace_urls_callback($text, 'common_shorten_link');; -} - -function common_shorten_link($url, $reverse = false) -{ - - static $url_cache = array(); - if ($reverse) return isset($url_cache[$url]) ? $url_cache[$url] : $url; - - $user = common_current_user(); - if (!isset($user)) { - // common current user does not find a user when called from the XMPP daemon - // therefore we'll set one here fix, so that XMPP given URLs may be shortened - $user->urlshorteningservice = 'ur1.ca'; - } - $curlh = curl_init(); - curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait - curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica'); - curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true); - - switch($user->urlshorteningservice) { - case 'ur1.ca': - $short_url_service = new LilUrl; - $short_url = $short_url_service->shorten($url); - break; - - case '2tu.us': - $short_url_service = new TightUrl; - $short_url = $short_url_service->shorten($url); - break; - - case 'ptiturl.com': - $short_url_service = new PtitUrl; - $short_url = $short_url_service->shorten($url); - break; - - case 'bit.ly': - curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($url)); - $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl; - break; - - case 'is.gd': - curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - case 'snipr.com': - curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - case 'metamark.net': - curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - case 'tinyurl.com': - curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - default: - $short_url = false; - } - - curl_close($curlh); - - if ($short_url) { - $url_cache[(string)$short_url] = $url; - return (string)$short_url; - } - return $url; + return common_replace_urls_callback($text, array('File_redirection', 'makeShort')); } function common_xml_safe_str($str) -- cgit v1.2.3-54-g00ecf From fecb8c706dadb790c3a8c219275b4ba88e00b8ea Mon Sep 17 00:00:00 2001 From: Robin Millette Date: Fri, 15 May 2009 15:04:58 -0400 Subject: Attachments and their list now provide "ajax" view. Also added a few sidebars relating tags and attachments. --- actions/attachment.php | 250 +++++++++++++++++++++++++++++ actions/attachment_ajax.php | 141 ++++++++++++++++ actions/attachments.php | 292 ++++++++++++++++++++++++++++++++++ actions/attachments_ajax.php | 115 ++++++++++++++ actions/tag.php | 12 +- classes/File.php | 11 ++ classes/File_oembed.php | 11 +- classes/File_thumbnail.php | 10 ++ classes/Notice.php | 12 +- js/jquery.joverlay.min.js | 6 + js/util.js | 4 + lib/action.php | 12 +- lib/attachmentlist.php | 300 +++++++++++++++++++++++++++++++++++ lib/noticelist.php | 71 ++++++++- lib/router.php | 14 ++ theme/base/images/icons/clip-big.png | Bin 0 -> 11245 bytes theme/base/images/icons/clip.png | Bin 0 -> 2298 bytes 17 files changed, 1243 insertions(+), 18 deletions(-) create mode 100644 actions/attachment.php create mode 100644 actions/attachment_ajax.php create mode 100644 actions/attachments.php create mode 100644 actions/attachments_ajax.php create mode 100644 js/jquery.joverlay.min.js create mode 100644 lib/attachmentlist.php create mode 100644 theme/base/images/icons/clip-big.png create mode 100644 theme/base/images/icons/clip.png (limited to 'classes') diff --git a/actions/attachment.php b/actions/attachment.php new file mode 100644 index 000000000..981882a5b --- /dev/null +++ b/actions/attachment.php @@ -0,0 +1,250 @@ +. + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +//require_once INSTALLDIR.'/lib/personalgroupnav.php'; +//require_once INSTALLDIR.'/lib/feedlist.php'; +require_once INSTALLDIR.'/lib/attachmentlist.php'; + +/** + * Show notice attachments + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class AttachmentAction extends Action +{ + /** + * Attachment object to show + */ + + var $attachment = null; + + /** + * Profile of the notice object + */ + +// var $profile = null; + + /** + * Avatar of the profile of the notice object + */ + +// var $avatar = null; + + /** + * Load attributes based on database arguments + * + * Loads all the DB stuff + * + * @param array $args $_REQUEST array + * + * @return success flag + */ + + function prepare($args) + { + parent::prepare($args); + + $id = $this->arg('attachment'); + + $this->attachment = File::staticGet($id); + + if (!$this->attachment) { + $this->clientError(_('No such attachment.'), 404); + return false; + } + return true; + } + + /** + * Is this action read-only? + * + * @return boolean true + */ + + function isReadOnly($args) + { + return true; + } + + /** + * Title of the page + * + * @return string title of the page + */ + function title() + { + $a = new Attachment($this->attachment); + return $a->title(); + } + + + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ +/* + function lastModified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } +*/ + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ +/* + function etag() + { + $avtime = ($this->avatar) ? + strtotime($this->avatar->modified) : 0; + + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + $avtime)) . '"'; + } +*/ + + + /** + * Handle input + * + * Only handles get, so just show the page. + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showPage(); + } + + /** + * Don't show local navigation + * + * @return void + */ + + function showLocalNavBlock() + { + } + + /** + * Fill the content area of the page + * + * Shows a single notice list item. + * + * @return void + */ + + function showContent() + { + $this->elementStart('ul', array('class' => 'attachments')); + $ali = new Attachment($this->attachment, $this); + $cnt = $ali->show(); + $this->elementEnd('ul'); + } + + /** + * Don't show page notice + * + * @return void + */ + + function showPageNoticeBlock() + { + } + + /** + * Show aside: this attachments appears in what notices + * + * @return void + */ + + function showAside() { + $notice = new Notice; + $f2p = new File_to_post; + $f2p->file_id = $this->attachment->id; + $notice->joinAdd($f2p); + $notice->orderBy('created desc'); + $x = $notice->find(); + $this->elementStart('ol'); + while($notice->fetch()) { + $this->elementStart('li'); + $profile = $notice->getProfile(); + $this->element('a', array('href' => $notice->uri), $profile->nickname . ' on ' . $notice->created); + $this->elementEnd('li'); + } + $this->elementEnd('ol'); + $notice->free(); + $f2p->free(); + + $notice_tag = new Notice_tag; + $attachment = new File; + + $query = 'select tag,count(tag) as c from notice_tag join file_to_post on (notice_tag.notice_id=post_id) join notice on notice_id = notice.id where file_id=' . $notice_tag->escape($this->attachment->id) . ' group by tag order by c desc'; + + $notice_tag->query($query); + $this->elementStart('ol'); + while($notice_tag->fetch()) { + $this->elementStart('li'); + $href = common_local_url('tag', array('tag' => $notice_tag->tag)); + $this->element('a', array('href' => $href), $notice_tag->tag . ' (' . $notice_tag->c . ')'); + $this->elementEnd('li'); + } + $this->elementEnd('ol'); + } +} diff --git a/actions/attachment_ajax.php b/actions/attachment_ajax.php new file mode 100644 index 000000000..1620b27dd --- /dev/null +++ b/actions/attachment_ajax.php @@ -0,0 +1,141 @@ +. + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/actions/attachment.php'; + +/** + * Show notice attachments + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class Attachment_ajaxAction extends AttachmentAction +{ + /** + * Load attributes based on database arguments + * + * Loads all the DB stuff + * + * @param array $args $_REQUEST array + * + * @return success flag + */ + + function prepare($args) + { + parent::prepare($args); + if (!$this->attachment) { + $this->clientError(_('No such attachment.'), 404); + return false; + } + return true; + } + + /** + * Show page, a template method. + * + * @return nothing + */ + function showPage() + { + if (Event::handle('StartShowBody', array($this))) { + $this->showCore(); + Event::handle('EndShowBody', array($this)); + } + } + + /** + * Show core. + * + * Shows local navigation, content block and aside. + * + * @return nothing + */ + function showCore() + { + $this->elementStart('div', array('id' => 'core')); + if (Event::handle('StartShowContentBlock', array($this))) { + $this->showContentBlock(); + Event::handle('EndShowContentBlock', array($this)); + } + $this->elementEnd('div'); + } + + + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ +/* + function lastModified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } +*/ + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ +/* + function etag() + { + $avtime = ($this->avatar) ? + strtotime($this->avatar->modified) : 0; + + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + $avtime)) . '"'; + } +*/ +} + diff --git a/actions/attachments.php b/actions/attachments.php new file mode 100644 index 000000000..6b31c839d --- /dev/null +++ b/actions/attachments.php @@ -0,0 +1,292 @@ +. + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +//require_once INSTALLDIR.'/lib/personalgroupnav.php'; +//require_once INSTALLDIR.'/lib/feedlist.php'; +require_once INSTALLDIR.'/lib/attachmentlist.php'; + +/** + * Show notice attachments + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class AttachmentsAction extends Action +{ + /** + * Notice object to show + */ + + var $notice = null; + + /** + * Profile of the notice object + */ + + var $profile = null; + + /** + * Avatar of the profile of the notice object + */ + + var $avatar = null; + + /** + * Is this action read-only? + * + * @return boolean true + */ + + function isReadOnly($args) + { + return true; + } + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ + + function lastModified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ + + function etag() + { + $avtime = ($this->avatar) ? + strtotime($this->avatar->modified) : 0; + + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + $avtime)) . '"'; + } + + /** + * Title of the page + * + * @return string title of the page + */ + + function title() + { + return sprintf(_('%1$s\'s status on %2$s'), + $this->profile->nickname, + common_exact_date($this->notice->created)); + } + + + /** + * Load attributes based on database arguments + * + * Loads all the DB stuff + * + * @param array $args $_REQUEST array + * + * @return success flag + */ + + function prepare($args) + { + parent::prepare($args); + + $id = $this->arg('notice'); + + $this->notice = Notice::staticGet($id); + + if (!$this->notice) { + $this->clientError(_('No such notice.'), 404); + return false; + } + + +/* +// STOP if there are no attachments +// maybe even redirect if there's a single one +// RYM FIXME TODO + $this->clientError(_('No such attachment.'), 404); + return false; + +*/ + + + + + $this->profile = $this->notice->getProfile(); + + if (!$this->profile) { + $this->serverError(_('Notice has no profile'), 500); + return false; + } + + $this->avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE); + return true; + } + + + + /** + * Handle input + * + * Only handles get, so just show the page. + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($this->notice->is_local == 0) { + if (!empty($this->notice->url)) { + common_redirect($this->notice->url, 301); + } else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) { + common_redirect($this->notice->uri, 301); + } + } else { + $f2p = new File_to_post; + $f2p->post_id = $this->notice->id; + $file = new File; + $file->joinAdd($f2p); + $file->selectAdd(); + $file->selectAdd('file.id as id'); + $count = $file->find(true); + if (!$count) return; + if (1 === $count) { + common_redirect(common_local_url('attachment', array('attachment' => $file->id)), 301); + } else { + $this->showPage(); + } + } + } + + /** + * Don't show local navigation + * + * @return void + */ + + function showLocalNavBlock() + { + } + + /** + * Fill the content area of the page + * + * Shows a single notice list item. + * + * @return void + */ + + function showContent() + { + $al = new AttachmentList($this->notice, $this); + $cnt = $al->show(); + } + + /** + * Don't show page notice + * + * @return void + */ + + function showPageNoticeBlock() + { + } + + /** + * Don't show aside + * + * @return void + */ + + function showAside() { + } + + /** + * Extra content + * + * We show the microid(s) for the author, if any. + * + * @return void + */ + + function extraHead() + { + $user = User::staticGet($this->profile->id); + + if (!$user) { + return; + } + + if ($user->emailmicroid && $user->email && $this->notice->uri) { + $id = new Microid('mailto:'. $user->email, + $this->notice->uri); + $this->element('meta', array('name' => 'microid', + 'content' => $id->toString())); + } + + if ($user->jabbermicroid && $user->jabber && $this->notice->uri) { + $id = new Microid('xmpp:', $user->jabber, + $this->notice->uri); + $this->element('meta', array('name' => 'microid', + 'content' => $id->toString())); + } + } +} + diff --git a/actions/attachments_ajax.php b/actions/attachments_ajax.php new file mode 100644 index 000000000..402d8b5e7 --- /dev/null +++ b/actions/attachments_ajax.php @@ -0,0 +1,115 @@ +. + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +//require_once INSTALLDIR.'/lib/personalgroupnav.php'; +//require_once INSTALLDIR.'/lib/feedlist.php'; +require_once INSTALLDIR.'/actions/attachments.php'; + +/** + * Show notice attachments + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class Attachments_ajaxAction extends AttachmentsAction +{ + function showContent() + { + } + + /** + * Fill the content area of the page + * + * Shows a single notice list item. + * + * @return void + */ + + function showContentBlock() + { + $al = new AttachmentList($this->notice, $this); + $cnt = $al->show(); + } + + /** + * Extra content + * + * We show the microid(s) for the author, if any. + * + * @return void + */ + + function extraHead() + { + } + + + /** + * Show page, a template method. + * + * @return nothing + */ + function showPage() + { + if (Event::handle('StartShowBody', array($this))) { + $this->showCore(); + Event::handle('EndShowBody', array($this)); + } + } + + /** + * Show core. + * + * Shows local navigation, content block and aside. + * + * @return nothing + */ + function showCore() + { + $this->elementStart('div', array('id' => 'core')); + if (Event::handle('StartShowContentBlock', array($this))) { + $this->showContentBlock(); + Event::handle('EndShowContentBlock', array($this)); + } + $this->elementEnd('div'); + } + + + + +} + diff --git a/actions/tag.php b/actions/tag.php index 02f3e3522..2202f9bb0 100644 --- a/actions/tag.php +++ b/actions/tag.php @@ -49,8 +49,18 @@ class TagAction extends Action { $pop = new PopularNoticeSection($this); $pop->show(); - } + $notice_tag = new Notice_tag; + $query = 'select file_id, count(file_id) as c from notice_tag join file_to_post on post_id = notice_id where tag="' . $notice_tag->escape($this->tag) . '" group by file_id order by c desc'; + $notice_tag->query($query); + $this->elementStart('ol'); + while ($notice_tag->fetch()) { + $this->elementStart('li'); + $this->element('a', array('class' => 'attachment', 'href' => common_local_url('attachment', array('attachment' => $notice_tag->file_id))), "Attachment tagged {$notice_tag->c} times"); + $this->elementEnd('li'); + } + $this->elementEnd('ol'); + } function title() { diff --git a/classes/File.php b/classes/File.php index 2ddc5deb8..e5913115b 100644 --- a/classes/File.php +++ b/classes/File.php @@ -54,6 +54,17 @@ class File extends Memcached_DataObject return 'http://www.facebook.com/login.php' === $url; } + function getAttachments($post_id) { + $query = "select file.* from file join file_to_post on (file_id = file.id) join notice on (post_id = notice.id) where post_id = " . $this->escape($post_id); + $this->query($query); + $att = array(); + while ($this->fetch()) { + $att[] = clone($this); + } + $this->free(); + return $att; + } + function saveNew($redir_data, $given_url) { $x = new File; $x->url = $given_url; diff --git a/classes/File_oembed.php b/classes/File_oembed.php index 2846f49db..f1b2cb13c 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -78,18 +78,9 @@ class File_oembed extends Memcached_DataObject 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'])) { - $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->insert(); + File_thumbnail::saveNew($data, $file_id); } - - - } } diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php index 7b906a07c..1a65b92c9 100644 --- a/classes/File_thumbnail.php +++ b/classes/File_thumbnail.php @@ -42,4 +42,14 @@ class File_thumbnail extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + 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->insert(); + } } + diff --git a/classes/Notice.php b/classes/Notice.php index c2fa2d19e..30508070e 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -124,8 +124,6 @@ class Notice extends Memcached_DataObject $profile = Profile::staticGet($profile_id); -// $final = common_shorten_links($content); - if (!$profile) { common_log(LOG_ERR, 'Problem saving notice. Unknown user.'); return _('Problem saving notice. Unknown user.'); @@ -279,6 +277,16 @@ class Notice extends Memcached_DataObject return true; } + function hasAttachments() { + $post = clone($this); + $query = "select count(file_id) as n_attachments from file join file_to_post on (file_id = file.id) join notice on (post_id = notice.id) where post_id = " . $post->escape($this->id); + $post->query($query); + $post->fetch(); + $n_attachments = intval($post->n_attachments); + $post->free(); + return $n_attachments; + } + function blowCaches($blowLast=false) { $this->blowSubsCache($blowLast); diff --git a/js/jquery.joverlay.min.js b/js/jquery.joverlay.min.js new file mode 100644 index 000000000..c9168506a --- /dev/null +++ b/js/jquery.joverlay.min.js @@ -0,0 +1,6 @@ +/* Copyright (c) 2009 Alvaro A. Lima Jr http://alvarojunior.com/jquery/joverlay.html + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * Version: 0.6 (Abr 23, 2009) + * Requires: jQuery 1.3+ + */ +(function($){var f=$.browser.msie&&$.browser.version==6.0;var g=null;$.fn.jOverlay=function(b){var b=$.extend({},$.fn.jOverlay.options,b);if(g!=null){clearTimeout(g)}var c=this.is('*')?this:'#jOverlayContent';var d=f?'absolute':'fixed';var e=b.imgLoading?"":'';$('body').prepend(e+"
"+"