]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.7.x' into 0.8.x
authorEvan Prodromou <evan@controlyourself.ca>
Mon, 8 Jun 2009 18:55:32 +0000 (11:55 -0700)
committerEvan Prodromou <evan@controlyourself.ca>
Mon, 8 Jun 2009 18:55:32 +0000 (11:55 -0700)
Conflicts:
classes/Notice.php
classes/Profile.php
lib/common.php
lib/util.php
scripts/getvaliddaemons.php
scripts/stopdaemons.sh

1  2 
README
actions/shownotice.php
classes/Notice.php
classes/Profile.php
classes/User.php
classes/User_group.php
db/notice_source.sql
lib/util.php
scripts/getvaliddaemons.php

diff --combined README
index 9cbe84f0e51bfc68244314c8ac39dc5530802e18,769c416d328e5eb27a939dd016b163eae212eb58..2099f94d62f46f953bd13716ef9b3abc0d98cd00
--- 1/README
--- 2/README
+++ b/README
@@@ -2,8 -2,8 +2,8 @@@
  README
  ------
  
- Laconica 0.7.3 ("You Are The Everything")
7 April 2009
+ Laconica 0.7.4 ("Can't Get There From Here")
29 May 2009
  
  This is the README file for Laconica, the Open Source microblogging
  platform. It includes installation instructions, descriptions of
@@@ -71,29 -71,29 +71,29 @@@ for additional terms
  New this version
  ================
  
- This is a minor bug-fix and feature release since version 0.7.2.1,
- released Mar 11 2009. Notable changes this version:
- - A plugin to allow a templating language for customization
- - A plugin for Piwik Analytics engine
- - A bookmarklet for posting a notice about a Web page you're reading
- - A welcome notice ('welcomebot') and default subscription for new users
- - Support for SSL for some or all pages on the site
- - Better handling of empty notice lists on many pages
- - Major improvements to the Twitter friend-sync offline processing
- subscribers, subscriptions, groups are listed on the Personal page.
- - "Invite" link restored to main menu
- - Better memory handling in FOAF output
- - Fix for SUP support (FriendFeed)
- - Correct and intelligent redirect HTTP status codes
- - Fix DB collations for search and sort
- - Better H1s and Titles using user full names
- - Fixes to make the linkback plugin operational
- - Better indication that a notice is being published by Ajax (spinner)
- - Better and unified Atom output
- - Hiding "register" and "join now" messages when site is closed
- - ping, twitter and facebook queuehandlers working better
- - Updated RPM spec
+ This is a minor bug-fix and feature release since version 0.7.3,
+ released Apr 4 2009. Notable changes this version:
+ - Improved handling of UTF-8 characters. The new code is *not* backwards
+   compatible by default; see "Upgrading" below for instructions on
+   converting existing databases to the correct character set.
+ - Unroll joins for large queries. This greatly enhanced database
+   performance -- up to 50x for some queries -- at the expense of making
+   an extra DB hit for some queries.
+ - Added an optional plugin to use WikiHashtags
  (http://hashtags.wikia.com/) for the sidebar on hashtag pages.
+ - Optimized Twitter friend synchronization.
+ - Better error handling for Ajax posting of notices, including
+   HTTP errors and timeouts.
+ - Experimental Comet plugin -- supports the cometd and the Bayeux
+   protocol. Using this plugin, you can show "real time" updates on the
+   public and tag pages. However, server configuration is complex.
+ - If queues are enabled, update inboxes and memcached off-line. Speeds
+   up posting considerably.
+ - Correctly shorten links posted through XMPP.
+ - <link> elements for pagination, supported by some browsers like Opera.
+ - Corrected date format in search API.
+ - Made the public XRDS file work correctly.
  
  Prerequisites
  =============
@@@ -176,10 -176,6 +176,10 @@@ and the URLs are listed here for your c
    version may render your Laconica site unable to send or receive XMPP
    messages.
  - Facebook library. Used for the Facebook application.
 +- PEAR Services_oEmbed. Used for some multimedia integration.
 +- PEAR HTTP_Request is an oEmbed dependency.
 +- PEAR Validat is an oEmbed dependency.e
 +- PEAR Net_URL is an oEmbed dependency.2
  
  A design goal of Laconica is that the basic Web functionality should
  work on even the most restrictive commercial hosting services.
@@@ -197,9 -193,9 +197,9 @@@ especially if you've previously install
  1. Unpack the tarball you downloaded on your Web server. Usually a
     command like this will work:
  
-          tar zxf laconica-0.7.3.tar.gz
+          tar zxf laconica-0.7.4.tar.gz
  
-    ...which will make a laconica-0.7.3 subdirectory in your current
+    ...which will make a laconica-0.7.4 subdirectory in your current
     directory. (If you don't have shell access on your Web server, you
     may have to unpack the tarball on your local computer and FTP the
     files to the server.)
  2. Move the tarball to a directory of your choosing in your Web root
     directory. Usually something like this will work:
  
-          mv laconica-0.7.3 /var/www/mublog
+          mv laconica-0.7.4 /var/www/mublog
  
     This will make your Laconica instance available in the mublog path of
     your server, like "http://example.net/mublog". "microblog" or
@@@ -704,7 -700,7 +704,7 @@@ with this situation
  If you've been using Laconica 0.6, 0.5 or lower, or if you've been
  tracking the "git" version of the software, you will probably want
  to upgrade and keep your existing data. There is no automated upgrade
- procedure in Laconica 0.7.3. Try these step-by-step instructions; read
+ procedure in Laconica 0.7.4. Try these step-by-step instructions; read
  to the end first before trying them.
  
  0. Download Laconica and set up all the prerequisites as if you were
@@@ -1170,63 -1166,6 +1170,63 @@@ welcome: nickname of a user account tha
  If either of these special user accounts are specified, the users should
  be created before the configuration is updated.
  
 +snapshot
 +--------
 +
 +The software will, by default, send statistical snapshots about the
 +local installation to a stats server on the laconi.ca Web site. This
 +data is used by the developers to prioritize development decisions. No
 +identifying data about users or organizations is collected. The data
 +is available to the public for review. Participating in this survey
 +helps Laconica developers take your needs into account when updating
 +the software.
 +
 +run: string indicating when to run the statistics. Values can be 'web'
 +     (run occasionally at Web time), 'cron' (run from a cron script),
 +     or 'never' (don't ever run). If you set it to 'cron', remember to
 +     schedule the script to run on a regular basis.
 +frequency: if run value is 'web', how often to report statistics.
 +           Measured in Web hits; depends on how active your site is.
 +           Default is 10000 -- that is, one report every 10000 Web hits,
 +           on average.
 +reporturl: URL to post statistics to. Defaults to Laconica developers'
 +           report system, but if they go evil or disappear you may
 +           need to update this to another value. Note: if you
 +           don't want to report stats, it's much better to
 +           set 'run' to 'never' than to set this value to something
 +           nonsensical.
 +
 +
 +attachments
 +-----------
 +
 +The software lets users upload files with their notices. You can configure
 +the types of accepted files by mime types and a trio of quota options:
 +per file, per user (total), per user per month.
 +
 +We suggest the use of the pecl file_info extension to handle mime type
 +detection.
 +
 +supported: an array of mime types you accept to store and distribute,
 +           like 'image/gif', 'video/mpeg', 'audio/mpeg', etc. Make sure you
 +           setup your server to properly reckognize the types you want to
 +           support.
 +
 +For quotas, be sure you've set the upload_max_filesize and post_max_size
 +in php.ini to be large enough to handle your upload. In httpd.conf
 +(if you're using apache), check that the LimitRequestBody directive isn't
 +set too low (it's optional, so it may not be there at all).
 +
 +file_quota: maximum size for a single file upload in bytes. A user can send
 +            any amount of notices with attachments as long as each attachment
 +            is smaller than file_quota.
 +user_quota: total size in bytes a user can store on this server. Each user
 +            can store any number of files as long as their total size does
 +            not exceed the user_quota.
 +monthly_quota: total size permitted in the current month. This is the total
 +            size in bytes that a user can upload each month.
 +
 +
  Troubleshooting
  ===============
  
@@@ -1239,7 -1178,7 +1239,7 @@@ repository (see below), and you get a c
  T_STRING") in the browser, check to see that you don't have any
  conflicts in your code.
  
- If you upgraded to Laconica 0.7.3 without reading the "Notice inboxes"
+ If you upgraded to Laconica 0.7.4 without reading the "Notice inboxes"
  section above, and all your users' 'Personal' tabs are empty, read the
  "Notice inboxes" section above.
  
@@@ -1330,6 -1269,8 +1330,8 @@@ if anyone's been overlooked in error
  * Ken Sedgwick
  * Brian Hendrickson
  * Tobias Diekershoff
+ * Dan Moore
+ * Fil
  
  Thanks also to the developers of our upstream library code and to the
  thousands of people who have tried out Identi.ca, installed Laconi.ca,
diff --combined actions/shownotice.php
index 34c8a8e9464c68e21c47a45ca52467b6b8b3b3d5,1be1e2414c9cc0d10986c3f89985af07a47d674d..b0d973a991cad8e61cfa2d8ce632013e89f3201b
@@@ -122,7 -122,7 +122,7 @@@ class ShownoticeAction extends Actio
  
      function lastModified()
      {
-         return max(strtotime($this->notice->created),
+         return max(strtotime($this->notice->modified),
                     strtotime($this->profile->modified),
                     ($this->avatar) ? strtotime($this->avatar->modified) : 0);
      }
  
      function showContent()
      {
 -        $this->elementStart('ul', array('class' => 'notices'));
 +        $this->elementStart('ol', array('class' => 'notices xoxo'));
          $nli = new NoticeListItem($this->notice, $this);
          $nli->show();
 -        $this->elementEnd('ul');
 +        $this->elementEnd('ol');
      }
  
      /**
diff --combined classes/Notice.php
index 1b5c0ab0a55afdc53592b860ec71ca414fa615a5,b4c86ebeb5ba093366230d1d35d00b84e69b91eb..ca67306df1a7d7d025a57c24e8eb9923e5b56b23
@@@ -46,7 -46,6 +46,7 @@@ class Notice extends Memcached_DataObje
      public $reply_to;                        // int(4)
      public $is_local;                        // tinyint(1)
      public $source;                          // varchar(32)
 +    public $conversation;                    // int(4)
  
      /* Static get */
      function staticGet($k,$v=NULL) {
  
          $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.');
              return _('Too many notices too fast; take a breather and post again in a few minutes.');
          }
  
-         if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $content)) {
+         if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $final)) {
              common_log(LOG_WARNING, 'Dupe posting by profile #' . $profile_id . '; throttled.');
                        return _('Too many duplicate messages too quickly; take a breather and post again in a few minutes.');
          }
  
                $notice->reply_to = $reply_to;
                $notice->created = common_sql_now();
-               $notice->content = $content;
-               $notice->rendered = common_render_content($content, $notice);
+               $notice->content = $final;
+               $notice->rendered = common_render_content($final, $notice);
                $notice->source = $source;
                $notice->uri = $uri;
  
 +        if (!empty($reply_to)) {
 +            $reply_notice = Notice::staticGet('id', $reply_to);
 +            if (!empty($reply_notice)) {
 +                $notice->reply_to = $reply_to;
 +                $notice->conversation = $reply_notice->conversation;
 +            }
 +        }
 +
          if (Event::handle('StartNoticeSave', array(&$notice))) {
  
              $id = $notice->insert();
  
              $notice->saveReplies();
              $notice->saveTags();
-             $notice->saveGroups();
  
-             if (common_config('queue', 'enabled')) {
-                 $notice->addToAuthorInbox();
-             } else {
-                 $notice->addToInboxes();
-             }
+             $notice->addToInboxes();
+             $notice->saveGroups();
  
              $notice->query('COMMIT');
  
          # Clear the cache for subscribed users, so they'll update at next request
          # XXX: someone clever could prepend instead of clearing the cache
  
-         if (common_config('memcached', 'enabled')) {
-             if (common_config('queue', 'enabled')) {
-                 $notice->blowAuthorCaches();
-             } else {
-                 $notice->blowCaches();
-             }
-         }
+         $notice->blowCaches();
  
          return $notice;
      }
          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($post->id);
 +        $post->query($query);
 +        $post->fetch();
 +        $n_attachments = intval($post->n_attachments);
 +        $post->free();
 +        return $n_attachments;
 +    }
 +
      function blowCaches($blowLast=false)
      {
          $this->blowSubsCache($blowLast);
          $this->blowGroupCache($blowLast);
      }
  
-     function blowAuthorCaches($blowLast=false)
-     {
-         // Clear the user's cache
-         $cache = common_memcache();
-         if (!empty($cache)) {
-             $cache->delete(common_cache_key('notice_inbox:by_user:'.$this->profile_id));
-         }
-         $this->blowNoticeCache($blowLast);
-         $this->blowPublicCache($blowLast);
-     }
      function blowGroupCache($blowLast=false)
      {
          $cache = common_memcache();
      # XXX: too many args; we need to move to named params or even a separate
      # class for notice streams
  
-     static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $order=null, $since=null) {
+     static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $order=null, $since=null) {
  
          if (common_config('memcached', 'enabled')) {
  
-             # Skip the cache if this is a since, since_id or before_id qry
-             if ($since_id > 0 || $before_id > 0 || $since) {
-                 return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since);
+             # Skip the cache if this is a since, since_id or max_id qry
+             if ($since_id > 0 || $max_id > 0 || $since) {
+                 return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since);
              } else {
                  return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order);
              }
          }
  
-         return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since);
+         return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since);
      }
  
-     static function getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since) {
+     static function getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since) {
  
          $needAnd = false;
          $needWhere = true;
              $qry .= ' notice.id > ' . $since_id;
          }
  
-         if ($before_id > 0) {
+         if ($max_id > 0) {
  
              if ($needWhere) {
                  $qry .= ' WHERE ';
                  $qry .= ' AND ';
              }
  
-             $qry .= ' notice.id < ' . $before_id;
+             $qry .= ' notice.id <= ' . $max_id;
          }
  
          if ($since) {
          }
      }
  
-     function publicStream($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null)
+     function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
      {
          $ids = Notice::stream(array('Notice', '_publicStreamDirect'),
                                array(),
                                'public',
-                               $offset, $limit, $since_id, $before_id, $since);
+                               $offset, $limit, $since_id, $max_id, $since);
  
          return Notice::getStreamByIds($ids);
      }
  
-     function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null)
+     function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
      {
          $notice = new Notice();
  
              $notice->whereAdd('id > ' . $since_id);
          }
  
-         if ($before_id != 0) {
-             $notice->whereAdd('id < ' . $before_id);
+         if ($max_id != 0) {
+             $notice->whereAdd('id <= ' . $max_id);
          }
  
          if (!is_null($since)) {
          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');
                  if ($recipient_notice) {
                      $orig = clone($this);
                      $this->reply_to = $recipient_notice->id;
 +                    $this->conversation = $recipient_notice->conversation;
                      $this->update($orig);
                  }
              }
              }
          }
  
 +        // If it's not a reply, make it the root of a new conversation
 +
 +        if (empty($this->conversation)) {
 +            $orig = clone($this);
 +            $this->conversation = $this->id;
 +            $this->update($orig);
 +        }
 +
          foreach (array_keys($replied) as $recipient) {
              $user = User::staticGet('id', $recipient);
              if ($user) {
          }
      }
  
-     function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $since=null, $tag=null)
+     function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
      {
          $cache = common_memcache();
  
          if (empty($cache) ||
-             $since_id != 0 || $before_id != 0 || !is_null($since) ||
+             $since_id != 0 || $max_id != 0 || (!is_null($since) && $since > 0) ||
              ($offset + $limit) > NOTICE_CACHE_WINDOW) {
              return call_user_func_array($fn, array_merge($args, array($offset, $limit, $since_id,
-                                                                       $before_id, $since, $tag)));
+                                                                       $max_id, $since)));
          }
  
          $idkey = common_cache_key($cachekey);
              $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)));
 +                                                                          $last_id, 0, null, $tag)));
  
              $new_window = array_merge($new_ids, $window);
  
          }
  
          $window = call_user_func_array($fn, array_merge($args, array(0, NOTICE_CACHE_WINDOW,
 -                                                                     0, 0, null)));
 +                                                                     0, 0, null, $tag)));
  
          $windowstr = implode(',', $window);
  
diff --combined classes/Profile.php
index afc0ea4f74557b29824ef266a54be6fd59d81202,3d13cd46af0146f946457200656e6c529a6efef9..4a459b9740e4055b5a62c9349ba689b9dc9598fd
@@@ -153,66 -153,18 +153,66 @@@ class Profile extends Memcached_DataObj
          return null;
      }
  
 -    function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
 +    function getTaggedNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null, $tag=null)
 +    {
 +        // XXX: I'm not sure this is going to be any faster. It probably isn't.
 +        $ids = Notice::stream(array($this, '_streamTaggedDirect'),
 +                              array(),
 +                              'profile:notice_ids:' . $this->id,
 +                              $offset, $limit, $since_id, $before_id, $since, $tag);
 +        common_debug(print_r($ids, true));
 +        return Notice::getStreamByIds($ids);
 +    }
 +
 +    function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
      {
          // 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, $since);
 -                              $offset, $limit, $since_id, $max_id);
++                              $offset, $limit, $since_id, $max_id, $since);
  
          return Notice::getStreamByIds($ids);
      }
  
 -    function _streamDirect($offset, $limit, $since_id, $max_id, $since)
 +    function _streamTaggedDirect($offset, $limit, $since_id, $before_id, $since=null, $tag=null)
 +    {
 +        common_debug('_streamTaggedDirect()');
 +        $notice = new Notice();
 +        $notice->profile_id = $this->id;
 +        $query = "select id from notice join notice_tag on id=notice_id where tag='" . $notice->escape($tag) . "' and profile_id=" . $notice->escape($notice->profile_id);
 +        if ($since_id != 0) {
 +            $query .= " and id > $since_id";
 +        }
 +
 +        if ($before_id != 0) {
 +            $query .= " and id < $before_id";
 +        }
 +
 +        if (!is_null($since)) {
 +            $query .= " and created > '" . date('Y-m-d H:i:s', $since) . "'";
 +        }
 +
 +        $query .= ' order by id DESC';
 +
 +        if (!is_null($offset)) {
 +            $query .= " limit $offset, $limit";
 +        }
 +        $notice->query($query);
 +        $ids = array();
 +
 +        while ($notice->fetch()) {
 +            common_debug(print_r($notice, true));
 +            $ids[] = $notice->id;
 +        }
 +
 +        return $ids;
 +    }
 +
 +
 +
 +
 +    function _streamDirect($offset, $limit, $since_id, $before_id, $since = null)
      {
          $notice = new Notice();
  
              $notice->whereAdd('id > ' . $since_id);
          }
  
-         if ($before_id != 0) {
-             $notice->whereAdd('id < ' . $before_id);
+         if ($max_id != 0) {
+             $notice->whereAdd('id <= ' . $max_id);
          }
  
          if (!is_null($since)) {
diff --combined classes/User.php
index ea8ba40817ac74c3901c9f8f519deb677f616339,59451258ec881c44e6fad7886829132afc85e114..08a166d5ae4a4478be5943cd69eee776834e54ec
@@@ -403,26 -403,16 +403,25 @@@ class User extends Memcached_DataObjec
      function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
      {
          $ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id, $since);
-         common_debug("Ids = " . implode(',', $ids));
          return Notice::getStreamByIds($ids);
      }
  
 +    function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) {
 +        $profile = $this->getProfile();
 +        if (!$profile) {
 +            return null;
 +        } else {
 +            return $profile->getTaggedNotices($tag, $offset, $limit, $since_id, $before_id, $since);
 +        }
 +    }
 +
      function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
      {
          $profile = $this->getProfile();
          if (!$profile) {
              return null;
          } else {
 -            return $profile->getNotices($offset, $limit, $since_id, $before_id);
 +            return $profile->getNotices($offset, $limit, $since_id, $before_id, $since);
          }
      }
  
diff --combined classes/User_group.php
index 7cc31e7026d0aeb693d481c1a7c820b3807f17dc,a135015baca3a1f2d67601ebb9ae6f03d4c5dd0f..a135015baca3a1f2d67601ebb9ae6f03d4c5dd0f
mode 100644,100755..100644
@@@ -58,7 -58,7 +58,7 @@@ class User_group extends Memcached_Data
          return Notice::getStreamByIds($ids);
      }
  
-     function _streamDirect($offset, $limit, $since_id, $before_id, $since)
+     function _streamDirect($offset, $limit, $since_id, $max_id, $since)
      {
          $inbox = new Group_inbox();
  
@@@ -71,8 -71,8 +71,8 @@@
              $inbox->whereAdd('notice_id > ' . $since_id);
          }
  
-         if ($before_id != 0) {
-             $inbox->whereAdd('notice_id < ' . $before_id);
+         if ($max_id != 0) {
+             $inbox->whereAdd('notice_id <= ' . $max_id);
          }
  
          if (!is_null($since)) {
diff --combined db/notice_source.sql
index d5a280b82cedd18f9ba0df4107b7277c18a4394c,4c3dc2113d2392131a87dec7544a86fac71a53a2..983ea915023ded6aeaae99a9049cfde4ec29b05e
@@@ -2,7 -2,9 +2,9 @@@ INSERT INTO notice_sourc
      (code, name, url, created)
  VALUES
      ('adium', 'Adium', 'http://www.adiumx.com/', now()),
+     ('Afficheur', 'Afficheur', 'http://afficheur.sourceforge.jp/', now()),
      ('AgentSolo.com','AgentSolo.com','http://www.agentsolo.com/', now()),
+     ('anyio', 'Any.IO', 'http://any.io/', now()),
      ('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', now()),
      ('bti','bti','http://gregkh.github.com/bti/', now()),
      ('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()),
@@@ -11,6 -13,7 +13,7 @@@
      ('eventbox','EventBox','http://thecosmicmachine.com/eventbox/ ', now()),
      ('Facebook','Facebook','http://apps.facebook.com/identica/', now()),
      ('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', now()),
+     ('get2gnow', 'get2gnow', 'http://uberchicgeekchick.com/?projects=get2gnow', now()),
      ('gravity', 'Gravity', 'http://mobileways.de/gravity', now()),
      ('Gwibber','Gwibber','http://launchpad.net/gwibber', now()),
      ('HelloTxt','HelloTxt','http://hellotxt.com/', now()),
@@@ -48,7 -51,6 +51,7 @@@
      ('twidge','Twidge','http://software.complete.org/twidge', now()),
      ('twidroid','twidroid','http://www.twidroid.com/', now()),
      ('twittelator','Twittelator','http://www.stone.com/iPhone/Twittelator/', now()),
 +    ('twitter','Twitter','http://twitter.com/', now()),
      ('twitterfeed','twitterfeed','http://twitterfeed.com/', now()),
      ('twitterphoto','TwitterPhoto','http://richfish.org/twitterphoto/', now()),
      ('twitterpm','Net::Twitter','http://search.cpan.org/dist/Net-Twitter/', now()),
diff --combined lib/util.php
index ab12723098b1b706354257b9f9b4ceba925f15ec,22308f432c79324f0aa71586c7ce86ee891d2de8..9872d97c4895f8f66cbae97ae4bf565cf817623d
@@@ -395,7 -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 = '#'.
      '(?:'.
          $url = (mb_strpos($orig_url, htmlspecialchars($url)) === FALSE) ? $url:htmlspecialchars($url);
  
          // Call user specified func
 -        $modified_url = call_user_func($callback, $url);
 +        if (empty($notice_id)) {
 +            $modified_url = call_user_func($callback, $url);
 +        } else {
 +            $modified_url = call_user_func($callback, array($url, $notice_id));
 +        }
  
          // Replace it!
          $start = mb_strpos($text, $url, $offset);
@@@ -485,50 -481,107 +485,50 @@@ 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');
 -
 -    if ($longurl = common_longurl($url)) {
 -        $attrs['title'] = $longurl;
 +    $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');
      }
  
 +    $attrs = array('href' => $longurl, 'rel' => 'external');
 +
 +// if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
 +// where ID is the id of the attachment for the given URL.
 +//
 +// we need a better test telling what can be shown as an attachment
 +// we're currently picking up oembeds only.
 +// I think the best option is another file_view table in the db
 +// and associated dbobject.
 +    $query = "select file_oembed.file_id as file_id from file join file_oembed on file.id = file_oembed.file_id where file.url='$longurl'";
 +    $file = new File;
 +    $file->query($query);
 +    $file->fetch();
 +
 +    if (!empty($file->file_id)) {
 +        $query = "select file_thumbnail.file_id as file_id from file join file_thumbnail on file.id = file_thumbnail.file_id where file.url='$longurl'";
 +        $file2 = new File;
 +        $file2->query($query);
 +        $file2->fetch();
 +
 +        if (empty($file2->file_id)) {
 +            $attrs['class'] = 'attachment';
 +        } else {
 +            $attrs['class'] = 'attachment thumbnail';
 +        }
 +        $attrs['id'] = "attachment-{$file->file_id}";
 +    }
      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']);
 -}
 -
  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)
@@@ -826,80 -879,47 +826,108 @@@ function common_broadcast_notice($notic
  
  function common_enqueue_notice($notice)
  {
 -    $transports = array('twitter', 'facebook', 'ping');
 -
 -    // If inboxes are enabled, wait till inboxes are filled
 -    // before doing inbox-dependent broadcasts
 -
 -    $transports = array_merge($transports, common_post_inbox_transports());
 -
 -    foreach ($transports as $transport) {
 -        common_enqueue_notice_transport($notice, $transport);
 +    if (common_config('queue','subsystem') == 'stomp') {
 +      // use an external message queue system via STOMP
 +      require_once("Stomp.php");
 +      $con = new Stomp(common_config('queue','stomp_server'));
 +      if (!$con->connect()) {
 +              common_log(LOG_ERR, 'Failed to connect to queue server');
 +              return false;
 +      }
 +      $queue_basename = common_config('queue','queue_basename');
 +      foreach (array('jabber', 'omb', 'sms', 'public', 'twitter', 'facebook', 'ping') as $transport) {
 +              if (!$con->send(
 +                      '/queue/'.$queue_basename.'-'.$transport, // QUEUE
 +                      $notice->id,            // BODY of the message
 +                      array (                 // HEADERS of the msg
 +                      'created' => $notice->created
 +                      ))) {
 +                      common_log(LOG_ERR, 'Error sending to '.$transport.' queue');
 +                      return false;
 +              }
 +        common_log(LOG_DEBUG, 'complete remote queueing notice ID = ' . $notice->id . ' for ' . $transport);
 +      }
 +
 +      //send tags as headers, so they can be used as JMS selectors
 +        common_log(LOG_DEBUG, 'searching for tags ' . $notice->id);
 +        $tags = array();
 +      $tag = new Notice_tag();
 +        $tag->notice_id = $notice->id;
 +        if ($tag->find()) {
 +            while ($tag->fetch()) {
 +              common_log(LOG_DEBUG, 'tag found = ' . $tag->tag);
 +              array_push($tags,$tag->tag);
 +            }
 +        }
 +        $tag->free();
 +
 +      $con->send('/topic/laconica.'.$notice->profile_id,
 +                      $notice->content,
 +                      array(
 +                              'profile_id' => $notice->profile_id,
 +                              'created' => $notice->created,
 +                              'tags' => implode($tags,' - ')
 +                              )
 +                      );
 +        common_log(LOG_DEBUG, 'sent to personal topic ' . $notice->id);
 +      $con->send('/topic/laconica.allusers',
 +                      $notice->content,
 +                      array(
 +                              'profile_id' => $notice->profile_id,
 +                              'created' => $notice->created,
 +                              'tags' => implode($tags,' - ')
 +                              )
 +                      );
 +        common_log(LOG_DEBUG, 'sent to catch-all topic ' . $notice->id);
 +      $result = true;
 +    }
 +    else {
 +      // in any other case, 'internal'
 +      foreach (array('jabber', 'omb', 'sms', 'public', 'twitter', 'facebook', 'ping') as $transport) {
 +              $qi = new Queue_item();
 +              $qi->notice_id = $notice->id;
 +              $qi->transport = $transport;
 +              $qi->created = $notice->created;
 +              $result = $qi->insert();
 +              if (!$result) {
 +                  $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
 +                  common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message);
 +                  return false;
 +              }
 +              common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport);
 +        }
      }
 -
      return $result;
  }
  
+ function common_post_inbox_transports()
+ {
+     $transports = array('omb', 'sms');
+     if (common_config('xmpp', 'enabled')) {
+         $transports = array_merge($transports, array('jabber', 'public'));
+     }
+     return $transports;
+ }
+ function common_enqueue_notice_transport($notice, $transport)
+ {
+     $qi = new Queue_item();
+     $qi->notice_id = $notice->id;
+     $qi->transport = $transport;
+     $qi->created = $notice->created;
+     $result = $qi->insert();
+     if (!$result) {
+         $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
+         common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message);
+         throw new ServerException('DB error inserting queue item: ' . $last_error->message);
++>>>>>>> 0.7.x:lib/util.php
+     }
+     common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport);
+     return true;
+ }
  function common_real_broadcast($notice, $remote=false)
  {
      $success = true;
@@@ -1104,7 -1124,7 +1132,7 @@@ function common_accept_to_prefs($accept
  
      foreach($parts as $part) {
          // FIXME: doesn't deal with params like 'text/html; level=1'
 -        @list($value, $qpart) = explode(';', $part);
 +        @list($value, $qpart) = explode(';', trim($part));
          $match = array();
          if(!isset($qpart)) {
              $prefs[$value] = 1;
index a10233e69fcc2ce453a969118eccd867c37c63f7,0996ba9f415da150bc3b076c1c44b929d76d762a..4e49f9bd4bc776e1ef7b5928dd3deab39972ac89
@@@ -25,7 -25,6 +25,6 @@@
   * daemon names.
   */
  
  # 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";
@@@ -41,15 -40,8 +40,14 @@@ if(common_config('xmpp','enabled')) 
      echo "xmppdaemon.php jabberqueuehandler.php publicqueuehandler.php ";
      echo "xmppconfirmhandler.php ";
  }
 +if(common_config('memcached','enabled')) {
 +    echo "memcachedqueuehandler.php ";
 +}
 +if(common_config('twitterbridge','enabled')) {
 +    echo "twitterstatusfetcher.php ";
 +}
  echo "ombqueuehandler.php ";
  echo "twitterqueuehandler.php ";
  echo "facebookqueuehandler.php ";
  echo "pingqueuehandler.php ";
- echo "inboxqueuehandler.php ";
  echo "smsqueuehandler.php ";