]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.9.x' into 1.0.x
authorBrion Vibber <brion@pobox.com>
Thu, 7 Oct 2010 20:32:26 +0000 (13:32 -0700)
committerBrion Vibber <brion@pobox.com>
Thu, 7 Oct 2010 20:32:26 +0000 (13:32 -0700)
13 files changed:
lib/util.php
plugins/BitlyUrl/BitlyUrlPlugin.php
plugins/BitlyUrl/README [new file with mode: 0644]
plugins/BitlyUrl/bitlyadminpanelaction.php [new file with mode: 0644]
plugins/OStatus/OStatusPlugin.php
plugins/OStatus/scripts/fixup-shadow.php
theme/clean/css/display.css
theme/clean/css/ie.css [new file with mode: 0644]
theme/rebase/css/display.css
theme/rebase/css/ie.css
theme/shiny/css/display.css
theme/shiny/css/ie.css
theme/shiny/images/wrap_bg.png [new file with mode: 0644]

index df339d4b1e39e6058c84ea3d31becf2e1bb05210..85d7c72f453fb59b58ecd671bb975409b17b081d 100644 (file)
@@ -946,7 +946,12 @@ function common_shorten_links($text, $always = false)
 function common_validate_utf8($str)
 {
     // preg_replace will return NULL on invalid UTF-8 input.
-    return preg_replace('//u', '', $str);
+    //
+    // Note: empty regex //u also caused NULL return on some
+    // production machines, but none of our test machines.
+    //
+    // This should be replaced with a more reliable check.
+    return preg_replace('/\x00/u', '', $str);
 }
 
 /**
index e1c8d3462eb9b40d69541bf87445b0a27e4262cf..f4d987489ab0a6509a3ec1b2f1b83f692fa2bd29 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * StatusNet, the distributed open-source microblogging tool
  *
- * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ * Plugin to use bit.ly URL shortening services.
  *
  * PHP version 5
  *
@@ -22,7 +22,9 @@
  * @category  Plugin
  * @package   StatusNet
  * @author    Craig Andrews <candrews@integralblue.com>
+ * @author    Brion Vibber <brion@status.net>
  * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @copyright 2010 StatusNet, Inc http://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/
  */
@@ -33,26 +35,135 @@ if (!defined('STATUSNET')) {
 
 class BitlyUrlPlugin extends UrlShortenerPlugin
 {
-    public $serviceUrl;
+    public $shortenerName = 'bit.ly';
+    public $serviceUrl = 'http://bit.ly/api?method=shorten&version=2.0.1&longUrl=%s';
+    public $login; // To set a site-default when admins or users don't override it.
+    public $apiKey;
 
     function onInitializePlugin(){
         parent::onInitializePlugin();
         if(!isset($this->serviceUrl)){
-            throw new Exception(_m("You must specify a serviceUrl."));
+            throw new Exception(_m("You must specify a serviceUrl for bit.ly shortening."));
         }
     }
 
+    /**
+     * Add bit.ly to the list of available URL shorteners if it's configured,
+     * otherwise leave it out.
+     *
+     * @param array $shorteners
+     * @return boolean hook return value
+     */
+    function onGetUrlShorteners(&$shorteners)
+    {
+        if ($this->getLogin() && $this->getApiKey()) {
+            return parent::onGetUrlShorteners($shorteners);
+        }
+        return true;
+    }
+
+    /**
+     * Short a URL
+     * @param url
+     * @return string shortened version of the url, or null if URL shortening failed
+     */
     protected function shorten($url) {
-        $response = $this->http_get($url);
-        if(!$response) return;
-        return current(json_decode($response)->results)->hashUrl;
+        $response = $this->query($url);
+        if ($this->isOk($url, $response)) {
+            return $this->decode($url, $response->getBody());
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Get the user's or site-wide default bit.ly login name.
+     *
+     * @return string
+     */
+    protected function getLogin()
+    {
+        $login = common_config('bitly', 'default_login');
+        if (!$login) {
+            $login = $this->login;
+        }
+        return $login;
+    }
+
+    /**
+     * Get the user's or site-wide default bit.ly API key.
+     *
+     * @return string
+     */
+    protected function getApiKey()
+    {
+        $key = common_config('bitly', 'default_apikey');
+        if (!$key) {
+            $key = $this->apiKey;
+        }
+        return $key;
+    }
+
+    /**
+     * Inject API key into query before sending out...
+     *
+     * @param string $url
+     * @return HTTPResponse
+     */
+    protected function query($url)
+    {
+        // http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/shorten
+        $params = http_build_query(array(
+            'login' => $this->getLogin(),
+            'apiKey' => $this->getApiKey()), '', '&');
+        $serviceUrl = sprintf($this->serviceUrl, $url) . '&' . $params;
+
+        $request = HTTPClient::start();
+        return $request->get($serviceUrl);
+    }
+
+    /**
+     * JSON decode for API result
+     */
+    protected function decode($url, $body)
+    {
+        $json = json_decode($body, true);
+        return $json['results'][$url]['shortUrl'];
+    }
+
+    /**
+     * JSON decode for API result
+     */
+    protected function isOk($url, $response)
+    {
+        $code = 'unknown';
+        $msg = '';
+        if ($response->isOk()) {
+            $body = $response->getBody();
+            common_log(LOG_INFO, $body);
+            $json = json_decode($body, true);
+            if ($json['statusCode'] == 'OK') {
+                $data = $json['results'][$url];
+                if (isset($data['shortUrl'])) {
+                    return true;
+                } else if (isset($data['statusCode']) && $data['statusCode'] == 'ERROR') {
+                    $code = $data['errorCode'];
+                    $msg = $data['errorMessage'];
+                }
+            } else if ($json['statusCode'] == 'ERROR') {
+                $code = $json['errorCode'];
+                $msg = $json['errorMessage'];
+            }
+            common_log(LOG_ERR, "bit.ly returned error $code $msg for $url");
+        }
+        return false;
     }
 
     function onPluginVersion(&$versions)
     {
         $versions[] = array('name' => sprintf('BitlyUrl (%s)', $this->shortenerName),
                             'version' => STATUSNET_VERSION,
-                            'author' => 'Craig Andrews',
+                            'author' => 'Craig Andrews, Brion Vibber',
                             'homepage' => 'http://status.net/wiki/Plugin:BitlyUrl',
                             'rawdescription' =>
                             sprintf(_m('Uses <a href="http://%1$s/">%1$s</a> URL-shortener service.'),
@@ -60,4 +171,85 @@ class BitlyUrlPlugin extends UrlShortenerPlugin
 
         return true;
     }
+
+    /**
+     * Hook for RouterInitialized event.
+     *
+     * @param Net_URL_Mapper $m path-to-action mapper
+     * @return boolean hook return
+     */
+    function onRouterInitialized($m)
+    {
+        $m->connect('admin/bitly',
+                    array('action' => 'bitlyadminpanel'));
+        return true;
+    }
+
+    /**
+     * If the plugin's installed, this should be accessible to admins.
+     */
+    function onAdminPanelCheck($name, &$isOK)
+    {
+        if ($name == 'bitly') {
+            $isOK = true;
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Add the bit.ly admin panel to the list...
+     */
+    function onEndAdminPanelNav($nav)
+    {
+        if (AdminPanelAction::canAdmin('bitly')) {
+            $action_name = $nav->action->trimmed('action');
+
+            $nav->out->menuItem(common_local_url('bitlyadminpanel'),
+                                _m('bit.ly'),
+                                _m('bit.ly URL shortening'),
+                                $action_name == 'bitlyadminpanel',
+                                'nav_bitly_admin_panel');
+        }
+
+        return true;
+    }
+
+    /**
+     * Automatically load the actions and libraries used by the plugin
+     *
+     * @param Class $cls the class
+     *
+     * @return boolean hook return
+     *
+     */
+    function onAutoload($cls)
+    {
+        $base = dirname(__FILE__);
+        $lower = strtolower($cls);
+        switch ($lower) {
+        case 'bitlyadminpanelaction':
+            require_once "$base/$lower.php";
+            return false;
+        default:
+            return true;
+        }
+    }
+
+    /**
+     * Internal hook point to check the default global credentials so
+     * the admin form knows if we have a fallback or not.
+     *
+     * @param string $login
+     * @param string $apiKey
+     * @return boolean hook return value
+     */
+    function onBitlyDefaultCredentials(&$login, &$apiKey)
+    {
+        $login = $this->login;
+        $apiKey = $this->apiKey;
+        return false;
+    }
+
 }
diff --git a/plugins/BitlyUrl/README b/plugins/BitlyUrl/README
new file mode 100644 (file)
index 0000000..0b3af1d
--- /dev/null
@@ -0,0 +1,37 @@
+bit.ly URL shortening requires the login name and API key for a bit.ly account.
+Register for an account or set up your API key here:
+
+  http://bit.ly/a/your_api_key
+
+Administrators can configure a login and API key to use through the admin panels
+on the site; these credentials will then be used for all users.
+
+(In the future, options will be added for individual users to override the keys
+with their own login for URLs they post.)
+
+If the login and API key are left empty in the admin panel, then bit.ly will be
+disabled and hidden from the list of available URL shorteners unless a global
+default was provided in the plugin configuration.
+
+
+To enable bit.ly with no default credentials, simply slip into your config.php:
+
+  addPlugin('BitlyUrl');
+
+To provide default credentials, add them as parameters:
+
+  addPlugin('BitlyUrl', array(
+      'login' => 'myname',
+      'apiKey' => '############################'
+  ));
+
+These settings will not be individually exposed to the admin panels, but the
+panel will indicate whether or not the global default settings are available;
+this makes it suitable as a global default for multi-site hosting, where admins
+on individual sites can change to use their own settings.
+
+
+If you're using a bit.ly pro account with a custom domain etc, it should all
+"just work" as long as you use the correct login name and API key for your
+account.
+
diff --git a/plugins/BitlyUrl/bitlyadminpanelaction.php b/plugins/BitlyUrl/bitlyadminpanelaction.php
new file mode 100644 (file)
index 0000000..05b8e83
--- /dev/null
@@ -0,0 +1,238 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Admin panel for plugin to use bit.ly URL shortening services.
+ *
+ * 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    Brion Vibber <brion@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 global bit.ly URL shortener settings
+ *
+ * @category Admin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@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 BitlyadminpanelAction extends AdminPanelAction
+{
+    /**
+     * Returns the page title
+     *
+     * @return string page title
+     */
+
+    function title()
+    {
+        return _m('bit.ly URL shortening');
+    }
+
+    /**
+     * Instructions for using this form.
+     *
+     * @return string instructions
+     */
+
+    function getInstructions()
+    {
+        return _m('URL shortening with bit.ly requires ' .
+                  '[a bit.ly account and API key](http://bit.ly/a/your_api_key). ' .
+                  'This verifies that this is an authorized account, and ' .
+                  'allow you to use bit.ly\'s tracking features and custom domains.');
+    }
+
+    /**
+     * Show the bit.ly admin panel form
+     *
+     * @return void
+     */
+
+    function showForm()
+    {
+        $form = new BitlyAdminPanelForm($this);
+        $form->show();
+        return;
+    }
+
+    /**
+     * Save settings from the form
+     *
+     * @return void
+     */
+
+    function saveSettings()
+    {
+        static $settings = array(
+            'bitly' => array('default_login', 'default_apikey')
+        );
+
+        $values = array();
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting]
+                    = $this->trimmed($setting);
+            }
+        }
+
+        // This throws an exception on validation errors
+
+        $this->validate($values);
+
+        // assert(all values are valid);
+
+        $config = new Config();
+
+        $config->query('BEGIN');
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        $config->query('COMMIT');
+
+        return;
+    }
+
+    function validate(&$values)
+    {
+        // Validate consumer key and secret (can't be too long)
+
+        if (mb_strlen($values['bitly']['default_apikey']) > 255) {
+            $this->clientError(
+                _m("Invalid login. Max length is 255 characters.")
+            );
+        }
+
+        if (mb_strlen($values['bitly']['default_apikey']) > 255) {
+            $this->clientError(
+                _m("Invalid API key. Max length is 255 characters.")
+            );
+        }
+    }
+}
+
+class BitlyAdminPanelForm extends AdminForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        return 'bitlyadminpanel';
+    }
+
+    /**
+     * 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('bitlyadminpanel');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->out->elementStart(
+            'fieldset',
+            array('id' => 'settings_bitly')
+        );
+        $this->out->element('legend', null, _m('Credentials'));
+
+        // Do we have global defaults to fall back on?
+        $login = $apiKey = false;
+        Event::handle('BitlyDefaultCredentials', array(&$login, &$apiKey));
+        $haveGlobalDefaults = ($login && $apiKey);
+        if ($login && $apiKey) {
+            $this->out->element('p', 'form_guide',
+                _m('Leave these empty to use global default credentials.'));
+        } else {
+            $this->out->element('p', 'form_guide',
+                _m('If you leave these empty, bit.ly will be unavailable to users.'));
+        }
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->input(
+            'default_login',
+            _m('Login name'),
+            null,
+            'bitly'
+        );
+        $this->unli();
+
+        $this->li();
+        $this->input(
+            'default_apikey',
+             _m('API key'),
+            null,
+            'bitly'
+        );
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        $this->out->submit('submit', _('Save'), 'submit', null, _m('Save bit.ly settings'));
+    }
+}
index f9e7adacddc6dde844673fed7406429c11839c51..f7900110f3298d0ba4b6fdfc4e3a205cdb6c9248 100644 (file)
@@ -964,7 +964,7 @@ class OStatusPlugin extends Plugin
     {
         $group = User_group::staticGet('uri', $url);
         if ($group) {
-            $local = Local_group::staticGet('id', $group->id);
+            $local = Local_group::staticGet('group_id', $group->id);
             if ($local) {
                 return $group->id;
             }
index 3e2c18e02fb314780a4c382136de4fd90ee7fa0e..18df2995fb56beede2cb53a5d7a4f06fa1b503d7 100644 (file)
@@ -84,6 +84,50 @@ while ($group->fetch()) {
 }
 echo "\n";
 
+// And there may be user_group entries remaining where we've already killed
+// the ostatus_profile. These were "harmless" until our lookup started actually
+// using the uri field, at which point we can clearly see it breaks stuff.
+echo "Checking for leftover bogus user_group.uri entries obscuring local_group entries...\n";
+
+$group = new User_group();
+$group->joinAdd(array('id', 'local_group:group_id'), 'LEFT');
+$group->whereAdd('group_id IS NULL');
+
+
+$marker = mt_rand(31337, 31337000);
+$groupTemplate = common_local_url('groupbyid', array('id' => $marker));
+$encGroup = $group->escape($groupTemplate, true);
+$encGroup = str_replace($marker, '%', $encGroup);
+echo "  LIKE '$encGroup'\n";
+$group->whereAdd("uri LIKE '$encGroup'");
+
+$group->find();
+$count = $group->N;
+echo "Found $count...\n";
+
+while ($group->fetch()) {
+    $uri = $group->uri;
+    if (preg_match('!/group/(\d+)/id!', $uri, $matches)) {
+        $id = intval($matches[1]);
+        $local = Local_group::staticGet('group_id', $id);
+        if ($local) {
+            $nick = $local->nickname;
+        } else {
+            $nick = '<deleted>';
+        }
+        echo "local group $id ($local->nickname) hidden by $uri (bogus group id $group->id)";
+        if ($dry) {
+            echo " - skipping\n";
+        } else {
+            echo " - removing bogus user_group entry...";
+            $evil = User_group::staticGet('id', $group->id);
+            $evil->delete();
+            echo "  ok\n";
+        }
+    }
+}
+echo "\n";
+
 
 // Fallback?
 echo "Checking for bogus profiles blocking local users/groups by URI pattern match...\n";
index be72993b559b06c300969e983916c00cfd0e7591..260ee5926a0d7487c45aa84879909d74b25eebf9 100644 (file)
@@ -117,7 +117,8 @@ address {
     top: 74px;
     right:0;
     height: 2.4em;
-    width: 106px;
+    width: 96px;
+    right: 10px;
 }
 
 #core {
diff --git a/theme/clean/css/ie.css b/theme/clean/css/ie.css
new file mode 100644 (file)
index 0000000..ede8f07
--- /dev/null
@@ -0,0 +1,62 @@
+/* IE specific styles */
+
+/* base theme overrides */
+
+input.checkbox,
+input.radio {
+top:0;
+}
+.form_notice textarea {
+    width: 364px;
+}
+.form_notice .form_note + label {
+position:absolute;
+top:25px;
+left:83%;
+text-indent:-9999px;
+height:16px;
+width:16px;
+display:block;
+        top: 31px;
+        right: 88px;
+}
+.form_notice #notice_action-submit {
+    width: 96px;
+    max-width: 96px;
+}
+.form_notice #notice_data-attach_selected,
+.form_notice #notice_data-geo_selected {
+width:78.75%;
+}
+.form_notice #notice_data-attach_selected button,
+.form_notice #notice_data-geo_selected button {
+padding:0 4px;
+}
+.notice-options input.submit {
+font-size:0;
+text-align:right;
+text-indent:0;
+}
+.notice div.entry-content .timestamp a {
+margin-right:4px;
+}
+.entity_profile {
+width:64%;
+}
+.notice {
+z-index:1;
+}
+.notice:hover {
+z-index:9999;
+}
+.notice .thumbnail img {
+z-index:9999;
+}
+
+.form_settings fieldset fieldset legend {
+line-height:auto;
+}
+
+.form_notice #notice_data-attach {
+filter: alpha(opacity=0);
+}
index bc0b5a4f271bbf2d9e537e29d7c0e1331ff0c685..b532be5d038fd5a0e62b6da68007e2bb197b071f 100644 (file)
@@ -196,6 +196,7 @@ address .poweredby {
     width: 485px;
     height: 63px;
     padding-bottom: 15px;
+    z-index: 9;
 }
 
 .form_notice label[for=notice_data-attach],
index 48b5cd6af7409294ab2b3d6f59b68433631c12a7..88985efb744bf2b37e53ccf5c69d131e8fa19da6 100644 (file)
@@ -58,3 +58,8 @@ z-index:9999;
 .form_settings fieldset fieldset legend {
 line-height:auto;
 }
+
+
+.form_notice #notice_data-attach {
+filter: alpha(opacity=0);
+}
index 5b51b530dcf1a8120e5e88e1388cf4d9d3506c3f..ec98a049c0e147435812cd50f6b4c9168b59f941 100644 (file)
@@ -209,7 +209,8 @@ address {
     top: 74px;
     right:0;
     height: 2.4em;
-    width: 106px;
+    width: 96px;
+    right: 10px;
 }
 
 #content {
index ca6c09d44ec5a3827a45725fcc5142bb9191900f..3c8e2230d54d023db9d610a2e58e3118d4b1dab0 100644 (file)
@@ -1,9 +1,72 @@
 /* IE specific styles */
 
+/* IE specific styles */
+
+/* base theme overrides */
+
+input.checkbox,
+input.radio {
+top:0;
+}
+.form_notice textarea {
+    width: 374px;
+}
+.form_notice .form_note + label {
+position:absolute;
+top:25px;
+left:83%;
+text-indent:-9999px;
+height:16px;
+width:16px;
+display:block;
+        top: 31px;
+        right: 88px;
+}
+.form_notice #notice_action-submit {
+    width: 96px;
+    max-width: 96px;
+}
+.form_notice #notice_data-attach_selected,
+.form_notice #notice_data-geo_selected {
+width:78.75%;
+}
+.form_notice #notice_data-attach_selected button,
+.form_notice #notice_data-geo_selected button {
+padding:0 4px;
+}
+.notice-options input.submit {
+font-size:0;
+text-align:right;
+text-indent:0;
+}
+.notice div.entry-content .timestamp a {
+margin-right:4px;
+}
+.entity_profile {
+width:64%;
+}
+.notice {
+z-index:1;
+}
+.notice:hover {
+z-index:9999;
+}
+.notice .thumbnail img {
+z-index:9999;
+}
+
+.form_settings fieldset fieldset legend {
+line-height:auto;
+}
+
+.form_notice #notice_data-attach {
+filter: alpha(opacity=0);
+}
+
 #wrap {
-       background-color: #c9c9c9;
+    background: url(../images/wrap_bg.png) repeat top left;
 }
 
 #aside_primary .section {
-    background-color: #c9c9c9;
+    background: url(../images/wrap_bg.png) repeat top left;
 }
diff --git a/theme/shiny/images/wrap_bg.png b/theme/shiny/images/wrap_bg.png
new file mode 100644 (file)
index 0000000..ce9e4ec
Binary files /dev/null and b/theme/shiny/images/wrap_bg.png differ