]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.9.x' of git@gitorious.org:statusnet/mainline into 0.9.x
authorEvan Prodromou <evan@status.net>
Tue, 15 Sep 2009 21:16:35 +0000 (17:16 -0400)
committerEvan Prodromou <evan@status.net>
Tue, 15 Sep 2009 21:16:35 +0000 (17:16 -0400)
73 files changed:
EVENTS.txt
README
actions/all.php
actions/api.php
actions/avatarsettings.php
actions/editgroup.php
actions/emailsettings.php
actions/foaf.php
actions/grouplogo.php
actions/groupsearch.php
actions/invite.php
actions/login.php
actions/noticesearch.php
actions/othersettings.php
actions/passwordsettings.php
actions/peoplesearch.php
actions/profilesettings.php
actions/public.php
actions/register.php
actions/smssettings.php
actions/subscriptions.php
actions/twitapifriendships.php
actions/twitapigroups.php
actions/twitapitags.php
actions/twittersettings.php
classes/File.php
classes/Notice.php
classes/Status_network.php
classes/User.php
db/08to09.sql
db/08to09_pg.sql
db/statusnet_pg.sql
doc-src/contact
extlib/OAuth.php
extlib/Services/oEmbed.php
extlib/Stomp.php
index.php
install.php
lib/Shorturl_api.php
lib/command.php
lib/commandinterpreter.php
lib/designsettings.php
lib/galleryaction.php
lib/htmloutputter.php
lib/mailbox.php
lib/oauthclient.php
lib/plugin.php
lib/twitter.php
lib/twitterapi.php
lib/twitterbasicauthclient.php [new file with mode: 0644]
lib/twitteroauthclient.php
lib/util.php
plugins/LilUrl/LilUrlPlugin.php [new file with mode: 0644]
plugins/LinkbackPlugin.php
plugins/OpenID/openidlogin.php
plugins/OpenID/openidsettings.php
plugins/PtitUrl/PtitUrlPlugin.php [new file with mode: 0644]
plugins/PubSubHubBub/PubSubHubBubPlugin.php [new file with mode: 0644]
plugins/PubSubHubBub/publisher.php [new file with mode: 0644]
plugins/SimpleUrl/SimpleUrlPlugin.php [new file with mode: 0644]
plugins/TightUrl/TightUrlPlugin.php [new file with mode: 0644]
scripts/synctwitterfriends.php
scripts/twitterstatusfetcher.php
tests/URLDetectionTest.php
theme/base/css/display.css
theme/biz/css/base.css
theme/biz/css/display.css
theme/biz/logo.png
theme/cloudy/logo.png
theme/default/css/display.css
theme/h4ck3r/logo.png
theme/identica/css/display.css
theme/pigeonthoughts/logo.png

index 05d172585549f413a4be6e3ed457ad38044f7d80..121ae175d0d675c59dbd2684a025b4646e1b5569 100644 (file)
@@ -247,3 +247,10 @@ StartLoadDoc: before loading a help doc (hook this to show your own documentatio
 EndLoadDoc: after loading a help doc (hook this to modify other documentation)
 - $title: title of the document
 - $output: HTML output to show
+
+StartApiRss: after the rss <channel> element is started
+- $action: action object being shown
+
+StartApiAtom: after the <feed> element is started
+- $action: action object being shown
+
diff --git a/README b/README
index 173ea4c253dcc12b69df28dd8769b421aa8b7b19..f3b2528b85ab6bd1932ce997badd5266efb65f73 100644 (file)
--- a/README
+++ b/README
@@ -146,6 +146,7 @@ Your PHP installation must include the following PHP extensions:
 - GD. For scaling down avatar images.
 - mbstring. For handling Unicode (UTF-8) encoded strings.
 - gettext. For multiple languages. Default on many PHP installs.
+- tidy. Used to clean up HTML/URLs for the URL shortener to consume.
 
 For some functionality, you will also need the following extensions:
 
index 29a19afb6ba9d27e9b6ab406178312f4443d7752..e56e10c21faab6a30ede7d0c9855a72067d1be59 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/*
+/**
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, StatusNet, Inc.
  *
  *
  * 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 Actions
+ * @package  Actions
+ * @author   Evan Prodromou <evan@prodromou.name>
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @author   Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @author   Adrian Lang <mail@adrianlang.de>
+ * @author   Meitar Moscovitz <meitarm@gmail.com>
+ * @author   Sarven Capadisli <csarven@controlyourself.ca>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Evan Prodromou <evan@controlezvous.ca>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Jeffery To <jeffery.to@gmail.com>
+ * @author   Zach Copley <zach@controlyourself.ca>
+ * @author   csarven <csarven@controlyourself.ca>
+ * @license  GNU Affero General Public License http://www.gnu.org/licenses/
+ * @link     http://status.net
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+if (!defined('STATUSNET') && !defined('LACONICA')) { 
+    exit(1); 
+}
 
 require_once INSTALLDIR.'/lib/personalgroupnav.php';
 require_once INSTALLDIR.'/lib/noticelist.php';
@@ -43,8 +63,8 @@ class AllAction extends ProfileAction
             $this->notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
         }
 
-        if($this->page > 1 && $this->notice->N == 0){
-            $this->serverError(_('No such page'),$code=404);
+        if ($this->page > 1 && $this->notice->N == 0) {
+            $this->serverError(_('No such page'), $code = 404);
         }
 
         return true;
@@ -73,20 +93,33 @@ class AllAction extends ProfileAction
 
     function getFeeds()
     {
-        return array(new Feed(Feed::RSS1,
-                              common_local_url('allrss', array('nickname' =>
-                                                               $this->user->nickname)),
-                              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_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_timeline',
-                                                            'argument' => $this->user->nickname.'.atom')),
-                              sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname)));
+        return array(
+            new Feed(Feed::RSS1,
+                common_local_url(
+                    'allrss', array(
+                        'nickname' =>
+                        $this->user->nickname)
+                ),
+                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_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_timeline',
+                        'argument' => $this->user->nickname.'.atom'
+                    )
+                ),
+                sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))
+        );
     }
 
     function showLocalNav()
@@ -106,8 +139,7 @@ class AllAction extends ProfileAction
             } else {
                 $message .= sprintf(_('You can try to [nudge %s](../%s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
             }
-        }
-        else {
+        } else {
             $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
         }
 
@@ -126,17 +158,19 @@ class AllAction extends ProfileAction
             $this->showEmptyListMessage();
         }
 
-        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
-                          $this->page, 'all', array('nickname' => $this->user->nickname));
+        $this->pagination(
+            $this->page > 1, $cnt > NOTICES_PER_PAGE,
+            $this->page, 'all', array('nickname' => $this->user->nickname)
+        );
     }
 
     function showPageTitle()
     {
         $user =& common_current_user();
         if ($user && ($user->id == $this->user->id)) {
-            $this->element('h1', NULL, _("You and friends"));
+            $this->element('h1', null, _("You and friends"));
         } else {
-            $this->element('h1', NULL, sprintf(_('%s and friends'), $this->user->nickname));
+            $this->element('h1', null, sprintf(_('%s and friends'), $this->user->nickname));
         }
     }
 
index 93e33085f93346bb2474dc6c856693d0624a22ac..9b5f548074838b647ec176dfb0bf14b2c5bf911a 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/*
+/**
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, StatusNet, Inc.
  *
  *
  * 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 Actions
+ * @package  Actions
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @author   Evan Prodromou <evan@prodromou.name>
+ * @author   Brenda Wallace <shiny@cpan.org>
+ * @author   Jeffery To <jeffery.to@gmail.com>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @author   Tom Adams <tom@holizz.com>
+ * @author   Christopher Vollick <psycotica0@gmail.com>
+ * @author   CiaranG <ciaran@ciarang.com>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Evan Prodromou <evan@controlezvous.ca>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Gina Haeussge <osd@foosel.net>
+ * @author   Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author   Sarven Capadisli <csarven@controlyourself.ca>
+ * @license  GNU Affero General Public License http://www.gnu.org/licenses/
+ * @link     http://status.net
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+if (!defined('STATUSNET') && !defined('LACONICA')) { 
+    exit(1); 
+}
 
 class ApiAction extends Action
 {
@@ -27,6 +48,8 @@ class ApiAction extends Action
     var $api_arg;
     var $api_method;
     var $api_action;
+    var $auth_user;
+    var $auth_pw;
 
     function handle($args)
     {
@@ -35,6 +58,7 @@ class ApiAction extends Action
         $this->api_action = $this->arg('apiaction');
         $method = $this->arg('method');
         $argument = $this->arg('argument');
+        $this->basic_auth_process_header();
 
         if (isset($argument)) {
             $cmdext = explode('.', $argument);
@@ -43,30 +67,30 @@ class ApiAction extends Action
             $this->content_type = strtolower($cmdext[1]);
         } else {
 
-            Requested format / content-type will be an extension on the method
+            //Requested format / content-type will be an extension on the method
             $cmdext = explode('.', $method);
             $this->api_method = $cmdext[0];
             $this->content_type = strtolower($cmdext[1]);
         }
 
         if ($this->requires_auth()) {
-            if (!isset($_SERVER['PHP_AUTH_USER'])) {
+            if (!isset($this->auth_user)) {
 
-                This header makes basic auth go
+                //This header makes basic auth go
                 header('WWW-Authenticate: Basic realm="StatusNet API"');
 
-                If the user hits cancel -- bam!
+                //If the user hits cancel -- bam!
                 $this->show_basic_auth_error();
             } else {
-                $nickname = $_SERVER['PHP_AUTH_USER'];
-                $password = $_SERVER['PHP_AUTH_PW'];
+                $nickname = $this->auth_user;
+                $password = $this->auth_pw;
                 $user = common_check_user($nickname, $password);
 
                 if ($user) {
                     $this->user = $user;
                     $this->process_command();
                 } else {
-                    basic authentication failed
+                    //basic authentication failed
                     list($proxy, $ip) = common_client_ip();
 
                     common_log(LOG_WARNING, "Failed API auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
@@ -76,12 +100,12 @@ class ApiAction extends Action
         } else {
 
             // Caller might give us a username even if not required
-            if (isset($_SERVER['PHP_AUTH_USER'])) {
-                $user = User::staticGet('nickname', $_SERVER['PHP_AUTH_USER']);
+            if (isset($this->auth_user)) {
+                $user = User::staticGet('nickname', $this->auth_user);
                 if ($user) {
                     $this->user = $user;
                 }
-                Twitter doesn't throw an error if the user isn't found
+                //Twitter doesn't throw an error if the user isn't found
             }
 
             $this->process_command();
@@ -94,7 +118,7 @@ class ApiAction extends Action
         $actionfile = INSTALLDIR."/actions/$action.php";
 
         if (file_exists($actionfile)) {
-            require_once($actionfile);
+            include_once $actionfile;
             $action_class = ucfirst($action)."Action";
             $action_obj = new $action_class();
 
@@ -110,10 +134,10 @@ class ApiAction extends Action
 
                 call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata);
             } else {
-                $this->clientError("API method not found!", $code=404);
+                $this->clientError("API method not found!", $code = 404);
             }
         } else {
-            $this->clientError("API method not found!", $code=404);
+            $this->clientError("API method not found!", $code = 404);
         }
     }
 
@@ -133,6 +157,8 @@ class ApiAction extends Action
                                 'groups/show',
                                 'groups/timeline',
                                 'groups/list_all',
+                                'groups/membership',
+                                'groups/is_member',
                                 'groups/timeline');
 
         static $bareauth = array('statuses/user_timeline',
@@ -179,10 +205,11 @@ class ApiAction extends Action
             $user_id     = $this->arg('user_id');
             $screen_name = $this->arg('screen_name');
 
-            if (empty($this->api_arg) &&
-                empty($id)            &&
-                empty($user_id)       &&
-                empty($screen_name)) {
+            if (empty($this->api_arg) 
+                && empty($id)            
+                && empty($user_id)       
+                && empty($screen_name)
+            ) {
                 return true;
             } else {
                 return false;
@@ -201,6 +228,33 @@ class ApiAction extends Action
         }
     }
 
+    function basic_auth_process_header()
+    {
+        if (isset($_SERVER['AUTHORIZATION']) || isset($_SERVER['HTTP_AUTHORIZATION'])) {
+            $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['AUTHORIZATION'];
+        }
+
+        if (isset($_SERVER['PHP_AUTH_USER'])) {
+            $this->auth_user = $_SERVER['PHP_AUTH_USER'];
+            $this->auth_pw = $_SERVER['PHP_AUTH_PW'];
+        } elseif (isset($authorization_header) && strstr(substr($authorization_header, 0, 5), 'Basic')) {
+            // decode the HTTP_AUTHORIZATION header on php-cgi server self
+            // on fcgid server the header name is AUTHORIZATION
+
+            $auth_hash = base64_decode(substr($authorization_header, 6));
+            list($this->auth_user, $this->auth_pw) = explode(':', $auth_hash);
+
+            // set all to null on a empty basic auth request
+            if ($this->auth_user == "") {
+                $this->auth_user = null;
+                $this->auth_pw = null;
+            }
+        } else {
+            $this->auth_user = null;
+            $this->auth_pw = null;
+        }
+    }
+
     function show_basic_auth_error()
     {
         header('HTTP/1.1 401 Unauthorized');
@@ -214,7 +268,7 @@ class ApiAction extends Action
             $this->element('request', null, $_SERVER['REQUEST_URI']);
             $this->elementEnd('hash');
             $this->endXML();
-        } else if ($this->content_type == 'json')  {
+        } else if ($this->content_type == 'json') {
             header('Content-Type: application/json; charset=utf-8');
             $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
             print(json_encode($error_array));
index 0bc439ff122a944f653ca8bd7978e38ec77d691a..02a684b38f2649c5e8d45a3d096114e631a8bf6d 100644 (file)
@@ -399,5 +399,7 @@ class AvatarsettingsAction extends AccountSettingsAction
             $this->script('js/jcrop/jquery.Jcrop.min.js');
             $this->script('js/jcrop/jquery.Jcrop.go.js');
         }
+
+        $this->autofocus('avatarfile');
     }
 }
index c92ca6ec65fdfd5c6854016d24c79e83e8de00ae..0c2dc8bdf89ef027141d09d38ae1de434696bfc7 100644 (file)
@@ -160,6 +160,12 @@ class EditgroupAction extends GroupDesignAction
         }
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('nickname');
+    }
+
     function trySave()
     {
         $cur = common_current_user();
index af528a892ef877556b02dc05b173a1171f612d2e..6eff06c0d667b66fc6056ed706a83a7141efb89a 100644 (file)
@@ -71,6 +71,12 @@ class EmailsettingsAction extends AccountSettingsAction
         return _('Manage how you get email from %%site.name%%.');
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('email');
+    }
+
     /**
      * Content area of the page
      *
index 4dae9dfc19aeae6dafc73236ea2068556920e333..356393304ed5abbc0a243bcd68e8974d395aa05b 100644 (file)
@@ -146,8 +146,10 @@ class FoafAction extends Action
             while ($sub->fetch()) {
                 if ($sub->token) {
                     $other = Remote_profile::staticGet('id', $sub->subscriber);
+                    $profile = Profile::staticGet('id', $sub->subscriber);
                 } else {
                     $other = User::staticGet('id', $sub->subscriber);
+                    $profile = Profile::staticGet('id', $sub->subscriber);
                 }
                 if (!$other) {
                     common_debug('Got a bad subscription: '.print_r($sub,true));
@@ -158,12 +160,15 @@ class FoafAction extends Action
                 } else {
                     $person[$other->uri] = array(LISTENER,
                                                  $other->id,
-                                                 $other->nickname,
+                                                 $profile->nickname,
                                                  (empty($sub->token)) ? 'User' : 'Remote_profile');
                 }
                 $other->free();
                 $other = null;
                 unset($other);
+                $profile->free();
+                $profile = null;
+                unset($profile);
             }
         }
 
@@ -254,8 +259,10 @@ class FoafAction extends Action
                 while ($sub->fetch()) {
                     if (!empty($sub->token)) {
                         $other = Remote_profile::staticGet('id', $sub->subscribed);
+                        $profile = Profile::staticGet('id', $sub->subscribed);
                     } else {
                         $other = User::staticGet('id', $sub->subscribed);
+                        $profile = Profile::staticGet('id', $sub->subscribed);
                     }
                     if (empty($other)) {
                         common_debug('Got a bad subscription: '.print_r($sub,true));
@@ -264,11 +271,14 @@ class FoafAction extends Action
                     $this->element('sioc:follows', array('rdf:resource' => $other->uri.'#acct'));
                     $person[$other->uri] = array(LISTENEE,
                                                  $other->id,
-                                                 $other->nickname,
+                                                 $profile->nickname,
                                                  (empty($sub->token)) ? 'User' : 'Remote_profile');
                     $other->free();
                     $other = null;
                     unset($other);
+                    $profile->free();
+                    $profile = null;
+                    unset($profile);
                 }
             }
 
index c6f376915ef2e44ef2c0696c2fe549c59da30bc1..63ba769c7a61feb5a2e5eb71d513859496123cdf 100644 (file)
@@ -445,6 +445,8 @@ class GrouplogoAction extends GroupDesignAction
             $this->script('js/jcrop/jquery.Jcrop.min.js');
             $this->script('js/jcrop/jquery.Jcrop.go.js');
         }
+
+        $this->autofocus('avatarfile');
     }
 
     function showLocalNav()
index ee4167552a467c8019feaf8fcc2359bc1cab0703..f0cca715615aa3940b5a145507d2f915467a1a2d 100644 (file)
@@ -90,6 +90,12 @@ class GroupsearchAction extends SearchAction
             $user_group->free();
         }
     }
+
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('q');
+    }
 }
 
 class GroupSearchResults extends GroupList
index 9f9368701cbcb1ec279c1ba981671c60cf8b32b3..788130c5825e4989a9e7f93fc6baf0270fe184bd 100644 (file)
@@ -98,6 +98,12 @@ class InviteAction extends CurrentUserDesignAction
         $this->showPage();
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('addresses');
+    }
+
     function title()
     {
         if ($this->mode == 'sent') {
index 4b00b2f08622aa6a98430b822d700ac1edb2e307..f6d0163105f42675a8667c55411ce4a8c96626e7 100644 (file)
@@ -22,6 +22,7 @@
  * @category  Login
  * @package   StatusNet
  * @author    Evan Prodromou <evan@status.net>
+ * @author    Sarven Capadisli <csarven@status.net>
  * @copyright 2008-2009 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://status.net/
@@ -37,6 +38,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
  * @category Personal
  * @package  StatusNet
  * @author   Evan Prodromou <evan@status.net>
+ * @author   Sarven Capadisli <csarven@status.net>
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://status.net/
  */
@@ -158,6 +160,12 @@ class LoginAction extends Action
         $this->showPage();
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('nickname');
+    }
+
     /**
      * Title of the page
      *
index 734e43464800df221f2862671919b28a73c0c71a..79cf572ccaf59f2e4e2a5777966bd886c7625dc2 100644 (file)
@@ -135,6 +135,12 @@ class NoticesearchAction extends SearchAction
         $this->pagination($page > 1, $cnt > NOTICES_PER_PAGE,
                           $page, 'noticesearch', array('q' => $q));
     }
+
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('q');
+    }
 }
 
 class SearchNoticeList extends NoticeList {
@@ -190,7 +196,7 @@ class SearchNoticeListItem extends NoticeListItem {
         $result  = preg_replace($pattern, '<strong>\\1</strong>', $text);
 
         /* Remove highlighting from inside links, loop incase multiple highlights in links */
-        $pattern = '/(href="[^"]*)<strong>('.$options.')<\/strong>([^"]*")/iU';
+        $pattern = '/(\w+="[^"]*)<strong>('.$options.')<\/strong>([^"]*")/iU';
         do {
             $result = preg_replace($pattern, '\\1\\2\\3', $result, -1, $count);
         } while ($count);
index 8b674161a89bb1a28408e4e91b158083a5492167..011b4fc83832e7c39de789855ea44067318a871f 100644 (file)
@@ -71,6 +71,12 @@ class OthersettingsAction extends AccountSettingsAction
         return _('Manage various other options.');
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('urlshorteningservice');
+    }
+
     /**
      * Content area of the page
      *
@@ -91,19 +97,20 @@ class OthersettingsAction extends AccountSettingsAction
         $this->elementStart('fieldset');
         $this->hidden('token', common_session_token());
 
-        // I18N
-
-        $services = array(
-                          '' => 'None',
-                          'ur1.ca' => 'ur1.ca (free service)',
-                          '2tu.us' => '2tu.us (free service)',
-                          'ptiturl.com' => 'ptiturl.com',
-                          'bit.ly' => 'bit.ly',
-                          'tinyurl.com' => 'tinyurl.com',
-                          'is.gd' => 'is.gd',
-                          'snipr.com' => 'snipr.com',
-                          'metamark.net' => 'metamark.net'
-                          );
+        $services=array();
+        global $_shorteners;
+        if($_shorteners){
+            foreach($_shorteners as $name=>$value)
+            {
+                $services[$name]=$name;
+                if($value['info']['freeService']){
+                    // I18N
+                    $services[$name].=' (free service)';
+                }
+            }
+        }
+        asort($services);
+        $services['']='None';
 
         $this->elementStart('ul', 'form_data');
         $this->elementStart('li');
index ec842600f6c1c252206aa7a1a5f117e93140c99f..cd4beac3f2120a9c83a81f4ddc8a4e98c8573777 100644 (file)
@@ -69,6 +69,12 @@ class PasswordsettingsAction extends AccountSettingsAction
         return _('Change your password.');
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('oldpassword');
+    }
+
     /**
      * Content area of the page
      *
index ba0f71e392165acb4fa9d55d99ab6765ebe2187d..38135ecbdeac6076ad1207e27705c5fab658bcfe 100644 (file)
@@ -85,6 +85,12 @@ class PeoplesearchAction extends SearchAction
             $profile->free();
         }
     }
+
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('q');
+    }
 }
 
 /**
index 83051c6fd3e8a2cb778c2b32f42a2dc106fff962..5445d9bb257ee0c513ead7c2799a1f11958cbe81 100644 (file)
@@ -23,6 +23,7 @@
  * @package   StatusNet
  * @author    Evan Prodromou <evan@status.net>
  * @author    Zach Copley <zach@status.net>
+ * @author    Sarven Capadisli <csarven@status.net>
  * @copyright 2008-2009 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://status.net/
@@ -41,6 +42,7 @@ require_once INSTALLDIR.'/lib/accountsettingsaction.php';
  * @package  StatusNet
  * @author   Evan Prodromou <evan@status.net>
  * @author   Zach Copley <zach@status.net>
+ * @author   Sarven Capadisli <csarven@status.net>
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://status.net/
  */
@@ -70,6 +72,12 @@ class ProfilesettingsAction extends AccountSettingsAction
                   'so people know more about you.');
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('nickname');
+    }
+
     /**
      * Content area of the page
      *
index 86b0d6f56b2350200cc0402c7e320e90c4103913..73fad182a3f2ab71e7452f349637826dfb95d2d9 100644 (file)
@@ -225,10 +225,10 @@ class PublicAction extends Action
     function showAnonymousMessage()
     {
         if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
-            $m = _('This is %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+            $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                    'based on the Free Software [StatusNet](http://status.net/) tool. ' .
-                   '[Join now](%%%%action.register%%%%) to share notices about yourself with friends, family, and colleagues! ' .
-                   '([Read more](%%%%doc.help%%%%))');
+                   '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ' .
+                   '([Read more](%%doc.help%%))');
         } else {
             $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                    'based on the Free Software [StatusNet](http://status.net/) tool.');
index b40ac266d2eceb6d5f869e6f70bdf59fd3b4645f..100ab74242c74a4244739a3fcc46a56ca937c027 100644 (file)
@@ -136,6 +136,12 @@ class RegisterAction extends Action
         }
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('nickname');
+    }
+
     /**
      * Try to register a user
      *
index b956ccebaaa178773fa8c05459535ab91ce03576..672abcef8c15587e0eda8e089578e4bbea496c0a 100644 (file)
@@ -69,6 +69,12 @@ class SmssettingsAction extends ConnectSettingsAction
         return _('You can receive SMS messages through email from %%site.name%%.');
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('sms');
+    }
+
     /**
      * Content area of the page
      *
index b1c6682283c2ab1473219cfb077d2fb7f43b206c..cc7b38ee461f21d21f44408d6b645a70d086db00 100644 (file)
@@ -107,6 +107,12 @@ class SubscriptionsAction extends GalleryAction
                           array('nickname' => $this->user->nickname));
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('tag');
+    }
+
     function showEmptyListMessage()
     {
         if (common_logged_in()) {
index f2ea46910ee92edbf0c0618b1e51e66787085e0a..eea8945c393402d7080bda3534f38116ffe9bc7b 100644 (file)
@@ -99,6 +99,12 @@ class TwitapifriendshipsAction extends TwitterapiAction
         $other = $this->get_profile($id);
         $user = $apidata['user']; // Alwyas the auth user
 
+       if ($user->id == $other->id) {
+           $this->clientError(_("You cannot unfollow yourself!"),
+                              403, $apidata['content-type']);
+           return;
+       }
+
         $sub = new Subscription();
         $sub->subscriber = $user->id;
         $sub->subscribed = $other->id;
index 214fa8214f9b6e19a5fe73885bec4ed90302c30b..4deb1b764adb7ba6d8bebbef666b8e314a22c82d 100644 (file)
@@ -21,7 +21,7 @@
  *
  * @category  Twitter
  * @package   StatusNet
- * @author    Craig Andrews
+ * @author    Craig Andrews <candrews@integralblue.com>
  * @author    Zach Copley <zach@status.net>
  * @copyright 2009 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
@@ -41,7 +41,7 @@ require_once INSTALLDIR.'/lib/twitterapi.php';
  *
  * @category  Twitter
  * @package   StatusNet
- * @author    Craig Andrews
+ * @author    Craig Andrews <candrews@integralblue.com>
  * @author    Zach Copley <zach@status.net>
  * @copyright 2009 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
@@ -233,4 +233,97 @@ require_once INSTALLDIR.'/lib/twitterapi.php';
          }
      }
 
+     function membership($args, $apidata)
+     {
+         parent::handle($args);
+
+         common_debug("in groups api action");
+
+         $this->auth_user = $apidata['user'];
+         $group = $this->get_group($apidata['api_arg'], $apidata);
+         
+         if (empty($group)) {
+             $this->clientError('Not Found', 404, $apidata['content-type']);
+             return;
+         }
+
+         $sitename   = common_config('site', 'name');
+         $title      = sprintf(_("Members of %s group"), $group->nickname);
+         $taguribase = common_config('integration', 'taguri');
+         $id         = "tag:$taguribase:GroupMembership:".$group->id;
+         $link       = common_local_url('showgroup',
+             array('nickname' => $group->nickname));
+         $subtitle   = sprintf(_('Members of %1$s on %2$s'),
+             $group->nickname, $sitename);
+
+         $page     = (int)$this->arg('page', 1);
+         $count    = (int)$this->arg('count', 20);
+         $max_id   = (int)$this->arg('max_id', 0);
+         $since_id = (int)$this->arg('since_id', 0);
+         $since    = $this->arg('since');
+
+         $member = $group->getMembers(($page-1)*$count,
+             $count, $since_id, $max_id, $since);
+
+         switch($apidata['content-type']) {
+          case 'xml':
+             $this->show_twitter_xml_users($member);
+             break;
+          //TODO implement the RSS and ATOM content types
+          /*case 'rss':
+             $this->show_rss_users($member, $title, $link, $subtitle);
+             break;*/
+          /*case 'atom':
+             if (isset($apidata['api_arg'])) {
+                 $selfuri = common_root_url() .
+                     'api/statusnet/groups/membership/' .
+                         $apidata['api_arg'] . '.atom';
+             } else {
+                 $selfuri = common_root_url() .
+                  'api/statusnet/groups/membership.atom';
+             }
+             $this->show_atom_users($member, $title, $id, $link,
+                 $subtitle, null, $selfuri);
+             break;*/
+          case 'json':
+             $this->show_json_users($member);
+             break;
+          default:
+             $this->clientError(_('API method not found!'), $code = 404);
+         }
+     }
+
+     function is_member($args, $apidata)
+     {
+         parent::handle($args);
+
+         common_debug("in groups api action");
+
+         $this->auth_user = $apidata['user'];
+         $group = User_group::staticGet($args['group_id']);
+         if(! $group){
+            $this->clientError(_('Group not found'), $code = 500);
+         }
+         $user = User::staticGet('id', $args['user_id']);
+         if(! $user){
+            $this->clientError(_('User not found'), $code = 500);
+         }
+         
+         $is_member=$user->isMember($group);
+
+         switch($apidata['content-type']) {
+          case 'xml':
+             $this->init_document('xml');
+             $this->element('is_member', null, $is_member);
+             $this->end_document('xml');
+             break;
+          case 'json':
+             $this->init_document('json');
+             $this->show_json_objects(array('is_member'=>$is_member));
+             $this->end_document('json');
+             break;
+          default:
+             $this->clientError(_('API method not found!'), $code = 404);
+         }
+     }
 }
index 2bb7ee01abb0cb98034fa92524a501806c1abd57..0bcc55d378faa869739a491cc8baaf5a8a4638d0 100644 (file)
@@ -21,7 +21,7 @@
  *
  * @category  Twitter
  * @package   StatusNet
- * @author    Craig Andrews
+ * @author    Craig Andrews <candrews@integralblue.com>
  * @author    Zach Copley <zach@status.net>
  * @copyright 2009 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
@@ -41,7 +41,7 @@ require_once INSTALLDIR.'/lib/twitterapi.php';
  *
  * @category  Twitter
  * @package   StatusNet
- * @author    Craig Andrews
+ * @author    Craig Andrews <candrews@integralblue.com>
  * @author    Zach Copley <zach@status.net>
  * @copyright 2009 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
index 563d867a49be7dd6b3357f12b93308acdb09e1a0..89169941eb4424cbdc7a960e8f0d4df982e81421 100644 (file)
@@ -165,7 +165,7 @@ class TwittersettingsAction extends ConnectSettingsAction
                                 ($flink->noticesync & FOREIGN_NOTICE_RECV) :
                                 false);
                 $this->elementEnd('li');
-
+            } else {
                 // preserve setting even if bidrection bridge toggled off
 
                 if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) {
index dc668ab235fdbc263561591808357a58547514a9..9758cf7f5576a5db08150740032ba276994de0a1 100644 (file)
@@ -78,14 +78,14 @@ class File extends Memcached_DataObject
         $file_id = $x->insert();
 
         if (isset($redir_data['type'])
-            && ('text/html' === substr($redir_data['type'], 0, 9))
+            && (('text/html' === substr($redir_data['type'], 0, 9) || 'application/xhtml+xml' === substr($redir_data['type'], 0, 21)))
             && ($oembed_data = File_oembed::_getOembed($given_url))) {
                 File_oembed::saveNew($oembed_data, $file_id);
         }
         return $x;
     }
 
-    function processNew($given_url, $notice_id) {
+    function processNew($given_url, $notice_id=null) {
         if (empty($given_url)) return -1;   // error, no url to process
         $given_url = File_redirection::_canonUrl($given_url);
         if (empty($given_url)) return -1;   // error, no url to process
@@ -96,7 +96,7 @@ class File extends Memcached_DataObject
                 $redir_data = File_redirection::where($given_url);
                 $redir_url = $redir_data['url'];
                 // TODO: max field length
-                if ($redir_url === $given_url || strlen($redir_url) > 255) { 
+                if ($redir_url === $given_url || strlen($redir_url) > 255) {
                     $x = File::saveNew($redir_data, $given_url);
                     $file_id = $x->id;
                 } else {
@@ -119,7 +119,9 @@ class File extends Memcached_DataObject
             }
         }
 
-        File_to_post::processNew($file_id, $notice_id);
+        if (!empty($notice_id)) {
+            File_to_post::processNew($file_id, $notice_id);
+        }
         return $x;
     }
 
index e569d8305a3175d637513543b450d1936bbbdf95..2138e0561299094cb34bb6c1a2dc6407399fe034 100644 (file)
@@ -56,7 +56,7 @@ class Notice extends Memcached_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-    /* Notice types */ 
+    /* Notice types */
     const LOCAL_PUBLIC    =  1;
     const REMOTE_OMB      =  0;
     const LOCAL_NONPUBLIC = -1;
@@ -260,17 +260,6 @@ class Notice extends Memcached_DataObject
 
             $notice->saveUrls();
 
-            // FIXME: why do we have to re-render the content?
-            // Remove this if it's not necessary.
-
-            $orig2 = clone($notice);
-
-            $notice->rendered = common_render_content($final, $notice);
-            if (!$notice->update($orig2)) {
-                common_log_db_error($notice, 'UPDATE', __FILE__);
-                return _('Problem saving notice.');
-            }
-
             $notice->query('COMMIT');
 
             Event::handle('EndNoticeSave', array($notice));
@@ -755,6 +744,10 @@ class Notice extends Memcached_DataObject
             return new ArrayWrapper($notices);
         } else {
             $notice = new Notice();
+            if (empty($ids)) {
+                //if no IDs requested, just return the notice object
+                return $notice;
+            }
             $notice->whereAdd('id in (' . implode(', ', $ids) . ')');
             $notice->orderBy('id DESC');
 
index d526cb4d617ab49651df4138617bc43f944edd80..fe4f0b0c580c7f14d8fc0c91150488fc3331abdc 100644 (file)
@@ -54,7 +54,7 @@ class Status_network extends DB_DataObject
         global $config;
 
         $config['db']['database_'.$dbname] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname";
-        $config['db']['ini_'.$dbname] = INSTALLDIR.'/classes/statusnet.ini';
+        $config['db']['ini_'.$dbname] = INSTALLDIR.'/classes/status_network.ini';
         $config['db']['table_status_network'] = $dbname;
 
         self::$cache = new Memcache();
index 58f9a6874e2534385a12e79086f1321ca72fe86f..11cb4f08b4b5a70132ee4581238aee392960f628 100644 (file)
@@ -103,10 +103,7 @@ class User extends Memcached_DataObject
         }
         $toupdate = implode(', ', $parts);
 
-        $table = $this->tableName();
-        if(common_config('db','quote_identifiers')) {
-            $table = '"' . $table . '"';
-        }
+        $table = common_database_tablename($this->tableName());
         $qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
           ' WHERE id = ' . $this->id;
         $orig->decache();
@@ -630,11 +627,7 @@ class User extends Memcached_DataObject
           'ORDER BY subscription.created DESC ';
 
         if ($offset) {
-            if (common_config('db','type') == 'pgsql') {
-                $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-            } else {
-                $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-            }
+            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
         }
 
         $profile = new Profile();
@@ -657,11 +650,7 @@ class User extends Memcached_DataObject
           'AND subscription.subscribed != subscription.subscriber ' .
           'ORDER BY subscription.created DESC ';
 
-        if (common_config('db','type') == 'pgsql') {
-            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-        } else {
-            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-        }
+        $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
 
         $profile = new Profile();
 
index 223a99fa37a96722bccd177c78554e496bfa8eee..953e0e5f481a6fcf8d9905fe5eb868c18cc2392e 100644 (file)
@@ -12,3 +12,23 @@ alter table user_group
 
 alter table file_oembed
      add column mimetype varchar(50) comment 'mime type of resource';
+
+create table config (
+
+    section varchar(32) comment 'configuration section',
+    setting varchar(32) comment 'configuration setting',
+    value varchar(255) comment 'configuration value',
+
+    constraint primary key (section, setting)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table user_role (
+
+    user_id integer not null comment 'user having the role' references user (id),
+    role    varchar(32) not null comment 'string representing the role',
+    created datetime not null comment 'date the role was granted',
+
+    constraint primary key (user_id, role)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
index 892df4a39f626e72dd850aeb89929ad7221efef2..9e37314aa82f92b849ef6fe81cab228f40092ee2 100644 (file)
@@ -1,2 +1,40 @@
-// SQL commands to update an 0.8.x version of Laconica
-// to 0.9.x.
+-- SQL commands to update an 0.8.x version of Laconica
+-- to 0.9.x.
+
+--these are just comments
+/*
+alter table notice
+     modify column content text comment 'update content';
+
+alter table message
+     modify column content text comment 'message content';
+
+alter table profile
+     modify column bio text comment 'descriptive biography';
+
+alter table user_group
+     modify column description text comment 'group description';
+*/
+
+alter table file_oembed
+     add column mimetype varchar(50) /*comment 'mime type of resource'*/;
+
+create table config (
+
+    section varchar(32) /* comment 'configuration section'*/,
+    setting varchar(32) /* comment 'configuration setting'*/,
+    value varchar(255) /* comment 'configuration value'*/,
+
+    primary key (section, setting)
+
+);
+
+create table user_role (
+
+    user_id integer not null /* comment 'user having the role'*/ references "user" (id),
+    role    varchar(32) not null /* comment 'string representing the role'*/,
+    created timestamp /* not null comment 'date the role was granted'*/,
+
+    primary key (user_id, role)
+
+);
index 5b4d0485af3f202f4470cc9f13149f1a9f3b8738..672877ddf0bac25ba3bdfdbcb0c18e8a14460470 100644 (file)
@@ -549,3 +549,23 @@ create index noticecontent_idx on notice using gist(to_tsvector('english',conten
 create trigger textsearchupdate before insert or update on profile for each row
 execute procedure tsvector_update_trigger(textsearch, 'pg_catalog.english', nickname, fullname, location, bio, homepage);
 
+
+create table config (
+
+    section varchar(32) /* comment 'configuration section'*/,
+    setting varchar(32) /* comment 'configuration setting'*/,
+    value varchar(255) /* comment 'configuration value'*/,
+
+    primary key (section, setting)
+
+);
+
+create table user_role (
+
+    user_id integer not null /* comment 'user having the role'*/ references "user" (id),
+    role    varchar(32) not null /* comment 'string representing the role'*/,
+    created timestamp /* not null comment 'date the role was granted'*/,
+
+    primary key (user_id, role)
+
+);
index 31f3a4d2b335b9538e77de316f60493f1fb1df3b..c63fcd01ad8be36119a3621429fa5dc92f2892de 100644 (file)
@@ -13,7 +13,7 @@ Bugs
 ----
 
 If you think you've found a bug in the [StatusNet](http://status.net/) software,
-or if there's a new feature you'd like to see, add it into the [StatusNet bug database](http://status.net/PITS/HomePage). Don't forget to check the list of
+or if there's a new feature you'd like to see, add it into the [StatusNet bug database](http://status.net/bugs/). Don't forget to check the list of
 existing bugs to make sure it hasn't already been reported!
 
 Email
index 029166175c5f4b6ad07fac4bb47ea0502c7b95f9..fd485355459843c2bbf41d6240d687423d56d6f9 100644 (file)
@@ -199,7 +199,8 @@ class OAuthRequest {/*{{{*/
     } 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") ) {
+      if ($http_method == "POST" &&
+        ( @strstr($request_headers["Content-Type"], "application/x-www-form-urlencoded") || @strstr($_ENV["CONTENT_TYPE"], "application/x-www-form-urlencoded") )) {
         $req_parameters = array_merge($req_parameters, $_POST);
       }
 
index 7d507b6f622cea9ada872dbe1cde3c4e020586ba..0dc8f01b2f4580ba3734ec133370ec58cca33628 100644 (file)
@@ -256,7 +256,7 @@ class Services_oEmbed
 
         $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
         if (substr($code, 0, 1) != '2') {
-            throw new Services_oEmbed_Exception('Non-200 code returned');
+            throw new Services_oEmbed_Exception('Non-200 code returned. Got code ' . $code);
         }
 
         curl_close($ch);
@@ -302,8 +302,8 @@ class Services_oEmbed
 
         // Find all <link /> tags that have a valid oembed type set. We then
         // extract the href attribute for each type.
-        $regexp = '#<link([^>]*)type="' . 
-                  '(application/json|text/xml)\+oembed"([^>]*)>#i';
+        $regexp = '#<link([^>]*)type[\s\n]*=[\s\n]*"' . 
+                  '(application/json|text/xml)\+oembed"([^>]*)>#im';
 
         $m = $ret = array();
         if (!preg_match_all($regexp, $body, $m)) {
@@ -314,7 +314,7 @@ class Services_oEmbed
 
         foreach ($m[0] as $i => $link) {
             $h = array();
-            if (preg_match('/href="([^"]+)"/i', $link, $h)) {
+            if (preg_match('/[\s\n]+href[\s\n]*=[\s\n]*"([^"]+)"/im', $link, $h)) {
                 $ret[$m[2][$i]] = $h[1];
             }
         } 
@@ -347,7 +347,7 @@ class Services_oEmbed
 
         $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
         if (substr($code, 0, 1) != '2') {
-            throw new Services_oEmbed_Exception('Non-200 code returned');
+            throw new Services_oEmbed_Exception('Non-200 code returned. Got code ' . $code);
         }
 
         return $result;
index 9e1c97b3b32b47551fac51c5944f9e26bc930033..abd9cba62b44f633f97b75a2fce83424eb958748 100644 (file)
@@ -454,7 +454,7 @@ class Stomp
      */
     public function disconnect ()
     {
-               $header = array();
+               $headers = array();
 
                if ($this->clientId != null) {
                        $headers["client-id"] = $this->clientId;
index e2296549f3efd96e5b5ff284265ad14152ff5d08..a1d983dce7723ddc35d905df654699a6c05eff49 100644 (file)
--- a/index.php
+++ b/index.php
  *
  * 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 StatusNet
+ * @package  StatusNet
+ * @license  GNU Affero General Public License http://www.gnu.org/licenses/
+ * @author Brenda Wallace <shiny@cpan.org> 
+ * @author Christopher Vollick <psycotica0@gmail.com> 
+ * @author CiaranG <ciaran@ciarang.com>
+ * @author Craig Andrews <candrews@integralblue.com> 
+ * @author Evan Prodromou <evan@controlezvous.ca>
+ * @author Evan Prodromou <evan@controlyourself.ca> 
+ * @author Evan Prodromou <evan@prodromou.name>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Gina Haeussge <osd@foosel.net> 
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <millette@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@controlyourself.ca> 
+ * @author Tom Adams <tom@holizz.com>
  */
 
 define('INSTALLDIR', dirname(__FILE__));
@@ -29,7 +47,8 @@ $action = null;
 function getPath($req)
 {
     if ((common_config('site', 'fancy') || !array_key_exists('PATH_INFO', $_SERVER))
-        && array_key_exists('p', $req)) {
+        && array_key_exists('p', $req)
+    ) {
         return $req['p'];
     } else if (array_key_exists('PATH_INFO', $_SERVER)) {
         return $_SERVER['PATH_INFO'];
@@ -45,28 +64,35 @@ function handleError($error)
     }
 
     $logmsg = "PEAR error: " . $error->getMessage();
-    if(common_config('site', 'logdebug')) {
+    if (common_config('site', 'logdebug')) {
         $logmsg .= " : ". $error->getDebugInfo();
     }
     common_log(LOG_ERR, $logmsg);
-    if(common_config('site', 'logdebug')) {
+    if (common_config('site', 'logdebug')) {
         $bt = $error->getBacktrace();
         foreach ($bt as $line) {
             common_log(LOG_ERR, $line);
         }
     }
-    if ($error instanceof DB_DataObject_Error ||
-        $error instanceof DB_Error) {
-        $msg = sprintf(_('The database for %s isn\'t responding correctly, '.
-                         'so the site won\'t work properly. '.
-                         'The site admins probably know about the problem, '.
-                         'but you can contact them at %s to make sure. '.
-                         'Otherwise, wait a few minutes and try again.'),
-                       common_config('site', 'name'),
-                       common_config('site', 'email'));
+    if ($error instanceof DB_DataObject_Error 
+        || $error instanceof DB_Error
+    ) {
+        $msg = sprintf(
+            _(
+                'The database for %s isn\'t responding correctly, '.
+                'so the site won\'t work properly. '.
+                'The site admins probably know about the problem, '.
+                'but you can contact them at %s to make sure. '.
+                'Otherwise, wait a few minutes and try again.'
+            ),
+            common_config('site', 'name'),
+            common_config('site', 'email')
+        );
     } else {
-        $msg = _('An important error occured, probably related to email setup. '.
-                 'Check logfiles for more info..');
+        $msg = _(
+            'An important error occured, probably related to email setup. '.
+            'Check logfiles for more info..'
+        );
     }
 
     $dac = new DBErrorAction($msg, 500);
@@ -127,10 +153,11 @@ function main()
         $_lighty_url = @parse_url($_lighty_url);
 
         if ($_lighty_url['path'] != '/index.php' && $_lighty_url['path'] != '/') {
-            $_lighty_path = preg_replace('/^'.preg_quote(common_config('site','path')).'\//', '', substr($_lighty_url['path'], 1));
+            $_lighty_path = preg_replace('/^'.preg_quote(common_config('site', 'path')).'\//', '', substr($_lighty_url['path'], 1));
             $_SERVER['QUERY_STRING'] = 'p='.$_lighty_path;
-            if ($_lighty_url['query'])
+            if ($_lighty_url['query']) {
                 $_SERVER['QUERY_STRING'] .= '&'.$_lighty_url['query'];
+            }
             parse_str($_lighty_url['query'], $_lighty_query);
             foreach ($_lighty_query as $key => $val) {
                 $_GET[$key] = $_REQUEST[$key] = $val;
@@ -141,7 +168,7 @@ function main()
     $_SERVER['REDIRECT_URL'] = preg_replace("/\?.+$/", "", $_SERVER['REQUEST_URI']);
 
     // quick check for fancy URL auto-detection support in installer.
-    if (isset($_SERVER['REDIRECT_URL']) && (preg_replace("/^\/$/","",(dirname($_SERVER['REQUEST_URI']))) . '/check-fancy') === $_SERVER['REDIRECT_URL']) {
+    if (isset($_SERVER['REDIRECT_URL']) && (preg_replace("/^\/$/", "", (dirname($_SERVER['REQUEST_URI']))) . '/check-fancy') === $_SERVER['REDIRECT_URL']) {
         die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs.");
     }
     global $user, $action;
@@ -149,8 +176,12 @@ function main()
     Snapshot::check();
 
     if (!_have_config()) {
-        $msg = sprintf(_("No configuration file found. Try running ".
-                         "the installation program first."));
+        $msg = sprintf(
+            _(
+                "No configuration file found. Try running ".
+                "the installation program first."
+            )
+        );
         $sac = new ServerErrorAction($msg);
         $sac->showPage();
         return;
@@ -196,9 +227,10 @@ function main()
     // If the site is private, and they're not on one of the "public"
     // parts of the site, redirect to login
 
-    if (!$user && common_config('site', 'private') &&
-        !isLoginAction($action) &&
-        !preg_match('/rss$/', $action)) {
+    if (!$user && common_config('site', 'private') 
+        && !isLoginAction($action) 
+        && !preg_match('/rss$/', $action)
+    ) {
         common_redirect(common_local_url('login'));
         return;
     }
index 4c5bc38aee6e78cd6723b2ad4aad6fdf4a1a298f..07a7bfaaf7148ce3c0d2e0cae40cacf906b6b757 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/**
+/** 
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2009, StatusNet, Inc.
  *
  *
  * 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 Installation
+ * @package  Installation
+ * 
+ * @author   Adrian Lang <mail@adrianlang.de>
+ * @author   Brenda Wallace <shiny@cpan.org>
+ * @author   Brett Taylor <brett@webfroot.co.nz>
+ * @author   Brion Vibber <brion@pobox.com>
+ * @author   CiaranG <ciaran@ciarang.com>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Eric Helgeson <helfire@Erics-MBP.local>
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @author   Sarven Capadisli <csarven@controlyourself.ca>
+ * @author   Tom Adams <tom@holizz.com>
+ * @license  GNU Affero General Public License http://www.gnu.org/licenses/
  */
-
 define('INSTALLDIR', dirname(__FILE__));
 
 $external_libraries=array(
@@ -181,17 +198,34 @@ $external_libraries=array(
         'check_class'=>'Validate'
     )
 );
+$dbModules = array(
+    'mysql' => array(
+        'name' => 'MySQL',
+        'check_module' => 'mysql', // mysqli?
+        'installer' => 'mysql_db_installer',
+    ),
+    'pgsql' => array(
+        'name' => 'PostgreSQL',
+        'check_module' => 'pgsql',
+        'installer' => 'pgsql_db_installer',
+    ),
+);
 
+/** 
+ * the actual installation.
+ * If call libraries are present, then install
+ * 
+ * @return void
+ */
 function main()
 {
-    if (!checkPrereqs())
-    {
+    if (!checkPrereqs()) {
         return;
     }
     
-    if( $_GET['checklibs'] ){
+    if ($_GET['checklibs']) {
         showLibs();
-    }else{
+    } else {
         if ($_SERVER['REQUEST_METHOD'] == 'POST') {
             handlePost();
         } else {
@@ -200,71 +234,97 @@ function main()
     }
 }
 
+/**
+ * checks if an external libary is present
+ *
+ * @param string $external_library Name of library
+ *
+ * @return boolean indicates if library present
+ */
 function haveExternalLibrary($external_library)
 {
-    if(isset($external_library['include']) && ! include_once($external_library['include'])){
+    if (isset($external_library['include']) && ! include_once $external_library['include'] ) {
         return false;
     }
-    if(isset($external_library['check_function']) && ! function_exists($external_library['check_function'])){
+    if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
         return false;
     }
-    if(isset($external_library['check_class']) && ! class_exists($external_library['check_class'])){
+    if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
         return false;
     }
     return true;
 }
 
+/**
+ * Check if all is ready for installation
+ *
+ * @return void
+ */
 function checkPrereqs()
 {
-       $pass = true;
+    $pass = true;
 
     if (file_exists(INSTALLDIR.'/config.php')) {
-         ?><p class="error">Config file &quot;config.php&quot; already exists.</p>
-         <?php
+         printf('<p class="error">Config file &quot;config.php&quot; already exists.</p>');
         $pass = false;
     }
 
     if (version_compare(PHP_VERSION, '5.2.3', '<')) {
-            ?><p class="error">Require PHP version 5.2.3 or greater.</p><?php
-                   $pass = false;
+        printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
+        $pass = false;
     }
 
     $reqs = array('gd', 'curl',
-                  'xmlwriter', 'mbstring');
+                  'xmlwriter', 'mbstring','tidy');
 
     foreach ($reqs as $req) {
         if (!checkExtension($req)) {
-            ?><p class="error">Cannot load required extension: <code><?php echo $req; ?></code></p><?php
-                   $pass = false;
+            printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
+            $pass = false;
+        }
+    }    
+    // Make sure we have at least one database module available
+    global $dbModules;
+    $missingExtensions = array();
+    foreach ($dbModules as $type => $info) {
+        if (!checkExtension($info['check_module'])) {
+            $missingExtensions[] = $info['check_module'];
         }
     }
-    if (!checkExtension('pgsql') && !checkExtension('mysql')) {
-      ?><p class="error">Cannot find mysql or pgsql extension. You need one or the other: <code><?php echo $req; ?></code></p><?php
-                    $pass = false;
-    }
-
-       if (!is_writable(INSTALLDIR)) {
-         ?><p class="error">Cannot write config file to: <code><?php echo INSTALLDIR; ?></code></p>
-              <p>On your server, try this command: <code>chmod a+w <?php echo INSTALLDIR; ?></code>
-         <?php
-            $pass = false;
-       }
-
-       // Check the subdirs used for file uploads
-       $fileSubdirs = array('avatar', 'background', 'file');
-       foreach ($fileSubdirs as $fileSubdir) {
-               $fileFullPath = INSTALLDIR."/$fileSubdir/";
-               if (!is_writable($fileFullPath)) {
-            ?><p class="error">Cannot write <?php echo $fileSubdir; ?> directory: <code><?php echo $fileFullPath; ?></code></p>
-                      <p>On your server, try this command: <code>chmod a+w <?php echo $fileFullPath; ?></code></p>
-            <?php
-                    $pass = false;
-               }
-       }
-
-       return $pass;
+    
+    if (count($missingExtensions) == count($dbModules)) {
+        $req = implode(', ', $missingExtensions);
+        printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other: <code>%s</code></p>', $req);
+        $pass = false;
+    }
+    
+    if (!is_writable(INSTALLDIR)) {
+        printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
+        printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
+        $pass = false;
+    }
+
+    // Check the subdirs used for file uploads
+    $fileSubdirs = array('avatar', 'background', 'file');
+    foreach ($fileSubdirs as $fileSubdir) {
+        $fileFullPath = INSTALLDIR."/$fileSubdir/";
+        if (!is_writable($fileFullPath)) {
+            printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
+            printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
+            $pass = false;
+        }
+    }
+
+    return $pass;
 }
 
+/**
+ * Checks if a php extension is both installed and loaded
+ *
+ * @param string $name of extension to check
+ *
+ * @return boolean whether extension is installed and loaded
+ */
 function checkExtension($name)
 {
     if (!extension_loaded($name)) {
@@ -275,15 +335,20 @@ function checkExtension($name)
     return true;
 }
 
+/**
+ * Show list of libraries
+ *
+ * @return void
+ */
 function showLibs()
 {
     global $external_libraries;
     $present_libraries=array();
     $absent_libraries=array();
-    foreach($external_libraries as $external_library){
-        if(haveExternalLibrary($external_library)){
+    foreach ($external_libraries as $external_library) {
+        if (haveExternalLibrary($external_library)) {
             $present_libraries[]=$external_library;
-        }else{
+        } else {
             $absent_libraries[]=$external_library;
         }
     }
@@ -298,22 +363,21 @@ function showLibs()
     <h2>Absent Libraries</h2>
     <ul id="absent_libraries">
 E_O_T;
-    foreach($absent_libraries as $library)
-    {
+    foreach ($absent_libraries as $library) {
         echo '<li>';
-        if($library['url']){
+        if ($library['url']) {
             echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
-        }else{
+        } else {
             echo htmlentities($library['name']);
         }
         echo '<ul>';
-        if($library['deb']){
+        if ($library['deb']) {
             echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
         }
-        if($library['rpm']){
+        if ($library['rpm']) {
             echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
         }
-        if($library['pear']){
+        if ($library['pear']) {
             echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
         }
         echo '</ul>';
@@ -323,12 +387,11 @@ E_O_T;
     <h2>Installed Libraries</h2>
     <ul id="present_libraries">
 E_O_T;
-    foreach($present_libraries as $library)
-    {
+    foreach ($present_libraries as $library) {
         echo '<li>';
-        if($library['url']){
+        if ($library['url']) {
             echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
-        }else{
+        } else {
             echo htmlentities($library['name']);
         }
         echo '</li>';
@@ -340,6 +403,15 @@ E_O_T;
 
 function showForm()
 {
+    global $dbModules;
+    $dbRadios = '';
+    $checked = 'checked="checked" '; // Check the first one which exists
+    foreach ($dbModules as $type => $info) {
+        if (checkExtension($info['check_module'])) {
+            $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
+            $checked = '';
+        }
+    }
     echo<<<E_O_T
         </ul>
     </dd>
@@ -376,8 +448,7 @@ function showForm()
             <li>
 
                 <label for="dbtype">Type</label>
-                <input type="radio" name="dbtype" id="fancy-mysql" value="mysql" checked='checked' /> MySQL<br />
-                <input type="radio" name="dbtype" id="dbtype-pgsql" value="pgsql" /> PostgreSQL<br />
+                $dbRadios
                 <p class="form_guide">Database type</p>
             </li>
 
@@ -406,17 +477,12 @@ E_O_T;
 
 function updateStatus($status, $error=false)
 {
-?>
-                <li <?php echo ($error) ? 'class="error"': ''; ?>><?php echo $status;?></li>
-
-<?php
+    echo '<li ' . ($error) ? 'class="error"': '';
+    echo ">$status</li>";
 }
 
 function handlePost()
 {
-?>
-
-<?php
     $host     = $_POST['host'];
     $dbtype   = $_POST['dbtype'];
     $database = $_POST['database'];
@@ -427,55 +493,41 @@ function handlePost()
     $server = $_SERVER['HTTP_HOST'];
     $path = substr(dirname($_SERVER['PHP_SELF']), 1);
 
-?>
+    echo <<<STR
     <dl class="system_notice">
         <dt>Page notice</dt>
         <dd>
             <ul>
-<?php
-       $fail = false;
+STR;
+    $fail = false;
 
     if (empty($host)) {
         updateStatus("No hostname specified.", true);
-               $fail = true;
+        $fail = true;
     }
 
     if (empty($database)) {
         updateStatus("No database specified.", true);
-               $fail = true;
+        $fail = true;
     }
 
     if (empty($username)) {
         updateStatus("No username specified.", true);
-               $fail = true;
+        $fail = true;
     }
 
-//     if (empty($password)) {
-//         updateStatus("No password specified.", true);
-//             $fail = true;
-//     }
-
     if (empty($sitename)) {
         updateStatus("No sitename specified.", true);
-               $fail = true;
+        $fail = true;
     }
 
-    if($fail){
-            showForm();
+    if ($fail) {
+        showForm();
         return;
     }
 
-    // FIXME: use PEAR::DB or PDO instead of our own switch
-
-    switch($dbtype) {
-        case 'mysql':
-            $db = mysql_db_installer($host, $database, $username, $password);
-            break;
-        case 'pgsql':
-            $db = pgsql_db_installer($host, $database, $username, $password);
-            break;
-        default:
-    }
+    global $dbModules;
+    $db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
 
     if (!$db) {
         // database connection failed, do not move on to create config file.
@@ -498,112 +550,110 @@ function handlePost()
 
     updateStatus("StatusNet has been installed at $link");
     updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
-?>
-
-<?php
 }
 
-function pgsql_db_installer($host, $database, $username, $password) {
-  $connstring = "dbname=$database host=$host user=$username";
-
-  //No password would mean trust authentication used.
-  if (!empty($password)) {
-    $connstring .= " password=$password";
-  }
-  updateStatus("Starting installation...");
-  updateStatus("Checking database...");
-  $conn = pg_connect($connstring);
-
-  if ($conn ===false) {
-    updateStatus("Failed to connect to database: $connstring");
-    showForm();
-    return false;
-  }
-
-  //ensure database encoding is UTF8
-  $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
-  if ($record->server_encoding != 'UTF8') {
-    updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
-    showForm();
-    return false;
-  }
-
-  updateStatus("Running database script...");
-  //wrap in transaction;
-  pg_query($conn, 'BEGIN');
-  $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
-
-  if ($res === false) {
-      updateStatus("Can't run database script.", true);
-      showForm();
-      return false;
-  }
-  foreach (array('sms_carrier' => 'SMS carrier',
+function Pgsql_Db_installer($host, $database, $username, $password) 
+{
+    $connstring = "dbname=$database host=$host user=$username";
+
+    //No password would mean trust authentication used.
+    if (!empty($password)) {
+        $connstring .= " password=$password";
+    }
+    updateStatus("Starting installation...");
+    updateStatus("Checking database...");
+    $conn = pg_connect($connstring);
+
+    if ($conn ===false) {
+        updateStatus("Failed to connect to database: $connstring");
+        showForm();
+        return false;
+    }
+
+    //ensure database encoding is UTF8
+    $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
+    if ($record->server_encoding != 'UTF8') {
+        updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
+        showForm();
+        return false;
+    }
+
+    updateStatus("Running database script...");
+    //wrap in transaction;
+    pg_query($conn, 'BEGIN');
+    $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
+
+    if ($res === false) {
+        updateStatus("Can't run database script.", true);
+        showForm();
+        return false;
+    }
+    foreach (array('sms_carrier' => 'SMS carrier',
                 'notice_source' => 'notice source',
                 'foreign_services' => 'foreign service')
           as $scr => $name) {
-      updateStatus(sprintf("Adding %s data to database...", $name));
-      $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
-      if ($res === false) {
-          updateStatus(sprintf("Can't run %d script.", $name), true);
-          showForm();
-          return false;
-      }
-  }
-  pg_query($conn, 'COMMIT');
-
-  if (empty($password)) {
-    $sqlUrl = "pgsql://$username@$host/$database";
-  }
-  else {
-    $sqlUrl = "pgsql://$username:$password@$host/$database";
-  }
-
-  $db = array('type' => 'pgsql', 'database' => $sqlUrl);
-
-  return $db;
+        updateStatus(sprintf("Adding %s data to database...", $name));
+        $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
+        if ($res === false) {
+            updateStatus(sprintf("Can't run %d script.", $name), true);
+            showForm();
+            return false;
+        }
+    }
+    pg_query($conn, 'COMMIT');
+
+    if (empty($password)) {
+        $sqlUrl = "pgsql://$username@$host/$database";
+    } else {
+        $sqlUrl = "pgsql://$username:$password@$host/$database";
+    }
+
+    $db = array('type' => 'pgsql', 'database' => $sqlUrl);
+
+    return $db;
 }
 
-function mysql_db_installer($host, $database, $username, $password) {
-  updateStatus("Starting installation...");
-  updateStatus("Checking database...");
-
-  $conn = mysql_connect($host, $username, $password);
-  if (!$conn) {
-      updateStatus("Can't connect to server '$host' as '$username'.", true);
-      showForm();
-      return false;
-  }
-  updateStatus("Changing to database...");
-  $res = mysql_select_db($database, $conn);
-  if (!$res) {
-      updateStatus("Can't change to database.", true);
-      showForm();
-      return false;
-  }
-  updateStatus("Running database script...");
-  $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
-  if ($res === false) {
-      updateStatus("Can't run database script.", true);
-      showForm();
-      return false;
-  }
-  foreach (array('sms_carrier' => 'SMS carrier',
+function Mysql_Db_installer($host, $database, $username, $password) 
+{
+    updateStatus("Starting installation...");
+    updateStatus("Checking database...");
+
+    $conn = mysql_connect($host, $username, $password);
+    if (!$conn) {
+        updateStatus("Can't connect to server '$host' as '$username'.", true);
+        showForm();
+        return false;
+    }
+    updateStatus("Changing to database...");
+    $res = mysql_select_db($database, $conn);
+    if (!$res) {
+        updateStatus("Can't change to database.", true);
+        showForm();
+        return false;
+    }
+    updateStatus("Running database script...");
+    $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
+    if ($res === false) {
+        updateStatus("Can't run database script.", true);
+        showForm();
+        return false;
+    }
+    foreach (array('sms_carrier' => 'SMS carrier',
                 'notice_source' => 'notice source',
                 'foreign_services' => 'foreign service')
           as $scr => $name) {
-      updateStatus(sprintf("Adding %s data to database...", $name));
-      $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
-      if ($res === false) {
-          updateStatus(sprintf("Can't run %d script.", $name), true);
-          showForm();
-          return false;
-      }
-  }
-
-      $sqlUrl = "mysqli://$username:$password@$host/$database";
-      $db = array('type' => 'mysql', 'database' => $sqlUrl);
-      return $db;
+        updateStatus(sprintf("Adding %s data to database...", $name));
+        $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
+        if ($res === false) {
+            updateStatus(sprintf("Can't run %d script.", $name), true);
+            showForm();
+            return false;
+        }
+    }
+
+    $sqlUrl = "mysqli://$username:$password@$host/$database";
+    $db = array('type' => 'mysql', 'database' => $sqlUrl);
+    return $db;
 }
 
 function writeConf($sitename, $server, $path, $fancy, $db)
@@ -624,7 +674,7 @@ function writeConf($sitename, $server, $path, $fancy, $db)
 
             // database
             "\$config['db']['database'] = '{$db['database']}';\n\n".
-            ($type == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
+            ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
             "\$config['db']['type'] = '{$db['type']}';\n\n".
 
             "?>";
@@ -634,7 +684,16 @@ function writeConf($sitename, $server, $path, $fancy, $db)
     return $res;
 }
 
-function runDbScript($filename, $conn, $type = 'mysql')
+/**
+ * Install schema into the database
+ *
+ * @param string $filename location of database schema file
+ * @param dbconn $conn     connection to database
+ * @param string $type     type of database, currently mysql or pgsql
+ * 
+ * @return boolean - indicating success or failure
+ */
+function runDbScript($filename, $conn, $type = 'mysqli')
 {
     $sql = trim(file_get_contents($filename));
     $stmts = explode(';', $sql);
@@ -645,7 +704,7 @@ function runDbScript($filename, $conn, $type = 'mysql')
         }
         // FIXME: use PEAR::DB or PDO instead of our own switch
         switch ($type) {
-        case 'mysql':
+        case 'mysqli':
             $res = mysql_query($stmt, $conn);
             if ($res === false) {
                 $error = mysql_error();
index 6402dbc099615918d7ee777330b61abfe3c8873b..18ae7719b25c9df14146079ece46afade3fd362f 100644 (file)
@@ -19,7 +19,7 @@
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-class ShortUrlApi
+abstract class ShortUrlApi
 {
     protected $service_url;
     protected $long_limit = 27;
@@ -35,11 +35,9 @@ class ShortUrlApi
         return $url;
     }
 
-    protected function shorten_imp($url) {
-        return "To Override";
-    }
+    protected  abstract function shorten_imp($url);
 
-    private function is_long($url) {
+    protected function is_long($url) {
         return strlen($url) >= common_config('site', 'shorturllength');
     }
 
@@ -71,61 +69,3 @@ class ShortUrlApi
     }
 }
 
-class LilUrl extends ShortUrlApi
-{
-    function __construct()
-    {
-        parent::__construct('http://ur1.ca/');
-    }
-
-    protected function shorten_imp($url) {
-        $data['longurl'] = $url;
-        $response = $this->http_post($data);
-        if (!$response) return $url;
-        $y = @simplexml_load_string($response);
-        if (!isset($y->body)) return $url;
-        $x = $y->body->p[0]->a->attributes();
-        if (isset($x['href'])) return $x['href'];
-        return $url;
-    }
-}
-
-
-class PtitUrl extends ShortUrlApi
-{
-    function __construct()
-    {
-        parent::__construct('http://ptiturl.com/?creer=oui&action=Reduire&url=');
-    }
-
-    protected function shorten_imp($url) {
-        $response = $this->http_get($url);
-        if (!$response) return $url;
-        $response = $this->tidy($response);
-        $y = @simplexml_load_string($response);
-        if (!isset($y->body)) return $url;
-        $xml = $y->body->center->table->tr->td->pre->a->attributes();
-        if (isset($xml['href'])) return $xml['href'];
-        return $url;
-    }
-}
-
-class TightUrl extends ShortUrlApi
-{
-    function __construct()
-    {
-        parent::__construct('http://2tu.us/?save=y&url=');
-    }
-
-    protected function shorten_imp($url) {
-        $response = $this->http_get($url);
-        if (!$response) return $url;
-        $response = $this->tidy($response);
-        $y = @simplexml_load_string($response);
-        if (!isset($y->body)) return $url;
-        $xml = $y->body->p[0]->code[0]->a->attributes();
-        if (isset($xml['href'])) return $xml['href'];
-        return $url;
-    }
-}
-
index cae7a829872f98a3266cb399dd494e1d1a470d6a..11d40b8e156da190df5b33aa0a45f8aa36a34041 100644 (file)
@@ -114,7 +114,6 @@ class StatsCommand extends Command
 
 class FavCommand extends Command
 {
-
     var $other = null;
 
     function __construct($user, $other)
@@ -158,6 +157,108 @@ class FavCommand extends Command
 
         $channel->output($this->user, _('Notice marked as fave.'));
     }
+
+}
+class JoinCommand extends Command
+{
+    var $other = null;
+
+    function __construct($user, $other)
+    {
+        parent::__construct($user);
+        $this->other = $other;
+    }
+
+    function execute($channel)
+    {
+
+        $nickname = common_canonical_nickname($this->other);
+        $group    = User_group::staticGet('nickname', $nickname);
+        $cur      = $this->user;
+
+        if (!$group) {
+            $channel->error($cur, _('No such group.'));
+            return;
+        }
+
+        if ($cur->isMember($group)) {
+            $channel->error($cur, _('You are already a member of that group'));
+            return;
+        }
+        if (Group_block::isBlocked($group, $cur->getProfile())) {
+          $channel->error($cur, _('You have been blocked from that group by the admin.'));
+            return;
+        }
+
+        $member = new Group_member();
+
+        $member->group_id   = $group->id;
+        $member->profile_id = $cur->id;
+        $member->created    = common_sql_now();
+
+        $result = $member->insert();
+        if (!$result) {
+          common_log_db_error($member, 'INSERT', __FILE__);
+          $channel->error($cur, sprintf(_('Could not join user %s to group %s'),
+                                       $cur->nickname, $group->nickname));
+          return;
+        }
+
+        $channel->output($cur, sprintf(_('%s joined group %s'),
+                                              $cur->nickname,
+                                              $group->nickname));
+    }
+
+}
+class DropCommand extends Command
+{
+    var $other = null;
+
+    function __construct($user, $other)
+    {
+        parent::__construct($user);
+        $this->other = $other;
+    }
+
+    function execute($channel)
+    {
+
+        $nickname = common_canonical_nickname($this->other);
+        $group    = User_group::staticGet('nickname', $nickname);
+        $cur      = $this->user;
+
+        if (!$group) {
+            $channel->error($cur, _('No such group.'));
+            return;
+        }
+
+        if (!$cur->isMember($group)) {
+            $channel->error($cur, _('You are not a member of that group.'));
+            return;
+        }
+
+        $member = new Group_member();
+
+        $member->group_id   = $group->id;
+        $member->profile_id = $cur->id;
+
+        if (!$member->find(true)) {
+          $channel->error($cur,_('Could not find membership record.'));
+          return;
+        }
+        $result = $member->delete();
+        if (!$result) {
+          common_log_db_error($member, 'INSERT', __FILE__);
+          $channel->error($cur, sprintf(_('Could not remove user %s to group %s'),
+                                       $cur->nickname, $group->nickname));
+          return;
+        }
+
+        $channel->output($cur, sprintf(_('%s left group %s'),
+                                              $cur->nickname,
+                                              $group->nickname));
+    }
+
 }
 
 class WhoisCommand extends Command
@@ -396,6 +497,8 @@ class HelpCommand extends Command
                            "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".
+                           "join <group> - join group\n".
+                           "drop <group> - leave group\n".
                            "stats - get your stats\n".
                            "stop - same as 'off'\n".
                            "quit - same as 'off'\n".
index ac6bf73c8e6a0a816f363415fa8d24b1e1c7a16e..6e4340e5dcac05529218c6246b2aff02e35a62fa 100644 (file)
@@ -70,6 +70,26 @@ class CommandInterpreter
             } else {
                 return new OffCommand($user);
             }
+         case 'join':
+             if (!$arg) {
+                return null;
+            }
+            list($other, $extra) = explode(' ', $arg, 2);
+            if ($extra) {
+                return null;
+            } else {
+                return new JoinCommand($user, $other);
+            }
+         case 'drop':
+            if (!$arg) {
+                return null;
+            }
+            list($other, $extra) = explode(' ', $arg, 2);
+            if ($extra) {
+                return null;
+            } else {
+                return new DropCommand($user, $other);
+            }
          case 'follow':
          case 'sub':
             if (!$arg) {
index fe42225974ff8c9d797ac83b7b3c9eb0b2a245a3..fdc05562e08fbfafd13ef12223ce626fba000288 100644 (file)
@@ -327,6 +327,8 @@ class DesignSettingsAction extends AccountSettingsAction
         $this->script('js/farbtastic/farbtastic.js');
         $this->script('js/farbtastic/farbtastic.go.js');
         $this->script('js/userdesign.go.js');
+
+        $this->autofocus('design_background-image_file');
     }
 
     /**
index 18cc7b9295ff7d2cd127bf72a8cd132967ca2f0b..31e36803a731f1677a5f84550b92ada1ddd97ed0 100644 (file)
@@ -132,13 +132,16 @@ class GalleryAction extends OwnerDesignAction
             $this->elementEnd('li');
             $this->elementStart('li', array('id'=>'filter_tags_item'));
             $this->elementStart('form', array('name' => 'bytag',
-                                               'id' => 'bytag',
+                                               'id' => 'form_filter_bytag',
                                                'action' => common_path('?action=' . $this->trimmed('action')),
                                                'method' => 'post'));
+            $this->elementStart('fieldset');
+            $this->element('legend', null, _('Select tag to filter'));
             $this->dropdown('tag', _('Tag'), $content,
                             _('Choose a tag to narrow list'), false, $tag);
             $this->hidden('nickname', $this->user->nickname);
             $this->submit('submit', _('Go'));
+            $this->elementEnd('fieldset');
             $this->elementEnd('form');
             $this->elementEnd('li');
             $this->elementEnd('ul');
index 8ad7dc20fa3645e4e731387b96b8014614ba8643..aa01f6b1d9d268e5855864efe428027cf4aa5916 100644 (file)
@@ -412,4 +412,29 @@ class HTMLOutputter extends XMLOutputter
             $this->element('p', 'form_guide', $instructions);
         }
     }
+
+
+    /**
+    * Internal script to autofocus the given element on page onload.
+    *
+    * @param string $id element ID, must refer to an existing element
+    *
+    * @return void
+    *
+    */
+    function autofocus($id)
+    {
+        $this->elementStart('script', array('type' => 'text/javascript'));
+        $this->raw('
+        <!--
+        $(document).ready(function() {
+            var el = $("#' . $id . '");
+            if (el.length) {
+                el.focus();
+            }
+        });
+        -->
+        ');
+        $this->elementEnd('script');
+    }
 }
index a09bf1060a7837dbccba26ed8d7256df2e98bcfb..e1d384a0635207d97e9c04c1482f8d6fa65e05a7 100644 (file)
@@ -213,26 +213,20 @@ class MailboxAction extends CurrentUserDesignAction
         }
 
         $this->elementStart('div', 'entry-content');
-        $this->elementStart('dl', 'timestamp');
-        $this->element('dt', null, _('Published'));
-        $this->elementStart('dd', null);
-        $dt = common_date_iso8601($message->created);
         $this->elementStart('a', array('rel' => 'bookmark',
+                                       'class' => 'timestamp',
                                        'href' => $messageurl));
+        $dt = common_date_iso8601($message->created);
         $this->element('abbr', array('class' => 'published',
                                      'title' => $dt),
                                common_date_string($message->created));
         $this->elementEnd('a');
-        $this->elementEnd('dd');
-        $this->elementEnd('dl');
 
         if ($message->source) {
-            $this->elementStart('dl', 'device');
-            $this->elementStart('dt');
-            $this->text(_('From'));
-            $this->elementEnd('dt');
-            $this->showSource($message->source);
-            $this->elementEnd('dl');
+            $this->elementStart('span', 'source');
+            $this->text(_('from'));
+            $this->element('span', 'device', $this->showSource($message->source));
+            $this->elementEnd('span');
         }
         $this->elementEnd('div');
 
@@ -277,18 +271,18 @@ class MailboxAction extends CurrentUserDesignAction
         case 'mail':
         case 'omb':
         case 'api':
-            $this->element('dd', null, $source_name);
+            $this->element('span', 'device', $source_name);
             break;
         default:
             $ns = Notice_source::staticGet($source);
             if ($ns) {
-                $this->elementStart('dd', null);
+                $this->elementStart('span', 'device');
                 $this->element('a', array('href' => $ns->url,
-                                          'rel' => 'external'),
-                               $ns->name);
-                $this->elementEnd('dd');
+                                               'rel' => 'external'),
+                                    $ns->name);
+                $this->elementEnd('span');
             } else {
-                $this->element('dd', null, $source_name);
+                $this->out->element('span', 'device', $source_name);
             }
             break;
         }
index cc10cea8f98ed9c1948ac40ecc53960a069f9565..f1827726e71e843d4e2e17daeedc6005308304f0 100644 (file)
@@ -22,7 +22,7 @@
  * @category  Action
  * @package   StatusNet
  * @author    Zach Copley <zach@status.net>
- * @copyright 2008 StatusNet, Inc.
+ * @copyright 2009 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://status.net/
  */
index 87d7be5a7597296bb2c2d777358f7365455beb5d..59bf3ba9d695914c6544dd76dade659e97bd2dad 100644 (file)
@@ -76,4 +76,18 @@ class Plugin
     {
         return true;
     }
+
+    /* 
+    * the name of the shortener
+    * shortenerInfo associative array with additional information. One possible element is 'freeService' which can be true or false
+    * shortener array, first element is the name of the class, second element is an array to be passed as constructor parameters to the class
+    */
+    function registerUrlShortener($name, $shortenerInfo, $shortener)
+    {
+        global $_shorteners;
+        if(!is_array($_shorteners)){
+            $_shorteners=array();
+        }
+        $_shorteners[$name]=array('info'=>$shortenerInfo, 'callInfo'=>$shortener);
+    }
 }
index 7546ffa98a38e0c533929be384e5a6815516646e..b49e2e11902997bdfb1681a48729361397f956f1 100644 (file)
@@ -154,72 +154,126 @@ function broadcast_twitter($notice)
                                        TWITTER_SERVICE);
 
     if (is_twitter_bound($notice, $flink)) {
+        if (TwitterOAuthClient::isPackedToken($flink->credentials)) {
+            return broadcast_oauth($notice, $flink);
+        } else {
+            return broadcast_basicauth($notice, $flink);
+        }
+    }
 
-        $user = $flink->getUser();
-
-        // XXX: Hack to get around PHP cURL's use of @ being a a meta character
-        $statustxt = preg_replace('/^@/', ' @', $notice->content);
+    return true;
+}
 
-        $token = TwitterOAuthClient::unpackToken($flink->credentials);
+function broadcast_oauth($notice, $flink) {
+    $user = $flink->getUser();
+    $statustxt = format_status($notice);
+    // Convert !groups to #hashes
+    $statustxt = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/', "\\1#\\2", $statustxt);
+    $token = TwitterOAuthClient::unpackToken($flink->credentials);
+    $client = new TwitterOAuthClient($token->key, $token->secret);
+    $status = null;
+
+    try {
+        $status = $client->statusesUpdate($statustxt);
+    } catch (OAuthClientCurlException $e) {
+        return process_error($e, $flink);
+    }
 
-        $client = new TwitterOAuthClient($token->key, $token->secret);
+    if (empty($status)) {
 
-        $status = null;
+        // This could represent a failure posting,
+        // or the Twitter API might just be behaving flakey.
 
-        try {
-            $status = $client->statusesUpdate($statustxt);
-        } catch (OAuthClientCurlException $e) {
+        $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' .
+                          'trying to send update for %1$s (user id %2$s).',
+                          $user->nickname, $user->id);
+        common_log(LOG_WARNING, $errmsg);
 
-            if ($e->getMessage() == 'The requested URL returned error: 401') {
+        return false;
+    }
 
-                $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' .
-                                  'Twitter OAuth access token.',
-                                  $user->nickname, $user->id);
-                common_log(LOG_WARNING, $errmsg);
+    // Notice crossed the great divide
 
-                // Bad auth token! We need to delete the foreign_link
-                // to Twitter and inform the user.
+    $msg = sprintf('Twitter bridge - posted notice %s to Twitter using OAuth.',
+                   $notice->id);
+    common_log(LOG_INFO, $msg);
 
-                remove_twitter_link($flink);
-                return true;
+    return true;
+}
 
-            } else {
+function broadcast_basicauth($notice, $flink)
+{
+    $user = $flink->getUser();
 
-                // Some other error happened, so we should probably
-                // try to send again later.
+    $statustxt = format_status($notice);
 
-                $errmsg = sprintf('cURL error trying to send notice to Twitter ' .
-                                  'for user %1$s (user id: %2$s) - ' .
-                                  'code: %3$s message: $4$s.',
-                                  $user->nickname, $user->id,
-                                  $e->getCode(), $e->getMessage());
-                common_log(LOG_WARNING, $errmsg);
+    $client = new TwitterBasicAuthClient($flink);
+    $status = null;
 
-                return false;
-            }
-        }
+    try {
+        $status = $client->statusesUpdate($statustxt);
+    } catch (BasicAuthCurlException $e) {
+        return process_error($e, $flink);
+    }
 
-        if (empty($status)) {
+    if (empty($status)) {
 
-            // This could represent a failure posting,
-            // or the Twitter API might just be behaving flakey.
+        $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' .
+                          'trying to send update for %1$s (user id %2$s).',
+                          $user->nickname, $user->id);
+        common_log(LOG_WARNING, $errmsg);
 
-            $errmsg = sprint('No data returned by Twitter API when ' .
+            $errmsg = sprintf('No data returned by Twitter API when ' .
                              'trying to send update for %1$s (user id %2$s).',
                              $user->nickname, $user->id);
             common_log(LOG_WARNING, $errmsg);
+        return false;
+    }
 
-            return false;
-        }
+    $msg = sprintf('Twitter bridge - posted notice %s to Twitter using basic auth.',
+                   $notice->id);
+    common_log(LOG_INFO, $msg);
 
-        // Notice crossed the great divide
+    return true;
+}
 
-        $msg = sprintf('Twitter bridge posted notice %s to Twitter.',
-                       $notice->id);
-        common_log(LOG_INFO, $msg);
+function process_error($e, $flink)
+{
+    $user        = $flink->getUser();
+    $errmsg      = $e->getMessage();
+    $delivered   = false;
+
+    switch($errmsg) {
+     case 'The requested URL returned error: 401':
+        $logmsg = sprintf('Twiter bridge - User %1$s (user id: %2$s) has an invalid ' .
+                          'Twitter screen_name/password combo or an invalid acesss token.',
+                          $user->nickname, $user->id);
+        $delivered = true;
+        remove_twitter_link($flink);
+        break;
+     case 'The requested URL returned error: 403':
+        $logmsg = sprintf('Twitter bridge - User %1$s (user id: %2$s) has exceeded ' .
+                          'his/her Twitter request limit.',
+                          $user->nickname, $user->id);
+        break;
+     default:
+        $logmsg = sprintf('Twitter bridge - cURL error trying to send notice to Twitter ' .
+                          'for user %1$s (user id: %2$s) - ' .
+                          'code: %3$s message: %4$s.',
+                          $user->nickname, $user->id,
+                          $e->getCode(), $e->getMessage());
+        break;
     }
 
-    return true;
+    common_log(LOG_WARNING, $logmsg);
+
+    return $delivered;
+}
+
+function format_status($notice)
+{
+    // XXX: Hack to get around PHP cURL's use of @ being a a meta character
+    return preg_replace('/^@/', ' @', $notice->content);
 }
 
 function remove_twitter_link($flink)
@@ -227,7 +281,7 @@ function remove_twitter_link($flink)
     $user = $flink->getUser();
 
     common_log(LOG_INFO, 'Removing Twitter bridge Foreign link for ' .
-        "user $user->nickname (user id: $user->id).");
+               "user $user->nickname (user id: $user->id).");
 
     $result = $flink->delete();
 
index 9055d8b9820886e6291b19c535e7f1be46c7476c..d199e4dee27d657e05bcadf20c3b07200706068c 100644 (file)
@@ -595,7 +595,6 @@ class TwitterapiAction extends Action
 
         $this->init_document('rss');
 
-        $this->elementStart('channel');
         $this->element('title', null, $title);
         $this->element('link', null, $link);
         if (!is_null($suplink)) {
@@ -621,7 +620,6 @@ class TwitterapiAction extends Action
             }
         }
 
-        $this->elementEnd('channel');
         $this->end_twitter_rss();
     }
 
@@ -668,7 +666,6 @@ class TwitterapiAction extends Action
 
         $this->init_document('rss');
 
-        $this->elementStart('channel');
         $this->element('title', null, $title);
         $this->element('link', null, $link);
         $this->element('description', null, $subtitle);
@@ -687,7 +684,6 @@ class TwitterapiAction extends Action
             }
         }
 
-        $this->elementEnd('channel');
         $this->end_twitter_rss();
     }
 
@@ -792,6 +788,52 @@ class TwitterapiAction extends Action
         $this->end_document('xml');
     }
 
+    function show_twitter_xml_users($user)
+    {
+
+        $this->init_document('xml');
+        $this->elementStart('users', array('type' => 'array'));
+
+        if (is_array($user)) {
+            foreach ($group as $g) {
+                $twitter_user = $this->twitter_user_array($g);
+                $this->show_twitter_xml_user($twitter_user,'user');
+            }
+        } else {
+            while ($user->fetch()) {
+                $twitter_user = $this->twitter_user_array($user);
+                $this->show_twitter_xml_user($twitter_user);
+            }
+        }
+
+        $this->elementEnd('users');
+        $this->end_document('xml');
+    }
+
+    function show_json_users($user)
+    {
+
+        $this->init_document('json');
+
+        $users = array();
+
+        if (is_array($user)) {
+            foreach ($user as $u) {
+                $twitter_user = $this->twitter_user_array($u);
+                array_push($users, $twitter_user);
+            }
+        } else {
+            while ($user->fetch()) {
+                $twitter_user = $this->twitter_user_array($user);
+                array_push($users, $twitter_user);
+            }
+        }
+
+        $this->show_json_objects($users);
+
+        $this->end_document('json');
+    }
+
     function show_single_json_group($group)
     {
         $this->init_document('json');
@@ -944,11 +986,14 @@ class TwitterapiAction extends Action
     function init_twitter_rss()
     {
         $this->startXML();
-        $this->elementStart('rss', array('version' => '2.0'));
+        $this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
+        $this->elementStart('channel');
+        Event::handle('StartApiRss', array($this));
     }
 
     function end_twitter_rss()
     {
+        $this->elementEnd('channel');
         $this->elementEnd('rss');
         $this->endXML();
     }
@@ -960,6 +1005,7 @@ class TwitterapiAction extends Action
         $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
                                           'xml:lang' => 'en-US',
                                           'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
+        Event::handle('StartApiAtom', array($this));
     }
 
     function end_twitter_atom()
diff --git a/lib/twitterbasicauthclient.php b/lib/twitterbasicauthclient.php
new file mode 100644 (file)
index 0000000..fd331fb
--- /dev/null
@@ -0,0 +1,236 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Class for doing OAuth calls against Twitter
+ *
+ * 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  Integration
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Exception wrapper for cURL errors
+ *
+ * @category Integration
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ */
+class BasicAuthCurlException extends Exception
+{
+}
+
+/**
+ * Class for talking to the Twitter API with HTTP Basic Auth.
+ *
+ * @category Integration
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ */
+class TwitterBasicAuthClient
+{
+    var $screen_name = null;
+    var $password    = null;
+
+    /**
+     * constructor
+     *
+     * @param Foreign_link $flink a Foreign_link storing the
+     *                            Twitter user's password, etc.
+     */
+    function __construct($flink)
+    {
+        $fuser             = $flink->getForeignUser();
+        $this->screen_name = $fuser->nickname;
+        $this->password    = $flink->credentials;
+    }
+
+    /**
+     * Calls Twitter's /statuses/update API method
+     *
+     * @param string $status                text of the status
+     * @param int    $in_reply_to_status_id optional id of the status it's
+     *                                      a reply to
+     *
+     * @return mixed the status
+     */
+    function statusesUpdate($status, $in_reply_to_status_id = null)
+    {
+        $url      = 'https://twitter.com/statuses/update.json';
+        $params   = array('status' => $status,
+                          'source' => common_config('integration', 'source'),
+                          'in_reply_to_status_id' => $in_reply_to_status_id);
+        $response = $this->httpRequest($url, $params);
+        $status   = json_decode($response);
+        return $status;
+    }
+
+    /**
+     * Calls Twitter's /statuses/friends_timeline API method
+     *
+     * @param int $since_id show statuses after this id
+     * @param int $max_id   show statuses before this id
+     * @param int $cnt      number of statuses to show
+     * @param int $page     page number
+     *
+     * @return mixed an array of statuses
+     */
+    function statusesFriendsTimeline($since_id = null, $max_id = null,
+                                     $cnt = null, $page = null)
+    {
+        $url    = 'https://twitter.com/statuses/friends_timeline.json';
+        $params = array('since_id' => $since_id,
+                        'max_id' => $max_id,
+                        'count' => $cnt,
+                        'page' => $page);
+        $qry    = http_build_query($params);
+
+        if (!empty($qry)) {
+            $url .= "?$qry";
+        }
+
+        $response = $this->httpRequest($url);
+        $statuses = json_decode($response);
+        return $statuses;
+    }
+
+    /**
+     * Calls Twitter's /statuses/friends API method
+     *
+     * @param int $id          id of the user whom you wish to see friends of
+     * @param int $user_id     numerical user id
+     * @param int $screen_name screen name
+     * @param int $page        page number
+     *
+     * @return mixed an array of twitter users and their latest status
+     */
+    function statusesFriends($id = null, $user_id = null, $screen_name = null,
+                             $page = null)
+    {
+        $url = "https://twitter.com/statuses/friends.json";
+
+        $params = array('id' => $id,
+                        'user_id' => $user_id,
+                        'screen_name' => $screen_name,
+                        'page' => $page);
+        $qry    = http_build_query($params);
+
+        if (!empty($qry)) {
+            $url .= "?$qry";
+        }
+
+        $response = $this->httpRequest($url);
+        $friends  = json_decode($response);
+        return $friends;
+    }
+
+    /**
+     * Calls Twitter's /statuses/friends/ids API method
+     *
+     * @param int $id          id of the user whom you wish to see friends of
+     * @param int $user_id     numerical user id
+     * @param int $screen_name screen name
+     * @param int $page        page number
+     *
+     * @return mixed a list of ids, 100 per page
+     */
+    function friendsIds($id = null, $user_id = null, $screen_name = null,
+                        $page = null)
+    {
+        $url = "https://twitter.com/friends/ids.json";
+
+        $params = array('id' => $id,
+                        'user_id' => $user_id,
+                        'screen_name' => $screen_name,
+                        'page' => $page);
+        $qry    = http_build_query($params);
+
+        if (!empty($qry)) {
+            $url .= "?$qry";
+        }
+
+        $response = $this->httpRequest($url);
+        $ids      = json_decode($response);
+        return $ids;
+    }
+
+    /**
+     * Make a HTTP request using cURL.
+     *
+     * @param string $url    Where to make the request
+     * @param array  $params post parameters
+     *
+     * @return mixed the request
+     */
+    function httpRequest($url, $params = null, $auth = true)
+    {
+        $options = array(
+                         CURLOPT_RETURNTRANSFER => true,
+                         CURLOPT_FAILONERROR    => true,
+                         CURLOPT_HEADER         => false,
+                         CURLOPT_FOLLOWLOCATION => true,
+                         CURLOPT_USERAGENT      => 'StatusNet',
+                         CURLOPT_CONNECTTIMEOUT => 120,
+                         CURLOPT_TIMEOUT        => 120,
+                         CURLOPT_HTTPAUTH       => CURLAUTH_ANY,
+                         CURLOPT_SSL_VERIFYPEER => false,
+
+                         // Twitter is strict about accepting invalid "Expect" headers
+
+                         CURLOPT_HTTPHEADER => array('Expect:')
+                         );
+
+        if (isset($params)) {
+            $options[CURLOPT_POST]       = true;
+            $options[CURLOPT_POSTFIELDS] = $params;
+        }
+
+        if ($auth) {
+            $options[CURLOPT_USERPWD] = $this->screen_name .
+              ':' . $this->password;
+        }
+
+        $ch = curl_init($url);
+        curl_setopt_array($ch, $options);
+        $response = curl_exec($ch);
+
+        if ($response === false) {
+            $msg  = curl_error($ch);
+            $code = curl_errno($ch);
+            throw new BasicAuthCurlException($msg, $code);
+        }
+
+        curl_close($ch);
+
+        return $response;
+    }
+
+}
index 3da522fc519a88d83a25a6093c856e059d378160..bad2b74ca324101388eaa6feae1e787e65c72892 100644 (file)
@@ -22,7 +22,7 @@
  * @category  Integration
  * @package   StatusNet
  * @author    Zach Copley <zach@status.net>
- * @copyright 2008 StatusNet, Inc.
+ * @copyright 2009 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://status.net/
  */
@@ -81,6 +81,15 @@ class TwitterOAuthClient extends OAuthClient
         return new OAuthToken($vals[0], $vals[1]);
     }
 
+    static function isPackedToken($str)
+    {
+        if (strpos($str, chr(0)) === false) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
     /**
      * Builds a link to Twitter's endpoint for authorizing a request token
      *
@@ -109,7 +118,7 @@ class TwitterOAuthClient extends OAuthClient
     }
 
     /**
-     * Calls Twitter's /stutuses/update API method
+     * Calls Twitter's /statuses/update API method
      *
      * @param string $status                text of the status
      * @param int    $in_reply_to_status_id optional id of the status it's
@@ -128,7 +137,7 @@ class TwitterOAuthClient extends OAuthClient
     }
 
     /**
-     * Calls Twitter's /stutuses/friends_timeline API method
+     * Calls Twitter's /statuses/friends_timeline API method
      *
      * @param int $since_id show statuses after this id
      * @param int $max_id   show statuses before this id
@@ -158,7 +167,7 @@ class TwitterOAuthClient extends OAuthClient
     }
 
     /**
-     * Calls Twitter's /stutuses/friends API method
+     * Calls Twitter's /statuses/friends API method
      *
      * @param int $id          id of the user whom you wish to see friends of
      * @param int $user_id     numerical user id
@@ -188,7 +197,7 @@ class TwitterOAuthClient extends OAuthClient
     }
 
     /**
-     * Calls Twitter's /stutuses/friends/ids API method
+     * Calls Twitter's /statuses/friends/ids API method
      *
      * @param int $id          id of the user whom you wish to see friends of
      * @param int $user_id     numerical user id
index 337113c70189b6171a8cf6e8447862b181680afd..b74dc619c12d13aafc8b84b948b0b5e90b040948 100644 (file)
@@ -59,7 +59,7 @@ function common_init_language()
     textdomain("statusnet");
     setlocale(LC_CTYPE, 'C');
     if(!$locale_set) {
-        common_log(LOG_INFO,'Language requested:'.$language.' - locale could not be set:',__FILE__);
+        common_log(LOG_INFO, 'Language requested:' . $language . ' - locale could not be set. Perhaps that system locale is not installed.', __FILE__);
     }
 }
 
@@ -413,7 +413,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
     // Start off with a regex
     $regex = '#'.
     '(?:^|[\s\(\)\[\]\{\}\\\'\\\";]+)(?![\@\!\#])'.
-    '(?P<url>'.
+    '('.
         '(?:'.
             '(?:'. //Known protocols
                 '(?:'.
@@ -421,7 +421,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
                     '|'.
                     '(?:(?:mailto|aim|tel|xmpp):)'.
                 ')'.
-                '(?:[\pN\pL\-\_\+]+(?::[\pN\pL\-\_\+]+)?\@)?'. //user:pass@
+                '(?:[\pN\pL\-\_\+\%\~]+(?::[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@
                 '(?:'.
                     '(?:'.
                         '\[[\pN\pL\-\_\:\.]+(?<![\.\:])\]'. //[dns]
@@ -432,9 +432,9 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
             ')'.
             '|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'. //IPv4
             '|(?:'. //IPv6
-                '\[?(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}(?:(?:[0-9A-Fa-f]{1,4})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){6}(?::|(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(?::[0-9A-Fa-f]{1,4})))|(?:(?:[0-9A-Fa-f]{1,4}:){5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){4}(?::[0-9A-Fa-f]{1,4}){0,1}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){3}(?::[0-9A-Fa-f]{1,4}){0,2}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){2}(?::[0-9A-Fa-f]{1,4}){0,3}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:)(?::[0-9A-Fa-f]{1,4}){0,4}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?::(?::[0-9A-Fa-f]{1,4}){0,5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))\]?'.
+                '\[?(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}(?:(?:[0-9A-Fa-f]{1,4})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){6}(?::|(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(?::[0-9A-Fa-f]{1,4})))|(?:(?:[0-9A-Fa-f]{1,4}:){5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){4}(?::[0-9A-Fa-f]{1,4}){0,1}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){3}(?::[0-9A-Fa-f]{1,4}){0,2}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){2}(?::[0-9A-Fa-f]{1,4}){0,3}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:)(?::[0-9A-Fa-f]{1,4}){0,4}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?::(?::[0-9A-Fa-f]{1,4}){0,5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))\]?(?<!:)'.
             ')|(?:'. //DNS
-                '(?:[\pN\pL\-\_\+]+(?:\:[\pN\pL\-\_\+]+)?\@)?'. //user:pass@
+                '(?:[\pN\pL\-\_\+\%\~]+(?:\:[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@
                 '[\pN\pL\-\_]+(?:\.[\pN\pL\-\_]+)*\.'.
                 //tld list from http://data.iana.org/TLD/tlds-alpha-by-domain.txt, also added local, loc, and onion
                 '(?:AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XN--0ZWM56D|测试|XN--11B5BS3A9AJ6G|परीक्षा|XN--80AKHBYKNJ4F|испытание|XN--9T4B11YI5A|테스트|XN--DEBA0AD|טעסט|XN--G6W251D|測試|XN--HGBK6AJ7F53BBA|آزمایشی|XN--HLCJ6AYA9ESC7A|பரிட்சை|XN--JXALPDLP|δοκιμή|XN--KGBECHTV|إختبار|XN--ZCKZAH|テスト|YE|YT|YU|ZA|ZM|ZW|local|loc|onion)'.
@@ -442,22 +442,22 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
         ')'.
         '(?:'.
             '(?:\:\d+)?'. //:port
-            '(?:/[\pN\pL$\[\]\,\!\(\)\.\-\_\+\/\=\&\;]*)?'. // /path
-            '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\-\_\+\/\=\&\;\/]*)?'. // ?query string
-            '(?:\#[\pN\pL$\[\]\,\!\(\)\.\-\_\+\/\=\&\;\/\?\#]*)?'. // #fragment
+            '(?:/[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"]*)?'. // /path
+            '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"\/]*)?'. // ?query string
+            '(?:\#[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"\/\?\#]*)?'. // #fragment
         ')(?<![\?\.\,\#\,])'.
     ')'.
     '#ixu';
-    preg_match_all($regex,$text,$matches);
+    //preg_match_all($regex,$text,$matches);
     //print_r($matches);
-    return preg_replace_callback($regex, curry(callback_helper,$callback,$notice_id) ,$text);
+    return preg_replace_callback($regex, curry('callback_helper',$callback,$notice_id) ,$text);
 }
 
 function callback_helper($matches, $callback, $notice_id) {
-    $url=$matches['url'];
+    $url=$matches[1];
     $left = strpos($matches[0],$url);
     $right = $left+strlen($url);
-    
+
     $groupSymbolSets=array(
         array(
             'left'=>'(',
@@ -491,13 +491,11 @@ function callback_helper($matches, $callback, $notice_id) {
             $url=substr($url,0,-1);
         }
     }while($original_url!=$url);
-    
-    
-    
+
     if(empty($notice_id)){
         $result = call_user_func_array($callback,$url);
     }else{
-        $result = call_user_func_array($callback, array($url,$notice_id) );
+        $result = call_user_func_array($callback, array(array($url,$notice_id)) );
     }
     return substr($matches[0],0,$left) . $result . substr($matches[0],$right);
 }
@@ -508,16 +506,13 @@ function curry($fn) {
     array_shift($args);
     $id = uniqid('_partial');
     $GLOBALS[$id] = array($fn, $args);
-    return create_function(
-        '',
-        '
-        $args = func_get_args();
-        return call_user_func_array(
-        $GLOBALS["'.$id.'"][0],
-        array_merge(
-            $args,
-            $GLOBALS["'.$id.'"][1]));
-    ');
+    return create_function('',
+                           '$args = func_get_args(); '.
+                           'return call_user_func_array('.
+                           '$GLOBALS["'.$id.'"][0],'.
+                           'array_merge('.
+                           '$args,'.
+                           '$GLOBALS["'.$id.'"][1]));');
 }
 
 function common_linkify($url) {
@@ -525,7 +520,7 @@ function common_linkify($url) {
     // functions
     $url = htmlspecialchars_decode($url);
 
-   if(strpos($url, '@')!==false && strpos($url, ':')===false){
+   if(strpos($url, '@') !== false && strpos($url, ':') === false) {
        //url is an email address without the mailto: protocol
        return XMLStringer::estring('a', array('href' => "mailto:$url", 'rel' => 'external'), $url);
    }
@@ -547,42 +542,30 @@ function common_linkify($url) {
     $attachment_id = null;
     $has_thumb = false;
 
-    // Check to see whether there's a filename associated with this URL.
-    // If there is, it's an upload and qualifies as an attachment
+    // Check to see whether this is a known "attachment" URL.
 
-    $localfile = File::staticGet('url', $longurl);
+    $f = File::staticGet('url', $longurl);
 
-    if (!empty($localfile)) {
-        if (isset($localfile->filename)) {
-            $is_attachment = true;
-            $attachment_id = $localfile->id;
-        }
+    if (empty($f)) {
+        // XXX: this writes to the database. :<
+        $f = File::processNew($longurl);
     }
 
-    // if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
-    // where ID is the id of the attachment for the given URL.
-    //
-    // we need a better test telling what can be shown as an attachment
-    // we're currently picking up oembeds only.
-    // I think the best option is another file_view table in the db
-    // and associated dbobject.
-
-    $query = "select file_oembed.file_id as file_id from file join file_oembed on file.id = file_oembed.file_id where file.url='$longurl'";
-    $file = new File;
-    $file->query($query);
-    $file->fetch();
-
-    if (!empty($file->file_id)) {
-        $is_attachment = true;
-        $attachment_id = $file->file_id;
-
-        $query = "select file_thumbnail.file_id as file_id from file join file_thumbnail on file.id = file_thumbnail.file_id where file.url='$longurl'";
-        $file2 = new File;
-        $file2->query($query);
-        $file2->fetch();
-
-        if (!empty($file2)) {
-            $has_thumb = true;
+    if (!empty($f)) {
+        if (isset($f->filename)) {
+            $is_attachment = true;
+            $attachment_id = $f->id;
+        } else { // if it has OEmbed info, it's an attachment, too
+            $foe = File_oembed::staticGet('file_id', $f->id);
+            if (!empty($foe)) {
+                $is_attachment = true;
+                $attachment_id = $f->id;
+
+                $thumb = File_thumbnail::staticGet('file_id', $f->id);
+                if (!empty($thumb)) {
+                    $has_thumb = true;
+                }
+            }
         }
     }
 
@@ -1390,58 +1373,19 @@ function common_shorten_url($long_url)
     } else {
         $svc = $user->urlshorteningservice;
     }
-
-    $curlh = curl_init();
-    curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
-    curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet');
-    curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
-
-    switch($svc) {
-     case 'ur1.ca':
-        require_once INSTALLDIR.'/lib/Shorturl_api.php';
-        $short_url_service = new LilUrl;
-        $short_url = $short_url_service->shorten($long_url);
-        break;
-
-     case '2tu.us':
-        $short_url_service = new TightUrl;
-        require_once INSTALLDIR.'/lib/Shorturl_api.php';
-        $short_url = $short_url_service->shorten($long_url);
-        break;
-
-     case 'ptiturl.com':
-        require_once INSTALLDIR.'/lib/Shorturl_api.php';
-        $short_url_service = new PtitUrl;
-        $short_url = $short_url_service->shorten($long_url);
-        break;
-
-     case 'bit.ly':
-        curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url));
-        $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl;
-        break;
-
-     case 'is.gd':
-        curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url));
-        $short_url = curl_exec($curlh);
-        break;
-     case 'snipr.com':
-        curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url));
-        $short_url = curl_exec($curlh);
-        break;
-     case 'metamark.net':
-        curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url));
-        $short_url = curl_exec($curlh);
-        break;
-     case 'tinyurl.com':
-        curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url));
-        $short_url = curl_exec($curlh);
-        break;
-     default:
-        $short_url = false;
+    global $_shorteners;
+    if(! $_shorteners[$svc]){
+        //the user selected service doesn't exist, so default to ur1.ca
+        $svc = 'ur1.ca';
     }
 
-    curl_close($curlh);
+    $reflectionObj = new ReflectionClass($_shorteners[$svc]['callInfo'][0]);
+    $short_url_service = $reflectionObj->newInstanceArgs($_shorteners[$svc]['callInfo'][1]); 
+    $short_url = $short_url_service->shorten($long_url);
 
+    if(substr($short_url,0,7)=='http://'){
+        $short_url = substr($short_url,7);
+    }
     return $short_url;
 }
 
diff --git a/plugins/LilUrl/LilUrlPlugin.php b/plugins/LilUrl/LilUrlPlugin.php
new file mode 100644 (file)
index 0000000..7665b6c
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * 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   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once(INSTALLDIR.'/lib/Shorturl_api.php');
+
+class LilUrlPlugin extends Plugin
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->registerUrlShortener(
+            'ur1.ca',
+            array('freeService'=>true),
+            array('LilUrl',array('http://ur1.ca/'))
+        );
+    }
+}
+
+class LilUrl extends ShortUrlApi
+{
+    protected function shorten_imp($url) {
+        $data['longurl'] = $url;
+        $response = $this->http_post($data);
+        if (!$response) return $url;
+        $y = @simplexml_load_string($response);
+        if (!isset($y->body)) return $url;
+        $x = $y->body->p[0]->a->attributes();
+        if (isset($x['href'])) return $x['href'];
+        return $url;
+    }
+}
index c49f70de0d54d0598e71d735233bfc51c86cfe59..60f7a60c797f60e80b9914f20a1403eefdeb4aaa 100644 (file)
@@ -75,6 +75,8 @@ class LinkbackPlugin extends Plugin
 
     function linkbackUrl($url)
     {
+        common_log(LOG_DEBUG,"Attempting linkback for " . $url);
+
         $orig = $url;
         $url = htmlspecialchars_decode($orig);
         $scheme = parse_url($url, PHP_URL_SCHEME);
@@ -134,15 +136,20 @@ class LinkbackPlugin extends Plugin
                                                                "User-Agent: " . $this->userAgent(),
                                                                'content' => $request)));
         $file = file_get_contents($endpoint, false, $context);
-        $response = xmlrpc_decode($file);
-        if (xmlrpc_is_fault($response)) {
+        if (!$file) {
             common_log(LOG_WARNING,
+                      "Pingback request failed for '$url' ($endpoint)");
+        } else {
+            $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,
+            } else {
+                common_log(LOG_INFO,
                        "Pingback success for '$url' ($endpoint): ".
                        "'$response'");
+            }
         }
     }
 
index 07c82db3e1497f602d277160cc2e5549bcc40f78..29e89234e9c5b8526aac17ac3e15342405021601 100644 (file)
@@ -84,6 +84,12 @@ class OpenidloginAction extends Action
         }
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('openid_url');
+    }
+
     function title()
     {
         return _('OpenID Login');
index 104ea8d889d95d5ea5d13037762a6f46f25bb6ea..3ad46f5f57ce6824ff4717f3be37ba176f593c80 100644 (file)
@@ -72,6 +72,12 @@ class OpenidsettingsAction extends AccountSettingsAction
                  ' Manage your associated OpenIDs from here.');
     }
 
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('openid_url');
+    }
+
     /**
      * Show the form for OpenID management
      *
diff --git a/plugins/PtitUrl/PtitUrlPlugin.php b/plugins/PtitUrl/PtitUrlPlugin.php
new file mode 100644 (file)
index 0000000..f00d3e2
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * 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   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class PtitUrlPlugin extends Plugin
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->registerUrlShortener(
+            'ptiturl.com',
+            array(),
+            array('PtitUrl',array('http://ptiturl.com/?creer=oui&action=Reduire&url='))
+        );
+    }
+}
+
+class PtitUrl extends ShortUrlApi
+{
+    protected function shorten_imp($url) {
+        $response = $this->http_get($url);
+        if (!$response) return $url;
+        $response = $this->tidy($response);
+        $y = @simplexml_load_string($response);
+        if (!isset($y->body)) return $url;
+        $xml = $y->body->center->table->tr->td->pre->a->attributes();
+        if (isset($xml['href'])) return $xml['href'];
+        return $url;
+    }
+}
diff --git a/plugins/PubSubHubBub/PubSubHubBubPlugin.php b/plugins/PubSubHubBub/PubSubHubBubPlugin.php
new file mode 100644 (file)
index 0000000..013a234
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * 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   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+define('DEFAULT_HUB','http://2pubsubhubbub.appspot.com');
+
+require_once(INSTALLDIR.'/plugins/PubSubHubBub/publisher.php');
+
+class PubSubHubBubPlugin extends Plugin
+{
+    private $hub;
+
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->hub = common_config('PubSubHubBub', 'hub');
+        if(empty($this->hub)){
+            $this->hub = DEFAULT_HUB;
+        }
+    }
+
+    function onStartApiAtom($action){
+        $action->element('link',array('rel'=>'hub','href'=>$this->hub),null);
+    }
+
+    function onStartApiRss($action){
+        $action->element('atom:link',array('rel'=>'hub','href'=>$this->hub),null);
+    }
+
+    function onEndNoticeSave($notice){
+        $publisher = new Publisher($this->hub);
+
+        $feeds = array();
+
+        //public timeline feeds
+        $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'public_timeline.rss'));
+        $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'public_timeline.atom'));
+
+        //author's own feeds
+        $user = User::staticGet('id',$notice->profile_id);
+        $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss'));
+        $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom'));
+
+        //tag feeds
+        $tag = new Notice_tag();
+        $tag->notice_id = $notice->id;
+        if ($tag->find()) {
+            while ($tag->fetch()) {
+                $feeds[]=common_local_url('api',array('apiaction' => 'tags','method' => 'timeline', 'argument'=>$tag->tag.'.atom'));
+                $feeds[]=common_local_url('api',array('apiaction' => 'tags','method' => 'timeline', 'argument'=>$tag->tag.'.rss'));
+            }
+        }
+
+        //group feeds
+        $group_inbox = new Group_inbox();
+        $group_inbox->notice_id = $notice->id;
+        if ($group_inbox->find()) {
+            while ($group_inbox->fetch()) {
+                $group = User_group::staticGet('id',$group_inbox->group_id);
+                $feeds[]=common_local_url('api',array('apiaction' => 'groups','method' => 'timeline','argument' => $group->nickname.'.rss'));
+                $feeds[]=common_local_url('api',array('apiaction' => 'groups','method' => 'timeline','argument' => $group->nickname.'.atom'));
+            }
+        }
+
+        //feed of each user that subscribes to the notice's author
+        $notice_inbox = new Notice_inbox();
+        $notice_inbox->notice_id = $notice->id;
+        if ($notice_inbox->find()) {
+            while ($notice_inbox->fetch()) {
+                $user = User::staticGet('id',$notice_inbox->user_id);
+                $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss'));
+                $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom'));
+            }
+        }
+
+        /* TODO: when the reply page gets RSS and ATOM feeds, implement this
+        //feed of user replied to
+        if($notice->reply_to){
+                $user = User::staticGet('id',$notice->reply_to);
+                $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss'));
+                $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom'));
+        }*/
+
+        foreach(array_unique($feeds) as $feed){
+            if(! $publisher->publish_update($feed)){
+                common_log_line(LOG_WARNING,$feed.' was not published to hub at '.$this->hub.':'.$publisher->last_response());
+            }
+        }
+    }
+}
diff --git a/plugins/PubSubHubBub/publisher.php b/plugins/PubSubHubBub/publisher.php
new file mode 100644 (file)
index 0000000..f176a9b
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+
+// a PHP client library for pubsubhubbub
+// as defined at http://code.google.com/p/pubsubhubbub/
+// written by Josh Fraser | joshfraser.com | josh@eventvue.com
+// Released under Apache License 2.0
+
+class Publisher {
+    
+    protected $hub_url;
+    protected $last_response;
+    
+    // create a new Publisher
+    public function __construct($hub_url) {
+        
+        if (!isset($hub_url))
+            throw new Exception('Please specify a hub url');
+        
+        if (!preg_match("|^https?://|i",$hub_url)) 
+            throw new Exception('The specified hub url does not appear to be valid: '.$hub_url);
+            
+        $this->hub_url = $hub_url;
+    }
+
+    // accepts either a single url or an array of urls
+    public function publish_update($topic_urls, $http_function = false) {
+        if (!isset($topic_urls))
+            throw new Exception('Please specify a topic url');
+        
+        // check that we're working with an array
+        if (!is_array($topic_urls)) {
+            $topic_urls = array($topic_urls);
+        }
+        
+        // set the mode to publish
+        $post_string = "hub.mode=publish";
+        // loop through each topic url 
+        foreach ($topic_urls as $topic_url) {
+
+            // lightweight check that we're actually working w/ a valid url
+            if (!preg_match("|^https?://|i",$topic_url)) 
+                throw new Exception('The specified topic url does not appear to be valid: '.$topic_url);
+            
+            // append the topic url parameters
+            $post_string .= "&hub.url=".urlencode($topic_url);
+        }
+        
+        // make the http post request and return true/false
+        // easy to over-write to use your own http function
+        if ($http_function)
+            return $http_function($this->hub_url,$post_string);
+        else
+            return $this->http_post($this->hub_url,$post_string);
+    }
+
+    // returns any error message from the latest request
+    public function last_response() {
+        return $this->last_response;
+    }
+    
+    // default http function that uses curl to post to the hub endpoint
+    private function http_post($url, $post_string) {
+        
+        // add any additional curl options here
+        $options = array(CURLOPT_URL => $url,
+                         CURLOPT_POST => true,
+                         CURLOPT_POSTFIELDS => $post_string,
+                         CURLOPT_USERAGENT => "PubSubHubbub-Publisher-PHP/1.0");
+
+       $ch = curl_init();
+       curl_setopt_array($ch, $options);
+
+        $response = curl_exec($ch);
+        $this->last_response = $response;
+        $info = curl_getinfo($ch);
+
+        curl_close($ch);
+        
+        // all good
+        if ($info['http_code'] == 204) 
+            return true;
+        return false;  
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/plugins/SimpleUrl/SimpleUrlPlugin.php b/plugins/SimpleUrl/SimpleUrlPlugin.php
new file mode 100644 (file)
index 0000000..82d7720
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * 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   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class SimpleUrlPlugin extends Plugin
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->registerUrlShortener(
+            'is.gd',
+            array(),
+            array('SimpleUrl',array('http://is.gd/api.php?longurl='))
+        );
+        $this->registerUrlShortener(
+            'snipr.com',
+            array(),
+            array('SimpleUrl',array('http://snipr.com/site/snip?r=simple&link='))
+        );
+        $this->registerUrlShortener(
+            'metamark.net',
+            array(),
+            array('SimpleUrl',array('http://metamark.net/api/rest/simple?long_url='))
+        );
+        $this->registerUrlShortener(
+            'tinyurl.com',
+            array(),
+            array('SimpleUrl',array('http://tinyurl.com/api-create.php?url='))
+        );
+    }
+}
+
+class SimpleUrl extends ShortUrlApi
+{
+    protected function shorten_imp($url) {
+        $curlh = curl_init();
+        curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
+        curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet');
+        curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
+
+        curl_setopt($curlh, CURLOPT_URL, $this->service_url.urlencode($url));
+        $short_url = curl_exec($curlh);
+
+        curl_close($curlh);
+        return $short_url;
+    }
+}
diff --git a/plugins/TightUrl/TightUrlPlugin.php b/plugins/TightUrl/TightUrlPlugin.php
new file mode 100644 (file)
index 0000000..48efb35
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * 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   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class TightUrlPlugin extends Plugin
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function onInitializePlugin(){
+        $this->registerUrlShortener(
+            '2tu.us',
+            array('freeService'=>true),
+            array('TightUrl',array('http://2tu.us/?save=y&url='))
+        );
+    }
+}
+
+class TightUrl extends ShortUrlApi
+{
+    protected function shorten_imp($url) {
+        $response = $this->http_get($url);
+        if (!$response) return $url;
+        $response = $this->tidy($response);
+        $y = @simplexml_load_string($response);
+        if (!isset($y->body)) return $url;
+        $xml = $y->body->p[0]->code[0]->a->attributes();
+        if (isset($xml['href'])) return $xml['href'];
+        return $url;
+    }
+}
index 545cb23b3cac4678fb2753472ee8a11a918e5870..b30e700a1c4a298a0633701932ebe824d511649e 100755 (executable)
@@ -142,13 +142,20 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
     {
         $friends = array();
 
-        $token = TwitterOAuthClient::unpackToken($flink->credentials);
-
-        $client = new TwitterOAuthClient($token->key, $token->secret);
+        $client = null;
+
+        if (TwitterOAuthClient::isPackedToken($flink->credentials)) {
+            $token = TwitterOAuthClient::unpackToken($flink->credentials);
+            $client = new TwitterOAuthClient($token->key, $token->secret);
+            common_debug($this->name() . '- Grabbing friends IDs with OAuth.');
+        } else {
+            $client = new TwitterBasicAuthClient($flink);
+            common_debug($this->name() . '- Grabbing friends IDs with basic auth.');
+        }
 
         try {
             $friends_ids = $client->friendsIds();
-        } catch (OAuthCurlException $e) {
+        } catch (Exception $e) {
             common_log(LOG_WARNING, $this->name() .
                        ' - cURL error getting friend ids ' .
                        $e->getCode() . ' - ' . $e->getMessage());
@@ -177,7 +184,7 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
 
         try {
             $more_friends = $client->statusesFriends(null, null, null, $i);
-        } catch (OAuthCurlException $e) {
+        } catch (Exception $e) {
             common_log(LOG_WARNING, $this->name() .
                        ' - cURL error getting Twitter statuses/friends ' .
                        "page $i - " . $e->getCode() . ' - ' .
index 68f7e9bf758cee4d73ffde0a59b191818b188717..3cdf1867a197319c486d61bab7c19d8edf735fd8 100755 (executable)
@@ -148,9 +148,9 @@ class TwitterStatusFetcher extends ParallelizingDaemon
 
     function getTimeline($flink)
     {
-         if (empty($flink)) {
+        if (empty($flink)) {
             common_log(LOG_WARNING, $this->name() .
-                " - Can't retrieve Foreign_link for foreign ID $fid");
+                       " - Can't retrieve Foreign_link for foreign ID $fid");
             return;
         }
 
@@ -161,17 +161,24 @@ class TwitterStatusFetcher extends ParallelizingDaemon
         // to start importing?  How many statuses?  Right now I'm going
         // with the default last 20.
 
-        $token = TwitterOAuthClient::unpackToken($flink->credentials);
+        $client = null;
 
-        $client = new TwitterOAuthClient($token->key, $token->secret);
+        if (TwitterOAuthClient::isPackedToken($flink->credentials)) {
+            $token = TwitterOAuthClient::unpackToken($flink->credentials);
+            $client = new TwitterOAuthClient($token->key, $token->secret);
+            common_debug($this->name() . ' - Grabbing friends timeline with OAuth.');
+        } else {
+            $client = new TwitterBasicAuthClient($flink);
+            common_debug($this->name() . ' - Grabbing friends timeline with basic auth.');
+        }
 
         $timeline = null;
 
         try {
             $timeline = $client->statusesFriendsTimeline();
-        } catch (OAuthClientCurlException $e) {
+        } catch (Exception $e) {
             common_log(LOG_WARNING, $this->name() .
-                       ' - OAuth client unable to get friends timeline for user ' .
+                       ' - Twitter client unable to get friends timeline for user ' .
                        $flink->user_id . ' - code: ' .
                        $e->getCode() . 'msg: ' . $e->getMessage());
         }
index 767f895bbae4c3c1ec8d7af169a19b95910d4066..87b53764679db95d990bda94faa296be0dd6b490 100644 (file)
@@ -25,14 +25,36 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase
     static public function provider()
     {
         return array(
+                     array('not a link :: no way',
+                           'not a link :: no way'),
                      array('http://127.0.0.1',
                            '<a href="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'),
                      array('127.0.0.1',
                            '<a href="http://127.0.0.1/" rel="external">127.0.0.1</a>'),
                      array('127.0.0.1:99',
                            '<a href="http://127.0.0.1:99/" rel="external">127.0.0.1:99</a>'),
-                     array('127.0.0.1/test.php',
-                           '<a href="http://127.0.0.1/test.php" rel="external">127.0.0.1/test.php</a>'),
+                     array('127.0.0.1/Name:test.php',
+                           '<a href="http://127.0.0.1/Name:test.php" rel="external">127.0.0.1/Name:test.php</a>'),
+                     array('127.0.0.1/~test',
+                           '<a href="http://127.0.0.1/~test" rel="external">127.0.0.1/~test</a>'),
+                     array('127.0.0.1/+test',
+                           '<a href="http://127.0.0.1/+test" rel="external">127.0.0.1/+test</a>'),
+                     array('127.0.0.1/$test',
+                           '<a href="http://127.0.0.1/$test" rel="external">127.0.0.1/$test</a>'),
+                     array('127.0.0.1/\'test',
+                           '<a href="http://127.0.0.1/\'test" rel="external">127.0.0.1/\'test</a>'),
+                     array('127.0.0.1/"test',
+                           '<a href="http://127.0.0.1/&quot;test" rel="external">127.0.0.1/&quot;test</a>'),
+                     array('127.0.0.1/-test',
+                           '<a href="http://127.0.0.1/-test" rel="external">127.0.0.1/-test</a>'),
+                     array('127.0.0.1/_test',
+                           '<a href="http://127.0.0.1/_test" rel="external">127.0.0.1/_test</a>'),
+                     array('127.0.0.1/!test',
+                           '<a href="http://127.0.0.1/!test" rel="external">127.0.0.1/!test</a>'),
+                     array('127.0.0.1/*test',
+                           '<a href="http://127.0.0.1/*test" rel="external">127.0.0.1/*test</a>'),
+                     array('127.0.0.1/test%20stuff',
+                           '<a href="http://127.0.0.1/test%20stuff" rel="external">127.0.0.1/test%20stuff</a>'),
                      array('http://[::1]:99/test.php',
                            '<a href="http://[::1]:99/test.php" rel="external">http://[::1]:99/test.php</a>'),
                      array('http://::1/test.php',
index 6f1a29f4aa67a1bd950165fe980cfa61c6df9d57..1f37a7637bc8e69ab5fc3ce4a0caad5ab119a0c1 100644 (file)
@@ -156,7 +156,8 @@ font-weight:bold;
 #form_notice_delete legend,
 #form_password_recover legend,
 #form_password_change legend,
-.form_entity_block legend {
+.form_entity_block legend,
+#form_filter_bytag legend {
 display:none;
 }
 
@@ -510,6 +511,7 @@ margin-top:7px;
 margin-bottom:7px;
 margin-left:18px;
 float:left;
+max-width:322px;
 }
 #form_notice .error,
 #form_notice .success {
@@ -1049,36 +1051,37 @@ display:none;
 #filter_tags ul {
 list-style-type:none;
 }
-#filter_tags ul li {
+#filter_tags li {
 float:left;
 margin-left:7px;
 padding-left:7px;
 border-left-width:1px;
 border-left-style:solid;
 }
-#filter_tags ul li.child_1 {
+#filter_tags #filter_tags_all {
 margin-left:0;
 border-left:0;
 padding-left:0;
 }
-#filter_tags ul li#filter_tags_all a {
+#filter_tags_all a {
 font-weight:bold;
 margin-top:7px;
 float:left;
 }
 
-#filter_tags ul li#filter_tags_item label {
+#filter_tags_item label {
 margin-right:7px;
 }
-#filter_tags ul li#filter_tags_item label,
-#filter_tags ul li#filter_tags_item select {
-display:inline;
+#filter_tags_item label,
+#filter_tags_item select {
+float:left;
 }
-#filter_tags ul li#filter_tags_item p {
+#filter_tags_item p {
 float:left;
+clear:both;
 margin-left:38px;
 }
-#filter_tags ul li#filter_tags_item input {
+#filter_tags_item .submit {
 position:relative;
 top:3px;
 left:3px;
index 5245ea5d2ffec57558770a3628a5d3e84598f6a0..6357e55b4e23805fb50376303ec172fe9b105e27 100644 (file)
@@ -849,6 +849,10 @@ float:left;
 font-size:1.025em;
 }
 
+.notice div.entry-content .timestamp {
+display:inline-block;
+}
+
 .notice div.entry-content dl,
 .notice div.entry-content dt,
 .notice div.entry-content dd {
@@ -866,15 +870,12 @@ display:inline-block;
 text-transform:lowercase;
 }
 
-
 .notice-options {
-padding-left:2%;
-float:left;
-width:50%;
 position:relative;
 font-size:0.95em;
-width:12.5%;
+width:90px;
 float:right;
+margin-right:11px;
 }
 
 .notice-options a {
@@ -897,38 +898,28 @@ left:29px;
 .notice-options .notice_delete {
 right:0;
 }
-.notice-options .notice_reply dt {
-display:none;
-}
-
 .notice-options input,
 .notice-options a {
 text-indent:-9999px;
 outline:none;
 }
-
-.notice-options .notice_reply a,
 .notice-options input.submit {
 display:block;
 border:0;
 }
-.notice-options .notice_reply a,
-.notice-options .notice_delete {
+.notice-options .notice_reply,
+.notice-options .notice_delete {
 text-decoration:none;
 padding-left:16px;
 }
-
 .notice-options form input.submit {
 width:16px;
 padding:2px 0;
 }
-
-.notice-options .notice_delete dt,
 .notice-options .form_favor legend,
 .notice-options .form_disfavor legend {
 display:none;
 }
-.notice-options .notice_delete fieldset,
 .notice-options .form_favor fieldset,
 .notice-options .form_disfavor fieldset {
 border:0;
index 240060b104fe4a8cdf3084c58c9df280a5522405..7ea4515769f4d1ec26f767ea75ecbba6a5bff510 100644 (file)
@@ -30,10 +30,10 @@ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
 }
 input, textarea, select,
 .entity_remote_subscribe {
-border-color:#aaa;
+border-color:#AAAAAA;
 }
 #filter_tags ul li {
-border-color:#ddd;
+border-color:#DDDDDD;
 }
 
 .form_settings input.form_action-primary {
@@ -50,11 +50,14 @@ background-color:#9BB43E;
 input:focus, textarea:focus, select:focus,
 #form_notice.warning #notice_data-text {
 border-color:#9BB43E;
+box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
 }
 input.submit,
 .entity_remote_subscribe,
 #site_nav_local_views a {
-color:#fff;
+color:#FFFFFF;
 }
 
 a,
@@ -62,10 +65,13 @@ a,
 div.notice-options input,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
 .entity_send-a-message a,
 .form_user_nudge input.submit,
 .entity_nudge p,
-.form_settings input.form_action-primary {
+.form_settings input.form_action-primary,
+.form_make_admin input.submit {
 color:#002E6E;
 }
 
@@ -82,13 +88,6 @@ border-top-color:#CEE1E9;
 border-top-color:#87B4C8;
 }
 
-#content .notice p.entry-content a:visited {
-background-color:#fcfcfc;
-}
-#content .notice p.entry-content .vcard a {
-background-color:#fcfffc;
-}
-
 .aside .section {
 background-color:#F1F5F8;
 background-position:100% 0;
@@ -97,10 +96,10 @@ background-repeat:no-repeat;
 }
 
 #notice_text-count {
-color:#333;
+color:#333333;
 }
 #form_notice.warning #notice_text-count {
-color:#000;
+color:#000000;
 }
 #form_notice label[for=notice_data-attach] {
 background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
@@ -109,28 +108,43 @@ background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no
 opacity:0;
 }
 
-#form_notice.processing #notice_action-submit {
-background:#fff url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
+#wrap form.processing input.submit {
+background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
 cursor:wait;
 text-indent:-9999px;
+outline:none;
 }
 
+#content {
+box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+}
 #content,
 #site_nav_local_views a,
 .aside .section {
-border-color:#fff;
+border-color:#FFFFFF;
 }
 #content,
 #site_nav_local_views .current a {
-background-color:#fff;
+background-color:#FFFFFF;
 }
 
+#site_nav_local_views li {
+box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+-webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+}
 #site_nav_local_views a {
-background-color:rgba(135, 180, 200, 0.3);
+background-color:rgba(194, 194, 194, 0.5);
 }
 #site_nav_local_views a:hover {
 background-color:rgba(255, 255, 255, 0.7);
 }
+#site_nav_local_views .current a {
+text-shadow: rgba(194,194,194,0.5) 1px 1px 1px;
+}
+
 
 .error {
 background-color:#F7E8E8;
@@ -140,10 +154,7 @@ background-color:#EFF3DC;
 }
 
 #anon_notice {
-color:#fff;
-}
-
-#showstream #anon_notice {
+color:#FFFFFF;
 }
 
 #export_data li a {
@@ -165,7 +176,10 @@ background-image:url(../../base/images/icons/icon_foaf.gif);
 .form_user_nudge input.submit,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
-.entity_nudge p {
+.form_group_block input.submit,
+.form_group_unblock input.submit,
+.entity_nudge p,
+.form_make_admin input.submit {
 background-position: 0 40%;
 background-repeat: no-repeat;
 background-color:transparent;
@@ -175,7 +189,7 @@ background-color:transparent;
 .form_user_subscribe input.submit,
 .form_user_unsubscribe input.submit {
 background-color:#9BB43E;
-color:#fff;
+color:#FFFFFF;
 }
 .form_user_unsubscribe input.submit,
 .form_group_leave input.submit,
@@ -194,20 +208,23 @@ background-image:url(../../base/images/icons/twotone/green/quote.gif);
 background-image:url(../../base/images/icons/twotone/green/mail.gif);
 }
 .form_user_block input.submit,
-.form_user_unblock input.submit {
+.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit {
 background-image:url(../../base/images/icons/twotone/green/shield.gif);
 }
+.form_make_admin input.submit {
+background-image:url(../../base/images/icons/twotone/green/admin.gif);
+}
 
 /* NOTICES */
-.notices li.over {
-background-color:#fcfcfc;
+.notice .attachment {
+background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%;
 }
-
-.notice-options .notice_reply a,
-.notice-options form input.submit {
-background-color:transparent;
+#attachments .attachment {
+background:none;
 }
-.notice-options .notice_reply {
+.notice-options .notice_reply {
 background:transparent url(../../base/images/icons/twotone/green/reply.gif) no-repeat 0 45%;
 }
 .notice-options form.form_favor input.submit {
@@ -216,7 +233,7 @@ background:transparent url(../../base/images/icons/twotone/green/favourite.gif)
 .notice-options form.form_disfavor input.submit {
 background:transparent url(../../base/images/icons/twotone/green/disfavourite.gif) no-repeat 0 45%;
 }
-.notice-options .notice_delete {
+.notice-options .notice_delete {
 background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%;
 }
 
@@ -224,19 +241,32 @@ background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-r
 .notices div.notice-options {
 opacity:0.4;
 }
-.notices li.hover div.entry-content,
-.notices li.hover div.notice-options {
+.notices li:hover div.entry-content,
+.notices li:hover div.notice-options {
 opacity:1;
 }
-div.entry-content {
-color:#333;
-}
 div.notice-options a,
 div.notice-options input {
 font-family:sans-serif;
 }
-.notices li.hover {
-background-color:#fcfcfc;
+#content .notices li:hover {
+background-color:rgba(240, 240, 240, 0.2);
+}
+#conversation .notices li:hover {
+background-color:transparent;
+}
+
+.notices .notices {
+background-color:rgba(200, 200, 200, 0.050);
+}
+.notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.100);
+}
+.notices .notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.150);
+}
+.notices .notices .notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.300);
 }
 /*END: NOTICES */
 
index fdead6c4a0a23614552329ee68bb0860b0200a0c..550d373fef4005342c4e1daa7cb2c115db54f46c 100644 (file)
Binary files a/theme/biz/logo.png and b/theme/biz/logo.png differ
index fdead6c4a0a23614552329ee68bb0860b0200a0c..550d373fef4005342c4e1daa7cb2c115db54f46c 100644 (file)
Binary files a/theme/cloudy/logo.png and b/theme/cloudy/logo.png differ
index a1c4a2171d3bc59cf5e9feb746eb7f9f46432042..86369cb9935d64f82f9757f1bdf81a398e5e8742 100644 (file)
@@ -94,10 +94,11 @@ background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no
 opacity:0;
 }
 
-#form_notice.processing #notice_action-submit {
+#wrap form.processing input.submit {
 background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
 cursor:wait;
 text-indent:-9999px;
+outline:none;
 }
 
 #content {
@@ -223,10 +224,6 @@ background:transparent url(../../base/images/icons/twotone/green/favourite.gif)
 .notice-options form.form_disfavor input.submit {
 background:transparent url(../../base/images/icons/twotone/green/disfavourite.gif) no-repeat 0 45%;
 }
-.notice-options form.form_favor.processing input.submit,
-.notice-options form.form_disfavor.processing input.submit {
-background:transparent url(../../base/images/icons/icon_processing.gif) no-repeat 0 45%;
-}
 .notice-options .notice_delete {
 background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%;
 }
index fdead6c4a0a23614552329ee68bb0860b0200a0c..550d373fef4005342c4e1daa7cb2c115db54f46c 100644 (file)
Binary files a/theme/h4ck3r/logo.png and b/theme/h4ck3r/logo.png differ
index 51286657ec88fae6b1c2500964eb998a82a9050c..9fc97180d4e997cccbac55d70b5237c1bdec1f7f 100644 (file)
@@ -94,10 +94,11 @@ background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no
 opacity:0;
 }
 
-#form_notice.processing #notice_action-submit {
+#wrap form.processing input.submit {
 background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
 cursor:wait;
 text-indent:-9999px;
+outline:none;
 }
 
 #content {
index fdead6c4a0a23614552329ee68bb0860b0200a0c..550d373fef4005342c4e1daa7cb2c115db54f46c 100644 (file)
Binary files a/theme/pigeonthoughts/logo.png and b/theme/pigeonthoughts/logo.png differ