]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'tagprofile-ajax-fix' into 'nightly'
authormmn <mmn@hethane.se>
Sat, 6 Jun 2015 10:26:43 +0000 (10:26 +0000)
committermmn <mmn@hethane.se>
Sat, 6 Jun 2015 10:26:43 +0000 (10:26 +0000)
Only serve tagprofile HTML if we aren't POSTing via ajax

This fixes an issue where POSTing the selftag form in the
profile_block sidebar via AJAX would generate an XML response
containing both the content from doPost() and showPage(),
resulting in invalid XML.

These changes make it so that if we're POSTing via AJAX,
we serve content from doPost(), otherwise we serve showPage()
but never both.

See merge request !8

59 files changed:
INSTALL
README.md
actions/all.php
actions/conversation.php
actions/emailsettings.php
actions/networkpublic.php
actions/public.php
actions/publicxrds.php [deleted file]
actions/recoverpassword.php
actions/replies.php
actions/showstream.php
actions/userbyid.php
classes/File.php
classes/File_redirection.php
classes/File_thumbnail.php
classes/File_to_post.php
classes/Managed_DataObject.php
classes/Memcached_DataObject.php
classes/Notice.php
classes/Profile.php
classes/Profile_prefs.php
classes/Queue_item.php
classes/User.php
js/extlib/jquery.js
lib/apiaction.php
lib/commandinterpreter.php
lib/default.php
lib/framework.php
lib/galleryaction.php
lib/groupminilist.php
lib/httpclient.php
lib/implugin.php
lib/installer.php
lib/mediafile.php
lib/nickname.php
lib/noparentnoticeexception.php [new file with mode: 0644]
lib/plugin.php
lib/profileaction.php
lib/profileminilist.php
lib/publicgroupnav.php
lib/sitestreamaction.php [new file with mode: 0644]
lib/util.php
lib/xrdsoutputter.php [deleted file]
plugins/ActivityVerb/actions/activityverb.php
plugins/AntiBrute/AntiBrutePlugin.php
plugins/ModPlus/ModPlusPlugin.php
plugins/ModPlus/actions/remoteprofile.php [deleted file]
plugins/OStatus/classes/Ostatus_profile.php
plugins/OpenID/OpenIDPlugin.php
plugins/OpenID/actions/publicxrds.php [new file with mode: 0644]
plugins/OpenID/lib/xrdsoutputter.php [new file with mode: 0644]
plugins/Share/SharePlugin.php
plugins/TwitterBridge/lib/twitterimport.php
plugins/WebFinger/WebFingerPlugin.php
plugins/Xmpp/XmppPlugin.php
scripts/console.php
theme/base/css/display.css
theme/neo-quitter/css/display.css
theme/neo-quitter/favicon.ico

diff --git a/INSTALL b/INSTALL
index aad21756fe1c3f1976e97dcf956491604e626c95..90fa84923bb92bda257863687ef5d71455e4b51d 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -26,7 +26,7 @@ PHP modules
 The following software packages are *required* for this software to
 run correctly.
 
-- PHP 5.4+      For newer versions, some functions that are used may be
+- PHP 5.5+      For newer versions, some functions that are used may be
                 disabled by default, such as the pcntl_* family. See the
                 section on 'Queues and daemons' for more information.
 - MariaDB 5+    GNU Social uses, by default, a MariaDB server for data
index 556ac07130d766a4ce40fedce056f01554237488..3127bb479565a707863c29bf951e93d3d76bb8bf 100644 (file)
--- a/README.md
+++ b/README.md
@@ -144,7 +144,7 @@ to install the development version of GNU social.
 To get it, use the git version control tool
 <http://git-scm.com/> like so:
 
-    git clone git@gitorious.org:social/mainline.git
+    git clone git@git.gnu.io:gnu/gnu-social.git
 
 In the current phase of development it is probably
 recommended to use git as a means to stay up to date
@@ -166,8 +166,8 @@ There are several ways to get more information about GNU social.
 * Following us on GNU social -- <https://quitter.se/gnusocial>
 
 * GNU social has a bug tracker for any defects you may find, or ideas for
-  making things better. <https://bugz.foocorp.net/>
-* Patches are welcome, preferrably to our repository on Gitorious. <https://gitorious.org/social/mainline>
+  making things better. <https://git.gnu.io/gnu/gnu-social/issues/>
+* Patches are welcome, preferrably to our repository on git.gnu.io. <https://git.gnu.io/gnu/gnu-social>
 
 Credits
 =======
index 9cab91264f00bd2337127dbaba6cdb27f3116db3..886117e6a46096eebe99695e076179f9297b7b30 100644 (file)
  * @link     http://status.net
  */
 
-if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
+if (!defined('GNUSOCIAL')) { exit(1); }
 
-class AllAction extends ProfileAction
+class AllAction extends ShowstreamAction
 {
     var $notice;
 
-    protected function profileActionPreparation()
+    protected function getStream()
     {
         if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
             $stream = new InboxNoticeStream($this->target, $this->scoped);
@@ -49,13 +49,7 @@ class AllAction extends ProfileAction
             $stream = new ThreadingInboxNoticeStream($this->target, $this->scoped);
         }
 
-        $this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
-                                            NOTICES_PER_PAGE + 1);
-
-        if ($this->page > 1 && $this->notice->N == 0) {
-            // TRANS: Client error when page not found (404).
-            $this->clientError(_('No such page.'), 404);
-        }
+        return $stream;
     }
 
     function title()
index 3b6f48c853f59a976d22e7340616f11e67e87162..5a6e4b5c7a51c6357c1b482fa9ad9aad280af73e 100644 (file)
@@ -128,7 +128,7 @@ class ConversationAction extends ManagedAction
                                                     'format' => 'atom')),
                               // TRANS: Title for link to notice feed.
                               // TRANS: %s is a user nickname.
-                              _('Conversation feed (Activity Streams JSON)')));
+                              _('Conversation feed (Atom)')));
     }
 }
 
index dfdbe1bad066cac4dcb6f93156f3537fe1bcb699..a0f111c0d5ab6bc0a6b698b6d96fed60dda6f36d 100644 (file)
@@ -410,6 +410,7 @@ class EmailsettingsAction extends SettingsAction
                 $this->serverError(_('Could not insert confirmation code.'));
             }
 
+            common_debug('Sending confirmation address for user '.$user->id.' to email '.$email);
             mail_confirm_address($user, $confirm->code, $user->nickname, $email);
 
             Event::handle('EndAddEmailAddress', array($user, $email));
index 7baa313bee3313a0e68274090e3d53ed2c641671..41c4e37e3cd41cce1db97872a390fc79f4ba3471 100644 (file)
@@ -2,7 +2,7 @@
 
 if (!defined('GNUSOCIAL')) { exit(1); }
 
-class NetworkpublicAction extends PublicAction
+class NetworkpublicAction extends SitestreamAction
 {
     protected function streamPrepare()
     {
@@ -28,13 +28,6 @@ class NetworkpublicAction extends PublicAction
         }
     }
 
-    function extraHead()
-    {
-        // the PublicAction has some XRDS stuff that might be unique to the non-network public feed
-        // FIXME: Solve this with a call that doesn't rely on parent:: and is unique for each class.
-        ManagedAction::extraHead();
-    }
-
     function showSections()
     {
         // Show invite button, as long as site isn't closed, and
index 06ee75b8d1c5fa9b72b52393b0554c90f1669f3a..000f82cb93d5e96b49609cf01676326e9aec6ae3 100644 (file)
 
 if (!defined('GNUSOCIAL')) { exit(1); }
 
-// Farther than any human will go
-
-define('MAX_PUBLIC_PAGE', 100);
-
 /**
  * Action for displaying the public stream
  *
@@ -43,54 +39,9 @@ define('MAX_PUBLIC_PAGE', 100);
  * @link     http://status.net/
  *
  * @see      PublicrssAction
- * @see      PublicxrdsAction
  */
-class PublicAction extends ManagedAction
+class PublicAction extends SitestreamAction
 {
-    /**
-     * page of the stream we're on; default = 1
-     */
-
-    var $page = null;
-    var $notice;
-
-    protected $stream = null;
-
-    function isReadOnly($args)
-    {
-        return true;
-    }
-
-    protected function doPreparation()
-    {
-        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-
-        if ($this->page > MAX_PUBLIC_PAGE) {
-            // TRANS: Client error displayed when requesting a public timeline page beyond the page limit.
-            // TRANS: %s is the page limit.
-            $this->clientError(sprintf(_('Beyond the page limit (%s).'), MAX_PUBLIC_PAGE));
-        }
-
-        common_set_returnto($this->selfUrl());
-
-        $this->streamPrepare();
-
-        $this->notice = $this->stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
-                                            NOTICES_PER_PAGE + 1);
-
-        if (!$this->notice) {
-            // TRANS: Server error displayed when a public timeline cannot be retrieved.
-            $this->serverError(_('Could not retrieve public timeline.'));
-        }
-
-        if ($this->page > 1 && $this->notice->N == 0){
-            // TRANS: Client error when page not found (404).
-            $this->clientError(_('No such page.'), 404);
-        }
-
-        return true;
-    }
-
     protected function streamPrepare()
     {
         if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
@@ -117,24 +68,32 @@ class PublicAction extends ManagedAction
         }
     }
 
-    function extraHead()
+    function showSections()
     {
-        parent::extraHead();
-        $this->element('meta', array('http-equiv' => 'X-XRDS-Location',
-                                           'content' => common_local_url('publicxrds')));
-
-        $rsd = common_local_url('rsd');
-
-        // RSD, http://tales.phrasewise.com/rfc/rsd
+        // Show invite button, as long as site isn't closed, and
+        // we have a logged in user.
+        if (common_config('invite', 'enabled') && !common_config('site', 'closed') && common_logged_in()) {
+            if (!common_config('site', 'private')) {
+                $ibs = new InviteButtonSection(
+                    $this,
+                    // TRANS: Button text for inviting more users to the StatusNet instance.
+                    // TRANS: Less business/enterprise-oriented language for public sites.
+                    _m('BUTTON', 'Send invite')
+                );
+            } else {
+                $ibs = new InviteButtonSection($this);
+            }
+            $ibs->show();
+        }
 
-        $this->element('link', array('rel' => 'EditURI',
-                                     'type' => 'application/rsd+xml',
-                                     'href' => $rsd));
+        $p = Profile::current();
 
-        if ($this->page != 1) {
-            $this->element('link', array('rel' => 'canonical',
-                                         'href' => common_local_url('public')));
+        if (!common_config('performance', 'high')) {
+            $cloud = new PublicTagCloudSection($this);
+            $cloud->show();
         }
+        $feat = new FeaturedUsersSection($this);
+        $feat->show();
     }
 
     /**
@@ -163,99 +122,4 @@ class PublicAction extends ManagedAction
                               // TRANS: Link description for public timeline feed.
                               _('Public Timeline Feed (Atom)')));
     }
-
-    function showEmptyList()
-    {
-        // TRANS: Text displayed for public feed when there are no public notices.
-        $message = _('This is the public timeline for %%site.name%% but no one has posted anything yet.') . ' ';
-
-        if (common_logged_in()) {
-            // TRANS: Additional text displayed for public feed when there are no public notices for a logged in user.
-            $message .= _('Be the first to post!');
-        }
-        else {
-            if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
-                // TRANS: Additional text displayed for public feed when there are no public notices for a not logged in user.
-                $message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
-            }
-        }
-
-        $this->elementStart('div', 'guide');
-        $this->raw(common_markup_to_html($message));
-        $this->elementEnd('div');
-    }
-
-    /**
-     * Fill the content area
-     *
-     * Shows a list of the notices in the public stream, with some pagination
-     * controls.
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
-            $nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
-        } else {
-            $nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
-        }
-
-        $cnt = $nl->show();
-
-        if ($cnt == 0) {
-            $this->showEmptyList();
-        }
-
-        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
-                          $this->page, $this->action);
-    }
-
-    function showSections()
-    {
-        // Show invite button, as long as site isn't closed, and
-        // we have a logged in user.
-        if (common_config('invite', 'enabled') && !common_config('site', 'closed') && common_logged_in()) {
-            if (!common_config('site', 'private')) {
-                $ibs = new InviteButtonSection(
-                    $this,
-                    // TRANS: Button text for inviting more users to the StatusNet instance.
-                    // TRANS: Less business/enterprise-oriented language for public sites.
-                    _m('BUTTON', 'Send invite')
-                );
-            } else {
-                $ibs = new InviteButtonSection($this);
-            }
-            $ibs->show();
-        }
-
-        $p = Profile::current();
-
-        if (!common_config('performance', 'high')) {
-            $cloud = new PublicTagCloudSection($this);
-            $cloud->show();
-        }
-        $feat = new FeaturedUsersSection($this);
-        $feat->show();
-    }
-
-    function showAnonymousMessage()
-    {
-        if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
-            // TRANS: Message for not logged in users at an invite-only site trying to view the public feed of notices.
-            // TRANS: This message contains Markdown links. Please mind the formatting.
-            $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
-                   'based on the Free Software [StatusNet](http://status.net/) tool. ' .
-                   '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ' .
-                   '([Read more](%%doc.help%%))');
-        } else {
-            // TRANS: Message for not logged in users at a closed site trying to view the public feed of notices.
-            // TRANS: This message contains Markdown links. Please mind the formatting.
-            $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
-                   'based on the Free Software [StatusNet](http://status.net/) tool.');
-        }
-        $this->elementStart('div', array('id' => 'anon_notice'));
-        $this->raw(common_markup_to_html($m));
-        $this->elementEnd('div');
-    }
 }
diff --git a/actions/publicxrds.php b/actions/publicxrds.php
deleted file mode 100644 (file)
index aac6f42..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-<?php
-/**
- * Public XRDS for OpenID
- *
- * PHP version 5
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @author   Craig Andrews <candrews@integralblue.com>
- * @author   Robin Millette <millette@status.net>
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, 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/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/plugins/OpenID/openid.php';
-require_once INSTALLDIR.'/lib/xrdsoutputter.php';
-
-/**
- * Public XRDS
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @author   Robin Millette <millette@status.net>
- * @author   Craig Andrews <candrews@integralblue.com>
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @todo factor out similarities with XrdsAction
- */
-class PublicxrdsAction extends Action
-{
-    /**
-     * Is read only?
-     *
-     * @return boolean true
-     */
-    function isReadOnly($args)
-    {
-        return true;
-    }
-
-    /**
-     * Class handler.
-     *
-     * @param array $args array of arguments
-     *
-     * @return nothing
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-        $xrdsOutputter = new XRDSOutputter();
-        $xrdsOutputter->startXRDS();
-        Event::handle('StartPublicXRDS', array($this,&$xrdsOutputter));
-        Event::handle('EndPublicXRDS', array($this,&$xrdsOutputter));
-        $xrdsOutputter->endXRDS();
-    }
-}
-
index 4839a036c0e4be2dcb0f632f3d3fd32c71204336..060ba83510b44fb82fc870e7f7d7f74e49e6a5c3 100644 (file)
@@ -272,10 +272,16 @@ class RecoverpasswordAction extends Action
         try {
             User::recoverPassword($nore);
             $this->mode = 'sent';
-            // TRANS: User notification after an e-mail with instructions was sent from the password recovery form.
-            $this->msg = _('Instructions for recovering your password ' .
-                           'have been sent to the email address registered to your ' .
-                           'account.');
+            if (common_is_email($nore) && common_config('site', 'fakeaddressrecovery')) {
+                // TRANS: User notification when recovering password by giving email address,
+                //        regardless if the mail was sent or not (to hide registered email status).
+                $this->msg = _('If the email address you provided was found in the database, a recovery mail with instructions has been sent there.');
+            } else {
+                // TRANS: User notification after an e-mail with instructions was sent from the password recovery form.
+                $this->msg = _('Instructions for recovering your password ' .
+                               'have been sent to the email address registered to your ' .
+                               'account.');
+            }
             $this->success = true;
         } catch (Exception $e) {
             $this->success = false;
index 49098d0950781f62942b29dc0fb131089e70e0fc..3c7e950d012e1a8d9c518f3b2e4ee28d790ace93 100644 (file)
@@ -38,42 +38,14 @@ if (!defined('GNUSOCIAL')) { exit(1); }
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://status.net/
  */
-class RepliesAction extends ManagedAction
+class RepliesAction extends ShowstreamAction
 {
     var $page = null;
     var $notice;
 
-    protected function doPreparation()
+    protected function getStream()
     {
-        $nickname = common_canonical_nickname($this->arg('nickname'));
-
-        $this->user = User::getKV('nickname', $nickname);
-
-        if (!$this->user instanceof User) {
-            // TRANS: Client error displayed when trying to reply to a non-exsting user.
-            $this->clientError(_('No such user.'));
-        }
-
-        $this->target = $this->user->getProfile();
-
-        if (!$this->target instanceof Profile) {
-            // TRANS: Error message displayed when referring to a user without a profile.
-            $this->serverError(_('User has no profile.'));
-        }
-
-        $this->page = $this->int('page') ?: 1;
-
-        common_set_returnto($this->selfUrl());
-
-        $stream = new ReplyNoticeStream($this->target->getID(), $this->scoped);
-
-        $this->notice = $stream->getNotices(($this->page-1) * NOTICES_PER_PAGE,
-                                            NOTICES_PER_PAGE + 1);
-
-        if ($this->page > 1 && $this->notice->N == 0) {
-            // TRANS: Client error when page not found (404)
-            $this->clientError(_('No such page.'), 404);
-        }
+        return new ReplyNoticeStream($this->target->getID(), $this->scoped);
     }
 
     /**
index 64d85e63be2ee559a2515dc519fa49df1a2cd10c..8e8b055b4b4749b3a51ad7248963386bb4aecccc 100644 (file)
@@ -49,7 +49,47 @@ class ShowstreamAction extends ProfileAction
 {
     var $notice;
 
+    protected function doPreparation()
+    {
+        // showstream requires a nickname
+        $nickname_arg = $this->arg('nickname');
+        $nickname     = common_canonical_nickname($nickname_arg);
+
+        // Permanent redirect on non-canonical nickname
+
+        if ($nickname_arg != $nickname) {
+            $args = array('nickname' => $nickname);
+            if ($this->arg('page') && $this->arg('page') != 1) {
+                $args['page'] = $this->arg['page'];
+            }
+            common_redirect(common_local_url($this->getActionName(), $args), 301);
+        }
+        $this->user = User::getKV('nickname', $nickname);
+
+        if (!$this->user) {
+            $group = Local_group::getKV('nickname', $nickname);
+            if ($group instanceof Local_group) {
+                common_redirect($group->getProfile()->getUrl());
+            }
+            // TRANS: Client error displayed when calling a profile action without specifying a user.
+            $this->clientError(_('No such user.'), 404);
+        }
+
+        $this->target = $this->user->getProfile();
+    }
+
     protected function profileActionPreparation()
+    {
+        $stream = $this->getStream();
+        $this->notice = $stream->getNotices(($this->page-1) * NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
+
+        if ($this->page > 1 && $this->notice->N == 0) {
+            // TRANS: Client error when page not found (404).
+            $this->clientError(_('No such page.'), 404);
+        }
+    }
+
+    protected function getStream()
     {
         if (empty($this->tag)) {
             $stream = new ProfileNoticeStream($this->target, $this->scoped);
@@ -57,9 +97,7 @@ class ShowstreamAction extends ProfileAction
             $stream = new TaggedProfileNoticeStream($this->target, $this->tag, $this->scoped);
         }
 
-        $this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
-
-        return true;
+        return $stream;
     }
 
 
@@ -121,7 +159,7 @@ class ShowstreamAction extends ProfileAction
         return array(new Feed(Feed::JSON,
                               common_local_url('ApiTimelineUser',
                                                array(
-                                                    'id' => $this->user->id,
+                                                    'id' => $this->target->getID(),
                                                     'format' => 'as')),
                               // TRANS: Title for link to notice feed.
                               // TRANS: %s is a user nickname.
@@ -137,7 +175,7 @@ class ShowstreamAction extends ProfileAction
                      new Feed(Feed::RSS2,
                               common_local_url('ApiTimelineUser',
                                                array(
-                                                    'id' => $this->user->id,
+                                                    'id' => $this->target->getID(),
                                                     'format' => 'rss')),
                               // TRANS: Title for link to notice feed.
                               // TRANS: %s is a user nickname.
@@ -146,7 +184,7 @@ class ShowstreamAction extends ProfileAction
                      new Feed(Feed::ATOM,
                               common_local_url('ApiTimelineUser',
                                                array(
-                                                    'id' => $this->user->id,
+                                                    'id' => $this->target->getID(),
                                                     'format' => 'atom')),
                               // TRANS: Title for link to notice feed.
                               // TRANS: %s is a user nickname.
@@ -197,23 +235,22 @@ class ShowstreamAction extends ProfileAction
     function showEmptyListMessage()
     {
         // TRANS: First sentence of empty list message for a timeline. $1%s is a user nickname.
-        $message = sprintf(_('This is the timeline for %1$s, but %1$s hasn\'t posted anything yet.'), $this->target->nickname) . ' ';
+        $message = sprintf(_('This is the timeline for %1$s, but %1$s hasn\'t posted anything yet.'), $this->target->getNickname()) . ' ';
 
-        if (common_logged_in()) {
-            $current_user = common_current_user();
-            if ($this->user->id === $current_user->id) {
+        if ($this->scoped instanceof Profile) {
+            if ($this->target->getID() === $this->scoped->getID()) {
                 // TRANS: Second sentence of empty list message for a stream for the user themselves.
                 $message .= _('Seen anything interesting recently? You haven\'t posted any notices yet, now would be a good time to start :)');
             } else {
                 // TRANS: Second sentence of empty  list message for a non-self timeline. %1$s is a user nickname, %2$s is a part of a URL.
                 // TRANS: This message contains a Markdown link. Keep "](" together.
-                $message .= sprintf(_('You can try to nudge %1$s or [post something to them](%%%%action.newnotice%%%%?status_textarea=%2$s).'), $this->target->nickname, '@' . $this->target->nickname);
+                $message .= sprintf(_('You can try to nudge %1$s or [post something to them](%%%%action.newnotice%%%%?status_textarea=%2$s).'), $this->target->getNickname(), '@' . $this->target->getNickname());
             }
         }
         else {
             // TRANS: Second sentence of empty message for anonymous users. %s is a user nickname.
             // TRANS: This message contains a Markdown link. Keep "](" together.
-            $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->nickname);
+            $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->getNickname());
         }
 
         $this->elementStart('div', 'guide');
@@ -229,7 +266,7 @@ class ShowstreamAction extends ProfileAction
             $this->showEmptyListMessage();
         }
 
-        $args = array('nickname' => $this->target->nickname);
+        $args = array('nickname' => $this->target->getNickname());
         if (!empty($this->tag))
         {
             $args['tag'] = $this->tag;
@@ -246,13 +283,13 @@ class ShowstreamAction extends ProfileAction
             $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                            'based on the Free Software [StatusNet](http://status.net/) tool. ' .
                            '[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
-                         $this->target->nickname, $this->target->nickname);
+                         $this->target->getNickname(), $this->target->getNickname());
         } else {
             // TRANS: Announcement for anonymous users showing a timeline if site registrations are closed or invite only.
             // TRANS: This message contains a Markdown link. Keep "](" together.
             $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
                            'based on the Free Software [StatusNet](http://status.net/) tool.'),
-                         $this->target->nickname, $this->target->nickname);
+                         $this->target->getNickname(), $this->target->getNickname());
         }
         $this->elementStart('div', array('id' => 'anon_notice'));
         $this->raw(common_markup_to_html($m));
index 09fb4258d9c4a3c4fd0a27813c47707d77e6042e..1e3c9d8519bf13be3618881547508f3bbb4ffbe5 100644 (file)
@@ -28,9 +28,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * User by ID action class.
@@ -42,50 +40,27 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
  * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
  * @link     http://status.net/
  */
-class UserbyidAction extends Action
+class UserbyidAction extends ShowstreamAction
 {
-     /**
-     * Is read only?
-     *
-     * @return boolean true
-     */
-    function isReadOnly($args)
+    protected function doPreparation()
     {
-        return true;
-    }
+        // accessing by ID just requires an ID, not a nickname
+        $this->target = Profile::getByID($this->trimmed('id'));
 
-     /**
-     * Class handler.
-     *
-     * @param array $args array of arguments
-     *
-     * @return nothing
-     */
-    protected function handle()
-    {
-        parent::handle();
-        $id = $this->trimmed('id');
-        if (!$id) {
-            // TRANS: Client error displayed trying to find a user by ID without providing an ID.
-            $this->clientError(_('No ID.'));
+        // For local users when accessed by id number, redirect with
+        // the nickname as argument instead of id.
+        if ($this->target->isLocal()) {
+            // Support redirecting to FOAF rdf/xml if the agent prefers it...
+            // Internet Explorer doesn't specify "text/html" and does list "*/*"
+            // at least through version 8. We need to list text/html up front to
+            // ensure that only user-agents who specifically ask for RDF get it.
+            $page_prefs = 'text/html,application/xhtml+xml,application/rdf+xml,application/xml;q=0.3,text/xml;q=0.2';
+            $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : null;
+            $type       = common_negotiate_type(common_accept_to_prefs($httpaccept),
+                                                common_accept_to_prefs($page_prefs));
+            $page       = $type === 'application/rdf+xml' ? 'foaf' : 'showstream';
+            $url        = common_local_url($page, array('nickname' => $this->target->getNickname()));
+            common_redirect($url, 303);
         }
-        $user = User::getKV($id);
-        if (!$user) {
-            // TRANS: Client error displayed trying to find a user by ID for a non-existing ID.
-            $this->clientError(_('No such user.'));
-        }
-
-        // Support redirecting to FOAF rdf/xml if the agent prefers it...
-        // Internet Explorer doesn't specify "text/html" and does list "*/*"
-        // at least through version 8. We need to list text/html up front to
-        // ensure that only user-agents who specifically ask for RDF get it.
-        $page_prefs = 'text/html,application/xhtml+xml,application/rdf+xml,application/xml;q=0.3,text/xml;q=0.2';
-        $httpaccept = isset($_SERVER['HTTP_ACCEPT'])
-                      ? $_SERVER['HTTP_ACCEPT'] : null;
-        $type       = common_negotiate_type(common_accept_to_prefs($httpaccept),
-                      common_accept_to_prefs($page_prefs));
-        $page       = $type == 'application/rdf+xml' ? 'foaf' : 'showstream';
-        $url        = common_local_url($page, array('nickname' => $user->nickname));
-        common_redirect($url, 303);
     }
 }
index 1cb7eab6b856c203e6023d79e72cb98415fe538f..d4abbfddeef3f5f45c26621fce6414c16e75827f 100644 (file)
@@ -116,14 +116,14 @@ class File extends Managed_DataObject
      *
      * @fixme refactor this mess, it's gotten pretty scary.
      * @param string $given_url the URL we're looking at
-     * @param int $notice_id (optional)
+     * @param Notice $notice (optional)
      * @param bool $followRedirects defaults to true
      *
      * @return mixed File on success, -1 on some errors
      *
      * @throws ServerException on failure
      */
-    public static function processNew($given_url, $notice_id=null, $followRedirects=true) {
+    public static function processNew($given_url, Notice $notice=null, $followRedirects=true) {
         if (empty($given_url)) {
             throw new ServerException('No given URL to process');
         }
@@ -181,7 +181,7 @@ class File extends Managed_DataObject
                 //
                 // Seen in the wild with clojure.org, which redirects through
                 // wikispaces for auth and appends session data in the URL params.
-                $file = self::processNew($redir_url, $notice_id, /*followRedirects*/false);
+                $file = self::processNew($redir_url, $notice, /*followRedirects*/false);
                 File_redirection::saveNew($redir_data, $file->id, $given_url);
             }
 
@@ -193,8 +193,8 @@ class File extends Managed_DataObject
             }
         }
 
-        if (!empty($notice_id)) {
-            File_to_post::processNew($file->id, $notice_id);
+        if ($notice instanceof Notice) {
+            File_to_post::processNew($file, $notice);
         }
         return $file;
     }
@@ -510,9 +510,9 @@ class File extends Managed_DataObject
 
     function blowCache($last=false)
     {
-        self::blow('file:notice-ids:%s', $this->urlhash);
+        self::blow('file:notice-ids:%s', $this->id);
         if ($last) {
-            self::blow('file:notice-ids:%s;last', $this->urlhash);
+            self::blow('file:notice-ids:%s;last', $this->id);
         }
         self::blow('file:notice-count:%d', $this->id);
     }
index 12619b0394e6686d38eff0bb0478865d1977486e..ea9db8e89154912a27fdb31f191811a2196a7716 100644 (file)
@@ -59,12 +59,7 @@ class File_redirection extends Managed_DataObject
 
     static public function getByUrl($url)
     {
-        $file = new File_redirection();
-        $file->urlhash = File::hashurl($url);
-        if (!$file->find(true)) {
-            throw new NoResultException($file);
-        }
-        return $file;
+        return self::getByPK(array('urlhash' => File::hashurl($url)));
     }
 
     static function _commonHttp($url, $redirs) {
@@ -261,7 +256,7 @@ class File_redirection extends Managed_DataObject
             // store it
             $file = File::getKV('url', $long_url);
             if ($file instanceof File) {
-                $file_id = $file->id;
+                $file_id = $file->getID();
             } else {
                 // Check if the target URL is itself a redirect...
                 $redir_data = File_redirection::where($long_url);
@@ -269,7 +264,7 @@ class File_redirection extends Managed_DataObject
                     // We haven't seen the target URL before.
                     // Save file and embedding data about it!
                     $file = File::saveNew($redir_data, $long_url);
-                    $file_id = $file->id;
+                    $file_id = $file->getID();
                 } else if (is_string($redir_data)) {
                     // The file is a known redirect target.
                     $file = File::getKV('url', $redir_data);
@@ -281,7 +276,7 @@ class File_redirection extends Managed_DataObject
                         // SSL sites with cert issues.
                         return null;
                     }
-                    $file_id = $file->id;
+                    $file_id = $file->getID();
                 }
             }
             $file_redir = File_redirection::getKV('url', $short_url);
index a2e633249f950c345b54b9bbd80ce713200929a4..fb2515f9f5c02efd028205c45f48b8c318cb108c 100644 (file)
@@ -82,9 +82,9 @@ class File_thumbnail extends Managed_DataObject
      * Fetch an entry by using a File's id
      */
     static function byFile(File $file) {
-        $file_thumbnail = self::getKV('file_id', $file->id);
+        $file_thumbnail = self::getKV('file_id', $file->getID());
         if (!$file_thumbnail instanceof File_thumbnail) {
-            throw new ServerException(sprintf('No File_thumbnail entry for File id==%u', $file->id));
+            throw new ServerException(sprintf('No File_thumbnail entry for File id==%u', $file->getID()));
         }
         return $file_thumbnail;
     }
@@ -167,11 +167,6 @@ class File_thumbnail extends Managed_DataObject
 
     public function getFile()
     {
-        $file = new File();
-        $file->id = $this->file_id;
-        if (!$file->find(true)) {
-            throw new NoResultException($file);
-        }
-        return $file;
+        return File::getByID($this->file_id);
     }
 }
index b3c44d4a224f3a982c468922a050c666c0a0698a..e06e34aa46fae2df132cb15454116a3b50377f0c 100644 (file)
@@ -17,9 +17,7 @@
  * along with this program.     If not, see <http://www.gnu.org/licenses/>.
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * Table Definition for file_to_post
@@ -58,30 +56,29 @@ class File_to_post extends Managed_DataObject
         );
     }
 
-    function processNew($file_id, $notice_id) {
+    function processNew(File $file, Notice $notice) {
         static $seen = array();
-        if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) {
 
-            $f2p = File_to_post::pkeyGet(array('post_id' => $notice_id,
-                                               'file_id' => $file_id));
-            if (empty($f2p)) {
+        $file_id = $file->getID();
+        $notice_id = $notice->getID();
+        if (!array_key_exists($notice_id, $seen)) {
+            $seen[$notice_id] = array();
+        }
+
+        if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) {
+            try {
+                $f2p = File_to_post::getByPK(array('post_id' => $notice_id,
+                                                   'file_id' => $file_id));
+            } catch (NoResultException $e) {
                 $f2p = new File_to_post;
                 $f2p->file_id = $file_id;
                 $f2p->post_id = $notice_id;
                 $f2p->insert();
                 
-                $f = File::getKV($file_id);
-
-                if (!empty($f)) {
-                    $f->blowCache();
-                }
+                $file->blowCache();
             }
 
-            if (empty($seen[$notice_id])) {
-                $seen[$notice_id] = array($file_id);
-            } else {
-                $seen[$notice_id][] = $file_id;
-            }
+            $seen[$notice_id][] = $file_id;
         }
     }
 
@@ -92,7 +89,7 @@ class File_to_post extends Managed_DataObject
         $f2p->selectAdd();
         $f2p->selectAdd('post_id');
 
-        $f2p->file_id = $file->id;
+        $f2p->file_id = $file->getID();
 
         $ids = array();
 
@@ -105,10 +102,13 @@ class File_to_post extends Managed_DataObject
 
     function delete($useWhere=false)
     {
-        $f = File::getKV('id', $this->file_id);
-        if ($f instanceof File) {
+        try {
+            $f = File::getByID($this->file_id);
             $f->blowCache();
+        } catch (NoResultException $e) {
+            // ...alright, that's weird, but no File to delete anyway.
         }
+
         return parent::delete($useWhere);
     }
 }
index b324984b7f8e7512d4c8b77606654ca6b10b8ae2..a69a957bcc0225bad8b4085312b1b73a53b9ea13 100644 (file)
@@ -64,6 +64,11 @@ abstract class Managed_DataObject extends Memcached_DataObject
         return parent::pkeyGetClass(get_called_class(), $kv);
     }
 
+    static function pkeyCols()
+    {
+        return parent::pkeyColsClass(get_called_class());
+    }
+
     /**
      * Get multiple items from the database by key
      *
@@ -304,6 +309,53 @@ abstract class Managed_DataObject extends Memcached_DataObject
         return common_database_tablename($this->tableName());
     }
 
+    /**
+     * Returns an object by looking at the primary key column(s).
+     *
+     * Will require all primary key columns to be defined in an associative array
+     * and ignore any keys which are not part of the primary key.
+     *
+     * Will NOT accept NULL values as part of primary key.
+     *
+     * @param   array   $vals       Must match all primary key columns for the dataobject.
+     *
+     * @return  Managed_DataObject  of the get_called_class() type
+     * @throws  NoResultException   if no object with that primary key
+     */
+    static function getByPK(array $vals)
+    {
+        $classname = get_called_class();
+
+        $pkey = static::pkeyCols();
+        if (is_null($pkey)) {
+            throw new ServerException("Failed to get primary key columns for class '{$classname}'");
+        }
+
+        $object = new $classname();
+        foreach ($pkey as $col) {
+            if (!array_key_exists($col, $vals)) {
+                throw new ServerException("Missing primary key column '{$col}'");
+            } elseif (is_null($vals[$col])) {
+                throw new ServerException("NULL values not allowed in getByPK for column '{$col}'");
+            }
+            $object->$col = $vals[$col];
+        }
+        if (!$object->find(true)) {
+            throw new NoResultException($object);
+        }
+        return $object;
+    }
+
+    static function getByID($id)
+    {
+        if (empty($id)) {
+            throw new ServerException('Empty ID on lookup');
+        }
+        // getByPK throws exception if id is null
+        // or if the class does not have a single 'id' column as primary key
+        return static::getByPK(array('id' => $id));
+    }
+
     /**
      * Returns an ID, checked that it is set and reasonably valid
      *
index 3f1945205afcb48fd0cde54beb4257ed095b2791..91b986891cea53aa6a74ee04f3c0512f2e047ba6 100644 (file)
@@ -34,7 +34,7 @@ class Memcached_DataObject extends Safe_DataObject
     {
         if (is_null($v)) {
             $v = $k;
-            $keys = self::pkeyCols($cls);
+            $keys = static::pkeyCols();
             if (count($keys) > 1) {
                 // FIXME: maybe call pkeyGetClass() ourselves?
                 throw new Exception('Use pkeyGetClass() for compound primary keys');
@@ -246,7 +246,7 @@ class Memcached_DataObject extends Safe_DataObject
         return $query;
     }
 
-    static function pkeyCols($cls)
+    static function pkeyColsClass($cls)
     {
         $i = new $cls;
         $types = $i->keyTypes();
@@ -279,7 +279,7 @@ class Memcached_DataObject extends Safe_DataObject
         $pkeyMap = array_fill_keys($keyVals, array());
         $result = array_fill_keys($keyVals, array());
 
-        $pkeyCols = self::pkeyCols($cls);
+        $pkeyCols = static::pkeyCols();
 
         $toFetch = array();
         $allPkeys = array();
index 38e31cb2745189ff0a3b41ee1711f48803318e0e..9246c26919ac13897a22e261e5cc3b044f2fb3ad 100644 (file)
@@ -84,7 +84,7 @@ class Notice extends Managed_DataObject
                 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
                 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'who made the update'),
                 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
-                'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8_general_ci'),
+                'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8mb4_general_ci'),
                 'rendered' => array('type' => 'text', 'description' => 'HTML version of the content'),
                 'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL of any attachment (image, video, bookmark, whatever)'),
                 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
@@ -313,16 +313,6 @@ class Notice extends Managed_DataObject
         return $notice;
     }
 
-    public static function getById($id)
-    {
-        $notice = new Notice();
-        $notice->id = $id;
-        if (!$notice->find(true)) {
-            throw new NoResultException($notice);
-        }
-        return $notice;
-    }
-
     /**
      * Extract #hashtags from this notice's content and save them to the database.
      */
@@ -1109,7 +1099,7 @@ class Notice extends Managed_DataObject
      */
     function saveUrls() {
         if (common_config('attachments', 'process_links')) {
-            common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this->id);
+            common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this);
         }
     }
 
@@ -1126,11 +1116,7 @@ class Notice extends Managed_DataObject
         if (common_config('attachments', 'process_links')) {
             // @fixme validation?
             foreach (array_unique($urls) as $url) {
-                try {
-                    File::processNew($url, $this->id);
-                } catch (ServerException $e) {
-                    // Could not save URL. Log it?
-                }
+                $this->saveUrl($url, $this);
             }
         }
     }
@@ -1138,9 +1124,9 @@ class Notice extends Managed_DataObject
     /**
      * @private callback
      */
-    function saveUrl($url, $notice_id) {
+    function saveUrl($url, Notice $notice) {
         try {
-            File::processNew($url, $notice_id);
+            File::processNew($url, $notice);
         } catch (ServerException $e) {
             // Could not save URL. Log it?
         }
@@ -1311,7 +1297,7 @@ class Notice extends Managed_DataObject
                     $last = $parent;
                     continue;
                 }
-            } catch (Exception $e) {
+            } catch (NoParentNoticeException $e) {
                 // Latest notice has no parent
             }
             // No parent, or parent out of scope
@@ -1617,7 +1603,7 @@ class Notice extends Managed_DataObject
             $this->saveReply($parentauthor->id);
             $replied[$parentauthor->id] = 1;
             self::blow('reply:stream:%d', $parentauthor->id);
-        } catch (Exception $e) {
+        } catch (NoParentNoticeException $e) {
             // Not a reply, since it has no parent!
         }
 
@@ -1634,8 +1620,7 @@ class Notice extends Managed_DataObject
             foreach ($mention['mentioned'] as $mentioned) {
 
                 // skip if they're already covered
-
-                if (!empty($replied[$mentioned->id])) {
+                if (array_key_exists($mentioned->id, $replied)) {
                     continue;
                 }
 
@@ -1852,8 +1837,8 @@ class Notice extends Managed_DataObject
             try {
                 $reply = $this->getParent();
                 $ctx->replyToID  = $reply->getUri();
-                $ctx->replyToUrl = $reply->getUrl();
-            } catch (Exception $e) {
+                $ctx->replyToUrl = $reply->getUrl(true);    // true for fallback to local URL, less messy
+            } catch (NoParentNoticeException $e) {
                 // This is not a reply to something
             }
 
@@ -2763,13 +2748,10 @@ class Notice extends Managed_DataObject
 
     public function getParent()
     {
-        $parent = Notice::getKV('id', $this->reply_to);
-
-        if (!$parent instanceof Notice) {
-            throw new ServerException('Notice has no parent');
+        if (empty($this->reply_to)) {
+            throw new NoParentNoticeException($this);
         }
-
-        return $parent;
+        return self::getByID($this->reply_to);
     }
 
     /**
index 6eb09782b18785574ce54f28585bae3e37480724..b5ba00caa9e24dc625e61efb88ab85e5962baedf 100644 (file)
@@ -48,12 +48,12 @@ class Profile extends Managed_DataObject
             'description' => 'local and remote users have profiles',
             'fields' => array(
                 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
-                'nickname' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username', 'collate' => 'utf8_general_ci'),
-                'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name', 'collate' => 'utf8_general_ci'),
+                'nickname' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username', 'collate' => 'utf8mb4_general_ci'),
+                'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name', 'collate' => 'utf8mb4_general_ci'),
                 'profileurl' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
-                'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'identifying URL', 'collate' => 'utf8_general_ci'),
-                'bio' => array('type' => 'text', 'description' => 'descriptive biography', 'collate' => 'utf8_general_ci'),
-                'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'physical location', 'collate' => 'utf8_general_ci'),
+                'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'identifying URL', 'collate' => 'utf8mb4_general_ci'),
+                'bio' => array('type' => 'text', 'description' => 'descriptive biography', 'collate' => 'utf8mb4_general_ci'),
+                'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'physical location', 'collate' => 'utf8mb4_general_ci'),
                 'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
                 'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
                 'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
@@ -880,6 +880,11 @@ class Profile extends Managed_DataObject
             $inst->delete();
         }
 
+        $localuser = User::getKV('id', $this->id);
+        if ($localuser instanceof User) {
+            $localuser->delete();
+        }
+
         return parent::delete($useWhere);
     }
 
index 27034390f848d4293ff35f701b95d2ac2ef57a77..72a707cae86813149afad1297b1afd0c8a1e2bcb 100644 (file)
@@ -62,11 +62,11 @@ class Profile_prefs extends Managed_DataObject
     {
         if (empty($topic)) {
             $prefs = new Profile_prefs();
-            $prefs->profile_id = $profile->id;
+            $prefs->profile_id = $profile->getID();
             $prefs->namespace  = $namespace;
             $prefs->find();
         } else {
-            $prefs = self::pivotGet('profile_id', $profile->id, array('namespace'=>$namespace, 'topic'=>$topic));
+            $prefs = self::pivotGet('profile_id', $profile->getID(), array('namespace'=>$namespace, 'topic'=>$topic));
         }
 
         if (empty($prefs->N)) {
@@ -85,7 +85,7 @@ class Profile_prefs extends Managed_DataObject
     static function getAll(Profile $profile)
     {
         try {
-            $prefs = self::listFind('profile_id', $profile->id);
+            $prefs = self::listFind('profile_id', $profile->getID());
         } catch (NoResultException $e) {
             return array();
         }
@@ -101,15 +101,9 @@ class Profile_prefs extends Managed_DataObject
     }
 
     static function getTopic(Profile $profile, $namespace, $topic) {
-        $pref = new Profile_prefs;
-        $pref->profile_id = $profile->id;
-        $pref->namespace  = $namespace;
-        $pref->topic      = $topic;
-
-        if (!$pref->find(true)) {
-            throw new NoResultException($pref);
-        }
-        return $pref;
+        return Profile_prefs::getByPK(array('profile_id' => $profile->getID(),
+                                            'namespace'  => $namespace,
+                                            'topic'      => $topic));
     }
 
     static function getData(Profile $profile, $namespace, $topic, $def=null) {
@@ -164,7 +158,7 @@ class Profile_prefs extends Managed_DataObject
         }
 
         $pref = new Profile_prefs();
-        $pref->profile_id = $profile->id;
+        $pref->profile_id = $profile->getID();
         $pref->namespace  = $namespace;
         $pref->topic      = $topic;
         $pref->data       = $data;
index 0d6fd56af278df6adff8654ae4302d4850a94b96..3a7d05adef7249d203a7dee5c24f2c64f39392e2 100644 (file)
@@ -63,7 +63,7 @@ class Queue_item extends Managed_DataObject
             // XXX: potential race condition
             // can we force it to only update if claimed is still null
             // (or old)?
-            common_log(LOG_INFO, 'claiming queue item id = ' . $qi->id .
+            common_log(LOG_INFO, 'claiming queue item id = ' . $qi->getID() .
                 ' for transport ' . $qi->transport);
             $orig = clone($qi);
             $qi->claimed = common_sql_now();
@@ -85,7 +85,7 @@ class Queue_item extends Managed_DataObject
     function releaseClaim()
     {
         // DB_DataObject doesn't let us save nulls right now
-        $sql = sprintf("UPDATE queue_item SET claimed=NULL WHERE id=%d", $this->id);
+        $sql = sprintf("UPDATE queue_item SET claimed=NULL WHERE id=%d", $this->getID());
         $this->query($sql);
 
         $this->claimed = null;
index f543a7552855fb74af4ac729bf8cfb579c3e2c47..3efaa5e72131b5ba7cda2262ba4e4879d0277b1d 100644 (file)
@@ -853,57 +853,59 @@ class User extends Managed_DataObject
 
     static function recoverPassword($nore)
     {
-        $user = User::getKV('email', common_canonical_email($nore));
-
-        if (!$user) {
-            try {
-                $user = User::getKV('nickname', common_canonical_nickname($nore));
-            } catch (NicknameException $e) {
-                // invalid
+        // $confirm_email will be used as a fallback if our user doesn't have a confirmed email
+        $confirm_email = null;
+
+        if (common_is_email($nore)) {
+            $user = User::getKV('email', common_canonical_email($nore));
+
+            // See if it's an unconfirmed email address
+            if (!$user instanceof User) {
+                // Warning: it may actually be legit to have multiple folks
+                // who have claimed, but not yet confirmed, the same address.
+                // We'll only send to the first one that comes up.
+                $confirm_email = new Confirm_address();
+                $confirm_email->address = common_canonical_email($nore);
+                $confirm_email->address_type = 'email';
+                if ($confirm_email->find(true)) {
+                    $user = User::getKV('id', $confirm_email->user_id);
+                }
             }
-        }
-
-        // See if it's an unconfirmed email address
 
-        if (!$user) {
-            // Warning: it may actually be legit to have multiple folks
-            // who have claimed, but not yet confirmed, the same address.
-            // We'll only send to the first one that comes up.
-            $confirm_email = new Confirm_address();
-            $confirm_email->address = common_canonical_email($nore);
-            $confirm_email->address_type = 'email';
-            $confirm_email->find();
-            if ($confirm_email->fetch()) {
-                $user = User::getKV($confirm_email->user_id);
-            } else {
-                $confirm_email = null;
+            // No luck finding anyone by that email address.
+            if (!$user instanceof User) {
+                if (common_config('site', 'fakeaddressrecovery')) {
+                    // Return without actually doing anything! We fake address recovery
+                    // to avoid revealing which email addresses are registered with the site.
+                    return;
+                }
+                // TRANS: Information on password recovery form if no known e-mail address was specified.
+                throw new ClientException(_('No user with that email address exists here.'));
             }
         } else {
-            $confirm_email = null;
-        }
-
-        if (!$user) {
-            // TRANS: Information on password recovery form if no known username or e-mail address was specified.
-            throw new ClientException(_('No user with that email address or username.'));
-            return;
+            // This might throw a NicknameException on bad nicknames
+            $user = User::getKV('nickname', common_canonical_nickname($nore));
+            if (!$user instanceof User) {
+                // TRANS: Information on password recovery form if no known username was specified.
+                throw new ClientException(_('No user with that nickname exists here.'));
+            }
         }
 
         // Try to get an unconfirmed email address if they used a user name
-
-        if (!$user->email && !$confirm_email) {
+        if (empty($user->email) && $confirm_email === null) {
             $confirm_email = new Confirm_address();
             $confirm_email->user_id = $user->id;
             $confirm_email->address_type = 'email';
             $confirm_email->find();
             if (!$confirm_email->fetch()) {
+                // Nothing found, so let's reset it to null
                 $confirm_email = null;
             }
         }
 
-        if (!$user->email && !$confirm_email) {
+        if (empty($user->email) && !$confirm_email instanceof Confirm_address) {
             // TRANS: Client error displayed on password recovery form if a user does not have a registered e-mail address.
             throw new ClientException(_('No registered email address for that user.'));
-            return;
         }
 
         // Success! We have a valid user and a confirmed or unconfirmed email address
@@ -912,13 +914,12 @@ class User extends Managed_DataObject
         $confirm->code = common_confirmation_code(128);
         $confirm->address_type = 'recover';
         $confirm->user_id = $user->id;
-        $confirm->address = (!empty($user->email)) ? $user->email : $confirm_email->address;
+        $confirm->address = $user->email ?: $confirm_email->address;
 
         if (!$confirm->insert()) {
             common_log_db_error($confirm, 'INSERT', __FILE__);
             // TRANS: Server error displayed if e-mail address confirmation fails in the database on the password recovery form.
             throw new ServerException(_('Error saving address confirmation.'));
-            return;
         }
 
          // @todo FIXME: needs i18n.
index 79d631ff4639efcbf886547a40ec02143244e55c..eed17778c688271208406367c0c1681d81feca6f 100644 (file)
@@ -1,5 +1,5 @@
 /*!
- * jQuery JavaScript Library v2.1.3
+ * jQuery JavaScript Library v2.1.4
  * http://jquery.com/
  *
  * Includes Sizzle.js
@@ -9,7 +9,7 @@
  * Released under the MIT license
  * http://jquery.org/license
  *
- * Date: 2014-12-18T15:11Z
+ * Date: 2015-04-28T16:01Z
  */
 
 (function( global, factory ) {
@@ -67,7 +67,7 @@ var
        // Use the correct document accordingly with window argument (sandbox)
        document = window.document,
 
-       version = "2.1.3",
+       version = "2.1.4",
 
        // Define a local copy of jQuery
        jQuery = function( selector, context ) {
@@ -531,7 +531,12 @@ jQuery.each("Boolean Number String Function Array Date RegExp Object Error".spli
 });
 
 function isArraylike( obj ) {
-       var length = obj.length,
+
+       // Support: iOS 8.2 (not reproducible in simulator)
+       // `in` check used to prevent JIT error (gh-2145)
+       // hasOwn isn't used here due to false negatives
+       // regarding Nodelist length in IE
+       var length = "length" in obj && obj.length,
                type = jQuery.type( obj );
 
        if ( type === "function" || jQuery.isWindow( obj ) ) {
index 0eea08bed63f147a4c05b1edaec6fbfc68793cd2..724447f120cbadc7df1c66bd09ee095d597ba0fb 100755 (executable)
@@ -328,7 +328,7 @@ class ApiAction extends Action
             // different story for parenting.
             $parent = $notice->getParent();
             $in_reply_to = $parent->id;
-        } catch (Exception $e) {
+        } catch (NoParentNoticeException $e) {
             $in_reply_to = null;
         }
         $twitter_status['in_reply_to_status_id'] = $in_reply_to;
index d2b744e93d79c41554dc2788277efcc7f3f6883c..c546cf0fca94ca34f500a804ad34868981f93f83 100644 (file)
@@ -36,6 +36,7 @@ class CommandInterpreter
         // StatusNet
 
         $cmd = strtolower($cmd);
+        $result = false;
 
         if (Event::handle('StartInterpretCommand', array($cmd, $arg, $user, &$result))) {
             switch($cmd) {
@@ -297,8 +298,6 @@ class CommandInterpreter
                     $result = new TrackingCommand($user);
                 }
                 break;
-            default:
-                $result = false;
             }
 
             Event::handle('EndInterpretCommand', array($cmd, $arg, $user, &$result));
index 6369fbddc6004f12d6f978168e29142a9f1be0e3..0ec9fc4e14d1cc5a67c2f9b7316b17b526797733 100644 (file)
@@ -48,6 +48,7 @@ $default =
               'languages' => get_all_languages(),
               'email' =>
               array_key_exists('SERVER_ADMIN', $_SERVER) ? $_SERVER['SERVER_ADMIN'] : null,
+              'fakeaddressrecovery' => true,
               'broughtby' => null,
               'timezone' => 'UTC',
               'broughtbyurl' => null,
index 4ec8b083e02955de5c08275824dd2729bbe5bcab..da43297d1050222d0ec1b42f10ee33d601b44663 100644 (file)
@@ -23,7 +23,7 @@ define('GNUSOCIAL_ENGINE', 'GNU social');
 define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/');
 
 define('GNUSOCIAL_BASE_VERSION', '1.2.0');
-define('GNUSOCIAL_LIFECYCLE', 'dev'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
+define('GNUSOCIAL_LIFECYCLE', 'alpha1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
 
 define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
 
@@ -38,6 +38,9 @@ define('PROFILES_PER_PAGE', 20);
 define('MESSAGES_PER_PAGE', 20);
 define('GROUPS_PER_PAGE', 20);
 
+define('GROUPS_PER_MINILIST', 8);
+define('PROFILES_PER_MINILIST', 8);
+
 define('FOREIGN_NOTICE_SEND', 1);
 define('FOREIGN_NOTICE_RECV', 2);
 define('FOREIGN_NOTICE_SEND_REPLY', 4);
index 4fdd97d468331b0bbf942ad31fc3297cc2c49031..f87043ac063840ced2c0d16fed372e1d2f0e1a45 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/profilelist.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 // 10x8
 
@@ -40,6 +36,35 @@ class GalleryAction extends ProfileAction
         parent::handle();
     }
 
+    protected function doPreparation()
+    {
+        // showstream requires a nickname
+        $nickname_arg = $this->arg('nickname');
+        $nickname     = common_canonical_nickname($nickname_arg);
+
+        // Permanent redirect on non-canonical nickname
+
+        if ($nickname_arg != $nickname) {
+            $args = array('nickname' => $nickname);
+            if ($this->arg('page') && $this->arg('page') != 1) {
+                $args['page'] = $this->arg['page'];
+            }
+            common_redirect(common_local_url($this->getActionName(), $args), 301);
+        }
+        $this->user = User::getKV('nickname', $nickname);
+
+        if (!$this->user) {
+            $group = Local_group::getKV('nickname', $nickname);
+            if ($group instanceof Local_group) {
+                common_redirect($group->getProfile()->getUrl());
+            }
+            // TRANS: Client error displayed when calling a profile action without specifying a user.
+            $this->clientError(_('No such user.'), 404);
+        }
+
+        $this->target = $this->user->getProfile();
+    }
+
     function showContent()
     {
         $this->showTagsDropdown();
index ca7f8775c60dd0d2895868091be29e237d2c963e..8212b81b17965f57e2dbd31f6ef86c721b9b300b 100644 (file)
@@ -33,8 +33,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
 
 require_once INSTALLDIR.'/lib/grouplist.php';
 
-define('GROUPS_PER_MINILIST', 8);
-
 /**
  * Widget to show a list of groups, good for sidebar
  *
index 6016f89314400e90059a1b93eaf232a9a482219c..865fc9029e847134b0668fc3ba9eee7b8eab33aa 100644 (file)
@@ -103,7 +103,7 @@ class GNUsocial_HTTPResponse extends HTTP_Request2_Response
  *
  * This extends the PEAR HTTP_Request2 package:
  * - sends StatusNet-specific User-Agent header
- * - 'follow_redirects' config option, defaulting off
+ * - 'follow_redirects' config option, defaulting on
  * - 'max_redirs' config option, defaulting to 10
  * - extended response class adds getRedirectCount() and getUrl() methods
  * - get() and post() convenience functions return body content directly
@@ -205,12 +205,28 @@ class HTTPClient extends HTTP_Request2
     /**
      * Convenience function to run a HEAD request.
      *
+     * NOTE: Will probably turn into a GET request if you let it follow redirects!
+     *       That option is only there to be flexible and may be removed in the future!
+     *
      * @return GNUsocial_HTTPResponse
      * @throws HTTP_Request2_Exception
      */
-    public function head($url, $headers=array())
+    public function head($url, $headers=array(), $follow_redirects=false)
     {
-        return $this->doRequest($url, self::METHOD_HEAD, $headers);
+        // Save the configured value for follow_redirects
+        $old_follow = $this->config['follow_redirects'];
+        try {
+            // Temporarily (possibly) override the follow_redirects setting
+            $this->config['follow_redirects'] = $follow_redirects;
+            return $this->doRequest($url, self::METHOD_HEAD, $headers);
+        } catch (Exception $e) {
+            // Let the exception go on its merry way.
+            throw $e;
+        } finally {
+            // reset to the old value
+            $this->config['follow_redirects'] = $old_follow;
+        }
+        //we've either returned or thrown exception here
     }
 
     /**
index 5b0f3dbe092fa42dbd73d01dc3e727392151d1bb..2da4fa961a6cf4962a1dafbdcbb80b72235334b4 100644 (file)
@@ -380,7 +380,7 @@ abstract class ImPlugin extends Plugin
             $parent = $notice->getParent();
             $orig_profile = $parent->getProfile();
             $nicknames = sprintf('%1$s => %2$s', $profile->nickname, $orig_profile->nickname);
-        } catch (Exception $e) {
+        } catch (NoParentNoticeException $e) {
             $nicknames = $profile->nickname;
         }
 
@@ -402,9 +402,8 @@ abstract class ImPlugin extends Plugin
             $chan = new IMChannel($this);
             $cmd->execute($chan);
             return true;
-        } else {
-            return false;
         }
+        return false;
     }
 
     /**
index eb7c5c8690160a77f0c52c0b7a36a512b7731cd8..0a46b2a50815e697647d7c87371d9faf3bcf0b42 100644 (file)
@@ -622,7 +622,7 @@ abstract class Installer
 
         $this->updateStatus("GNU social has been installed at $link");
         $this->updateStatus(
-            '<strong>DONE!</strong> You can visit your <a href="'.htmlspecialchars($link).'">new GNU social site</a> (log in as "'.htmlspecialchars($this->adminNick).'"). If this is your first GNU social install, make your experience the best possible by visiting our resource site to join the mailing list and <a href="http://gnu.io/resources/">good documentation</a>.'
+            '<strong>DONE!</strong> You can visit your <a href="'.htmlspecialchars($link).'">new GNU social site</a> (log in as "'.htmlspecialchars($this->adminNick).'"). If this is your first GNU social install, make your experience the best possible by visiting our resource site to join the <a href="https://gnu.io/social/resources/">mailing list or IRC.</a>. <a href="'.htmlspecialchars($link).'/doc/faq/">FAQ is found here</a>.'
         );
 
         return true;
index 546239ed7d3bf6de2bbf4b7421139a1d9c694690..2b8f324df2f88d5e8671c95f2e6166d07b57a380 100644 (file)
@@ -61,7 +61,7 @@ class MediaFile
 
     public function attachToNotice(Notice $notice)
     {
-        File_to_post::processNew($this->fileRecord->id, $notice->id);
+        File_to_post::processNew($this->fileRecord, $notice);
     }
 
     public function getPath()
index 80be6239c500decb6064920e21f342c924acd36d..1ed0abbe78dbfcec46365c97f73684cb2ff34b83 100644 (file)
@@ -48,6 +48,14 @@ class Nickname
      */
     const DISPLAY_FMT = '[0-9a-zA-Z_]{1,64}';
 
+    /**
+     * Simplified regex fragment for acceptable full WebFinger ID of a user
+     *
+     * We could probably use an email regex here, but mainly we are interested
+     * in matching it in our URLs, like https://social.example/user@example.com
+     */
+    const WEBFINGER_FMT = '[0-9a-zA-Z_]{1,64}\@[0-9a-zA-Z_-.]{3,255}';
+
     /**
      * Regex fragment for checking a canonical nickname.
      *
diff --git a/lib/noparentnoticeexception.php b/lib/noparentnoticeexception.php
new file mode 100644 (file)
index 0000000..fea179c
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Class for an exception when a database lookup returns no results
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Exception
+ * @package   GNUsocial
+ * @author    Mikael Nordfeldth <mmn@hethane.se>
+ * @copyright 2013 Free Software Foundation, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://www.gnu.org/software/social/
+ */
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+class NoParentNoticeException extends ServerException
+{
+    public $notice;    // The notice which has no parent
+
+    public function __construct(Notice $notice)
+    {
+        $this->notice = $notice;
+        parent::__construct(sprintf(_('No parent for notice with ID "%s".'), $this->notice->id));
+    }
+}
index 42339137526a136db70c882bf73d7d953b1e813a..94bb1e124a16570702ad0fa2b6777c744e3a4979 100644 (file)
@@ -144,6 +144,9 @@ class Plugin
                 // @fixme this will fail for things installed in local/plugins
                 // ... but then so will web links so far.
                 $path = INSTALLDIR . "/plugins/$name/locale";
+                if (!file_exists($path)) {
+                    $path = INSTALLDIR . "/local/plugins/$name/locale";
+                }
             }
             if (file_exists($path) && is_dir($path)) {
                 bindtextdomain($name, $path);
index bd5bb5a148d2290524d97e972e152f6dcc1b9672..08e03bac5a62625888a4357346b90109b6bfddde 100644 (file)
@@ -48,52 +48,13 @@ abstract class ProfileAction extends ManagedAction
 
     protected $target  = null;    // Profile that we're showing
 
-    protected function doPreparation()
+    protected function prepare(array $args=array())
     {
-        try {
-            $nickname_arg = $this->arg('nickname');
-            $nickname     = common_canonical_nickname($nickname_arg);
+        // this will call ->doPreparation() which lower classes can use
+        parent::prepare($args);
 
-            // Permanent redirect on non-canonical nickname
-
-            if ($nickname_arg != $nickname) {
-                $args = array('nickname' => $nickname);
-                if ($this->arg('page') && $this->arg('page') != 1) {
-                    $args['page'] = $this->arg['page'];
-                }
-                common_redirect(common_local_url($this->getActionName(), $args), 301);
-            }
-            $this->user = User::getKV('nickname', $nickname);
-
-            if (!$this->user) {
-                $group = Local_group::getKV('nickname', $nickname);
-                if ($group instanceof Local_group) {
-                    common_redirect($group->getProfile()->getUrl());
-                }
-                // TRANS: Client error displayed when calling a profile action without specifying a user.
-                $this->clientError(_('No such user.'), 404);
-            }
-
-            $this->target = $this->user->getProfile();
-        } catch (NicknameException $e) {
-            $id = (int)$this->arg('id');
-            $this->target = Profile::getKV('id', $id);
-
-            if (!$this->target instanceof Profile) {
-                // TRANS: Error message displayed when referring to a user without a profile.
-                $this->serverError(_m('Profile ID does not exist.'));
-            }
-
-            if ($this->target->isLocal()) {
-                // For local users when accessed by id number, redirect to
-                // the same action but using the nickname as argument.
-                common_redirect(common_local_url($this->getActionName(),
-                                                array('nickname'=>$user->getNickname())));
-            }
-        }
-
-        if ($this->target->hasRole(Profile_role::SILENCED) &&
-            (empty($this->scoped) || !$this->scoped->hasRight(Right::SILENCEUSER))) {
+        if ($this->target->hasRole(Profile_role::SILENCED)
+                && (!$this->scoped instanceof Profile || !$this->scoped->hasRight(Right::SILENCEUSER))) {
             throw new ClientException(_('This profile has been silenced by site moderators'), 403);
         }
 
@@ -104,12 +65,20 @@ abstract class ProfileAction extends ManagedAction
         $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
         common_set_returnto($this->selfUrl());
 
-        return $this->profileActionPreparation();
+        // fetch the actual stream stuff
+        $this->profileActionPreparation();
+
+        return true;
     }
 
     protected function profileActionPreparation()
     {
-        // No-op by default.
+        // Nothing to do by default.
+    }
+
+    public function getTarget()
+    {
+        return $this->target;
     }
 
     function isReadOnly($args)
index 4f4748722055aca1ebb555f60e601f7d0b23c634..d2997d12de55cad30aa1a64d40a64b75570ae413 100644 (file)
@@ -29,8 +29,6 @@
 
 if (!defined('GNUSOCIAL')) { exit(1); }
 
-define('PROFILES_PER_MINILIST', 8);
-
 /**
  * Widget to show a list of profiles, good for sidebar
  *
index 69347bc0d4970ff99da8c5d2df9881b459496f81..620a61ddd95bdd886d67b87bdd907e398b383a42 100644 (file)
@@ -64,6 +64,13 @@ class PublicGroupNav extends Menu
                                      // TRANS: Menu item title in search group navigation panel.
                                      _('Public timeline'), $this->actionName == 'public', 'nav_timeline_public');
             }
+            if (!common_config('public', 'localonly') || $this->action->getScoped() instanceof Profile) {
+                // Allow network wide view if you're logged in
+                // TRANS: Menu item in search group navigation panel.
+                $this->out->menuItem(common_local_url('networkpublic'), _m('MENU','Network'),
+                                     // TRANS: Menu item title in search group navigation panel.
+                                     _('Network public timeline'), $this->actionName == 'networkpublic', 'nav_timeline_networkpublic');
+            }
 
             // TRANS: Menu item in search group navigation panel.
             $this->out->menuItem(common_local_url('groups'), _m('MENU','Groups'),
diff --git a/lib/sitestreamaction.php b/lib/sitestreamaction.php
new file mode 100644 (file)
index 0000000..d462c49
--- /dev/null
@@ -0,0 +1,182 @@
+<?php
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+// Farther than any human will go
+
+define('MAX_PUBLIC_PAGE', 100);
+
+class SitestreamAction extends ManagedAction
+{
+    /**
+     * page of the stream we're on; default = 1
+     */
+
+    var $page = null;
+    var $notice;
+
+    protected $stream = null;
+
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
+    protected function doPreparation()
+    {
+        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
+
+        if ($this->page > MAX_PUBLIC_PAGE) {
+            // TRANS: Client error displayed when requesting a public timeline page beyond the page limit.
+            // TRANS: %s is the page limit.
+            $this->clientError(sprintf(_('Beyond the page limit (%s).'), MAX_PUBLIC_PAGE));
+        }
+
+        common_set_returnto($this->selfUrl());
+
+        $this->streamPrepare();
+
+        $this->notice = $this->stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
+                                            NOTICES_PER_PAGE + 1);
+
+        if (!$this->notice) {
+            // TRANS: Server error displayed when a public timeline cannot be retrieved.
+            $this->serverError(_('Could not retrieve public timeline.'));
+        }
+
+        if ($this->page > 1 && $this->notice->N == 0){
+            // TRANS: Client error when page not found (404).
+            $this->clientError(_('No such page.'), 404);
+        }
+
+        return true;
+    }
+
+    /**
+     * Title of the page
+     *
+     * @return page title, including page number if over 1
+     */
+    function title()
+    {
+        if ($this->page > 1) {
+            // TRANS: Title for all public timeline pages but the first.
+            // TRANS: %d is the page number.
+            return sprintf(_('Public timeline, page %d'), $this->page);
+        } else {
+            // TRANS: Title for the first public timeline page.
+            return _('Public timeline');
+        }
+    }
+
+    function extraHead()
+    {
+        parent::extraHead();
+        $rsd = common_local_url('rsd');
+
+        // RSD, http://tales.phrasewise.com/rfc/rsd
+
+        $this->element('link', array('rel' => 'EditURI',
+                                     'type' => 'application/rsd+xml',
+                                     'href' => $rsd));
+
+        if ($this->page != 1) {
+            $this->element('link', array('rel' => 'canonical',
+                                         'href' => common_local_url('public')));
+        }
+    }
+
+    /**
+     * Output <head> elements for RSS and Atom feeds
+     *
+     * @return void
+     */
+    function getFeeds()
+    {
+        return array(new Feed(Feed::JSON,
+                              common_local_url('ApiTimelinePublic',
+                                               array('format' => 'as')),
+                              // TRANS: Link description for public timeline feed.
+                              _('Public Timeline Feed (Activity Streams JSON)')),
+                    new Feed(Feed::RSS1, common_local_url('publicrss'),
+                              // TRANS: Link description for public timeline feed.
+                              _('Public Timeline Feed (RSS 1.0)')),
+                     new Feed(Feed::RSS2,
+                              common_local_url('ApiTimelinePublic',
+                                               array('format' => 'rss')),
+                              // TRANS: Link description for public timeline feed.
+                              _('Public Timeline Feed (RSS 2.0)')),
+                     new Feed(Feed::ATOM,
+                              common_local_url('ApiTimelinePublic',
+                                               array('format' => 'atom')),
+                              // TRANS: Link description for public timeline feed.
+                              _('Public Timeline Feed (Atom)')));
+    }
+
+    function showEmptyList()
+    {
+        // TRANS: Text displayed for public feed when there are no public notices.
+        $message = _('This is the public timeline for %%site.name%% but no one has posted anything yet.') . ' ';
+
+        if (common_logged_in()) {
+            // TRANS: Additional text displayed for public feed when there are no public notices for a logged in user.
+            $message .= _('Be the first to post!');
+        }
+        else {
+            if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
+                // TRANS: Additional text displayed for public feed when there are no public notices for a not logged in user.
+                $message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
+            }
+        }
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
+
+    /**
+     * Fill the content area
+     *
+     * Shows a list of the notices in the public stream, with some pagination
+     * controls.
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
+            $nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
+        } else {
+            $nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
+        }
+
+        $cnt = $nl->show();
+
+        if ($cnt == 0) {
+            $this->showEmptyList();
+        }
+
+        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
+                          $this->page, $this->action);
+    }
+
+    function showAnonymousMessage()
+    {
+        if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
+            // TRANS: Message for not logged in users at an invite-only site trying to view the public feed of notices.
+            // TRANS: This message contains Markdown links. Please mind the formatting.
+            $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+                   'based on the Free Software [StatusNet](http://status.net/) tool. ' .
+                   '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ' .
+                   '([Read more](%%doc.help%%))');
+        } else {
+            // TRANS: Message for not logged in users at a closed site trying to view the public feed of notices.
+            // TRANS: This message contains Markdown links. Please mind the formatting.
+            $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+                   'based on the Free Software [StatusNet](http://status.net/) tool.');
+        }
+        $this->elementStart('div', array('id' => 'anon_notice'));
+        $this->raw(common_markup_to_html($m));
+        $this->elementEnd('div');
+    }
+}
index dbc036c4613aa4e7dffc1429e69987f506c6b26d..f29d9559b9b7af119a2c1499b5201b1e951cf7d6 100644 (file)
@@ -628,7 +628,7 @@ function common_render_content($text, Notice $notice)
  * @param Notice $notice in-progress or complete Notice object for context
  * @return string partially-rendered HTML
  */
-function common_linkify_mentions($text, $notice)
+function common_linkify_mentions($text, Notice $notice)
 {
     $mentions = common_find_mentions($text, $notice);
 
@@ -655,7 +655,7 @@ function common_linkify_mentions($text, $notice)
     return $text;
 }
 
-function common_linkify_mention($mention)
+function common_linkify_mention(array $mention)
 {
     $output = null;
 
@@ -695,13 +695,10 @@ function common_linkify_mention($mention)
  *
  * @access private
  */
-function common_find_mentions($text, $notice)
+function common_find_mentions($text, Notice $notice)
 {
-    try {
-        $sender = Profile::getKV('id', $notice->profile_id);
-    } catch (NoProfileException $e) {
-        return array();
-    }
+    // The getProfile call throws NoProfileException on failure
+    $sender = $notice->getProfile();
 
     $mentions = array();
 
@@ -728,8 +725,8 @@ function common_find_mentions($text, $notice)
                 }
             } catch (NoProfileException $e) {
                 common_log(LOG_WARNING, sprintf('Notice %d author profile id %d does not exist', $origNotice->id, $origNotice->profile_id));
-            } catch (ServerException $e) {
-                // Probably just no parent. Should get a specific NoParentException
+            } catch (NoParentNoticeException $e) {
+                // This notice is not in reply to anything
             } catch (Exception $e) {
                 common_log(LOG_WARNING, __METHOD__ . ' got exception ' . get_class($e) . ' : ' . $e->getMessage());
             }
diff --git a/lib/xrdsoutputter.php b/lib/xrdsoutputter.php
deleted file mode 100644 (file)
index 95dc733..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Low-level generator for HTML
- *
- * 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  Output
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2008 StatusNet, Inc.
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @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);
-}
-
-require_once INSTALLDIR.'/lib/xmloutputter.php';
-
-/**
- * Low-level generator for XRDS XML
- *
- * @category Output
- * @package  StatusNet
- * @author   Craig Andrews <candrews@integralblue.com>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- * @see      Action
- * @see      XMLOutputter
- */
-class XRDSOutputter extends XMLOutputter
-{
-    public function startXRDS()
-    {
-        header('Content-Type: application/xrds+xml');
-        $this->startXML();
-        $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds'));
-    }
-    
-    public function endXRDS()
-    {
-        $this->elementEnd('XRDS');
-        $this->endXML();
-    }
-
-    /**
-     * Show service.
-     *
-     * @param string $type    XRDS type
-     * @param string $uri     URI
-     * @param array  $params  type parameters, null by default
-     * @param array  $sigs    type signatures, null by default
-     * @param string $localId local ID, null by default
-     *
-     * @return void
-     */
-    function showXrdsService($type, $uri, $params=null, $sigs=null, $localId=null)
-    {
-        $this->elementStart('Service');
-        if ($uri) {
-            $this->element('URI', null, $uri);
-        }
-        $this->element('Type', null, $type);
-        if ($params) {
-            foreach ($params as $param) {
-                $this->element('Type', null, $param);
-            }
-        }
-        if ($sigs) {
-            foreach ($sigs as $sig) {
-                $this->element('Type', null, $sig);
-            }
-        }
-        if ($localId) {
-            $this->element('LocalID', null, $localId);
-        }
-        $this->elementEnd('Service');
-    }
-}
index 0abfacd645fb34a671ce8ed18c1efd40c202d5f1..45bb18be469bbb40d6df17d033de90e2126ea398 100644 (file)
@@ -50,7 +50,7 @@ class ActivityverbAction extends ManagedAction
             throw new ServerException('A verb has not been specified.');
         }
 
-        $this->notice = Notice::getById($this->trimmed('id'));
+        $this->notice = Notice::getByID($this->trimmed('id'));
 
         if (!$this->notice->inScope($this->scoped)) {
             // TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
index 365937fedf9bf4d1a6c27bbfbce31e8d1d946415..625180d23d7d330ba5f8adfc07179deecc146662 100755 (executable)
@@ -9,6 +9,13 @@ class AntiBrutePlugin extends Plugin {
 
     const FAILED_LOGIN_IP_SECTION = 'failed_login_ip';
 
+    public function initialize()
+    {
+        // This probably needs some work. For example with IPv6 you can easily generate new IPs...
+        $client_ip = common_client_ip();
+        $this->client_ip = $client_ip[0] ?: $client_ip[1];   // [0] is proxy, [1] should be the real IP
+    }
+
     public function onStartCheckPassword($nickname, $password, &$authenticatedUser)
     {
         if (common_is_email($nickname)) {
@@ -22,9 +29,6 @@ class AntiBrutePlugin extends Plugin {
             return true;
         }
 
-        // This probably needs some work. For example with IPv6 you can easily generate new IPs...
-        $client_ip = common_client_ip();
-        $this->client_ip = $client_ip[0] ?: $client_ip[1];   // [0] is proxy, [1] should be the real IP
         $this->failed_attempts = (int)$this->unauthed_user->getPref(self::FAILED_LOGIN_IP_SECTION, $this->client_ip);
         switch (true) {
         case $this->failed_attempts >= 5:
index 1ca024e9aca29c77d829d90c8a9bdf5cd3761e0d..2e1077c22532972b8f73bad13e2a1516ba7265ec 100644 (file)
@@ -17,9 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-if (!defined('STATUSNET')) {
-    exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * Some UI extras for now...
@@ -59,24 +57,6 @@ class ModPlusPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Add ModPlus-related paths to the router table
-     *
-     * Hook for RouterInitialized event.
-     *
-     * @param URLMapper $m URL mapper
-     *
-     * @return boolean hook return
-     */
-    public function onStartInitializeRouter(URLMapper $m)
-    {
-        $m->connect('user/remote/:id',
-                array('action' => 'remoteprofile'),
-                array('id' => '[\d]+'));
-
-        return true;
-    }
-
     /**
      * Add per-profile info popup menu for author on notice lists.
      *
@@ -110,7 +90,7 @@ class ModPlusPlugin extends Plugin
     protected function showProfileOptions(HTMLOutputter $out, Profile $profile)
     {
         if (!$profile->isGroup() && !$profile->isLocal()) {
-            $target = common_local_url('remoteprofile', array('id' => $profile->id));
+            $target = common_local_url('userbyid', array('id' => $profile->getID()));
             // TRANS: Label for access to remote profile options.
             $label = _m('Remote profile options...');
             $out->elementStart('div', 'remote-profile-options');
diff --git a/plugins/ModPlus/actions/remoteprofile.php b/plugins/ModPlus/actions/remoteprofile.php
deleted file mode 100644 (file)
index 4203672..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-class RemoteProfileAction extends ShowstreamAction
-{
-    function title()
-    {
-        $base = $this->target->getBestName();
-        $host = parse_url($this->target->profileurl, PHP_URL_HOST);
-        // TRANS: Remote profile action page title.
-        // TRANS: %1$s is a username, %2$s is a hostname.
-        return sprintf(_m('%1$s on %2$s'), $base, $host);
-    }
-
-    /**
-     * Instead of showing notices, link to the original offsite profile.
-     */
-    function showNotices()
-    {
-        $url = $this->target->profileurl;
-        $host = parse_url($url, PHP_URL_HOST);
-        $markdown = sprintf(
-                // TRANS: Message on remote profile page.
-                // TRANS: This message contains Markdown links in the form [description](link).
-                // TRANS: %1$s is a profile nickname, %2$s is a hostname, %3$s is a URL.
-                _m('This remote profile is registered on another site; see [%1$s\'s original profile page on %2$s](%3$s).'),
-                $this->target->nickname,
-                $host,
-                $url);
-        $html = common_markup_to_html($markdown);
-        $this->raw($html);
-
-        if ($this->target->hasRole(Profile_role::SILENCED)) {
-            // TRANS: Message on blocked remote profile page.
-            $markdown = _m('Site moderators have silenced this profile, which prevents delivery of new messages to any users on this site.');
-            $this->raw(common_markup_to_html($markdown));
-        }else{
-
-            $pnl = new NoticeList($this->notice, $this);
-            $cnt = $pnl->show();
-            if (0 == $cnt) {
-                $this->showEmptyListMessage();
-            }
-
-            $args = array('id' => $this->target->id);
-            if (!empty($this->tag))
-            {
-                $args['tag'] = $this->tag;
-            }
-            $this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
-                              'remoteprofile', $args);
-
-        }
-    }
-
-    function getFeeds()
-    {
-        // none
-    }
-
-    /**
-     * Don't do various extra stuff, and also trim some things to avoid crawlers.
-     */
-    function extraHead()
-    {
-        $this->element('meta', array('name' => 'robots',
-                                     'content' => 'noindex,nofollow'));
-    }
-
-    function showLocalNav()
-    {
-        // skip
-    }
-
-    function showSections()
-    {
-        // skip
-    }
-
-    function showStatistics()
-    {
-        // skip
-    }
-}
index 07c9d1c182a59e38eec64f93c652838db3fa4497..4d1b95e2b76eeedd30c5d31847f73a4be2b19a23 100644 (file)
@@ -691,8 +691,8 @@ class Ostatus_profile extends Managed_DataObject
                                      $options);
             if ($saved instanceof Notice) {
                 Ostatus_source::saveNew($saved, $this, $method);
-                if (!empty($attachment)) {
-                    File_to_post::processNew($attachment->id, $saved->id);
+                if ($attachment instanceof File) {
+                    File_to_post::processNew($attachment, $saved);
                 }
             }
         } catch (Exception $e) {
index 0d093f2868fbdb588cf9d27ee9ee065a77b20c62..3ba2f4e5abb5d34354e8c659ad08d7ecf28b85ef 100644 (file)
@@ -154,7 +154,7 @@ class OpenIDPlugin extends Plugin
      *
      * @return boolean hook return
      */
-    function onEndPublicXRDS($action, &$xrdsOutputter)
+    function onEndPublicXRDS(Action $action, &$xrdsOutputter)
     {
         $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
                                                   'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
@@ -174,37 +174,6 @@ class OpenIDPlugin extends Plugin
         $xrdsOutputter->elementEnd('XRD');
     }
 
-    /**
-     * User XRDS output hook
-     *
-     * Puts the bits of code needed to discover OpenID endpoints.
-     *
-     * @param Action       $action         Action being executed
-     * @param XMLOutputter &$xrdsOutputter Output channel
-     *
-     * @return boolean hook return
-     */
-    function onEndUserXRDS($action, &$xrdsOutputter)
-    {
-        $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
-                                                  'xml:id' => 'openid',
-                                                  'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
-                                                  'version' => '2.0'));
-        $xrdsOutputter->element('Type', null, 'xri://$xrds*simple');
-
-        //consumer
-        $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/return_to',
-                                        common_local_url('finishopenidlogin'));
-
-        //provider
-        $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/signon',
-                                        common_local_url('openidserver'),
-                                        null,
-                                        null,
-                                        common_profile_url($action->user->nickname));
-        $xrdsOutputter->elementEnd('XRD');
-    }
-
     /**
      * If we're in OpenID-only mode, hide all the main menu except OpenID login.
      *
@@ -415,7 +384,7 @@ class OpenIDPlugin extends Plugin
      *
      * @return void
      */
-    function onEndShowHeadElements($action)
+    function onEndShowHeadElements(Action $action)
     {
         if ($action instanceof ShowstreamAction) {
             $action->element('link', array('rel' => 'openid2.provider',
@@ -427,6 +396,11 @@ class OpenIDPlugin extends Plugin
             $action->element('link', array('rel' => 'openid.delegate',
                                            'href' => $action->profile->profileurl));
         }
+
+        if ($action instanceof SitestreamAction) {
+            $action->element('meta', array('http-equiv' => 'X-XRDS-Location',
+                                         'content' => common_local_url('publicxrds')));
+        }
         return true;
     }
 
diff --git a/plugins/OpenID/actions/publicxrds.php b/plugins/OpenID/actions/publicxrds.php
new file mode 100644 (file)
index 0000000..25801e7
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Public XRDS for OpenID
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Robin Millette <millette@status.net>
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, 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/>.
+ */
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+require_once __DIR__.'/../openid.php';
+
+/**
+ * Public XRDS
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class PublicxrdsAction extends Action
+{
+    /**
+     * Is read only?
+     *
+     * @return boolean true
+     */
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
+    /**
+     * Class handler.
+     *
+     * @param array $args array of arguments
+     *
+     * @return nothing
+     */
+    protected function handle()
+    {
+        parent::handle();
+        $xrdsOutputter = new XRDSOutputter();
+        $xrdsOutputter->startXRDS();
+        Event::handle('StartPublicXRDS', array($this,&$xrdsOutputter));
+        Event::handle('EndPublicXRDS', array($this,&$xrdsOutputter));
+        $xrdsOutputter->endXRDS();
+    }
+}
diff --git a/plugins/OpenID/lib/xrdsoutputter.php b/plugins/OpenID/lib/xrdsoutputter.php
new file mode 100644 (file)
index 0000000..9841d9e
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Low-level generator for HTML
+ *
+ * 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  Output
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2008 StatusNet, Inc.
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @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('GNUSOCIAL')) { exit(1); }
+
+/**
+ * Low-level generator for XRDS XML
+ *
+ * @category Output
+ * @package  StatusNet
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ * @see      Action
+ * @see      XMLOutputter
+ */
+class XRDSOutputter extends XMLOutputter
+{
+    public function startXRDS()
+    {
+        header('Content-Type: application/xrds+xml');
+        $this->startXML();
+        $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds'));
+    }
+    
+    public function endXRDS()
+    {
+        $this->elementEnd('XRDS');
+        $this->endXML();
+    }
+
+    /**
+     * Show service.
+     *
+     * @param string $type    XRDS type
+     * @param string $uri     URI
+     * @param array  $params  type parameters, null by default
+     * @param array  $sigs    type signatures, null by default
+     * @param string $localId local ID, null by default
+     *
+     * @return void
+     */
+    function showXrdsService($type, $uri, $params=null, $sigs=null, $localId=null)
+    {
+        $this->elementStart('Service');
+        if ($uri) {
+            $this->element('URI', null, $uri);
+        }
+        $this->element('Type', null, $type);
+        if ($params) {
+            foreach ($params as $param) {
+                $this->element('Type', null, $param);
+            }
+        }
+        if ($sigs) {
+            foreach ($sigs as $sig) {
+                $this->element('Type', null, $sig);
+            }
+        }
+        if ($localId) {
+            $this->element('LocalID', null, $localId);
+        }
+        $this->elementEnd('Service');
+    }
+}
index b5643c1d0912f6b68767a1af0d71d744c8760f03..c337efbaeca10660b02376212f006df46cee722e 100644 (file)
@@ -161,7 +161,7 @@ class SharePlugin extends ActivityVerbHandlerPlugin
     public function extendActivity(Notice $stored, Activity $act, Profile $scoped=null)
     {
         // TODO: How to handle repeats of deleted notices?
-        $target = Notice::getById($stored->repeat_of);
+        $target = Notice::getByID($stored->repeat_of);
         // TRANS: A repeat activity's title. %1$s is repeater's nickname
         //        and %2$s is the repeated user's nickname.
         $act->title = sprintf(_('%1$s repeated a notice by %2$s'),
index 5258bfc2c981767bba9d297f33ef56905a3a08a3..45b7547ce2fe7e5b79b6fef2bba0f2686eab0cd9 100644 (file)
@@ -564,13 +564,13 @@ class TwitterImport
      * @param Notice $notice
      * @param object $status
      */
-    function saveStatusAttachments($notice, $status)
+    function saveStatusAttachments(Notice $notice, $status)
     {
         if (common_config('attachments', 'process_links')) {
             if (!empty($status->entities) && !empty($status->entities->urls)) {
                 foreach ($status->entities->urls as $url) {
                     try {
-                        File::processNew($url->url, $notice->id);
+                        File::processNew($url->url, $notice);
                     } catch (ServerException $e) {
                         // Could not process attached URL
                     }
index e5759e886c4df902cd83ab3a140d59ac91b915d8..6f8ec9397d34a72dc82cc45b903be947f0beff9f 100644 (file)
@@ -31,6 +31,10 @@ if (!defined('GNUSOCIAL')) { exit(1); }
 
 class WebFingerPlugin extends Plugin
 {
+    const OAUTH_ACCESS_TOKEN_REL    = 'http://apinamespace.org/oauth/access_token';
+    const OAUTH_REQUEST_TOKEN_REL   = 'http://apinamespace.org/oauth/request_token';
+    const OAUTH_AUTHORIZE_REL       = 'http://apinamespace.org/oauth/authorize';
+
     public $http_alias = false;
 
     public function initialize()
@@ -127,6 +131,11 @@ class WebFingerPlugin extends Plugin
                             $type,
                             true);    // isTemplate
         }
+
+        // OAuth connections
+        $links[] = new XML_XRD_Element_link(self::OAUTH_ACCESS_TOKEN_REL,  common_local_url('ApiOAuthAccessToken'));
+        $links[] = new XML_XRD_Element_link(self::OAUTH_REQUEST_TOKEN_REL, common_local_url('ApiOAuthRequestToken'));
+        $links[] = new XML_XRD_Element_link(self::OAUTH_AUTHORIZE_REL,     common_local_url('ApiOAuthAuthorize'));
     }
 
     /**
index 2974e8b2ab40e29ed30f0371652f2751868a65c1..d95ffcf0d6d0d2bd636546b276b5ace8407a49eb 100644 (file)
@@ -354,7 +354,7 @@ class XmppPlugin extends ImPlugin
             $xs->text(": ");
         } catch (InvalidUrlException $e) {
             $xs->text(sprintf(' => %s', $orig_profile->nickname));
-        } catch (Exception $e) {
+        } catch (NoParentNoticeException $e) {
             $xs->text(": ");
         }
         if (!empty($notice->rendered)) {
index c260ffaa0037cb41dc5942deae59589f5c8526b3..692cedf8d1e7396a267ea618cd1d89f7955d55c6 100755 (executable)
@@ -113,7 +113,7 @@ function readline_emulation($prompt)
 
 function console_help()
 {
-    print "Welcome to StatusNet's interactive PHP console!\n";
+    print "Welcome to GNU social's interactive PHP console!\n";
     print "Type some PHP code and it'll execute...\n";
     print "\n";
     print "Hint: return a value of any type to output it via var_export():\n";
@@ -128,8 +128,8 @@ function console_help()
 }
 
 if (CONSOLE_INTERACTIVE) {
-    print "StatusNet interactive PHP console... type ctrl+D or enter 'exit' to exit.\n";
-    $prompt = common_config('site', 'name') . '> ';
+    print "GNU social interactive PHP console... type ctrl+D or enter 'exit' to exit.\n";
+    $prompt = common_slugify(common_config('site', 'name')) . '> ';
 } else {
     $prompt = '';
 }
index 7c0e01c96d2a0ca1b8147043dfecac0bd50d35fa..949f40e79167b15db2c64987a6c8eb324b2c1564 100644 (file)
@@ -20,7 +20,11 @@ ol, ul {list-style-position: inside;}
 fieldset {border: none;}
 legend {font-weight: bold; font-size: 1.2em;}
 input, textarea, select, option {padding: 2px;}
-textarea {overflow:auto;}
+
+textarea {
+    overflow:auto;
+    resize: vertical;
+}
 
 body {
     background-color: #f2f2f2;
@@ -729,7 +733,6 @@ font-style:italic;
 
 .notice-options {
     margin-bottom: 7px;
-    margin-top: 12px;
     float: right;    
     position: relative;
 }
@@ -859,7 +862,6 @@ content: ":";
 
 .threaded-replies {
     clear: both;
-    float: left;
     width: 458px;
     margin-left: 55px;
     margin-bottom: 10px;
@@ -989,7 +991,6 @@ content: ":";
 /* old school conversation style */
 
 #conversation .notices .notices {
-    float: left;
     background-color:rgba(200, 200, 200, 0.050);
     margin-left: 15px;
     width: auto;
index 10ac168666e5bb4ea402ac38fdd9ac36730c8a3a..8af4a32cb38cf8c49b17a1bfe73f20f78a5cb2fc 100644 (file)
@@ -56,8 +56,8 @@ input, textarea, select, option {
     font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
 }
 
-a {color: #006c9b;}
-a:hover {color: #006c9b;text-decoration:underline;}
+a {color: #666;}
+a:hover {color: #a22430;text-decoration:underline;}
 
 #aside_primary a {color: rgba(0,0,0,0.8);}
 
@@ -104,7 +104,7 @@ address {
     top: 6px;
     width: 89px;
     z-index: 1001;
-    background-color:#0084B4;
+    background-color:#a22430;
 }
 
 address:hover {
@@ -195,8 +195,8 @@ address a {
 }
 
 #site_nav_global_primary a:hover {
-    color: #ED7EFF;
-    border-bottom:3px solid #ED7EFF;
+    color: #c33541;
+    border-bottom:3px solid #c33541;
     text-decoration: none;
 }
 
@@ -457,28 +457,28 @@ body#doc #content_inner p.faq-credits {
 }
 
 #input_form_nav li.current a {
-    background: -moz-linear-gradient(top,  #33bcef 0%, #019ad2 100%); /* FF3.6+ */
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#33bcef), color-stop(100%,#019ad2)); /* Chrome,Safari4+ */
-    background: -webkit-linear-gradient(top,  #33bcef 0%,#019ad2 100%); /* Chrome10+,Safari5.1+ */
-    background: -o-linear-gradient(top,  #33bcef 0%,#019ad2 100%); /* Opera 11.10+ */
-    background: -ms-linear-gradient(top,  #33bcef 0%,#019ad2 100%); /* IE10+ */
-    background: linear-gradient(to bottom,  #33bcef 0%,#019ad2 100%); /* W3C */
+    background: -moz-linear-gradient(top,  #a22430 0%, #c33541 100%); /* FF3.6+ */
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#a22430), color-stop(100%,#c33541)); /* Chrome,Safari4+ */
+    background: -webkit-linear-gradient(top,  #a22430 0%,#c33541 100%); /* Chrome10+,Safari5.1+ */
+    background: -o-linear-gradient(top,  #a22430 0%,#c33541 100%); /* Opera 11.10+ */
+    background: -ms-linear-gradient(top,  #a22430 0%,#c33541 100%); /* IE10+ */
+    background: linear-gradient(to bottom,  #a22430 0%,#c33541 100%); /* W3C */
     background-repeat: repeat-x;
     text-shadow: rgba(0, 0, 0, 0.25) 0px -1px 1px;
-    border-color:#096eb3;
+    border-color:#c33541;
     color:#fff;
 }
 
 #input_form_nav li:hover a {
-    background: -moz-linear-gradient(top,  #2daddc 0%, #0271bf 100%); /* FF3.6+ */
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#2daddc), color-stop(100%,#0271bf)); /* Chrome,Safari4+ */
-    background: -webkit-linear-gradient(top,  #2daddc 0%,#0271bf 100%); /* Chrome10+,Safari5.1+ */
-    background: -o-linear-gradient(top,  #2daddc 0%,#0271bf 100%); /* Opera 11.10+ */
-    background: -ms-linear-gradient(top,  #2daddc 0%,#0271bf 100%); /* IE10+ */
-    background: linear-gradient(to bottom,  #2daddc 0%,#0271bf 100%); /* W3C */
+    background: -moz-linear-gradient(top,  #c33541 0%, #d44652 100%); /* FF3.6+ */
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#c33541), color-stop(100%,#d44652)); /* Chrome,Safari4+ */
+    background: -webkit-linear-gradient(top,  #c33541 0%,#d44652 100%); /* Chrome10+,Safari5.1+ */
+    background: -o-linear-gradient(top,  #c33541 0%,#d44652 100%); /* Opera 11.10+ */
+    background: -ms-linear-gradient(top,  #c33541 0%,#d44652 100%); /* IE10+ */
+    background: linear-gradient(to bottom,  #c33541 0%,#d44652 100%); /* W3C */
     background-repeat: repeat-x;
     text-shadow: rgba(0, 0, 0, 0.25) 0px -1px 1px;
-    border-color:#096eb3;
+    border-color:#c33541;
     color:#fff;
 }
 
@@ -491,7 +491,7 @@ body#doc #content_inner p.faq-credits {
 }
 
 .form_notice .error, .form_notice .success, .form_notice .notice-status {
-    background-color: #33bcef;
+    background-color: #a22430;
     border: 0 none;
     border-radius: 0;
     color: rgba(255, 255, 255, 0.9);
@@ -682,10 +682,16 @@ ul.profile_list li {
     margin-top: 0;
 }
 
-.form_notice_placeholder .placeholder,
-.form_notice textarea ,
-.input_form .form_settings li input,
-.input_form .form_settings li textarea,
+.threaded-replies {
+    margin-top: 4px;
+    float: none;
+}
+
+#conversation .notices .notices {
+    float: none;
+    margin-left: 55px;
+}
+
 .threaded-replies .placeholder { /* TODO combine all these declarations */
     border-radius: 4px;
     box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2);
@@ -1314,6 +1320,9 @@ div.entry-content a.response:after {
     right: 10px !important;
 }
 
+#realtime_actions {
+    padding-top: 0px !important;
+}
 
 #realtime_actions button {
     margin-right: 5px;
@@ -1399,6 +1408,10 @@ div.entry-content a.response:after {
     color: rgba(0, 0, 0, 0.3);
 }
 
+.notice > footer {
+    margin-bottom: 5px;
+}
+
 /* loading */
 
 #infscr-loading {
@@ -1545,16 +1558,18 @@ div.entry-content a.response:after {
     border: 1px solid #CDD1DD;
 }
 
-.profile_list .form_group_leave input.submit,
-.profile_list .form_user_unsubscribe input.submit {
+.form_user_unsubscribe input.submit {
     background-position: 2px -1250px;
 }
 
-.profile_list .form_group_join input.submit,
-.profile_list .form_user_subscribe input.submit {
+.form_user_subscribe input.submit {
     background-position: 2px -1184px;
 }
 
+.form_user_block input.submit {
+    background-position: 2px -920px;
+}
+
 .profile_list .entity_actions input:hover {
     width: auto;
     padding-right: 10px;
@@ -1615,12 +1630,12 @@ border: 1px solid rgba(0, 0, 0, 0.3);
     font-weight: 700;
     color: #FFFFFF;
     line-height: 16px;
-    background: -moz-linear-gradient(top,  #33bcef 0%, #019ad2 100%); /* FF3.6+ */
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#33bcef), color-stop(100%,#019ad2)); /* Chrome,Safari4+ */
-    background: -webkit-linear-gradient(top,  #33bcef 0%,#019ad2 100%); /* Chrome10+,Safari5.1+ */
-    background: -o-linear-gradient(top,  #33bcef 0%,#019ad2 100%); /* Opera 11.10+ */
-    background: -ms-linear-gradient(top,  #33bcef 0%,#019ad2 100%); /* IE10+ */
-    background: linear-gradient(to bottom,  #33bcef 0%,#019ad2 100%); /* W3C */
+    background: -moz-linear-gradient(top,  #a22430 0%, #c33541 100%); /* FF3.6+ */
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#a22430), color-stop(100%,#c33541)); /* Chrome,Safari4+ */
+    background: -webkit-linear-gradient(top,  #a22430 0%,#c33541 100%); /* Chrome10+,Safari5.1+ */
+    background: -o-linear-gradient(top,  #a22430 0%,#c33541 100%); /* Opera 11.10+ */
+    background: -ms-linear-gradient(top,  #a22430 0%,#c33541 100%); /* IE10+ */
+    background: linear-gradient(to bottom,  #a22430 0%,#c33541 100%); /* W3C */
     background-repeat: repeat-x;
     padding-top: 5px;
     padding-right: 10px;
@@ -1630,10 +1645,10 @@ border: 1px solid rgba(0, 0, 0, 0.3);
     border-right-width: 1px;
     border-bottom-width: 1px;
     border-left-width: 1px;
-    border-top-color: #057ED0;
-    border-right-color: #057ED0;
-    border-bottom-color: #057ED0;
-    border-left-color: #057ED0;
+    border-top-color: #c33541;
+    border-right-color: #c33541;
+    border-bottom-color: #c33541;
+    border-left-color: #c33541;
     border-top-style: solid;
     border-right-style: solid;
     border-bottom-style: solid;
@@ -1657,14 +1672,14 @@ border: 1px solid rgba(0, 0, 0, 0.3);
 .entity_tag .dialogbox input.submit_dialogbox:hover,
 .form_repeat.dialogbox input.submit_dialogbox:hover
 {
-    background: -moz-linear-gradient(top,  #2daddc 0%, #0271bf 100%); /* FF3.6+ */
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#2daddc), color-stop(100%,#0271bf)); /* Chrome,Safari4+ */
-    background: -webkit-linear-gradient(top,  #2daddc 0%,#0271bf 100%); /* Chrome10+,Safari5.1+ */
-    background: -o-linear-gradient(top,  #2daddc 0%,#0271bf 100%); /* Opera 11.10+ */
-    background: -ms-linear-gradient(top,  #2daddc 0%,#0271bf 100%); /* IE10+ */
-    background: linear-gradient(to bottom,  #2daddc 0%,#0271bf 100%); /* W3C */
+    background: -moz-linear-gradient(top,  #c33541 0%, #d44652 100%); /* FF3.6+ */
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#c33541), color-stop(100%,#d44652)); /* Chrome,Safari4+ */
+    background: -webkit-linear-gradient(top,  #c33541 0%,#d44652 100%); /* Chrome10+,Safari5.1+ */
+    background: -o-linear-gradient(top,  #c33541 0%,#d44652 100%); /* Opera 11.10+ */
+    background: -ms-linear-gradient(top,  #c33541 0%,#d44652 100%); /* IE10+ */
+    background: linear-gradient(to bottom,  #c33541 0%,#d44652 100%); /* W3C */
     background-repeat: repeat-x;
-    border-color:#096eb3;
+    border-color:#c33541;
     color:#fff;
 }
 
index af7a8481315353bfd882da21ed8664d593fa6ba4..4f7eb37d09de09f7af448d435faf7550458d7f6c 100755 (executable)
Binary files a/theme/neo-quitter/favicon.ico and b/theme/neo-quitter/favicon.ico differ