]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.8.x' of git://gitorious.org/laconica/dev into 0.8.x
authorFederico Marani <federico.marani@ymail.com>
Mon, 16 Mar 2009 22:43:51 +0000 (22:43 +0000)
committerFederico Marani <federico.marani@ymail.com>
Mon, 16 Mar 2009 22:43:51 +0000 (22:43 +0000)
70 files changed:
README
actions/all.php
actions/allrss.php
actions/api.php
actions/favorited.php
actions/finishopenidlogin.php
actions/finishremotesubscribe.php
actions/grouplogo.php
actions/joingroup.php
actions/leavegroup.php
actions/noticesearchrss.php
actions/peopletag.php
actions/postnotice.php
actions/publicrss.php
actions/remotesubscribe.php
actions/showgroup.php
actions/sup.php
actions/twitapidirect_messages.php
actions/twitapifavorites.php
actions/twitapisearchatom.php [new file with mode: 0644]
actions/twitapisearchjson.php
actions/twitapistatuses.php
actions/twittersettings.php
actions/updateprofile.php
actions/userauthorization.php
actions/userrss.php
bin/flowplayer-3.0.5.swf [deleted file]
bin/flowplayer.audio-3.0.3.swf [deleted file]
bin/flowplayer.controls-3.0.3.swf [deleted file]
classes/Nonce.php
classes/Notice.php
classes/User.php
classes/laconica.ini
config.php.sample
db/laconica.sql
db/laconica_pg.sql
extlib/OAuth.php
extlib/PEAR/Exception.php [new file with mode: 0644]
index.php
js/flowplayer-3.0.5.min.js [deleted file]
js/jquery.simplemodal-1.2.2.pack.js [deleted file]
js/video.js [deleted file]
lib/action.php
lib/common.php
lib/jabber.php
lib/jsonsearchresultslist.php
lib/mail.php
lib/oauthstore.php
lib/omb.php
lib/openid.php
lib/profilelist.php
lib/router.php
lib/searchaction.php
lib/subgroupnav.php
lib/topposterssection.php
lib/twitter.php
lib/twitterapi.php
lib/util.php
local/.gitignore [new file with mode: 0644]
locale/laconica.po
plugins/LinkbackPlugin.php [new file with mode: 0644]
scripts/SearchMonkey-MrH.0.txt [new file with mode: 0644]
scripts/SearchMonkey-Om3.0.txt
scripts/SearchMonkey-yQP.0.txt
scripts/rebuilddb_psql.sh [new file with mode: 0755]
scripts/stopdaemons.sh
scripts/update_pot.sh
theme/base/css/modal.css [deleted file]
theme/base/css/modal_ie.css [deleted file]
theme/base/images/x.png [deleted file]

diff --git a/README b/README
index d3c80df458287d3589f6870eeb50d56ae4b83d57..cdb3feba6fdb50568eb5bb40a3fc108dbe68fbb8 100644 (file)
--- a/README
+++ b/README
@@ -2,8 +2,8 @@
 README
 ------
 
-Laconica 0.7.1 ("West of the Fields")
-6 February 2009
+Laconica 0.7.2 ("Talk about the Passion")
+11 March 2009
 
 This is the README file for Laconica, the Open Source microblogging
 platform. It includes installation instructions, descriptions of
@@ -71,8 +71,47 @@ for additional terms.
 New this version
 ================
 
-This is a minor bug-fix release since version 0.7.0, released Jan 29
-2009. Notable changes this version:
+This is a minor bug-fix and feature release since version 0.7.1,
+released Feb 9 2009. Notable changes this version:
+
+- First version of a web-based installer
+- Use Net_URL_Mapper instead of mod_rewrite to map "fancy URLs",
+  for a much simpler installation and use of PATH_INFO on sites
+  that don't have mod_rewrite.
+- A plugin framework for system events, to make it easier to build
+  server-side plugins.
+- A plugin for Google Analytics
+- A plugin to use blogspam.net to check notices for spam
+- A plugin to send linkbacks for notices about blog posts
+- Configurable check for duplicate notices in a specific time
+  period
+- Better Atom feeds
+- First implementation of Twitter Search API
+- Add streamlined mobile device-friendly styles when enabled in config.
+- A queue server for sending notices to Twitter
+- A queue server for sending notices to Facebook
+- A queue server for sending notices to a ping server
+- Fixed a bug in nonces for OAuth in OpenMicroBlogging
+- Fixed bugs in transfer of avatars in OpenMicroBlogging
+- @-links go to permalinks for local users
+- Better handling of DB errors (instead of dreaded DB_DataObject blank
+  screen)
+- Initial version of an RPM spec file
+- More consistent display of notices in notice search
+- A stylesheet for printed output
+- "Social graph" methods for Twitter API
+- Documentation for the JavaScript badge
+- Debugged a ton of problems that happened with E_NOTICE on
+- Better caching in RSS feeds
+- Optionally send email when an @-message is received
+- Automatically add tags for every group message
+- Add framebusting JavaScript to help avoid clickjacking attacks.
+- Optionally ignore some notice sources for public page.
+- Add default SMS carriers and notice sources to distribution file.
+- Change titles to use mixed case instead of all uppercase.
+- Use exceptions for error handling.
+
+Changes in version 0.7.1:
 
 - Vast improvement in auto-linking to URLs.
 - Link to group search from user's group page
@@ -749,16 +788,19 @@ to the end first before trying them.
    directory to your new directory.
 9. Copy htaccess.sample to .htaccess in the new directory. Change the
    RewriteBase to use the correct path.
-10. Rebuild the database. Go to your Laconica directory and run the
-   rebuilddb.sh script like this:
-
-   ./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql
-
-   Here, rootuser and rootpassword are the username and password for a
-   user who can drop and create databases as well as tables; typically
-   that's _not_ the user Laconica runs as.
-11. Use mysql client to log into your database and make sure that the
-    notice, user, profile, subscription etc. tables are non-empty.
+10. Rebuild the database. For MySQL, go to your Laconica directory and
+    run the rebuilddb.sh script like this:
+
+    ./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql
+
+    Here, rootuser and rootpassword are the username and password for a
+    user who can drop and create databases as well as tables; typically
+    that's _not_ the user Laconica runs as.
+    For PostgreSQL databases there is an equivalent, rebuilddb_psql.sh,
+    which operates slightly differently. Read the documentation in that
+    script before running it.
+11. Use mysql or psql client to log into your database and make sure that
+    the notice, user, profile, subscription etc. tables are non-empty.
 12. Turn back on the Web server, and check that things still work.
 13. Turn back on XMPP bots and email maildaemon. Note that the XMPP
     bots have changed since version 0.5; see above for details.
@@ -883,6 +925,10 @@ notice: A plain string that will appear on every page. A good place
        to put introductory information about your service, or info about
        upgrades and outages, or other community info. Any HTML will
         be escaped.
+dupelimit: Time in which it's not OK for the same person to post the
+           same notice; default = 60 seconds.
+logo: URL of an image file to use as the logo for the site. Overrides
+      the logo in the theme, if any.
 
 db
 --
@@ -1225,6 +1271,9 @@ if anyone's been overlooked in error.
 * Ken Sheppardson (Trac server, man-about-town)
 * Tiago 'gouki' Faria (i18n managerx)
 * Sean Murphy
+* Leslie Michael Orchard
+* Eric Helgeson
+* Ken Sedgwick
 
 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,
index 08dcccbddb711c12ff7578e165d99c72b1068545..8e67ec0f3b49eff9db6b6b7020762b952f9843da 100644 (file)
@@ -77,12 +77,12 @@ class AllAction extends Action
                               sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
                      new Feed(Feed::RSS2,
                               common_local_url('api', array('apiaction' => 'statuses',
-                                                            'method' => 'friends',
+                                                            'method' => 'friends_timeline',
                                                             'argument' => $this->user->nickname.'.rss')),
                               sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
                      new Feed(Feed::ATOM,
                               common_local_url('api', array('apiaction' => 'statuses',
-                                                            'method' => 'friends',
+                                                            'method' => 'friends_timeline',
                                                             'argument' => $this->user->nickname.'.atom')),
                               sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname)));
     }
index 0114c43962ad312075337ef347010fada0c25a1b..45f3946a61cfda010765ae3b54ca94360dc4d0a7 100644 (file)
@@ -81,8 +81,9 @@ class AllrssAction extends Rss10Action
      */
     function getNotices($limit=0)
     {
-        $user   = $this->user;
-        $notice = $user->noticesWithFriends(0, $limit);
+        $user    = $this->user;
+        $notice  = $user->noticesWithFriends(0, $limit);
+        $notices = array();
 
         while ($notice->fetch()) {
             $notices[] = clone($notice);
index a27d244929f032468cc5ae0e4373a936a8d47c37..c18d551b6d487151286e011c65f1f534ee8afd2c 100644 (file)
@@ -127,7 +127,9 @@ class ApiAction extends Action
                                 'laconica/wadl');
 
         static $bareauth = array('statuses/user_timeline',
+                                 'statuses/friends_timeline',
                                  'statuses/friends',
+                                 'statuses/replies',
                                  'statuses/followers',
                                  'favorites/favorites');
 
index 5082f4a4eb3e5ae258c67804516924c73907c686..231b97897377b9026b6104fb904bd5f211830d08 100644 (file)
@@ -178,7 +178,7 @@ class FavoritedAction extends Action
         $qry = 'SELECT notice.*, '.
           $weightexpr . ' as weight ' .
           'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
-          'GROUP BY fave.notice_id ' .
+          'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source ' .
           'ORDER BY weight DESC';
 
         $offset = ($this->page - 1) * NOTICES_PER_PAGE;
index 6d92cb9aaecb787edcf2f26ac7a8f8d4a13e2005..52d9be29c1e650d202dbf7d9c2645b6a7358f991 100644 (file)
@@ -62,9 +62,8 @@ class FinishopenidloginAction extends Action
         if ($this->error) {
             $this->element('div', array('class' => 'error'), $this->error);
         } else {
-            global $config;
             $this->element('div', 'instructions',
-                           sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), $config['site']['name']));
+                           sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
         }
     }
 
index acfacbdc1c4c4582372ec5b5a480e57442975fe9..eaf57c2d8fac59ea5862d22b155de58eb1f45e55 100644 (file)
@@ -136,16 +136,16 @@ class FinishremotesubscribeAction extends Action
         $profile->nickname = $nickname;
         $profile->profileurl = $profile_url;
 
-        if ($fullname) {
+        if (!is_null($fullname)) {
             $profile->fullname = $fullname;
         }
-        if ($homepage) {
+        if (!is_null($homepage)) {
             $profile->homepage = $homepage;
         }
-        if ($bio) {
+        if (!is_null($bio)) {
             $profile->bio = $bio;
         }
-        if ($location) {
+        if (!is_null($location)) {
             $profile->location = $location;
         }
 
index 499db4ae8d7da865da7c5aaf037b147bbb3cab17..fe6127da296243a0c13f15625a8fc57f162f5c83 100644 (file)
@@ -83,7 +83,7 @@ class GrouplogoAction extends Action
 
         if ($nickname_arg != $nickname) {
             $args = array('nickname' => $nickname);
-            common_redirect(common_local_url('editgroup', $args), 301);
+            common_redirect(common_local_url('grouplogo', $args), 301);
             return false;
         }
 
index 1888ecdab27389420aa5a3e84b8390cb60687e28..eeea4a37bfb1ed9b0233c8f74f8362aad24d94fe 100644 (file)
@@ -73,7 +73,7 @@ class JoingroupAction extends Action
 
         if ($nickname_arg != $nickname) {
             $args = array('nickname' => $nickname);
-            common_redirect(common_local_url('editgroup', $args), 301);
+            common_redirect(common_local_url('joingroup', $args), 301);
             return false;
         }
 
index c7152e3c0e32cc9db0b46855c28b3140570a8218..eb30d0e505977ca8e226230e171468b6bf714551 100644 (file)
@@ -73,7 +73,7 @@ class LeavegroupAction extends Action
 
         if ($nickname_arg != $nickname) {
             $args = array('nickname' => $nickname);
-            common_redirect(common_local_url('editgroup', $args), 301);
+            common_redirect(common_local_url('leavegroup', $args), 301);
             return false;
         }
 
@@ -96,12 +96,6 @@ class LeavegroupAction extends Action
             return false;
         }
 
-        if ($cur->isAdmin($this->group)) {
-            $this->clientError(_('You may not leave a group while you are its administrator.'), 403);
-            return false;
-
-        }
-
         return true;
     }
 
index 7172977ee7a761acb47dcad396a263f57fa237ed..0f98ed04bb97e26eebadbd30e116653e5398a5b2 100644 (file)
@@ -82,10 +82,9 @@ class NoticesearchrssAction extends Rss10Action
 
     function getChannel()
     {
-        global $config;
         $q = $this->trimmed('q');
         $c = array('url' => common_local_url('noticesearchrss', array('q' => $q)),
-                   'title' => $config['site']['name'] . sprintf(_(' Search Stream for "%s"'), $q),
+                   'title' => common_config('site', 'name') . sprintf(_(' Search Stream for "%s"'), $q),
                    'link' => common_local_url('noticesearch', array('q' => $q)),
                    'description' => sprintf(_('All updates matching search term "%s"'), $q));
         return $c;
index 6b1e34f1ab47db2b1c2aa4d2f273171233ed23a2..5add75485808d08f704fa6dfa73faa34593605d6 100644 (file)
@@ -119,7 +119,7 @@ class PeopletagAction extends Action
                 'FROM profile JOIN profile_tag ' .
                 'ON profile.id = profile_tag.tagger ' .
                 'WHERE profile_tag.tagger = profile_tag.tagged ' .
-                'AND tag = "%s" ' .
+                "AND tag = '%s' " .
                 'ORDER BY profile_tag.modified DESC%s';
 
         $profile->query(sprintf($qry, $this->tag, $lim));
index 0b47352964f8685538ee84ee9213e193932f9b94..c32d8ca94b611dd3fa356efc5de1ab1ae8e2364b 100644 (file)
@@ -79,7 +79,7 @@ class PostnoticeAction extends Action
         }
         $notice = Notice::staticGet('uri', $notice_uri);
         if (!$notice) {
-            $notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, 0, $notice_uri);
+            $notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, null, $notice_uri);
             if (is_string($notice)) {
                 common_server_serror($notice, 500);
                 return false;
index c3587799718b2c40fa33cb29ea661c082f9c4f53..77e26e0f4c5c15104d7e736fa90c0208e36c73f4 100644 (file)
@@ -84,12 +84,11 @@ class PublicrssAction extends Rss10Action
      */
     function getChannel()
     {
-        global $config;
         $c = array(
               'url' => common_local_url('publicrss')
-            , 'title' => sprintf(_('%s Public Stream'), $config['site']['name'])
+            , 'title' => sprintf(_('%s Public Stream'), common_config('site', 'name'))
             , 'link' => common_local_url('public')
-            , 'description' => sprintf(_('All updates for %s'), $config['site']['name']));
+            , 'description' => sprintf(_('All updates for %s'), common_config('site', 'name')));
         return $c;
     }
 
index 7ea7acd6d336bb6e04056c1d63fd5e5df5155f3d..af130f42588057ba1b2a811352f1ebbef5f9d59c 100644 (file)
@@ -333,8 +333,6 @@ class RemotesubscribeAction extends Action
 
     function requestAuthorization($user, $omb, $token, $secret)
     {
-        global $config; # for license URL
-
         $con = omb_oauth_consumer();
         $tok = new OAuthToken($token, $secret);
 
@@ -358,7 +356,7 @@ class RemotesubscribeAction extends Action
         $req->set_parameter('omb_listenee', $user->uri);
         $req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname));
         $req->set_parameter('omb_listenee_nickname', $user->nickname);
-        $req->set_parameter('omb_listenee_license', $config['license']['url']);
+        $req->set_parameter('omb_listenee_license', common_config('license', 'url'));
 
         $profile = $user->getProfile();
         if (!$profile) {
@@ -367,16 +365,16 @@ class RemotesubscribeAction extends Action
             return;
         }
 
-        if ($profile->fullname) {
+        if (!is_null($profile->fullname)) {
             $req->set_parameter('omb_listenee_fullname', $profile->fullname);
         }
-        if ($profile->homepage) {
+        if (!is_null($profile->homepage)) {
             $req->set_parameter('omb_listenee_homepage', $profile->homepage);
         }
-        if ($profile->bio) {
+        if (!is_null($profile->bio)) {
             $req->set_parameter('omb_listenee_bio', $profile->bio);
         }
-        if ($profile->location) {
+        if (!is_null($profile->location)) {
             $req->set_parameter('omb_listenee_location', $profile->location);
         }
         $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
index c20941a35ee07776acb475fd69152e4b139b79b4..b6022840bf47997f89cda54970e56b9779d123b6 100644 (file)
@@ -275,10 +275,8 @@ class ShowgroupAction extends Action
         $cur = common_current_user();
         if ($cur) {
             if ($cur->isMember($this->group)) {
-                if (!$cur->isAdmin($this->group)) {
-                    $lf = new LeaveForm($this, $this->group);
-                    $lf->show();
-                }
+                $lf = new LeaveForm($this, $this->group);
+                $lf->show();
             } else {
                 $jf = new JoinForm($this, $this->group);
                 $jf->show();
index f4b1cda230ef3776370aeb09217c548df11b7835..8ef9207facce41d02ac44d00ce771ec38df39720 100644 (file)
@@ -65,7 +65,9 @@ class SupAction extends Action
 
         $notice->query('SELECT profile_id, max(id) AS max_id ' .
                        'FROM notice ' .
-                       'WHERE created > (now() - ' . $seconds . ') ' .
+                        ((common_config('db','type') == 'pgsql') ?
+                       'WHERE extract(epoch from created) > (extract(epoch from now()) - ' . $seconds . ') ' :
+                       'WHERE created > (now() - ' . $seconds . ') ' ) .
                        'GROUP BY profile_id');
 
         $updates = array();
index db55e8cd0220de6a01c68763af20d7ad5f45cb49..7101db8df585354462150a1eae3e98364ca2b96a 100644 (file)
@@ -38,7 +38,6 @@ class Twitapidirect_messagesAction extends TwitterapiAction
 
     function show_messages($args, $apidata, $type)
     {
-
         $user = $apidata['user'];
 
         $count = $this->arg('count');
@@ -102,7 +101,17 @@ class Twitapidirect_messagesAction extends TwitterapiAction
             $this->show_rss_dmsgs($message, $title, $link, $subtitle);
             break;
          case 'atom':
-            $this->show_atom_dmsgs($message, $title, $link, $subtitle);
+            $selfuri = common_root_url() . 'api/direct_messages';
+            $selfuri .= ($type == 'received') ? '.atom' : '/sent.atom';
+            $taguribase = common_config('integration', 'taguri');
+
+            if ($type == 'sent') {
+                $id = "tag:$taguribase:SentDirectMessages:" . $user->id;
+            } else {
+                $id = "tag:$taguribase:DirectMessages:" . $user->id;
+            }
+
+            $this->show_atom_dmsgs($message, $title, $link, $subtitle, $selfuri, $id);
             break;
          case 'json':
             $this->show_json_dmsgs($message);
@@ -190,7 +199,7 @@ class Twitapidirect_messagesAction extends TwitterapiAction
         $this->init_document('xml');
         $this->elementStart('direct-messages', array('type' => 'array'));
 
-        if (is_array($messages)) {
+        if (is_array($message)) {
             foreach ($message as $m) {
                 $twitter_dm = $this->twitter_dmsg_array($m);
                 $this->show_twitter_xml_dmsg($twitter_dm);
@@ -261,16 +270,17 @@ class Twitapidirect_messagesAction extends TwitterapiAction
 
     }
 
-    function show_atom_dmsgs($message, $title, $link, $subtitle)
+    function show_atom_dmsgs($message, $title, $link, $subtitle, $selfuri, $id)
     {
 
         $this->init_document('atom');
 
         $this->element('title', null, $title);
-        $siteserver = common_config('site', 'server');
-        $this->element('id', null, "tag:$siteserver,2008:DirectMessage");
+        $this->element('id', null, $id);
         $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
-        $this->element('updated', null, common_date_iso8601(strftime('%c')));
+        $this->element('link', array('href' => $selfuri, 'rel' => 'self',
+            'type' => 'application/atom+xml'), null);
+        $this->element('updated', null, common_date_iso8601('now'));
         $this->element('subtitle', null, $subtitle);
 
         if (is_array($message)) {
index 737b7229f0335a63bc6fa21d08c7895fca1435e7..31dce341b7ca713ff0371916d56b8875ed100d7c 100644 (file)
@@ -61,10 +61,9 @@ class TwitapifavoritesAction extends TwitterapiAction
         }
 
         $sitename = common_config('site', 'name');
-        $siteserver = common_config('site', 'server');
-
         $title = sprintf(_('%s / Favorites from %s'), $sitename, $user->nickname);
-        $id = "tag:$siteserver:favorites:".$user->id;
+        $taguribase = common_config('integration', 'taguri');
+        $id = "tag:$taguribase:Favorites:".$user->id;
         $link = common_local_url('favorites', array('nickname' => $user->nickname));
         $subtitle = sprintf(_('%s updates favorited by %s / %s.'), $sitename, $profile->getBestName(), $user->nickname);
 
@@ -76,7 +75,14 @@ class TwitapifavoritesAction extends TwitterapiAction
             $this->show_rss_timeline($notice, $title, $link, $subtitle);
             break;
          case 'atom':
-            $this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
+            if (isset($apidata['api_arg'])) {
+                 $selfuri = $selfuri = common_root_url() .
+                     'api/favorites/' . $apidata['api_arg'] . '.atom';
+            } else {
+                 $selfuri = $selfuri = common_root_url() .
+                  'api/favorites.atom';
+            }
+            $this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
             break;
          case 'json':
             $this->show_json_timeline($notice);
diff --git a/actions/twitapisearchatom.php b/actions/twitapisearchatom.php
new file mode 100644 (file)
index 0000000..eb9ab5d
--- /dev/null
@@ -0,0 +1,377 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Action for showing Twitter-like Atom search results
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Search
+ * @package   Laconica
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @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/twitterapi.php';
+
+/**
+ * Action for outputting search results in Twitter compatible Atom
+ * format.
+ *
+ * TODO: abstract Atom stuff into a ruseable base class like
+ * RSS10Action.
+ *
+ * @category Search
+ * @package  Laconica
+ * @author   Zach Copley <zach@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ *
+ * @see      TwitterapiAction
+ */
+
+class TwitapisearchatomAction extends TwitterapiAction
+{
+
+    var $cnt;
+    var $query;
+    var $lang;
+    var $rpp;
+    var $page;
+    var $since_id;
+    var $geocode;
+
+    /**
+     * Constructor
+     *
+     * Just wraps the Action constructor.
+     *
+     * @param string  $output URI to output to, default = stdout
+     * @param boolean $indent Whether to indent output, default true
+     *
+     * @see Action::__construct
+     */
+
+    function __construct($output='php://output', $indent=true)
+    {
+        parent::__construct($output, $indent);
+    }
+
+    /**
+     * Do we need to write to the database?
+     *
+     * @return boolean true
+     */
+
+    function isReadonly()
+    {
+        return true;
+    }
+
+    /**
+     * Read arguments and initialize members
+     *
+     * @param array $args Arguments from $_REQUEST
+     *
+     * @return boolean success
+     *
+     */
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->query = $this->trimmed('q');
+        $this->lang  = $this->trimmed('lang');
+        $this->rpp   = $this->trimmed('rpp');
+
+        if (!$this->rpp) {
+            $this->rpp = 15;
+        }
+
+        if ($this->rpp > 100) {
+            $this->rpp = 100;
+        }
+
+        $this->page = $this->trimmed('page');
+
+        if (!$this->page) {
+            $this->page = 1;
+        }
+
+        // TODO: Suppport since_id -- we need to tweak the backend
+        // Search classes to support it.
+
+        $this->since_id = $this->trimmed('since_id');
+        $this->geocode  = $this->trimmed('geocode');
+
+        // TODO: Also, language and geocode
+
+        return true;
+    }
+
+    /**
+     * Handle a request
+     *
+     * @param array $args Arguments from $_REQUEST
+     *
+     * @return void
+     */
+
+    function handle($args)
+    {
+        parent::handle($args);
+        $this->showAtom();
+    }
+
+    /**
+     * Get the notices to output as results. This also sets some class
+     * attrs so we can use them to calculate pagination, and output
+     * since_id and max_id.
+     *
+     * @return array an array of Notice objects sorted in reverse chron
+     */
+
+    function getNotices()
+    {
+        // TODO: Support search operators like from: and to:, boolean, etc.
+
+        $notices = array();
+        $notice = new Notice();
+
+        // lcase it for comparison
+        $q = strtolower($this->query);
+
+        $search_engine = $notice->getSearchEngine('identica_notices');
+        $search_engine->set_sort_mode('chron');
+        $search_engine->limit(($this->page - 1) * $this->rpp,
+            $this->rpp + 1, true);
+        $search_engine->query($q);
+        $this->cnt = $notice->find();
+
+        $cnt = 0;
+
+        while ($notice->fetch()) {
+
+            ++$cnt;
+
+            if (!$this->max_id) {
+                $this->max_id = $notice->id;
+            }
+
+            if ($cnt > $this->rpp) {
+                break;
+            }
+
+            $notices[] = clone($notice);
+        }
+
+        return $notices;
+    }
+
+    /**
+     * Output search results as an Atom feed
+     *
+     * @return void
+     */
+
+    function showAtom()
+    {
+        $notices = $this->getNotices();
+
+        $this->initAtom();
+        $this->showFeed();
+
+        foreach ($notices as $n) {
+            $this->showEntry($n);
+        }
+
+        $this->endAtom();
+    }
+
+    /**
+     * Show feed specific Atom elements
+     *
+     * @return void
+     */
+
+    function showFeed()
+    {
+        // TODO: A9 OpenSearch stuff like search.twitter.com?
+
+        $server   = common_config('site', 'server');
+        $sitename = common_config('site', 'name');
+
+        // XXX: Use xmlns:laconica instead?
+
+        $this->elementStart('feed',
+            array('xmlns' => 'http://www.w3.org/2005/Atom',
+
+                             // XXX: xmlns:twitter causes Atom validation to fail
+                             // It's used for the source attr on notices
+
+                             'xmlns:twitter' => 'http://api.twitter.com/',
+                             'xml:lang' => 'en-US')); // XXX Other locales ?
+
+        $taguribase = common_config('integration', 'taguri');
+        $this->element('id', null, "tag:$taguribase:search/$server");
+
+        $site_uri = common_path(false);
+
+        $search_uri = $site_uri . 'api/search.atom?q=' . urlencode($this->query);
+
+        if ($this->rpp != 15) {
+            $search_uri .= '&rpp=' . $this->rpp;
+        }
+
+        // FIXME: this alternate link is not quite right because our
+        // web-based notice search doesn't support a rpp (responses per
+        // page) param yet
+
+        $this->element('link', array('type' => 'text/html',
+                                     'rel'  => 'alternate',
+                                     'href' => $site_uri . 'search/notice?q=' .
+                                        urlencode($this->query)));
+
+        // self link
+
+        $self_uri = $search_uri;
+        $self_uri .= ($this->page > 1) ? '&page=' . $this->page : '';
+
+        $this->element('link', array('type' => 'application/atom+xml',
+                                     'rel'  => 'self',
+                                     'href' => $self_uri));
+
+        $this->element('title', null, "$this->query - $sitename Search");
+        $this->element('updated', null, common_date_iso8601('now'));
+
+        // XXX: The below "rel" links are not valid Atom, but it's what
+        // Twitter does...
+
+        // refresh link
+
+        $refresh_uri = $search_uri . "&since_id=" . $this->max_id;
+
+        $this->element('link', array('type' => 'application/atom+xml',
+                                     'rel'  => 'refresh',
+                                     'href' => $refresh_uri));
+
+        // pagination links
+
+        if ($this->cnt > $this->rpp) {
+
+            $next_uri = $search_uri . "&max_id=" . $this->max_id .
+                '&page=' . ($this->page + 1);
+
+            $this->element('link', array('type' => 'application/atom+xml',
+                                         'rel'  => 'next',
+                                         'href' => $next_uri));
+        }
+
+        if ($this->page > 1) {
+
+            $previous_uri = $search_uri . "&max_id=" . $this->max_id .
+                '&page=' . ($this->page - 1);
+
+            $this->element('link', array('type' => 'application/atom+xml',
+                                         'rel'  => 'previous',
+                                         'href' => $previous_uri));
+        }
+
+    }
+
+    /**
+     * Build an Atom entry similar to search.twitter.com's based on
+     * a given notice
+     *
+     * @param Notice $notice the notice to use
+     *
+     * @return void
+     */
+
+    function showEntry($notice)
+    {
+        $server  = common_config('site', 'server');
+        $profile = $notice->getProfile();
+        $nurl    = common_local_url('shownotice', array('notice' => $notice->id));
+
+        $this->elementStart('entry');
+
+        $taguribase = common_config('integration', 'taguri');
+
+        $this->element('id', null, "tag:$taguribase:$notice->id");
+        $this->element('published', null, common_date_w3dtf($notice->created));
+        $this->element('link', array('type' => 'text/html',
+                                     'rel'  => 'alternate',
+                                     'href' => $nurl));
+        $this->element('title', null, common_xml_safe_str(trim($notice->content)));
+        $this->element('content', array('type' => 'html'), $notice->rendered);
+        $this->element('updated', null, common_date_w3dtf($notice->created));
+        $this->element('link', array('type' => 'image/png',
+                                     // XXX: Twitter uses rel="image" (not valid)
+                                     'rel' => 'related',
+                                     'href' => $profile->avatarUrl()));
+
+        // TODO: Here is where we'd put in a link to an atom feed for threads
+
+        $this->element("twitter:source", null,
+            htmlentities($this->source_link($notice->source)));
+
+        $this->elementStart('author');
+
+        $name = $profile->nickname;
+
+        if ($profile->fullname) {
+            $name .= ' (' . $profile->fullname . ')';
+        }
+
+        $this->element('name', null, $name);
+        $this->element('uri', null, common_profile_uri($profile));
+        $this->elementEnd('author');
+
+        $this->elementEnd('entry');
+    }
+
+    /**
+     * Initialize the Atom output, send headers
+     *
+     * @return void
+     */
+
+    function initAtom()
+    {
+        header('Content-Type: application/atom+xml; charset=utf-8');
+        $this->startXml();
+    }
+
+    /**
+     * End the Atom feed
+     *
+     * @return void
+     */
+
+    function endAtom()
+    {
+        $this->elementEnd('feed');
+    }
+
+}
index b50aa86b7ac119a2200d0522671660fc4f50cabe..0f9f523a14f7b65d88290ab7492c28b0ba4437b2 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Laconica, the distributed open-source microblogging tool
  *
- * List of replies
+ * Action for showing Twitter-like JSON search results
  *
  * PHP version 5
  *
@@ -114,7 +114,7 @@ class TwitapisearchjsonAction extends TwitterapiAction
     function showResults()
     {
 
-        // TODO: Support search operators like from: and to:
+        // TODO: Support search operators like from: and to:, boolean, etc.
 
         $notice = new Notice();
 
@@ -137,7 +137,7 @@ class TwitapisearchjsonAction extends TwitterapiAction
     }
 
     /**
-     * This is a read-only action
+     * Do we need to write to the database?
      *
      * @return boolean true
      */
index 216835026de7806b1a5c9d65211c9102cfa88433..323c4f1f8824c0d197d5ec6b1b788f25efc35515 100644 (file)
@@ -29,10 +29,12 @@ class TwitapistatusesAction extends TwitterapiAction
         parent::handle($args);
 
         $sitename = common_config('site', 'name');
-        $siteserver = common_config('site', 'server');
         $title = sprintf(_("%s public timeline"), $sitename);
-        $id = "tag:$siteserver:Statuses";
+
+        $taguribase = common_config('integration', 'taguri');
+        $id = "tag:$taguribase:PublicTimeline";
         $link = common_root_url();
+
         $subtitle = sprintf(_("%s updates from everyone!"), $sitename);
 
         // Number of public statuses to return by default -- Twitter sends 20
@@ -70,7 +72,8 @@ class TwitapistatusesAction extends TwitterapiAction
                     $this->show_rss_timeline($notice, $title, $link, $subtitle);
                     break;
                 case 'atom':
-                    $this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
+                    $selfuri = common_root_url() . 'api/statuses/public_timeline.atom';
+                    $this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
                     break;
                 case 'json':
                     $this->show_json_timeline($notice);
@@ -114,17 +117,19 @@ class TwitapistatusesAction extends TwitterapiAction
         }
 
         $since = strtotime($this->arg('since'));
-
-        $user = $this->get_user(null, $apidata);
+        $user = $this->get_user($apidata['api_arg'], $apidata);
         $this->auth_user = $user;
 
-        $profile = $user->getProfile();
+        if (empty($user)) {
+             $this->clientError(_('No such user!'), 404, $apidata['content-type']);
+            return;
+        }
 
+        $profile = $user->getProfile();
         $sitename = common_config('site', 'name');
-        $siteserver = common_config('site', 'server');
-
         $title = sprintf(_("%s and friends"), $user->nickname);
-        $id = "tag:$siteserver:friends:" . $user->id;
+        $taguribase = common_config('integration', 'taguri');
+        $id = "tag:$taguribase:FriendsTimeline:" . $user->id;
         $link = common_local_url('all', array('nickname' => $user->nickname));
         $subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename);
 
@@ -138,7 +143,14 @@ class TwitapistatusesAction extends TwitterapiAction
             $this->show_rss_timeline($notice, $title, $link, $subtitle);
             break;
          case 'atom':
-            $this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
+            if (isset($apidata['api_arg'])) {
+                $selfuri = $selfuri = common_root_url() .
+                    'api/statuses/friends_timeline/' . $apidata['api_arg'] . '.atom';
+            } else {
+                $selfuri = $selfuri = common_root_url() .
+                    'api/statuses/friends_timeline.atom';
+            }
+            $this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
             break;
          case 'json':
             $this->show_json_timeline($notice);
@@ -194,10 +206,9 @@ class TwitapistatusesAction extends TwitterapiAction
         $since = strtotime($this->arg('since'));
 
         $sitename = common_config('site', 'name');
-        $siteserver = common_config('site', 'server');
-
         $title = sprintf(_("%s timeline"), $user->nickname);
-        $id = "tag:$siteserver:user:".$user->id;
+        $taguribase = common_config('integration', 'taguri');
+        $id = "tag:$taguribase:UserTimeline:".$user->id;
         $link = common_local_url('showstream', array('nickname' => $user->nickname));
         $subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename);
 
@@ -219,7 +230,14 @@ class TwitapistatusesAction extends TwitterapiAction
             $this->show_rss_timeline($notice, $title, $link, $subtitle, $suplink);
             break;
          case 'atom':
-            $this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink);
+            if (isset($apidata['api_arg'])) {
+                $selfuri = $selfuri = common_root_url() .
+                    'api/statuses/user_timeline/' . $apidata['api_arg'] . '.atom';
+            } else {
+                $selfuri = $selfuri = common_root_url() .
+                 'api/statuses/user_timeline.atom';
+            }
+            $this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink, $selfuri);
             break;
          case 'json':
             $this->show_json_timeline($notice);
@@ -337,15 +355,14 @@ class TwitapistatusesAction extends TwitterapiAction
         $since_id = $this->arg('since_id');
         $before_id = $this->arg('before_id');
 
+        $user = $this->get_user($apidata['api_arg'], $apidata);
         $this->auth_user = $apidata['user'];
-        $user = $this->auth_user;
         $profile = $user->getProfile();
 
         $sitename = common_config('site', 'name');
-        $siteserver = common_config('site', 'server');
-
         $title = sprintf(_('%1$s / Updates replying to %2$s'), $sitename, $user->nickname);
-        $id = "tag:$siteserver:replies:".$user->id;
+        $taguribase = common_config('integration', 'taguri');
+        $id = "tag:$taguribase:Replies:".$user->id;
         $link = common_local_url('replies', array('nickname' => $user->nickname));
         $subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName());
 
@@ -383,7 +400,14 @@ class TwitapistatusesAction extends TwitterapiAction
             $this->show_rss_timeline($notices, $title, $link, $subtitle);
             break;
          case 'atom':
-            $this->show_atom_timeline($notices, $title, $id, $link, $subtitle);
+             if (isset($apidata['api_arg'])) {
+                 $selfuri = $selfuri = common_root_url() .
+                     'api/statuses/replies/' . $apidata['api_arg'] . '.atom';
+             } else {
+                 $selfuri = $selfuri = common_root_url() .
+                  'api/statuses/replies.atom';
+             }
+            $this->show_atom_timeline($notices, $title, $id, $link, $subtitle, null, $selfuri);
             break;
          case 'json':
             $this->show_json_timeline($notices);
index a79859bbf0a9205c40c25022913477b0a4d5193c..45725d3ff44e289cdef8b7c1ed80399c30db5ede 100644 (file)
@@ -186,12 +186,12 @@ class TwittersettingsAction extends ConnectSettingsAction
 
         $current_user = common_current_user();
 
-        $qry = 'SELECT user.* ' .
+        $qry = 'SELECT "user".* ' .
           'FROM subscription ' .
-          'JOIN user ON subscription.subscribed = user.id ' .
-          'JOIN foreign_link ON foreign_link.user_id = user.id ' .
+          'JOIN "user" ON subscription.subscribed = "user".id ' .
+          'JOIN foreign_link ON foreign_link.user_id = "user".id ' .
           'WHERE subscriber = %d ' .
-          'ORDER BY user.nickname';
+          'ORDER BY "user".nickname';
 
         $user = new User();
 
index 4751a04ff31726b9002caca41db06099f2cbc3fd..7dc52fda9eaf69d315d83a585793accb563f7172 100644 (file)
@@ -34,6 +34,8 @@ class UpdateprofileAction extends Action
             $server = omb_oauth_server();
             list($consumer, $token) = $server->verify_request($req);
             if ($this->update_profile($req, $consumer, $token)) {
+                header('HTTP/1.1 200 OK');
+                header('Content-type: text/plain');
                 print "omb_version=".OMB_VERSION_01;
             }
         } catch (OAuthException $e) {
@@ -136,22 +138,24 @@ class UpdateprofileAction extends Action
 
         $orig_profile = clone($profile);
 
-        if ($nickname) {
+        /* Use values even if they are an empty string. Parsing an empty string in
+           updateProfile is the specified way of clearing a parameter in OMB. */
+        if (!is_null($nickname)) {
             $profile->nickname = $nickname;
         }
-        if ($profile_url) {
+        if (!is_null($profile_url)) {
             $profile->profileurl = $profile_url;
         }
-        if ($fullname) {
+        if (!is_null($fullname)) {
             $profile->fullname = $fullname;
         }
-        if ($homepage) {
+        if (!is_null($homepage)) {
             $profile->homepage = $homepage;
         }
-        if ($bio) {
+        if (!is_null($bio)) {
             $profile->bio = $bio;
         }
-        if ($location) {
+        if (!is_null($location)) {
             $profile->location = $location;
         }
 
@@ -173,10 +177,6 @@ class UpdateprofileAction extends Action
                     return false;
                 }
             }
-            header('HTTP/1.1 200 OK');
-            header('Content-type: text/plain');
-            print 'Updated profile';
-            print "\n";
             return true;
         }
     }
index 0dc1841d4fbd873c89997a2265b63792f7acf40e..6a76e3a4c20809e9eb37537e1a1258fa77bab3a4 100644 (file)
@@ -113,9 +113,9 @@ class UserauthorizationAction extends Action
         $this->element('a', array('href' => $profile,
                                   'class' => 'external profile nickname'),
                        $nickname);
-        if ($fullname) {
+        if (!is_null($fullname)) {
             $this->elementStart('div', 'fullname');
-            if ($homepage) {
+            if (!is_null($homepage)) {
                 $this->element('a', array('href' => $homepage),
                                $fullname);
             } else {
@@ -123,10 +123,10 @@ class UserauthorizationAction extends Action
             }
             $this->elementEnd('div');
         }
-        if ($location) {
+        if (!is_null($location)) {
             $this->element('div', 'location', $location);
         }
-        if ($bio) {
+        if (!is_null($bio)) {
             $this->element('div', 'bio', $bio);
         }
         $this->elementStart('div', 'license');
@@ -179,16 +179,16 @@ class UserauthorizationAction extends Action
                 $params['omb_listener_nickname'] = $user->nickname;
                 $params['omb_listener_profile'] = common_local_url('showstream',
                                                                    array('nickname' => $user->nickname));
-                if ($profile->fullname) {
+                if (!is_null($profile->fullname)) {
                     $params['omb_listener_fullname'] = $profile->fullname;
                 }
-                if ($profile->homepage) {
+                if (!is_null($profile->homepage)) {
                     $params['omb_listener_homepage'] = $profile->homepage;
                 }
-                if ($profile->bio) {
+                if (!is_null($profile->bio)) {
                     $params['omb_listener_bio'] = $profile->bio;
                 }
-                if ($profile->location) {
+                if (!is_null($profile->location)) {
                     $params['omb_listener_location'] = $profile->location;
                 }
                 $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
@@ -197,7 +197,7 @@ class UserauthorizationAction extends Action
                 }
                 $parts = array();
                 foreach ($params as $k => $v) {
-                    $parts[] = $k . '=' . OAuthUtil::urlencodeRFC3986($v);
+                    $parts[] = $k . '=' . OAuthUtil::urlencode_rfc3986($v);
                 }
                 $query_string = implode('&', $parts);
                 $parsed = parse_url($callback);
@@ -267,16 +267,16 @@ class UserauthorizationAction extends Action
         $profile->nickname = $nickname;
         $profile->profileurl = $profile_url;
 
-        if ($fullname) {
+        if (!is_null($fullname)) {
             $profile->fullname = $fullname;
         }
-        if ($homepage) {
+        if (!is_null($homepage)) {
             $profile->homepage = $homepage;
         }
-        if ($bio) {
+        if (!is_null($bio)) {
             $profile->bio = $bio;
         }
-        if ($location) {
+        if (!is_null($location)) {
             $profile->location = $location;
         }
 
@@ -409,7 +409,7 @@ class UserauthorizationAction extends Action
                        'omb_listenee_profile', 'omb_listenee_nickname',
                        'omb_listenee_license') as $param)
         {
-            if (!$req->get_parameter($param)) {
+            if (is_null($req->get_parameter($param))) {
                 throw new OAuthException("Required parameter '$param' not found");
             }
         }
index a3e5a3aab710a05699ef64eeffb5c6084397d35a..d3bf352d8df3cd71bca218686657be344a2454c9 100644 (file)
@@ -53,6 +53,7 @@ class UserrssAction extends Rss10Action
 
         $notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
 
+        $notices = array();
         while ($notice->fetch()) {
             $notices[] = clone($notice);
         }
diff --git a/bin/flowplayer-3.0.5.swf b/bin/flowplayer-3.0.5.swf
deleted file mode 100644 (file)
index 05b64a0..0000000
Binary files a/bin/flowplayer-3.0.5.swf and /dev/null differ
diff --git a/bin/flowplayer.audio-3.0.3.swf b/bin/flowplayer.audio-3.0.3.swf
deleted file mode 100644 (file)
index ef85f1b..0000000
Binary files a/bin/flowplayer.audio-3.0.3.swf and /dev/null differ
diff --git a/bin/flowplayer.controls-3.0.3.swf b/bin/flowplayer.controls-3.0.3.swf
deleted file mode 100644 (file)
index 09a27e8..0000000
Binary files a/bin/flowplayer.controls-3.0.3.swf and /dev/null differ
index 2c0edfa14d70561bb4c6341310a93ddd3c837ef0..486a65a3c7e2aa6d78bc8478f579b27e8d80ab93 100644 (file)
@@ -4,22 +4,21 @@
  */
 require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 
-class Nonce extends Memcached_DataObject 
+class Nonce extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'nonce';                           // table name
     public $consumer_key;                    // varchar(255)  primary_key not_null
-    public $tok;                             // char(32)  primary_key not_null
+    public $tok;                             // char(32)
     public $nonce;                           // char(32)  primary_key not_null
-    public $ts;                              // datetime()   not_null
+    public $ts;                              // datetime()  primary_key not_null
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=null)
-    { return Memcached_DataObject::staticGet('Nonce',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Nonce',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
index 9b5194a5c8e63dd4a0b32fb2ec773fb7d7d92ae2..adeed2796bff0ba1771f76021f6dbf6ef0b20e1e 100644 (file)
@@ -122,6 +122,8 @@ 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.');
@@ -132,7 +134,12 @@ class Notice extends Memcached_DataObject
             return _('Too many notices too fast; take a breather and post again in a few minutes.');
         }
 
-        $banned = common_config('profile', 'banned');
+        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.');
+        }
+
+               $banned = common_config('profile', 'banned');
 
         if ( in_array($profile_id, $banned) || in_array($profile->nickname, $banned)) {
             common_log(LOG_WARNING, "Attempted post from banned user: $profile->nickname (user id = $profile_id).");
@@ -156,11 +163,12 @@ class Notice extends Memcached_DataObject
 
                $notice->query('BEGIN');
 
-        $notice->created = common_sql_now();
-        $notice->content = common_shorten_links($content);
-        $notice->rendered = common_render_content($notice->content, $notice);
-        $notice->source = $source;
-        $notice->uri = $uri;
+               $notice->reply_to = $reply_to;
+               $notice->created = common_sql_now();
+               $notice->content = $final;
+               $notice->rendered = common_render_content($final, $notice);
+               $notice->source = $source;
+               $notice->uri = $uri;
 
         if (!empty($reply_to)) {
             $reply_notice = Notice::staticGet('id', $reply_to);
@@ -212,6 +220,36 @@ class Notice extends Memcached_DataObject
         return $notice;
     }
 
+    static function checkDupes($profile_id, $content) {
+        $profile = Profile::staticGet($profile_id);
+        if (!$profile) {
+            return false;
+        }
+        $notice = $profile->getNotices(0, NOTICE_CACHE_WINDOW);
+        if ($notice) {
+            $last = 0;
+            while ($notice->fetch()) {
+                if (time() - strtotime($notice->created) >= common_config('site', 'dupelimit')) {
+                    return true;
+                } else if ($notice->content == $content) {
+                    return false;
+                }
+            }
+        }
+        # If we get here, oldest item in cache window is not
+        # old enough for dupe limit; do direct check against DB
+        $notice = new Notice();
+        $notice->profile_id = $profile_id;
+        $notice->content = $content;
+        if (common_config('db','type') == 'pgsql')
+            $notice->whereAdd('extract(epoch from now() - created) < ' . common_config('site', 'dupelimit'));
+        else
+            $notice->whereAdd('now() - created < ' . common_config('site', 'dupelimit'));
+
+        $cnt = $notice->count();
+        return ($cnt == 0);
+    }
+
     static function checkEditThrottle($profile_id) {
         $profile = Profile::staticGet($profile_id);
         if (!$profile) {
index 8b0b9acd506f3fb07ea7b0dfa43f355c1609ab93..d9f30bec58cf18181a0e78347b2b1b1b2209a5a7 100644 (file)
@@ -121,7 +121,8 @@ class User extends Memcached_DataObject
         static $blacklist = array('rss', 'xrds', 'doc', 'main',
                                   'settings', 'notice', 'user',
                                   'search', 'avatar', 'tag', 'tags',
-                                  'api', 'message', 'group', 'groups');
+                                  'api', 'message', 'group', 'groups',
+                                  'local');
         $merged = array_merge($blacklist, common_config('nickname', 'blacklist'));
         return !in_array($nickname, $merged);
     }
index aaa7035a445f4f4adc02f4b71d8f48eaa5249dbb..dd424bbdd376338908a206f2f9627eadeef5a84a 100755 (executable)
@@ -145,7 +145,7 @@ id = N
 
 [nonce]
 consumer_key = 130
-tok = 130
+tok = 2
 nonce = 130
 ts = 142
 created = 142
@@ -153,8 +153,8 @@ modified = 384
 
 [nonce__keys]
 consumer_key = K
-tok = K
 nonce = K
+ts = K
 
 [notice]
 id = 129
index c1f628489619d4080f25b8932cb1aedde3a3e012..098fd27501814236b35f70e93ae3b6614bed04ab 100644 (file)
@@ -37,6 +37,9 @@ $config['site']['path'] = 'laconica';
 # Enables extra log information, for example full details of PEAR DB errors
 #$config['site']['logdebug'] = true;
 
+#To set your own logo, overriding the one in the theme
+#$config['site']['logo'] = '/mylogo.png';
+
 # This is a PEAR DB DSN, see http://pear.php.net/manual/en/package.database.db.intro-dsn.php
 # Set it to match your actual database
 
@@ -167,3 +170,15 @@ $config['sphinx']['port'] = 3312;
 # Add Google Analytics
 # require_once('plugins/GoogleAnalyticsPlugin.php');
 # $ga = new GoogleAnalyticsPlugin('your secret code');
+
+#Don't allow saying the same thing more than once per hour
+#$config['site']['dupelimit'] = 3600;
+#Don't enforce the dupe limit
+#$config['site']['dupelimit'] = -1;
+
+#Base string for minting Tag URIs in Atom feeds. Defaults to
+#"yourserver,2009". This needs to be configured properly for your Atom
+#feeds to validate.  See: http://www.faqs.org/rfcs/rfc4151.html and
+#http://taguri.org/ Examples:
+#$config['integration']['taguri'] = 'example.net,2008';
+#$config['integration']['taguri'] = 'admin@example.net,2009-03-09'
index c7c1826d260f4c04b080a66bfcf409c6eb767224..0d66716053caa5190028f7d46c005b64ff6275eb 100644 (file)
@@ -183,15 +183,14 @@ create table token (
 
 create table nonce (
     consumer_key varchar(255) not null comment 'unique identifier, root URL',
-    tok char(32) not null comment 'identifying value',
+    tok char(32) null comment 'buggy old value, ignored',
     nonce char(32) not null comment 'nonce',
     ts datetime not null comment 'timestamp sent',
 
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
 
-    constraint primary key (consumer_key, tok, nonce),
-    constraint foreign key (consumer_key, tok) references token (consumer_key, tok)
+    constraint primary key (consumer_key, ts, nonce)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 /* One-to-many relationship of user to openid_url */
index 2d83f784a293dd388af61b0f24e7fc3b92f28b25..f879d7936ff1c83cf62957c9d9155de84a0abf7c 100644 (file)
@@ -1,7 +1,8 @@
 /* local and remote users have profiles */\r
 \r
+create sequence profile_seq;\r
 create table profile (\r
-    id serial primary key /* comment 'unique identifier' */,\r
+    id bigint default nextval('profile_seq') primary key /* comment 'unique identifier' */,\r
     nickname varchar(64) not null /* comment 'nickname or username' */,\r
     fullname varchar(255) /* comment 'display name' */,\r
     profileurl varchar(255) /* comment 'URL, cached so we dont regenerate' */,\r
@@ -30,8 +31,9 @@ create table avatar (
 );\r
 create index avatar_profile_id_idx on avatar using btree(profile_id);\r
 \r
+create sequence sms_carrier_seq;\r
 create table sms_carrier (\r
-    id serial primary key /* comment 'primary key for SMS carrier' */,\r
+    id bigint default nextval('sms_carrier_seq') primary key /* comment 'primary key for SMS carrier' */,\r
     name varchar(64) unique /* comment 'name of the carrier' */,\r
     email_pattern varchar(255) not null /* comment 'sprintf pattern for making an email address from a phone number' */,\r
     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,\r
@@ -101,9 +103,10 @@ create table subscription (
 create index subscription_subscriber_idx on subscription using btree(subscriber);\r
 create index subscription_subscribed_idx on subscription using btree(subscribed);\r
 \r
+create sequence notice_seq;\r
 create table notice (\r
 \r
-    id serial primary key /* comment 'unique identifier' */,\r
+    id bigint default nextval('notice_seq') primary key /* comment 'unique identifier' */,\r
     profile_id integer not null /* comment 'who made the update' */ references profile (id) ,\r
     uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI' */,\r
     content varchar(140) /* comment 'update content' */,\r
@@ -180,14 +183,13 @@ create table token (
 create table nonce (\r
     consumer_key varchar(255) not null /* comment 'unique identifier, root URL' */,\r
     tok char(32) not null /* comment 'identifying value' */,\r
-    nonce char(32) not null /* comment 'nonce' */,\r
+    nonce char(32) null /* comment 'buggy old value, ignored */,\r
     ts integer not null /* comment 'timestamp sent' values are epoch, and only used internally */,\r
 \r
     created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,\r
     modified timestamp /* comment 'date this record was modified' */,\r
 \r
-    primary key (consumer_key, tok, nonce),\r
-    foreign key (consumer_key, tok) references token (consumer_key, tok)\r
+    primary key (consumer_key, ts, nonce)\r
 );\r
 \r
 /* One-to-many relationship of user to openid_url */\r
@@ -318,9 +320,10 @@ create table invitation (
 create index invitation_address_idx on invitation using btree(address,address_type);\r
 create index invitation_user_id_idx on invitation using btree(user_id);\r
 \r
+create sequence message_seq;\r
 create table message (\r
 \r
-    id serial primary key /* comment 'unique identifier' */,\r
+    id bigint default nextval('message_seq') primary key /* comment 'unique identifier' */,\r
     uri varchar(255) unique /* comment 'universally unique identifier' */,\r
     from_profile integer not null /* comment 'who the message is from' */ references profile (id),\r
     to_profile integer not null /* comment 'who the message is to' */ references profile (id),\r
@@ -368,9 +371,10 @@ create table profile_block (
 \r
 );\r
 \r
+create sequence user_group_seq;\r
 create table user_group (\r
 \r
-    id serial primary key /* comment 'unique identifier' */,\r
+    id bigint default nextval('user_group_seq') primary key /* comment 'unique identifier' */,\r
 \r
     nickname varchar(64) unique /* comment 'nickname for addressing' */,\r
     fullname varchar(255) /* comment 'display name' */,\r
index 6dc6b3f356faf87d243132ec5f82e6f161e4a194..029166175c5f4b6ad07fac4bb47ea0502c7b95f9 100644 (file)
@@ -16,6 +16,10 @@ class OAuthConsumer {/*{{{*/
     $this->secret = $secret;
     $this->callback_url = $callback_url;
   }/*}}}*/
+
+  function __toString() {/*{{{*/
+    return "OAuthConsumer[key=$this->key,secret=$this->secret]";
+  }/*}}}*/
 }/*}}}*/
 
 class OAuthToken {/*{{{*/
@@ -37,8 +41,8 @@ class OAuthToken {/*{{{*/
    * would respond to request_token and access_token calls with
    */
   function to_string() {/*{{{*/
-    return "oauth_token=" . OAuthUtil::urlencodeRFC3986($this->key) . 
-        "&oauth_token_secret=" . OAuthUtil::urlencodeRFC3986($this->secret);
+    return "oauth_token=" . OAuthUtil::urlencode_rfc3986($this->key) . 
+        "&oauth_token_secret=" . OAuthUtil::urlencode_rfc3986($this->secret);
   }/*}}}*/
 
   function __toString() {/*{{{*/
@@ -67,7 +71,7 @@ class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {/*{{{*/
       ($token) ? $token->secret : ""
     );
 
-    $key_parts = array_map(array('OAuthUtil','urlencodeRFC3986'), $key_parts);
+    $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
     $key = implode('&', $key_parts);
 
     return base64_encode( hash_hmac('sha1', $base_string, $key, true));
@@ -81,11 +85,11 @@ class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {/*{{{*/
 
   public function build_signature($request, $consumer, $token) {/*{{{*/
     $sig = array(
-      OAuthUtil::urlencodeRFC3986($consumer->secret)
+      OAuthUtil::urlencode_rfc3986($consumer->secret)
     );
 
     if ($token) {
-      array_push($sig, OAuthUtil::urlencodeRFC3986($token->secret));
+      array_push($sig, OAuthUtil::urlencode_rfc3986($token->secret));
     } else {
       array_push($sig, '');
     }
@@ -94,7 +98,7 @@ class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {/*{{{*/
     // for debug purposes
     $request->base_string = $raw;
 
-    return OAuthUtil::urlencodeRFC3986($raw);
+    return OAuthUtil::urlencode_rfc3986($raw);
   }/*}}}*/
 }/*}}}*/
 
@@ -182,7 +186,7 @@ class OAuthRequest {/*{{{*/
    */
   public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {/*{{{*/
     $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' : 'https';
-    @$http_url or $http_url = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+    @$http_url or $http_url = $scheme . '://' . $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'];
     @$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
     
     $request_headers = OAuthRequest::get_headers();
@@ -192,27 +196,23 @@ class OAuthRequest {/*{{{*/
     // do this
     if ($parameters) {
       $req = new OAuthRequest($http_method, $http_url, $parameters);
+    } else {
+      // collect request parameters from query string (GET) and post-data (POST) if appropriate (note: POST vars have priority)
+      $req_parameters = $_GET;
+      if ($http_method == "POST" && @strstr($request_headers["Content-Type"], "application/x-www-form-urlencoded") ) {
+        $req_parameters = array_merge($req_parameters, $_POST);
+      }
+
+      // next check for the auth header, we need to do some extra stuff
+      // if that is the case, namely suck in the parameters from GET or POST
+      // so that we can include them in the signature
+      if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
+        $header_parameters = OAuthRequest::split_header($request_headers['Authorization']);
+        $parameters = array_merge($req_parameters, $header_parameters);
+        $req = new OAuthRequest($http_method, $http_url, $parameters);
+      } else $req = new OAuthRequest($http_method, $http_url, $req_parameters);
     }
-    // next check for the auth header, we need to do some extra stuff
-    // if that is the case, namely suck in the parameters from GET or POST
-    // so that we can include them in the signature
-    else if (@substr($request_headers['Authorization'], 0, 5) == "OAuth") {
-      $header_parameters = OAuthRequest::split_header($request_headers['Authorization']);
-      if ($http_method == "GET") {
-        $req_parameters = $_GET;
-      } 
-      else if ($http_method == "POST") {
-        $req_parameters = $_POST;
-      } 
-      $parameters = array_merge($header_parameters, $req_parameters);
-      $req = new OAuthRequest($http_method, $http_url, $parameters);
-    }
-    else if ($http_method == "GET") {
-      $req = new OAuthRequest($http_method, $http_url, $_GET);
-    }
-    else if ($http_method == "POST") {
-      $req = new OAuthRequest($http_method, $http_url, $_POST);
-    }
+
     return $req;
   }/*}}}*/
 
@@ -238,7 +238,7 @@ class OAuthRequest {/*{{{*/
   }/*}}}*/
 
   public function get_parameter($name) {/*{{{*/
-    return $this->parameters[$name];
+    return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
   }/*}}}*/
 
   public function get_parameters() {/*{{{*/
@@ -267,12 +267,12 @@ class OAuthRequest {/*{{{*/
     }
                
     // Urlencode both keys and values
-    $keys = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_keys($params));
-    $values = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_values($params));
+    $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
+    $values = OAuthUtil::urlencode_rfc3986(array_values($params));
     $params = array_combine($keys, $values);
 
     // Sort by keys (natsort)
-    uksort($params, 'strnatcmp');
+    uksort($params, 'strcmp');
 
     // Generate key=value pairs
     $pairs = array();
@@ -307,7 +307,7 @@ class OAuthRequest {/*{{{*/
       $this->get_signable_parameters()
     );
 
-    $parts = array_map(array('OAuthUtil', 'urlencodeRFC3986'), $parts);
+    $parts = OAuthUtil::urlencode_rfc3986($parts);
 
     return implode('&', $parts);
   }/*}}}*/
@@ -351,11 +351,21 @@ class OAuthRequest {/*{{{*/
 
   /**
    * builds the data one would send in a POST request
+   *
+   * TODO(morten.fangel):
+   * this function might be easily replaced with http_build_query()
+   * and corrections for rfc3986 compatibility.. but not sure
    */
   public function to_postdata() {/*{{{*/
     $total = array();
     foreach ($this->parameters as $k => $v) {
-      $total[] = OAuthUtil::urlencodeRFC3986($k) . "=" . OAuthUtil::urlencodeRFC3986($v);
+      if (is_array($v)) {
+        foreach ($v as $va) {
+          $total[] = OAuthUtil::urlencode_rfc3986($k) . "[]=" . OAuthUtil::urlencode_rfc3986($va);
+        }
+      } else {
+        $total[] = OAuthUtil::urlencode_rfc3986($k) . "=" . OAuthUtil::urlencode_rfc3986($v);
+      }
     }
     $out = implode("&", $total);
     return $out;
@@ -364,12 +374,13 @@ class OAuthRequest {/*{{{*/
   /**
    * builds the Authorization: header
    */
-  public function to_header($realm="") {/*{{{*/
-    $out ='"Authorization: OAuth realm="' . $realm . '",';
+  public function to_header() {/*{{{*/
+    $out ='Authorization: OAuth realm=""';
     $total = array();
     foreach ($this->parameters as $k => $v) {
       if (substr($k, 0, 5) != "oauth") continue;
-      $out .= ',' . OAuthUtil::urlencodeRFC3986($k) . '="' . OAuthUtil::urlencodeRFC3986($v) . '"';
+      if (is_array($v)) throw new OAuthException('Arrays not supported in headers');
+      $out .= ',' . OAuthUtil::urlencode_rfc3986($k) . '="' . OAuthUtil::urlencode_rfc3986($v) . '"';
     }
     return $out;
   }/*}}}*/
@@ -412,24 +423,22 @@ class OAuthRequest {/*{{{*/
    * parameters, has to do some unescaping
    */
   private static function split_header($header) {/*{{{*/
-    // remove 'OAuth ' at the start of a header 
-    $header = substr($header, 6); 
-
-    // error cases: commas in parameter values?
-    $parts = explode(",", $header);
-    $out = array();
-    foreach ($parts as $param) {
-      $param = ltrim($param);
-      // skip the "realm" param, nobody ever uses it anyway
-      if (substr($param, 0, 5) != "oauth") continue;
-
-      $param_parts = explode("=", $param);
-
-      // rawurldecode() used because urldecode() will turn a "+" in the
-      // value into a space
-      $out[$param_parts[0]] = rawurldecode(substr($param_parts[1], 1, -1));
+    $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
+    $offset = 0;
+    $params = array();
+    while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
+      $match = $matches[0];
+      $header_name = $matches[2][0];
+      $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
+      $params[$header_name] = OAuthUtil::urldecode_rfc3986( $header_content );
+      $offset = $match[1] + strlen($match[0]);
     }
-    return $out;
+  
+    if (isset($params['realm'])) {
+       unset($params['realm']);
+    }
+
+    return $params;
   }/*}}}*/
 
   /**
@@ -506,6 +515,7 @@ class OAuthServer {/*{{{*/
     // requires authorized request token
     $token = $this->get_token($request, $consumer, "request");
 
+
     $this->check_signature($request, $consumer, $token);
 
     $new_token = $this->data_store->new_access_token($token, $consumer);
@@ -654,11 +664,11 @@ class OAuthDataStore {/*{{{*/
     // implement me
   }/*}}}*/
 
-  function fetch_request_token($consumer) {/*{{{*/
+  function new_request_token($consumer) {/*{{{*/
     // return a new token attached to this consumer
   }/*}}}*/
 
-  function fetch_access_token($token, $consumer) {/*{{{*/
+  function new_access_token($token, $consumer) {/*{{{*/
     // return a new access token attached to this consumer
     // for the user associated with this token if the request token
     // is authorized
@@ -737,17 +747,22 @@ class SimpleOAuthDataStore extends OAuthDataStore {/*{{{*/
 }/*}}}*/
 
 class OAuthUtil {/*{{{*/
-  public static function urlencodeRFC3986($string) {/*{{{*/
-    return str_replace('+', ' ',
-                       str_replace('%7E', '~', rawurlencode($string)));
-    
+  public static function urlencode_rfc3986($input) {/*{{{*/
+       if (is_array($input)) {
+               return array_map(array('OAuthUtil','urlencode_rfc3986'), $input);
+       } else if (is_scalar($input)) {
+               return str_replace('+', ' ',
+                              str_replace('%7E', '~', rawurlencode($input)));
+       } else {
+               return '';
+       }
   }/*}}}*/
     
 
   // This decode function isn't taking into consideration the above 
   // modifications to the encoding process. However, this method doesn't 
   // seem to be used anywhere so leaving it as is.
-  public static function urldecodeRFC3986($string) {/*{{{*/
+  public static function urldecode_rfc3986($string) {/*{{{*/
     return rawurldecode($string);
   }/*}}}*/
 }/*}}}*/
diff --git a/extlib/PEAR/Exception.php b/extlib/PEAR/Exception.php
new file mode 100644 (file)
index 0000000..b3d75b2
--- /dev/null
@@ -0,0 +1,397 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
+/**
+ * PEAR_Exception
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Hans Lellelid <hans@velum.net>
+ * @author     Bertrand Mansion <bmansion@mamasam.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2008 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id: Exception.php,v 1.29 2008/01/03 20:26:35 cellog Exp $
+ * @link       http://pear.php.net/package/PEAR
+ * @since      File available since Release 1.3.3
+ */
+
+
+/**
+ * Base PEAR_Exception Class
+ *
+ * 1) Features:
+ *
+ * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
+ * - Definable triggers, shot when exceptions occur
+ * - Pretty and informative error messages
+ * - Added more context info available (like class, method or cause)
+ * - cause can be a PEAR_Exception or an array of mixed
+ *   PEAR_Exceptions/PEAR_ErrorStack warnings
+ * - callbacks for specific exception classes and their children
+ *
+ * 2) Ideas:
+ *
+ * - Maybe a way to define a 'template' for the output
+ *
+ * 3) Inherited properties from PHP Exception Class:
+ *
+ * protected $message
+ * protected $code
+ * protected $line
+ * protected $file
+ * private   $trace
+ *
+ * 4) Inherited methods from PHP Exception Class:
+ *
+ * __clone
+ * __construct
+ * getMessage
+ * getCode
+ * getFile
+ * getLine
+ * getTraceSafe
+ * getTraceSafeAsString
+ * __toString
+ *
+ * 5) Usage example
+ *
+ * <code>
+ *  require_once 'PEAR/Exception.php';
+ *
+ *  class Test {
+ *     function foo() {
+ *         throw new PEAR_Exception('Error Message', ERROR_CODE);
+ *     }
+ *  }
+ *
+ *  function myLogger($pear_exception) {
+ *     echo $pear_exception->getMessage();
+ *  }
+ *  // each time a exception is thrown the 'myLogger' will be called
+ *  // (its use is completely optional)
+ *  PEAR_Exception::addObserver('myLogger');
+ *  $test = new Test;
+ *  try {
+ *     $test->foo();
+ *  } catch (PEAR_Exception $e) {
+ *     print $e;
+ *  }
+ * </code>
+ *
+ * @category   pear
+ * @package    PEAR
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Hans Lellelid <hans@velum.net>
+ * @author     Bertrand Mansion <bmansion@mamasam.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2008 The PHP Group
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: 1.7.2
+ * @link       http://pear.php.net/package/PEAR
+ * @since      Class available since Release 1.3.3
+ *
+ */
+class PEAR_Exception extends Exception
+{
+    const OBSERVER_PRINT = -2;
+    const OBSERVER_TRIGGER = -4;
+    const OBSERVER_DIE = -8;
+    protected $cause;
+    private static $_observers = array();
+    private static $_uniqueid = 0;
+    private $_trace;
+
+    /**
+     * Supported signatures:
+     *  - PEAR_Exception(string $message);
+     *  - PEAR_Exception(string $message, int $code);
+     *  - PEAR_Exception(string $message, Exception $cause);
+     *  - PEAR_Exception(string $message, Exception $cause, int $code);
+     *  - PEAR_Exception(string $message, PEAR_Error $cause);
+     *  - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
+     *  - PEAR_Exception(string $message, array $causes);
+     *  - PEAR_Exception(string $message, array $causes, int $code);
+     * @param string exception message
+     * @param int|Exception|PEAR_Error|array|null exception cause
+     * @param int|null exception code or null
+     */
+    public function __construct($message, $p2 = null, $p3 = null)
+    {
+        if (is_int($p2)) {
+            $code = $p2;
+            $this->cause = null;
+        } elseif (is_object($p2) || is_array($p2)) {
+            // using is_object allows both Exception and PEAR_Error
+            if (is_object($p2) && !($p2 instanceof Exception)) {
+                if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
+                    throw new PEAR_Exception('exception cause must be Exception, ' .
+                        'array, or PEAR_Error');
+                }
+            }
+            $code = $p3;
+            if (is_array($p2) && isset($p2['message'])) {
+                // fix potential problem of passing in a single warning
+                $p2 = array($p2);
+            }
+            $this->cause = $p2;
+        } else {
+            $code = null;
+            $this->cause = null;
+        }
+        parent::__construct($message, $code);
+        $this->signal();
+    }
+
+    /**
+     * @param mixed $callback  - A valid php callback, see php func is_callable()
+     *                         - A PEAR_Exception::OBSERVER_* constant
+     *                         - An array(const PEAR_Exception::OBSERVER_*,
+     *                           mixed $options)
+     * @param string $label    The name of the observer. Use this if you want
+     *                         to remove it later with removeObserver()
+     */
+    public static function addObserver($callback, $label = 'default')
+    {
+        self::$_observers[$label] = $callback;
+    }
+
+    public static function removeObserver($label = 'default')
+    {
+        unset(self::$_observers[$label]);
+    }
+
+    /**
+     * @return int unique identifier for an observer
+     */
+    public static function getUniqueId()
+    {
+        return self::$_uniqueid++;
+    }
+
+    private function signal()
+    {
+        foreach (self::$_observers as $func) {
+            if (is_callable($func)) {
+                call_user_func($func, $this);
+                continue;
+            }
+            settype($func, 'array');
+            switch ($func[0]) {
+                case self::OBSERVER_PRINT :
+                    $f = (isset($func[1])) ? $func[1] : '%s';
+                    printf($f, $this->getMessage());
+                    break;
+                case self::OBSERVER_TRIGGER :
+                    $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
+                    trigger_error($this->getMessage(), $f);
+                    break;
+                case self::OBSERVER_DIE :
+                    $f = (isset($func[1])) ? $func[1] : '%s';
+                    die(printf($f, $this->getMessage()));
+                    break;
+                default:
+                    trigger_error('invalid observer type', E_USER_WARNING);
+            }
+        }
+    }
+
+    /**
+     * Return specific error information that can be used for more detailed
+     * error messages or translation.
+     *
+     * This method may be overridden in child exception classes in order
+     * to add functionality not present in PEAR_Exception and is a placeholder
+     * to define API
+     *
+     * The returned array must be an associative array of parameter => value like so:
+     * <pre>
+     * array('name' => $name, 'context' => array(...))
+     * </pre>
+     * @return array
+     */
+    public function getErrorData()
+    {
+        return array();
+    }
+
+    /**
+     * Returns the exception that caused this exception to be thrown
+     * @access public
+     * @return Exception|array The context of the exception
+     */
+    public function getCause()
+    {
+        return $this->cause;
+    }
+
+    /**
+     * Function must be public to call on caused exceptions
+     * @param array
+     */
+    public function getCauseMessage(&$causes)
+    {
+        $trace = $this->getTraceSafe();
+        $cause = array('class'   => get_class($this),
+                       'message' => $this->message,
+                       'file' => 'unknown',
+                       'line' => 'unknown');
+        if (isset($trace[0])) {
+            if (isset($trace[0]['file'])) {
+                $cause['file'] = $trace[0]['file'];
+                $cause['line'] = $trace[0]['line'];
+            }
+        }
+        $causes[] = $cause;
+        if ($this->cause instanceof PEAR_Exception) {
+            $this->cause->getCauseMessage($causes);
+        } elseif ($this->cause instanceof Exception) {
+            $causes[] = array('class'   => get_class($this->cause),
+                              'message' => $this->cause->getMessage(),
+                              'file' => $this->cause->getFile(),
+                              'line' => $this->cause->getLine());
+        } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
+            $causes[] = array('class' => get_class($this->cause),
+                              'message' => $this->cause->getMessage(),
+                              'file' => 'unknown',
+                              'line' => 'unknown');
+        } elseif (is_array($this->cause)) {
+            foreach ($this->cause as $cause) {
+                if ($cause instanceof PEAR_Exception) {
+                    $cause->getCauseMessage($causes);
+                } elseif ($cause instanceof Exception) {
+                    $causes[] = array('class'   => get_class($cause),
+                                   'message' => $cause->getMessage(),
+                                   'file' => $cause->getFile(),
+                                   'line' => $cause->getLine());
+                } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
+                    $causes[] = array('class' => get_class($cause),
+                                      'message' => $cause->getMessage(),
+                                      'file' => 'unknown',
+                                      'line' => 'unknown');
+                } elseif (is_array($cause) && isset($cause['message'])) {
+                    // PEAR_ErrorStack warning
+                    $causes[] = array(
+                        'class' => $cause['package'],
+                        'message' => $cause['message'],
+                        'file' => isset($cause['context']['file']) ?
+                                            $cause['context']['file'] :
+                                            'unknown',
+                        'line' => isset($cause['context']['line']) ?
+                                            $cause['context']['line'] :
+                                            'unknown',
+                    );
+                }
+            }
+        }
+    }
+
+    public function getTraceSafe()
+    {   
+        if (!isset($this->_trace)) {
+            $this->_trace = $this->getTrace();
+            if (empty($this->_trace)) {
+                $backtrace = debug_backtrace();
+                $this->_trace = array($backtrace[count($backtrace)-1]);
+            }
+        }
+        return $this->_trace;
+    }
+
+    public function getErrorClass()
+    {
+        $trace = $this->getTraceSafe();
+        return $trace[0]['class'];
+    }
+
+    public function getErrorMethod()
+    {
+        $trace = $this->getTraceSafe();
+        return $trace[0]['function'];
+    }
+
+    public function __toString()
+    {
+        if (isset($_SERVER['REQUEST_URI'])) {
+            return $this->toHtml();
+        }
+        return $this->toText();
+    }
+
+    public function toHtml()
+    {
+        $trace = $this->getTraceSafe();
+        $causes = array();
+        $this->getCauseMessage($causes);
+        $html =  '<table border="1" cellspacing="0">' . "\n";
+        foreach ($causes as $i => $cause) {
+            $html .= '<tr><td colspan="3" bgcolor="#ff9999">'
+               . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
+               . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
+               . 'on line <b>' . $cause['line'] . '</b>'
+               . "</td></tr>\n";
+        }
+        $html .= '<tr><td colspan="3" bgcolor="#aaaaaa" align="center"><b>Exception trace</b></td></tr>' . "\n"
+               . '<tr><td align="center" bgcolor="#cccccc" width="20"><b>#</b></td>'
+               . '<td align="center" bgcolor="#cccccc"><b>Function</b></td>'
+               . '<td align="center" bgcolor="#cccccc"><b>Location</b></td></tr>' . "\n";
+
+        foreach ($trace as $k => $v) {
+            $html .= '<tr><td align="center">' . $k . '</td>'
+                   . '<td>';
+            if (!empty($v['class'])) {
+                $html .= $v['class'] . $v['type'];
+            }
+            $html .= $v['function'];
+            $args = array();
+            if (!empty($v['args'])) {
+                foreach ($v['args'] as $arg) {
+                    if (is_null($arg)) $args[] = 'null';
+                    elseif (is_array($arg)) $args[] = 'Array';
+                    elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
+                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
+                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
+                    else {
+                        $arg = (string)$arg;
+                        $str = htmlspecialchars(substr($arg, 0, 16));
+                        if (strlen($arg) > 16) $str .= '&hellip;';
+                        $args[] = "'" . $str . "'";
+                    }
+                }
+            }
+            $html .= '(' . implode(', ',$args) . ')'
+                   . '</td>'
+                   . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
+                   . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
+                   . '</td></tr>' . "\n";
+        }
+        $html .= '<tr><td align="center">' . ($k+1) . '</td>'
+               . '<td>{main}</td>'
+               . '<td>&nbsp;</td></tr>' . "\n"
+               . '</table>';
+        return $html;
+    }
+
+    public function toText()
+    {
+        $causes = array();
+        $this->getCauseMessage($causes);
+        $causeMsg = '';
+        foreach ($causes as $i => $cause) {
+            $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
+                   . $cause['message'] . ' in ' . $cause['file']
+                   . ' on line ' . $cause['line'] . "\n";
+        }
+        return $causeMsg . $this->getTraceAsString();
+    }
+}
+
+?>
\ No newline at end of file
index dae1ae75145e127a906b64a488f5248ebae05ae8..e83d08c13e9024fe5482aa7b4540b178754dbad4 100644 (file)
--- a/index.php
+++ b/index.php
@@ -63,7 +63,7 @@ function handleError($error)
 
 function main()
 {
-    global $user, $action;
+    global $user, $action, $config;
 
     if (!_have_config()) {
         $msg = sprintf(_("No configuration file found. Try running ".
@@ -131,11 +131,11 @@ function main()
         if (common_config('db', 'mirror') && $action_obj->isReadOnly()) {
             if (is_array(common_config('db', 'mirror'))) {
                 // "load balancing", ha ha
-                $k = array_rand($config['db']['mirror']);
-
-                $mirror = $config['db']['mirror'][$k];
+                $arr = common_config('db', 'mirror');
+                $k = array_rand($arr);
+                $mirror = $arr[$k];
             } else {
-                $mirror = $config['db']['mirror'];
+                $mirror = common_config('db', 'mirror');
             }
             $config['db']['database'] = $mirror;
         }
diff --git a/js/flowplayer-3.0.5.min.js b/js/flowplayer-3.0.5.min.js
deleted file mode 100644 (file)
index b1c3315..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/** 
- * flowplayer.js 3.0.5. The Flowplayer API
- * 
- * Copyright 2009 Flowplayer Oy
- * 
- * This file is part of Flowplayer.
- * 
- * Flowplayer is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * Flowplayer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Flowplayer.  If not, see <http://www.gnu.org/licenses/>.
- * 
- * Version: 3.0.5 - Tue Feb 03 2009 13:14:17 GMT-0000 (GMT+00:00)
- */
-(function(){function log(args){console.log("$f.fireEvent",[].slice.call(args));}function clone(obj){if(!obj||typeof obj!='object'){return obj;}var temp=new obj.constructor();for(var key in obj){if(obj.hasOwnProperty(key)){temp[key]=clone(obj[key]);}}return temp;}function each(obj,fn){if(!obj){return;}var name,i=0,length=obj.length;if(length===undefined){for(name in obj){if(fn.call(obj[name],name,obj[name])===false){break;}}}else{for(var value=obj[0];i<length&&fn.call(value,i,value)!==false;value=obj[++i]){}}return obj;}function el(id){return document.getElementById(id);}function extend(to,from,skipFuncs){if(to&&from){each(from,function(name,value){if(!skipFuncs||typeof value!='function'){to[name]=value;}});}}function select(query){var index=query.indexOf(".");if(index!=-1){var tag=query.substring(0,index)||"*";var klass=query.substring(index+1,query.length);var els=[];each(document.getElementsByTagName(tag),function(){if(this.className&&this.className.indexOf(klass)!=-1){els.push(this);}});return els;}}function stopEvent(e){e=e||window.event;if(e.preventDefault){e.stopPropagation();e.preventDefault();}else{e.returnValue=false;e.cancelBubble=true;}return false;}function bind(to,evt,fn){to[evt]=to[evt]||[];to[evt].push(fn);}function makeId(){return"_"+(""+Math.random()).substring(2,10);}var Clip=function(json,index,player){var self=this;var cuepoints={};var listeners={};self.index=index;if(typeof json=='string'){json={url:json};}extend(this,json,true);each(("Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop").split(","),function(){var evt="on"+this;if(evt.indexOf("*")!=-1){evt=evt.substring(0,evt.length-1);var before="onBefore"+evt.substring(2);self[before]=function(fn){bind(listeners,before,fn);return self;};}self[evt]=function(fn){bind(listeners,evt,fn);return self;};if(index==-1){if(self[before]){player[before]=self[before];}if(self[evt]){player[evt]=self[evt];}}});extend(this,{onCuepoint:function(points,fn){if(arguments.length==1){cuepoints.embedded=[null,points];return self;}if(typeof points=='number'){points=[points];}var fnId=makeId();cuepoints[fnId]=[points,fn];if(player.isLoaded()){player._api().fp_addCuepoints(points,index,fnId);}return self;},update:function(json){extend(self,json);if(player.isLoaded()){player._api().fp_updateClip(json,index);}var conf=player.getConfig();var clip=(index==-1)?conf.clip:conf.playlist[index];extend(clip,json,true);},_fireEvent:function(evt,arg1,arg2,target){if(evt=='onLoad'){each(cuepoints,function(key,val){if(val[0]){player._api().fp_addCuepoints(val[0],index,key);}});return false;}target=target||self;if(evt=='onCuepoint'){var fn=cuepoints[arg1];if(fn){return fn[1].call(player,target,arg2);}}if(evt=='onStart'||evt=='onUpdate'){extend(target,arg1);if(!target.duration){target.duration=arg1.metaData.duration;}else{target.fullDuration=arg1.metaData.duration;}}var ret=true;each(listeners[evt],function(){ret=this.call(player,target,arg1,arg2);});return ret;}});if(json.onCuepoint){var arg=json.onCuepoint;self.onCuepoint.apply(self,typeof arg=='function'?[arg]:arg);delete json.onCuepoint;}each(json,function(key,val){if(typeof val=='function'){bind(listeners,key,val);delete json[key];}});if(index==-1){player.onCuepoint=this.onCuepoint;}};var Plugin=function(name,json,player,fn){var listeners={};var self=this;var hasMethods=false;if(fn){extend(listeners,fn);}each(json,function(key,val){if(typeof val=='function'){listeners[key]=val;delete json[key];}});extend(this,{animate:function(props,speed,fn){if(!props){return self;}if(typeof speed=='function'){fn=speed;speed=500;}if(typeof props=='string'){var key=props;props={};props[key]=speed;speed=500;}if(fn){var fnId=makeId();listeners[fnId]=fn;}if(speed===undefined){speed=500;}json=player._api().fp_animate(name,props,speed,fnId);return self;},css:function(props,val){if(val!==undefined){var css={};css[props]=val;props=css;}json=player._api().fp_css(name,props);extend(self,json);return self;},show:function(){this.display='block';player._api().fp_showPlugin(name);return self;},hide:function(){this.display='none';player._api().fp_hidePlugin(name);return self;},toggle:function(){this.display=player._api().fp_togglePlugin(name);return self;},fadeTo:function(o,speed,fn){if(typeof speed=='function'){fn=speed;speed=500;}if(fn){var fnId=makeId();listeners[fnId]=fn;}this.display=player._api().fp_fadeTo(name,o,speed,fnId);this.opacity=o;return self;},fadeIn:function(speed,fn){return self.fadeTo(1,speed,fn);},fadeOut:function(speed,fn){return self.fadeTo(0,speed,fn);},getName:function(){return name;},_fireEvent:function(evt,arg){if(evt=='onUpdate'){var json=player._api().fp_getPlugin(name);if(!json){return;}extend(self,json);delete self.methods;if(!hasMethods){each(json.methods,function(){var method=""+this;self[method]=function(){var a=[].slice.call(arguments);var ret=player._api().fp_invoke(name,method,a);return ret=='undefined'?self:ret;};});hasMethods=true;}}var fn=listeners[evt];if(fn){fn.call(self,arg);if(evt.substring(0,1)=="_"){delete listeners[evt];}}}});};function Player(wrapper,params,conf){var
-self=this,api=null,html,commonClip,playlist=[],plugins={},listeners={},playerId,apiId,playerIndex,activeIndex,swfHeight,wrapperHeight;extend(self,{id:function(){return playerId;},isLoaded:function(){return(api!==null);},getParent:function(){return wrapper;},hide:function(all){if(all){wrapper.style.height="0px";}if(api){api.style.height="0px";}return self;},show:function(){wrapper.style.height=wrapperHeight+"px";if(api){api.style.height=swfHeight+"px";}return self;},isHidden:function(){return api&&parseInt(api.style.height,10)===0;},load:function(fn){if(!api&&self._fireEvent("onBeforeLoad")!==false){each(players,function(){this.unload();});html=wrapper.innerHTML;flashembed(wrapper,params,{config:conf});if(fn){fn.cached=true;bind(listeners,"onLoad",fn);}}return self;},unload:function(){try{if(api&&api.fp_isFullscreen()){}}catch(error){return;}if(api&&html.replace(/\s/g,'')!==''&&!api.fp_isFullscreen()&&self._fireEvent("onBeforeUnload")!==false){api.fp_close();wrapper.innerHTML=html;self._fireEvent("onUnload");api=null;}return self;},getClip:function(index){if(index===undefined){index=activeIndex;}return playlist[index];},getCommonClip:function(){return commonClip;},getPlaylist:function(){return playlist;},getPlugin:function(name){var plugin=plugins[name];if(!plugin&&self.isLoaded()){var json=self._api().fp_getPlugin(name);if(json){plugin=new Plugin(name,json,self);plugins[name]=plugin;}}return plugin;},getScreen:function(){return self.getPlugin("screen");},getControls:function(){return self.getPlugin("controls");},getConfig:function(copy){return copy?clone(conf):conf;},getFlashParams:function(){return params;},loadPlugin:function(name,url,props,fn){if(typeof props=='function'){fn=props;props={};}var fnId=fn?makeId():"_";self._api().fp_loadPlugin(name,url,props,fnId);var arg={};arg[fnId]=fn;var p=new Plugin(name,null,self,arg);plugins[name]=p;return p;},getState:function(){return api?api.fp_getState():-1;},play:function(clip){function play(){if(clip!==undefined){self._api().fp_play(clip);}else{self._api().fp_play();}}if(api){play();}else{self.load(function(){play();});}return self;},getVersion:function(){var js="flowplayer.js 3.0.5";if(api){var ver=api.fp_getVersion();ver.push(js);return ver;}return js;},_api:function(){if(!api){throw"Flowplayer "+self.id()+" not loaded. Try moving your call to player's onLoad event";}return api;},_dump:function(){console.log(listeners);},setClip:function(clip){self.setPlaylist([clip]);},getIndex:function(){return playerIndex;}});each(("Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,Fullscreen*,FullscreenExit,Error").split(","),function(){var name="on"+this;if(name.indexOf("*")!=-1){name=name.substring(0,name.length-1);var name2="onBefore"+name.substring(2);self[name2]=function(fn){bind(listeners,name2,fn);return self;};}self[name]=function(fn){bind(listeners,name,fn);return self;};});each(("pause,resume,mute,unmute,stop,toggle,seek,getStatus,getVolume,setVolume,getTime,isPaused,isPlaying,startBuffering,stopBuffering,isFullscreen,reset,close,setPlaylist").split(","),function(){var name=this;self[name]=function(arg){if(!api){return self;}var ret=(arg===undefined)?api["fp_"+name]():api["fp_"+name](arg);return ret=='undefined'?self:ret;};});self._fireEvent=function(evt,arg0,arg1,arg2){if(conf.debug){log(arguments);}if(!api&&evt=='onLoad'&&arg0=='player'){api=api||el(apiId);swfHeight=api.clientHeight;each(playlist,function(){this._fireEvent("onLoad");});each(plugins,function(name,p){p._fireEvent("onUpdate");});commonClip._fireEvent("onLoad");}if(evt=='onLoad'&&arg0!='player'){return;}if(evt=='onError'){if(typeof arg0=='string'||(typeof arg0=='number'&&typeof arg1=='number')){arg0=arg1;arg1=arg2;}}if(evt=='onContextMenu'){each(conf.contextMenu[arg0],function(key,fn){fn.call(self);});return;}if(evt=='onPluginEvent'){var name=arg0.name||arg0;var p=plugins[name];if(p){p._fireEvent("onUpdate",arg0);p._fireEvent(arg1);}return;}if(evt=='onPlaylistReplace'){playlist=[];var index=0;each(arg0,function(){playlist.push(new Clip(this,index++,self));});}var ret=true;if(typeof arg0=='number'&&arg0<playlist.length){activeIndex=arg0;var clip=playlist[arg0];if(clip){ret=clip._fireEvent(evt,arg1,arg2);}if(!clip||ret!==false){ret=commonClip._fireEvent(evt,arg1,arg2,clip);}}var i=0;each(listeners[evt],function(){ret=this.call(self,arg0,arg1);if(this.cached){listeners[evt].splice(i,1);}if(ret===false){return false;}i++;});return ret;};function init(){if($f(wrapper)){$f(wrapper).getParent().innerHTML="";playerIndex=$f(wrapper).getIndex();players[playerIndex]=self;}else{players.push(self);playerIndex=players.length-1;}wrapperHeight=parseInt(wrapper.style.height,10)||wrapper.clientHeight;if(typeof params=='string'){params={src:params};}playerId=wrapper.id||"fp"+makeId();apiId=params.id||playerId+"_api";params.id=apiId;conf.playerId=playerId;if(typeof conf=='string'){conf={clip:{url:conf}};}if(typeof conf.clip=='string'){conf.clip={url:conf.clip};}conf.clip=conf.clip||{};if(wrapper.getAttribute("href",2)&&!conf.clip.url){conf.clip.url=wrapper.getAttribute("href",2);}commonClip=new Clip(conf.clip,-1,self);conf.playlist=conf.playlist||[conf.clip];var index=0;each(conf.playlist,function(){var clip=this;if(typeof clip=='object'&&clip.length){clip=""+clip;}if(typeof clip=='string'){clip={url:clip};}each(conf.clip,function(key,val){if(conf.clip[key]!==undefined&&typeof val!='function'){clip[key]=val;}});conf.playlist[index]=clip;clip=new Clip(clip,index,self);playlist.push(clip);index++;});each(conf,function(key,val){if(typeof val=='function'){bind(listeners,key,val);delete conf[key];}});each(conf.plugins,function(name,val){if(val){plugins[name]=new Plugin(name,val,self);}});if(!conf.plugins||conf.plugins.controls===undefined){plugins.controls=new Plugin("controls",null,self);}params.bgcolor=params.bgcolor||"#000000";params.version=params.version||[9,0];params.expressInstall='http://www.flowplayer.org/swf/expressinstall.swf';function doClick(e){if(!self.isLoaded()&&self._fireEvent("onBeforeClick")!==false){self.load();}return stopEvent(e);}html=wrapper.innerHTML;if(html.replace(/\s/g,'')!==''){if(wrapper.addEventListener){wrapper.addEventListener("click",doClick,false);}else if(wrapper.attachEvent){wrapper.attachEvent("onclick",doClick);}}else{if(wrapper.addEventListener){wrapper.addEventListener("click",stopEvent,false);}self.load();}}if(typeof wrapper=='string'){flashembed.domReady(function(){var node=el(wrapper);if(!node){throw"Flowplayer cannot access element: "+wrapper;}else{wrapper=node;init();}});}else{init();}}var players=[];function Iterator(arr){this.length=arr.length;this.each=function(fn){each(arr,fn);};this.size=function(){return arr.length;};}window.flowplayer=window.$f=function(){var instance=null;var arg=arguments[0];if(!arguments.length){each(players,function(){if(this.isLoaded()){instance=this;return false;}});return instance||players[0];}if(arguments.length==1){if(typeof arg=='number'){return players[arg];}else{if(arg=='*'){return new Iterator(players);}each(players,function(){if(this.id()==arg.id||this.id()==arg||this.getParent()==arg){instance=this;return false;}});return instance;}}if(arguments.length>1){var swf=arguments[1];var conf=(arguments.length==3)?arguments[2]:{};if(typeof arg=='string'){if(arg.indexOf(".")!=-1){var instances=[];each(select(arg),function(){instances.push(new Player(this,clone(swf),clone(conf)));});return new Iterator(instances);}else{var node=el(arg);return new Player(node!==null?node:arg,swf,conf);}}else if(arg){return new Player(arg,swf,conf);}}return null;};extend(window.$f,{fireEvent:function(id,evt,a0,a1,a2){var p=$f(id);return p?p._fireEvent(evt,a0,a1,a2):null;},addPlugin:function(name,fn){Player.prototype[name]=fn;return $f;},each:each,extend:extend});if(document.all){window.onbeforeunload=function(){$f("*").each(function(){if(this.isLoaded()){this.close();}});};}if(typeof jQuery=='function'){jQuery.prototype.flowplayer=function(params,conf){if(!arguments.length||typeof arguments[0]=='number'){var arr=[];this.each(function(){var p=$f(this);if(p){arr.push(p);}});return arguments.length?arr[arguments[0]]:new Iterator(arr);}return this.each(function(){$f(this,clone(params),conf?clone(conf):{});});};}})();(function(){var jQ=typeof jQuery=='function';function isDomReady(){if(domReady.done){return false;}var d=document;if(d&&d.getElementsByTagName&&d.getElementById&&d.body){clearInterval(domReady.timer);domReady.timer=null;for(var i=0;i<domReady.ready.length;i++){domReady.ready[i].call();}domReady.ready=null;domReady.done=true;}}var domReady=jQ?jQuery:function(f){if(domReady.done){return f();}if(domReady.timer){domReady.ready.push(f);}else{domReady.ready=[f];domReady.timer=setInterval(isDomReady,13);}};function extend(to,from){if(from){for(key in from){if(from.hasOwnProperty(key)){to[key]=from[key];}}}return to;}function asString(obj){switch(typeOf(obj)){case'string':obj=obj.replace(new RegExp('(["\\\\])','g'),'\\$1');obj=obj.replace(/^\s?(\d+)%/,"$1pct");return'"'+obj+'"';case'array':return'['+map(obj,function(el){return asString(el);}).join(',')+']';case'function':return'"function()"';case'object':var str=[];for(var prop in obj){if(obj.hasOwnProperty(prop)){str.push('"'+prop+'":'+asString(obj[prop]));}}return'{'+str.join(',')+'}';}return String(obj).replace(/\s/g," ").replace(/\'/g,"\"");}function typeOf(obj){if(obj===null||obj===undefined){return false;}var type=typeof obj;return(type=='object'&&obj.push)?'array':type;}if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};});}function map(arr,func){var newArr=[];for(var i in arr){if(arr.hasOwnProperty(i)){newArr[i]=func(arr[i]);}}return newArr;}function getHTML(p,c){var ie=document.all;var html='<object width="'+p.width+'" height="'+p.height+'"';if(ie&&!p.id){p.id="_"+(""+Math.random()).substring(9);}if(p.id){html+=' id="'+p.id+'"';}if(p.w3c||!ie){html+=' data="'+p.src+'" type="application/x-shockwave-flash"';}else{html+=' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';}html+='>';if(p.w3c||ie){html+='<param name="movie" value="'+p.src+'" />';}var e=extend({},p);e.width=e.height=e.id=e.w3c=e.src=null;for(var k in e){if(e[k]!==null){html+='<param name="'+k+'" value="'+e[k]+'" />';}}var vars="";if(c){for(var key in c){if(c[key]!==null){vars+=key+'='+(typeof c[key]=='object'?asString(c[key]):c[key])+'&';}}vars=vars.substring(0,vars.length-1);html+='<param name="flashvars" value=\''+vars+'\' />';}html+="</object>";return html;}function Flash(root,opts,flashvars){var version=flashembed.getVersion();extend(this,{getContainer:function(){return root;},getConf:function(){return conf;},getVersion:function(){return version;},getFlashvars:function(){return flashvars;},getApi:function(){return root.firstChild;},getHTML:function(){return getHTML(opts,flashvars);}});var required=opts.version;var express=opts.expressInstall;var ok=!required||flashembed.isSupported(required);if(ok){opts.onFail=opts.version=opts.expressInstall=null;root.innerHTML=getHTML(opts,flashvars);}else if(required&&express&&flashembed.isSupported([6,65])){extend(opts,{src:express});flashvars={MMredirectURL:location.href,MMplayerType:'PlugIn',MMdoctitle:document.title};root.innerHTML=getHTML(opts,flashvars);}else{if(root.innerHTML.replace(/\s/g,'')!==''){}else{root.innerHTML="<h2>Flash version "+required+" or greater is required</h2>"+"<h3>"+(version[0]>0?"Your version is "+version:"You have no flash plugin installed")+"</h3>"+"<p>Download latest version from <a href='http://www.adobe.com/go/getflashplayer'>here</a></p>";}}if(!ok&&opts.onFail){var ret=opts.onFail.call(this);if(typeof ret=='string'){root.innerHTML=ret;}}}window.flashembed=function(root,conf,flashvars){if(typeof root=='string'){var el=document.getElementById(root);if(el){root=el;}else{domReady(function(){flashembed(root,conf,flashvars);});return;}}if(!root){return;}var opts={width:'100%',height:'100%',allowfullscreen:true,allowscriptaccess:'always',quality:'high',version:null,onFail:null,expressInstall:null,w3c:false};if(typeof conf=='string'){conf={src:conf};}extend(opts,conf);return new Flash(root,opts,flashvars);};extend(window.flashembed,{getVersion:function(){var version=[0,0];if(navigator.plugins&&typeof navigator.plugins["Shockwave Flash"]=="object"){var _d=navigator.plugins["Shockwave Flash"].description;if(typeof _d!="undefined"){_d=_d.replace(/^.*\s+(\S+\s+\S+$)/,"$1");var _m=parseInt(_d.replace(/^(.*)\..*$/,"$1"),10);var _r=/r/.test(_d)?parseInt(_d.replace(/^.*r(.*)$/,"$1"),10):0;version=[_m,_r];}}else if(window.ActiveXObject){try{var _a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");version=[6,0];_a.AllowScriptAccess="always";}catch(ee){if(version[0]==6){return;}}try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(eee){}}if(typeof _a=="object"){_d=_a.GetVariable("$version");if(typeof _d!="undefined"){_d=_d.replace(/^\S+\s+(.*)$/,"$1").split(",");version=[parseInt(_d[0],10),parseInt(_d[2],10)];}}}return version;},isSupported:function(version){var now=flashembed.getVersion();var ret=(now[0]>version[0])||(now[0]==version[0]&&now[1]>=version[1]);return ret;},domReady:domReady,asString:asString,getHTML:getHTML});if(jQ){jQuery.prototype.flashembed=function(conf,flashvars){return this.each(function(){flashembed(this,conf,flashvars);});};}})();
\ No newline at end of file
diff --git a/js/jquery.simplemodal-1.2.2.pack.js b/js/jquery.simplemodal-1.2.2.pack.js
deleted file mode 100644 (file)
index b5ad5c2..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * SimpleModal 1.2.2 - jQuery Plugin
- * http://www.ericmmartin.com/projects/simplemodal/
- * Copyright (c) 2008 Eric Martin
- * Dual licensed under the MIT and GPL licenses
- * Revision: $Id: jquery.simplemodal.js 181 2008-12-16 16:51:44Z emartin24 $
- */
-eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(g($){m f=$.Q.1Q&&1a($.Q.1D)==6&&!10[\'2g\'],1f=$.Q.1Q&&!$.2a,w=[];$.y=g(a,b){I $.y.12.1n(a,b)};$.y.D=g(){$.y.12.D()};$.1P.y=g(a){I $.y.12.1n(3,a)};$.y.1O={V:29,1J:\'r-H\',1B:{},1z:\'r-n\',20:{},1Z:{},v:2t,D:1o,1T:\'<a 2j="2h" 2f="2e"></a>\',X:\'r-D\',l:F,1g:K,1e:F,1d:F,1c:F};$.y.12={7:F,4:{},1n:g(a,b){8(3.4.j){I K}3.7=$.U({},$.y.1O,b);3.v=3.7.v;3.1w=K;8(J a==\'27\'){a=a 25 1A?a:$(a);8(a.1v().1v().23()>0){3.4.T=a.1v();8(!3.7.1g){3.4.21=a.2x(1o)}}}q 8(J a==\'2w\'||J a==\'1r\'){a=$(\'<1q/>\').2s(a)}q{2r(\'2q 2p: 2o j 2l: \'+J a);I K}3.4.j=a.11(\'r-j\').E(3.7.1Z);a=F;3.1S();3.1R();8($.1m(3.7.1d)){3.7.1d.1l(3,[3.4])}I 3},1S:g(){w=3.1k();8(f){3.4.x=$(\'<x 2d="2c:K;"/>\').E($.U(3.7.2b,{1j:\'1i\',V:0,l:\'1h\',A:w[0],z:w[1],v:3.7.v,L:0,B:0})).O(\'u\')}3.4.H=$(\'<1q/>\').1N(\'1M\',3.7.1J).11(\'r-H\').E($.U(3.7.1B,{1j:\'1i\',V:3.7.V/1b,A:w[0],z:w[1],l:\'1h\',B:0,L:0,v:3.7.v+1})).O(\'u\');3.4.n=$(\'<1q/>\').1N(\'1M\',3.7.1z).11(\'r-n\').E($.U(3.7.20,{1j:\'1i\',l:\'1h\',v:3.7.v+2})).1K(3.7.D?$(3.7.1T).11(3.7.X):\'\').O(\'u\');3.19();8(f||1f){3.18()}3.4.n.1K(3.4.j.1I())},1H:g(){m a=3;$(\'.\'+3.7.X).1G(\'1L.r\',g(e){e.28();a.D()});$(10).1G(\'1F.r\',g(){w=a.1k();a.19();8(f||1f){a.18()}q{a.4.x&&a.4.x.E({A:w[0],z:w[1]});a.4.H.E({A:w[0],z:w[1]})}})},1E:g(){$(\'.\'+3.7.X).1C(\'1L.r\');$(10).1C(\'1F.r\')},18:g(){m p=3.7.l;$.26([3.4.x||F,3.4.H,3.4.n],g(i,e){8(e){m a=\'k.u.17\',N=\'k.u.1W\',16=\'k.u.24\',S=\'k.u.1y\',R=\'k.u.1x\',15=\'k.u.22\',1t=\'k.P.17\',1s=\'k.P.1W\',C=\'k.P.1y\',G=\'k.P.1x\',s=e[0].2v;s.l=\'2u\';8(i<2){s.14(\'A\');s.14(\'z\');s.Z(\'A\',\'\'+16+\' > \'+a+\' ? \'+16+\' : \'+a+\' + "o"\');s.Z(\'z\',\'\'+15+\' > \'+N+\' ? \'+15+\' : \'+N+\' + "o"\')}q{m b,W;8(p&&p.1Y==1X){8(p[0]){m c=J p[0]==\'1r\'?p[0].1V():p[0].13(/o/,\'\');b=c.1U(\'%\')==-1?c+\' + (t = \'+G+\' ? \'+G+\' : \'+R+\') + "o"\':1a(c.13(/%/,\'\'))+\' * ((\'+1t+\' || \'+a+\') / 1b) + (t = \'+G+\' ? \'+G+\' : \'+R+\') + "o"\'}8(p[1]){m d=J p[1]==\'1r\'?p[1].1V():p[1].13(/o/,\'\');W=d.1U(\'%\')==-1?d+\' + (t = \'+C+\' ? \'+C+\' : \'+S+\') + "o"\':1a(d.13(/%/,\'\'))+\' * ((\'+1s+\' || \'+N+\') / 1b) + (t = \'+C+\' ? \'+C+\' : \'+S+\') + "o"\'}}q{b=\'(\'+1t+\' || \'+a+\') / 2 - (3.2n / 2) + (t = \'+G+\' ? \'+G+\' : \'+R+\') + "o"\';W=\'(\'+1s+\' || \'+N+\') / 2 - (3.2m / 2) + (t = \'+C+\' ? \'+C+\' : \'+S+\') + "o"\'}s.14(\'L\');s.14(\'B\');s.Z(\'L\',b);s.Z(\'B\',W)}}})},1k:g(){m a=$(10);m h=$.Q.2k&&$.Q.1D>\'9.5\'&&$.1P.2i<=\'1.2.6\'?k.P[\'17\']:a.A();I[h,a.z()]},19:g(){m a,B,1u=(w[0]/2)-((3.4.n.A()||3.4.j.A())/2),1p=(w[1]/2)-((3.4.n.z()||3.4.j.z())/2);8(3.7.l&&3.7.l.1Y==1X){a=3.7.l[0]||1u;B=3.7.l[1]||1p}q{a=1u;B=1p}3.4.n.E({B:B,L:a})},1R:g(){3.4.x&&3.4.x.Y();8($.1m(3.7.1e)){3.7.1e.1l(3,[3.4])}q{3.4.H.Y();3.4.n.Y();3.4.j.Y()}3.1H()},D:g(){8(!3.4.j){I K}8($.1m(3.7.1c)&&!3.1w){3.1w=1o;3.7.1c.1l(3,[3.4])}q{8(3.4.T){8(3.7.1g){3.4.j.1I().O(3.4.T)}q{3.4.j.M();3.4.21.O(3.4.T)}}q{3.4.j.M()}3.4.n.M();3.4.H.M();3.4.x&&3.4.x.M();3.4={}}3.1E()}}})(1A);',62,158,'|||this|dialog|||opts|if||||||||function|||data|document|position|var|container|px||else|simplemodal|||body|zIndex||iframe|modal|width|height|left|sl|close|css|null|st|overlay|return|typeof|false|top|remove|bcw|appendTo|documentElement|browser|bst|bsl|parentNode|extend|opacity|le|closeClass|show|setExpression|window|addClass|impl|replace|removeExpression|bsw|bsh|clientHeight|fixIE|setPosition|parseInt|100|onClose|onShow|onOpen|ieQuirks|persist|fixed|none|display|getDimensions|apply|isFunction|init|true|vCenter|div|number|cw|ch|hCenter|parent|occb|scrollTop|scrollLeft|containerId|jQuery|overlayCss|unbind|version|unbindEvents|resize|bind|bindEvents|hide|overlayId|append|click|id|attr|defaults|fn|msie|open|create|closeHTML|indexOf|toString|clientWidth|Array|constructor|dataCss|containerCss|orig|scrollWidth|size|scrollHeight|instanceof|each|object|preventDefault|50|boxModel|iframeCss|javascript|src|Close|title|XMLHttpRequest|modalCloseImg|jquery|class|opera|type|offsetWidth|offsetHeight|Unsupported|Error|SimpleModal|alert|html|1000|absolute|style|string|clone'.split('|'),0,{}))
\ No newline at end of file
diff --git a/js/video.js b/js/video.js
deleted file mode 100644 (file)
index 936a631..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-$('document').ready(function() {
-    $('a.media, a.mediamp3').append(' <sup>[PLAY]</sup>');
-    $('a.mediamp3').html('').css('display', 'block').css('width', '224px').css('height','24px').flowplayer('../bin/flowplayer-3.0.5.swf');
-    $('a.media').click(function() {
-        $('<a id="p1i"></a>').attr('href', $(this).attr('href')).flowplayer('../bin/flowplayer-3.0.5.swf').modal({'closeHTML':'<a class="modalCloseImg" title="Close"><img src="x.png" /></a>'});
-        return false;
-    });
-});
-
index ee23059115cba0629f74934420cbc90f0baf9b39..5020b92b0936a129901178015002e9c60179b3a1 100644 (file)
@@ -156,15 +156,10 @@ class Action extends HTMLOutputter // lawsuit
     {
         if (Event::handle('StartShowStyles', array($this))) {
             if (Event::handle('StartShowLaconicaStyles', array($this))) {
-
                 $this->element('link', array('rel' => 'stylesheet',
                                              'type' => 'text/css',
                                              'href' => theme_path('css/display.css', 'base') . '?version=' . LACONICA_VERSION,
                                              'media' => 'screen, projection, tv'));
-                $this->element('link', array('rel' => 'stylesheet',
-                                             'type' => 'text/css',
-                                             'href' => theme_path('css/modal.css', 'base') . '?version=' . LACONICA_VERSION,
-                                             'media' => 'screen, projection, tv'));
                 $this->element('link', array('rel' => 'stylesheet',
                                              'type' => 'text/css',
                                              'href' => theme_path('css/display.css', null) . '?version=' . LACONICA_VERSION,
@@ -215,11 +210,6 @@ class Action extends HTMLOutputter // lawsuit
                 $this->element('script', array('type' => 'text/javascript',
                                                'src' => common_path('js/jquery.form.js')),
                                ' ');
-
-                $this->element('script', array('type' => 'text/javascript',
-                                               'src' => common_path('js/jquery.simplemodal-1.2.2.pack.js')),
-                               ' ');
-
                 Event::handle('EndShowJQueryScripts', array($this));
             }
             if (Event::handle('StartShowLaconicaScripts', array($this))) {
@@ -232,14 +222,6 @@ class Action extends HTMLOutputter // lawsuit
                 // Frame-busting code to avoid clickjacking attacks.
                 $this->element('script', array('type' => 'text/javascript'),
                                'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }');
-
-                $this->element('script', array('type' => 'text/javascript',
-                                               'src' => common_path('js/flowplayer-3.0.5.min.js')),
-                               ' ');
-
-                $this->element('script', array('type' => 'text/javascript',
-                                               'src' => common_path('js/video.js')),
-                               ' ');
                 Event::handle('EndShowLaconicaScripts', array($this));
             }
             Event::handle('EndShowScripts', array($this));
index 0355d01e3abe3a8e643163da7759f6d663e57182..44ed270d78c1bf715394c84015f5442914265c49 100644 (file)
@@ -19,7 +19,7 @@
 
 if (!defined('LACONICA')) { exit(1); }
 
-define('LACONICA_VERSION', '0.7.1');
+define('LACONICA_VERSION', '0.7.2');
 
 define('AVATAR_PROFILE_SIZE', 96);
 define('AVATAR_STREAM_SIZE', 48);
@@ -73,6 +73,7 @@ $config =
               'theme' => 'default',
               'path' => $_path,
               'logfile' => null,
+              'logo' => null,
               'logdebug' => false,
               'fancy' => false,
               'locale_path' => INSTALLDIR.'/locale',
@@ -85,7 +86,8 @@ $config =
               'broughtbyurl' => null,
               'closed' => false,
               'inviteonly' => false,
-              'private' => false),
+              'private' => false,
+              'dupelimit' => 60), # default for same person saying the same thing
         'syslog' =>
         array('appname' => 'laconica', # for syslog
               'priority' => 'debug'), # XXX: currently ignored
@@ -139,7 +141,8 @@ $config =
               'user' => false,
               'group' => false),
         'integration' =>
-        array('source' => 'Laconica'), # source attribute for Twitter
+        array('source' => 'Laconica', # source attribute for Twitter
+              'taguri' => $_server.',2009'), # base for tag URIs
         'memcached' =>
         array('enabled' => false,
               'server' => 'localhost',
index ed786f4ffc5e1fd7cfc1d304963a2fe775b1cf19..996d23bfc98542ac5da73cda76f14dad61b3af31 100644 (file)
@@ -410,8 +410,8 @@ function jabber_broadcast_notice($notice)
                  "ON $UT.id = notice_inbox.user_id " .
                  'WHERE notice_inbox.notice_id = ' . $notice->id . ' ' .
                  'AND notice_inbox.source = 2 ' .
-                 'AND user.jabber is not null ' .
-                 'AND user.jabbernotify = 1 ');
+                 "AND $UT.jabber is not null " .
+                 "AND $UT.jabbernotify = 1 ");
 
     while ($user->fetch()) {
         if (!array_key_exists($user->id, $sent_to)) {
index 171e1db4d62ee7e1285e09f1705a789147d35bd9..0cdcf0c51642bfec6081c7e8ed8a5d16313c29bc 100644 (file)
@@ -22,7 +22,7 @@
  * @category  Search
  * @package   Laconica
  * @author    Zach Copley <zach@controlyourself.ca>
- * @copyright 2008-2009 Control Yourself, Inc.
+ * @copyright 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/
  */
@@ -62,14 +62,19 @@ class JSONSearchResultsList
     /**
      * constructor
      *
-     * @param Notice $notice stream of notices from DB_DataObject
+     * @param Notice $notice   stream of notices from DB_DataObject
+     * @param string $query    the original search query
+     * @param int    $rpp      the number of results to display per page
+     * @param int    $page     a page offset
+     * @param int    $since_id only display notices newer than this
      */
 
     function __construct($notice, $query, $rpp, $page, $since_id = 0)
     {
         $this->notice           = $notice;
         $this->query            = urlencode($query);
-        $this->results_per_page = $this->rpp = $rpp;
+        $this->results_per_page = $rpp;
+        $this->rpp              = $rpp;
         $this->page             = $page;
         $this->since_id         = $since_id;
         $this->results          = array();
@@ -78,7 +83,7 @@ class JSONSearchResultsList
     /**
      * show the list of search results
      *
-     * @return int count of the search results listed.
+     * @return int $count of the search results listed.
      */
 
     function show()
@@ -103,7 +108,7 @@ class JSONSearchResultsList
             array_push($this->results, $item);
         }
 
-        $time_end = microtime(true);
+        $time_end           = microtime(true);
         $this->completed_in = $time_end - $time_start;
 
         // Set other attrs
@@ -197,7 +202,7 @@ class ResultItem
 
     function buildResult()
     {
-        $this->text = $this->notice->content;
+        $this->text      = $this->notice->content;
         $replier_profile = null;
 
         if ($this->notice->reply_to) {
@@ -209,18 +214,21 @@ class ResultItem
 
         $this->to_user_id = ($replier_profile) ?
             intval($replier_profile->id) : null;
-        $this->to_user = ($replier_profile) ?
+        $this->to_user    = ($replier_profile) ?
             $replier_profile->nickname : null;
-        $this->from_user = $this->profile->nickname;
-        $this->id = $this->notice->id;
+
+        $this->from_user    = $this->profile->nickname;
+        $this->id           = $this->notice->id;
         $this->from_user_id = $this->profile->id;
 
         $user = User::staticGet('id', $this->profile->id);
+
         $this->iso_language_code = $this->user->language;
 
         $this->source = $this->getSourceLink($this->notice->source);
 
         $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE);
+
         $this->profile_image_url = ($avatar) ?
             $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE);
 
@@ -233,27 +241,30 @@ class ResultItem
      * Either the name (and link) of the API client that posted the notice,
      * or one of other other channels.
      *
-     * @return string the source of the Notice
+     * @param string $source the source of the Notice
+     *
+     * @return string a fully rendered source of the Notice
      */
 
-     function getSourceLink($source)
-     {
-         $source_name = _($source);
-         switch ($source) {
-          case 'web':
-          case 'xmpp':
-          case 'mail':
-          case 'omb':
-          case 'api':
-             break;
-          default:
-             $ns = Notice_source::staticGet($source);
-             if ($ns) {
-                 $source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>';
-             }
-             break;
-         }
-         return $source_name;
-     }
+    function getSourceLink($source)
+    {
+        $source_name = _($source);
+        switch ($source) {
+        case 'web':
+        case 'xmpp':
+        case 'mail':
+        case 'omb':
+        case 'api':
+            break;
+        default:
+            $ns = Notice_source::staticGet($source);
+            if ($ns) {
+                $source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>';
+            }
+            break;
+        }
+
+        return $source_name;
+    }
 
 }
index 9fa86de5cc81a40643340db104248c79adb40c8e..dde7571ebe68b8eb4439ad81f347c47d951a166f 100644 (file)
@@ -50,10 +50,9 @@ function mail_backend()
     static $backend = null;
 
     if (!$backend) {
-        global $config;
-        $backend = Mail::factory($config['mail']['backend'],
-                                 ($config['mail']['params']) ?
-                                 $config['mail']['params'] :
+        $backend = Mail::factory(common_config('mail', 'backend'),
+                                 (common_config('mail', 'params')) ?
+                                 common_config('mail', 'params') :
                                  array());
         if (PEAR::isError($backend)) {
             common_server_error($backend->getMessage(), 500);
index 9af05ea2de82f663e2689ceae7bcf724e3e3ad24..183164e170a86ae2353d4c3d6266d5a6c1024ebc 100644 (file)
@@ -54,16 +54,21 @@ class LaconicaOAuthDataStore extends OAuthDataStore
         }
     }
 
+    // http://oauth.net/core/1.0/#nonce
+    // "The Consumer SHALL then generate a Nonce value that is unique for
+    // all requests with that timestamp."
+
+    // XXX: It's not clear why the token is here
+
     function lookup_nonce($consumer, $token, $nonce, $timestamp)
     {
         $n = new Nonce();
         $n->consumer_key = $consumer->key;
-        $n->tok = $token->key;
+        $n->ts = $timestamp;
         $n->nonce = $nonce;
         if ($n->find(true)) {
             return true;
         } else {
-            $n->ts = $timestamp;
             $n->created = DB_DataObject_Cast::dateTime();
             $n->insert();
             return false;
index befcf4666a85bef45296aad94b84e8b1303e614b..e8e1acc4139b6aa59fe916f1938e6817986ffa19 100644 (file)
@@ -251,7 +251,6 @@ function omb_broadcast_profile($profile)
 
 function omb_update_profile($profile, $remote_profile, $subscription)
 {
-    global $config; # for license URL
     $user = User::staticGet($profile->id);
     $con = omb_oauth_consumer();
     $token = new OAuthToken($subscription->token, $subscription->secret);
@@ -295,7 +294,7 @@ function omb_update_profile($profile, $remote_profile, $subscription)
 
     common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__);
 
-    if (empty($result) || $result) {
+    if (empty($result) || !$result) {
         common_debug("Unable to contact " . $req->get_normalized_http_url());
     } else if ($result->status == 403) { # not authorized, don't send again
         common_debug('403 result, deleting subscription', __FILE__);
@@ -306,7 +305,7 @@ function omb_update_profile($profile, $remote_profile, $subscription)
         return false;
     } else { # success!
         parse_str($result->body, $return);
-        if ($return['omb_version'] == OMB_VERSION_01) {
+        if (isset($return['omb_version']) && $return['omb_version'] === OMB_VERSION_01) {
             return true;
         } else {
             return false;
index 5c3d460dafdb7c2e7814663953f54eb7c5788f8f..3aa488b6d422df9f7d5240da0a9d016fdecdbf91 100644 (file)
@@ -160,7 +160,7 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
         $auth_request->addExtension($sreg_request);
     }
 
-    $trust_root = common_local_url('public');
+    $trust_root = common_path('');
     $process_url = common_local_url($returnto);
 
     if ($auth_request->shouldSendRedirect()) {
index c2040fbc232a980fa2d926d926abbd6850b67614..766189ab4cbb7812f52e960fec7f9c2914c511c3 100644 (file)
@@ -89,6 +89,7 @@ class ProfileList extends Widget
                                              'id' => 'profile-' . $this->profile->id));
 
         $user = common_current_user();
+        $is_own = !is_null($user) && isset($this->user) && ($user->id === $this->user->id);
 
         $this->out->elementStart('div', 'entity_profile vcard');
 
@@ -102,13 +103,13 @@ class ProfileList extends Widget
                                          'alt' =>
                                          ($this->profile->fullname) ? $this->profile->fullname :
                                          $this->profile->nickname));
-        $hasFN = ($this->profile->fullname) ? 'nickname' : 'fn nickname';
+        $hasFN = ($this->profile->fullname !== '') ? 'nickname' : 'fn nickname';
         $this->out->elementStart('span', $hasFN);
         $this->out->raw($this->highlight($this->profile->nickname));
         $this->out->elementEnd('span');
         $this->out->elementEnd('a');
 
-        if ($this->profile->fullname) {
+        if ($this->profile->fullname !== '') {
             $this->out->elementStart('dl', 'entity_fn');
             $this->out->element('dt', null, 'Full name');
             $this->out->elementStart('dd');
@@ -118,7 +119,7 @@ class ProfileList extends Widget
             $this->out->elementEnd('dd');
             $this->out->elementEnd('dl');
         }
-        if ($this->profile->location) {
+        if ($this->profile->location !== '') {
             $this->out->elementStart('dl', 'entity_location');
             $this->out->element('dt', null, _('Location'));
             $this->out->elementStart('dd', 'label');
@@ -126,7 +127,7 @@ class ProfileList extends Widget
             $this->out->elementEnd('dd');
             $this->out->elementEnd('dl');
         }
-        if ($this->profile->homepage) {
+        if ($this->profile->homepage !== '') {
             $this->out->elementStart('dl', 'entity_url');
             $this->out->element('dt', null, _('URL'));
             $this->out->elementStart('dd');
@@ -137,7 +138,7 @@ class ProfileList extends Widget
             $this->out->elementEnd('dd');
             $this->out->elementEnd('dl');
         }
-        if ($this->profile->bio) {
+        if ($this->profile->bio !== '') {
             $this->out->elementStart('dl', 'entity_note');
             $this->out->element('dt', null, _('Note'));
             $this->out->elementStart('dd', 'note');
@@ -154,7 +155,7 @@ class ProfileList extends Widget
 
             $this->out->elementStart('dl', 'entity_tags');
             $this->out->elementStart('dt');
-            if ($user->id == $this->owner->id) {
+            if ($is_own) {
                 $this->out->element('a', array('href' => common_local_url('tagother',
                                                                           array('id' => $this->profile->id))),
                                     _('Tags'));
@@ -183,7 +184,7 @@ class ProfileList extends Widget
             $this->out->elementEnd('dl');
         }
 
-        if ($user && $user->id == $this->owner->id) {
+        if ($is_own) {
             $this->showOwnerControls($this->profile);
         }
 
@@ -193,11 +194,11 @@ class ProfileList extends Widget
 
         $this->out->elementStart('ul');
 
-        if ($user && $user->id != $this->profile->id) {
+        if (!$is_own) {
             # XXX: special-case for user looking at own
             # subscriptions page
             $this->out->elementStart('li', 'entity_subscribe');
-            if ($user->isSubscribed($this->profile)) {
+            if (!is_null($user) && $user->isSubscribed($this->profile)) {
                 $usf = new UnsubscribeForm($this->out, $this->profile);
                 $usf->show();
             } else {
@@ -206,9 +207,6 @@ class ProfileList extends Widget
             }
             $this->out->elementEnd('li');
             $this->out->elementStart('li', 'entity_block');
-            if ($user && $user->id == $this->owner->id) {
-                $this->showBlockForm();
-            }
             $this->out->elementEnd('li');
         }
 
index aab28672109476f575705ca3cba7c9925417e003..d0b56e88b4fb2cddb7b5aa3be6320de85a60d304 100644 (file)
@@ -49,6 +49,9 @@ class Router
 {
     var $m = null;
     static $inst = null;
+    static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
+                         'postnotice', 'updateprofile', 'finishremotesubscribe',
+                         'finishopenidlogin', 'finishaddopenid');
 
     static function get()
     {
@@ -98,7 +101,7 @@ class Router
         $main = array('login', 'logout', 'register', 'subscribe',
                       'unsubscribe', 'confirmaddress', 'recoverpassword',
                       'invite', 'favor', 'disfavor', 'sup',
-                      'block');
+                      'block', 'subedit');
 
         foreach ($main as $a) {
             $m->connect('main/'.$a, array('action' => $a));
@@ -118,8 +121,7 @@ class Router
         $m->connect('main/remote', array('action' => 'remotesubscribe'));
         $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
 
-        foreach (array('requesttoken', 'accesstoken', 'userauthorization',
-                    'postnotice', 'updateprofile', 'finishremotesubscribe') as $action) {
+        foreach (Router::$bare as $action) {
             $m->connect('index.php?action=' . $action, array('action' => $action));
         }
 
@@ -230,7 +232,7 @@ class Router
         $m->connect('api/statuses/:method/:argument',
                     array('action' => 'api',
                           'apiaction' => 'statuses'),
-                    array('method' => '(user_timeline|friends_timeline|show|destroy|friends|followers)'));
+                    array('method' => '(user_timeline|friends_timeline|replies|show|destroy|friends|followers)'));
 
         // users
 
@@ -261,7 +263,7 @@ class Router
         }
 
         foreach (array('xml', 'json', 'rss', 'atom') as $e) {
-            $m->connect('api/direct_message/sent.'.$e,
+            $m->connect('api/direct_messages/sent.'.$e,
                         array('action' => 'api',
                         'apiaction' => 'direct_messages',
                         'method' => 'sent.'.$e));
@@ -281,7 +283,7 @@ class Router
         $m->connect('api/friendships/:method',
                     array('action' => 'api',
                           'apiaction' => 'friendships'),
-                    array('method' => 'exists(\.(xml|json|rss|atom))'));
+                    array('method' => 'exists(\.(xml|json))'));
 
         // Social graph
 
@@ -357,7 +359,6 @@ class Router
                     array('action' => 'api',
                           'apiaction' => 'laconica'));
 
-
         // search
         $m->connect('api/search.atom', array('action' => 'twitapisearchatom'));
         $m->connect('api/search.json', array('action' => 'twitapisearchjson'));
@@ -365,7 +366,7 @@ class Router
 
         // user stuff
 
-      foreach (array('subscriptions', 'subscribers',
+        foreach (array('subscriptions', 'subscribers',
                        'nudge', 'xrds', 'all', 'foaf',
                        'replies', 'inbox', 'outbox', 'microsummary') as $a) {
             $m->connect(':nickname/'.$a,
index df6876445992b8c3838f5c3a51d660dc746805b4..c762db16f049a7f84f3bee989c78d41148b8d2f2 100644 (file)
@@ -110,8 +110,6 @@ class SearchAction extends Action
 
     function showForm($error=null)
     {
-        global $config;
-
         $q = $this->trimmed('q');
         $page = $this->trimmed('page', 1);
         $this->elementStart('form', array('method' => 'get',
@@ -122,7 +120,7 @@ class SearchAction extends Action
         $this->element('legend', null, _('Search site'));
         $this->elementStart('ul', 'form_data');
         $this->elementStart('li');
-        if (!isset($config['site']['fancy']) || !$config['site']['fancy']) {
+        if (!common_config('site', 'fancy')) {
             $this->hidden('action', $this->trimmed('action'));
         }
         $this->input('q', 'Keyword(s)', $q);
index 5fd8a72a25dbc5b6d0be9065e1faa01539b0fb62..31c3ea0b568e8ed62b1e0524cec920ba621ef958 100644 (file)
@@ -98,7 +98,7 @@ class SubGroupNav extends Widget
                                      $this->user->nickname),
                              $action == 'usergroups',
                              'nav_usergroups');
-        if ($this->user->id == $cur->id) {
+        if (!is_null($cur) && $this->user->id === $cur->id) {
             $this->out->menuItem(common_local_url('invite'),
                                  _('Invite'),
                                  sprintf(_('Invite friends and colleagues to join you on %s'),
index 4bd59ac7971cfba0384a70c3e00f5c61c6166143..1a2ce00140f6c0e092f1173779dc009d74651ec8 100644 (file)
@@ -51,7 +51,7 @@ class TopPostersSection extends ProfileSection
         $qry = 'SELECT profile.*, count(*) as value ' .
           'FROM profile JOIN notice ON profile.id = notice.profile_id ' .
           (common_config('public', 'localonly') ? 'WHERE is_local = 1 ' : '') .
-          'GROUP BY profile.id ' .
+          'GROUP BY profile.id,nickname,fullname,profileurl,homepage,bio,location,profile.created,profile.modified,textsearch ' .
           'ORDER BY value DESC ';
 
         $limit = PROFILES_PER_SECTION;
index 8a54afb9c40730c836a96a1cbaad5fc1578b912a..db2092210bd6520e8a88255616b7cece305671e0 100644 (file)
@@ -224,7 +224,6 @@ function is_twitter_bound($notice, $flink) {
 
 function broadcast_twitter($notice)
 {
-    global $config;
     $success = true;
 
     $flink = Foreign_link::getByUserID($notice->profile_id,
@@ -232,7 +231,7 @@ function broadcast_twitter($notice)
 
     // XXX: Not sure WHERE to check whether a notice should go to
     // Twitter. Should we even put in the queue if it shouldn't? --Zach
-    if (is_twitter_bound($notice, $flink)) {
+    if (!is_null($flink) && is_twitter_bound($notice, $flink)) {
 
         $fuser = $flink->getForeignUser();
         $twitter_user = $fuser->nickname;
@@ -248,7 +247,7 @@ function broadcast_twitter($notice)
             CURLOPT_POSTFIELDS     =>
                 array(
                         'status' => $statustxt,
-                        'source' => $config['integration']['source']
+                        'source' => common_config('integration', 'source')
                      ),
             CURLOPT_RETURNTRANSFER => true,
             CURLOPT_FAILONERROR    => true,
index 1de169a0b139255a8b7a02abaf5f0b2d3d1bfd17..e7239acd5e4e1d6c84a790ec2d30fac5d5ab6c18 100644 (file)
@@ -127,8 +127,6 @@ class TwitterapiAction extends Action
     {
 
         $profile = $notice->getProfile();
-
-        $server = common_config('site', 'server');
         $entry = array();
 
         # We trim() to avoid extraneous whitespace in the output
@@ -137,8 +135,12 @@ class TwitterapiAction extends Action
         $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
         $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id));
         $entry['published'] = common_date_iso8601($notice->created);
-        $entry['id'] = "tag:$server,2008:$entry[link]";
+
+        $taguribase = common_config('integration', 'taguri');
+        $entry['id'] = "tag:$taguribase:$entry[link]";
+
         $entry['updated'] = $entry['published'];
+        $entry['author'] = $profile->getBestName();
 
         # RSS Item specific
         $entry['description'] = $entry['content'];
@@ -151,7 +153,6 @@ class TwitterapiAction extends Action
     function twitter_rss_dmsg_array($message)
     {
 
-        $server = common_config('site', 'server');
         $entry = array();
 
         $entry['title'] = sprintf('Message from %s to %s',
@@ -160,8 +161,12 @@ class TwitterapiAction extends Action
         $entry['content'] = common_xml_safe_str(trim($message->content));
         $entry['link'] = common_local_url('showmessage', array('message' => $message->id));
         $entry['published'] = common_date_iso8601($message->created);
-        $entry['id'] = "tag:$server,2008:$entry[link]";
+
+        $taguribase = common_config('integration', 'taguri');
+
+        $entry['id'] = "tag:$taguribase,:$entry[link]";
         $entry['updated'] = $entry['published'];
+        $entry['author'] = $message->getFrom()->getBestName();
 
         # RSS Item specific
         $entry['description'] = $entry['content'];
@@ -242,6 +247,9 @@ class TwitterapiAction extends Action
         $this->element('published', null, $entry['published']);
         $this->element('updated', null, $entry['updated']);
         $this->element('link', array('href' => $entry['link'], 'rel' => 'alternate', 'type' => 'text/html'), null);
+        $this->elementStart('author');
+        $this->element('name', null, $entry['author']);
+        $this->elementEnd('author');
         $this->elementEnd('entry');
     }
 
@@ -358,7 +366,7 @@ class TwitterapiAction extends Action
         $this->end_twitter_rss();
     }
 
-    function show_atom_timeline($notice, $title, $id, $link, $subtitle=null, $suplink=null)
+    function show_atom_timeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null)
     {
 
         $this->init_document('atom');
@@ -366,12 +374,20 @@ class TwitterapiAction extends Action
         $this->element('title', null, $title);
         $this->element('id', null, $id);
         $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
+
         if (!is_null($suplink)) {
             # For FriendFeed's SUP protocol
             $this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
                                          'href' => $suplink,
                                          'type' => 'application/json'));
         }
+
+        if (!is_null($selfuri)) {
+            $this->element('link', array('href' => $selfuri, 
+                'rel' => 'self', 'type' => 'application/atom+xml'), null);
+        }
+
+        $this->element('updated', null, common_date_iso8601('now'));
         $this->element('subtitle', null, $subtitle);
 
         if (is_array($notice)) {
@@ -634,79 +650,4 @@ class TwitterapiAction extends Action
         return $source_name;
     }
 
-    function show_extended_profile($user, $apidata)
-    {
-
-        $this->auth_user = $apidata['user'];
-
-        $profile = $user->getProfile();
-
-        if (!$profile) {
-            common_server_error(_('User has no profile.'));
-            return;
-        }
-
-        $twitter_user = $this->twitter_user_array($profile, true);
-
-        // Add in extended user fields offered up by this method
-        $twitter_user['created_at'] = $this->date_twitter($profile->created);
-
-        $subbed = DB_DataObject::factory('subscription');
-        $subbed->subscriber = $profile->id;
-        $subbed_count = (int) $subbed->count() - 1;
-
-        $notices = DB_DataObject::factory('notice');
-        $notices->profile_id = $profile->id;
-        $notice_count = (int) $notices->count();
-
-        $twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0;
-        $twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0;
-
-        // Other fields Twitter sends...
-        $twitter_user['profile_background_color'] = '';
-        $twitter_user['profile_text_color'] = '';
-        $twitter_user['profile_link_color'] = '';
-        $twitter_user['profile_sidebar_fill_color'] = '';
-
-        $faves = DB_DataObject::factory('fave');
-        $faves->user_id = $user->id;
-        $faves_count = (int) $faves->count();
-        $twitter_user['favourites_count'] = $faves_count;
-
-        $timezone = 'UTC';
-
-        if ($user->timezone) {
-            $timezone = $user->timezone;
-        }
-
-        $t = new DateTime;
-        $t->setTimezone(new DateTimeZone($timezone));
-        $twitter_user['utc_offset'] = $t->format('Z');
-        $twitter_user['time_zone'] = $timezone;
-
-        $following = 'false';
-
-        if (isset($this->auth_user)) {
-            if ($this->auth_user->isSubscribed($profile)) {
-                $following = 'true';
-            }
-
-            // Not implemented yet
-            $twitter_user['notifications'] = 'false';
-        }
-
-        $twitter_user['following'] = $following;
-
-        if ($apidata['content-type'] == 'xml') {
-            $this->init_document('xml');
-            $this->show_twitter_xml_user($twitter_user);
-            $this->end_document('xml');
-        } elseif ($apidata['content-type'] == 'json') {
-            $this->init_document('json');
-            $this->show_json_objects($twitter_user);
-            $this->end_document('json');
-        }
-
-    }
-
 }
index 91b15ccf554afcb3f7080493b73956b016335122..4e09bffcecb6f251d860d7eeaab628b53483090e 100644 (file)
@@ -72,8 +72,7 @@ function common_timezone()
         }
     }
 
-    global $config;
-    return $config['site']['timezone'];
+    return common_config('site', 'timezone');
 }
 
 function common_language()
@@ -467,7 +466,7 @@ function common_replace_urls_callback($text, $callback) {
         $url = (mb_strpos($orig_url, htmlspecialchars($url)) === FALSE) ? $url:htmlspecialchars($url);
 
         // Call user specified func
-        $modified_url = $callback($url);
+        $modified_url = call_user_func($callback, $url);
 
         // Replace it!
         $start = mb_strpos($text, $url, $offset);
@@ -481,18 +480,12 @@ function common_replace_urls_callback($text, $callback) {
 function common_linkify($url) {
     // It comes in special'd, so we unspecial it before passing to the stringifying
     // functions
-    $ext = pathinfo($url, PATHINFO_EXTENSION);
     $url = htmlspecialchars_decode($url);
-    $video_ext = array('mp4', 'flv', 'avi', 'mpg', 'mp3', 'ogg');
     $display = $url;
     $url = (!preg_match('#^([a-z]+://|(mailto|aim|tel):)#i', $url)) ? 'http://'.$url : $url;
 
     $attrs = array('href' => $url, 'rel' => 'external');
 
-    if (in_array($ext, $video_ext)) {
-        $attrs['class'] = 'media';
-    }
-
     if ($longurl = common_longurl($url)) {
         $attrs['title'] = $longurl;
     }
@@ -688,7 +681,7 @@ function common_relative_profile($sender, $nickname, $dt=null)
     $recipient = new Profile();
     // XXX: use a join instead of a subquery
     $recipient->whereAdd('EXISTS (SELECT subscribed from subscription where subscriber = '.$sender->id.' and subscribed = id)', 'AND');
-    $recipient->whereAdd('nickname = "' . trim($nickname) . '"', 'AND');
+    $recipient->whereAdd("nickname = '" . trim($nickname) . "'", 'AND');
     if ($recipient->find(true)) {
         // XXX: should probably differentiate between profiles with
         // the same name by date of most recent update
@@ -698,7 +691,7 @@ function common_relative_profile($sender, $nickname, $dt=null)
     $recipient = new Profile();
     // XXX: use a join instead of a subquery
     $recipient->whereAdd('EXISTS (SELECT subscriber from subscription where subscribed = '.$sender->id.' and subscriber = id)', 'AND');
-    $recipient->whereAdd('nickname = "' . trim($nickname) . '"', 'AND');
+    $recipient->whereAdd("nickname = '" . trim($nickname) . "'", 'AND');
     if ($recipient->find(true)) {
         // XXX: should probably differentiate between profiles with
         // the same name by date of most recent update
@@ -722,21 +715,23 @@ function common_local_url($action, $args=null, $params=null, $fragment=null)
 {
     $r = Router::get();
     $path = $r->build($action, $args, $params, $fragment);
-    if ($path) {
-    }
+
     if (common_config('site','fancy')) {
         $url = common_path(mb_substr($path, 1));
     } else {
-        $url = common_path('index.php'.$path);
+        if (mb_strpos($path, '/index.php') === 0) {
+            $url = common_path(mb_substr($path, 1));
+        } else {
+            $url = common_path('index.php'.$path);
+        }
     }
     return $url;
 }
 
 function common_path($relative)
 {
-    global $config;
-    $pathpart = ($config['site']['path']) ? $config['site']['path']."/" : '';
-    return "http://".$config['site']['server'].'/'.$pathpart.$relative;
+    $pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
+    return "http://".common_config('site', 'server').'/'.$pathpart.$relative;
 }
 
 function common_date_string($dt)
@@ -1029,8 +1024,7 @@ function common_ensure_syslog()
 {
     static $initialized = false;
     if (!$initialized) {
-        global $config;
-        openlog($config['syslog']['appname'], 0, LOG_USER);
+        openlog(common_config('syslog', 'appname'), 0, LOG_USER);
         $initialized = true;
     }
 }
diff --git a/local/.gitignore b/local/.gitignore
new file mode 100644 (file)
index 0000000..e69de29
index d3c1dc80974237e04ee5a877d6386b46df650da8..85b949a0896573a3f6d6326f716caa8f0dfbc4c4 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-01-25 16:24+0000\n"
+"POT-Creation-Date: 2009-03-11 09:33+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -35,17 +35,18 @@ msgid " from "
 msgstr ""
 
 #: ../actions/twitapistatuses.php:478 actions/twitapistatuses.php:412
-#: actions/twitapistatuses.php:347
+#: actions/twitapistatuses.php:347 actions/twitapistatuses.php:363
 #, php-format
 msgid "%1$s / Updates replying to %2$s"
 msgstr ""
 
 #: ../actions/invite.php:168 actions/invite.php:176 actions/invite.php:211
+#: actions/invite.php:218
 #, php-format
 msgid "%1$s has invited you to join them on %2$s"
 msgstr ""
 
-#: ../actions/invite.php:170
+#: ../actions/invite.php:170 actions/invite.php:220
 #, php-format
 msgid ""
 "%1$s has invited you to join them on %2$s (%3$s).\n"
@@ -77,6 +78,7 @@ msgid ""
 msgstr ""
 
 #: ../lib/mail.php:124 lib/mail.php:124 lib/mail.php:126 lib/mail.php:241
+#: lib/mail.php:236
 #, php-format
 msgid "%1$s is now listening to your notices on %2$s."
 msgstr ""
@@ -93,7 +95,7 @@ msgid ""
 msgstr ""
 
 #: ../actions/twitapistatuses.php:482 actions/twitapistatuses.php:415
-#: actions/twitapistatuses.php:350
+#: actions/twitapistatuses.php:350 actions/twitapistatuses.php:367
 #, php-format
 msgid "%1$s updates that reply to updates from %2$s / %3$s."
 msgstr ""
@@ -121,47 +123,49 @@ msgstr ""
 #: actions/allrss.php:60 actions/twitapistatuses.php:155 lib/personal.php:51
 #: actions/all.php:65 actions/allrss.php:103 actions/facebookhome.php:164
 #: actions/twitapistatuses.php:126 lib/personalgroupnav.php:99
+#: actions/all.php:68 actions/all.php:114 actions/allrss.php:106
+#: actions/facebookhome.php:163 actions/twitapistatuses.php:130
 #, php-format
 msgid "%s and friends"
 msgstr ""
 
 #: ../actions/twitapistatuses.php:49 actions/twitapistatuses.php:49
-#: actions/twitapistatuses.php:33
+#: actions/twitapistatuses.php:33 actions/twitapistatuses.php:32
 #, php-format
 msgid "%s public timeline"
 msgstr ""
 
-#: ../lib/mail.php:206 lib/mail.php:212 lib/mail.php:411
+#: ../lib/mail.php:206 lib/mail.php:212 lib/mail.php:411 lib/mail.php:412
 #, php-format
 msgid "%s status"
 msgstr ""
 
 #: ../actions/twitapistatuses.php:338 actions/twitapistatuses.php:265
-#: actions/twitapistatuses.php:199
+#: actions/twitapistatuses.php:199 actions/twitapistatuses.php:209
 #, php-format
 msgid "%s timeline"
 msgstr ""
 
 #: ../actions/twitapistatuses.php:52 actions/twitapistatuses.php:52
-#: actions/twitapistatuses.php:36
+#: actions/twitapistatuses.php:36 actions/twitapistatuses.php:38
 #, php-format
 msgid "%s updates from everyone!"
 msgstr ""
 
-#: ../actions/register.php:213
+#: ../actions/register.php:213 actions/register.php:497
 msgid ""
 "(You should receive a message by email momentarily, with instructions on how "
 "to confirm your email address.)"
 msgstr ""
 
-#: ../lib/util.php:257 lib/util.php:273 lib/action.php:605
+#: ../lib/util.php:257 lib/util.php:273 lib/action.php:605 lib/action.php:702
 #, php-format
 msgid ""
 "**%%site.name%%** is a microblogging service brought to you by [%%site."
 "broughtby%%](%%site.broughtbyurl%%). "
 msgstr ""
 
-#: ../lib/util.php:259 lib/util.php:275 lib/action.php:607
+#: ../lib/util.php:259 lib/util.php:275 lib/action.php:607 lib/action.php:704
 #, php-format
 msgid "**%%site.name%%** is a microblogging service. "
 msgstr ""
@@ -178,6 +182,7 @@ msgid "1-64 lowercase letters or numbers, no punctuation or spaces"
 msgstr ""
 
 #: ../actions/register.php:152 actions/register.php:166
+#: actions/register.php:368
 msgid "1-64 lowercase letters or numbers, no punctuation or spaces. Required."
 msgstr ""
 
@@ -197,6 +202,7 @@ msgid "6 or more characters. Required."
 msgstr ""
 
 #: ../actions/imsettings.php:197 actions/imsettings.php:205
+#: actions/imsettings.php:321
 #, php-format
 msgid ""
 "A confirmation code was sent to the IM address you added. You must approve %"
@@ -204,6 +210,7 @@ msgid ""
 msgstr ""
 
 #: ../actions/emailsettings.php:213 actions/emailsettings.php:231
+#: actions/emailsettings.php:350
 msgid ""
 "A confirmation code was sent to the email address you added. Check your "
 "inbox (and spam box!) for the code and instructions on how to use it."
@@ -233,7 +240,12 @@ msgstr ""
 #: actions/twitapistatuses.php:147 actions/twitapistatuses.php:228
 #: actions/twitapistatuses.php:239 actions/twitapistatuses.php:392
 #: actions/twitapistatuses.php:402 actions/twitapistatuses.php:429
-#: actions/twitapiusers.php:32
+#: actions/twitapiusers.php:32 actions/twitapidirect_messages.php:120
+#: actions/twitapifavorites.php:91 actions/twitapifavorites.php:108
+#: actions/twitapistatuses.php:82 actions/twitapistatuses.php:159
+#: actions/twitapistatuses.php:246 actions/twitapistatuses.php:257
+#: actions/twitapistatuses.php:416 actions/twitapistatuses.php:426
+#: actions/twitapistatuses.php:453
 msgid "API method not found!"
 msgstr ""
 
@@ -256,11 +268,14 @@ msgstr ""
 #: actions/twitapidirect_messages.php:184 actions/twitapifavorites.php:143
 #: actions/twitapihelp.php:52 actions/twitapilaconica.php:172
 #: actions/twitapinotifications.php:31 actions/twitapinotifications.php:37
-#: actions/twitapistatuses.php:562
+#: actions/twitapistatuses.php:562 actions/twitapiaccount.php:46
+#: actions/twitapiaccount.php:98 actions/twitapiaccount.php:104
+#: actions/twitapidirect_messages.php:193 actions/twitapifavorites.php:149
+#: actions/twitapistatuses.php:625 actions/twitapitrends.php:87
 msgid "API method under construction."
 msgstr ""
 
-#: ../lib/util.php:324 lib/util.php:340 lib/action.php:568
+#: ../lib/util.php:324 lib/util.php:340 lib/action.php:568 lib/action.php:661
 msgid "About"
 msgstr ""
 
@@ -276,6 +291,7 @@ msgstr ""
 #: actions/twittersettings.php:85 actions/emailsettings.php:120
 #: actions/imsettings.php:127 actions/openidsettings.php:111
 #: actions/smssettings.php:133 actions/twittersettings.php:163
+#: actions/twittersettings.php:166
 msgid "Add"
 msgstr ""
 
@@ -298,6 +314,7 @@ msgid "Address"
 msgstr ""
 
 #: ../actions/invite.php:131 actions/invite.php:139 actions/invite.php:176
+#: actions/invite.php:181
 msgid "Addresses of friends to invite (one per line)"
 msgstr ""
 
@@ -332,7 +349,7 @@ msgid "Already subscribed!."
 msgstr ""
 
 #: ../actions/deletenotice.php:54 actions/deletenotice.php:55
-#: actions/deletenotice.php:113
+#: actions/deletenotice.php:113 actions/deletenotice.php:114
 msgid "Are you sure you want to delete this notice?"
 msgstr ""
 
@@ -342,11 +359,13 @@ msgid "Authorize subscription"
 msgstr ""
 
 #: ../actions/login.php:104 ../actions/register.php:178
-#: actions/register.php:192
+#: actions/register.php:192 actions/login.php:218 actions/openidlogin.php:117
+#: actions/register.php:416
 msgid "Automatically login in the future; not for shared computers!"
 msgstr ""
 
 #: ../actions/profilesettings.php:65 actions/profilesettings.php:98
+#: actions/profilesettings.php:144
 msgid ""
 "Automatically subscribe to whoever subscribes to me (best for non-humans)"
 msgstr ""
@@ -354,15 +373,17 @@ msgstr ""
 #: ../actions/avatar.php:32 ../lib/settingsaction.php:90
 #: actions/profilesettings.php:34 actions/avatarsettings.php:65
 #: actions/showgroup.php:209 lib/accountsettingsaction.php:107
+#: actions/avatarsettings.php:67 actions/showgroup.php:211
 msgid "Avatar"
 msgstr ""
 
 #: ../actions/avatar.php:113 actions/profilesettings.php:350
-#: actions/avatarsettings.php:395
+#: actions/avatarsettings.php:395 actions/avatarsettings.php:346
 msgid "Avatar updated."
 msgstr ""
 
 #: ../actions/imsettings.php:55 actions/imsettings.php:56
+#: actions/imsettings.php:108
 #, php-format
 msgid ""
 "Awaiting confirmation on this address. Check your Jabber/GTalk account for a "
@@ -370,6 +391,7 @@ msgid ""
 msgstr ""
 
 #: ../actions/emailsettings.php:54 actions/emailsettings.php:55
+#: actions/emailsettings.php:107
 msgid ""
 "Awaiting confirmation on this address. Check your inbox (and spam box!) for "
 "a message with further instructions."
@@ -394,7 +416,7 @@ msgstr ""
 #: ../actions/updateprofile.php:103 actions/profilesettings.php:216
 #: actions/register.php:89 actions/updateprofile.php:104
 #: actions/profilesettings.php:205 actions/register.php:174
-#: actions/updateprofile.php:107
+#: actions/updateprofile.php:107 actions/updateprofile.php:109
 msgid "Bio is too long (max 140 chars)."
 msgstr ""
 
@@ -403,7 +425,7 @@ msgid "Can't delete this notice."
 msgstr ""
 
 #: ../actions/updateprofile.php:119 actions/updateprofile.php:120
-#: actions/updateprofile.php:123
+#: actions/updateprofile.php:123 actions/updateprofile.php:125
 #, php-format
 msgid "Can't read avatar URL '%s'"
 msgstr ""
@@ -411,6 +433,7 @@ msgstr ""
 #: ../actions/password.php:85 ../actions/recoverpassword.php:300
 #: actions/profilesettings.php:404 actions/recoverpassword.php:313
 #: actions/passwordsettings.php:169 actions/recoverpassword.php:347
+#: actions/passwordsettings.php:174
 msgid "Can't save new password."
 msgstr ""
 
@@ -423,6 +446,7 @@ msgid "Cancel"
 msgstr ""
 
 #: ../lib/openid.php:121 lib/openid.php:121 lib/openid.php:130
+#: lib/openid.php:133
 msgid "Cannot instantiate OpenID consumer object."
 msgstr ""
 
@@ -432,7 +456,7 @@ msgid "Cannot normalize that Jabber ID"
 msgstr ""
 
 #: ../actions/emailsettings.php:181 actions/emailsettings.php:199
-#: actions/emailsettings.php:311
+#: actions/emailsettings.php:311 actions/emailsettings.php:318
 msgid "Cannot normalize that email address"
 msgstr ""
 
@@ -478,7 +502,7 @@ msgstr ""
 #: ../actions/smssettings.php:245 actions/emailsettings.php:256
 #: actions/imsettings.php:230 actions/smssettings.php:253
 #: actions/emailsettings.php:379 actions/imsettings.php:361
-#: actions/smssettings.php:374
+#: actions/smssettings.php:374 actions/emailsettings.php:386
 msgid "Confirmation cancelled."
 msgstr ""
 
@@ -492,7 +516,7 @@ msgstr ""
 msgid "Confirmation code not found."
 msgstr ""
 
-#: ../actions/register.php:202
+#: ../actions/register.php:202 actions/register.php:473
 #, php-format
 msgid ""
 "Congratulations, %s! And welcome to %%%%site.name%%%%. From here, you may "
@@ -512,7 +536,8 @@ msgid ""
 msgstr ""
 
 #: ../actions/finishopenidlogin.php:91 actions/finishopenidlogin.php:97
-#: actions/finishopenidlogin.php:119 lib/action.php:330
+#: actions/finishopenidlogin.php:119 lib/action.php:330 lib/action.php:403
+#: lib/action.php:406
 msgid "Connect"
 msgstr ""
 
@@ -521,11 +546,12 @@ msgstr ""
 msgid "Connect existing account"
 msgstr ""
 
-#: ../lib/util.php:332 lib/util.php:348 lib/action.php:576
+#: ../lib/util.php:332 lib/util.php:348 lib/action.php:576 lib/action.php:669
 msgid "Contact"
 msgstr ""
 
 #: ../lib/openid.php:178 lib/openid.php:178 lib/openid.php:187
+#: lib/openid.php:190
 #, php-format
 msgid "Could not create OpenID form: %s"
 msgstr ""
@@ -543,17 +569,18 @@ msgid "Could not follow user: User not found."
 msgstr ""
 
 #: ../lib/openid.php:160 lib/openid.php:160 lib/openid.php:169
+#: lib/openid.php:172
 #, php-format
 msgid "Could not redirect to server: %s"
 msgstr ""
 
 #: ../actions/updateprofile.php:162 actions/updateprofile.php:163
-#: actions/updateprofile.php:166
+#: actions/updateprofile.php:166 actions/updateprofile.php:176
 msgid "Could not save avatar info"
 msgstr ""
 
 #: ../actions/updateprofile.php:155 actions/updateprofile.php:156
-#: actions/updateprofile.php:159
+#: actions/updateprofile.php:159 actions/updateprofile.php:163
 msgid "Could not save new profile info"
 msgstr ""
 
@@ -581,6 +608,7 @@ msgstr ""
 #: actions/imsettings.php:226 actions/smssettings.php:249
 #: actions/confirmaddress.php:126 actions/emailsettings.php:375
 #: actions/imsettings.php:357 actions/smssettings.php:370
+#: actions/emailsettings.php:382
 msgid "Couldn't delete email confirmation."
 msgstr ""
 
@@ -589,7 +617,7 @@ msgid "Couldn't delete subscription."
 msgstr ""
 
 #: ../actions/twitapistatuses.php:93 actions/twitapistatuses.php:98
-#: actions/twitapistatuses.php:84
+#: actions/twitapistatuses.php:84 actions/twitapistatuses.php:87
 msgid "Couldn't find any statuses."
 msgstr ""
 
@@ -602,7 +630,7 @@ msgstr ""
 #: ../actions/smssettings.php:206 actions/emailsettings.php:223
 #: actions/imsettings.php:195 actions/smssettings.php:214
 #: actions/emailsettings.php:337 actions/imsettings.php:311
-#: actions/smssettings.php:325
+#: actions/smssettings.php:325 actions/emailsettings.php:344
 msgid "Couldn't insert confirmation code."
 msgstr ""
 
@@ -614,6 +642,7 @@ msgstr ""
 #: ../actions/profilesettings.php:184 ../actions/twitapiaccount.php:96
 #: actions/profilesettings.php:299 actions/twitapiaccount.php:94
 #: actions/profilesettings.php:302 actions/twitapiaccount.php:81
+#: actions/twitapiaccount.php:82
 msgid "Couldn't save profile."
 msgstr ""
 
@@ -625,6 +654,8 @@ msgstr ""
 #: ../actions/emailsettings.php:280 ../actions/emailsettings.php:294
 #: actions/emailsettings.php:298 actions/emailsettings.php:312
 #: actions/emailsettings.php:440 actions/emailsettings.php:462
+#: actions/emailsettings.php:447 actions/emailsettings.php:469
+#: actions/smssettings.php:515 actions/smssettings.php:539
 msgid "Couldn't update user record."
 msgstr ""
 
@@ -640,7 +671,8 @@ msgstr ""
 #: actions/emailsettings.php:411 actions/imsettings.php:252
 #: actions/imsettings.php:395 actions/othersettings.php:162
 #: actions/profilesettings.php:259 actions/smssettings.php:266
-#: actions/smssettings.php:408
+#: actions/smssettings.php:408 actions/emailsettings.php:287
+#: actions/emailsettings.php:418
 msgid "Couldn't update user."
 msgstr ""
 
@@ -684,23 +716,25 @@ msgid "Currently"
 msgstr ""
 
 #: ../classes/Notice.php:72 classes/Notice.php:86 classes/Notice.php:91
+#: classes/Notice.php:114
 #, php-format
 msgid "DB error inserting hashtag: %s"
 msgstr ""
 
 #: ../lib/util.php:1061 lib/util.php:1110 classes/Notice.php:698
+#: classes/Notice.php:757
 #, php-format
 msgid "DB error inserting reply: %s"
 msgstr ""
 
 #: ../actions/deletenotice.php:41 actions/deletenotice.php:41
-#: actions/deletenotice.php:79
+#: actions/deletenotice.php:79 actions/deletenotice.php:111
 msgid "Delete notice"
 msgstr ""
 
 #: ../actions/profilesettings.php:51 ../actions/register.php:172
 #: actions/profilesettings.php:84 actions/register.php:186
-#: actions/profilesettings.php:114
+#: actions/profilesettings.php:114 actions/register.php:404
 msgid "Describe yourself and your interests in 140 chars"
 msgstr ""
 
@@ -735,6 +769,7 @@ msgid "Email address, like \"UserName@example.org\""
 msgstr ""
 
 #: ../actions/invite.php:129 actions/invite.php:137 actions/invite.php:174
+#: actions/invite.php:179
 msgid "Email addresses"
 msgstr ""
 
@@ -754,7 +789,7 @@ msgid "Error authorizing token"
 msgstr ""
 
 #: ../actions/finishopenidlogin.php:253 actions/finishopenidlogin.php:259
-#: actions/finishopenidlogin.php:297
+#: actions/finishopenidlogin.php:297 actions/finishopenidlogin.php:302
 msgid "Error connecting user to OpenID."
 msgstr ""
 
@@ -789,15 +824,17 @@ msgid "Error saving remote profile"
 msgstr ""
 
 #: ../lib/openid.php:226 lib/openid.php:226 lib/openid.php:235
+#: lib/openid.php:238
 msgid "Error saving the profile."
 msgstr ""
 
 #: ../lib/openid.php:237 lib/openid.php:237 lib/openid.php:246
+#: lib/openid.php:249
 msgid "Error saving the user."
 msgstr ""
 
 #: ../actions/password.php:80 actions/profilesettings.php:399
-#: actions/passwordsettings.php:164
+#: actions/passwordsettings.php:164 actions/passwordsettings.php:169
 msgid "Error saving user; invalid."
 msgstr ""
 
@@ -806,6 +843,7 @@ msgstr ""
 #: actions/login.php:47 actions/login.php:73 actions/recoverpassword.php:320
 #: actions/register.php:108 actions/login.php:112 actions/login.php:138
 #: actions/recoverpassword.php:354 actions/register.php:198
+#: actions/login.php:120
 msgid "Error setting user."
 msgstr ""
 
@@ -829,29 +867,31 @@ msgstr ""
 msgid "Existing nickname"
 msgstr ""
 
-#: ../lib/util.php:326 lib/util.php:342 lib/action.php:570
+#: ../lib/util.php:326 lib/util.php:342 lib/action.php:570 lib/action.php:663
 msgid "FAQ"
 msgstr ""
 
 #: ../actions/avatar.php:115 actions/profilesettings.php:352
-#: actions/avatarsettings.php:397
+#: actions/avatarsettings.php:397 actions/avatarsettings.php:349
 msgid "Failed updating avatar."
 msgstr ""
 
 #: ../actions/all.php:61 ../actions/allrss.php:64 actions/all.php:61
 #: actions/allrss.php:64 actions/all.php:75 actions/allrss.php:107
+#: actions/allrss.php:110
 #, php-format
 msgid "Feed for friends of %s"
 msgstr ""
 
 #: ../actions/replies.php:65 ../actions/repliesrss.php:80
 #: actions/replies.php:65 actions/repliesrss.php:66 actions/replies.php:134
-#: actions/repliesrss.php:71
+#: actions/repliesrss.php:71 actions/replies.php:136
 #, php-format
 msgid "Feed for replies to %s"
 msgstr ""
 
 #: ../actions/tag.php:55 actions/tag.php:55 actions/tag.php:61
+#: actions/tag.php:68
 #, php-format
 msgid "Feed for tag %s"
 msgstr ""
@@ -866,7 +906,7 @@ msgstr ""
 msgid "Find people on this site"
 msgstr ""
 
-#: ../actions/login.php:122
+#: ../actions/login.php:122 actions/login.php:247
 msgid ""
 "For security reasons, please re-enter your user name and password before "
 "changing your settings."
@@ -877,6 +917,8 @@ msgstr ""
 #: actions/profilesettings.php:103 actions/register.php:391
 #: actions/showgroup.php:235 actions/showstream.php:262
 #: actions/tagother.php:105 lib/groupeditform.php:142
+#: actions/showgroup.php:237 actions/showstream.php:255
+#: actions/tagother.php:104
 msgid "Full name"
 msgstr ""
 
@@ -885,16 +927,17 @@ msgstr ""
 #: actions/register.php:86 actions/updateprofile.php:94
 #: actions/editgroup.php:195 actions/newgroup.php:146
 #: actions/profilesettings.php:202 actions/register.php:171
-#: actions/updateprofile.php:97
+#: actions/updateprofile.php:97 actions/updateprofile.php:99
 msgid "Full name is too long (max 255 chars)."
 msgstr ""
 
 #: ../lib/util.php:322 lib/util.php:338 lib/action.php:344 lib/action.php:566
+#: lib/action.php:421 lib/action.php:659
 msgid "Help"
 msgstr ""
 
 #: ../lib/util.php:298 lib/util.php:314 lib/action.php:322
-#: lib/facebookaction.php:200
+#: lib/facebookaction.php:200 lib/action.php:393 lib/facebookaction.php:213
 msgid "Home"
 msgstr ""
 
@@ -913,7 +956,7 @@ msgid "Homepage is not a valid URL."
 msgstr ""
 
 #: ../actions/emailsettings.php:91 actions/emailsettings.php:98
-#: actions/emailsettings.php:173
+#: actions/emailsettings.php:173 actions/emailsettings.php:178
 msgid "I want to post notices by email."
 msgstr ""
 
@@ -939,13 +982,13 @@ msgid ""
 "connect it to your OpenID."
 msgstr ""
 
-#: ../actions/openidsettings.php:45
+#: ../actions/openidsettings.php:45 actions/openidsettings.php:96
 msgid ""
 "If you want to add an OpenID to your account, enter it in the box below and "
 "click \"Add\"."
 msgstr ""
 
-#: ../actions/recoverpassword.php:137
+#: ../actions/recoverpassword.php:137 actions/recoverpassword.php:152
 msgid ""
 "If you've forgotten or lost your password, you can get a new one sent to the "
 "email address you have stored  in your account."
@@ -958,28 +1001,29 @@ msgid "Incoming email"
 msgstr ""
 
 #: ../actions/emailsettings.php:283 actions/emailsettings.php:301
-#: actions/emailsettings.php:443
+#: actions/emailsettings.php:443 actions/emailsettings.php:450
+#: actions/smssettings.php:518
 msgid "Incoming email address removed."
 msgstr ""
 
 #: ../actions/password.php:69 actions/profilesettings.php:388
-#: actions/passwordsettings.php:153
+#: actions/passwordsettings.php:153 actions/passwordsettings.php:158
 msgid "Incorrect old password"
 msgstr ""
 
 #: ../actions/login.php:67 actions/login.php:67 actions/facebookhome.php:131
-#: actions/login.php:132
+#: actions/login.php:132 actions/facebookhome.php:130 actions/login.php:114
 msgid "Incorrect username or password."
 msgstr ""
 
-#: ../actions/recoverpassword.php:265
+#: ../actions/recoverpassword.php:265 actions/recoverpassword.php:304
 msgid ""
 "Instructions for recovering your password have been sent to the email "
 "address registered to your account."
 msgstr ""
 
 #: ../actions/updateprofile.php:114 actions/updateprofile.php:115
-#: actions/updateprofile.php:118
+#: actions/updateprofile.php:118 actions/updateprofile.php:120
 #, php-format
 msgid "Invalid avatar URL '%s'"
 msgstr ""
@@ -990,13 +1034,13 @@ msgid "Invalid email address: %s"
 msgstr ""
 
 #: ../actions/updateprofile.php:98 actions/updateprofile.php:99
-#: actions/updateprofile.php:102
+#: actions/updateprofile.php:102 actions/updateprofile.php:104
 #, php-format
 msgid "Invalid homepage '%s'"
 msgstr ""
 
 #: ../actions/updateprofile.php:82 actions/updateprofile.php:83
-#: actions/updateprofile.php:86
+#: actions/updateprofile.php:86 actions/updateprofile.php:88
 #, php-format
 msgid "Invalid license URL '%s'"
 msgstr ""
@@ -1017,7 +1061,7 @@ msgid "Invalid notice url"
 msgstr ""
 
 #: ../actions/updateprofile.php:87 actions/updateprofile.php:88
-#: actions/updateprofile.php:91
+#: actions/updateprofile.php:91 actions/updateprofile.php:93
 #, php-format
 msgid "Invalid profile URL '%s'."
 msgstr ""
@@ -1041,7 +1085,7 @@ msgstr ""
 #: ../actions/register.php:111 actions/finishopenidlogin.php:241
 #: actions/register.php:103 actions/register.php:121
 #: actions/finishopenidlogin.php:279 actions/register.php:193
-#: actions/register.php:211
+#: actions/register.php:211 actions/finishopenidlogin.php:284
 msgid "Invalid username or password."
 msgstr ""
 
@@ -1054,7 +1098,7 @@ msgid "Invitation(s) sent to the following people:"
 msgstr ""
 
 #: ../lib/util.php:306 lib/util.php:322 lib/facebookaction.php:207
-#: lib/subgroupnav.php:103
+#: lib/subgroupnav.php:103 lib/facebookaction.php:220
 msgid "Invite"
 msgstr ""
 
@@ -1062,7 +1106,7 @@ msgstr ""
 msgid "Invite new users"
 msgstr ""
 
-#: ../lib/util.php:261 lib/util.php:277 lib/action.php:609
+#: ../lib/util.php:261 lib/util.php:277 lib/action.php:609 lib/action.php:706
 #, php-format
 msgid ""
 "It runs the [Laconica](http://laconi.ca/) microblogging software, version %"
@@ -1076,6 +1120,7 @@ msgid "Jabber ID already belongs to another user."
 msgstr ""
 
 #: ../actions/imsettings.php:62 actions/imsettings.php:63
+#: actions/imsettings.php:120
 #, php-format
 msgid ""
 "Jabber or GTalk address, like \"UserName@example.org\". First, make sure to "
@@ -1097,7 +1142,8 @@ msgstr ""
 #: actions/profilesettings.php:117 actions/register.php:408
 #: actions/showgroup.php:244 actions/showstream.php:271
 #: actions/tagother.php:113 lib/groupeditform.php:156 lib/grouplist.php:126
-#: lib/profilelist.php:125
+#: lib/profilelist.php:125 actions/showgroup.php:246
+#: actions/showstream.php:264 actions/tagother.php:112 lib/profilelist.php:123
 msgid "Location"
 msgstr ""
 
@@ -1106,7 +1152,7 @@ msgstr ""
 #: actions/register.php:92 actions/updateprofile.php:109
 #: actions/editgroup.php:201 actions/newgroup.php:152
 #: actions/profilesettings.php:208 actions/register.php:177
-#: actions/updateprofile.php:112
+#: actions/updateprofile.php:112 actions/updateprofile.php:114
 msgid "Location is too long (max 255 chars)."
 msgstr ""
 
@@ -1115,18 +1161,20 @@ msgstr ""
 #: actions/login.php:106 actions/openidlogin.php:77 lib/util.php:326
 #: actions/facebooklogin.php:93 actions/login.php:186 actions/login.php:239
 #: actions/openidlogin.php:112 lib/action.php:335 lib/facebookaction.php:288
-#: lib/facebookaction.php:315 lib/logingroupnav.php:75
+#: lib/facebookaction.php:315 lib/logingroupnav.php:75 actions/login.php:169
+#: actions/login.php:222 actions/openidlogin.php:121 lib/action.php:412
+#: lib/facebookaction.php:293 lib/facebookaction.php:319
 #, php-format
 msgid "Login"
 msgstr ""
 
 #: ../actions/openidlogin.php:44 actions/openidlogin.php:52
-#: actions/openidlogin.php:62
+#: actions/openidlogin.php:62 actions/openidlogin.php:70
 #, php-format
 msgid "Login with an [OpenID](%%doc.openid%%) account."
 msgstr ""
 
-#: ../actions/login.php:126
+#: ../actions/login.php:126 actions/login.php:251
 #, php-format
 msgid ""
 "Login with your username and password. Don't have a username yet? [Register]"
@@ -1134,7 +1182,7 @@ msgid ""
 "%). "
 msgstr ""
 
-#: ../lib/util.php:308 lib/util.php:324 lib/action.php:332
+#: ../lib/util.php:308 lib/util.php:324 lib/action.php:332 lib/action.php:409
 msgid "Logout"
 msgstr ""
 
@@ -1144,12 +1192,13 @@ msgid "Longer name, preferably your \"real\" name"
 msgstr ""
 
 #: ../actions/login.php:110 actions/login.php:110 actions/login.php:245
-#: lib/facebookaction.php:320
+#: lib/facebookaction.php:320 actions/login.php:228 lib/facebookaction.php:325
 msgid "Lost or forgotten password?"
 msgstr ""
 
 #: ../actions/emailsettings.php:80 ../actions/smssettings.php:89
 #: actions/emailsettings.php:81 actions/smssettings.php:89
+#: actions/emailsettings.php:139 actions/smssettings.php:150
 msgid "Make a new email address for posting to; cancels the old one."
 msgstr ""
 
@@ -1169,7 +1218,7 @@ msgstr ""
 msgid "Microblog by %s"
 msgstr ""
 
-#: ../actions/smssettings.php:304
+#: ../actions/smssettings.php:304 actions/smssettings.php:464
 #, php-format
 msgid ""
 "Mobile carrier for your phone. If you know a carrier that accepts SMS over "
@@ -1179,6 +1228,7 @@ msgstr ""
 #: ../actions/finishopenidlogin.php:79 ../actions/register.php:188
 #: actions/finishopenidlogin.php:85 actions/register.php:202
 #: actions/finishopenidlogin.php:107 actions/register.php:429
+#: actions/register.php:430
 msgid "My text and files are available under "
 msgstr ""
 
@@ -1194,7 +1244,8 @@ msgid "New email address for posting to %s"
 msgstr ""
 
 #: ../actions/emailsettings.php:297 actions/emailsettings.php:315
-#: actions/emailsettings.php:465
+#: actions/emailsettings.php:465 actions/emailsettings.php:472
+#: actions/smssettings.php:542
 msgid "New incoming email address added."
 msgstr ""
 
@@ -1214,7 +1265,7 @@ msgstr ""
 msgid "New password"
 msgstr ""
 
-#: ../actions/recoverpassword.php:314
+#: ../actions/recoverpassword.php:314 actions/recoverpassword.php:361
 msgid "New password successfully saved. You are now logged in."
 msgstr ""
 
@@ -1224,7 +1275,9 @@ msgstr ""
 #: actions/login.php:228 actions/profilesettings.php:98
 #: actions/register.php:367 actions/showgroup.php:224
 #: actions/showstream.php:251 actions/tagother.php:95
-#: lib/facebookaction.php:308 lib/groupeditform.php:137
+#: lib/facebookaction.php:308 lib/groupeditform.php:137 actions/login.php:211
+#: actions/showgroup.php:226 actions/showstream.php:244
+#: actions/tagother.php:94 lib/facebookaction.php:312
 msgid "Nickname"
 msgstr ""
 
@@ -1242,7 +1295,9 @@ msgstr ""
 #: actions/finishopenidlogin.php:171 actions/profilesettings.php:203
 #: actions/register.php:74 actions/updateprofile.php:78
 #: actions/finishopenidlogin.php:205 actions/profilesettings.php:192
-#: actions/updateprofile.php:81
+#: actions/updateprofile.php:81 actions/editgroup.php:179
+#: actions/newgroup.php:130 actions/register.php:156
+#: actions/updateprofile.php:83
 msgid "Nickname must have only lowercase letters and numbers and no spaces."
 msgstr ""
 
@@ -1263,6 +1318,7 @@ msgstr ""
 
 #: ../actions/deletenotice.php:59 actions/deletenotice.php:60
 #: actions/block.php:147 actions/deletenotice.php:118
+#: actions/deletenotice.php:116
 msgid "No"
 msgstr ""
 
@@ -1294,11 +1350,12 @@ msgstr ""
 #: ../actions/newnotice.php:44 actions/newmessage.php:53
 #: actions/newnotice.php:44 classes/Command.php:197 actions/newmessage.php:109
 #: actions/newnotice.php:126 classes/Command.php:223
+#: actions/newmessage.php:142 actions/newnotice.php:131 lib/command.php:223
 msgid "No content!"
 msgstr ""
 
 #: ../actions/emailsettings.php:174 actions/emailsettings.php:192
-#: actions/emailsettings.php:304
+#: actions/emailsettings.php:304 actions/emailsettings.php:311
 msgid "No email address."
 msgstr ""
 
@@ -1307,7 +1364,8 @@ msgid "No id."
 msgstr ""
 
 #: ../actions/emailsettings.php:271 actions/emailsettings.php:289
-#: actions/emailsettings.php:430
+#: actions/emailsettings.php:430 actions/emailsettings.php:437
+#: actions/smssettings.php:505
 msgid "No incoming email address."
 msgstr ""
 
@@ -1325,7 +1383,7 @@ msgstr ""
 #: ../actions/smssettings.php:229 actions/emailsettings.php:240
 #: actions/imsettings.php:214 actions/smssettings.php:237
 #: actions/emailsettings.php:363 actions/imsettings.php:345
-#: actions/smssettings.php:358
+#: actions/smssettings.php:358 actions/emailsettings.php:370
 msgid "No pending confirmation to cancel."
 msgstr ""
 
@@ -1352,7 +1410,7 @@ msgstr ""
 #: ../actions/noticesearch.php:64 ../actions/peoplesearch.php:64
 #: actions/noticesearch.php:69 actions/peoplesearch.php:69
 #: actions/groupsearch.php:81 actions/noticesearch.php:104
-#: actions/peoplesearch.php:85
+#: actions/peoplesearch.php:85 actions/noticesearch.php:117
 msgid "No results"
 msgstr ""
 
@@ -1363,12 +1421,13 @@ msgstr ""
 
 #: ../actions/twitapistatuses.php:595 actions/twitapifavorites.php:136
 #: actions/twitapistatuses.php:520 actions/twitapifavorites.php:112
-#: actions/twitapistatuses.php:446
+#: actions/twitapistatuses.php:446 actions/twitapifavorites.php:118
+#: actions/twitapistatuses.php:470
 msgid "No status found with that ID."
 msgstr ""
 
 #: ../actions/twitapistatuses.php:555 actions/twitapistatuses.php:478
-#: actions/twitapistatuses.php:418
+#: actions/twitapistatuses.php:418 actions/twitapistatuses.php:442
 msgid "No status with that ID found."
 msgstr ""
 
@@ -1422,7 +1481,9 @@ msgstr ""
 #: actions/usergroups.php:92 actions/userrss.php:38 actions/xrds.php:73
 #: classes/Command.php:140 classes/Command.php:185 classes/Command.php:234
 #: classes/Command.php:271 lib/galleryaction.php:60 lib/mailbox.php:82
-#: lib/subs.php:34 lib/subs.php:109
+#: lib/subs.php:34 lib/subs.php:109 actions/all.php:56 actions/allrss.php:68
+#: actions/favoritesrss.php:74 lib/command.php:140 lib/command.php:185
+#: lib/command.php:234 lib/command.php:271 lib/mailbox.php:84
 msgid "No such user."
 msgstr ""
 
@@ -1448,7 +1509,8 @@ msgstr ""
 #: ../lib/twitterapi.php:226 ../lib/twitterapi.php:247
 #: ../lib/twitterapi.php:332 lib/twitterapi.php:391 lib/twitterapi.php:418
 #: lib/twitterapi.php:502 lib/twitterapi.php:448 lib/twitterapi.php:476
-#: lib/twitterapi.php:566
+#: lib/twitterapi.php:566 lib/twitterapi.php:483 lib/twitterapi.php:511
+#: lib/twitterapi.php:601
 msgid "Not a supported data format."
 msgstr ""
 
@@ -1458,11 +1520,12 @@ msgid "Not a valid Jabber ID"
 msgstr ""
 
 #: ../lib/openid.php:131 lib/openid.php:131 lib/openid.php:140
+#: lib/openid.php:143
 msgid "Not a valid OpenID."
 msgstr ""
 
 #: ../actions/emailsettings.php:185 actions/emailsettings.php:203
-#: actions/emailsettings.php:315
+#: actions/emailsettings.php:315 actions/emailsettings.php:322
 msgid "Not a valid email address"
 msgstr ""
 
@@ -1493,7 +1556,7 @@ msgid "Not a valid profile URL (no YADIS document)."
 msgstr ""
 
 #: ../actions/avatar.php:95 actions/profilesettings.php:332
-#: lib/imagefile.php:87
+#: lib/imagefile.php:87 lib/imagefile.php:90
 msgid "Not an image or corrupt file."
 msgstr ""
 
@@ -1508,7 +1571,7 @@ msgid "Not expecting this response!"
 msgstr ""
 
 #: ../actions/twitapistatuses.php:422 actions/twitapistatuses.php:361
-#: actions/twitapistatuses.php:309
+#: actions/twitapistatuses.php:309 actions/twitapistatuses.php:327
 msgid "Not found"
 msgstr ""
 
@@ -1524,7 +1587,7 @@ msgstr ""
 #: actions/newmessage.php:83 actions/newnotice.php:90 actions/nudge.php:63
 #: actions/subedit.php:31 actions/subscribe.php:30 actions/unblock.php:60
 #: actions/unsubscribe.php:27 lib/deleteaction.php:66
-#: lib/settingsaction.php:72
+#: lib/settingsaction.php:72 actions/newmessage.php:87
 msgid "Not logged in."
 msgstr ""
 
@@ -1551,12 +1614,12 @@ msgstr ""
 
 #: ../actions/showstream.php:316 actions/showstream.php:331
 #: actions/showstream.php:504 lib/facebookaction.php:477 lib/mailbox.php:116
-#: lib/noticelist.php:87
+#: lib/noticelist.php:87 lib/facebookaction.php:581 lib/mailbox.php:118
 msgid "Notices"
 msgstr ""
 
 #: ../actions/tag.php:35 ../actions/tag.php:81 actions/tag.php:35
-#: actions/tag.php:81 actions/tag.php:41
+#: actions/tag.php:81 actions/tag.php:41 actions/tag.php:49
 #, php-format
 msgid "Notices tagged with %s"
 msgstr ""
@@ -1568,7 +1631,7 @@ msgstr ""
 
 #: ../lib/settingsaction.php:96 ../lib/util.php:314 lib/settingsaction.php:90
 #: lib/util.php:330 lib/accountsettingsaction.php:116 lib/action.php:341
-#: lib/logingroupnav.php:81
+#: lib/logingroupnav.php:81 lib/action.php:418
 msgid "OpenID"
 msgstr ""
 
@@ -1578,6 +1641,7 @@ msgid "OpenID Account Setup"
 msgstr ""
 
 #: ../lib/openid.php:180 lib/openid.php:180 lib/openid.php:266
+#: lib/openid.php:269
 msgid "OpenID Auto-Submit"
 msgstr ""
 
@@ -1585,12 +1649,14 @@ msgstr ""
 #: ../actions/openidlogin.php:60 actions/finishaddopenid.php:99
 #: actions/finishopenidlogin.php:146 actions/openidlogin.php:68
 #: actions/finishaddopenid.php:170 actions/openidlogin.php:80
+#: actions/openidlogin.php:89
 msgid "OpenID Login"
 msgstr ""
 
 #: ../actions/openidlogin.php:65 ../actions/openidsettings.php:49
 #: actions/openidlogin.php:74 actions/openidsettings.php:50
 #: actions/openidlogin.php:102 actions/openidsettings.php:101
+#: actions/openidlogin.php:111
 msgid "OpenID URL"
 msgstr ""
 
@@ -1608,6 +1674,7 @@ msgid "OpenID authentication failed: %s"
 msgstr ""
 
 #: ../lib/openid.php:133 lib/openid.php:133 lib/openid.php:142
+#: lib/openid.php:145
 #, php-format
 msgid "OpenID failure: %s"
 msgstr ""
@@ -1623,11 +1690,12 @@ msgid "OpenID settings"
 msgstr ""
 
 #: ../actions/invite.php:135 actions/invite.php:143 actions/invite.php:180
+#: actions/invite.php:186
 msgid "Optionally add a personal message to the invitation."
 msgstr ""
 
 #: ../actions/avatar.php:84 actions/profilesettings.php:321
-#: lib/imagefile.php:75
+#: lib/imagefile.php:75 lib/imagefile.php:79
 msgid "Partial upload."
 msgstr ""
 
@@ -1637,6 +1705,7 @@ msgstr ""
 #: actions/register.php:167 actions/finishopenidlogin.php:118
 #: actions/login.php:231 actions/register.php:372
 #: lib/accountsettingsaction.php:110 lib/facebookaction.php:311
+#: actions/login.php:214 lib/facebookaction.php:315
 msgid "Password"
 msgstr ""
 
@@ -1659,12 +1728,14 @@ msgstr ""
 #: ../actions/password.php:89 ../actions/recoverpassword.php:313
 #: actions/profilesettings.php:408 actions/recoverpassword.php:326
 #: actions/passwordsettings.php:173 actions/recoverpassword.php:200
+#: actions/passwordsettings.php:178
 msgid "Password saved."
 msgstr ""
 
 #: ../actions/password.php:61 ../actions/register.php:88
 #: actions/profilesettings.php:380 actions/register.php:98
 #: actions/passwordsettings.php:145 actions/register.php:183
+#: actions/passwordsettings.php:150
 msgid "Passwords don't match."
 msgstr ""
 
@@ -1688,10 +1759,12 @@ msgid "Personal"
 msgstr ""
 
 #: ../actions/invite.php:133 actions/invite.php:141 actions/invite.php:178
+#: actions/invite.php:184
 msgid "Personal message"
 msgstr ""
 
 #: ../actions/smssettings.php:69 actions/smssettings.php:69
+#: actions/smssettings.php:128
 msgid "Phone number, no punctuation or spaces, with area code"
 msgstr ""
 
@@ -1712,7 +1785,7 @@ msgstr ""
 #: actions/imsettings.php:68 actions/smssettings.php:94
 #: actions/twittersettings.php:70 actions/emailsettings.php:147
 #: actions/imsettings.php:133 actions/smssettings.php:157
-#: actions/twittersettings.php:134
+#: actions/twittersettings.php:134 actions/twittersettings.php:137
 msgid "Preferences"
 msgstr ""
 
@@ -1721,6 +1794,7 @@ msgstr ""
 #: actions/imsettings.php:152 actions/smssettings.php:171
 #: actions/emailsettings.php:286 actions/imsettings.php:258
 #: actions/othersettings.php:168 actions/smssettings.php:272
+#: actions/emailsettings.php:293
 msgid "Preferences saved."
 msgstr ""
 
@@ -1729,12 +1803,13 @@ msgstr ""
 msgid "Preferred language"
 msgstr ""
 
-#: ../lib/util.php:328 lib/util.php:344 lib/action.php:572
+#: ../lib/util.php:328 lib/util.php:344 lib/action.php:572 lib/action.php:665
 msgid "Privacy"
 msgstr ""
 
 #: ../classes/Notice.php:95 ../classes/Notice.php:106 classes/Notice.php:109
 #: classes/Notice.php:119 classes/Notice.php:145 classes/Notice.php:155
+#: classes/Notice.php:178 classes/Notice.php:188
 msgid "Problem saving notice."
 msgstr ""
 
@@ -1757,6 +1832,7 @@ msgstr ""
 #: ../actions/postnotice.php:51 ../actions/updateprofile.php:52
 #: actions/postnotice.php:52 actions/updateprofile.php:53
 #: actions/postnotice.php:55 actions/updateprofile.php:56
+#: actions/updateprofile.php:58
 msgid "Profile unknown"
 msgstr ""
 
@@ -1765,7 +1841,7 @@ msgid "Public Stream Feed"
 msgstr ""
 
 #: ../actions/public.php:33 actions/public.php:33 actions/public.php:109
-#: lib/publicgroupnav.php:77
+#: lib/publicgroupnav.php:77 actions/public.php:112 lib/publicgroupnav.php:79
 msgid "Public timeline"
 msgstr ""
 
@@ -1775,7 +1851,7 @@ msgid "Publish a MicroID for my Jabber/GTalk address."
 msgstr ""
 
 #: ../actions/emailsettings.php:94 actions/emailsettings.php:101
-#: actions/emailsettings.php:178
+#: actions/emailsettings.php:178 actions/emailsettings.php:183
 msgid "Publish a MicroID for my email address."
 msgstr ""
 
@@ -1803,6 +1879,7 @@ msgstr ""
 #: actions/register.php:152 actions/register.php:207 lib/util.php:328
 #: actions/register.php:69 actions/register.php:436 lib/action.php:338
 #: lib/facebookaction.php:277 lib/logingroupnav.php:78
+#: actions/register.php:438 lib/action.php:415 lib/facebookaction.php:279
 msgid "Register"
 msgstr ""
 
@@ -1823,12 +1900,12 @@ msgstr ""
 
 #: ../actions/login.php:103 ../actions/register.php:176 actions/login.php:103
 #: actions/register.php:190 actions/login.php:234 actions/openidlogin.php:107
-#: actions/register.php:414
+#: actions/register.php:414 actions/login.php:217 actions/openidlogin.php:116
 msgid "Remember me"
 msgstr ""
 
 #: ../actions/updateprofile.php:70 actions/updateprofile.php:71
-#: actions/updateprofile.php:74
+#: actions/updateprofile.php:74 actions/updateprofile.php:76
 msgid "Remote profile with no matching profile"
 msgstr ""
 
@@ -1847,6 +1924,7 @@ msgstr ""
 #: actions/emailsettings.php:134 actions/imsettings.php:102
 #: actions/openidsettings.php:166 actions/smssettings.php:103
 #: actions/smssettings.php:146 actions/twittersettings.php:115
+#: actions/twittersettings.php:118
 msgid "Remove"
 msgstr ""
 
@@ -1855,7 +1933,7 @@ msgstr ""
 msgid "Remove OpenID"
 msgstr ""
 
-#: ../actions/openidsettings.php:73
+#: ../actions/openidsettings.php:73 actions/openidsettings.php:128
 msgid ""
 "Removing your only OpenID would make it impossible to log in! If you need to "
 "remove it, add another OpenID first."
@@ -1868,7 +1946,7 @@ msgstr ""
 #: ../actions/replies.php:47 ../actions/repliesrss.php:76 ../lib/stream.php:56
 #: actions/replies.php:47 actions/repliesrss.php:62 lib/personal.php:56
 #: actions/replies.php:116 actions/repliesrss.php:67
-#: lib/personalgroupnav.php:104
+#: lib/personalgroupnav.php:104 actions/replies.php:118
 #, php-format
 msgid "Replies to %s"
 msgstr ""
@@ -1885,6 +1963,7 @@ msgstr ""
 
 #: ../lib/settingsaction.php:99 lib/settingsaction.php:93
 #: actions/subscriptions.php:123 lib/connectsettingsaction.php:107
+#: actions/subscriptions.php:125
 msgid "SMS"
 msgstr ""
 
@@ -1898,7 +1977,7 @@ msgstr ""
 msgid "SMS Settings"
 msgstr ""
 
-#: ../lib/mail.php:219 lib/mail.php:225 lib/mail.php:437
+#: ../lib/mail.php:219 lib/mail.php:225 lib/mail.php:437 lib/mail.php:438
 msgid "SMS confirmation"
 msgstr ""
 
@@ -1921,12 +2000,14 @@ msgstr ""
 #: actions/othersettings.php:117 actions/profilesettings.php:150
 #: actions/smssettings.php:169 actions/subscriptions.php:124
 #: actions/tagother.php:152 actions/twittersettings.php:161
-#: lib/groupeditform.php:171
+#: lib/groupeditform.php:171 actions/emailsettings.php:187
+#: actions/subscriptions.php:126 actions/tagother.php:154
+#: actions/twittersettings.php:164
 msgid "Save"
 msgstr ""
 
 #: ../lib/searchaction.php:84 ../lib/util.php:300 lib/searchaction.php:84
-#: lib/util.php:316 lib/action.php:325
+#: lib/util.php:316 lib/action.php:325 lib/action.php:396
 msgid "Search"
 msgstr ""
 
@@ -1936,14 +2017,14 @@ msgid "Search Stream Feed"
 msgstr ""
 
 #: ../actions/noticesearch.php:30 actions/noticesearch.php:30
-#: actions/noticesearch.php:57
+#: actions/noticesearch.php:57 actions/noticesearch.php:68
 #, php-format
 msgid ""
 "Search for notices on %%site.name%% by their contents. Separate search terms "
 "by spaces; they must be 3 characters or more."
 msgstr ""
 
-#: ../actions/peoplesearch.php:28
+#: ../actions/peoplesearch.php:28 actions/peoplesearch.php:52
 #, php-format
 msgid ""
 "Search for people on %%site.name%% by their name, location, or interests. "
@@ -1957,7 +2038,8 @@ msgstr ""
 
 #: ../actions/invite.php:137 ../lib/util.php:1172 actions/invite.php:145
 #: lib/util.php:1306 lib/util.php:1731 actions/invite.php:182
-#: lib/messageform.php:167 lib/noticeform.php:177
+#: lib/messageform.php:167 lib/noticeform.php:177 actions/invite.php:189
+#: lib/messageform.php:165
 msgid "Send"
 msgstr ""
 
@@ -1978,16 +2060,19 @@ msgid "Send me notices through Jabber/GTalk."
 msgstr ""
 
 #: ../actions/smssettings.php:97 actions/smssettings.php:97
+#: actions/smssettings.php:162
 msgid ""
 "Send me notices through SMS; I understand I may incur exorbitant charges "
 "from my carrier."
 msgstr ""
 
 #: ../actions/imsettings.php:76 actions/imsettings.php:77
+#: actions/imsettings.php:147
 msgid "Send me replies through Jabber/GTalk from people I'm not subscribed to."
 msgstr ""
 
 #: ../lib/util.php:304 lib/util.php:320 lib/facebookaction.php:215
+#: lib/facebookaction.php:228
 msgid "Settings"
 msgstr ""
 
@@ -2021,18 +2106,19 @@ msgstr ""
 msgid "Sorry, that is not your incoming email address."
 msgstr ""
 
-#: ../lib/util.php:330 lib/util.php:346 lib/action.php:574
+#: ../lib/util.php:330 lib/util.php:346 lib/action.php:574 lib/action.php:667
 msgid "Source"
 msgstr ""
 
 #: ../actions/showstream.php:296 actions/showstream.php:311
-#: actions/showstream.php:476
+#: actions/showstream.php:476 actions/showgroup.php:375
 msgid "Statistics"
 msgstr ""
 
 #: ../actions/finishopenidlogin.php:182 ../actions/finishopenidlogin.php:246
 #: actions/finishopenidlogin.php:188 actions/finishopenidlogin.php:252
 #: actions/finishopenidlogin.php:222 actions/finishopenidlogin.php:290
+#: actions/finishopenidlogin.php:295
 msgid "Stored OpenID not found."
 msgstr ""
 
@@ -2052,12 +2138,12 @@ msgid "Subscribers"
 msgstr ""
 
 #: ../actions/userauthorization.php:310 actions/userauthorization.php:322
-#: actions/userauthorization.php:338
+#: actions/userauthorization.php:338 actions/userauthorization.php:344
 msgid "Subscription authorized"
 msgstr ""
 
 #: ../actions/userauthorization.php:320 actions/userauthorization.php:332
-#: actions/userauthorization.php:349
+#: actions/userauthorization.php:349 actions/userauthorization.php:355
 msgid "Subscription rejected"
 msgstr ""
 
@@ -2070,14 +2156,15 @@ msgid "Subscriptions"
 msgstr ""
 
 #: ../actions/avatar.php:87 actions/profilesettings.php:324
-#: lib/imagefile.php:78
+#: lib/imagefile.php:78 lib/imagefile.php:82
 msgid "System error uploading file."
 msgstr ""
 
 #: ../actions/tag.php:41 ../lib/util.php:301 actions/tag.php:41
 #: lib/util.php:317 actions/profilesettings.php:122 actions/showstream.php:297
 #: actions/tagother.php:147 actions/tagother.php:207 lib/profilelist.php:162
-#: lib/profilelist.php:164
+#: lib/profilelist.php:164 actions/showstream.php:290 actions/tagother.php:149
+#: actions/tagother.php:209 lib/profilelist.php:160
 msgid "Tags"
 msgstr ""
 
@@ -2086,7 +2173,7 @@ msgid "Text"
 msgstr ""
 
 #: ../actions/noticesearch.php:34 actions/noticesearch.php:34
-#: actions/noticesearch.php:67
+#: actions/noticesearch.php:67 actions/noticesearch.php:78
 msgid "Text search"
 msgstr ""
 
@@ -2106,6 +2193,7 @@ msgid "That confirmation code is not for you!"
 msgstr ""
 
 #: ../actions/emailsettings.php:191 actions/emailsettings.php:209
+#: actions/emailsettings.php:328
 msgid "That email address already belongs to another user."
 msgstr ""
 
@@ -2120,7 +2208,7 @@ msgid "That is already your Jabber ID."
 msgstr ""
 
 #: ../actions/emailsettings.php:188 actions/emailsettings.php:206
-#: actions/emailsettings.php:318
+#: actions/emailsettings.php:318 actions/emailsettings.php:325
 msgid "That is already your email address."
 msgstr ""
 
@@ -2135,7 +2223,7 @@ msgid "That is not your Jabber ID."
 msgstr ""
 
 #: ../actions/emailsettings.php:249 actions/emailsettings.php:267
-#: actions/emailsettings.php:397
+#: actions/emailsettings.php:397 actions/emailsettings.php:404
 msgid "That is not your email address."
 msgstr ""
 
@@ -2147,6 +2235,7 @@ msgstr ""
 #: ../actions/emailsettings.php:226 ../actions/imsettings.php:210
 #: actions/emailsettings.php:244 actions/imsettings.php:218
 #: actions/emailsettings.php:367 actions/imsettings.php:349
+#: actions/emailsettings.php:374
 msgid "That is the wrong IM address."
 msgstr ""
 
@@ -2163,15 +2252,18 @@ msgstr ""
 #: ../actions/newnotice.php:49 ../actions/twitapistatuses.php:408
 #: actions/newnotice.php:49 actions/twitapistatuses.php:330
 #: actions/facebookhome.php:243 actions/twitapistatuses.php:276
+#: actions/newnotice.php:136 actions/twitapistatuses.php:294
+#: lib/facebookaction.php:485
 msgid "That's too long. Max notice size is 140 chars."
 msgstr ""
 
 #: ../actions/twitapiaccount.php:74 actions/twitapiaccount.php:72
-#: actions/twitapiaccount.php:62
+#: actions/twitapiaccount.php:62 actions/twitapiaccount.php:63
 msgid "That's too long. Max notice size is 255 chars."
 msgstr ""
 
 #: ../actions/confirmaddress.php:92 actions/confirmaddress.php:92
+#: actions/confirmaddress.php:159
 #, php-format
 msgid "The address \"%s\" has been confirmed for your account."
 msgstr ""
@@ -2180,18 +2272,18 @@ msgstr ""
 #: ../actions/smssettings.php:274 actions/emailsettings.php:282
 #: actions/imsettings.php:258 actions/smssettings.php:282
 #: actions/emailsettings.php:416 actions/imsettings.php:402
-#: actions/smssettings.php:413
+#: actions/smssettings.php:413 actions/emailsettings.php:423
 msgid "The address was removed."
 msgstr ""
 
-#: ../actions/userauthorization.php:312
+#: ../actions/userauthorization.php:312 actions/userauthorization.php:346
 msgid ""
 "The subscription has been authorized, but no callback URL was passed. Check "
 "with the site's instructions for details on how to authorize the "
 "subscription. Your subscription token is:"
 msgstr ""
 
-#: ../actions/userauthorization.php:322
+#: ../actions/userauthorization.php:322 actions/userauthorization.php:357
 msgid ""
 "The subscription has been rejected, but no callback URL was passed. Check "
 "with the site's instructions for details on how to fully reject the "
@@ -2199,20 +2291,24 @@ msgid ""
 msgstr ""
 
 #: ../actions/subscribers.php:35 actions/subscribers.php:35
+#: actions/subscribers.php:67
 #, php-format
 msgid "These are the people who listen to %s's notices."
 msgstr ""
 
 #: ../actions/subscribers.php:33 actions/subscribers.php:33
+#: actions/subscribers.php:63
 msgid "These are the people who listen to your notices."
 msgstr ""
 
 #: ../actions/subscriptions.php:35 actions/subscriptions.php:35
+#: actions/subscriptions.php:69
 #, php-format
 msgid "These are the people whose notices %s listens to."
 msgstr ""
 
 #: ../actions/subscriptions.php:33 actions/subscriptions.php:33
+#: actions/subscriptions.php:65
 msgid "These are the people whose notices you listen to."
 msgstr ""
 
@@ -2221,11 +2317,11 @@ msgid ""
 "These people are already users and you were automatically subscribed to them:"
 msgstr ""
 
-#: ../actions/recoverpassword.php:88
+#: ../actions/recoverpassword.php:88 actions/recoverpassword.php:97
 msgid "This confirmation code is too old. Please start again."
 msgstr ""
 
-#: ../lib/openid.php:195
+#: ../lib/openid.php:195 lib/openid.php:206
 msgid ""
 "This form should automatically submit itself. If not, click the submit "
 "button to go to your OpenID provider."
@@ -2244,6 +2340,7 @@ msgstr ""
 #: actions/twitapifavorites.php:127 actions/twitapifriendships.php:108
 #: actions/twitapistatuses.php:511 actions/twitapifavorites.php:97
 #: actions/twitapifriendships.php:85 actions/twitapistatuses.php:436
+#: actions/twitapifavorites.php:103 actions/twitapistatuses.php:460
 msgid "This method requires a POST or DELETE."
 msgstr ""
 
@@ -2252,11 +2349,12 @@ msgstr ""
 #: actions/twitapidirect_messages.php:114 actions/twitapifriendships.php:44
 #: actions/twitapistatuses.php:303 actions/twitapiaccount.php:53
 #: actions/twitapidirect_messages.php:122 actions/twitapifriendships.php:32
-#: actions/twitapistatuses.php:244
+#: actions/twitapistatuses.php:244 actions/twitapiaccount.php:54
+#: actions/twitapidirect_messages.php:131 actions/twitapistatuses.php:262
 msgid "This method requires a POST."
 msgstr ""
 
-#: ../lib/util.php:164 lib/util.php:246
+#: ../lib/util.php:164 lib/util.php:246 lib/htmloutputter.php:104
 msgid "This page is not available in a media type you accept"
 msgstr ""
 
@@ -2270,7 +2368,7 @@ msgstr ""
 msgid "Timezone not selected."
 msgstr ""
 
-#: ../actions/remotesubscribe.php:43
+#: ../actions/remotesubscribe.php:43 actions/remotesubscribe.php:74
 #, php-format
 msgid ""
 "To subscribe, you can [login](%%action.login%%), or [register](%%action."
@@ -2285,7 +2383,7 @@ msgstr ""
 
 #: ../actions/profilesettings.php:48 ../actions/register.php:169
 #: actions/profilesettings.php:81 actions/register.php:183
-#: actions/profilesettings.php:109
+#: actions/profilesettings.php:109 actions/register.php:398
 msgid "URL of your homepage, blog, or profile on another site"
 msgstr ""
 
@@ -2302,6 +2400,8 @@ msgstr ""
 #: actions/emailsettings.php:242 actions/grouplogo.php:317
 #: actions/imsettings.php:214 actions/recoverpassword.php:44
 #: actions/smssettings.php:236 actions/twittersettings.php:302
+#: actions/avatarsettings.php:263 actions/emailsettings.php:247
+#: actions/grouplogo.php:324 actions/twittersettings.php:306
 msgid "Unexpected form submission."
 msgstr ""
 
@@ -2339,11 +2439,12 @@ msgstr ""
 #: ../actions/postnotice.php:44 ../actions/updateprofile.php:45
 #: actions/postnotice.php:45 actions/updateprofile.php:46
 #: actions/postnotice.php:48 actions/updateprofile.php:49
+#: actions/updateprofile.php:51
 msgid "Unsupported OMB version"
 msgstr ""
 
 #: ../actions/avatar.php:105 actions/profilesettings.php:342
-#: lib/imagefile.php:102
+#: lib/imagefile.php:102 lib/imagefile.php:99
 msgid "Unsupported image file format."
 msgstr ""
 
@@ -2358,19 +2459,20 @@ msgid "Updates by instant messenger (IM)"
 msgstr ""
 
 #: ../actions/twitapistatuses.php:241 actions/twitapistatuses.php:158
-#: actions/twitapistatuses.php:129
+#: actions/twitapistatuses.php:129 actions/twitapistatuses.php:134
 #, php-format
 msgid "Updates from %1$s and friends on %2$s!"
 msgstr ""
 
 #: ../actions/twitapistatuses.php:341 actions/twitapistatuses.php:268
-#: actions/twitapistatuses.php:202
+#: actions/twitapistatuses.php:202 actions/twitapistatuses.php:213
 #, php-format
 msgid "Updates from %1$s on %2$s!"
 msgstr ""
 
 #: ../actions/avatar.php:68 actions/profilesettings.php:161
 #: actions/avatarsettings.php:162 actions/grouplogo.php:232
+#: actions/avatarsettings.php:165 actions/grouplogo.php:238
 msgid "Upload"
 msgstr ""
 
@@ -2392,7 +2494,8 @@ msgid ""
 msgstr ""
 
 #: ../actions/register.php:159 ../actions/register.php:162
-#: actions/register.php:173 actions/register.php:176
+#: actions/register.php:173 actions/register.php:176 actions/register.php:382
+#: actions/register.php:386
 msgid "Used only for updates, announcements, and password recovery"
 msgstr ""
 
@@ -2416,6 +2519,8 @@ msgstr ""
 #: actions/twitapifavorites.php:42 actions/twitapistatuses.php:167
 #: actions/twitapistatuses.php:503 actions/twitapiusers.php:55
 #: actions/usergroups.php:99 lib/galleryaction.php:67 lib/twitterapi.php:626
+#: actions/twitapiaccount.php:71 actions/twitapistatuses.php:179
+#: actions/twitapistatuses.php:535 actions/twitapiusers.php:59
 msgid "User has no profile."
 msgstr ""
 
@@ -2440,18 +2545,18 @@ msgstr ""
 
 #: ../actions/profilesettings.php:54 ../actions/register.php:175
 #: actions/profilesettings.php:87 actions/register.php:189
-#: actions/profilesettings.php:119
+#: actions/profilesettings.php:119 actions/register.php:410
 msgid "Where you are, like \"City, State (or Region), Country\""
 msgstr ""
 
 #: ../actions/updateprofile.php:128 actions/updateprofile.php:129
-#: actions/updateprofile.php:132
+#: actions/updateprofile.php:132 actions/updateprofile.php:134
 #, php-format
 msgid "Wrong image type for '%s'"
 msgstr ""
 
 #: ../actions/updateprofile.php:123 actions/updateprofile.php:124
-#: actions/updateprofile.php:127
+#: actions/updateprofile.php:127 actions/updateprofile.php:129
 #, php-format
 msgid "Wrong size image at '%s'"
 msgstr ""
@@ -2459,7 +2564,7 @@ msgstr ""
 #: ../actions/deletenotice.php:63 ../actions/deletenotice.php:72
 #: actions/deletenotice.php:64 actions/deletenotice.php:79
 #: actions/block.php:148 actions/deletenotice.php:122
-#: actions/deletenotice.php:141
+#: actions/deletenotice.php:141 actions/deletenotice.php:115
 msgid "Yes"
 msgstr ""
 
@@ -2502,20 +2607,21 @@ msgstr ""
 msgid "You can receive SMS messages through email from %%site.name%%."
 msgstr ""
 
-#: ../actions/openidsettings.php:86
+#: ../actions/openidsettings.php:86 actions/openidsettings.php:143
 msgid ""
 "You can remove an OpenID from your account by clicking the button marked "
 "\"Remove\"."
 msgstr ""
 
 #: ../actions/imsettings.php:28 actions/imsettings.php:28
+#: actions/imsettings.php:70
 #, php-format
 msgid ""
 "You can send and receive notices through Jabber/GTalk [instant messages](%%"
 "doc.im%%). Configure your address and settings below."
 msgstr ""
 
-#: ../actions/profilesettings.php:27
+#: ../actions/profilesettings.php:27 actions/profilesettings.php:69
 msgid ""
 "You can update your personal profile info here so people know more about you."
 msgstr ""
@@ -2529,16 +2635,16 @@ msgstr ""
 
 #: ../actions/finishopenidlogin.php:33 ../actions/register.php:61
 #: actions/finishopenidlogin.php:38 actions/register.php:68
-#: actions/finishopenidlogin.php:43
+#: actions/finishopenidlogin.php:43 actions/register.php:149
 msgid "You can't register if you don't agree to the license."
 msgstr ""
 
 #: ../actions/updateprofile.php:63 actions/updateprofile.php:64
-#: actions/updateprofile.php:67
+#: actions/updateprofile.php:67 actions/updateprofile.php:69
 msgid "You did not send us that profile"
 msgstr ""
 
-#: ../lib/mail.php:147
+#: ../lib/mail.php:147 lib/mail.php:289
 #, php-format
 msgid ""
 "You have a new posting address on %1$s.\n"
@@ -2552,7 +2658,7 @@ msgid ""
 msgstr ""
 
 #: ../actions/twitapistatuses.php:612 actions/twitapistatuses.php:537
-#: actions/twitapistatuses.php:463
+#: actions/twitapistatuses.php:463 actions/twitapistatuses.php:486
 msgid "You may not delete another user's status."
 msgstr ""
 
@@ -2567,67 +2673,67 @@ msgid ""
 "on the site. Thanks for growing the community!"
 msgstr ""
 
-#: ../actions/recoverpassword.php:149
+#: ../actions/recoverpassword.php:149 actions/recoverpassword.php:158
 msgid "You've been identified. Enter a  new password below. "
 msgstr ""
 
 #: ../actions/openidlogin.php:67 actions/openidlogin.php:76
-#: actions/openidlogin.php:104
+#: actions/openidlogin.php:104 actions/openidlogin.php:113
 msgid "Your OpenID URL"
 msgstr ""
 
-#: ../actions/recoverpassword.php:164
+#: ../actions/recoverpassword.php:164 actions/recoverpassword.php:188
 msgid "Your nickname on this server, or your registered email address."
 msgstr ""
 
-#: ../actions/openidsettings.php:28
+#: ../actions/openidsettings.php:28 actions/openidsettings.php:70
 #, php-format
 msgid ""
 "[OpenID](%%doc.openid%%) lets you log into many sites  with the same user "
 "account.  Manage your associated OpenIDs from here."
 msgstr ""
 
-#: ../lib/util.php:943 lib/util.php:992 lib/util.php:945
+#: ../lib/util.php:943 lib/util.php:992 lib/util.php:945 lib/util.php:756
 msgid "a few seconds ago"
 msgstr ""
 
-#: ../lib/util.php:955 lib/util.php:1004 lib/util.php:957
+#: ../lib/util.php:955 lib/util.php:1004 lib/util.php:957 lib/util.php:768
 #, php-format
 msgid "about %d days ago"
 msgstr ""
 
-#: ../lib/util.php:951 lib/util.php:1000 lib/util.php:953
+#: ../lib/util.php:951 lib/util.php:1000 lib/util.php:953 lib/util.php:764
 #, php-format
 msgid "about %d hours ago"
 msgstr ""
 
-#: ../lib/util.php:947 lib/util.php:996 lib/util.php:949
+#: ../lib/util.php:947 lib/util.php:996 lib/util.php:949 lib/util.php:760
 #, php-format
 msgid "about %d minutes ago"
 msgstr ""
 
-#: ../lib/util.php:959 lib/util.php:1008 lib/util.php:961
+#: ../lib/util.php:959 lib/util.php:1008 lib/util.php:961 lib/util.php:772
 #, php-format
 msgid "about %d months ago"
 msgstr ""
 
-#: ../lib/util.php:953 lib/util.php:1002 lib/util.php:955
+#: ../lib/util.php:953 lib/util.php:1002 lib/util.php:955 lib/util.php:766
 msgid "about a day ago"
 msgstr ""
 
-#: ../lib/util.php:945 lib/util.php:994 lib/util.php:947
+#: ../lib/util.php:945 lib/util.php:994 lib/util.php:947 lib/util.php:758
 msgid "about a minute ago"
 msgstr ""
 
-#: ../lib/util.php:957 lib/util.php:1006 lib/util.php:959
+#: ../lib/util.php:957 lib/util.php:1006 lib/util.php:959 lib/util.php:770
 msgid "about a month ago"
 msgstr ""
 
-#: ../lib/util.php:961 lib/util.php:1010 lib/util.php:963
+#: ../lib/util.php:961 lib/util.php:1010 lib/util.php:963 lib/util.php:774
 msgid "about a year ago"
 msgstr ""
 
-#: ../lib/util.php:949 lib/util.php:998 lib/util.php:951
+#: ../lib/util.php:949 lib/util.php:998 lib/util.php:951 lib/util.php:762
 msgid "about an hour ago"
 msgstr ""
 
@@ -2654,7 +2760,8 @@ msgid "same as password above"
 msgstr ""
 
 #: ../actions/twitapistatuses.php:755 actions/twitapistatuses.php:678
-#: actions/twitapistatuses.php:555
+#: actions/twitapistatuses.php:555 actions/twitapistatuses.php:596
+#: actions/twitapistatuses.php:618
 msgid "unsupported file type"
 msgstr ""
 
@@ -2675,6 +2782,14 @@ msgstr ""
 #: actions/finishopenidlogin.php:38 actions/invite.php:54 actions/nudge.php:80
 #: actions/openidlogin.php:37 actions/recoverpassword.php:316
 #: actions/subscribe.php:46 actions/unblock.php:65 actions/unsubscribe.php:43
+#: actions/avatarsettings.php:251 actions/emailsettings.php:229
+#: actions/grouplogo.php:314 actions/imsettings.php:200 actions/login.php:103
+#: actions/newmessage.php:133 actions/newnotice.php:96
+#: actions/openidsettings.php:188 actions/othersettings.php:136
+#: actions/passwordsettings.php:131 actions/profilesettings.php:172
+#: actions/register.php:113 actions/remotesubscribe.php:53
+#: actions/smssettings.php:216 actions/subedit.php:38 actions/tagother.php:166
+#: actions/twittersettings.php:294 actions/userauthorization.php:39
 msgid "There was a problem with your session token. Try again, please."
 msgstr ""
 
@@ -2690,7 +2805,7 @@ msgstr ""
 msgid "Favor"
 msgstr ""
 
-#: actions/emailsettings.php:92
+#: actions/emailsettings.php:92 actions/emailsettings.php:157
 msgid "Send me email when someone adds my notice as a favorite."
 msgstr ""
 
@@ -2699,13 +2814,14 @@ msgid "Send me email when someone sends me a private message."
 msgstr ""
 
 #: actions/favor.php:53 actions/twitapifavorites.php:142 actions/favor.php:81
-#: actions/twitapifavorites.php:118
+#: actions/twitapifavorites.php:118 actions/twitapifavorites.php:124
 msgid "This notice is already a favorite!"
 msgstr ""
 
 #: actions/favor.php:60 actions/twitapifavorites.php:151
 #: classes/Command.php:132 actions/favor.php:86
 #: actions/twitapifavorites.php:125 classes/Command.php:152
+#: actions/twitapifavorites.php:131 lib/command.php:152
 msgid "Could not create favorite."
 msgstr ""
 
@@ -2715,11 +2831,13 @@ msgstr ""
 
 #: actions/favoritesrss.php:60 actions/showfavorites.php:47
 #: actions/favoritesrss.php:100 actions/showfavorites.php:77
+#: actions/favoritesrss.php:110
 #, php-format
 msgid "%s favorite notices"
 msgstr ""
 
 #: actions/favoritesrss.php:64 actions/favoritesrss.php:104
+#: actions/favoritesrss.php:114
 #, php-format
 msgid "Feed of favorite notices of %s"
 msgstr ""
@@ -2759,33 +2877,38 @@ msgid "Login with your username and password. "
 msgstr ""
 
 #: actions/newmessage.php:58 actions/twitapidirect_messages.php:130
-#: actions/twitapidirect_messages.php:141
+#: actions/twitapidirect_messages.php:141 actions/newmessage.php:148
+#: actions/twitapidirect_messages.php:150
 msgid "That's too long. Max message size is 140 chars."
 msgstr ""
 
 #: actions/newmessage.php:65 actions/newmessage.php:128
+#: actions/newmessage.php:155
 msgid "No recipient specified."
 msgstr ""
 
 #: actions/newmessage.php:68 actions/newmessage.php:113
 #: classes/Command.php:206 actions/newmessage.php:131
 #: actions/newmessage.php:168 classes/Command.php:237
+#: actions/newmessage.php:119 actions/newmessage.php:158 lib/command.php:237
 msgid "You can't send a message to this user."
 msgstr ""
 
 #: actions/newmessage.php:71 actions/twitapidirect_messages.php:146
 #: classes/Command.php:209 actions/twitapidirect_messages.php:158
-#: classes/Command.php:240
+#: classes/Command.php:240 actions/newmessage.php:161
+#: actions/twitapidirect_messages.php:167 lib/command.php:240
 msgid ""
 "Don't send a message to yourself; just say it to yourself quietly instead."
 msgstr ""
 
 #: actions/newmessage.php:108 actions/microsummary.php:62
-#: actions/newmessage.php:163
+#: actions/newmessage.php:163 actions/newmessage.php:114
 msgid "No such user"
 msgstr ""
 
 #: actions/newmessage.php:117 actions/newmessage.php:67
+#: actions/newmessage.php:71
 msgid "New message"
 msgstr ""
 
@@ -2839,6 +2962,8 @@ msgstr ""
 #: actions/avatarsettings.php:104 actions/avatarsettings.php:179
 #: actions/grouplogo.php:177 actions/remotesubscribe.php:367
 #: actions/userauthorization.php:176 actions/userrss.php:82
+#: actions/avatarsettings.php:106 actions/avatarsettings.php:182
+#: actions/grouplogo.php:183 actions/remotesubscribe.php:366
 msgid "User without matching profile"
 msgstr ""
 
@@ -2867,6 +2992,7 @@ msgid "New password successfully saved. "
 msgstr ""
 
 #: actions/register.php:95 actions/register.php:180
+#: actions/passwordsettings.php:147
 msgid "Password must be 6 or more characters."
 msgstr ""
 
@@ -2887,12 +3013,14 @@ msgid "To subscribe, you can [login](%%action.login%%),"
 msgstr ""
 
 #: actions/showfavorites.php:61 actions/showfavorites.php:145
+#: actions/showfavorites.php:147
 #, php-format
 msgid "Feed for favorites of %s"
 msgstr ""
 
 #: actions/showfavorites.php:84 actions/twitapifavorites.php:85
 #: actions/showfavorites.php:202 actions/twitapifavorites.php:59
+#: actions/showfavorites.php:179
 msgid "Could not retrieve favorite notices."
 msgstr ""
 
@@ -2900,7 +3028,7 @@ msgstr ""
 msgid "No such message."
 msgstr ""
 
-#: actions/showmessage.php:42
+#: actions/showmessage.php:42 actions/showmessage.php:98
 msgid "Only the sender and recipient may read this message."
 msgstr ""
 
@@ -2924,51 +3052,61 @@ msgid "Mobile carrier for your phone. "
 msgstr ""
 
 #: actions/twitapidirect_messages.php:76 actions/twitapidirect_messages.php:68
+#: actions/twitapidirect_messages.php:67
 #, php-format
 msgid "Direct messages to %s"
 msgstr ""
 
 #: actions/twitapidirect_messages.php:77 actions/twitapidirect_messages.php:69
+#: actions/twitapidirect_messages.php:68
 #, php-format
 msgid "All the direct messages sent to %s"
 msgstr ""
 
 #: actions/twitapidirect_messages.php:81 actions/twitapidirect_messages.php:73
+#: actions/twitapidirect_messages.php:72
 msgid "Direct Messages You've Sent"
 msgstr ""
 
 #: actions/twitapidirect_messages.php:82 actions/twitapidirect_messages.php:74
+#: actions/twitapidirect_messages.php:73
 #, php-format
 msgid "All the direct messages sent from %s"
 msgstr ""
 
 #: actions/twitapidirect_messages.php:128
 #: actions/twitapidirect_messages.php:137
+#: actions/twitapidirect_messages.php:146
 msgid "No message text!"
 msgstr ""
 
 #: actions/twitapidirect_messages.php:138
 #: actions/twitapidirect_messages.php:150
+#: actions/twitapidirect_messages.php:159
 msgid "Recipient user not found."
 msgstr ""
 
 #: actions/twitapidirect_messages.php:141
 #: actions/twitapidirect_messages.php:153
+#: actions/twitapidirect_messages.php:162
 msgid "Can't send direct messages to users who aren't your friend."
 msgstr ""
 
 #: actions/twitapifavorites.php:92 actions/twitapifavorites.php:66
+#: actions/twitapifavorites.php:64
 #, php-format
 msgid "%s / Favorites from %s"
 msgstr ""
 
 #: actions/twitapifavorites.php:95 actions/twitapifavorites.php:69
+#: actions/twitapifavorites.php:68
 #, php-format
 msgid "%s updates favorited by %s / %s."
 msgstr ""
 
 #: actions/twitapifavorites.php:187 lib/mail.php:275
 #: actions/twitapifavorites.php:164 lib/mail.php:553
+#: actions/twitapifavorites.php:170 lib/mail.php:554
 #, php-format
 msgid "%s added your notice as a favorite"
 msgstr ""
@@ -2987,14 +3125,17 @@ msgid ""
 msgstr ""
 
 #: actions/twittersettings.php:41 actions/twittersettings.php:60
+#: actions/twittersettings.php:61
 msgid "Twitter settings"
 msgstr ""
 
 #: actions/twittersettings.php:48 actions/twittersettings.php:105
+#: actions/twittersettings.php:106
 msgid "Twitter Account"
 msgstr ""
 
 #: actions/twittersettings.php:56 actions/twittersettings.php:113
+#: actions/twittersettings.php:114
 msgid "Current verified Twitter account."
 msgstr ""
 
@@ -3003,6 +3144,7 @@ msgid "Twitter Username"
 msgstr ""
 
 #: actions/twittersettings.php:65 actions/twittersettings.php:123
+#: actions/twittersettings.php:126
 msgid "No spaces, please."
 msgstr ""
 
@@ -3011,24 +3153,28 @@ msgid "Twitter Password"
 msgstr ""
 
 #: actions/twittersettings.php:72 actions/twittersettings.php:139
+#: actions/twittersettings.php:142
 msgid "Automatically send my notices to Twitter."
 msgstr ""
 
 #: actions/twittersettings.php:75 actions/twittersettings.php:146
+#: actions/twittersettings.php:149
 msgid "Send local \"@\" replies to Twitter."
 msgstr ""
 
 #: actions/twittersettings.php:78 actions/twittersettings.php:153
+#: actions/twittersettings.php:156
 msgid "Subscribe to my Twitter friends here."
 msgstr ""
 
-#: actions/twittersettings.php:122
+#: actions/twittersettings.php:122 actions/twittersettings.php:331
 msgid ""
 "Username must have only numbers, upper- and lowercase letters, and "
 "underscore (_). 15 chars max."
 msgstr ""
 
 #: actions/twittersettings.php:128 actions/twittersettings.php:334
+#: actions/twittersettings.php:338
 msgid "Could not verify your Twitter credentials!"
 msgstr ""
 
@@ -3039,33 +3185,39 @@ msgstr ""
 
 #: actions/twittersettings.php:151 actions/twittersettings.php:170
 #: actions/twittersettings.php:348 actions/twittersettings.php:368
+#: actions/twittersettings.php:352 actions/twittersettings.php:372
 msgid "Unable to save your Twitter settings!"
 msgstr ""
 
 #: actions/twittersettings.php:174 actions/twittersettings.php:376
+#: actions/twittersettings.php:380
 msgid "Twitter settings saved."
 msgstr ""
 
 #: actions/twittersettings.php:192 actions/twittersettings.php:395
+#: actions/twittersettings.php:399
 msgid "That is not your Twitter account."
 msgstr ""
 
 #: actions/twittersettings.php:200 actions/twittersettings.php:208
-#: actions/twittersettings.php:403
+#: actions/twittersettings.php:403 actions/twittersettings.php:407
 msgid "Couldn't remove Twitter user."
 msgstr ""
 
 #: actions/twittersettings.php:212 actions/twittersettings.php:407
+#: actions/twittersettings.php:411
 msgid "Twitter account removed."
 msgstr ""
 
 #: actions/twittersettings.php:225 actions/twittersettings.php:239
 #: actions/twittersettings.php:428 actions/twittersettings.php:439
-#: actions/twittersettings.php:453
+#: actions/twittersettings.php:453 actions/twittersettings.php:432
+#: actions/twittersettings.php:443 actions/twittersettings.php:457
 msgid "Couldn't save Twitter preferences."
 msgstr ""
 
 #: actions/twittersettings.php:245 actions/twittersettings.php:461
+#: actions/twittersettings.php:465
 msgid "Twitter preferences saved."
 msgstr ""
 
@@ -3082,18 +3234,19 @@ msgid "The subscription has been rejected, but no "
 msgstr ""
 
 #: classes/Channel.php:113 classes/Channel.php:132 classes/Channel.php:151
+#: lib/channel.php:138 lib/channel.php:158
 msgid "Command results"
 msgstr ""
 
-#: classes/Channel.php:148 classes/Channel.php:204
+#: classes/Channel.php:148 classes/Channel.php:204 lib/channel.php:210
 msgid "Command complete"
 msgstr ""
 
-#: classes/Channel.php:158 classes/Channel.php:215
+#: classes/Channel.php:158 classes/Channel.php:215 lib/channel.php:221
 msgid "Command failed"
 msgstr ""
 
-#: classes/Command.php:39 classes/Command.php:44
+#: classes/Command.php:39 classes/Command.php:44 lib/command.php:44
 msgid "Sorry, this command is not yet implemented."
 msgstr ""
 
@@ -3103,89 +3256,89 @@ msgid "Subscriptions: %1$s\n"
 msgstr ""
 
 #: classes/Command.php:125 classes/Command.php:242 classes/Command.php:145
-#: classes/Command.php:276
+#: classes/Command.php:276 lib/command.php:145 lib/command.php:276
 msgid "User has no last notice"
 msgstr ""
 
-#: classes/Command.php:146 classes/Command.php:166
+#: classes/Command.php:146 classes/Command.php:166 lib/command.php:166
 msgid "Notice marked as fave."
 msgstr ""
 
-#: classes/Command.php:166 classes/Command.php:189
+#: classes/Command.php:166 classes/Command.php:189 lib/command.php:189
 #, php-format
 msgid "%1$s (%2$s)"
 msgstr ""
 
-#: classes/Command.php:169 classes/Command.php:192
+#: classes/Command.php:169 classes/Command.php:192 lib/command.php:192
 #, php-format
 msgid "Fullname: %s"
 msgstr ""
 
-#: classes/Command.php:172 classes/Command.php:195
+#: classes/Command.php:172 classes/Command.php:195 lib/command.php:195
 #, php-format
 msgid "Location: %s"
 msgstr ""
 
-#: classes/Command.php:175 classes/Command.php:198
+#: classes/Command.php:175 classes/Command.php:198 lib/command.php:198
 #, php-format
 msgid "Homepage: %s"
 msgstr ""
 
-#: classes/Command.php:178 classes/Command.php:201
+#: classes/Command.php:178 classes/Command.php:201 lib/command.php:201
 #, php-format
 msgid "About: %s"
 msgstr ""
 
-#: classes/Command.php:200 classes/Command.php:228
+#: classes/Command.php:200 classes/Command.php:228 lib/command.php:228
 #, php-format
 msgid "Message too long - maximum is 140 characters, you sent %d"
 msgstr ""
 
-#: classes/Command.php:214 classes/Command.php:245
+#: classes/Command.php:214 classes/Command.php:245 lib/command.php:245
 #, php-format
 msgid "Direct message to %s sent"
 msgstr ""
 
-#: classes/Command.php:216 classes/Command.php:247
+#: classes/Command.php:216 classes/Command.php:247 lib/command.php:247
 msgid "Error sending direct message."
 msgstr ""
 
-#: classes/Command.php:263 classes/Command.php:300
+#: classes/Command.php:263 classes/Command.php:300 lib/command.php:300
 msgid "Specify the name of the user to subscribe to"
 msgstr ""
 
-#: classes/Command.php:270 classes/Command.php:307
+#: classes/Command.php:270 classes/Command.php:307 lib/command.php:307
 #, php-format
 msgid "Subscribed to %s"
 msgstr ""
 
-#: classes/Command.php:288 classes/Command.php:328
+#: classes/Command.php:288 classes/Command.php:328 lib/command.php:328
 msgid "Specify the name of the user to unsubscribe from"
 msgstr ""
 
-#: classes/Command.php:295 classes/Command.php:335
+#: classes/Command.php:295 classes/Command.php:335 lib/command.php:335
 #, php-format
 msgid "Unsubscribed from %s"
 msgstr ""
 
 #: classes/Command.php:310 classes/Command.php:330 classes/Command.php:353
-#: classes/Command.php:376
+#: classes/Command.php:376 lib/command.php:353 lib/command.php:376
 msgid "Command not yet implemented."
 msgstr ""
 
-#: classes/Command.php:313 classes/Command.php:356
+#: classes/Command.php:313 classes/Command.php:356 lib/command.php:356
 msgid "Notification off."
 msgstr ""
 
-#: classes/Command.php:315 classes/Command.php:358
+#: classes/Command.php:315 classes/Command.php:358 lib/command.php:358
 msgid "Can't turn off notification."
 msgstr ""
 
-#: classes/Command.php:333 classes/Command.php:379
+#: classes/Command.php:333 classes/Command.php:379 lib/command.php:379
 msgid "Notification on."
 msgstr ""
 
-#: classes/Command.php:335 classes/Command.php:381
+#: classes/Command.php:335 classes/Command.php:381 lib/command.php:381
 msgid "Can't turn on notification."
 msgstr ""
 
@@ -3212,7 +3365,7 @@ msgid ""
 "\n"
 msgstr ""
 
-#: lib/mail.php:249 lib/mail.php:508
+#: lib/mail.php:249 lib/mail.php:508 lib/mail.php:509
 #, php-format
 msgid "New private message from %s"
 msgstr ""
@@ -3224,7 +3377,7 @@ msgid ""
 "\n"
 msgstr ""
 
-#: lib/mailbox.php:43 lib/mailbox.php:89
+#: lib/mailbox.php:43 lib/mailbox.php:89 lib/mailbox.php:91
 msgid "Only the user can read their own mailboxes."
 msgstr ""
 
@@ -3270,6 +3423,7 @@ msgid "Twitter integration options"
 msgstr ""
 
 #: lib/util.php:1718 lib/messageform.php:139 lib/noticelist.php:422
+#: lib/messageform.php:137 lib/noticelist.php:425
 msgid "To"
 msgstr ""
 
@@ -3277,7 +3431,8 @@ msgstr ""
 msgid "Could not parse message."
 msgstr ""
 
-#: actions/all.php:63 actions/facebookhome.php:162
+#: actions/all.php:63 actions/facebookhome.php:162 actions/all.php:66
+#: actions/facebookhome.php:161
 #, php-format
 msgid "%s and friends, page %d"
 msgstr ""
@@ -3287,21 +3442,27 @@ msgid "You can upload your personal avatar."
 msgstr ""
 
 #: actions/avatarsettings.php:117 actions/avatarsettings.php:191
-#: actions/grouplogo.php:250
+#: actions/grouplogo.php:250 actions/avatarsettings.php:119
+#: actions/avatarsettings.php:194 actions/grouplogo.php:256
 msgid "Avatar settings"
 msgstr ""
 
 #: actions/avatarsettings.php:124 actions/avatarsettings.php:199
 #: actions/grouplogo.php:198 actions/grouplogo.php:258
+#: actions/avatarsettings.php:126 actions/avatarsettings.php:202
+#: actions/grouplogo.php:204 actions/grouplogo.php:264
 msgid "Original"
 msgstr ""
 
 #: actions/avatarsettings.php:139 actions/avatarsettings.php:211
 #: actions/grouplogo.php:209 actions/grouplogo.php:270
+#: actions/avatarsettings.php:141 actions/avatarsettings.php:214
+#: actions/grouplogo.php:215 actions/grouplogo.php:276
 msgid "Preview"
 msgstr ""
 
 #: actions/avatarsettings.php:225 actions/grouplogo.php:284
+#: actions/avatarsettings.php:228 actions/grouplogo.php:291
 msgid "Crop"
 msgstr ""
 
@@ -3318,21 +3479,24 @@ msgid "There was a problem with your session token. "
 msgstr ""
 
 #: actions/avatarsettings.php:303 actions/grouplogo.php:360
+#: actions/avatarsettings.php:308
 msgid "Pick a square area of the image to be your avatar"
 msgstr ""
 
 #: actions/avatarsettings.php:327 actions/grouplogo.php:384
+#: actions/avatarsettings.php:323 actions/grouplogo.php:382
 msgid "Lost our file data."
 msgstr ""
 
 #: actions/avatarsettings.php:334 actions/grouplogo.php:391
-#: classes/User_group.php:112
+#: classes/User_group.php:112 lib/imagefile.php:112
 msgid "Lost our file."
 msgstr ""
 
 #: actions/avatarsettings.php:349 actions/avatarsettings.php:383
 #: actions/grouplogo.php:406 actions/grouplogo.php:440
-#: classes/User_group.php:129 classes/User_group.php:161
+#: classes/User_group.php:129 classes/User_group.php:161 lib/imagefile.php:144
+#: lib/imagefile.php:191
 msgid "Unknown file type"
 msgstr ""
 
@@ -3381,27 +3545,30 @@ msgstr ""
 
 #: actions/editgroup.php:66 actions/groupbyid.php:72 actions/grouplogo.php:66
 #: actions/joingroup.php:60 actions/newgroup.php:65 actions/showgroup.php:100
+#: actions/grouplogo.php:70 actions/grouprss.php:80
 msgid "Inboxes must be enabled for groups to work"
 msgstr ""
 
 #: actions/editgroup.php:71 actions/grouplogo.php:71 actions/newgroup.php:70
+#: actions/grouplogo.php:75
 msgid "You must be logged in to create a group."
 msgstr ""
 
 #: actions/editgroup.php:87 actions/grouplogo.php:87
 #: actions/groupmembers.php:76 actions/joingroup.php:81
-#: actions/showgroup.php:121
+#: actions/showgroup.php:121 actions/grouplogo.php:91 actions/grouprss.php:96
 msgid "No nickname"
 msgstr ""
 
 #: actions/editgroup.php:99 actions/groupbyid.php:88 actions/grouplogo.php:100
 #: actions/groupmembers.php:83 actions/joingroup.php:88
-#: actions/showgroup.php:128
+#: actions/showgroup.php:128 actions/grouplogo.php:104
+#: actions/grouprss.php:103
 msgid "No such group"
 msgstr ""
 
 #: actions/editgroup.php:106 actions/editgroup.php:165
-#: actions/grouplogo.php:107
+#: actions/grouplogo.php:107 actions/grouplogo.php:111
 msgid "You must be an admin to edit the group"
 msgstr ""
 
@@ -3438,7 +3605,7 @@ msgstr ""
 msgid "Send me email when someone "
 msgstr ""
 
-#: actions/emailsettings.php:168
+#: actions/emailsettings.php:168 actions/emailsettings.php:173
 msgid "Allow friends to nudge me and send me an email."
 msgstr ""
 
@@ -3464,23 +3631,26 @@ msgstr ""
 msgid "Allow %s to update my Facebook status"
 msgstr ""
 
-#: actions/facebookhome.php:218
+#: actions/facebookhome.php:218 actions/facebookhome.php:223
 msgid "Skip"
 msgstr ""
 
-#: actions/facebookhome.php:235
+#: actions/facebookhome.php:235 lib/facebookaction.php:479
 msgid "No notice content!"
 msgstr ""
 
 #: actions/facebookhome.php:295 lib/action.php:870 lib/facebookaction.php:399
+#: actions/facebookhome.php:253 lib/action.php:973 lib/facebookaction.php:433
 msgid "Pagination"
 msgstr ""
 
 #: actions/facebookhome.php:304 lib/action.php:879 lib/facebookaction.php:408
+#: actions/facebookhome.php:262 lib/action.php:982 lib/facebookaction.php:442
 msgid "After"
 msgstr ""
 
 #: actions/facebookhome.php:312 lib/action.php:887 lib/facebookaction.php:416
+#: actions/facebookhome.php:270 lib/action.php:990 lib/facebookaction.php:450
 msgid "Before"
 msgstr ""
 
@@ -3493,22 +3663,22 @@ msgstr ""
 msgid "Invitations have been sent to the following users:"
 msgstr ""
 
-#: actions/facebookinvite.php:96
+#: actions/facebookinvite.php:96 actions/facebookinvite.php:102
 #, php-format
 msgid "You have been invited to %s"
 msgstr ""
 
-#: actions/facebookinvite.php:105
+#: actions/facebookinvite.php:105 actions/facebookinvite.php:111
 #, php-format
 msgid "Invite your friends to use %s"
 msgstr ""
 
-#: actions/facebookinvite.php:113
+#: actions/facebookinvite.php:113 actions/facebookinvite.php:126
 #, php-format
 msgid "Friends already using %s:"
 msgstr ""
 
-#: actions/facebookinvite.php:130
+#: actions/facebookinvite.php:130 actions/facebookinvite.php:143
 #, php-format
 msgid "Send invitations"
 msgstr ""
@@ -3555,7 +3725,8 @@ msgid "Disfavor favorite"
 msgstr ""
 
 #: actions/favorited.php:65 lib/popularnoticesection.php:76
-#: lib/publicgroupnav.php:91
+#: lib/publicgroupnav.php:91 lib/popularnoticesection.php:82
+#: lib/publicgroupnav.php:93
 msgid "Popular notices"
 msgstr ""
 
@@ -3569,7 +3740,7 @@ msgid "The most popular notices on the site right now."
 msgstr ""
 
 #: actions/featured.php:69 lib/featureduserssection.php:82
-#: lib/publicgroupnav.php:87
+#: lib/publicgroupnav.php:87 lib/publicgroupnav.php:89
 msgid "Featured users"
 msgstr ""
 
@@ -3592,6 +3763,7 @@ msgid "No ID"
 msgstr ""
 
 #: actions/grouplogo.php:138 actions/grouplogo.php:191
+#: actions/grouplogo.php:144 actions/grouplogo.php:197
 msgid "Group logo"
 msgstr ""
 
@@ -3599,11 +3771,11 @@ msgstr ""
 msgid "You can upload a logo image for your group."
 msgstr ""
 
-#: actions/grouplogo.php:448
+#: actions/grouplogo.php:448 actions/grouplogo.php:401
 msgid "Logo updated."
 msgstr ""
 
-#: actions/grouplogo.php:450
+#: actions/grouplogo.php:450 actions/grouplogo.php:403
 msgid "Failed updating logo."
 msgstr ""
 
@@ -3622,7 +3794,7 @@ msgid "A list of the users in this group."
 msgstr ""
 
 #: actions/groups.php:62 actions/showstream.php:518 lib/publicgroupnav.php:79
-#: lib/subgroupnav.php:96
+#: lib/subgroupnav.php:96 lib/publicgroupnav.php:81
 msgid "Groups"
 msgstr ""
 
@@ -3637,6 +3809,7 @@ msgid "%%%%site.name%%%% groups let you find and talk with "
 msgstr ""
 
 #: actions/groups.php:106 actions/usergroups.php:124 lib/groupeditform.php:123
+#: actions/usergroups.php:125
 msgid "Create a new group"
 msgstr ""
 
@@ -3706,21 +3879,21 @@ msgstr ""
 msgid "You may not leave a group while you are its administrator."
 msgstr ""
 
-#: actions/leavegroup.php:130
+#: actions/leavegroup.php:130 actions/leavegroup.php:124
 msgid "Could not find membership record."
 msgstr ""
 
-#: actions/leavegroup.php:138
+#: actions/leavegroup.php:138 actions/leavegroup.php:132
 #, php-format
 msgid "Could not remove user %s to group %s"
 msgstr ""
 
-#: actions/leavegroup.php:145
+#: actions/leavegroup.php:145 actions/leavegroup.php:139
 #, php-format
 msgid "%s left group %s"
 msgstr ""
 
-#: actions/login.php:225 lib/facebookaction.php:304
+#: actions/login.php:225 lib/facebookaction.php:304 actions/login.php:208
 msgid "Login to site"
 msgstr ""
 
@@ -3752,11 +3925,12 @@ msgstr ""
 msgid "Don't send a message to yourself; "
 msgstr ""
 
-#: actions/newnotice.php:166
+#: actions/newnotice.php:166 actions/newnotice.php:174
 msgid "Notice posted"
 msgstr ""
 
-#: actions/newnotice.php:200 classes/Channel.php:163
+#: actions/newnotice.php:200 classes/Channel.php:163 actions/newnotice.php:208
+#: lib/channel.php:170
 msgid "Ajax Error"
 msgstr ""
 
@@ -3773,7 +3947,7 @@ msgstr ""
 msgid "Nudge sent!"
 msgstr ""
 
-#: actions/openidlogin.php:97
+#: actions/openidlogin.php:97 actions/openidlogin.php:106
 msgid "OpenID login"
 msgstr ""
 
@@ -3813,12 +3987,12 @@ msgstr ""
 msgid "Password change"
 msgstr ""
 
-#: actions/peopletag.php:35
+#: actions/peopletag.php:35 actions/peopletag.php:70
 #, php-format
 msgid "Not a valid people tag: %s"
 msgstr ""
 
-#: actions/peopletag.php:47
+#: actions/peopletag.php:47 actions/peopletag.php:144
 #, php-format
 msgid "Users self-tagged with %s - page %d"
 msgstr ""
@@ -3842,6 +4016,7 @@ msgid "Automatically subscribe to whoever "
 msgstr ""
 
 #: actions/profilesettings.php:229 actions/tagother.php:176
+#: actions/tagother.php:178
 #, php-format
 msgid "Invalid tag: \"%s\""
 msgstr ""
@@ -3850,12 +4025,12 @@ msgstr ""
 msgid "Couldn't save tags."
 msgstr ""
 
-#: actions/public.php:107
+#: actions/public.php:107 actions/public.php:110
 #, php-format
 msgid "Public timeline, page %d"
 msgstr ""
 
-#: actions/public.php:173
+#: actions/public.php:173 actions/public.php:184
 msgid "Could not retrieve public stream."
 msgstr ""
 
@@ -3928,7 +4103,7 @@ msgstr ""
 msgid "That's a local profile! Login to subscribe."
 msgstr ""
 
-#: actions/replies.php:118
+#: actions/replies.php:118 actions/replies.php:120
 #, php-format
 msgid "Replies to %s, page %d"
 msgstr ""
@@ -3948,40 +4123,45 @@ msgstr ""
 msgid "%s group, page %d"
 msgstr ""
 
-#: actions/showgroup.php:206
+#: actions/showgroup.php:206 actions/showgroup.php:208
 msgid "Group profile"
 msgstr ""
 
 #: actions/showgroup.php:251 actions/showstream.php:278
 #: actions/tagother.php:119 lib/grouplist.php:134 lib/profilelist.php:133
+#: actions/showgroup.php:253 actions/showstream.php:271
+#: actions/tagother.php:118 lib/profilelist.php:131
 msgid "URL"
 msgstr ""
 
 #: actions/showgroup.php:262 actions/showstream.php:289
 #: actions/tagother.php:129 lib/grouplist.php:145 lib/profilelist.php:144
+#: actions/showgroup.php:264 actions/showstream.php:282
+#: actions/tagother.php:128 lib/profilelist.php:142
 msgid "Note"
 msgstr ""
 
-#: actions/showgroup.php:270
+#: actions/showgroup.php:270 actions/showgroup.php:272
 msgid "Group actions"
 msgstr ""
 
-#: actions/showgroup.php:323
+#: actions/showgroup.php:323 actions/showgroup.php:304
 #, php-format
 msgid "Notice feed for %s group"
 msgstr ""
 
-#: actions/showgroup.php:357 lib/groupnav.php:90
+#: actions/showgroup.php:357 lib/groupnav.php:90 actions/showgroup.php:339
+#: actions/showgroup.php:384
 msgid "Members"
 msgstr ""
 
 #: actions/showgroup.php:363 actions/showstream.php:413
 #: actions/showstream.php:442 actions/showstream.php:524 lib/section.php:95
-#: lib/tagcloudsection.php:71
+#: lib/tagcloudsection.php:71 actions/showgroup.php:344
 msgid "(None)"
 msgstr ""
 
-#: actions/showgroup.php:370
+#: actions/showgroup.php:370 actions/showgroup.php:350
 msgid "All members"
 msgstr ""
 
@@ -4006,14 +4186,16 @@ msgid "'s profile"
 msgstr ""
 
 #: actions/showstream.php:236 actions/tagother.php:77
+#: actions/showstream.php:220
 msgid "User profile"
 msgstr ""
 
 #: actions/showstream.php:240 actions/tagother.php:81
+#: actions/showstream.php:224
 msgid "Photo"
 msgstr ""
 
-#: actions/showstream.php:317
+#: actions/showstream.php:317 actions/showstream.php:309
 msgid "User actions"
 msgstr ""
 
@@ -4110,16 +4292,16 @@ msgstr ""
 msgid "These are the people whose "
 msgstr ""
 
-#: actions/subscriptions.php:122
+#: actions/subscriptions.php:122 actions/subscriptions.php:124
 msgid "Jabber"
 msgstr ""
 
-#: actions/tag.php:43
+#: actions/tag.php:43 actions/tag.php:51
 #, php-format
 msgid "Notices tagged with %s, page %d"
 msgstr ""
 
-#: actions/tag.php:66
+#: actions/tag.php:66 actions/tag.php:73
 #, php-format
 msgid "Messages tagged \"%s\", most recent first"
 msgstr ""
@@ -4141,7 +4323,7 @@ msgstr ""
 msgid "Tag user"
 msgstr ""
 
-#: actions/tagother.php:149
+#: actions/tagother.php:149 actions/tagother.php:151
 msgid ""
 "Tags for this user (letters, numbers, -, ., and _), comma- or space- "
 "separated"
@@ -4151,16 +4333,16 @@ msgstr ""
 msgid "There was a problem with your session token."
 msgstr ""
 
-#: actions/tagother.php:191
+#: actions/tagother.php:191 actions/tagother.php:193
 msgid ""
 "You can only tag people you are subscribed to or who are subscribed to you."
 msgstr ""
 
-#: actions/tagother.php:198
+#: actions/tagother.php:198 actions/tagother.php:200
 msgid "Could not save tags."
 msgstr ""
 
-#: actions/tagother.php:233
+#: actions/tagother.php:233 actions/tagother.php:235
 msgid "Use this form to add tags to your subscribers or subscriptions."
 msgstr ""
 
@@ -4168,7 +4350,7 @@ msgstr ""
 msgid "No such tag."
 msgstr ""
 
-#: actions/tagrss.php:66
+#: actions/tagrss.php:66 actions/tagrss.php:64
 #, php-format
 msgid "Microblog tagged with %s"
 msgstr ""
@@ -4181,7 +4363,7 @@ msgstr ""
 msgid "Unblock user failed."
 msgstr ""
 
-#: actions/twitapiusers.php:48
+#: actions/twitapiusers.php:48 actions/twitapiusers.php:52
 msgid "Not found."
 msgstr ""
 
@@ -4189,15 +4371,15 @@ msgstr ""
 msgid "Add your Twitter account to automatically send "
 msgstr ""
 
-#: actions/twittersettings.php:119
+#: actions/twittersettings.php:119 actions/twittersettings.php:122
 msgid "Twitter user name"
 msgstr ""
 
-#: actions/twittersettings.php:126
+#: actions/twittersettings.php:126 actions/twittersettings.php:129
 msgid "Twitter password"
 msgstr ""
 
-#: actions/twittersettings.php:228
+#: actions/twittersettings.php:228 actions/twittersettings.php:232
 msgid "Twitter Friends"
 msgstr ""
 
@@ -4236,16 +4418,16 @@ msgstr ""
 msgid "%s groups, page %d"
 msgstr ""
 
-#: classes/Notice.php:104
+#: classes/Notice.php:104 classes/Notice.php:128
 msgid "Problem saving notice. Unknown user."
 msgstr ""
 
-#: classes/Notice.php:109
+#: classes/Notice.php:109 classes/Notice.php:133
 msgid ""
 "Too many notices too fast; take a breather and post again in a few minutes."
 msgstr ""
 
-#: classes/Notice.php:116
+#: classes/Notice.php:116 classes/Notice.php:145
 msgid "You are banned from posting notices on this site."
 msgstr ""
 
@@ -4261,84 +4443,84 @@ msgstr ""
 msgid "Other options"
 msgstr ""
 
-#: lib/action.php:130
+#: lib/action.php:130 lib/action.php:132
 #, php-format
 msgid "%s - %s"
 msgstr ""
 
-#: lib/action.php:145
+#: lib/action.php:145 lib/action.php:147
 msgid "Untitled page"
 msgstr ""
 
-#: lib/action.php:316
+#: lib/action.php:316 lib/action.php:387
 msgid "Primary site navigation"
 msgstr ""
 
-#: lib/action.php:322
+#: lib/action.php:322 lib/action.php:393
 msgid "Personal profile and friends timeline"
 msgstr ""
 
-#: lib/action.php:325
+#: lib/action.php:325 lib/action.php:396
 msgid "Search for people or text"
 msgstr ""
 
-#: lib/action.php:328
+#: lib/action.php:328 lib/action.php:399
 msgid "Account"
 msgstr ""
 
-#: lib/action.php:328
+#: lib/action.php:328 lib/action.php:399
 msgid "Change your email, avatar, password, profile"
 msgstr ""
 
-#: lib/action.php:330
+#: lib/action.php:330 lib/action.php:403
 msgid "Connect to IM, SMS, Twitter"
 msgstr ""
 
-#: lib/action.php:332
+#: lib/action.php:332 lib/action.php:409
 msgid "Logout from the site"
 msgstr ""
 
-#: lib/action.php:335
+#: lib/action.php:335 lib/action.php:412
 msgid "Login to the site"
 msgstr ""
 
-#: lib/action.php:338
+#: lib/action.php:338 lib/action.php:415
 msgid "Create an account"
 msgstr ""
 
-#: lib/action.php:341
+#: lib/action.php:341 lib/action.php:418
 msgid "Login with OpenID"
 msgstr ""
 
-#: lib/action.php:344
+#: lib/action.php:344 lib/action.php:421
 msgid "Help me!"
 msgstr ""
 
-#: lib/action.php:362
+#: lib/action.php:362 lib/action.php:441
 msgid "Site notice"
 msgstr ""
 
-#: lib/action.php:417
+#: lib/action.php:417 lib/action.php:504
 msgid "Local views"
 msgstr ""
 
-#: lib/action.php:472
+#: lib/action.php:472 lib/action.php:559
 msgid "Page notice"
 msgstr ""
 
-#: lib/action.php:562
+#: lib/action.php:562 lib/action.php:654
 msgid "Secondary site navigation"
 msgstr ""
 
-#: lib/action.php:602 lib/action.php:623
+#: lib/action.php:602 lib/action.php:623 lib/action.php:699 lib/action.php:720
 msgid "Laconica software license"
 msgstr ""
 
-#: lib/action.php:630
+#: lib/action.php:630 lib/action.php:727
 msgid "All "
 msgstr ""
 
-#: lib/action.php:635
+#: lib/action.php:635 lib/action.php:732
 msgid "license."
 msgstr ""
 
@@ -4359,11 +4541,12 @@ msgstr ""
 msgid "To use the %s Facebook Application you need to login "
 msgstr ""
 
-#: lib/facebookaction.php:271
+#: lib/facebookaction.php:271 lib/facebookaction.php:273
 msgid " a new account."
 msgstr ""
 
 #: lib/facebookaction.php:557 lib/mailbox.php:214 lib/noticelist.php:354
+#: lib/facebookaction.php:675 lib/mailbox.php:216 lib/noticelist.php:357
 msgid "Published"
 msgstr ""
 
@@ -4500,7 +4683,7 @@ msgid ""
 "\n"
 msgstr ""
 
-#: lib/mail.php:461
+#: lib/mail.php:461 lib/mail.php:462
 #, php-format
 msgid "You've been nudged by %s"
 msgstr ""
@@ -4515,11 +4698,12 @@ msgstr ""
 msgid "%1$s just added your notice from %2$s"
 msgstr ""
 
-#: lib/mailbox.php:229 lib/noticelist.php:380
+#: lib/mailbox.php:229 lib/noticelist.php:380 lib/mailbox.php:231
+#: lib/noticelist.php:383
 msgid "From"
 msgstr ""
 
-#: lib/messageform.php:110
+#: lib/messageform.php:110 lib/messageform.php:109
 msgid "Send a direct notice"
 msgstr ""
 
@@ -4531,23 +4715,26 @@ msgstr ""
 msgid "Available characters"
 msgstr ""
 
-#: lib/noticelist.php:426
+#: lib/noticelist.php:426 lib/noticelist.php:429
 msgid "in reply to"
 msgstr ""
 
-#: lib/noticelist.php:447 lib/noticelist.php:450
+#: lib/noticelist.php:447 lib/noticelist.php:450 lib/noticelist.php:451
+#: lib/noticelist.php:454
 msgid "Reply to this notice"
 msgstr ""
 
-#: lib/noticelist.php:451
+#: lib/noticelist.php:451 lib/noticelist.php:455
 msgid "Reply"
 msgstr ""
 
-#: lib/noticelist.php:471 lib/noticelist.php:474
+#: lib/noticelist.php:471 lib/noticelist.php:474 lib/noticelist.php:476
+#: lib/noticelist.php:479
 msgid "Delete this notice"
 msgstr ""
 
-#: lib/noticelist.php:474
+#: lib/noticelist.php:474 actions/avatarsettings.php:148
+#: lib/noticelist.php:479
 msgid "Delete"
 msgstr ""
 
@@ -4568,27 +4755,28 @@ msgstr ""
 msgid "Tags in %s's notices"
 msgstr ""
 
-#: lib/profilelist.php:182
+#: lib/profilelist.php:182 lib/profilelist.php:180
 msgid "(none)"
 msgstr ""
 
-#: lib/publicgroupnav.php:76
+#: lib/publicgroupnav.php:76 lib/publicgroupnav.php:78
 msgid "Public"
 msgstr ""
 
-#: lib/publicgroupnav.php:80
+#: lib/publicgroupnav.php:80 lib/publicgroupnav.php:82
 msgid "User groups"
 msgstr ""
 
 #: lib/publicgroupnav.php:82 lib/publicgroupnav.php:83
+#: lib/publicgroupnav.php:84 lib/publicgroupnav.php:85
 msgid "Recent tags"
 msgstr ""
 
-#: lib/publicgroupnav.php:86
+#: lib/publicgroupnav.php:86 lib/publicgroupnav.php:88
 msgid "Featured"
 msgstr ""
 
-#: lib/publicgroupnav.php:90
+#: lib/publicgroupnav.php:90 lib/publicgroupnav.php:92
 msgid "Popular"
 msgstr ""
 
@@ -4651,3 +4839,462 @@ msgstr ""
 #: lib/unsubscribeform.php:113 lib/unsubscribeform.php:137
 msgid "Unsubscribe from this user"
 msgstr ""
+
+#: actions/all.php:77
+#, php-format
+msgid "Feed for friends of %s (RSS 1.0)"
+msgstr ""
+
+#: actions/all.php:82
+#, php-format
+msgid "Feed for friends of %s (RSS 2.0)"
+msgstr ""
+
+#: actions/all.php:87
+#, php-format
+msgid "Feed for friends of %s (Atom)"
+msgstr ""
+
+#: actions/all.php:112
+msgid "You and friends"
+msgstr ""
+
+#: actions/avatarsettings.php:78
+#, php-format
+msgid "You can upload your personal avatar. The maximum file size is %s."
+msgstr ""
+
+#: actions/avatarsettings.php:373
+msgid "Avatar deleted."
+msgstr ""
+
+#: actions/block.php:129
+msgid ""
+"Are you sure you want to block this user? Afterwards, they will be "
+"unsubscribed from you, unable to subscribe to you in the future, and you "
+"will not be notified of any @-replies from them."
+msgstr ""
+
+#: actions/deletenotice.php:73
+msgid ""
+"You are about to permanently delete a notice. Once this is done, it cannot "
+"be undone."
+msgstr ""
+
+#: actions/deletenotice.php:127
+msgid "There was a problem with your session token.  Try again, please."
+msgstr ""
+
+#: actions/emailsettings.php:168
+msgid "Send me email when someone sends me an \"@-reply\"."
+msgstr ""
+
+#: actions/facebookhome.php:193
+#, php-format
+msgid ""
+"If you would like the %s app to automatically update your Facebook status "
+"with your latest notice, you need to give it permission."
+msgstr ""
+
+#: actions/facebookhome.php:217
+#, php-format
+msgid "Okay, do it!"
+msgstr ""
+
+#: actions/facebooksettings.php:124
+#, php-format
+msgid ""
+"If you would like %s to automatically update your Facebook status with your "
+"latest notice, you need to give it permission."
+msgstr ""
+
+#: actions/grouplogo.php:155
+#, php-format
+msgid ""
+"You can upload a logo image for your group. The maximum file size is %s."
+msgstr ""
+
+#: actions/grouplogo.php:367
+msgid "Pick a square area of the image to be the logo."
+msgstr ""
+
+#: actions/grouprss.php:136
+#, php-format
+msgid "Microblog by %s group"
+msgstr ""
+
+#: actions/groupsearch.php:57
+#, php-format
+msgid ""
+"Search for groups on %%site.name%% by their name, location, or description. "
+"Separate the terms by spaces; they must be 3 characters or more."
+msgstr ""
+
+#: actions/groups.php:90
+#, php-format
+msgid ""
+"%%%%site.name%%%% groups let you find and talk with people of similar "
+"interests. After you join a group you can send messages to all other members "
+"using the syntax \"!groupname\". Don't see a group you like? Try [searching "
+"for one](%%%%action.groupsearch%%%%) or [start your own!](%%%%action.newgroup"
+"%%%%)"
+msgstr ""
+
+#: actions/newmessage.php:102
+msgid "Only logged-in users can send direct messages."
+msgstr ""
+
+#: actions/noticesearch.php:91
+#, php-format
+msgid "Search results for \"%s\" on %s"
+msgstr ""
+
+#: actions/openidlogin.php:66
+#, php-format
+msgid ""
+"For security reasons, please re-login with your [OpenID](%%doc.openid%%) "
+"before changing your settings."
+msgstr ""
+
+#: actions/public.php:125
+msgid "Public Stream Feed (RSS 1.0)"
+msgstr ""
+
+#: actions/public.php:130
+msgid "Public Stream Feed (RSS 2.0)"
+msgstr ""
+
+#: actions/public.php:135
+msgid "Public Stream Feed (Atom)"
+msgstr ""
+
+#: actions/public.php:210
+#, php-format
+msgid ""
+"This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-"
+"blogging) service based on the Free Software [Laconica](http://laconi.ca/) "
+"tool. [Join now](%%action.register%%) to share notices about yourself with "
+"friends, family, and colleagues! ([Read more](%%doc.help%%))"
+msgstr ""
+
+#: actions/register.php:286
+#, php-format
+msgid ""
+"With this form you can create  a new account. You can then post notices and "
+"link up to friends and colleagues. (Have an [OpenID](http://openid.net/)? "
+"Try our [OpenID registration](%%action.openidlogin%%)!)"
+msgstr ""
+
+#: actions/register.php:432
+msgid "Creative Commons Attribution 3.0"
+msgstr ""
+
+#: actions/register.php:433
+msgid ""
+" except this private data: password, email address, IM address, and phone "
+"number."
+msgstr ""
+
+#: actions/showgroup.php:378
+msgid "Created"
+msgstr ""
+
+#: actions/showgroup.php:393
+#, php-format
+msgid ""
+"**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en."
+"wikipedia.org/wiki/Micro-blogging) service based on the Free Software "
+"[Laconica](http://laconi.ca/) tool. Its members share short messages about "
+"their life and interests. [Join now](%%%%action.register%%%%) to become part "
+"of this group and many more! ([Read more](%%%%doc.help%%%%))"
+msgstr ""
+
+#: actions/showstream.php:147
+msgid "Your profile"
+msgstr ""
+
+#: actions/showstream.php:149
+#, php-format
+msgid "%s's profile"
+msgstr ""
+
+#: actions/showstream.php:163
+#, php-format
+msgid "Notice feed for %s (RSS 1.0)"
+msgstr ""
+
+#: actions/showstream.php:170
+#, php-format
+msgid "Notice feed for %s (RSS 2.0)"
+msgstr ""
+
+#: actions/showstream.php:177
+#, php-format
+msgid "Notice feed for %s (Atom)"
+msgstr ""
+
+#: actions/showstream.php:182
+#, php-format
+msgid "FOAF for %s"
+msgstr ""
+
+#: actions/showstream.php:237
+msgid "Edit Avatar"
+msgstr ""
+
+#: actions/showstream.php:316
+msgid "Edit profile settings"
+msgstr ""
+
+#: actions/showstream.php:317
+msgid "Edit"
+msgstr ""
+
+#: actions/showstream.php:542
+#, php-format
+msgid ""
+"**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en."
+"wikipedia.org/wiki/Micro-blogging) service based on the Free Software "
+"[Laconica](http://laconi.ca/) tool. [Join now](%%%%action.register%%%%) to "
+"follow **%s**'s notices and many more! ([Read more](%%%%doc.help%%%%))"
+msgstr ""
+
+#: actions/smssettings.php:335
+msgid ""
+"A confirmation code was sent to the phone number you added. Check your phone "
+"for the code and instructions on how to use it."
+msgstr ""
+
+#: actions/twitapifavorites.php:171 lib/mail.php:556
+#, php-format
+msgid ""
+"%1$s just added your notice from %2$s as one of their favorites.\n"
+"\n"
+"In case you forgot, you can see the text of your notice here:\n"
+"\n"
+"%3$s\n"
+"\n"
+"You can see the list of %1$s's favorites here:\n"
+"\n"
+"%4$s\n"
+"\n"
+"Faithfully yours,\n"
+"%5$s\n"
+msgstr ""
+
+#: actions/twitapistatuses.php:124
+msgid "No such user!"
+msgstr ""
+
+#: actions/twittersettings.php:72
+msgid ""
+"Add your Twitter account to automatically send  your notices to Twitter, and "
+"subscribe to Twitter friends already here."
+msgstr ""
+
+#: actions/twittersettings.php:345
+#, php-format
+msgid "Unable to retrieve account information For \"%s\" from Twitter."
+msgstr ""
+
+#: actions/userauthorization.php:86
+msgid ""
+"Please check these details to make sure that you want to subscribe to this "
+"user's notices. If you didn't just ask to subscribe to someone's notices, "
+"click \"Reject\"."
+msgstr ""
+
+#: actions/usergroups.php:131
+msgid "Search for more groups"
+msgstr ""
+
+#: classes/Notice.php:138
+msgid ""
+"Too many duplicate messages too quickly; take a breather and post again in a "
+"few minutes."
+msgstr ""
+
+#: lib/action.php:406
+msgid "Connect to SMS, Twitter"
+msgstr ""
+
+#: lib/action.php:671
+msgid "Badge"
+msgstr ""
+
+#: lib/command.php:113
+#, php-format
+msgid ""
+"Subscriptions: %1$s\n"
+"Subscribers: %2$s\n"
+"Notices: %3$s"
+msgstr ""
+
+#: lib/command.php:392
+msgid ""
+"Commands:\n"
+"on - turn on notifications\n"
+"off - turn off notifications\n"
+"help - show this help\n"
+"follow <nickname> - subscribe to user\n"
+"leave <nickname> - unsubscribe from user\n"
+"d <nickname> <text> - direct message to user\n"
+"get <nickname> - get last notice from user\n"
+"whois <nickname> - get profile info on user\n"
+"fav <nickname> - add user's last notice as a 'fave'\n"
+"stats - get your stats\n"
+"stop - same as 'off'\n"
+"quit - same as 'off'\n"
+"sub <nickname> - same as 'follow'\n"
+"unsub <nickname> - same as 'leave'\n"
+"last <nickname> - same as 'get'\n"
+"on <nickname> - not yet implemented.\n"
+"off <nickname> - not yet implemented.\n"
+"nudge <nickname> - not yet implemented.\n"
+"invite <phone number> - not yet implemented.\n"
+"track <word> - not yet implemented.\n"
+"untrack <word> - not yet implemented.\n"
+"track off - not yet implemented.\n"
+"untrack all - not yet implemented.\n"
+"tracks - not yet implemented.\n"
+"tracking - not yet implemented.\n"
+msgstr ""
+
+#: lib/dberroraction.php:60
+msgid "Database error"
+msgstr ""
+
+#: lib/facebookaction.php:271
+#, php-format
+msgid ""
+"To use the %s Facebook Application you need to login with your username and "
+"password. Don't have a username yet? "
+msgstr ""
+
+#: lib/feed.php:85
+msgid "RSS 1.0"
+msgstr ""
+
+#: lib/feed.php:87
+msgid "RSS 2.0"
+msgstr ""
+
+#: lib/feed.php:89
+msgid "Atom"
+msgstr ""
+
+#: lib/feed.php:91
+msgid "FOAF"
+msgstr ""
+
+#: lib/imagefile.php:75
+#, php-format
+msgid "That file is too big. The maximum file size is %d."
+msgstr ""
+
+#: lib/mail.php:175
+#, php-format
+msgid ""
+"Hey, %s.\n"
+"\n"
+"Someone just entered this email address on %s.\n"
+"\n"
+"If it was you, and you want to confirm your entry, use the URL below:\n"
+"\n"
+"\t%s\n"
+"\n"
+"If not, just ignore this message.\n"
+"\n"
+"Thanks for your time, \n"
+"%s\n"
+msgstr ""
+
+#: lib/mail.php:241
+#, php-format
+msgid ""
+"%1$s is now listening to your notices on %2$s.\n"
+"\n"
+"\t%3$s\n"
+"\n"
+"%4$s%5$s%6$s\n"
+"Faithfully yours,\n"
+"%7$s.\n"
+"\n"
+"----\n"
+"Change your email address or notification options at %8$s\n"
+msgstr ""
+
+#: lib/mail.php:466
+#, php-format
+msgid ""
+"%1$s (%2$s) is wondering what you are up to these days and is inviting you "
+"to post some news.\n"
+"\n"
+"So let's hear from you :)\n"
+"\n"
+"%3$s\n"
+"\n"
+"Don't reply to this email; it won't get to them.\n"
+"\n"
+"With kind regards,\n"
+"%4$s\n"
+msgstr ""
+
+#: lib/mail.php:513
+#, php-format
+msgid ""
+"%1$s (%2$s) sent you a private message:\n"
+"\n"
+"------------------------------------------------------\n"
+"%3$s\n"
+"------------------------------------------------------\n"
+"\n"
+"You can reply to their message here:\n"
+"\n"
+"%4$s\n"
+"\n"
+"Don't reply to this email; it won't get to them.\n"
+"\n"
+"With kind regards,\n"
+"%5$s\n"
+msgstr ""
+
+#: lib/mail.php:598
+#, php-format
+msgid "%s sent a notice to your attention"
+msgstr ""
+
+#: lib/mail.php:600
+#, php-format
+msgid ""
+"%1$s just sent a notice to your attention (an '@-reply') on %2$s.\n"
+"\n"
+"The notice is here:\n"
+"\n"
+"\t%3$s\n"
+"\n"
+"It reads:\n"
+"\n"
+"\t%4$s\n"
+"\n"
+"You can reply back here:\n"
+"\n"
+"\t%5$s\n"
+"\n"
+"The list of all @-replies for you here:\n"
+"\n"
+"%6$s\n"
+"\n"
+"Faithfully yours,\n"
+"%2$s\n"
+"\n"
+"P.S. You can turn off these email notifications here: %7$s\n"
+msgstr ""
+
+#: lib/searchaction.php:122
+msgid "Search site"
+msgstr ""
+
+#: lib/section.php:106
+msgid "More..."
+msgstr ""
diff --git a/plugins/LinkbackPlugin.php b/plugins/LinkbackPlugin.php
new file mode 100644 (file)
index 0000000..1b53651
--- /dev/null
@@ -0,0 +1,223 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Plugin to do linkbacks for notices containing links
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 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('Auth/Yadis/Yadis.php');
+
+define('LINKBACKPLUGIN_VERSION', '0.1');
+
+/**
+ * Plugin to do linkbacks for notices containing URLs
+ *
+ * After new notices are saved, we check their text for URLs. If there
+ * are URLs, we test each URL to see if it supports any
+ *
+ * @category Plugin
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ *
+ * @see      Event
+ */
+
+class LinkbackPlugin extends Plugin
+{
+    var $notice = null;
+
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onEndNoticeSave($notice)
+    {
+        if ($notice->is_local == 1) {
+            // Try to avoid actually mucking with the
+            // notice content
+            $c = $notice->content;
+            $this->notice = $notice;
+            // Ignoring results
+            common_replace_urls_callback($c,
+                                         array($this, 'linkbackUrl'));
+        }
+        return true;
+    }
+
+    function linkbackUrl($url)
+    {
+        $orig = $url;
+        $url = htmlspecialchars_decode($orig);
+        $scheme = parse_url($url, PHP_URL_SCHEME);
+        if (!in_array($scheme, array('http', 'https'))) {
+            return $orig;
+        }
+
+        // XXX: Do a HEAD first to save some time/bandwidth
+
+        $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+
+        $result = $fetcher->get($url,
+                                array('User-Agent: ' . $this->userAgent(),
+                                      'Accept: application/html+xml,text/html'));
+
+        if (!in_array($result->status, array('200', '206'))) {
+            return $orig;
+        }
+
+        $pb = null;
+        $tb = null;
+
+        if (array_key_exists('X-Pingback', $result->headers)) {
+            $pb = $result->headers['X-Pingback'];
+        } else if (preg_match('/<link rel="pingback" href="([^"]+)" ?/?>/',
+                              $result->body,
+                              $match)) {
+            $pb = $match[1];
+        }
+
+        $tb = $this->getTrackback($result->body, $result->final_url);
+
+        if (!empty($tb)) {
+            $this->trackback($result->final_url, $tb);
+        } else if (!empty($pb)) {
+            $this->pingback($result->final_url, $pb);
+        }
+
+        return $orig;
+    }
+
+    function pingback($url, $endpoint)
+    {
+        $args = array($this->notice->uri, $url);
+
+        $request = xmlrpc_encode_request('pingback.ping', $args);
+        $context = stream_context_create(array('http' => array('method' => "POST",
+                                                               'header' =>
+                                                               "Content-Type: text/xml\r\n".
+                                                               "User-Agent: " . $this->userAgent(),
+                                                               'content' => $request)));
+        $file = file_get_contents($endpoint, false, $context);
+        $response = xmlrpc_decode($file);
+        if (xmlrpc_is_fault($response)) {
+            common_log(LOG_WARNING,
+                       "Pingback error for '$url' ($endpoint): ".
+                       "$response[faultString] ($response[faultCode])");
+        } else {
+            common_log(LOG_INFO,
+                       "Pingback success for '$url' ($endpoint): ".
+                       "'$response'");
+        }
+    }
+
+    // Largely cadged from trackback_cls.php by
+    // Ran Aroussi <ran@blogish.org>, GPL2
+    // http://phptrackback.sourceforge.net/
+
+    function getTrackback($text, $url)
+    {
+        if (preg_match_all('/(<rdf:RDF.*?<\/rdf:RDF>)/sm', $text, $match, PREG_SET_ORDER)) {
+            for ($i = 0; $i < count($match); $i++) {
+                if (preg_match('|dc:identifier="' . preg_quote($url) . '"|ms', $match[$i][1])) {
+                    $rdf_array[] = trim($match[$i][1]);
+                }
+            }
+
+            // Loop through the RDFs array and extract trackback URIs
+
+            $tb_array = array(); // <- holds list of trackback URIs
+
+            if (!empty($rdf_array)) {
+
+                for ($i = 0; $i < count($rdf_array); $i++) {
+                    if (preg_match('/trackback:ping="([^"]+)"/', $rdf_array[$i], $array)) {
+                        $tb_array[] = trim($array[1]);
+                        break;
+                    }
+                }
+            }
+
+            // Return Trackbacks
+
+            if (empty($tb_array)) {
+                return null;
+            } else {
+                return $tb_array[0];
+            }
+        }
+
+        if (preg_match_all('/(<a[^>]*?rel=[\'"]trackback[\'"][^>]*?>)/', $text, $match)) {
+            foreach ($match[1] as $atag) {
+                if (preg_match('/href=[\'"]([^\'"]*?)[\'"]/', $atag, $url)) {
+                    return $url[1];
+                }
+            }
+        }
+
+        return null;
+
+    }
+
+    function trackback($url, $endpoint)
+    {
+        $profile = $this->notice->getProfile();
+
+        $args = array('title' => sprintf(_('%1$s\'s status on %2$s'),
+                                         $profile->nickname,
+                                         common_exact_date($this->notice->created)),
+                      'excerpt' => $this->notice->content,
+                      'url' => $this->notice->uri,
+                      'blog_name' => $profile->nickname);
+
+        $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+
+        $result = $fetcher->post($endpoint,
+                                 http_build_query($args),
+                                 array('User-Agent: ' . $this->userAgent()));
+
+        if ($result->status != '200') {
+            common_log(LOG_WARNING,
+                       "Trackback error for '$url' ($endpoint): ".
+                       "$result->body");
+        } else {
+            common_log(LOG_INFO,
+                       "Trackback success for '$url' ($endpoint): ".
+                       "'$result->body'");
+        }
+    }
+
+    function userAgent()
+    {
+        return 'LinkbackPlugin/'.LINKBACKPLUGIN_VERSION .
+          ' Laconica/' . LACONICA_VERSION;
+    }
+}
diff --git a/scripts/SearchMonkey-MrH.0.txt b/scripts/SearchMonkey-MrH.0.txt
new file mode 100644 (file)
index 0000000..34191d9
--- /dev/null
@@ -0,0 +1 @@
+b2.PXYLSJBuLkZnUQ7H.2gOSSTbVllQM_XyN0ya4sO9CNt5uBUATEGyKd3LkkR0QXTSduKb7xj7Bp1TX6.8XdIDsOVVuXe.3HPnPLtSacZm66.NLY3JGhJv1MfGvcJU42YEQiUvpAhZRe46E.lv38ZPzxvhjjAmGFyFv8UNuhdw6sNIlTwBdct3k1GKA1YSEeVJ_xLiAVa8IaU_XG8PlxMovsBPePOry8__.aAp5loHtmqX6vEVu3lcHEn5NvSZ502QoWkK_20Y.6oja.Fqqxtr6eli_AQXaVzWWDEu_D4_rDV9Mz_.EC8KzXKo.YdUaWQfS9akqNm1v54wTNM4ZDegsbKnaNUR7J0OYgzpzcPj4L57yWKFm7k2af.v2loGrUOKJGO3rBcOQSfUz5jyCKDdd29cdd2L96XA0Uy31RjvdWSsprigS94HjoE9e5TUMHHZt47Wi50DsMq5R87uznHTouuHOKyxT3c_wCejxb_A0lVM5JL4VnrjVEo0CkY5ju1fDXfXG.Q8T4WYbnW_4i4W.DAuaFiRxIsLtrSG5bpMvJWQ8MTfc39gaLkNtGACL_O8IxX7iyFjARapuIXPhu3v3ndEt6qlgcUsEBl6zhxe6WcTQssBTUSDSPQWMmbA8TCYe1xwHd3ukSdqkhkALdIri2oeLLhkbNQRKxouIWHqL5aH0r7CCKM.P87rJE1meb28ux00FrBg4Fa62RM5S5Wn24u0odvPGK9JGmzisi.zM2cULr8C03gwBEeGDXzLIPevVnoqcYvtUfs8_B9xtaFzsKEM.04JX94e5Y_qWqfJs2.vWdY02BwWNsM1ONUzsS5qBzFhNpVnvZYHkNuOGXLb7cg7sL0Zm9pGMQPJsffThK2KY5EGSxbCB_JMqZmu9UtmcFyrsny7Yo1z6uCgu7GzN9_6Tc_DGW4OyuOm6XaLZSbGApmGosEKRKIODNAUGyT6QChSEwnPxhgYVpIn1TYualgrBtwvON5d84Up5.mxTH5ZHDjQcW04oE1dp2mBQhlUHvTF.PbEuUztvA8mMuLZgERcE01oOKkDlh9gvjZNiMs6f8SPmulKmtvheecitvB4Urgkae_PiXbcD4aOL4PyjwByX4zpCw9NG8Ga.6JLv1nH.vEE5XBCzh0_rnEmv0XzNBjiE8nj_p2pntfxmdpEeSGrA8WzigZTHrjawltlC_sMGLqR0EfRqoVrs3NtH8jafQ8DoqKUnR1wiG1BFC9PiQzPbPunYvVT_vMILkfwHDS7gY4bfZAlLrfbC0n8EKumGJc1o1ag_VsfnO2O8vE9667oVTaAGMmxXA_g1qpPVJTDWHRm4kwA6SWTZ6RCSb0Scd_DTXN4cevjn3sSGOCKE5Tocd1s4JRCuDPGQ9XI1OXGSKZrUuuM8mUlZqj6A.FWgdv2c5qXoRla.Qlb6encfFh8gnBNNoIR5X4F9PFl7.Yq38Vi0BJ7zIJVYAU8UXqJqPKKN.qH4A941m8jG7trm9NBrsRIkyCJBE5W1UVZ4x4sBDSuJMT_UCYCyn_qsjHkYpJOaYt44c7PT5U_S8oVUUe0qtuvmQ5DK.cZAU1hdw9fINikie3yGxO66lWRPtmM1rqTHwEHqaJXkn4mnRhQmHE.pqB.N_Byir3AiezaA2EGi6Z7.G99XrOjxJ0tIyim39AkoPZbeWqVlLDL.4AGCzPLQyndMClwWJdjcHBaSrxGGL1OpZ9cGA6w1zLD_6uyP4gK2pGTGxvOk4TFqrpc07eX7j6agiBfkUzYykXSm_JYDVZqiivMwKVEynFsX24erN0kd379wr_QI8Ov2nfXudbZn9EY1bhmupuAtMqpW.dNGk7YJ3AD31zrfz2KcZjdQuLkTYwqW4zI3YEI0jFqk0WYXSUmCNRzbdvmoZI1kAWRw2COuUnYeMfoTiMeJGgUd.g--
\ No newline at end of file
index 45b782d0b2cb82bb96ba8c1bb38d3626f384312d..6f8840962eb72c481c2c1415eaf977ced051dceb 100644 (file)
@@ -1 +1 @@
-BE9KrPPSJAPljm0ykS59yJQmWBFY9RPVJFhZsiF_5Wcf_tkGwf4FP2Ncjs7qGfHFCb2pv.eDr5y0zhKrF4s3ugs89DbLaA.hrbRJwglsNym5TDwDycrGw3TvjfCVmHBx4VzOZ2QzUyXBIs0T30paT6PYQfARKBdMkieKbbQ1tfl.f.ul35_kcpoXZt_lTDWFDaia2RM41uDLHJyVbCcRkqfHCLabgNeOq_MGFXGA6DjnKQx7TNyQe.2N6IyVd1quVapHn6jUOsWNkahehPMHtO72yvPpugS0UHCKBqcd.UCcbIhmtzLnKoBQAH2AJqUrmfg1XRwqFvTo6y9Z5XmDQK2hRnv97InV5he1AMIqNUotAcIrYjq5Tn42whYsznnMYhMY44UqZGoJI_ZwsSvnH6Je.AhKU3hBW9Tsmggpxgnhx_o2vhyNw2QAgPJng0FKxaevCmPnFtLntwluhxLbiTo2IiIotP4VjIVKs76hHzAsmXYuvS01OU.XB43Gmcw5yP9OUIoh5fEA1ANxOb.ba8aVxu2FdvedbOECgO6gvr.kYilrdxwwnVbHwVP6esrOiYE32dRs0_tWdgfvJfyloLjj_M5stZLEBcVfoUSQHsxX6jW5jrs4BTEHhKrxlOzdBt8f8T2E6eA9x2h7B8zK4eRtC2NBrcj1jzYCVWUT7IdbN05NZBjGYvxtmX1KRW91QwdFXgytfOINFkDk0scNCGGt4OYDzKLJ7sRBy0iKhHNfTC3noUhWf1372uXQ_yiWDWB7cTfxF9meAU3TWrTS7DjGTkwLvCVUJ43QyCxBQd1jWU6sp_VvytDeHx7cqrkNsa3JRN9dht3POqq_mVL0c5MX_XRpV8O.tPSGmfUPUrR5qnV8Z739D6PGWkOQzfzTTv6vGkf4jj1xyDEfUqr767_yL7gHeI2VlbdD2ammzwEhRK9f8ME8FbrZTTgX1OD0.v1cFULt1lew.rtCOtKf4F2MBbi90edst.TpOoUh_TvFkgKf0zgteNo2JjzrmCO.uviLCM2weGksARL70mdU6W9N932YWr6E8HbUc02S8ifSnbMpiUTHwNzMErsg7BSyRVJngGLDreBkBEIjXkNoApJR7kMPMaYVwtvjU.8wLmyFLZ.PHqqcrADTr7R50hL3UKj1mLsL19gQL3cP9J7_zJJM4Q1GDj2dPWBLhJMosUVhW8XBFPhW0aEdmgqhpsAJ_qv52xfjBeEPyPmKHu1p.1G2QEmOqFm_swbVAPHnra5zd8x4OOlHGFeL3qPLW9_LXN46chs6cOpUdYaWRzT2YuOUHHE8RDV9Mev22p7WjrbgC8hY3wK55Cpw_fCekaHcJX2jU3FPl09httjI1i5yGU04yD5MpOQF5EadbSlfjDEg8H.GoOFZcyKKDnqj_8SYdrXWq4aTqAnWgvBPh.bznvmevDgEpH.9gtzLcao4Dzmk.K0PnRgvkzyaN5IGk60TiD4Fk0u3f4bqKaPCOIKCTajbNGIasyND0J5ROtAa_IlXljCpzeEYPhSASSFV6fWmlqHFiGa2cKlLNr157MAUVQg52dJRRyid1YDcHK8ZuMc4BX8QA0olb2CDbUHKxxwv0zbIsgUTL8gvKIAPwBfF0cPfV9v6Phs1rPFXnysxdnunXnr6r6ZCN00rZeA7XbH68f.UjW0.ERGvB72kMnB8fBhfEgD22Y0ZFkwxI00UVAm5yiwkjYx86EdCpkMLBVDR1kWictX04pFLzxZK8iRflPBy74Nx2SFO19qJFnQmKEV2IN2wlrH6z14902LEI2Vuh_dVxNphr1k5mJ9x2VNGmvol.vvHmaLCufHIrOMRkAFMxeP37Mnzyk6NpxAQbdV0KwC.YTfoK9Y5knYAherlh7x8NqELX6XqutF2Lm11dikoK8Yu8u3Rkhoaef00PISV0LOb0E.D1LRjFFGzUCt5Ezcq68Tt0rFNEF1gm5Xl9Rygln_67F0KMgacpKTJ2PBIqcteyRZoRohFBRUTLkKn784KacSBsWFkogD5n7blv943wWeZFvUcKsFyYZny1WLvGEQX01kbjyaDCLDxiEQWSvlDuD.2wOra0JO5.Bo2PpPK5tbNexZDVx3k1yb48ev2UC4J4IlD6oK1KYe46Y.7NX9kDYCcdRiQOVnLJ4PSbPhDW0hcE2SNtxmFdpAf2xQOiekXY35lkuNxvebVhKuoFWxUgQmFEfreJQsC5qf_dunxGquD6u3SXnLbTg8aNWZqtR74dr3s7C5kwBK6eBGwJfkAx8Z2JKwLr5QHdISfa0wLl7aIejz5Mg_tfeATLIwW8SiIQdJiZ992Bx1k_50XQVeHOdyEc8J1Tjnkrji_ZY9PFn3AZc94wXD8erpANuJIbDIdLUp1idqF_TDtmqSK2qQgz26IkewcZnSIQnm98qxkHf9DAIbE6_GUP0ZeWqLQrn0CdhejhOPsKsovcDI7zBHMQABquMeBh9YC1.QCO9br6YmYEAV30n5IsZgl5PRiRDjkhXv8VaE.fnRNndSy7BX6kmxSmDWQfY0FvbUBWBQm7tfU3Mrk2Ojm4ALIYUZnJxl.5xj6VvD.2ZX3ug_1zo7mAiAYG_F939DMccfgCVFUcdtbF9Q9YFeC2bVMfgX5gA75nBCu5R8KopeWfTjdqZBtJoT6HxFmCSLQqIdjrxD7X2RapCI0QoDsp9xyzRZJ5NF5ygVBJqtNn2lAwFENasqFvGRSb35B.sjTQoHW5WBoBjFy5tNF1vWYJENfKRjWn5Pr0FvFdQfBKqeiWrLtStAnGrd49frviyK5VtYa.eSuRyX32SGb7.Pxo0Hf0bhER4uvPBktf3ec5UjL_xRI3eQ__F7tRiGu2tRYIUYcUkhLKAGYoDx2_gdMOXcow5W3KwtuVNtZ4UMRmuB3ccyBUzDTRWNLFISq56JXU46_v7ANub44kTLDfGuJExu.0NaOuW6isndAIYslJlcCs6NJ6j_6Ag55G.UhQFfzDKVOEt.L
\ No newline at end of file
+WJofugbSJAtzh.lFcHxO_WHM64iJd7qHJFbsqC9VLnpiqoBxdbE0iwoKozxZC331Svk6O9Un9jonWUrQeTndGO8hldPlxqGdFdbl6OjoucDGG_h6qBw3Ugnbethbyuew1qOaUr6ItfU4Pe9O2GtUj2KbqxZpUAvMeiS20hykHbIZ8n_1.fl.fTToLiJn58zcLc1_ryM0f5jbkIGwmGX_SG6isGJRzC4.BiNmntpJ5ZCKa80e7oUH9S6NlM5SEmXoA3zhuzkbBxF_fKLendcC7EafnTT34MbgD5s4uwTyubSBJvdmBu.mWSO9xrnojM903Vo8h3.UdG.id1LmFrj4xxd24IQqZM2v4zOfl8ugF8ChHaIzp82A1AOuk0ulkqKgiALia6bsZMbKdPZtlu_HGrF06tCt5VGvu5f0ZgjRk_kOlP_f_oppyVKRWzZpSH9GryOpoqJA2vlxgEH2BvZqaaUYEmaYJjIh7eNkLRLVorbK7rGhPf_L5qfHir.31Ar1zZxWgvhjSW7ipkvsNr_3j7oOh1LSTVWNHo.dpLT0_dib6syFopav2FqStJG0RTsWNXkQ_6h.jf439DjwuZmCLMcbUrMAMrRVcsUyR5nfqP5cP0igSx8m.oONA7O.txOXSzc3HjqvIVSTRO8BIHx.vTMJ6mq4NUmZ6_pHWapHbO9esUlIyXf5CF2qhtlM9aulr2fwCf8BqtjQctAtvYLrI_qKP95wNaB_qhcuFsZN1aiql.FtigHe2YYTix4SWg9.67VSBAyOqjWcA5jbyoomf8Gxzks9PZQfSuKbkbw_x3XOshGl4QMo6CEypV0qIYVVW77bOJc7I0WgwgxuAKg6lROlmsIdN0.SeTV4sY1ZcoLEgdNCNb81SVstXdE0iPXPZZsEStez.qluWnQoVkkWfOaBe78PxPZUvQS02RaR0FCH4bksqGWXLA8chAHIe4vPNerCLJwskKG3rA43xRDEZY7sgdEfA93iTztmIRq8dkYA6dJOxOMtvpSdh9jds04xpdmfhb23bVYp1XCp3qWka6JoKlzvQU2THqFr7gNU3KMCbr2ngHHkDTZGNO__MLa3SKlp2Vg3qHXxkivFUmWTNUz5vuW7v.FhsKOVFIFfSrTl3pjJMD3hyIlxzf_HJ60q1LRBkwqxrA9e8Fr5tqWQj.evL5kqz_r26MdHFeMTx9VwfDnHLaVx3eu5Wy2NJmLVPRLNn4t1VhexsEdLquy0ynBVBOBBRa2HbYgv0lXOP.QRl5twTKwJO6fx6J4_L_HhPv6jhcQej1LtUyGyKwLuXrHxnHCOqD.e9ZL_xnqSRthM6NvBsvbnZOPr7falUuQWPzbikXMFUvSlckeetPi2Z11l9pH.cxZDj19B38v7jwT5zZMQN9FzQvTCvzrQCpHxfIxwh3a2cx18BEMKHL7MJWDsoYVURtI3iIdR84WzXcr3kkCz.LM8tqTKVkzHLk8Grx8KT3dF5ZIVhWK5yyb05RFOZdoZQ4cWXmvsaQhDUd6Dm2KRJiypSFntLLSp34URsLaqzxaakMnywHvfmsINah4oKBBy8.NFBRULU9SqTyIOwf6_ggWG06J1WJxWV94RRCD1rMNNM9ISMOo4yB_dmfLHnXKOZm1lOnisEN36gsxniy_psLN3Ed1ozRtbbD.wALfE.92DrDA9ASPOdu08P7xer76iXVbdt_4cg5whY59Hq2N.A1mrSbetDbSu7NeziZ.nBvSUg_YpVvMo2H3elySL.1UsLPFznStGuYfbISN79MZecU.ScSCtaeZQ2cBFekXMNT77HWZmv6YLsz84ceQwL_MidvAYqPh6PUNiBl8iwFR5PCpTB54.g0m3zuALOqF9hJ598v9rh9Zlu1SfE.UfmGpUICMSuXH7C8zHMq4TFZGzCuBY9EOuBc.JQaCfBr8N3jnDq2bwlQjDmgbVW2xm2WpPMCB0OxkqoggA0i4ZZA3gSfqhlZ9mdmr_l9mUe0dOd_EkyWyu0l.Z1.VQLY2KJHl.8pZWyP6S_RAFmABOlEGP3m1E8NxaMP87o9ot_BNg21DeHbwADKbv16deRlDrKmntHoPwfsaTPNo5KembI_4nHxQ3y2B2OjsUPcLYFZZtsP4ukLBhYusR8SdtsEC._plxsMQzS9nH.zRP8nbluMlUjCKgfepGN_rCq.NeGON.L_.FowXEOcX7M1SD.IfihdAHqk0zUpuyTfe.9E77aEu26iIZOGmyOwSqRvN9HksJx.iMk5xL.xah7hl.NByMBbmkRDS52wDMxygNpPXsIHdvu8OyLxq.ZsNBGF.IHs0qiquX4.RZ2TBqTQ6O4AhzOTpFeZlG3UzdmxsEtDI2a7tY_27Gg39apf_83ijjiLaJE2RG5Mb9krZ2RCIAXaFez74o_oqli5dy6WyT4lEIKtw38T7m1bJ_xpLju5FBQUE54RpsK.ZWSAoyh3c05b2mBeN2id_NV7sM4MinY7rEsOgNVevUNrWebNrrdunUF8gsa59cXdGSKvmUw9kEKW_Z6Y14AICD43zlmVGYDYlSLqpHsu32m9aGuQFlqzBlHCKPg9uD9dyFKsZJI8l2yq91sEj9snlYhApmHLVQzGWLiOJCJUclByiPmVD_.OnHE84lBcv0bkXKFYXvUhFs2Xu1VrN1mGXz50KkG4Xovzkw0ueuWlJ2vozPUf9CkJ1mlOA4jwuFLMrQZmN04aXyR3ZWFN8pdSVITI0ADqNr9OGacCra.emTRIWdBLEj18kp39vhlR6BpxI2V0FQjC_27n2hkDUNAUNFaJ1Q84OO2mgzZnPXcN7NYz57W3lB_mMVUtKngnRlTNWn1sFdN6.WFQ1szScjtpFa68NBv5pLUOvKy49VWjn14uS4aUoDL_fbMi9tGw0tlrMEr74fm5hJNwPX1QKd3cy0jqDHLIpZTb1aNxIz_KU4XIJ2wx0gZZQH2zV7eTxsrozzo3J0TtiQC6pPOqbnsRF1TuX1..DV5LzUdKX0TUXyO8x0710.EF4z6pim0HuVKzV2VBKmG6qtzpIRCKzVk14lx2E9YkJTQusU9qZnw9Ixu_YL0Ue_4LachvurMZDYTIRcSyd4St3TmtZswmeLPS6Md6NtHwxQEy.RVLhuCmliiZcw9hB.CH6v0UX1i9E6r_FZyboWtnzX42mZDrJR4n1u2nGFzYwJrDN_kJ3uHKs4EoV9ESEd5AojHDWSkOs4lKbyRlYwhGrHgB25mZ2xRdBtkHMDupYisNjOJ.QEIXzpK291wKHWTL.VdeTOLqGxsthxoR7AOdnzalMIuUItxagzB14Gw2eZ5PcIZvcgO9C4BouZW4SAg0D0Xhmd4A0MW0an7NqzXM4VDVg6WMgeBHqL54yfHjK4oMkwDUQTvqNH13AKINKbtr5t5JJzfpSN0fmfysA1Z1PdkJROhFkyH1_ZXcoMJp2XbYSk6ZXm69U4j4QCYe_vmkp3NM802bMpohywJRxYstmfIxinJukmZOEiSClAk1MhRGVlj60w8ggV29QYa_pdqUjTm0DwYZQF1Z_NVkhVXVv7bQMpRQJPIMwf3TggPPHVAMA1tk0OdPFOCx1207E-
\ No newline at end of file
index e42a04e160d6d985b9d2d1ca2a040ea62078dce3..dcfc31ad191d88e0328139e848bbaf922a791ae9 100644 (file)
@@ -1 +1 @@
-bzN6aQzSJBM8jb6JDpNRvtfEMpxOC6r2ffo5VJf0ah_Q3kXs8gwvDzGy0KGHkwDDijfnJAwhb.BIcFihUZVWe.vAy2xZZN8XUMxHwc16HwCsgDyOC9sH5WzOV0dvJcVZ.9ipAgI7RuXsAvZdJv90N89iOh1WmD6QwWyzyvnu.FjQDrQH1DlGwFs3ZNzY5lsd5uYhtJ3puBXrzSmGcR68_7OEw4QzAV9SyPSHSskXF56cpH8pUey8WiQieM4X_uuKsMjQfWEUdqNb6teXDplvPDRqHwP5rC6X1_oHBiocfgWiBfUfKOXE1g5J.JtqXYIBee8ROyx3sVIc7V2I0eotZCCiCJ1k74ru2OGC9UhX__ZESGrp9b0JZGZFO6w97IL1Y3BTgVXNox8L3FcFjl11s7YcYBhc_3e3WbJA4pZzczPzd0ouZiCjUcBCZXNu6fnM6XerBbsVj584ZbPdjQ6A5TcrED8dTcFdgfMWlMu7bHsE7e0QIJYmc2g5NWeur2ovAOPVxhELrvnJ6X6Z.06vAmwdJcl4hyYRVqA.9QynHlH2dezXS5y0eKXFUzg.tHNqZboEGutLL316mZQYvwZwATZFc8iO3EuiNV3MWbiCrLX.n3nbXp82A4v7Bv.PVJoeOTmiPmq8vuxts8nUEPf7gA3j6.DmlDvfZQNx9.WNmNFps0B08hzDMvKX98.ft9.GJlftPCYCk2qHRtWm3tjB2R9O6mF6XYvOLUK7aQ4rz5oCxHwmrF.n56G07MS5GS1pjV3ByUO88PI1i_rPbim_Up0jIO5q7PqXudyFE.nG0Dc8yaExZk7PryBfSHk2yMQVXcBjy3XQBVmrIwPwKs.YUFC1qLSThfA8UNckik2xuDZKvf29xpGvHlMbH67UV5HhY5CTeqlgvapRThmfrexqaBTTPPqR.Sy3Pvr9vHX.0UC6kNCTlxW3CExrx9EipRKUvRVClNLEG6M9hm5MQkdfgmd2Dmj5DmjcrZkWOqnNfLn_mtYdoD3nK4zCk.NZwXTFlx5FVr5bOzyncalNdb77qLEH9E9R8d2KUA4Axljx29kycILhZCvy3Qz22Vz_M72lVFKQAFnGlERRS7NLu2rB00e.MNeH9aB01uj.y9UHpfjTAwOJSFEBf7yKRCkVzqFpGAQo7txlC3NRgSyI3kQtW0WeWiNzghBv8c_5iXWkXqFyRfVZUCw2qqow3BF6SDtkhXAr8d.JrprGdG67U66ZD3JB10OLmmAaDiiCr81eVOkrsz3ONdigZOlP7RrYtodAORnheCZ10SAYbaru0sKmsJx4sB41QKn79dieWs5oihI5Rr3jUzPjfYgav5jeEfUYz5qHTtmps4vVJjt9s4zueRiT8AYzg.Xv.wdC1dvBr_kpGxwrkMEZJ0QfZkH.TDWBSWyxeG9gtuKd1gy9CGQTXlLvonJWHwK9gwWZwRKIanV4gnMfWz4pvhGixzEaiRU6g9giKYHpVKe.w0cwPfSDIhbBYqrlvuxkSTXfuFNyTpHM0Mgc6PMNnH6GooeLVK3IO2M8dRdDbL7EHykIJWe85pm5BxSqFTcWXn1nGuVWQV.F.J1XYJj1KheT725MVRlrE3sH.MgY6m4hKCUXT7mIgJ6xsfAQ8dWn7V1fCpqoIYub55iw_dg8yfZ7Yfgwj9bqN81lvTWQzBMLKYpG6pOh37JsTikpXa9kTSJfbJm0_GYZv_Bu10D1WqiO0UCvJXrlqkl5F610fkXkhwRXvmIQL2Mv_R71zSKqMr1g5RmnNifUv43jpXoOWmOKscg59pv8eOyb2JG26BtyVS2XOTDw6pHpHKuksqRKt62i1z83uv_ve5rO0zIKeMnWGlvXKHn8ld3fqGyJvqZ7wqYpnV_LTE3p73XCLEeniClXyk5Q_Icu1PgakYh3GIz8RVBjbjgrZIsvvkEp0Kgeo6oFY0wnboIwDlaUVHIue1nPkuAIsVk9i_IJWeqoRV0eO0wUGWgQ5.HT9RzcKdFVe2kl84RKuQkvoibiCfGlfTXpOfLOZtwsfl2TuNgZtP5oG1Wfsgi6g2a1N8B2WHYN8kZuG8pMnc4NQDIoO8BDnz89lDt7Y9JCslIM3O2Y25EGsylNKamKPJZr.ZSvvTVJG5sxftLxvF8vc8h5_pJD9Akj.84CjqQyfsnzFqc_.EqBaRVSy35wHHrdWTBsvjcobtSed8wXs.5En6HdubdATcgthozHWMEMsUdA29XkenPic2cR.8bQ5VdGZ2_YkJIXrxgt_XoEFFB8tgy061cghyL2fc2fHMQm4KBEc1vjkZjwRo9adWbg9dzAl782AwKC.C72bw--
\ No newline at end of file
+8Yd1y.bSJBPj3Ul5NjhQHFX0dN0NuJAfBObEdgIY2QDh0bVXkzcJbNlBamPSFBZqHojxbPzIj5GlY3XdxGMY6ttYzBHvc_FK5krngYtlLSmBtjRArxI_bBfngIxeI2kFC3wPvHIdzp8gygRp2.IlDahWHABFhAPrPrxAkAOqv6O3tKUTTzcnZX22eOdoXVXQSMiphlDQIxpfbdpLnduBvMxvI.hwrfAW4iIVY7iBjkdnrtdAVj4N2.u65khGuAwaX9i6Buca3XQJm601Atf6fjM57yOFA0.laixrOss1_F36iqrc4mOeWnYI_UDw8SgIrXroswrJ.KwXlqCdICxel3Ui0hfrOoGnCt4HHT9tiObu9VG.GxM2KQ_tjb1LwFFX0WEisFKXfQwpR_2NU..0j1icWhnmPHFJGsImvX1dFgeTqcNvoAcfZ0y9c8KQKzV5CbQeg9_oPC7uviIdxQK2lJ7SzgRfZAw4FDeDXKDxuVZRE7jZYQEWtkmp9dFlFLM_L0dPk4kiJuRrRPJ8DAyWSiex_ib1mJ3bHAxbsEcWoUunIOivWVdZDudCBath8AdoAQ0P8_f.fl8jYgbZil49Qhg.WmFD_H0w7ikdEi1iHsWnjSrDORtAjENGvoEje0h.gMBf38FbQ6vW2BbKC.RBKp.cQhVPvDHel1UFtqZ8IJm7mTP0BDSDG2iFJp1_N7L5MUoUxEwgskMxeQf99vE1UybdR4q0kbx6i_RR3_6.AQ5QKk1EBeKAfnVYDdwcLGVgWd9JxxvekkL7Scl1ncZ9ViLvlC3Hfrbtrm2Q5rr17pIG6L.a6gDKKH1NGgapyLUCVtKZ27Ysd9ExMF1yHfMAyOKL5I7KaJmZTZnxWQ9ex.hHS6tZ9WjPFhjvpve7H_mB8ZXNBdEc4bL.iRdknHNe3WSSgCLWf2RVFZ4wmVMYW4KwGpIp7iGbaUDBYdBFkcCmv6zeneEsLGJHUYipsay5unopuFyBe611pwFRNHZ3XznmqaK8TXl18k_PCmQ6g93.fXFeWKmszw_FArltYuXHNIm3zwRobvDo7Rp0znnt76Q0ha3q5WHggFd6.QmC6jywGLpki_09v0Pc6qGh4gpGt1vZeZrA1OGQ7VSB7Muo4Mr4TbQU_6342nXeG0tBImIanPZsQrew1.tOmAfn16UpzcCuEC3NWw99yipIaEZirdr4teITB7xX3x6UGRcjtoAlz8oo6VEYaFX7ybG6Bo.K4hspnJLWH6jKK4hcpYedE2qSB0W4z_ieFy6Y.2qrx8_3CL1_8h5w6PjXOFTmsyk0v0DXQVuGh8QxPgPVyrWyDzlXJZ.5_mHTohGRGCI41I6jGJcCOh3qEQZKF2VYb.3o00Le9YlXxpBIBh7rBw5vNypp9jSd.W6VPnQ53xM6QzVbYW4OV3gkVE__AgvKNUzfRHiK9dN7evwvKX66W4tUPGQapjGbRWnFc3Q_0__oMYTtSR3EMxs0Lvkjh87s0SMxy0gzvVCfNt4VrdZCzdBd8N8Tcj5Pc4KJ09ikPtNB3V8i6jtcVUuKzs32B37O6AA2iOydQdsPDEhfkDA0S_ocu3DqqBsJOYmzjRq39e1WFhlRijNftxHF6DRvzpSIIm5VzcwIg8LLl4zbHyeRXEmBDp.KvlfP_.z2IUqilhGeccCmV2XFD_VFidZclzscmceMmuBOXYmaplSW3sX94eM7mp2dyDjVXtR4igukNU8Fabaqilu98OKIv9mLdTlE9u7t25KNqMHQQOsYNM4BUocalSN1K3JOot544vz9VbWJfIljF2L6DCduJd6h9aDH5wLMjljRqrteltA3yz86ensnOZ5wmU8X3NMpPN.OCXM8zxqdhEGxE_jeXkpf4AKIFWYrn8zH84sFU60gpy3xMOleKK6NfrFDRdzxkBIJGV7yhBaDi1_0iZuwm8hVLFe2odNyEKbllCeXYpeMJwe3w_e7XQD1T2Zv4bvK69qYPhmkngPs7_nCYYUFWKlvkg0njZyxn3kNSTONHjzrLSlTWMvGobei1GV74wJzWmmZpC1yQNOe_8Stjbj5Lg47iELys5kCMWU1NyIQf16AT35xTlPCYJ_RNVy3yDNeh5kqy9AMX_HIJOHw8xwJEUoiqKzWzLV.BWmpzcny9_7P2naXBrzRlOYmYyWUtp.P0gGNxLZXtEaKKGgdQB.fYXUyuKH0ptXqj1YCl1g1KWo3.AwndjDy0PE8ZYCaCjz.EyXqx.8bvfJycccGRw5RVU74aydE0DRCC3vF6VI9qbPWK89HNTMWxPSSTHdChY6yAhNsc5UhDwsAmhZ_UIYynZ8AM_oCS232.einbw9fYI4X6yZm_fD60DJ1FsBweU9ivf3Y4X2ucLFY8xPp_8SxZ5UtaP67TKflzMYjuvhGXAUF9.1u8ZKJGOliFmMxIwJ7lPJs93HfZgLN2eyH6F.rZjREeyd0fbsRzZ_vMEs4fx6sX4eIGVoyVFpO5b11kxnBqlKnATI_nw1.exkl5sQj00clwphsecXiVLaDFCEjG94ve_1cw2foqQUoX6pUfqr_hSSpnacdacPclbxWPehuN5LpjMso_5b4v.rj4s9VtzRqxovBinIgBdTFAH560aN5f7wuN5ZZT_BT5aCFNwQBYoDfCTaU9jw3aaXU29vEJmYZcvC9WCpU0RTntciGlx8rIbagu7cUGrYFvLbU2ZekazCMklPO5Sno_z5zzxLnTpFxesNoreOj7M3Cp1RHKUmqAog04bYC9kPFvH_q.jF2g507LEN3nPL9EJUx460UG_o4HRQOhDi2YrmrHk.gLZECgnMwCN770fhL
\ No newline at end of file
diff --git a/scripts/rebuilddb_psql.sh b/scripts/rebuilddb_psql.sh
new file mode 100755 (executable)
index 0000000..ac169c2
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# ******************************* WARNING *********************************
+# Do not run this script until you have read and understood the information
+# below, AND backed up your database. Failure to observe these instructions
+# may result in losing all the data in your database.
+#
+# This script is used to upgrade Laconica's PostgreSQL database to the
+# latest version. It does the following:
+# 
+#  1. Dumps the existing data to /tmp/rebuilddb_psql.sql
+#  2. Clears out the objects (tables, etc) in the database schema
+#  3. Reconstructs the database schema using the latest script
+#  4. Restores the data dumped in step 1
+#
+# You MUST run this script as the 'postgres' user.
+# You MUST be able to write to /tmp/rebuilddb_psql.sql
+# You MUST specify the laconica database user and database name on the
+# command line, e.g. ./rebuilddb_psql.sh myuser mydbname
+#
+
+user=$1
+DB=$2
+
+cd `dirname $0`
+
+pg_dump -a -D --disable-trigger $DB > /tmp/rebuilddb_psql.sql
+psql -c "drop schema public cascade; create schema public;" $DB
+psql -c "grant all privileges on schema public to $user;" $DB
+psql $DB < ../db/laconica_pg.sql
+psql $DB < /tmp/rebuilddb_psql.sql
+for tab in `psql -c '\dts' $DB -tA | cut -d\| -f2`; do
+  psql -c "ALTER TABLE \"$tab\" OWNER TO $user;" $DB
+done
index fd4406d415b35f766c200c5e3dc85cf2705edde5..2bb8f9ecb2a6d02c7b66b443c35778c17b95de72 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 # Laconica - a distributed open-source microblogging tool
 
 SDIR=`dirname $0`
 DIR=`php $SDIR/getpiddir.php`
 
-for f in jabberhandler ombhandler publichandler smshandler \
+for f in jabberhandler ombhandler publichandler smshandler pinghandler \
         xmppconfirmhandler xmppdaemon twitterhandler facebookhandler ; do
 
        FILES="$DIR/$f.*.pid"
        for ff in "$FILES" ; do
 
-               echo -n "Stopping $f..."
-               PID=`cat $ff`
-               kill -3 $PID
-               if kill -9 $PID ; then
-                       echo "DONE."
-               else
-                       echo "FAILED."
+               PID=`cat $ff 2>/dev/null`
+               if [ -n "$PID" ] ; then
+                       echo -n "Stopping $f ($PID)..."
+                       if kill -3 $PID 2>/dev/null ; then
+                               count=0
+                               while kill -0 $PID 2>/dev/null ;  do
+                                       sleep 1
+                                       count=$(($count + 1))
+                                       if [ $count -gt 5 ]; then break; fi
+                               done
+                               if kill -9 $PID 2>/dev/null ; then
+                                       echo "FORCIBLY TERMINATED"
+                               else
+                                       echo "STOPPED CLEANLY"
+                               fi
+                       else
+                               echo "NOT FOUND"
+                       fi
                fi
                rm -f $ff
        done
index f3526d514c65fdc2b338ac230ea64e4a11cd6c18..a7f5e4d3a0f7dec92894db9a9e778bab3a0518dd 100755 (executable)
@@ -1,3 +1,3 @@
 cd `dirname $0`
 cd ..
-xgettext --from-code=UTF-8 --default-domain=laconica --output=locale/laconica.pot --language=PHP --join-existing actions/*.php classes/*.php lib/*.php scripts/*.php
+xgettext --from-code=UTF-8 --default-domain=laconica --output=locale/laconica.po --language=PHP --join-existing actions/*.php classes/*.php lib/*.php scripts/*.php
diff --git a/theme/base/css/modal.css b/theme/base/css/modal.css
deleted file mode 100644 (file)
index 985e4ad..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * SimpleModal Basic Modal Dialog
- * http://www.ericmmartin.com/projects/simplemodal/
- * http://code.google.com/p/simplemodal/
- *
- * Copyright (c) 2008 Eric Martin - http://ericmmartin.com
- *
- * Licensed under the MIT license:
- *   http://www.opensource.org/licenses/mit-license.php
- *
- * Revision: $Id: basic.css 162 2008-12-01 23:36:58Z emartin24 $
- *
- */
-
-
-/* Overlay */
-#simplemodal-overlay {background-color:#000; cursor:wait;}
-
-/* Container */
-#simplemodal-container {height:240px; width:320px; background-color:#fff; border:3px solid #ccc;}
-#simplemodal-container a.modalCloseImg {background:url(../images/x.png) no-repeat; width:25px; height:29px; display:inline; z-index:3200; position:absolute; top:-15px; right:-18px; cursor:pointer;}
-#simplemodal-container #basicModalContent {padding:8px;}
diff --git a/theme/base/css/modal_ie.css b/theme/base/css/modal_ie.css
deleted file mode 100644 (file)
index eab4637..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * SimpleModal Basic Modal Dialog
- * http://www.ericmmartin.com/projects/simplemodal/
- * http://code.google.com/p/simplemodal/
- *
- * Copyright (c) 2008 Eric Martin - http://ericmmartin.com
- *
- * Licensed under the MIT license:
- *   http://www.opensource.org/licenses/mit-license.php
- *
- * Revision: $Id: basic_ie.css 162 2008-12-01 23:36:58Z emartin24 $
- *
- */
-
-/* IE 6 hacks*/
-#simplemodal-container a.modalCloseImg {background:none; right:-14px; width:22px; height:26px; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../images/x.png',sizingMethod='scale');}
diff --git a/theme/base/images/x.png b/theme/base/images/x.png
deleted file mode 100644 (file)
index c11f7af..0000000
Binary files a/theme/base/images/x.png and /dev/null differ