]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'testing' into -1.9.x
authorZach Copley <zach@status.net>
Wed, 27 Jan 2010 22:27:22 +0000 (14:27 -0800)
committerZach Copley <zach@status.net>
Wed, 27 Jan 2010 22:27:22 +0000 (14:27 -0800)
* testing: (130 commits)
  HTTP auth provided is evaluated even if it's not required
  Rename rc3to09.sql to rc3torc4.sql to avoid confusion if we add a last-minute change after this!
  Add new oauth tables and modifications to 'consumer' table for rc4
  Centred leaderboard ad
  camelcase the uap param names
  move leaderboard to after the header
  Moved rectangle ad into aside and leaderboard to the right in header.
  Aligning wide skyscraper to the right instead of left
  CSS ids and classes fixed in UAPPlugin
  wrong height for rectangle in BlankAd
  Add the moved BlankAdPlugin
  make BlankAd dir and change to use a 1x1 image
  move BlankAdPlugin to its own dir
  Add BlankAdPlugin to test ad layout in different themes
  make uapplugin an abstract class
  move UAP plugin to core
  Lowercased switch cases in UAP Plugin
  Plugin for Universal Ad Package. Outputs four most widely used ad types.
  Add persistent:true property to Stomp messages so ActiveMQ doesn't decide to discard them even though persistence is enabled on the broker. :) (Thanks Aric!)
  quick fix: use common_path() on realtime update JS so it works with the new JS path code (will pull from main server for now)
  ...

Conflicts:
actions/apioauthaccesstoken.php
actions/apioauthauthorize.php
actions/apioauthrequesttoken.php
actions/editapplication.php
actions/newapplication.php
lib/apiauth.php
lib/queuemanager.php
lib/router.php

50 files changed:
README
actions/accessadminpanel.php [new file with mode: 0644]
actions/apioauthaccesstoken.php
actions/apioauthauthorize.php
actions/apioauthrequesttoken.php
actions/apistatusesupdate.php
actions/avatarsettings.php
actions/designadminpanel.php
actions/editapplication.php
actions/grouplogo.php
actions/inbox.php
actions/newapplication.php
actions/outbox.php
actions/pathsadminpanel.php
actions/replies.php
actions/showfavorites.php
actions/showgroup.php
actions/showstream.php
actions/siteadminpanel.php
actions/tag.php
actions/usergroups.php
classes/Memcached_DataObject.php
classes/Status_network.php
db/rc3to09.sql [deleted file]
db/rc3torc4.sql [new file with mode: 0644]
db/site.sql
lib/action.php
lib/adminpanelaction.php
lib/apiauth.php
lib/default.php
lib/designsettings.php
lib/htmloutputter.php
lib/iomaster.php
lib/personalgroupnav.php
lib/queuemanager.php
lib/router.php
lib/spawningdaemon.php
lib/stompqueuemanager.php
lib/uapplugin.php [new file with mode: 0644]
plugins/BlankAd/BlankAdPlugin.php [new file with mode: 0644]
plugins/BlankAd/redpixel.png [new file with mode: 0644]
plugins/Facebook/facebookaction.php
plugins/PubSubHubBub/PubSubHubBubPlugin.php
plugins/Realtime/RealtimePlugin.php
scripts/console.php
scripts/queuectl.php [new file with mode: 0755]
scripts/queuedaemon.php
scripts/xmppdaemon.php
tests/oauth/statusupdate.php [new file with mode: 0644]
theme/base/css/uap.css [new file with mode: 0644]

diff --git a/README b/README
index 6022887899c27db6e644b7226778c56d117d5e57..f83873ca84487803a276de5fbc71cc6b4b9270df 100644 (file)
--- a/README
+++ b/README
@@ -1492,6 +1492,15 @@ disabled: whether to enable this command. If enabled, users who send
          should enable it only after you've convinced yourself that
          it is safe. Default is 'false'.
 
+singleuser
+----------
+
+If an installation has only one user, this can simplify a lot of the
+interface. It also makes the user's profile the root URL.
+
+enabled: Whether to run in "single user mode". Default false.
+nickname: nickname of the single user.
+
 Plugins
 =======
 
diff --git a/actions/accessadminpanel.php b/actions/accessadminpanel.php
new file mode 100644 (file)
index 0000000..4768e2f
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Site access administration panel
+ *
+ * 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  Settings
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2010 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')) {
+    exit(1);
+}
+
+/**
+ * Administer site access settings
+ *
+ * @category Admin
+ * @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 AccessadminpanelAction extends AdminPanelAction
+{
+    /**
+     * Returns the page title
+     *
+     * @return string page title
+     */
+
+    function title()
+    {
+        return _('Access');
+    }
+
+    /**
+     * Instructions for using this form.
+     *
+     * @return string instructions
+     */
+
+    function getInstructions()
+    {
+        return _('Site access settings');
+    }
+
+    /**
+     * Show the site admin panel form
+     *
+     * @return void
+     */
+
+    function showForm()
+    {
+        $form = new AccessAdminPanelForm($this);
+        $form->show();
+        return;
+    }
+
+    /**
+     * Save settings from the form
+     *
+     * @return void
+     */
+
+    function saveSettings()
+    {
+        static $booleans = array('site' => array('private', 'inviteonly', 'closed'));
+
+        foreach ($booleans as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0;
+            }
+        }
+
+        $config = new Config();
+
+        $config->query('BEGIN');
+
+        foreach ($booleans as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        $config->query('COMMIT');
+
+        return;
+    }
+
+}
+
+class AccessAdminPanelForm extends AdminForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        return 'form_site_admin_panel';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+
+    function formClass()
+    {
+        return 'form_settings';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+
+    function action()
+    {
+        return common_local_url('accessadminpanel');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+       $this->out->elementStart('fieldset', array('id' => 'settings_admin_access'));
+        $this->out->element('legend', null, _('Registration'));
+        $this->out->elementStart('ul', 'form_data');
+        $this->li();
+        $this->out->checkbox('private', _('Private'),
+                             (bool) $this->value('private'),
+                             _('Prohibit anonymous users (not logged in) from viewing site?'));
+        $this->unli();
+
+        $this->li();
+        $this->out->checkbox('inviteonly', _('Invite only'),
+                             (bool) $this->value('inviteonly'),
+                             _('Make registration invitation only.'));
+        $this->unli();
+
+        $this->li();
+        $this->out->checkbox('closed', _('Closed'),
+                             (bool) $this->value('closed'),
+                             _('Disable new registrations.'));
+        $this->unli();
+        $this->out->elementEnd('ul');
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        $this->out->submit('submit', _('Save'), 'submit', null, _('Save access settings'));
+    }
+
+}
index 085ef6f0b1dba92fab4b2212b8ad2da8e0377e45..887df4c20d7170ce6d564f4abe9135ba7f043bae 100644 (file)
@@ -70,7 +70,7 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
             $atok = $server->fetch_access_token($req);
 
         } catch (OAuthException $e) {
-            common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage());
+            common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
             common_debug(var_export($req, true));
             $this->outputError($e->getMessage());
             return;
index 19128bdcefe5798580c0b1b5cfdb75bb432c08e9..fa074c4e76e15cbbdbfdc335d9856e45b8e25030 100644 (file)
@@ -303,8 +303,8 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
         $access = ($this->app->access_type & Oauth_application::$writeAccess) ?
           'access and update' : 'access';
 
-        $msg = _("The application <strong>%1$s</strong> by <strong>%2$s</strong> would like " .
-                 "the ability to <strong>%3$s</strong> your account data.");
+        $msg = _("The application <strong>%s</strong> by <strong>%s</strong> would like " .
+                 "the ability to <strong>%s</strong> your account data.");
 
         $this->raw(sprintf($msg,
                            $this->app->name,
index 467640b9aacf5d32864179da2d4ceaea02d50868..4fa626d866a084bfbd8e127d601a6e4cfa53309c 100644 (file)
@@ -89,7 +89,7 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
             $token = $server->fetch_request_token($req);
             print $token;
         } catch (OAuthException $e) {
-            common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage());
+            common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
             header('HTTP/1.1 401 Unauthorized');
             header('Content-Type: text/html; charset=utf-8');
             print $e->getMessage() . "\n";
index 31c9b20ce2783774927ab0f212e80627cd685eeb..bf367e1e181741e4d5626bd6332f0ae5432cc713 100644 (file)
@@ -28,7 +28,7 @@
  * @author    Mike Cochrane <mikec@mikenz.geek.nz>
  * @author    Robin Millette <robin@millette.info>
  * @author    Zach Copley <zach@status.net>
- * @copyright 2009 StatusNet, Inc.
+ * @copyright 2009-2010 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/
  */
@@ -79,7 +79,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
     {
         parent::prepare($args);
 
-        $this->user   = $this->auth_user;
         $this->status = $this->trimmed('status');
         $this->source = $this->trimmed('source');
         $this->lat    = $this->trimmed('lat');
@@ -145,7 +144,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
             return;
         }
 
-        if (empty($this->user)) {
+        if (empty($this->auth_user)) {
             $this->clientError(_('No such user.'), 404, $this->format);
             return;
         }
@@ -172,7 +171,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
         // Check for commands
 
         $inter = new CommandInterpreter();
-        $cmd = $inter->handle_command($this->user, $status_shortened);
+        $cmd = $inter->handle_command($this->auth_user, $status_shortened);
 
         if ($cmd) {
 
@@ -184,7 +183,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
             // And, it returns your last status whether the cmd was successful
             // or not!
 
-            $this->notice = $this->user->getCurrentNotice();
+            $this->notice = $this->auth_user->getCurrentNotice();
 
         } else {
 
@@ -211,7 +210,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
             $upload = null;
 
             try {
-                $upload = MediaFile::fromUpload('media', $this->user);
+                $upload = MediaFile::fromUpload('media', $this->auth_user);
             } catch (ClientException $ce) {
                 $this->clientError($ce->getMessage());
                 return;
@@ -234,19 +233,19 @@ class ApiStatusesUpdateAction extends ApiAuthAction
 
             $options = array('reply_to' => $reply_to);
 
-            if ($this->user->shareLocation()) {
+            if ($this->auth_user->shareLocation()) {
 
                 $locOptions = Notice::locationOptions($this->lat,
                                                       $this->lon,
                                                       null,
                                                       null,
-                                                      $this->user->getProfile());
+                                                      $this->auth_user->getProfile());
 
                 $options = array_merge($options, $locOptions);
             }
 
             $this->notice =
-              Notice::saveNew($this->user->id,
+              Notice::saveNew($this->auth_user->id,
                               $content,
                               $this->source,
                               $options);
@@ -255,7 +254,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
                 $upload->attachToNotice($this->notice);
             }
 
-
         }
 
         $this->showNotice();
index cf4525552029f1fde641d6311235991cca3732a5..6a7398746ae8f8ec2a5916812f588202c2487f8d 100644 (file)
@@ -416,8 +416,8 @@ class AvatarsettingsAction extends AccountSettingsAction
         parent::showScripts();
 
         if ($this->mode == 'crop') {
-            $this->script('js/jcrop/jquery.Jcrop.min.js');
-            $this->script('js/jcrop/jquery.Jcrop.go.js');
+            $this->script('jcrop/jquery.Jcrop.min.js');
+            $this->script('jcrop/jquery.Jcrop.go.js');
         }
 
         $this->autofocus('avatarfile');
index 72ad6ade2a12ced8c9dadba73b51069aec2e3119..30e8bde1a4e5d344ffa221a770183720cbfda204 100644 (file)
@@ -302,8 +302,8 @@ class DesignadminpanelAction extends AdminPanelAction
     {
         parent::showScripts();
 
-        $this->script('js/farbtastic/farbtastic.js');
-        $this->script('js/userdesign.go.js');
+        $this->script('farbtastic/farbtastic.js');
+        $this->script('userdesign.go.js');
 
         $this->autofocus('design_background-image_file');
     }
index 3b120259a8d2245a4e49f45561e950ad89d35df2..9cc3e3cead0cac9670a11f2761d7e0f5d9f11118 100644 (file)
@@ -51,7 +51,7 @@ class EditApplicationAction extends OwnerDesignAction
 
     function title()
     {
-        return _('Edit application');
+        return _('Edit Application');
     }
 
     /**
index f197aef33ec489baf748ab2b17fbb5f1e90f1227..3c9b562962e34944ab7864b8f96157de90d114b7 100644 (file)
@@ -437,8 +437,8 @@ class GrouplogoAction extends GroupDesignAction
         parent::showScripts();
 
         if ($this->mode == 'crop') {
-            $this->script('js/jcrop/jquery.Jcrop.min.js');
-            $this->script('js/jcrop/jquery.Jcrop.go.js');
+            $this->script('jcrop/jquery.Jcrop.min.js');
+            $this->script('jcrop/jquery.Jcrop.go.js');
         }
 
         $this->autofocus('avatarfile');
index f605cc9e8b3edf6d246c0febc68f716bf3e49384..8330f753ff1dd4161830656171b9373be10312d1 100644 (file)
@@ -56,10 +56,10 @@ class InboxAction extends MailboxAction
     function title()
     {
         if ($this->page > 1) {
-            return sprintf(_("Inbox for %1$s - page %2$d"), $this->user->nickname,
+            return sprintf(_('Inbox for %1$s - page %2$d'), $this->user->nickname,
                 $this->page);
         } else {
-            return sprintf(_("Inbox for %s"), $this->user->nickname);
+            return sprintf(_('Inbox for %s'), $this->user->nickname);
         }
     }
 
index bc5b4edaf84c5b0935ad0294ecf1744638b0e0e7..c499fe7c76ed995bcb9896448dc410f3738cccfb 100644 (file)
@@ -49,7 +49,7 @@ class NewApplicationAction extends OwnerDesignAction
 
     function title()
     {
-        return _('New application');
+        return _('New Application');
     }
 
     /**
index de30de018354ff5e599cb1a20f5d5126120c9872..b81d4b9d0d8d1fac1e5d27b240147b77f9155c82 100644 (file)
@@ -55,10 +55,10 @@ class OutboxAction extends MailboxAction
     function title()
     {
         if ($this->page > 1) {
-            return sprintf(_("Outbox for %1$s - page %2$d"),
+            return sprintf(_('Outbox for %1$s - page %2$d'),
                 $this->user->nickname, $page);
         } else {
-            return sprintf(_("Outbox for %s"), $this->user->nickname);
+            return sprintf(_('Outbox for %s'), $this->user->nickname);
         }
     }
 
index 3779fcfaaaeb94c90bd4dc47c4d47be23386524f..9155a7e42856ae19b61b405973c5de83aa3b3d5e 100644 (file)
@@ -24,7 +24,7 @@
  * @author    Evan Prodromou <evan@status.net>
  * @author    Zach Copley <zach@status.net>
  * @author    Sarven Capadisli <csarven@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2008-2010 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/
  */
@@ -98,6 +98,11 @@ class PathsadminpanelAction extends AdminPanelAction
             'background' => array('server', 'dir', 'path')
         );
 
+       // XXX: If we're only going to have one boolean on thi page we
+       // can remove some of the boolean processing code --Z
+
+       static $booleans = array('site' => array('fancy'));
+
         $values = array();
 
         foreach ($settings as $section => $parts) {
@@ -106,6 +111,12 @@ class PathsadminpanelAction extends AdminPanelAction
             }
         }
 
+        foreach ($booleans as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0;
+            }
+        }
+
         $this->validate($values);
 
         // assert(all values are valid);
@@ -120,7 +131,13 @@ class PathsadminpanelAction extends AdminPanelAction
             }
         }
 
-        $config->query('COMMIT');
+       foreach ($booleans as $section => $parts) {
+           foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+       }
+
+       $config->query('COMMIT');
 
         return;
     }
@@ -213,10 +230,14 @@ class PathsAdminPanelForm extends AdminForm
 
     function formData()
     {
-        $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale'));
+       $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale'));
         $this->out->element('legend', null, _('Site'), 'site');
         $this->out->elementStart('ul', 'form_data');
 
+       $this->li();
+        $this->input('server', _('Server'), _('Site\'s server hostname.'));
+        $this->unli();
+
         $this->li();
         $this->input('path', _('Path'), _('Site path'));
         $this->unli();
@@ -225,6 +246,12 @@ class PathsAdminPanelForm extends AdminForm
         $this->input('locale_path', _('Path to locales'), _('Directory path to locales'), 'site');
         $this->unli();
 
+       $this->li();
+        $this->out->checkbox('fancy', _('Fancy URLs'),
+                             (bool) $this->value('fancy'),
+                             _('Use fancy (more readable and memorable) URLs?'));
+       $this->unli();
+
         $this->out->elementEnd('ul');
         $this->out->elementEnd('fieldset');
 
index 2e50f1c3c4ce04f7379e8058073e5105c8da835e..164c328db3cd6e0f4749f11543254899f79e4acb 100644 (file)
@@ -124,7 +124,7 @@ class RepliesAction extends OwnerDesignAction
         if ($this->page == 1) {
             return sprintf(_("Replies to %s"), $this->user->nickname);
         } else {
-            return sprintf(_("Replies to %1$s, page %2$d"),
+            return sprintf(_('Replies to %1$s, page %2$d'),
                            $this->user->nickname,
                            $this->page);
         }
index 6023f015678185bfe18c680d5a5b1458840a6912..f2d0822936bc562c9014b4b531ec2eb3d2fdaf17 100644 (file)
@@ -74,9 +74,9 @@ class ShowfavoritesAction extends OwnerDesignAction
     function title()
     {
         if ($this->page == 1) {
-            return sprintf(_("%s's favorite notices"), $this->user->nickname);
+            return sprintf(_('%s\'s favorite notices'), $this->user->nickname);
         } else {
-            return sprintf(_("%1$s's favorite notices, page %2$d"),
+            return sprintf(_('%1$s\'s favorite notices, page %2$d'),
                            $this->user->nickname,
                            $this->page);
         }
index 06ae572e8107efd72ff30d82449fae612de7bd0c..8042a4951339a26f7aebaf4ad56f2e05de3a386b 100644 (file)
@@ -79,9 +79,9 @@ class ShowgroupAction extends GroupDesignAction
         }
 
         if ($this->page == 1) {
-            return sprintf(_("%s group"), $base);
+            return sprintf(_('%s group'), $base);
         } else {
-            return sprintf(_("%1$s group, page %2$d"),
+            return sprintf(_('%1$s group, page %2$d'),
                            $base,
                            $this->page);
         }
index 75e10858d08ad2f917074c56a5ed0861bec29346..90ff67073a1bd7d120cbcae560b010e9031d12bc 100644 (file)
@@ -76,7 +76,7 @@ class ShowstreamAction extends ProfileAction
         if ($this->page == 1) {
             return $base;
         } else {
-            return sprintf(_("%1$s, page %2$d"),
+            return sprintf(_('%1$s, page %2$d'),
                            $base,
                            $this->page);
         }
index dd388a18a2f1c6ac757b9196fcb59b85329d95bd..8c8f8b3742bf5c28590281c59dc14504c828b521 100644 (file)
@@ -24,7 +24,7 @@
  * @author    Evan Prodromou <evan@status.net>
  * @author    Zach Copley <zach@status.net>
  * @author    Sarven Capadisli <csarven@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2008-2010 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/
  */
@@ -95,8 +95,6 @@ class SiteadminpanelAction extends AdminPanelAction
                                                  'site', 'textlimit', 'dupelimit'),
                                  'snapshot' => array('run', 'reporturl', 'frequency'));
 
-        static $booleans = array('site' => array('private', 'inviteonly', 'closed', 'fancy'));
-
         $values = array();
 
         foreach ($settings as $section => $parts) {
@@ -105,12 +103,6 @@ class SiteadminpanelAction extends AdminPanelAction
             }
         }
 
-        foreach ($booleans as $section => $parts) {
-            foreach ($parts as $setting) {
-                $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0;
-            }
-        }
-
         // This throws an exception on validation errors
 
         $this->validate($values);
@@ -127,12 +119,6 @@ class SiteadminpanelAction extends AdminPanelAction
             }
         }
 
-        foreach ($booleans as $section => $parts) {
-            foreach ($parts as $setting) {
-                Config::save($section, $setting, $values[$section][$setting]);
-            }
-        }
-
         $config->query('COMMIT');
 
         return;
@@ -299,44 +285,6 @@ class SiteAdminPanelForm extends AdminForm
         $this->out->elementEnd('ul');
         $this->out->elementEnd('fieldset');
 
-        $this->out->elementStart('fieldset', array('id' => 'settings_admin_urls'));
-        $this->out->element('legend', null, _('URLs'));
-        $this->out->elementStart('ul', 'form_data');
-        $this->li();
-        $this->input('server', _('Server'), _('Site\'s server hostname.'));
-        $this->unli();
-
-        $this->li();
-        $this->out->checkbox('fancy', _('Fancy URLs'),
-                             (bool) $this->value('fancy'),
-                             _('Use fancy (more readable and memorable) URLs?'));
-        $this->unli();
-        $this->out->elementEnd('ul');
-        $this->out->elementEnd('fieldset');
-
-        $this->out->elementStart('fieldset', array('id' => 'settings_admin_access'));
-        $this->out->element('legend', null, _('Access'));
-        $this->out->elementStart('ul', 'form_data');
-        $this->li();
-        $this->out->checkbox('private', _('Private'),
-                             (bool) $this->value('private'),
-                             _('Prohibit anonymous users (not logged in) from viewing site?'));
-        $this->unli();
-
-        $this->li();
-        $this->out->checkbox('inviteonly', _('Invite only'),
-                             (bool) $this->value('inviteonly'),
-                             _('Make registration invitation only.'));
-        $this->unli();
-
-        $this->li();
-        $this->out->checkbox('closed', _('Closed'),
-                             (bool) $this->value('closed'),
-                             _('Disable new registrations.'));
-        $this->unli();
-        $this->out->elementEnd('ul');
-        $this->out->elementEnd('fieldset');
-
         $this->out->elementStart('fieldset', array('id' => 'settings_admin_snapshots'));
         $this->out->element('legend', null, _('Snapshots'));
         $this->out->elementStart('ul', 'form_data');
index 12857236eff733e03c22a4eec98942578492555d..e91df6ea97525752b1ea7e945b5e75d64ce95ddb 100644 (file)
@@ -63,9 +63,9 @@ class TagAction extends Action
     function title()
     {
         if ($this->page == 1) {
-            return sprintf(_("Notices tagged with %s"), $this->tag);
+            return sprintf(_('Notices tagged with %s'), $this->tag);
         } else {
-            return sprintf(_("Notices tagged with %1$s, page %2$d"),
+            return sprintf(_('Notices tagged with %1$s, page %2$d'),
                            $this->tag,
                            $this->page);
         }
@@ -85,7 +85,7 @@ class TagAction extends Action
                                                array('tag' => $this->tag)),
                               sprintf(_('Notice feed for tag %s (RSS 1.0)'),
                                       $this->tag)),
-                     new Feed(Feed::RSS2,   
+                     new Feed(Feed::RSS2,
                               common_local_url('ApiTimelineTag',
                                                array('format' => 'rss',
                                                      'tag' => $this->tag)),
index 504226143280bfff9f11e03b9cf2a5e3ca96632c..97faabae65ad9cd7f02ab1c5cc0cba3d04a21fa1 100644 (file)
@@ -59,9 +59,9 @@ class UsergroupsAction extends OwnerDesignAction
     function title()
     {
         if ($this->page == 1) {
-            return sprintf(_("%s groups"), $this->user->nickname);
+            return sprintf(_('%s groups'), $this->user->nickname);
         } else {
-            return sprintf(_("%1$s groups, page %2$d"),
+            return sprintf(_('%1$s groups, page %2$d'),
                            $this->user->nickname,
                            $this->page);
         }
index 43610dddbbf39cac81005afc39aa994948108ce0..2cc6377f83f1a5b38545d4faeb6709792fbfe721 100644 (file)
@@ -66,7 +66,6 @@ class Memcached_DataObject extends DB_DataObject
         // Clear this out so we don't accidentally break global
         // state in *this* process.
         $this->_DB_resultid = null;
-
         // We don't have any local DBO refs, so clear these out.
         $this->_link_loaded = false;
     }
@@ -91,9 +90,7 @@ class Memcached_DataObject extends DB_DataObject
             unset($i);
         }
         $i = Memcached_DataObject::getcached($cls, $k, $v);
-        if ($i) {
-            return $i;
-        } else {
+        if ($i === false) { // false == cache miss
             $i = DB_DataObject::factory($cls);
             if (empty($i)) {
                 $i = false;
@@ -101,22 +98,34 @@ class Memcached_DataObject extends DB_DataObject
             }
             $result = $i->get($k, $v);
             if ($result) {
+                // Hit!
                 $i->encache();
-                return $i;
             } else {
+                // save the fact that no such row exists
+                $c = self::memcache();
+                if (!empty($c)) {
+                    $ck = self::cachekey($cls, $k, $v);
+                    $c->set($ck, null);
+                }
                 $i = false;
-                return $i;
             }
         }
+        return $i;
     }
 
-    function &pkeyGet($cls, $kv)
+    /**
+     * @fixme Should this return false on lookup fail to match staticGet?
+     */
+    function pkeyGet($cls, $kv)
     {
         $i = Memcached_DataObject::multicache($cls, $kv);
-        if ($i) {
+        if ($i !== false) { // false == cache miss
             return $i;
         } else {
-            $i = new $cls();
+            $i = DB_DataObject::factory($cls);
+            if (empty($i)) {
+                return false;
+            }
             foreach ($kv as $k => $v) {
                 $i->$k = $v;
             }
@@ -124,6 +133,11 @@ class Memcached_DataObject extends DB_DataObject
                 $i->encache();
             } else {
                 $i = null;
+                $c = self::memcache();
+                if (!empty($c)) {
+                    $ck = self::multicacheKey($cls, $kv);
+                    $c->set($ck, null);
+                }
             }
             return $i;
         }
@@ -132,6 +146,9 @@ class Memcached_DataObject extends DB_DataObject
     function insert()
     {
         $result = parent::insert();
+        if ($result) {
+            $this->encache(); // in case of cached negative lookups
+        }
         return $result;
     }
 
@@ -186,6 +203,17 @@ class Memcached_DataObject extends DB_DataObject
 
     function keyTypes()
     {
+        // ini-based classes return number-indexed arrays. handbuilt
+        // classes return column => keytype. Make this uniform.
+
+        $keys = $this->keys();
+
+        $keyskeys = array_keys($keys);
+
+        if (is_string($keyskeys[0])) {
+            return $keys;
+        }
+
         global $_DB_DATAOBJECT;
         if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
             $this->databaseStructure();
@@ -197,6 +225,7 @@ class Memcached_DataObject extends DB_DataObject
     function encache()
     {
         $c = $this->memcache();
+
         if (!$c) {
             return false;
         } else if ($this->tableName() == 'user' && is_object($this->id)) {
@@ -206,64 +235,86 @@ class Memcached_DataObject extends DB_DataObject
                        str_replace("\n", " ", $e->getTraceAsString()));
             return false;
         } else {
-            $pkey = array();
-            $pval = array();
-            $types = $this->keyTypes();
-            ksort($types);
-            foreach ($types as $key => $type) {
-                if ($type == 'K') {
-                    $pkey[] = $key;
-                    $pval[] = $this->$key;
-                } else {
-                    $c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this);
-                }
-            }
-            # XXX: should work for both compound and scalar pkeys
-            $pvals = implode(',', $pval);
-            $pkeys = implode(',', $pkey);
-            $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this);
+               $keys = $this->_allCacheKeys();
+
+               foreach ($keys as $key) {
+                   $c->set($key, $this);
+               }
         }
     }
 
     function decache()
     {
         $c = $this->memcache();
+
         if (!$c) {
             return false;
-        } else {
-            $pkey = array();
-            $pval = array();
-            $types = $this->keyTypes();
-            ksort($types);
-            foreach ($types as $key => $type) {
-                if ($type == 'K') {
-                    $pkey[] = $key;
-                    $pval[] = $this->$key;
-                } else {
-                    $c->delete($this->cacheKey($this->tableName(), $key, $this->$key));
+        }
+
+        $keys = $this->_allCacheKeys();
+
+        foreach ($keys as $key) {
+            $c->delete($key, $this);
+        }
+    }
+
+    function _allCacheKeys()
+    {
+        $ckeys = array();
+
+        $types = $this->keyTypes();
+        ksort($types);
+
+        $pkey = array();
+        $pval = array();
+
+        foreach ($types as $key => $type) {
+
+            assert(!empty($key));
+
+            if ($type == 'U') {
+                if (empty($this->$key)) {
+                    continue;
                 }
+                $ckeys[] = $this->cacheKey($this->tableName(), $key, $this->$key);
+            } else if ($type == 'K' || $type == 'N') {
+                $pkey[] = $key;
+                $pval[] = $this->$key;
+            } else {
+                throw new Exception("Unknown key type $key => $type for " . $this->tableName());
             }
-            # should work for both compound and scalar pkeys
-            # XXX: comma works for now but may not be safe separator for future keys
-            $pvals = implode(',', $pval);
-            $pkeys = implode(',', $pkey);
-            $c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals));
         }
+
+        assert(count($pkey) > 0);
+
+        // XXX: should work for both compound and scalar pkeys
+        $pvals = implode(',', $pval);
+        $pkeys = implode(',', $pkey);
+
+        $ckeys[] = $this->cacheKey($this->tableName(), $pkeys, $pvals);
+
+        return $ckeys;
     }
 
     function multicache($cls, $kv)
     {
         ksort($kv);
-        $c = Memcached_DataObject::memcache();
+        $c = self::memcache();
         if (!$c) {
             return false;
         } else {
-            $pkeys = implode(',', array_keys($kv));
-            $pvals = implode(',', array_values($kv));
-            return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals));
+            return $c->get(self::multicacheKey($cls, $kv));
         }
     }
 
+    static function multicacheKey($cls, $kv)
+    {
+        ksort($kv);
+        $pkeys = implode(',', array_keys($kv));
+        $pvals = implode(',', array_values($kv));
+        return self::cacheKey($cls, $pkeys, $pvals);
+    }
+
     function getSearchEngine($table)
     {
         require_once INSTALLDIR.'/lib/search_engines.php';
@@ -298,7 +349,8 @@ class Memcached_DataObject extends DB_DataObject
         $key_part = common_keyize($cls).':'.md5($qry);
         $ckey = common_cache_key($key_part);
         $stored = $c->get($ckey);
-        if ($stored) {
+
+        if ($stored !== false) {
             return new ArrayWrapper($stored);
         }
 
index 445f8a5a3c5759e0c5a4b00ca1861ce85a9af5e3..4bda24b6a02d253aeb03ad2efdf65605da0ee65d 100644 (file)
@@ -39,9 +39,19 @@ class Status_network extends DB_DataObject
     public $logo;                            // varchar(255)
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+    public $tags;                            // text
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Status_network',$k,$v); }
+    function staticGet($k,$v=NULL) {
+        $i = DB_DataObject::staticGet('Status_network',$k,$v);
+
+        // Don't use local process cache; if we're fetching multiple
+        // times it's because we're reloading it in a long-running
+        // process; we need a fresh copy!
+        global $_DB_DATAOBJECT;
+        unset($_DB_DATAOBJECT['CACHE']['status_network']);
+        return $i;
+    }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -245,4 +255,23 @@ class Status_network extends DB_DataObject
             return $this->nickname . '.' . self::$wildcard;
         }
     }
+
+    /**
+     * Return site meta-info tags as an array
+     * @return array of strings
+     */
+    function getTags()
+    {
+        return array_filter(explode("|", strval($this->tags)));
+    }
+
+    /**
+     * Check if this site record has a particular meta-info tag attached.
+     * @param string $tag
+     * @return bool
+     */
+    function hasTag($tag)
+    {
+        return in_array($tag, $this->getTags());
+    }
 }
diff --git a/db/rc3to09.sql b/db/rc3to09.sql
deleted file mode 100644 (file)
index 02dc7a6..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-create table queue_item_new (
-    id integer auto_increment primary key comment 'unique identifier',
-    frame blob not null comment 'data: object reference or opaque string',
-    transport varchar(8) not null comment 'queue for what? "email", "jabber", "sms", "irc", ...',
-    created datetime not null comment 'date this record was created',
-    claimed datetime comment 'date this item was claimed',
-
-    index queue_item_created_idx (created)
-
-) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
-
-insert into queue_item_new (frame,transport,created,claimed)
-    select notice_id,transport,created,claimed from queue_item;
-alter table queue_item rename to queue_item_old;
-alter table queue_item_new rename to queue_item;
-
diff --git a/db/rc3torc4.sql b/db/rc3torc4.sql
new file mode 100644 (file)
index 0000000..8342c4b
--- /dev/null
@@ -0,0 +1,48 @@
+create table queue_item_new (
+    id integer auto_increment primary key comment 'unique identifier',
+    frame blob not null comment 'data: object reference or opaque string',
+    transport varchar(8) not null comment 'queue for what? "email", "jabber", "sms", "irc", ...',
+    created datetime not null comment 'date this record was created',
+    claimed datetime comment 'date this item was claimed',
+
+    index queue_item_created_idx (created)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+insert into queue_item_new (frame,transport,created,claimed)
+    select notice_id,transport,created,claimed from queue_item;
+alter table queue_item rename to queue_item_old;
+alter table queue_item_new rename to queue_item;
+
+alter table consumer
+    add consumer_secret varchar(255) not null comment 'secret value',
+    add verifier varchar(255) comment 'verifier string for OAuth 1.0a',
+    add verified_callback varchar(255) comment 'verified callback URL for OAuth 1.0a';
+
+create table oauth_application (
+    id integer auto_increment primary key comment 'unique identifier',
+    owner integer not null comment 'owner of the application' references profile (id),
+    consumer_key varchar(255) not null comment 'application consumer key' references consumer (consumer_key),
+    name varchar(255) not null comment 'name of the application',
+    description varchar(255) comment 'description of the application',
+    icon varchar(255) not null comment 'application icon',
+    source_url varchar(255) comment 'application homepage - used for source link',
+    organization varchar(255) comment 'name of the organization running the application',
+    homepage varchar(255) comment 'homepage for the organization',
+    callback_url varchar(255) comment 'url to redirect to after authentication',
+    type tinyint default 0 comment 'type of app, 1 = browser, 2 = desktop',
+    access_type tinyint default 0 comment 'default access type, bit 1 = read, bit 2 = write',
+    created datetime not null comment 'date this record was created',
+    modified timestamp comment 'date this record was modified'
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table oauth_application_user (
+    profile_id integer not null comment 'user of the application' references profile (id),
+    application_id integer not null comment 'id of the application' references oauth_application (id),
+    access_type tinyint default 0 comment 'access type, bit 1 = read, bit 2 = write, bit 3 = revoked',
+    token varchar(255) comment 'request or access token',
+    created datetime not null comment 'date this record was created',
+    modified timestamp comment 'date this record was modified',
+    constraint primary key (profile_id, application_id)
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
index a9f64e5a5d0c0f9eac3a30a1255db0b03eb451d1..791303bd540885857f0f2baef805994684b8261a 100644 (file)
@@ -14,6 +14,8 @@ create table status_network (
     sitename varchar(255) comment 'display name',
     theme varchar(255) comment 'theme name',
     logo varchar(255) comment 'site logo',
+    
+    tags text comment 'site meta-info tags (pipe-separated)',
 
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified'
index e2427755852120fa61d1b5b8cb6576b1170dba49..cc4f4aad074b910399ac84133cc993621dcb5e5a 100644 (file)
@@ -246,18 +246,18 @@ class Action extends HTMLOutputter // lawsuit
     {
         if (Event::handle('StartShowScripts', array($this))) {
             if (Event::handle('StartShowJQueryScripts', array($this))) {
-                $this->script('js/jquery.min.js');
-                $this->script('js/jquery.form.js');
-                $this->script('js/jquery.cookie.js');
-                $this->script('js/json2.js');
-                $this->script('js/jquery.joverlay.min.js');
+                $this->script('jquery.min.js');
+                $this->script('jquery.form.js');
+                $this->script('jquery.cookie.js');
+                $this->script('json2.js');
+                $this->script('jquery.joverlay.min.js');
                 Event::handle('EndShowJQueryScripts', array($this));
             }
             if (Event::handle('StartShowStatusNetScripts', array($this)) &&
                 Event::handle('StartShowLaconicaScripts', array($this))) {
-                $this->script('js/xbImportNode.js');
-                $this->script('js/util.js');
-                $this->script('js/geometa.js');
+                $this->script('xbImportNode.js');
+                $this->script('util.js');
+                $this->script('geometa.js');
                 // Frame-busting code to avoid clickjacking attacks.
                 $this->element('script', array('type' => 'text/javascript'),
                                'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }');
@@ -392,8 +392,14 @@ class Action extends HTMLOutputter // lawsuit
         $this->elementStart('address', array('id' => 'site_contact',
                                              'class' => 'vcard'));
         if (Event::handle('StartAddressData', array($this))) {
+            if (common_config('singleuser', 'enabled')) {
+                $url = common_local_url('showstream',
+                                        array('nickname' => common_config('singleuser', 'nickname')));
+            } else {
+                $url = common_local_url('public');
+            }
             $this->elementStart('a', array('class' => 'url home bookmark',
-                                           'href' => common_local_url('public')));
+                                           'href' => $url));
             if (common_config('site', 'logo') || file_exists(Theme::file('logo.png'))) {
                 $this->element('img', array('class' => 'logo photo',
                                             'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png'),
index a6981ac6117d703128a9f9a03ff06dfe36e55499..f62bfa458ac89cc14ea05e9fed7d8ead6706084a 100644 (file)
@@ -319,12 +319,17 @@ class AdminPanelNav extends Widget
 
             if ($this->canAdmin('user')) {
                 $this->out->menuItem(common_local_url('useradminpanel'), _('User'),
-                                     _('Paths configuration'), $action_name == 'useradminpanel', 'nav_design_admin_panel');
+                                     _('User configuration'), $action_name == 'useradminpanel', 'nav_design_admin_panel');
             }
 
-            if ($this->canAdmin('paths')) {
-                $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'),
-                                     _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel');
+            if ($this->canAdmin('access')) {
+                $this->out->menuItem(common_local_url('accessadminpanel'), _('Access'),
+                                     _('Access configuration'), $action_name == 'accessadminpanel', 'nav_design_admin_panel');
+            }
+
+           if ($this->canAdmin('paths')) {
+               $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'),
+                                    _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel');
             }
 
             Event::handle('EndAdminPanelNav', array($this));
index 927dcad6af47d9c139ed1084ddfc574c66f19808..ac5e997c78dfc9b96b96810a1bee61fcda410637 100644 (file)
@@ -29,7 +29,7 @@
  * @author    mEDI <medi@milaro.net>
  * @author    Sarven Capadisli <csarven@status.net>
  * @author    Zach Copley <zach@status.net>
- * @copyright 2009 StatusNet, Inc.
+ * @copyright 2009-2010 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/
  */
@@ -53,9 +53,11 @@ require_once INSTALLDIR . '/lib/apioauth.php';
 
 class ApiAuthAction extends ApiAction
 {
-    var $access_token;
-    var $oauth_access_type;
-    var $oauth_source;
+    var $auth_user_nickname = null;
+    var $auth_user_password = null;
+    var $access_token       = null;
+    var $oauth_source       = null;
+    var $auth_user          = null;
 
     /**
      * Take arguments for running, and output basic auth header if needed
@@ -70,11 +72,13 @@ class ApiAuthAction extends ApiAction
     {
         parent::prepare($args);
 
-        if ($this->requiresAuth()) {
+        $this->consumer_key = $this->arg('oauth_consumer_key');
+        $this->access_token = $this->arg('oauth_token');
 
-            $this->consumer_key = $this->arg('oauth_consumer_key');
-            $this->access_token = $this->arg('oauth_token');
+        // NOTE: $this->auth_user has to get set in prepare(), not handle(),
+        // because subclasses do stuff with it in their prepares.
 
+        if ($this->requiresAuth()) {
             if (!empty($this->access_token)) {
                 $this->checkOAuthRequest();
             } else {
@@ -88,6 +92,17 @@ class ApiAuthAction extends ApiAction
             $this->checkBasicAuthUser(false);
         }
 
+        // Reject API calls with the wrong access level
+
+        if ($this->isReadOnly($args) == false) {
+            if ($this->access != self::READ_WRITE) {
+                $msg = 'API resource requires read-write access, ' .
+                       'but you only have read access.';
+                $this->clientError($msg, 401, $this->format);
+                exit;
+            }
+        }
+
         return true;
     }
 
@@ -98,8 +113,6 @@ class ApiAuthAction extends ApiAction
 
     function checkOAuthRequest()
     {
-        common_debug("We have an OAuth request.");
-
         $datastore   = new ApiStatusNetOAuthDataStore();
         $server      = new OAuthServer($datastore);
         $hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
@@ -117,9 +130,10 @@ class ApiAuthAction extends ApiAction
 
             if (empty($app)) {
 
-                // this should really not happen
-                common_log(LOG_WARN,
-                           "Couldn't find the OAuth app for consumer key: $this->consumer_key");
+                // this should probably not happen
+                common_log(LOG_WARNING,
+                           'Couldn\'t find the OAuth app for consumer key: ' .
+                           $this->consumer_key);
 
                 throw new OAuthException('No application for that consumer key.');
             }
@@ -131,20 +145,18 @@ class ApiAuthAction extends ApiAction
             $appUser = Oauth_application_user::staticGet('token',
                                                          $this->access_token);
 
-            // XXX: check that app->id and appUser->application_id and consumer all
+            // XXX: Check that app->id and appUser->application_id and consumer all
             // match?
 
             if (!empty($appUser)) {
 
-                // read or read-write
-                $this->oauth_access_type = $appUser->access_type;
-
                 // If access_type == 0 we have either a request token
                 // or a bad / revoked access token
 
-                if ($this->oauth_access_type != 0) {
+                if ($appUser->access_type != 0) {
+
+                    // Set the access level for the api call
 
-                    // Set the read or read-write access for the api call
                     $this->access = ($appUser->access_type & Oauth_application::$writeAccess)
                       ? self::READ_WRITE : self::READ_ONLY;
 
@@ -154,38 +166,34 @@ class ApiAuthAction extends ApiAction
                     }
 
                     $msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " .
-                      "application '%s' (id: %d).";
+                      "application '%s' (id: %d) with %s access.";
 
                     common_log(LOG_INFO, sprintf($msg,
                                                  $this->auth_user->nickname,
                                                  $this->auth_user->id,
                                                  $app->name,
-                                                 $app->id));
+                                                 $app->id,
+                                                 ($this->access = self::READ_WRITE) ?
+                                                 'read-write' : 'read-only'
+                                                 ));
                     return true;
                 } else {
                     throw new OAuthException('Bad access token.');
                 }
             } else {
 
-                // also should not happen
+                // Also should not happen
+
                 throw new OAuthException('No user for that token.');
-        }
+            }
 
         } catch (OAuthException $e) {
-            common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage());
-            common_debug(var_export($req, true));
-            $this->showOAuthError($e->getMessage());
-            exit();
+            common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
+            $this->showAuthError();
+            exit;
         }
     }
 
-    function showOAuthError($msg)
-    {
-        header('HTTP/1.1 401 Unauthorized');
-        header('Content-Type: text/html; charset=utf-8');
-        print $msg . "\n";
-    }
-
     /**
      * Does this API resource require authentication?
      *
@@ -210,43 +218,43 @@ class ApiAuthAction extends ApiAction
 
         $realm = common_config('site', 'name') . ' API';
 
-        if (!isset($this->auth_user) && $required) {
+        if (!isset($this->auth_user_nickname) && $required) {
             header('WWW-Authenticate: Basic realm="' . $realm . '"');
 
             // show error if the user clicks 'cancel'
 
-            $this->showBasicAuthError();
+            $this->showAuthError();
             exit;
 
-        } else if (isset($this->auth_user)) {
-            $nickname = $this->auth_user;
-            $password = $this->auth_pw;
-            $user = common_check_user($nickname, $password);
-            if (Event::handle('StartSetApiUser', array(&$user))) {
-                $this->auth_user = $user;
+        } else {
 
-                // By default, all basic auth users have read and write access
-                $this->access = self::READ_WRITE;
+            if (Event::handle('StartSetApiUser', array(&$user))) {
+                $this->auth_user = common_check_user($this->auth_user_nickname,
+                                                     $this->auth_user_password);
 
                 Event::handle('EndSetApiUser', array($user));
             }
 
+            // By default, basic auth users have rw access
+
+            $this->access = self::READ_WRITE;
+
             if (empty($this->auth_user)) {
 
                 // basic authentication failed
 
                 list($proxy, $ip) = common_client_ip();
+
                 common_log(
                     LOG_WARNING,
                     'Failed API auth attempt, nickname = ' .
                     "$nickname, proxy = $proxy, ip = $ip."
                 );
-                $this->showBasicAuthError();
+
+                $this->showAuthError();
                 exit;
             }
         }
-
-        return true;
     }
 
     /**
@@ -260,32 +268,30 @@ class ApiAuthAction extends ApiAction
     {
         if (isset($_SERVER['AUTHORIZATION'])
             || isset($_SERVER['HTTP_AUTHORIZATION'])
-        ) {
-                $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])
-                ? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['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'];
+            $this->auth_user_nickname = $_SERVER['PHP_AUTH_USER'];
+            $this->auth_user_password = $_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
+            // 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);
+            list($this->auth_user_nickname,
+                 $this->auth_user_password) = explode(':', $auth_hash);
 
-            // set all to null on a empty basic auth request
+            // Set all to null on a empty basic auth request
 
-            if ($this->auth_user == "") {
-                $this->auth_user = null;
-                $this->auth_pw = null;
+            if (empty($this->auth_user_nickname)) {
+                $this->auth_user_nickname = null;
+                $this->auth_password = null;
             }
-        } else {
-            $this->auth_user = null;
-            $this->auth_pw = null;
         }
     }
 
@@ -296,7 +302,7 @@ class ApiAuthAction extends ApiAction
      * @return void
      */
 
-    function showBasicAuthError()
+    function showAuthError()
     {
         header('HTTP/1.1 401 Unauthorized');
         $msg = 'Could not authenticate you.';
index 4f24a6d22e2e116d0bc058fe0e08fe8b9b00c79e..a6b9919b21c8193b64f9e032d04824a5f0ebf4dc 100644 (file)
@@ -56,7 +56,7 @@ $default =
               'dupelimit' => 60, # default for same person saying the same thing
               'textlimit' => 140,
               'indent' => true,
-              'use_x_sendfile' => false,
+              'use_x_sendfile' => false
               ),
         'db' =>
         array('database' => 'YOU HAVE TO SET THIS IN config.php',
@@ -81,6 +81,7 @@ $default =
               'subsystem' => 'db', # default to database, or 'stomp'
               'stomp_server' => null,
               'queue_basename' => '/queue/statusnet/',
+              'control_channel' => '/topic/statusnet-control', // broadcasts to all queue daemons
               'stomp_username' => null,
               'stomp_password' => null,
               'monitor' => null, // URL to monitor ping endpoint (work in progress)
@@ -119,6 +120,9 @@ $default =
         array('server' => null,
               'dir' => null,
               'path'=> null),
+        'javascript' =>
+        array('server' => null,
+              'path'=> null),
         'throttle' =>
         array('enabled' => false, // whether to throttle edits; false by default
               'count' => 20, // number of allowed messages in timespan
@@ -261,5 +265,8 @@ $default =
                                  'OpenID' => null),
               ),
         'admin' =>
-        array('panels' => array('design', 'site', 'user', 'paths')),
+        array('panels' => array('design', 'site', 'user', 'paths', 'access')),
+        'singleuser' =>
+        array('enabled' => false,
+              'nickname' => null),
         );
index 8e44c03a9272942d16285d99d27e426894dd0401..4955e9219954c2bf000124ae8f0f8911c512bca7 100644 (file)
@@ -327,8 +327,8 @@ class DesignSettingsAction extends AccountSettingsAction
     {
         parent::showScripts();
 
-        $this->script('js/farbtastic/farbtastic.js');
-        $this->script('js/userdesign.go.js');
+        $this->script('farbtastic/farbtastic.js');
+        $this->script('userdesign.go.js');
 
         $this->autofocus('design_background-image_file');
     }
index 31660ce954982e365be8e68e05d179bda72cadbd..317f5ea612a79b486488875120188583a10d1e01 100644 (file)
@@ -351,14 +351,40 @@ class HTMLOutputter extends XMLOutputter
     function script($src, $type='text/javascript')
     {
         if(Event::handle('StartScriptElement', array($this,&$src,&$type))) {
+
             $url = parse_url($src);
+
             if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment']))
             {
-                $src = common_path($src) . '?version=' . STATUSNET_VERSION;
+                $path = common_config('javascript', 'path');
+
+                if (empty($path)) {
+                    $path = common_config('site', 'path') . '/js/';
+                }
+
+                if ($path[strlen($path)-1] != '/') {
+                    $path .= '/';
+                }
+
+                if ($path[0] != '/') {
+                    $path = '/'.$path;
+                }
+
+                $server = common_config('javascript', 'server');
+
+                if (empty($server)) {
+                    $server = common_config('site', 'server');
+                }
+
+                // XXX: protocol
+
+                $src = 'http://'.$server.$path.$src . '?version=' . STATUSNET_VERSION;
             }
+
             $this->element('script', array('type' => $type,
                                                    'src' => $src),
                                    ' ');
+
             Event::handle('EndScriptElement', array($this,$src,$type));
         }
     }
index 94abdeefcc28b7f3e8a8cf19ef7607c154dec0f9..1f6c31ee7e3a326ceda67b127c9890b7cba4277c 100644 (file)
@@ -38,6 +38,9 @@ abstract class IoMaster
     protected $pollTimeouts = array();
     protected $lastPoll = array();
 
+    public $shutdown = false; // Did we do a graceful shutdown?
+    public $respawn = true; // Should we respawn after shutdown?
+
     /**
      * @param string $id process ID to use in logging/monitoring
      */
@@ -148,7 +151,7 @@ abstract class IoMaster
         $this->logState('init');
         $this->start();
 
-        while (true) {
+        while (!$this->shutdown) {
             $timeouts = array_values($this->pollTimeouts);
             $timeouts[] = 60; // default max timeout
 
@@ -200,22 +203,31 @@ abstract class IoMaster
             $this->logState('idle');
             $this->idle();
 
-            $memoryLimit = $this->softMemoryLimit();
-            if ($memoryLimit > 0) {
-                $usage = memory_get_usage();
-                if ($usage > $memoryLimit) {
-                    common_log(LOG_INFO, "Queue thread hit soft memory limit ($usage > $memoryLimit); gracefully restarting.");
-                    break;
-                } else if (common_config('queue', 'debug_memory')) {
-                    common_log(LOG_DEBUG, "Memory usage $usage");
-                }
-            }
+            $this->checkMemory();
         }
 
         $this->logState('shutdown');
         $this->finish();
     }
 
+    /**
+     * Check runtime memory usage, possibly triggering a graceful shutdown
+     * and thread respawn if we've crossed the soft limit.
+     */
+    protected function checkMemory()
+    {
+        $memoryLimit = $this->softMemoryLimit();
+        if ($memoryLimit > 0) {
+            $usage = memory_get_usage();
+            if ($usage > $memoryLimit) {
+                common_log(LOG_INFO, "Queue thread hit soft memory limit ($usage > $memoryLimit); gracefully restarting.");
+                $this->requestRestart();
+            } else if (common_config('queue', 'debug_memory')) {
+                common_log(LOG_DEBUG, "Memory usage $usage");
+            }
+        }
+    }
+
     /**
      * Return fully-parsed soft memory limit in bytes.
      * @return intval 0 or -1 if not set
@@ -358,5 +370,24 @@ abstract class IoMaster
         $owners[] = "thread:" . $this->id;
         $this->monitor->stats($key, $owners);
     }
+
+    /**
+     * For IoManagers to request a graceful shutdown at end of event loop.
+     */
+    public function requestShutdown()
+    {
+        $this->shutdown = true;
+        $this->respawn = false;
+    }
+
+    /**
+     * For IoManagers to request a graceful restart at end of event loop.
+     */
+    public function requestRestart()
+    {
+        $this->shutdown = true;
+        $this->respawn = true;
+    }
+
 }
 
index cdde1feca081fdc922cb032c8522158ae31bfd76..25db5baa92c59ceef572ff67f2536c78425c33c7 100644 (file)
@@ -78,9 +78,9 @@ class PersonalGroupNav extends Widget
     function show()
     {
         $user = null;
-       
+
        // FIXME: we should probably pass this in
-       
+
         $action = $this->action->trimmed('action');
         $nickname = $this->action->trimmed('nickname');
 
@@ -117,7 +117,8 @@ class PersonalGroupNav extends Widget
 
             $cur = common_current_user();
 
-            if ($cur && $cur->id == $user->id) {
+            if ($cur && $cur->id == $user->id &&
+                !common_config('singleuser', 'enabled')) {
 
                 $this->out->menuItem(common_local_url('inbox', array('nickname' =>
                                                                          $nickname)),
index 0063ed5f3955e6609e17363f7ded30db5f91dab4..274e1c2f695604853e87a4cd94d89569991db956 100644 (file)
@@ -100,6 +100,23 @@ abstract class QueueManager extends IoManager
         $this->initialize();
     }
 
+    /**
+     * Optional; ping any running queue handler daemons with a notification
+     * such as announcing a new site to handle or requesting clean shutdown.
+     * This avoids having to restart all the daemons manually to update configs
+     * and such.
+     *
+     * Called from scripts/queuectl.php controller utility.
+     *
+     * @param string $event event key
+     * @param string $param optional parameter to append to key
+     * @return boolean success
+     */
+    public function sendControlSignal($event, $param='')
+    {
+        throw new Exception(get_class($this) . " does not support control signals.");
+    }
+
     /**
      * Store an object (usually/always a Notice) into the given queue
      * for later processing. No guarantee is made on when it will be
@@ -227,7 +244,6 @@ abstract class QueueManager extends IoManager
             // XMPP output handlers...
             $this->connect('jabber', 'JabberQueueHandler');
             $this->connect('public', 'PublicQueueHandler');
-
             // @fixme this should get an actual queue
             //$this->connect('confirm', 'XmppConfirmHandler');
 
index 42bff277889165ec4a3654c10e43de1198a99291..03765b39ddae8b8050d9c961fbf70d51a8085627 100644 (file)
@@ -73,12 +73,6 @@ class Router
 
         if (Event::handle('StartInitializeRouter', array(&$m))) {
 
-            // In the "root"
-
-            $m->connect('', array('action' => 'public'));
-            $m->connect('rss', array('action' => 'publicrss'));
-            $m->connect('featuredrss', array('action' => 'featuredrss'));
-            $m->connect('favoritedrss', array('action' => 'favoritedrss'));
             $m->connect('opensearch/people', array('action' => 'opensearch',
                                                    'type' => 'people'));
             $m->connect('opensearch/notice', array('action' => 'opensearch',
@@ -145,6 +139,18 @@ class Router
                 $m->connect('settings/'.$s, array('action' => $s.'settings'));
             }
 
+            $m->connect('settings/oauthapps/show/:id',
+                array('action' => 'showapplication'),
+                array('id' => '[0-9]+')
+            );
+            $m->connect('settings/oauthapps/new',
+                array('action' => 'newapplication')
+            );
+            $m->connect('settings/oauthapps/edit/:id',
+                array('action' => 'editapplication'),
+                array('id' => '[0-9]+')
+            );
+
             // search
 
             foreach (array('group', 'people', 'notice') as $s) {
@@ -227,11 +233,6 @@ class Router
                         array('action' => 'peopletag'),
                         array('tag' => '[a-zA-Z0-9]+'));
 
-            $m->connect('featured/', array('action' => 'featured'));
-            $m->connect('featured', array('action' => 'featured'));
-            $m->connect('favorited/', array('action' => 'favorited'));
-            $m->connect('favorited', array('action' => 'favorited'));
-
             // groups
 
             $m->connect('group/new', array('action' => 'newgroup'));
@@ -622,87 +623,146 @@ class Router
             $m->connect('api/search.json', array('action' => 'twitapisearchjson'));
             $m->connect('api/trends.json', array('action' => 'twitapitrends'));
 
+            $m->connect('api/oauth/request_token',
+                        array('action' => 'apioauthrequesttoken'));
+
+            $m->connect('api/oauth/access_token',
+                        array('action' => 'apioauthaccesstoken'));
+
+            $m->connect('api/oauth/authorize',
+                        array('action' => 'apioauthauthorize'));
+
+            // Admin
+
             $m->connect('admin/site', array('action' => 'siteadminpanel'));
             $m->connect('admin/design', array('action' => 'designadminpanel'));
             $m->connect('admin/user', array('action' => 'useradminpanel'));
+           $m->connect('admin/access', array('action' => 'accessadminpanel'));
             $m->connect('admin/paths', array('action' => 'pathsadminpanel'));
 
             $m->connect('getfile/:filename',
                         array('action' => 'getfile'),
                         array('filename' => '[A-Za-z0-9._-]+'));
 
-            // user stuff
+            // In the "root"
 
-            foreach (array('subscriptions', 'subscribers',
-                           'nudge', 'all', 'foaf', 'xrds',
-                           'replies', 'inbox', 'outbox', 'microsummary') as $a) {
-                $m->connect(':nickname/'.$a,
-                            array('action' => $a),
+            if (common_config('singleuser', 'enabled')) {
+
+                $nickname = common_config('singleuser', 'nickname');
+
+                foreach (array('subscriptions', 'subscribers',
+                               'all', 'foaf', 'xrds',
+                               'replies', 'microsummary') as $a) {
+                    $m->connect($a,
+                                array('action' => $a,
+                                      'nickname' => $nickname));
+                }
+
+                foreach (array('subscriptions', 'subscribers') as $a) {
+                    $m->connect($a.'/:tag',
+                                array('action' => $a,
+                                      'nickname' => $nickname),
+                                array('tag' => '[a-zA-Z0-9]+'));
+                }
+
+                foreach (array('rss', 'groups') as $a) {
+                    $m->connect($a,
+                                array('action' => 'user'.$a,
+                                      'nickname' => $nickname));
+                }
+
+                foreach (array('all', 'replies', 'favorites') as $a) {
+                    $m->connect($a.'/rss',
+                                array('action' => $a.'rss',
+                                      'nickname' => $nickname));
+                }
+
+                $m->connect('favorites',
+                            array('action' => 'showfavorites',
+                                  'nickname' => $nickname));
+
+                $m->connect('avatar/:size',
+                            array('action' => 'avatarbynickname',
+                                  'nickname' => $nickname),
+                            array('size' => '(original|96|48|24)'));
+
+                $m->connect('tag/:tag/rss',
+                            array('action' => 'userrss',
+                                  'nickname' => $nickname),
+                            array('tag' => '[a-zA-Z0-9]+'));
+
+                $m->connect('tag/:tag',
+                            array('action' => 'showstream',
+                                  'nickname' => $nickname),
+                            array('tag' => '[a-zA-Z0-9]+'));
+
+                $m->connect('',
+                            array('action' => 'showstream',
+                                  'nickname' => $nickname));
+
+            } else {
+
+                $m->connect('', array('action' => 'public'));
+                $m->connect('rss', array('action' => 'publicrss'));
+                $m->connect('featuredrss', array('action' => 'featuredrss'));
+                $m->connect('favoritedrss', array('action' => 'favoritedrss'));
+                $m->connect('featured/', array('action' => 'featured'));
+                $m->connect('featured', array('action' => 'featured'));
+                $m->connect('favorited/', array('action' => 'favorited'));
+                $m->connect('favorited', array('action' => 'favorited'));
+
+                foreach (array('subscriptions', 'subscribers',
+                               'nudge', 'all', 'foaf', 'xrds',
+                               'replies', 'inbox', 'outbox', 'microsummary') as $a) {
+                    $m->connect(':nickname/'.$a,
+                                array('action' => $a),
+                                array('nickname' => '[a-zA-Z0-9]{1,64}'));
+                }
+
+                foreach (array('subscriptions', 'subscribers') as $a) {
+                    $m->connect(':nickname/'.$a.'/:tag',
+                                array('action' => $a),
+                                array('tag' => '[a-zA-Z0-9]+',
+                                      'nickname' => '[a-zA-Z0-9]{1,64}'));
+                }
+
+                foreach (array('rss', 'groups') as $a) {
+                    $m->connect(':nickname/'.$a,
+                                array('action' => 'user'.$a),
+                                array('nickname' => '[a-zA-Z0-9]{1,64}'));
+                }
+
+                foreach (array('all', 'replies', 'favorites') as $a) {
+                    $m->connect(':nickname/'.$a.'/rss',
+                                array('action' => $a.'rss'),
+                                array('nickname' => '[a-zA-Z0-9]{1,64}'));
+                }
+
+                $m->connect(':nickname/favorites',
+                            array('action' => 'showfavorites'),
                             array('nickname' => '[a-zA-Z0-9]{1,64}'));
-            }
-
-            $m->connect('settings/oauthapps/show/:id',
-                array('action' => 'showapplication'),
-                array('id' => '[0-9]+')
-            );
-            $m->connect('settings/oauthapps/new',
-                array('action' => 'newapplication')
-            );
-            $m->connect('settings/oauthapps/edit/:id',
-                array('action' => 'editapplication'),
-                array('id' => '[0-9]+')
-            );
-
-            $m->connect('api/oauth/request_token',
-                        array('action' => 'apioauthrequesttoken'));
-
-            $m->connect('api/oauth/access_token',
-                        array('action' => 'apioauthaccesstoken'));
-
-            $m->connect('api/oauth/authorize',
-                        array('action' => 'apioauthauthorize'));
 
-            foreach (array('subscriptions', 'subscribers') as $a) {
-                $m->connect(':nickname/'.$a.'/:tag',
-                            array('action' => $a),
-                            array('tag' => '[a-zA-Z0-9]+',
+                $m->connect(':nickname/avatar/:size',
+                            array('action' => 'avatarbynickname'),
+                            array('size' => '(original|96|48|24)',
                                   'nickname' => '[a-zA-Z0-9]{1,64}'));
-            }
 
-            foreach (array('rss', 'groups') as $a) {
-                $m->connect(':nickname/'.$a,
-                            array('action' => 'user'.$a),
-                            array('nickname' => '[a-zA-Z0-9]{1,64}'));
-            }
+                $m->connect(':nickname/tag/:tag/rss',
+                            array('action' => 'userrss'),
+                            array('nickname' => '[a-zA-Z0-9]{1,64}'),
+                            array('tag' => '[a-zA-Z0-9]+'));
+
+                $m->connect(':nickname/tag/:tag',
+                            array('action' => 'showstream'),
+                            array('nickname' => '[a-zA-Z0-9]{1,64}'),
+                            array('tag' => '[a-zA-Z0-9]+'));
 
-            foreach (array('all', 'replies', 'favorites') as $a) {
-                $m->connect(':nickname/'.$a.'/rss',
-                            array('action' => $a.'rss'),
+                $m->connect(':nickname',
+                            array('action' => 'showstream'),
                             array('nickname' => '[a-zA-Z0-9]{1,64}'));
             }
 
-            $m->connect(':nickname/favorites',
-                        array('action' => 'showfavorites'),
-                        array('nickname' => '[a-zA-Z0-9]{1,64}'));
-
-            $m->connect(':nickname/avatar/:size',
-                        array('action' => 'avatarbynickname'),
-                        array('size' => '(original|96|48|24)',
-                              'nickname' => '[a-zA-Z0-9]{1,64}'));
-
-            $m->connect(':nickname/tag/:tag/rss',
-                        array('action' => 'userrss'),
-                        array('nickname' => '[a-zA-Z0-9]{1,64}'),
-                        array('tag' => '[a-zA-Z0-9]+'));
-
-            $m->connect(':nickname/tag/:tag',
-                        array('action' => 'showstream'),
-                        array('nickname' => '[a-zA-Z0-9]{1,64}'),
-                        array('tag' => '[a-zA-Z0-9]+'));
-
-            $m->connect(':nickname',
-                        array('action' => 'showstream'),
-                        array('nickname' => '[a-zA-Z0-9]{1,64}'));
+            // user stuff
 
             Event::handle('RouterInitialized', array($m));
         }
index 8baefe88e8184db42b216f589f19e79f9207ea5f..b1961d68801c308fc25429a4cee80b028439f72d 100644 (file)
@@ -36,6 +36,11 @@ abstract class SpawningDaemon extends Daemon
 {
     protected $threads=1;
 
+    const EXIT_OK = 0;
+    const EXIT_ERR = 1;
+    const EXIT_SHUTDOWN = 100;
+    const EXIT_RESTART = 101;
+
     function __construct($id=null, $daemonize=true, $threads=1)
     {
         parent::__construct($daemonize);
@@ -49,7 +54,7 @@ abstract class SpawningDaemon extends Daemon
     /**
      * Perform some actual work!
      *
-     * @return boolean true on success, false on failure
+     * @return int exit code; use self::EXIT_SHUTDOWN to request not to respawn.
      */
     public abstract function runThread();
 
@@ -84,23 +89,30 @@ abstract class SpawningDaemon extends Daemon
         while (count($children) > 0) {
             $status = null;
             $pid = pcntl_wait($status);
-            if ($pid > 0) {
+            if ($pid > 0 && pcntl_wifexited($status)) {
+                $exitCode = pcntl_wexitstatus($status);
+
                 $i = array_search($pid, $children);
                 if ($i === false) {
-                    $this->log(LOG_ERR, "Unrecognized child pid $pid exited!");
+                    $this->log(LOG_ERR, "Unrecognized child pid $pid exited with status $exitCode");
                     continue;
                 }
                 unset($children[$i]);
-                $this->log(LOG_INFO, "Thread $i pid $pid exited.");
-                
-                $pid = pcntl_fork();
-                if ($pid < 0) {
-                    $this->log(LOG_ERROR, "Couldn't fork to respawn thread $i; aborting thread.\n");
-                } else if ($pid == 0) {
-                    $this->initAndRunChild($i);
+
+                if ($this->shouldRespawn($exitCode)) {
+                    $this->log(LOG_INFO, "Thread $i pid $pid exited with status $exitCode; respawing.");
+
+                    $pid = pcntl_fork();
+                    if ($pid < 0) {
+                        $this->log(LOG_ERROR, "Couldn't fork to respawn thread $i; aborting thread.\n");
+                    } else if ($pid == 0) {
+                        $this->initAndRunChild($i);
+                    } else {
+                        $this->log(LOG_INFO, "Respawned thread $i as pid $pid");
+                        $children[$i] = $pid;
+                    }
                 } else {
-                    $this->log(LOG_INFO, "Respawned thread $i as pid $pid");
-                    $children[$i] = $pid;
+                    $this->log(LOG_INFO, "Thread $i pid $pid exited with status $exitCode; closing out thread.");
                 }
             }
         }
@@ -108,6 +120,24 @@ abstract class SpawningDaemon extends Daemon
         return true;
     }
 
+    /**
+     * Determine whether to respawn an exited subprocess based on its exit code.
+     * Otherwise we'll respawn all exits by default.
+     *
+     * @param int $exitCode
+     * @return boolean true to respawn
+     */
+    protected function shouldRespawn($exitCode)
+    {
+        if ($exitCode == self::EXIT_SHUTDOWN) {
+            // Thread requested a clean shutdown.
+            return false;
+        } else {
+            // Otherwise we should always respawn!
+            return true;
+        }
+    }
+
     /**
      * Initialize things for a fresh thread, call runThread(), and
      * exit at completion with appropriate return value.
@@ -116,8 +146,8 @@ abstract class SpawningDaemon extends Daemon
     {
         $this->set_id($this->get_id() . "." . $thread);
         $this->resetDb();
-        $ok = $this->runThread();
-        exit($ok ? 0 : 1);
+        $exitCode = $this->runThread();
+        exit($exitCode);
     }
 
     /**
index 8f0091a1384f51b1cf07ad6c4bddf7dfbd920f03..19e8c49b5ce9c9e7a996ef3f8d948a9360b8f952 100644 (file)
@@ -38,8 +38,10 @@ class StompQueueManager extends QueueManager
     var $password = null;
     var $base = null;
     var $con = null;
+    protected $control;
     
     protected $sites = array();
+    protected $subscriptions = array();
 
     protected $useTransactions = true;
     protected $transaction = null;
@@ -52,6 +54,7 @@ class StompQueueManager extends QueueManager
         $this->username = common_config('queue', 'stomp_username');
         $this->password = common_config('queue', 'stomp_password');
         $this->base     = common_config('queue', 'queue_basename');
+        $this->control  = common_config('queue', 'control_channel');
     }
 
     /**
@@ -77,6 +80,36 @@ class StompQueueManager extends QueueManager
         $this->initialize();
     }
 
+    /**
+     * Optional; ping any running queue handler daemons with a notification
+     * such as announcing a new site to handle or requesting clean shutdown.
+     * This avoids having to restart all the daemons manually to update configs
+     * and such.
+     *
+     * Currently only relevant for multi-site queue managers such as Stomp.
+     *
+     * @param string $event event key
+     * @param string $param optional parameter to append to key
+     * @return boolean success
+     */
+    public function sendControlSignal($event, $param='')
+    {
+        $message = $event;
+        if ($param != '') {
+            $message .= ':' . $param;
+        }
+        $this->_connect();
+        $result = $this->con->send($this->control,
+                                   $message,
+                                   array ('created' => common_sql_now()));
+        if ($result) {
+            $this->_log(LOG_INFO, "Sent control ping to queue daemons: $message");
+            return true;
+        } else {
+            $this->_log(LOG_ERR, "Failed sending control ping to queue daemons: $message");
+            return false;
+        }
+    }
 
     /**
      * Instantiate the appropriate QueueHandler class for the given queue.
@@ -86,7 +119,7 @@ class StompQueueManager extends QueueManager
      */
     function getHandler($queue)
     {
-        $handlers = $this->handlers[common_config('site', 'server')];
+        $handlers = $this->handlers[$this->currentSite()];
         if (isset($handlers[$queue])) {
             $class = $handlers[$queue];
             if (class_exists($class)) {
@@ -108,7 +141,7 @@ class StompQueueManager extends QueueManager
     function getQueues()
     {
         $group = $this->activeGroup();
-        $site = common_config('site', 'server');
+        $site = $this->currentSite();
         if (empty($this->groups[$site][$group])) {
             return array();
         } else {
@@ -126,8 +159,8 @@ class StompQueueManager extends QueueManager
      */
     public function connect($transport, $class, $group='queuedaemon')
     {
-        $this->handlers[common_config('site', 'server')][$transport] = $class;
-        $this->groups[common_config('site', 'server')][$group][$transport] = $class;
+        $this->handlers[$this->currentSite()][$transport] = $class;
+        $this->groups[$this->currentSite()][$group][$transport] = $class;
     }
 
     /**
@@ -145,7 +178,8 @@ class StompQueueManager extends QueueManager
 
         $result = $this->con->send($this->queueName($queue),
                                    $msg,               // BODY of the message
-                                   array ('created' => common_sql_now()));
+                                   array ('created' => common_sql_now(),
+                                          'persistent' => 'true'));
 
         if (!$result) {
             common_log(LOG_ERR, "Error sending $rep to $queue queue");
@@ -180,7 +214,16 @@ class StompQueueManager extends QueueManager
         $ok = true;
         $frames = $this->con->readFrames();
         foreach ($frames as $frame) {
-            $ok = $ok && $this->_handleItem($frame);
+            $dest = $frame->headers['destination'];
+            if ($dest == $this->control) {
+                if (!$this->handleControlSignal($frame)) {
+                    // We got a control event that requests a shutdown;
+                    // close out and stop handling anything else!
+                    break;
+                }
+            } else {
+                $ok = $ok && $this->handleItem($frame);
+            }
         }
         return $ok;
     }
@@ -197,6 +240,9 @@ class StompQueueManager extends QueueManager
     public function start($master)
     {
         parent::start($master);
+        $this->_connect();
+
+        $this->con->subscribe($this->control);
         if ($this->sites) {
             foreach ($this->sites as $server) {
                 StatusNet::init($server);
@@ -221,6 +267,7 @@ class StompQueueManager extends QueueManager
         // If there are any outstanding delivered messages we haven't processed,
         // free them for another thread to take.
         $this->rollback();
+        $this->con->unsubscribe($this->control);
         if ($this->sites) {
             foreach ($this->sites as $server) {
                 StatusNet::init($server);
@@ -231,7 +278,16 @@ class StompQueueManager extends QueueManager
         }
         return true;
     }
-    
+
+    /**
+     * Get identifier of the currently active site configuration
+     * @return string
+     */
+    protected function currentSite()
+    {
+        return common_config('site', 'server'); // @fixme switch to nickname
+    }
+
     /**
      * Lazy open connection to Stomp queue server.
      */
@@ -255,22 +311,29 @@ class StompQueueManager extends QueueManager
      */
     protected function doSubscribe()
     {
+        $site = $this->currentSite();
         $this->_connect();
         foreach ($this->getQueues() as $queue) {
             $rawqueue = $this->queueName($queue);
+            $this->subscriptions[$site][$queue] = $rawqueue;
             $this->_log(LOG_INFO, "Subscribing to $rawqueue");
             $this->con->subscribe($rawqueue);
         }
     }
-    
+
     /**
      * Subscribe from all enabled notice queues for the current site.
      */
     protected function doUnsubscribe()
     {
+        $site = $this->currentSite();
         $this->_connect();
-        foreach ($this->getQueues() as $queue) {
-            $this->con->unsubscribe($this->queueName($queue));
+        if (!empty($this->subscriptions[$site])) {
+            foreach ($this->subscriptions[$site] as $queue => $rawqueue) {
+                $this->_log(LOG_INFO, "Unsubscribing from $rawqueue");
+                $this->con->unsubscribe($rawqueue);
+                unset($this->subscriptions[$site][$queue]);
+            }
         }
     }
 
@@ -286,10 +349,10 @@ class StompQueueManager extends QueueManager
      * @param StompFrame $frame
      * @return bool
      */
-    protected function _handleItem($frame)
+    protected function handleItem($frame)
     {
         list($site, $queue) = $this->parseDestination($frame->headers['destination']);
-        if ($site != common_config('site', 'server')) {
+        if ($site != $this->currentSite()) {
             $this->stats('switch');
             StatusNet::init($site);
         }
@@ -317,7 +380,7 @@ class StompQueueManager extends QueueManager
 
         $handler = $this->getHandler($queue);
         if (!$handler) {
-            $this->_log(LOG_ERROR, "Missing handler class; skipping $info");
+            $this->_log(LOG_ERR, "Missing handler class; skipping $info");
             $this->ack($frame);
             $this->commit();
             $this->begin();
@@ -348,6 +411,77 @@ class StompQueueManager extends QueueManager
         return true;
     }
 
+    /**
+     * Process a control signal broadcast.
+     *
+     * @param array $frame Stomp frame
+     * @return bool true to continue; false to stop further processing.
+     */
+    protected function handleControlSignal($frame)
+    {
+        $message = trim($frame->body);
+        if (strpos($message, ':') !== false) {
+            list($event, $param) = explode(':', $message, 2);
+        } else {
+            $event = $message;
+            $param = '';
+        }
+
+        $shutdown = false;
+
+        if ($event == 'shutdown') {
+            $this->master->requestShutdown();
+            $shutdown = true;
+        } else if ($event == 'restart') {
+            $this->master->requestRestart();
+            $shutdown = true;
+        } else if ($event == 'update') {
+            $this->updateSiteConfig($param);
+        } else {
+            $this->_log(LOG_ERR, "Ignoring unrecognized control message: $message");
+        }
+
+        $this->ack($frame);
+        $this->commit();
+        $this->begin();
+        return $shutdown;
+    }
+    
+    /**
+     * Set us up with queue subscriptions for a new site added at runtime,
+     * triggered by a broadcast to the 'statusnet-control' topic.
+     *
+     * @param array $frame Stomp frame
+     * @return bool true to continue; false to stop further processing.
+     */
+    protected function updateSiteConfig($nickname)
+    {
+        if (empty($this->sites)) {
+            if ($nickname == common_config('site', 'nickname')) {
+                StatusNet::init(common_config('site', 'server'));
+                $this->doUnsubscribe();
+                $this->doSubscribe();
+            } else {
+                $this->_log(LOG_INFO, "Ignoring update ping for other site $nickname");
+            }
+        } else {
+            $sn = Status_network::staticGet($nickname);
+            if ($sn) {
+                $server = $sn->getServerName(); // @fixme do config-by-nick
+                StatusNet::init($server);
+                if (empty($this->sites[$server])) {
+                    $this->addSite($server);
+                }
+                $this->_log(LOG_INFO, "(Re)subscribing to queues for site $nickname / $server");
+                $this->doUnsubscribe();
+                $this->doSubscribe();
+                $this->stats('siteupdate');
+            } else {
+                $this->_log(LOG_ERR, "Ignoring ping for unrecognized new site $nickname");
+            }
+        }
+    }
+
     /**
      * Combines the queue_basename from configuration with the
      * site server name and queue name to give eg:
@@ -360,7 +494,7 @@ class StompQueueManager extends QueueManager
     protected function queueName($queue)
     {
         return common_config('queue', 'queue_basename') .
-            common_config('site', 'server') . '/' . $queue;
+            $this->currentSite() . '/' . $queue;
     }
 
     /**
diff --git a/lib/uapplugin.php b/lib/uapplugin.php
new file mode 100644 (file)
index 0000000..ef35baf
--- /dev/null
@@ -0,0 +1,204 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * UAP (Universal Ad Package) plugin
+ *
+ * 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  Action
+ * @package   StatusNet
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 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);
+}
+
+/**
+ * Abstract superclass for advertising plugins
+ *
+ * Plugins for showing ads should derive from this plugin.
+ *
+ * Outputs the following ad types (based on UAP):
+ *
+ * Medium Rectangle 300x250
+ * Rectangle        180x150
+ * Leaderboard      728x90
+ * Wide Skyscraper  160x600
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Sarven Capadisli <csarven@status.net>
+ * @author   Evan Prodromou <evan@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/
+ */
+
+abstract class UAPPlugin extends Plugin
+{
+    public $mediumRectangle = null;
+    public $rectangle       = null;
+    public $leaderboard     = null;
+    public $wideSkyscraper  = null;
+
+    /**
+     * Output our dedicated stylesheet
+     *
+     * @param Action $action Action being shown
+     *
+     * @return boolean hook flag
+     */
+
+    function onEndShowStatusNetStyles($action)
+    {
+        // XXX: allow override by theme
+        $action->cssLink('css/uap.css', 'base', 'screen, projection, tv');
+        return true;
+    }
+
+    /**
+     * Add a medium rectangle ad at the beginning of sidebar
+     *
+     * @param Action $action Action being shown
+     *
+     * @return boolean hook flag
+     */
+
+    function onStartShowAside($action)
+    {
+        if (!is_null($this->mediumRectangle)) {
+
+            $action->elementStart('div',
+                                  array('id' => 'ad_medium-rectangle',
+                                        'class' => 'ad'));
+
+            $this->showMediumRectangle($action);
+
+            $action->elementEnd('div');
+        }
+
+        return true;
+    }
+
+    /**
+     * Add a leaderboard in the header
+     *
+     * @param Action $action Action being shown
+     *
+     * @return boolean hook flag
+     */
+
+    function onEndShowHeader($action)
+    {
+        if (!is_null($this->leaderboard)) {
+            $action->elementStart('div',
+                                  array('id' => 'ad_leaderboard',
+                                        'class' => 'ad'));
+            $this->showLeaderboard($action);
+            $action->elementEnd('div');
+        }
+
+        return true;
+    }
+
+    /**
+     * Add a rectangle before aside sections
+     *
+     * @param Action $action Action being shown
+     *
+     * @return boolean hook flag
+     */
+
+    function onStartShowSections($action)
+    {
+        if (!is_null($this->rectangle)) {
+            $action->elementStart('div',
+                                  array('id' => 'ad_rectangle',
+                                        'class' => 'ad'));
+            $this->showRectangle($action);
+            $action->elementEnd('div');
+        }
+
+        return true;
+    }
+
+    /**
+     * Add a wide skyscraper after the aside
+     *
+     * @param Action $action Action being shown
+     *
+     * @return boolean hook flag
+     */
+
+    function onEndShowAside($action)
+    {
+        if (!is_null($this->wideSkyscraper)) {
+            $action->elementStart('div',
+                                  array('id' => 'ad_wide-skyscraper',
+                                        'class' => 'ad'));
+
+            $this->showWideSkyscraper($action);
+
+            $action->elementEnd('div');
+        }
+        return true;
+    }
+
+    /**
+     * Show a medium rectangle ad
+     *
+     * @param Action $action Action being shown
+     *
+     * @return void
+     */
+
+    abstract protected function showMediumRectangle($action);
+
+    /**
+     * Show a rectangle ad
+     *
+     * @param Action $action Action being shown
+     *
+     * @return void
+     */
+
+    abstract protected function showRectangle($action);
+
+    /**
+     * Show a wide skyscraper ad
+     *
+     * @param Action $action Action being shown
+     *
+     * @return void
+     */
+
+    abstract protected function showWideSkyscraper($action);
+
+    /**
+     * Show a leaderboard ad
+     *
+     * @param Action $action Action being shown
+     *
+     * @return void
+     */
+
+    abstract protected function showLeaderboard($action);
+}
diff --git a/plugins/BlankAd/BlankAdPlugin.php b/plugins/BlankAd/BlankAdPlugin.php
new file mode 100644 (file)
index 0000000..0e2719a
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin for testing ad layout
+ *
+ * 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  Ads
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 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')) {
+    exit(1);
+}
+
+/**
+ * Plugin for testing ad layout
+ *
+ * This plugin uses the UAPPlugin framework to output ad content. However,
+ * its ad content is just images with one red pixel stretched to the
+ * right size. It's mostly useful for debugging theme layout.
+ *
+ * To use this plugin, set the parameter for the ad size you want to use
+ * to true (or anything non-null). For example, to make a leaderboard:
+ *
+ *     addPlugin('BlankAd', array('leaderboard' => true));
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@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/
+ *
+ * @seeAlso  Location
+ */
+
+class BlankAdPlugin extends UAPPlugin
+{
+    /**
+     * Show a medium rectangle 'ad'
+     *
+     * @param Action $action Action being shown
+     *
+     * @return void
+     */
+
+    protected function showMediumRectangle($action)
+    {
+        $action->element('img',
+                         array('width' => 300,
+                               'height' => 250,
+                               'src' => common_path('plugins/BlankAd/redpixel.png')),
+                         '');
+    }
+
+    /**
+     * Show a rectangle 'ad'
+     *
+     * @param Action $action Action being shown
+     *
+     * @return void
+     */
+
+    protected function showRectangle($action)
+    {
+        $action->element('img',
+                         array('width' => 180,
+                               'height' => 150,
+                               'src' => common_path('plugins/BlankAd/redpixel.png')),
+                         '');
+    }
+
+    /**
+     * Show a wide skyscraper ad
+     *
+     * @param Action $action Action being shown
+     *
+     * @return void
+     */
+
+    protected function showWideSkyscraper($action)
+    {
+        $action->element('img',
+                         array('width' => 160,
+                               'height' => 600,
+                               'src' => common_path('plugins/BlankAd/redpixel.png')),
+                         '');
+    }
+
+    /**
+     * Show a leaderboard ad
+     *
+     * @param Action $action Action being shown
+     *
+     * @return void
+     */
+
+    protected function showLeaderboard($action)
+    {
+        $action->element('img',
+                         array('width' => 728,
+                               'height' => 90,
+                               'src' => common_path('plugins/BlankAd/redpixel.png')),
+                         '');
+    }
+}
\ No newline at end of file
diff --git a/plugins/BlankAd/redpixel.png b/plugins/BlankAd/redpixel.png
new file mode 100644 (file)
index 0000000..26299a5
Binary files /dev/null and b/plugins/BlankAd/redpixel.png differ
index 815fee094ca69510036994c9a628583cc0eb799d..389e1ea81fd0bf437e535ebc3e22057e7adbdb9c 100644 (file)
@@ -89,7 +89,7 @@ class FacebookAction extends Action
 
     function showScripts()
     {
-        $this->script('js/facebookapp.js');
+        $this->script('facebookapp.js');
     }
 
     /**
index ce6086df9443933b6c51e8075f243809afbaedb3..a880dc8666f5bc2b2f912917bcd9d66d90f72b2e 100644 (file)
@@ -79,6 +79,21 @@ class PubSubHubBubPlugin extends Plugin
         parent::__construct();
     }
 
+    /**
+     * Check if plugin should be active; may be mass-enabled.
+     * @return boolean
+     */
+
+    function enabled()
+    {
+        if (common_config('site', 'private')) {
+            // PuSH relies on public feeds
+            return false;
+        }
+        // @fixme check for being on a private network?
+        return true;
+    }
+
     /**
      * Hooks the StartApiAtom event
      *
@@ -92,8 +107,9 @@ class PubSubHubBubPlugin extends Plugin
 
     function onStartApiAtom($action)
     {
-        $action->element('link', array('rel' => 'hub', 'href' => $this->hub), null);
-
+        if ($this->enabled()) {
+            $action->element('link', array('rel' => 'hub', 'href' => $this->hub), null);
+        }
         return true;
     }
 
@@ -110,9 +126,11 @@ class PubSubHubBubPlugin extends Plugin
 
     function onStartApiRss($action)
     {
-        $action->element('atom:link', array('rel' => 'hub',
-                                            'href' => $this->hub),
-                         null);
+        if ($this->enabled()) {
+            $action->element('atom:link', array('rel' => 'hub',
+                                                'href' => $this->hub),
+                             null);
+        }
         return true;
     }
 
@@ -130,6 +148,9 @@ class PubSubHubBubPlugin extends Plugin
 
     function onHandleQueuedNotice($notice)
     {
+        if (!$this->enabled()) {
+            return false;
+        }
         $publisher = new Publisher($this->hub);
 
         $feeds = array();
@@ -243,16 +264,21 @@ class PubSubHubBubPlugin extends Plugin
 
     function onPluginVersion(&$versions)
     {
+        $about = _m('The PubSubHubBub plugin pushes RSS/Atom updates '.
+                    'to a <a href = "'.
+                    'http://pubsubhubbub.googlecode.com/'.
+                    '">PubSubHubBub</a> hub.');
+        if (!$this->enabled()) {
+            $about = '<span class="disabled" style="color:gray">' . $about . '</span> ' .
+                     _m('(inactive on private site)');
+        }
         $versions[] = array('name' => 'PubSubHubBub',
                             'version' => STATUSNET_VERSION,
                             'author' => 'Craig Andrews',
                             'homepage' =>
                             'http://status.net/wiki/Plugin:PubSubHubBub',
                             'rawdescription' =>
-                            _m('The PubSubHubBub plugin pushes RSS/Atom updates '.
-                               'to a <a href = "'.
-                               'http://pubsubhubbub.googlecode.com/'.
-                               '">PubSubHubBub</a> hub.'));
+                            $about);
 
         return true;
     }
index 89640f5beb25c7e41db6c4a3dceeb2569fa326cb..16e28e94d3eed57718c63e9efdd6de916ec0845f 100644 (file)
@@ -87,7 +87,7 @@ class RealtimePlugin extends Plugin
         $scripts = $this->_getScripts();
 
         foreach ($scripts as $script) {
-            $action->script($script);
+            $action->script(common_path($script));
         }
 
         $user = common_current_user();
index 8b62a3a96783d7a7442326f33007d4f859e8d4f3..4d207c261b5a7da9f52799107dd04805e0240520 100755 (executable)
@@ -45,10 +45,12 @@ function read_input_line($prompt)
     if (CONSOLE_INTERACTIVE) {
         if (CONSOLE_READLINE) {
             $line = readline($prompt);
-            readline_add_history($line);
-            if (defined('CONSOLE_HISTORY')) {
-                // Save often; it's easy to hit fatal errors.
-                readline_write_history(CONSOLE_HISTORY);
+            if (trim($line) != '') {
+                readline_add_history($line);
+                if (defined('CONSOLE_HISTORY')) {
+                    // Save often; it's easy to hit fatal errors.
+                    readline_write_history(CONSOLE_HISTORY);
+                }
             }
             return $line;
         } else {
diff --git a/scripts/queuectl.php b/scripts/queuectl.php
new file mode 100755 (executable)
index 0000000..1c9ea33
--- /dev/null
@@ -0,0 +1,85 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * 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/>.
+ */
+
+/**
+ * Sends control signals to running queue daemons.
+ *
+ * @author Brion Vibber <brion@status.net>
+ * @package QueueHandler
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = 'ur';
+$longoptions = array('update', 'restart', 'stop');
+
+$helptext = <<<END_OF_QUEUECTL_HELP
+Send broadcast events to control any running queue handlers.
+(Currently for Stomp queues only.)
+
+Events relating to current site (as selected with -s etc)
+    -u --update       Announce new site or updated configuration. Running
+                      daemons will start subscribing to any new queues needed
+                      for this site.
+
+Global events:
+    -r --restart      Graceful restart of all threads
+       --stop         Graceful shutdown of all threads
+
+END_OF_QUEUECTL_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+function doSendControl($message, $event, $param='')
+{
+    print $message;
+    $qm = QueueManager::get();
+    if ($qm->sendControlSignal($event, $param)) {
+        print " sent.\n";
+    } else {
+        print " FAILED.\n";
+    }
+}
+
+$actions = 0;
+
+if (have_option('u') || have_option('--update')) {
+    $nickname = common_config('site', 'nickname');
+    doSendControl("Sending site update signal to queue daemons for $nickname",
+                  "update", $nickname);
+    $actions++;
+}
+
+if (have_option('r') || have_option('--restart')) {
+    doSendControl("Sending graceful restart signal to queue daemons...",
+                  "restart");
+    $actions++;
+}
+
+if (have_option('--stop')) {
+    doSendControl("Sending graceful shutdown signal to queue daemons...",
+                  "shutdown");
+    $actions++;
+}
+
+if (!$actions) {
+    show_help();
+}
+
index a9cfda6d72e8f927ee513fe8572a26ccde00c96c..c2e2351c3910e307331303c0509e30692f89b012 100755 (executable)
@@ -21,7 +21,7 @@
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
 $shortoptions = 'fi:at:';
-$longoptions = array('id=', 'foreground', 'all', 'threads=', 'skip-xmpp', 'xmpp-only');
+$longoptions = array('id=', 'foreground', 'all', 'threads=');
 
 /**
  * Attempts to get a count of the processors available on the current system
@@ -115,7 +115,7 @@ class QueueDaemon extends SpawningDaemon
 
         $this->log(LOG_INFO, 'terminating normally');
 
-        return true;
+        return $master->respawn ? self::EXIT_RESTART : self::EXIT_SHUTDOWN;
     }
 }
 
@@ -163,13 +163,6 @@ if (!$threads) {
 $daemonize = !(have_option('f') || have_option('--foreground'));
 $all = have_option('a') || have_option('--all');
 
-if (have_option('--skip-xmpp')) {
-    define('XMPP_EMERGENCY_FLAG', true);
-}
-if (have_option('--xmpp-only')) {
-    define('XMPP_ONLY_FLAG', true);
-}
-
 $daemon = new QueueDaemon($id, $daemonize, $threads, $all);
 $daemon->runOnce();
 
index fd7cf055b485cedaae3f327952c16baf4f634456..46dd9b90cc636be75028f7ddbfac0ff563143879 100755 (executable)
@@ -56,7 +56,7 @@ class XMPPDaemon extends SpawningDaemon
 
         common_log(LOG_INFO, 'terminating normally');
 
-        return true;
+        return $master->respawn ? self::EXIT_RESTART : self::EXIT_SHUTDOWN;
     }
 
 }
diff --git a/tests/oauth/statusupdate.php b/tests/oauth/statusupdate.php
new file mode 100644 (file)
index 0000000..4aa230e
--- /dev/null
@@ -0,0 +1,115 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Affero General Public License as published by
+ *  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/>.
+ **/
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
+
+require_once INSTALLDIR . '/extlib/OAuth.php';
+
+$shortoptions = 'o:s:u:';
+$longoptions = array('oauth_token=', 'token_secret=', 'update=');
+
+$helptext = <<<END_OF_VERIFY_HELP
+    statusupdate.php [options]
+    Update your status using OAuth
+
+    -o --oauth_token       access token
+    -s --token_secret      access token secret
+    -u --update            status update
+
+
+END_OF_VERIFY_HELP;
+
+$token        = null;
+$token_secret = null;
+$update       = null;
+
+require_once INSTALLDIR . '/scripts/commandline.inc';
+
+if (have_option('o', 'oauth_token')) {
+    $token = get_option_value('oauth_token');
+}
+
+if (have_option('s', 'token_secret')) {
+    $token_secret = get_option_value('s', 'token_secret');
+}
+
+if (have_option('u', 'update')) {
+    $update = get_option_value('u', 'update');
+}
+
+if (empty($token)) {
+    print "Please specify an access token.\n";
+    exit(1);
+}
+
+if (empty($token_secret)) {
+    print "Please specify an access token secret.\n";
+    exit(1);
+}
+
+if (empty($update)) {
+    print "You forgot to update your status!\n";
+    exit(1);
+}
+
+$ini = parse_ini_file("oauth.ini");
+
+$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
+
+$endpoint = $ini['apiroot'] . '/statuses/update.xml';
+
+print "$endpoint\n";
+
+$at = new OAuthToken($token, $token_secret);
+
+$parsed = parse_url($endpoint);
+$params = array();
+parse_str($parsed['query'], $params);
+
+$params['status'] = $update;
+
+$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+
+$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $at, 'POST', $endpoint, $params);
+$req_req->sign_request($hmac_method, $test_consumer, $at);
+
+$r = httpRequest($req_req->to_url());
+
+$body = $r->getBody();
+
+print "$body\n";
+
+//print $req_req->to_url() . "\n\n";
+
+function httpRequest($url)
+{
+    $request = HTTPClient::start();
+
+    $request->setConfig(array(
+                              'follow_redirects' => true,
+                              'connect_timeout' => 120,
+                              'timeout' => 120,
+                              'ssl_verify_peer' => false,
+                              'ssl_verify_host' => false
+                              ));
+
+    return $request->post($url);
+}
+
diff --git a/theme/base/css/uap.css b/theme/base/css/uap.css
new file mode 100644 (file)
index 0000000..73be5f0
--- /dev/null
@@ -0,0 +1,54 @@
+/** Universal Ad Package styles:
+ * Medium Rectangle 300x250
+ * Rectangle        180x150
+ * Leaderboard      728x90
+ * Wide Skyscraper  160x600
+ *
+ * @package   StatusNet
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @copyright 2010 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/
+ */
+
+
+.ad {
+border:1px solid #CCC;
+float:left;
+}
+
+#ad_medium-rectangle {
+width:300px;
+height:250px;
+
+margin-left:1.35%;
+margin-bottom:18px;
+}
+
+#ad_rectangle {
+width:180px;
+height:150px;
+
+float:none;
+clear:both;
+margin:0 auto;
+margin-bottom:29px;
+}
+
+#ad_leaderboard {
+width:728px;
+height:90px;
+
+margin:0 auto 18px;
+float:none;
+clear:both;
+}
+
+#ad_wide-skyscraper {
+width:160px;
+height:600px;
+
+float:right;
+margin-top:18px;
+margin-right:8.25%;
+}