summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--EVENTS.txt147
-rw-r--r--README37
-rw-r--r--actions/accesstoken.php28
-rw-r--r--actions/all.php82
-rw-r--r--actions/allrss.php1
-rw-r--r--actions/api.php89
-rw-r--r--actions/avatarsettings.php10
-rw-r--r--actions/confirmaddress.php6
-rw-r--r--actions/doc.php26
-rw-r--r--actions/editgroup.php10
-rw-r--r--actions/emailsettings.php6
-rw-r--r--actions/favorited.php3
-rw-r--r--actions/favoritesrss.php11
-rw-r--r--actions/finishremotesubscribe.php313
-rw-r--r--actions/foaf.php14
-rw-r--r--actions/grouplogo.php2
-rw-r--r--actions/grouprss.php1
-rw-r--r--actions/groupsearch.php9
-rw-r--r--actions/invite.php8
-rw-r--r--actions/login.php19
-rw-r--r--actions/logout.php2
-rw-r--r--actions/newgroup.php4
-rw-r--r--actions/newmessage.php7
-rw-r--r--actions/newnotice.php15
-rw-r--r--actions/noticesearch.php12
-rw-r--r--actions/othersettings.php33
-rw-r--r--actions/passwordsettings.php6
-rw-r--r--actions/peoplesearch.php6
-rw-r--r--actions/postnotice.php109
-rw-r--r--actions/profilesettings.php22
-rw-r--r--actions/public.php30
-rw-r--r--actions/publicrss.php18
-rw-r--r--actions/publictagcloud.php3
-rw-r--r--actions/register.php46
-rw-r--r--actions/remotesubscribe.php336
-rw-r--r--actions/replies.php4
-rw-r--r--actions/repliesrss.php1
-rw-r--r--actions/requesttoken.php21
-rw-r--r--actions/showfavorites.php4
-rw-r--r--actions/showgroup.php5
-rw-r--r--actions/shownotice.php8
-rw-r--r--actions/showstream.php17
-rw-r--r--actions/smssettings.php6
-rw-r--r--actions/subscribers.php4
-rw-r--r--actions/subscriptions.php6
-rw-r--r--actions/twitapidirect_messages.php7
-rw-r--r--actions/twitapigroups.php124
-rw-r--r--actions/twitapistatuses.php17
-rw-r--r--actions/updateprofile.php202
-rw-r--r--actions/userauthorization.php421
-rw-r--r--actions/userrss.php5
-rw-r--r--actions/xrds.php108
-rw-r--r--classes/Config.php131
-rw-r--r--classes/Deleted_notice.php46
-rw-r--r--classes/File.php49
-rw-r--r--classes/File_oembed.php21
-rw-r--r--classes/Message.php51
-rw-r--r--classes/Notice.php47
-rw-r--r--classes/Profile.php21
-rw-r--r--classes/User.php78
-rw-r--r--classes/User_group.php18
-rw-r--r--classes/User_role.php48
-rw-r--r--classes/statusnet.ini39
-rw-r--r--config.php.sample5
-rw-r--r--db/08to09.sql34
-rw-r--r--db/08to09_pg.sql40
-rw-r--r--db/sms_carrier.sql3
-rw-r--r--db/statusnet.sql43
-rw-r--r--db/statusnet_pg.sql32
-rw-r--r--doc-src/bookmarklet4
-rw-r--r--doc-src/contact2
-rw-r--r--doc-src/help1
-rw-r--r--doc-src/im4
-rw-r--r--extlib/Auth/OpenID/BigMath.php2
-rw-r--r--extlib/Auth/Yadis/XML.php2
-rw-r--r--extlib/OAuth.php5
-rw-r--r--extlib/PEAR.php2
-rw-r--r--extlib/Services/oEmbed.php10
-rwxr-xr-xextlib/libomb/base_url_xrds_mapper.php51
-rw-r--r--extlib/libomb/constants.php58
-rwxr-xr-xextlib/libomb/datastore.php200
-rw-r--r--extlib/libomb/helper.php99
-rwxr-xr-xextlib/libomb/invalidparameterexception.php32
-rwxr-xr-xextlib/libomb/invalidyadisexception.php31
-rwxr-xr-xextlib/libomb/notice.php272
-rwxr-xr-xextlib/libomb/omb_yadis_xrds.php196
-rwxr-xr-xextlib/libomb/plain_xrds_writer.php124
-rwxr-xr-xextlib/libomb/profile.php317
-rwxr-xr-xextlib/libomb/remoteserviceexception.php42
-rwxr-xr-xextlib/libomb/service_consumer.php430
-rwxr-xr-xextlib/libomb/service_provider.php425
-rwxr-xr-xextlib/libomb/unsupportedserviceexception.php31
-rwxr-xr-xextlib/libomb/xrds_mapper.php33
-rwxr-xr-xextlib/libomb/xrds_writer.php33
-rw-r--r--index.php124
-rw-r--r--install.php463
-rw-r--r--js/util.js28
-rw-r--r--lib/Shorturl_api.php66
-rw-r--r--lib/accountsettingsaction.php61
-rw-r--r--lib/action.php16
-rw-r--r--lib/command.php121
-rw-r--r--lib/commandinterpreter.php58
-rw-r--r--lib/common.php61
-rw-r--r--lib/curlclient.php179
-rw-r--r--lib/designsettings.php3
-rw-r--r--lib/facebookaction.php20
-rw-r--r--lib/facebookutil.php7
-rw-r--r--lib/galleryaction.php5
-rw-r--r--lib/groupeditform.php24
-rw-r--r--lib/htmloutputter.php25
-rw-r--r--lib/httpclient.php122
-rw-r--r--lib/logingroupnav.php35
-rw-r--r--lib/mail.php16
-rw-r--r--lib/messageform.php17
-rw-r--r--lib/noticeform.php34
-rw-r--r--lib/noticelist.php27
-rw-r--r--lib/oauthstore.php351
-rw-r--r--lib/omb.php329
-rw-r--r--lib/plugin.php14
-rw-r--r--lib/router.php13
-rw-r--r--lib/rssaction.php59
-rw-r--r--lib/settingsaction.php4
-rw-r--r--lib/twitter.php7
-rw-r--r--lib/twitterapi.php19
-rw-r--r--lib/twitteroauthclient.php8
-rw-r--r--lib/unqueuemanager.php9
-rw-r--r--lib/util.php132
-rw-r--r--plugins/Autocomplete/Autocomplete.js51
-rw-r--r--plugins/Autocomplete/AutocompletePlugin.php16
-rw-r--r--plugins/Autocomplete/autocomplete.php136
-rw-r--r--plugins/Autocomplete/readme.txt2
-rw-r--r--plugins/InfiniteScroll/InfiniteScrollPlugin.php2
-rw-r--r--plugins/InfiniteScroll/infinitescroll.js2
-rw-r--r--plugins/InfiniteScroll/jquery.infinitescroll.js22
-rw-r--r--plugins/LilUrl/LilUrlPlugin.php64
-rw-r--r--plugins/LinkbackPlugin.php15
-rw-r--r--plugins/Meteor/meteorupdater.js5
-rw-r--r--plugins/OpenID/OpenIDPlugin.php225
-rw-r--r--plugins/OpenID/User_openid.php (renamed from classes/User_openid.php)13
-rw-r--r--plugins/OpenID/doc-src/openid (renamed from doc-src/openid)0
-rw-r--r--plugins/OpenID/finishaddopenid.php (renamed from actions/finishaddopenid.php)2
-rw-r--r--plugins/OpenID/finishopenidlogin.php (renamed from actions/finishopenidlogin.php)6
-rw-r--r--plugins/OpenID/openid.php (renamed from lib/openid.php)2
-rw-r--r--plugins/OpenID/openidlogin.php (renamed from actions/openidlogin.php)12
-rw-r--r--plugins/OpenID/openidsettings.php (renamed from actions/openidsettings.php)14
-rw-r--r--plugins/OpenID/publicxrds.php (renamed from actions/publicxrds.php)2
-rw-r--r--plugins/PiwikAnalyticsPlugin.php2
-rw-r--r--plugins/PtitUrl/PtitUrlPlugin.php62
-rw-r--r--plugins/PubSubHubBub/PubSubHubBubPlugin.php122
-rw-r--r--plugins/PubSubHubBub/publisher.php86
-rw-r--r--plugins/Realtime/RealtimePlugin.php172
-rw-r--r--plugins/Realtime/icon_external.gifbin0 -> 90 bytes
-rw-r--r--plugins/Realtime/jquery.getUrlParam.js72
-rw-r--r--plugins/Realtime/realtimeupdate.js94
-rw-r--r--plugins/SimpleUrl/SimpleUrlPlugin.php79
-rw-r--r--plugins/TightUrl/TightUrlPlugin.php62
-rw-r--r--plugins/recaptcha/README2
-rwxr-xr-xscripts/getvaliddaemons.php34
-rwxr-xr-xscripts/maildaemon.php22
-rwxr-xr-xscripts/ombqueuehandler.php2
-rwxr-xr-xscripts/pluginqueuehandler.php58
-rwxr-xr-xscripts/startdaemons.sh2
-rwxr-xr-xscripts/synctwitterfriends.php2
-rwxr-xr-xscripts/twitterqueuehandler.php2
-rwxr-xr-xscripts/twitterstatusfetcher.php2
-rwxr-xr-xscripts/xmppdaemon.php6
-rw-r--r--tests/URLDetectionTest.php222
-rw-r--r--theme/base/css/display.css23
-rw-r--r--theme/biz/css/base.css25
-rw-r--r--theme/biz/css/display.css110
-rw-r--r--theme/biz/logo.pngbin2228 -> 6389 bytes
-rw-r--r--theme/cloudy/css/display.css25
-rw-r--r--theme/cloudy/logo.pngbin2228 -> 6389 bytes
-rw-r--r--theme/default/css/display.css7
-rw-r--r--theme/h4ck3r/logo.pngbin2228 -> 6389 bytes
-rw-r--r--theme/identica/css/display.css3
-rw-r--r--theme/pigeonthoughts/logo.pngbin2228 -> 6389 bytes
-rw-r--r--theme/readme.txt2
178 files changed, 7291 insertions, 2600 deletions
diff --git a/EVENTS.txt b/EVENTS.txt
index 68cb28603..74923dcc0 100644
--- a/EVENTS.txt
+++ b/EVENTS.txt
@@ -32,10 +32,10 @@ StartShowLaconicaStyles: backwards compatibility; deprecated
EndShowLaconicaStyles: backwards compatibility; deprecated
- $action: the current action
-StartShowUAStyles: Showing custom UA Style links
+StartShowUAStyles: Showing custom User-Agent style links
- $action: the current action
-EndShowUAStyles: End showing custom UA Style links; good place to add user-agent (e.g., filter, -webkit, -moz) specific styles
+EndShowUAStyles: End showing custom User-Agent links; good place to add user-agent (e.g., filter, -webkit, -moz) specific styles
- $action: the current action
StartShowScripts: Showing JavaScript links
@@ -134,3 +134,146 @@ StartAddressData: Allows the site owner to provide additional information about
EndAddressData: At the end of <address>
- $action: the current action
+
+StartLoginGroupNav: Before showing the login and register navigation menu
+- $action: the current action
+
+EndLoginGroupNav: After showing the login and register navigation menu
+- $action: the current action
+
+StartAccountSettingsNav: Before showing the account settings menu
+- $action: the current action
+
+EndAccountSettingsNav: After showing the account settings menu
+- $action: the current action
+
+Autoload: When trying to autoload a class
+- $cls: the class being sought. A plugin might require_once the file for the class.
+
+SensitiveAction: determines if an action is 'sensitive' and should use SSL
+- $action: name of the action, like 'login'
+- $sensitive: flag for whether this is a sensitive action
+
+LoginAction: determines if an action is a 'login' action (OK for public view in private mode)
+- $action: name of the action, like 'register'
+- $login: flag for whether this is a login action
+
+StartShowHead: called before showing the <head> element and children
+- $action: action object being show
+
+EndShowHead: called after showing the <head> element (and </head>)
+- $action: action object being shown
+
+StartShowBody: called before showing the <body> element and children
+- $action: action object being shown
+
+EndShowBody: called after showing the <body> element (and </body>)
+- $action: action object being shown
+
+StartPersonalGroupNav: beginning of personal group nav menu
+- $action: action object being shown
+
+EndPersonalGroupNav: end of personal group nav menu (good place to add a menu item)
+- $action: action object being shown
+
+StartEndHTML: just before the </html> tag
+- $action: action object being shown
+
+EndEndHTML: just after the </html> tag
+- $action: action object being shown
+
+StartShowDesign: just before showing a site, user, or group design
+- $action: action object being shown
+
+EndShowDesign: just after showing a site, user, or group design
+- $action: action object being shown
+
+StartShowExportData: just before showing the <div> with export data (feeds)
+- $action: action object being shown
+
+EndShowExportData: just after showing the <div> with export data (feeds)
+- $action: action object being shown
+
+StartShowNoticeItem: just before showing the notice item
+- $action: action object being shown
+
+EndShowNoticeItem: just after showing the notice item
+- $action: action object being shown
+
+StartShowPageNotice: just before showing the page notice (instructions or error)
+- $action: action object being shown
+
+EndShowPageNotice: just after showing the page notice (instructions or error)
+- $action: action object being shown
+
+StartShowPageTitle: just before showing the main h1 title of a page (only for registration)
+- $action: action object being shown
+
+StartProfileFormData: just before showing text entry fields on profile settings page
+- $action: action object being shown
+
+EndProfileFormData: just after showing text entry fields on profile settings page
+- $action: action object being shown
+
+StartProfileSaveForm: before starting to save a profile settings form
+- $action: action object being shown
+
+EndProfileSaveForm: after saving a profile settings form (after commit, no profile or user object!)
+- $action: action object being shown
+
+StartRegistrationFormData: just before showing text entry fields on registration page
+- $action: action object being shown
+
+EndRegistrationFormData: just after showing text entry fields on registration page
+- $action: action object being shown
+
+StartRegistrationTry: before validating and saving a new user
+- $action: action object being shown
+
+EndRegistrationTry: after saving a new user (note: no profile or user object!)
+- $action: action object being shown
+
+StartNewQueueManager: before trying to start a new queue manager; good for plugins implementing new queue manager classes
+- $qm: empty queue manager to set
+
+RedirectToLogin: event when we force a redirect to login (like when going to a settings page on a remembered login)
+- $action: action object being shown
+- $user: current user
+
+StartLoadDoc: before loading a help doc (hook this to show your own documentation)
+- $title: title of the document
+- $output: HTML output to show
+
+EndLoadDoc: after loading a help doc (hook this to modify other documentation)
+- $title: title of the document
+- $output: HTML output to show
+
+StartApiRss: after the rss <channel> element is started
+- $action: action object being shown
+
+StartApiAtom: after the <feed> element is started
+- $action: action object being shown
+
+StartEnqueueNotice: about to add a notice to the queues (good place to add a new transport)
+- $notice: the notice being added
+- &$transports: modifiable list of transports (as strings) to queue for
+
+EndEnqueueNotice: after adding a notice to the queues
+- $notice: the notice being added
+- $transports: modifiable list of transports to use
+
+UnqueueHandleNotice: Handle a notice when no queue manager is available
+- $notice: the notice to handle
+- $queue: the "queue" that is being executed
+
+GetValidDaemons: Just before determining which daemons to run
+- &$daemons: modifiable list of daemon scripts to run, filenames relative to scripts/
+
+HandleQueuedNotice: Handle a queued notice at queue time (or immediately if no queue)
+- &$notice: notice to handle
+
+StartShowHeadElements: Right after the <head> tag
+- $action: the current action
+
+EndShowHeadElements: Right before the </head> tag; put <script>s here if you need them in <head>
+- $action: the current action
diff --git a/README b/README
index 3dd365e1f..f3b2528b8 100644
--- a/README
+++ b/README
@@ -146,6 +146,7 @@ Your PHP installation must include the following PHP extensions:
- GD. For scaling down avatar images.
- mbstring. For handling Unicode (UTF-8) encoded strings.
- gettext. For multiple languages. Default on many PHP installs.
+- tidy. Used to clean up HTML/URLs for the URL shortener to consume.
For some functionality, you will also need the following extensions:
@@ -967,8 +968,6 @@ closed: If set to 'true', will disallow registration on your site.
the service, *then* set this variable to 'true'.
inviteonly: If set to 'true', will only allow registration if the user
was invited by an existing user.
-openidonly: If set to 'true', will only allow registrations and logins
- through OpenID.
private: If set to 'true', anonymous users will be redirected to the
'login' page. Also, API methods that normally require no
authentication will require it. Note that this does not turn
@@ -996,6 +995,9 @@ shorturllength: Length of URL at which URLs in a message exceeding 140
dupelimit: minimum time allowed for one person to say the same thing
twice. Default 60s. Anything lower is considered a user
or UI error.
+textlimit: default max size for texts in the site. Defaults to 140.
+ 0 means no limit. Can be fine-tuned for notices, messages,
+ profile bios and group descriptions.
db
--
@@ -1196,14 +1198,6 @@ For configuring invites.
enabled: Whether to allow users to send invites. Default true.
-openid
-------
-
-For configuring OpenID.
-
-enabled: Whether to allow users to register and login using OpenID. Default
- true.
-
tag
---
@@ -1330,6 +1324,8 @@ banned: an array of usernames and/or profile IDs of 'banned' profiles.
The site will reject any notices by these users -- they will
not be accepted at all. (Compare with blacklisted users above,
whose posts just won't show up in the public stream.)
+biolimit: max character length of bio; 0 means no limit; null means to use
+ the site text limit default.
newuser
-------
@@ -1426,6 +1422,9 @@ Options for group functionality.
maxaliases: maximum number of aliases a group can have. Default 3. Set
to 0 or less to prevent aliases in a group.
+desclimit: maximum number of characters to allow in group descriptions.
+ null (default) means to use the site-wide text limits. 0
+ means no limit.
oohembed
--------
@@ -1504,6 +1503,24 @@ linkcolor: Hex color of all links.
backgroundimage: Image to use for the background.
disposition: Flags for whether or not to tile the background image.
+notice
+------
+
+Configuration options specific to notices.
+
+contentlimit: max length of the plain-text content of a notice.
+ Default is null, meaning to use the site-wide text limit.
+ 0 means no limit.
+
+message
+-------
+
+Configuration options specific to messages.
+
+contentlimit: max length of the plain-text content of a message.
+ Default is null, meaning to use the site-wide text limit.
+ 0 means no limit.
+
Plugins
=======
diff --git a/actions/accesstoken.php b/actions/accesstoken.php
index c99aaeded..76bd40473 100644
--- a/actions/accesstoken.php
+++ b/actions/accesstoken.php
@@ -1,6 +1,6 @@
<?php
/**
- * Access token class.
+ * Access token class
*
* PHP version 5
*
@@ -32,10 +32,11 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
require_once INSTALLDIR.'/lib/omb.php';
/**
- * Access token class.
+ * Access token class
*
* @category Action
* @package StatusNet
@@ -47,28 +48,23 @@ require_once INSTALLDIR.'/lib/omb.php';
class AccesstokenAction extends Action
{
/**
- * Class handler.
+ * Class handler
*
* @param array $args query arguments
*
- * @return boolean false if user doesn't exist
- */
+ * @return nothing
+ *
+ **/
function handle($args)
{
parent::handle($args);
try {
- common_debug('getting request from env variables', __FILE__);
- common_remove_magic_from_request();
- $req = OAuthRequest::from_request('POST', common_local_url('accesstoken'));
- common_debug('getting a server', __FILE__);
- $server = omb_oauth_server();
- common_debug('fetching the access token', __FILE__);
- $token = $server->fetch_access_token($req);
- common_debug('got this token: "'.print_r($token, true).'"', __FILE__);
- common_debug('printing the access token', __FILE__);
- print $token;
- } catch (OAuthException $e) {
+ $srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
+ omb_oauth_server());
+ $srv->writeAccessToken();
+ } catch (Exception $e) {
$this->serverError($e->getMessage());
}
}
}
+?>
diff --git a/actions/all.php b/actions/all.php
index bfde3a7e4..f1786462e 100644
--- a/actions/all.php
+++ b/actions/all.php
@@ -1,5 +1,5 @@
<?php
-/*
+/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
*
@@ -15,9 +15,25 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Actions
+ * @package Actions
+ * @author Evan Prodromou <evan@status.net>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <millette@controlyourself.ca>
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @author Meitar Moscovitz <meitarm@gmail.com>
+ * @author Sarven Capadisli <csarven@status.net>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Zach Copley <zach@controlyourself.ca>
+ * @license GNU Affero General Public License http://www.gnu.org/licenses/
+ * @link http://status.net
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
require_once INSTALLDIR.'/lib/personalgroupnav.php';
require_once INSTALLDIR.'/lib/noticelist.php';
@@ -43,8 +59,8 @@ class AllAction extends ProfileAction
$this->notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
}
- if($this->page > 1 && $this->notice->N == 0){
- $this->serverError(_('No such page'),$code=404);
+ if ($this->page > 1 && $this->notice->N == 0) {
+ $this->serverError(_('No such page'), $code = 404);
}
return true;
@@ -73,20 +89,33 @@ class AllAction extends ProfileAction
function getFeeds()
{
- return array(new Feed(Feed::RSS1,
- common_local_url('allrss', array('nickname' =>
- $this->user->nickname)),
- sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
- new Feed(Feed::RSS2,
- common_local_url('api', array('apiaction' => 'statuses',
- 'method' => 'friends_timeline',
- 'argument' => $this->user->nickname.'.rss')),
- sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
- new Feed(Feed::ATOM,
- common_local_url('api', array('apiaction' => 'statuses',
- 'method' => 'friends_timeline',
- 'argument' => $this->user->nickname.'.atom')),
- sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname)));
+ return array(
+ new Feed(Feed::RSS1,
+ common_local_url(
+ 'allrss', array(
+ 'nickname' =>
+ $this->user->nickname)
+ ),
+ sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
+ new Feed(Feed::RSS2,
+ common_local_url(
+ 'api', array(
+ 'apiaction' => 'statuses',
+ 'method' => 'friends_timeline',
+ 'argument' => $this->user->nickname.'.rss'
+ )
+ ),
+ sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
+ new Feed(Feed::ATOM,
+ common_local_url(
+ 'api', array(
+ 'apiaction' => 'statuses',
+ 'method' => 'friends_timeline',
+ 'argument' => $this->user->nickname.'.atom'
+ )
+ ),
+ sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))
+ );
}
function showLocalNav()
@@ -106,11 +135,8 @@ class AllAction extends ProfileAction
} else {
$message .= sprintf(_('You can try to [nudge %s](../%s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
}
- }
- else {
- $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and then nudge %s or post a notice to his or her attention.'),
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
- $this->user->nickname);
+ } else {
+ $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
@@ -128,17 +154,19 @@ class AllAction extends ProfileAction
$this->showEmptyListMessage();
}
- $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
- $this->page, 'all', array('nickname' => $this->user->nickname));
+ $this->pagination(
+ $this->page > 1, $cnt > NOTICES_PER_PAGE,
+ $this->page, 'all', array('nickname' => $this->user->nickname)
+ );
}
function showPageTitle()
{
$user =& common_current_user();
if ($user && ($user->id == $this->user->id)) {
- $this->element('h1', NULL, _("You and friends"));
+ $this->element('h1', null, _("You and friends"));
} else {
- $this->element('h1', NULL, sprintf(_('%s and friends'), $this->user->nickname));
+ $this->element('h1', null, sprintf(_('%s and friends'), $this->user->nickname));
}
}
diff --git a/actions/allrss.php b/actions/allrss.php
index 57efb73f0..28b1be27d 100644
--- a/actions/allrss.php
+++ b/actions/allrss.php
@@ -68,6 +68,7 @@ class AllrssAction extends Rss10Action
$this->clientError(_('No such user.'));
return false;
} else {
+ $this->notices = $this->getNotices($this->limit);
return true;
}
}
diff --git a/actions/api.php b/actions/api.php
index f425a8dcd..d570bb017 100644
--- a/actions/api.php
+++ b/actions/api.php
@@ -1,5 +1,5 @@
<?php
-/*
+/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
*
@@ -15,9 +15,27 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Actions
+ * @package Actions
+ * @author Evan Prodromou <evan@status.net>
+ * @author Brenda Wallace <shiny@cpan.org>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Robin Millette <millette@controlyourself.ca>
+ * @author Tom Adams <tom@holizz.com>
+ * @author Christopher Vollick <psycotica0@gmail.com>
+ * @author CiaranG <ciaran@ciarang.com>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Gina Haeussge <osd@foosel.net>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Sarven Capadisli <csarven@status.net>
+ * @license GNU Affero General Public License http://www.gnu.org/licenses/
+ * @link http://status.net
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
class ApiAction extends Action
{
@@ -27,6 +45,8 @@ class ApiAction extends Action
var $api_arg;
var $api_method;
var $api_action;
+ var $auth_user;
+ var $auth_pw;
function handle($args)
{
@@ -35,6 +55,7 @@ class ApiAction extends Action
$this->api_action = $this->arg('apiaction');
$method = $this->arg('method');
$argument = $this->arg('argument');
+ $this->basic_auth_process_header();
if (isset($argument)) {
$cmdext = explode('.', $argument);
@@ -43,30 +64,30 @@ class ApiAction extends Action
$this->content_type = strtolower($cmdext[1]);
} else {
- # Requested format / content-type will be an extension on the method
+ //Requested format / content-type will be an extension on the method
$cmdext = explode('.', $method);
$this->api_method = $cmdext[0];
$this->content_type = strtolower($cmdext[1]);
}
if ($this->requires_auth()) {
- if (!isset($_SERVER['PHP_AUTH_USER'])) {
+ if (!isset($this->auth_user)) {
- # This header makes basic auth go
+ //This header makes basic auth go
header('WWW-Authenticate: Basic realm="StatusNet API"');
- # If the user hits cancel -- bam!
+ //If the user hits cancel -- bam!
$this->show_basic_auth_error();
} else {
- $nickname = $_SERVER['PHP_AUTH_USER'];
- $password = $_SERVER['PHP_AUTH_PW'];
+ $nickname = $this->auth_user;
+ $password = $this->auth_pw;
$user = common_check_user($nickname, $password);
if ($user) {
$this->user = $user;
$this->process_command();
} else {
- # basic authentication failed
+ //basic authentication failed
list($proxy, $ip) = common_client_ip();
common_log(LOG_WARNING, "Failed API auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
@@ -76,12 +97,12 @@ class ApiAction extends Action
} else {
// Caller might give us a username even if not required
- if (isset($_SERVER['PHP_AUTH_USER'])) {
- $user = User::staticGet('nickname', $_SERVER['PHP_AUTH_USER']);
+ if (isset($this->auth_user)) {
+ $user = User::staticGet('nickname', $this->auth_user);
if ($user) {
$this->user = $user;
}
- # Twitter doesn't throw an error if the user isn't found
+ //Twitter doesn't throw an error if the user isn't found
}
$this->process_command();
@@ -94,7 +115,7 @@ class ApiAction extends Action
$actionfile = INSTALLDIR."/actions/$action.php";
if (file_exists($actionfile)) {
- require_once($actionfile);
+ include_once $actionfile;
$action_class = ucfirst($action)."Action";
$action_obj = new $action_class();
@@ -110,10 +131,10 @@ class ApiAction extends Action
call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata);
} else {
- $this->clientError("API method not found!", $code=404);
+ $this->clientError("API method not found!", $code = 404);
}
} else {
- $this->clientError("API method not found!", $code=404);
+ $this->clientError("API method not found!", $code = 404);
}
}
@@ -181,10 +202,11 @@ class ApiAction extends Action
$user_id = $this->arg('user_id');
$screen_name = $this->arg('screen_name');
- if (empty($this->api_arg) &&
- empty($id) &&
- empty($user_id) &&
- empty($screen_name)) {
+ if (empty($this->api_arg)
+ && empty($id)
+ && empty($user_id)
+ && empty($screen_name)
+ ) {
return true;
} else {
return false;
@@ -203,6 +225,33 @@ class ApiAction extends Action
}
}
+ function basic_auth_process_header()
+ {
+ if (isset($_SERVER['AUTHORIZATION']) || isset($_SERVER['HTTP_AUTHORIZATION'])) {
+ $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['AUTHORIZATION'];
+ }
+
+ if (isset($_SERVER['PHP_AUTH_USER'])) {
+ $this->auth_user = $_SERVER['PHP_AUTH_USER'];
+ $this->auth_pw = $_SERVER['PHP_AUTH_PW'];
+ } elseif (isset($authorization_header) && strstr(substr($authorization_header, 0, 5), 'Basic')) {
+ // decode the HTTP_AUTHORIZATION header on php-cgi server self
+ // on fcgid server the header name is AUTHORIZATION
+
+ $auth_hash = base64_decode(substr($authorization_header, 6));
+ list($this->auth_user, $this->auth_pw) = explode(':', $auth_hash);
+
+ // set all to null on a empty basic auth request
+ if ($this->auth_user == "") {
+ $this->auth_user = null;
+ $this->auth_pw = null;
+ }
+ } else {
+ $this->auth_user = null;
+ $this->auth_pw = null;
+ }
+ }
+
function show_basic_auth_error()
{
header('HTTP/1.1 401 Unauthorized');
@@ -216,7 +265,7 @@ class ApiAction extends Action
$this->element('request', null, $_SERVER['REQUEST_URI']);
$this->elementEnd('hash');
$this->endXML();
- } else if ($this->content_type == 'json') {
+ } else if ($this->content_type == 'json') {
header('Content-Type: application/json; charset=utf-8');
$error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
print(json_encode($error_array));
diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php
index 0bc439ff1..ded419dd7 100644
--- a/actions/avatarsettings.php
+++ b/actions/avatarsettings.php
@@ -362,13 +362,13 @@ class AvatarsettingsAction extends AccountSettingsAction
$profile = $user->getProfile();
$avatar = $profile->getOriginalAvatar();
- $avatar->delete();
+ if($avatar) $avatar->delete();
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
- $avatar->delete();
+ if($avatar) $avatar->delete();
$avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
- $avatar->delete();
+ if($avatar) $avatar->delete();
$avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
- $avatar->delete();
+ if($avatar) $avatar->delete();
$this->showForm(_('Avatar deleted.'), true);
}
@@ -399,5 +399,7 @@ class AvatarsettingsAction extends AccountSettingsAction
$this->script('js/jcrop/jquery.Jcrop.min.js');
$this->script('js/jcrop/jquery.Jcrop.go.js');
}
+
+ $this->autofocus('avatarfile');
}
}
diff --git a/actions/confirmaddress.php b/actions/confirmaddress.php
index 201694286..6fd74f3ff 100644
--- a/actions/confirmaddress.php
+++ b/actions/confirmaddress.php
@@ -67,11 +67,7 @@ class ConfirmaddressAction extends Action
parent::handle($args);
if (!common_logged_in()) {
common_set_returnto($this->selfUrl());
- if (!common_config('site', 'openidonly')) {
- common_redirect(common_local_url('login'));
- } else {
- common_redirect(common_local_url('openidlogin'));
- }
+ common_redirect(common_local_url('login'));
return;
}
$code = $this->trimmed('code');
diff --git a/actions/doc.php b/actions/doc.php
index 68295234c..836f039d3 100644
--- a/actions/doc.php
+++ b/actions/doc.php
@@ -58,12 +58,24 @@ class DocAction extends Action
function handle($args)
{
parent::handle($args);
- $this->title = $this->trimmed('title');
- $this->filename = INSTALLDIR.'/doc-src/'.$this->title;
- if (!file_exists($this->filename)) {
- $this->clientError(_('No such document.'));
- return;
+
+ $this->title = $this->trimmed('title');
+ $this->output = null;
+
+ if (Event::handle('StartLoadDoc', array(&$this->title, &$this->output))) {
+
+ $this->filename = INSTALLDIR.'/doc-src/'.$this->title;
+ if (!file_exists($this->filename)) {
+ $this->clientError(_('No such document.'));
+ return;
+ }
+
+ $c = file_get_contents($this->filename);
+ $this->output = common_markup_to_html($c);
+
+ Event::handle('EndLoadDoc', array($this->title, &$this->output));
}
+
$this->showPage();
}
@@ -93,9 +105,7 @@ class DocAction extends Action
*/
function showContent()
{
- $c = file_get_contents($this->filename);
- $output = common_markup_to_html($c);
- $this->raw($output);
+ $this->raw($this->output);
}
/**
diff --git a/actions/editgroup.php b/actions/editgroup.php
index cac910e9b..0c2dc8bdf 100644
--- a/actions/editgroup.php
+++ b/actions/editgroup.php
@@ -160,6 +160,12 @@ class EditgroupAction extends GroupDesignAction
}
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('nickname');
+ }
+
function trySave()
{
$cur = common_current_user();
@@ -196,8 +202,8 @@ class EditgroupAction extends GroupDesignAction
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
$this->showForm(_('Full name is too long (max 255 chars).'));
return;
- } else if (!is_null($description) && mb_strlen($description) > 140) {
- $this->showForm(_('description is too long (max 140 chars).'));
+ } else if (User_group::descriptionTooLong($description)) {
+ $this->showForm(sprintf(_('description is too long (max %d chars).'), User_group::maxDescription()));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).'));
diff --git a/actions/emailsettings.php b/actions/emailsettings.php
index af528a892..6eff06c0d 100644
--- a/actions/emailsettings.php
+++ b/actions/emailsettings.php
@@ -71,6 +71,12 @@ class EmailsettingsAction extends AccountSettingsAction
return _('Manage how you get email from %%site.name%%.');
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('email');
+ }
+
/**
* Content area of the page
*
diff --git a/actions/favorited.php b/actions/favorited.php
index 5ba508cdf..150b67b0b 100644
--- a/actions/favorited.php
+++ b/actions/favorited.php
@@ -153,8 +153,7 @@ class FavoritedAction extends Action
$message .= _('Be the first to add a notice to your favorites by clicking the fave button next to any notice you like.');
}
else {
- $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and be the first to add a notice to your favorites!'),
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+ $message .= _('Why not [register an account](%%action.register%%) and be the first to add a notice to your favorites!');
}
$this->elementStart('div', 'guide');
diff --git a/actions/favoritesrss.php b/actions/favoritesrss.php
index 2d5ce9854..62f06e841 100644
--- a/actions/favoritesrss.php
+++ b/actions/favoritesrss.php
@@ -50,11 +50,11 @@ require_once INSTALLDIR.'/lib/rssaction.php';
*/
class FavoritesrssAction extends Rss10Action
{
-
+
/** The user whose favorites to display */
-
+
var $user = null;
-
+
/**
* Find the user to display by supplied nickname
*
@@ -66,7 +66,7 @@ class FavoritesrssAction extends Rss10Action
function prepare($args)
{
parent::prepare($args);
-
+
$nickname = $this->trimmed('nickname');
$this->user = User::staticGet('nickname', $nickname);
@@ -74,10 +74,11 @@ class FavoritesrssAction extends Rss10Action
$this->clientError(_('No such user.'));
return false;
} else {
+ $this->notices = $this->getNotices($this->limit);
return true;
}
}
-
+
/**
* Get notices
*
diff --git a/actions/finishremotesubscribe.php b/actions/finishremotesubscribe.php
index 871bc3d2d..b1cec66f4 100644
--- a/actions/finishremotesubscribe.php
+++ b/actions/finishremotesubscribe.php
@@ -1,5 +1,16 @@
<?php
-/*
+/**
+ * Handler for remote subscription finish callback
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@status.net>
+ * @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.
*
@@ -15,285 +26,121 @@
*
* 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.'/lib/omb.php');
+require_once INSTALLDIR.'/extlib/libomb/service_consumer.php';
+require_once INSTALLDIR.'/lib/omb.php';
+/**
+ * Handler for remote subscription finish callback
+ *
+ * When a remote user subscribes a local user, a redirect to this action is
+ * issued after the remote user authorized his service to subscribe.
+ *
+ * @category Action
+ * @package Laconica
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://laconi.ca/
+ */
class FinishremotesubscribeAction extends Action
{
+ /**
+ * Class handler.
+ *
+ * @param array $args query arguments
+ *
+ * @return nothing
+ *
+ **/
function handle($args)
{
-
parent::handle($args);
- if (common_logged_in()) {
- $this->clientError(_('You can use the local subscription!'));
- return;
- }
-
- $omb = $_SESSION['oauth_authorization_request'];
+ /* Restore session data. RemotesubscribeAction should have stored
+ this entry. */
+ $service = unserialize($_SESSION['oauth_authorization_request']);
- if (!$omb) {
+ if (!$service) {
$this->clientError(_('Not expecting this response!'));
return;
}
- common_debug('stored request: '.print_r($omb,true), __FILE__);
-
- common_remove_magic_from_request();
- $req = OAuthRequest::from_request('POST', common_local_url('finishuserauthorization'));
-
- $token = $req->get_parameter('oauth_token');
-
- # I think this is the success metric
-
- if ($token != $omb['token']) {
- $this->clientError(_('Not authorized.'));
- return;
- }
-
- $version = $req->get_parameter('omb_version');
-
- if ($version != OMB_VERSION_01) {
- $this->clientError(_('Unknown version of OMB protocol.'));
- return;
- }
-
- $nickname = $req->get_parameter('omb_listener_nickname');
-
- if (!$nickname) {
- $this->clientError(_('No nickname provided by remote server.'));
- return;
- }
-
- $profile_url = $req->get_parameter('omb_listener_profile');
+ common_debug('stored request: '. print_r($service, true), __FILE__);
- if (!$profile_url) {
- $this->clientError(_('No profile URL returned by server.'));
- return;
- }
-
- if (!Validate::uri($profile_url, array('allowed_schemes' => array('http', 'https')))) {
- $this->clientError(_('Invalid profile URL returned by server.'));
- return;
- }
-
- if ($profile_url == common_local_url('showstream', array('nickname' => $nickname))) {
- $this->clientError(_('You can use the local subscription!'));
- return;
- }
-
- common_debug('listenee: "'.$omb['listenee'].'"', __FILE__);
-
- $user = User::staticGet('nickname', $omb['listenee']);
+ /* Create user objects for both users. Do it early for request
+ validation. */
+ $user = User::staticGet('uri', $service->getListeneeURI());
if (!$user) {
- $this->clientError(_('User being listened to doesn\'t exist.'));
+ $this->clientError(_('User being listened to does not exist.'));
return;
}
- $other = User::staticGet('uri', $omb['listener']);
+ $other = User::staticGet('uri', $service->getListenerURI());
if ($other) {
$this->clientError(_('You can use the local subscription!'));
return;
}
- $fullname = $req->get_parameter('omb_listener_fullname');
- $homepage = $req->get_parameter('omb_listener_homepage');
- $bio = $req->get_parameter('omb_listener_bio');
- $location = $req->get_parameter('omb_listener_location');
- $avatar_url = $req->get_parameter('omb_listener_avatar');
+ $remote = Remote_profile::staticGet('uri', $service->getListenerURI());
- list($newtok, $newsecret) = $this->access_token($omb);
+ $profile = Profile::staticGet($remote->id);
- if (!$newtok || !$newsecret) {
- $this->clientError(_('Couldn\'t convert request tokens to access tokens.'));
+ if ($user->hasBlocked($profile)) {
+ $this->clientError(_('That user has blocked you from subscribing.'));
return;
}
- # XXX: possible attack point; subscribe and return someone else's profile URI
-
- $remote = Remote_profile::staticGet('uri', $omb['listener']);
-
- if ($remote) {
- $exists = true;
- $profile = Profile::staticGet($remote->id);
- $orig_remote = clone($remote);
- $orig_profile = clone($profile);
- # XXX: compare current postNotice and updateProfile URLs to the ones
- # stored in the DB to avoid (possibly...) above attack
- } else {
- $exists = false;
- $remote = new Remote_profile();
- $remote->uri = $omb['listener'];
- $profile = new Profile();
- }
-
- $profile->nickname = $nickname;
- $profile->profileurl = $profile_url;
-
- if (!is_null($fullname)) {
- $profile->fullname = $fullname;
- }
- if (!is_null($homepage)) {
- $profile->homepage = $homepage;
- }
- if (!is_null($bio)) {
- $profile->bio = $bio;
- }
- if (!is_null($location)) {
- $profile->location = $location;
- }
-
- if ($exists) {
- $profile->update($orig_profile);
- } else {
- $profile->created = DB_DataObject_Cast::dateTime(); # current time
- $id = $profile->insert();
- if (!$id) {
- $this->serverError(_('Error inserting new profile'));
+ /* Perform the handling itself via libomb. */
+ try {
+ $service->finishAuthorization();
+ } catch (OAuthException $e) {
+ if ($e->getMessage() == 'The authorized token does not equal the ' .
+ 'submitted token.') {
+ $this->clientError(_('You are not authorized.'));
return;
- }
- $remote->id = $id;
- }
-
- if ($avatar_url) {
- if (!$this->add_avatar($profile, $avatar_url)) {
- $this->serverError(_('Error inserting avatar'));
- return;
- }
- }
-
- $remote->postnoticeurl = $omb['post_notice_url'];
- $remote->updateprofileurl = $omb['update_profile_url'];
-
- if ($exists) {
- if (!$remote->update($orig_remote)) {
- $this->serverError(_('Error updating remote profile'));
+ } else {
+ $this->clientError(_('Could not convert request token to ' .
+ 'access token.'));
return;
}
- } else {
- $remote->created = DB_DataObject_Cast::dateTime(); # current time
- if (!$remote->insert()) {
- $this->serverError(_('Error inserting remote profile'));
- return;
- }
- }
-
- if ($user->hasBlocked($profile)) {
- $this->clientError(_('That user has blocked you from subscribing.'));
+ } catch (OMB_RemoteServiceException $e) {
+ $this->clientError(_('Remote service uses unknown version of ' .
+ 'OMB protocol.'));
+ return;
+ } catch (Exception $e) {
+ common_debug('Got exception ' . print_r($e, true), __FILE__);
+ $this->clientError($e->getMessage());
return;
}
- $sub = new Subscription();
-
- $sub->subscriber = $remote->id;
- $sub->subscribed = $user->id;
-
- $sub_exists = false;
-
- if ($sub->find(true)) {
- $sub_exists = true;
- $orig_sub = clone($sub);
- } else {
- $sub_exists = false;
- $sub->created = DB_DataObject_Cast::dateTime(); # current time
- }
-
- $sub->token = $newtok;
- $sub->secret = $newsecret;
+ /* The service URLs are not accessible from datastore, so setting them
+ after insertion of the profile. */
+ $orig_remote = clone($remote);
- if ($sub_exists) {
- $result = $sub->update($orig_sub);
- } else {
- $result = $sub->insert();
- }
+ $remote->postnoticeurl =
+ $service->getServiceURI(OMB_ENDPOINT_POSTNOTICE);
+ $remote->updateprofileurl =
+ $service->getServiceURI(OMB_ENDPOINT_UPDATEPROFILE);
- if (!$result) {
- common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
- $this->clientError(_('Couldn\'t insert new subscription.'));
- return;
+ if (!$remote->update($orig_remote)) {
+ $this->serverError(_('Error updating remote profile'));
+ return;
}
- # Notify user, if necessary
-
- mail_subscribe_notify_profile($user, $profile);
-
- # Clear the data
+ /* Clear the session data. */
unset($_SESSION['oauth_authorization_request']);
- # If we show subscriptions in reverse chron order, this should
- # show up close to the top of the page
-
+ /* If we show subscriptions in reverse chronological order, the new one
+ should show up close to the top of the page. */
common_redirect(common_local_url('subscribers', array('nickname' =>
$user->nickname)),
303);
}
-
- function add_avatar($profile, $url)
- {
- $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
- copy($url, $temp_filename);
- $imagefile = new ImageFile($profile->id, $temp_filename);
- $filename = Avatar::filename($profile->id,
- image_type_to_extension($imagefile->type),
- null,
- common_timestamp());
- rename($temp_filename, Avatar::path($filename));
- return $profile->setOriginal($filename);
- }
-
- function access_token($omb)
- {
-
- common_debug('starting request for access token', __FILE__);
-
- $con = omb_oauth_consumer();
- $tok = new OAuthToken($omb['token'], $omb['secret']);
-
- common_debug('using request token "'.$tok.'"', __FILE__);
-
- $url = $omb['access_token_url'];
-
- common_debug('using access token url "'.$url.'"', __FILE__);
-
- # XXX: Is this the right thing to do? Strip off GET params and make them
- # POST params? Seems wrong to me.
-
- $parsed = parse_url($url);
- $params = array();
- parse_str($parsed['query'], $params);
-
- $req = OAuthRequest::from_consumer_and_token($con, $tok, "POST", $url, $params);
-
- $req->set_parameter('omb_version', OMB_VERSION_01);
-
- # XXX: test to see if endpoint accepts this signature method
-
- $req->sign_request(omb_hmac_sha1(), $con, $tok);
-
- # We re-use this tool's fetcher, since it's pretty good
-
- common_debug('posting to access token url "'.$req->get_normalized_http_url().'"', __FILE__);
- common_debug('posting request data "'.$req->to_postdata().'"', __FILE__);
-
- $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
- $result = $fetcher->post($req->get_normalized_http_url(),
- $req->to_postdata(),
- array('User-Agent: StatusNet/' . STATUSNET_VERSION));
-
- common_debug('got result: "'.print_r($result,true).'"', __FILE__);
-
- if ($result->status != 200) {
- return null;
- }
-
- parse_str($result->body, $return);
-
- return array($return['oauth_token'], $return['oauth_token_secret']);
- }
}
diff --git a/actions/foaf.php b/actions/foaf.php
index 4dae9dfc1..356393304 100644
--- a/actions/foaf.php
+++ b/actions/foaf.php
@@ -146,8 +146,10 @@ class FoafAction extends Action
while ($sub->fetch()) {
if ($sub->token) {
$other = Remote_profile::staticGet('id', $sub->subscriber);
+ $profile = Profile::staticGet('id', $sub->subscriber);
} else {
$other = User::staticGet('id', $sub->subscriber);
+ $profile = Profile::staticGet('id', $sub->subscriber);
}
if (!$other) {
common_debug('Got a bad subscription: '.print_r($sub,true));
@@ -158,12 +160,15 @@ class FoafAction extends Action
} else {
$person[$other->uri] = array(LISTENER,
$other->id,
- $other->nickname,
+ $profile->nickname,
(empty($sub->token)) ? 'User' : 'Remote_profile');
}
$other->free();
$other = null;
unset($other);
+ $profile->free();
+ $profile = null;
+ unset($profile);
}
}
@@ -254,8 +259,10 @@ class FoafAction extends Action
while ($sub->fetch()) {
if (!empty($sub->token)) {
$other = Remote_profile::staticGet('id', $sub->subscribed);
+ $profile = Profile::staticGet('id', $sub->subscribed);
} else {
$other = User::staticGet('id', $sub->subscribed);
+ $profile = Profile::staticGet('id', $sub->subscribed);
}
if (empty($other)) {
common_debug('Got a bad subscription: '.print_r($sub,true));
@@ -264,11 +271,14 @@ class FoafAction extends Action
$this->element('sioc:follows', array('rdf:resource' => $other->uri.'#acct'));
$person[$other->uri] = array(LISTENEE,
$other->id,
- $other->nickname,
+ $profile->nickname,
(empty($sub->token)) ? 'User' : 'Remote_profile');
$other->free();
$other = null;
unset($other);
+ $profile->free();
+ $profile = null;
+ unset($profile);
}
}
diff --git a/actions/grouplogo.php b/actions/grouplogo.php
index c6f376915..63ba769c7 100644
--- a/actions/grouplogo.php
+++ b/actions/grouplogo.php
@@ -445,6 +445,8 @@ class GrouplogoAction extends GroupDesignAction
$this->script('js/jcrop/jquery.Jcrop.min.js');
$this->script('js/jcrop/jquery.Jcrop.go.js');
}
+
+ $this->autofocus('avatarfile');
}
function showLocalNav()
diff --git a/actions/grouprss.php b/actions/grouprss.php
index 70c1ded48..6a6b55e78 100644
--- a/actions/grouprss.php
+++ b/actions/grouprss.php
@@ -104,6 +104,7 @@ class groupRssAction extends Rss10Action
return false;
}
+ $this->notices = $this->getNotices($this->limit);
return true;
}
diff --git a/actions/groupsearch.php b/actions/groupsearch.php
index bbd4c3a74..f0cca7156 100644
--- a/actions/groupsearch.php
+++ b/actions/groupsearch.php
@@ -82,8 +82,7 @@ class GroupsearchAction extends SearchAction
$message = _('If you can\'t find the group you\'re looking for, you can [create it](%%action.newgroup%%) yourself.');
}
else {
- $message = sprintf(_('Why not [register an account](%%%%action.%s%%%%) and [create the group](%%%%action.newgroup%%%%) yourself!'),
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+ $message = _('Why not [register an account](%%action.register%%) and [create the group](%%action.newgroup%%) yourself!');
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
@@ -91,6 +90,12 @@ class GroupsearchAction extends SearchAction
$user_group->free();
}
}
+
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('q');
+ }
}
class GroupSearchResults extends GroupList
diff --git a/actions/invite.php b/actions/invite.php
index ab43a2491..788130c58 100644
--- a/actions/invite.php
+++ b/actions/invite.php
@@ -98,6 +98,12 @@ class InviteAction extends CurrentUserDesignAction
$this->showPage();
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('addresses');
+ }
+
function title()
{
if ($this->mode == 'sent') {
@@ -235,7 +241,7 @@ class InviteAction extends CurrentUserDesignAction
common_root_url(),
$personal,
common_local_url('showstream', array('nickname' => $user->nickname)),
- common_local_url((!common_config('site', 'openidonly')) ? 'register' : 'openidlogin', array('code' => $invite->code)));
+ common_local_url('register', array('code' => $invite->code)));
mail_send($recipients, $headers, $body);
}
diff --git a/actions/login.php b/actions/login.php
index 37f3c54ff..f6d016310 100644
--- a/actions/login.php
+++ b/actions/login.php
@@ -22,6 +22,7 @@
* @category Login
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
+ * @author Sarven Capadisli <csarven@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
@@ -37,6 +38,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @category Personal
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
+ * @author Sarven Capadisli <csarven@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@@ -65,8 +67,6 @@ class LoginAction extends Action
*
* Switches on request method; either shows the form or handles its input.
*
- * Checks if only OpenID is allowed and redirects to openidlogin if so.
- *
* @param array $args $_REQUEST data
*
* @return void
@@ -75,9 +75,7 @@ class LoginAction extends Action
function handle($args)
{
parent::handle($args);
- if (common_config('site', 'openidonly')) {
- common_redirect(common_local_url('openidlogin'));
- } else if (common_is_real_login()) {
+ if (common_is_real_login()) {
$this->clientError(_('Already logged in.'));
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->checkLogin();
@@ -162,6 +160,12 @@ class LoginAction extends Action
$this->showPage();
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('nickname');
+ }
+
/**
* Title of the page
*
@@ -251,11 +255,6 @@ class LoginAction extends Action
return _('For security reasons, please re-enter your ' .
'user name and password ' .
'before changing your settings.');
- } else if (common_config('openid', 'enabled')) {
- return _('Login with your username and password. ' .
- 'Don\'t have a username yet? ' .
- '[Register](%%action.register%%) a new account, or ' .
- 'try [OpenID](%%action.openidlogin%%). ');
} else {
return _('Login with your username and password. ' .
'Don\'t have a username yet? ' .
diff --git a/actions/logout.php b/actions/logout.php
index 298b2a484..1e0adae57 100644
--- a/actions/logout.php
+++ b/actions/logout.php
@@ -32,8 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
-require_once INSTALLDIR.'/lib/openid.php';
-
/**
* Logout action class.
*
diff --git a/actions/newgroup.php b/actions/newgroup.php
index 01cb636aa..a2cf72528 100644
--- a/actions/newgroup.php
+++ b/actions/newgroup.php
@@ -146,8 +146,8 @@ class NewgroupAction extends Action
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
$this->showForm(_('Full name is too long (max 255 chars).'));
return;
- } else if (!is_null($description) && mb_strlen($description) > 140) {
- $this->showForm(_('description is too long (max 140 chars).'));
+ } else if (User_group::descriptionTooLong($description)) {
+ $this->showForm(sprintf(_('description is too long (max %d chars).'), User_group::maxDescription()));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).'));
diff --git a/actions/newmessage.php b/actions/newmessage.php
index 828a339cf..a0b17fc18 100644
--- a/actions/newmessage.php
+++ b/actions/newmessage.php
@@ -144,9 +144,10 @@ class NewmessageAction extends Action
} else {
$content_shortened = common_shorten_links($this->content);
- if (mb_strlen($content_shortened) > 140) {
- $this->showForm(_('That\'s too long. ' .
- 'Max message size is 140 chars.'));
+ if (Message::contentTooLong($content_shortened)) {
+ $this->showForm(sprintf(_('That\'s too long. ' .
+ 'Max message size is %d chars.'),
+ Message::maxContent()));
return;
}
}
diff --git a/actions/newnotice.php b/actions/newnotice.php
index 00a822860..23ec2a1b5 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -162,9 +162,10 @@ class NewnoticeAction extends Action
$this->clientError(_('No content!'));
} else {
$content_shortened = common_shorten_links($content);
- if (mb_strlen($content_shortened) > 140) {
- $this->clientError(_('That\'s too long. '.
- 'Max notice size is 140 chars.'));
+ if (Notice::contentTooLong($content_shortened)) {
+ $this->clientError(sprintf(_('That\'s too long. '.
+ 'Max notice size is %d chars.'),
+ Notice::maxContent()));
}
}
@@ -241,9 +242,10 @@ class NewnoticeAction extends Action
$short_fileurl = common_shorten_url($fileurl);
$content_shortened .= ' ' . $short_fileurl;
- if (mb_strlen($content_shortened) > 140) {
+ if (Notice::contentTooLong($content_shortened)) {
$this->deleteFile($filename);
- $this->clientError(_('Max notice size is 140 chars, including attachment URL.'));
+ $this->clientError(sprintf(_('Max notice size is %d chars, including attachment URL.'),
+ Notice::maxContent()));
}
// Also, not sure this is necessary -- Zach
@@ -431,13 +433,14 @@ class NewnoticeAction extends Action
$content = $this->trimmed('status_textarea');
if (!$content) {
$replyto = $this->trimmed('replyto');
+ $inreplyto = $this->trimmed('inreplyto');
$profile = Profile::staticGet('nickname', $replyto);
if ($profile) {
$content = '@' . $profile->nickname . ' ';
}
}
- $notice_form = new NoticeForm($this, '', $content);
+ $notice_form = new NoticeForm($this, '', $content, null, $inreplyto);
$notice_form->show();
}
diff --git a/actions/noticesearch.php b/actions/noticesearch.php
index 1188e7e10..79cf572cc 100644
--- a/actions/noticesearch.php
+++ b/actions/noticesearch.php
@@ -121,9 +121,7 @@ class NoticesearchAction extends SearchAction
$message = sprintf(_('Be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q));
}
else {
- $message = sprintf(_('Why not [register an account](%%%%action.%s%%%%) and be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'),
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
- urlencode($q));
+ $message = sprintf(_('Why not [register an account](%%%%action.register%%%%) and be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q));
}
$this->elementStart('div', 'guide');
@@ -137,6 +135,12 @@ class NoticesearchAction extends SearchAction
$this->pagination($page > 1, $cnt > NOTICES_PER_PAGE,
$page, 'noticesearch', array('q' => $q));
}
+
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('q');
+ }
}
class SearchNoticeList extends NoticeList {
@@ -192,7 +196,7 @@ class SearchNoticeListItem extends NoticeListItem {
$result = preg_replace($pattern, '<strong>\\1</strong>', $text);
/* Remove highlighting from inside links, loop incase multiple highlights in links */
- $pattern = '/(href="[^"]*)<strong>('.$options.')<\/strong>([^"]*")/iU';
+ $pattern = '/(\w+="[^"]*)<strong>('.$options.')<\/strong>([^"]*")/iU';
do {
$result = preg_replace($pattern, '\\1\\2\\3', $result, -1, $count);
} while ($count);
diff --git a/actions/othersettings.php b/actions/othersettings.php
index 8b674161a..011b4fc83 100644
--- a/actions/othersettings.php
+++ b/actions/othersettings.php
@@ -71,6 +71,12 @@ class OthersettingsAction extends AccountSettingsAction
return _('Manage various other options.');
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('urlshorteningservice');
+ }
+
/**
* Content area of the page
*
@@ -91,19 +97,20 @@ class OthersettingsAction extends AccountSettingsAction
$this->elementStart('fieldset');
$this->hidden('token', common_session_token());
- // I18N
-
- $services = array(
- '' => 'None',
- 'ur1.ca' => 'ur1.ca (free service)',
- '2tu.us' => '2tu.us (free service)',
- 'ptiturl.com' => 'ptiturl.com',
- 'bit.ly' => 'bit.ly',
- 'tinyurl.com' => 'tinyurl.com',
- 'is.gd' => 'is.gd',
- 'snipr.com' => 'snipr.com',
- 'metamark.net' => 'metamark.net'
- );
+ $services=array();
+ global $_shorteners;
+ if($_shorteners){
+ foreach($_shorteners as $name=>$value)
+ {
+ $services[$name]=$name;
+ if($value['info']['freeService']){
+ // I18N
+ $services[$name].=' (free service)';
+ }
+ }
+ }
+ asort($services);
+ $services['']='None';
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
diff --git a/actions/passwordsettings.php b/actions/passwordsettings.php
index ec842600f..cd4beac3f 100644
--- a/actions/passwordsettings.php
+++ b/actions/passwordsettings.php
@@ -69,6 +69,12 @@ class PasswordsettingsAction extends AccountSettingsAction
return _('Change your password.');
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('oldpassword');
+ }
+
/**
* Content area of the page
*
diff --git a/actions/peoplesearch.php b/actions/peoplesearch.php
index ba0f71e39..38135ecbd 100644
--- a/actions/peoplesearch.php
+++ b/actions/peoplesearch.php
@@ -85,6 +85,12 @@ class PeoplesearchAction extends SearchAction
$profile->free();
}
}
+
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('q');
+ }
}
/**
diff --git a/actions/postnotice.php b/actions/postnotice.php
index e775ca17e..c2e1c44ca 100644
--- a/actions/postnotice.php
+++ b/actions/postnotice.php
@@ -1,5 +1,16 @@
<?php
-/*
+/**
+ * Handle postnotice action
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@status.net>
+ * @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.
*
@@ -19,73 +30,67 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
+/**
+ * Handler for postnotice action
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ */
class PostnoticeAction extends Action
{
+ /**
+ * For initializing members of the class.
+ *
+ * @param array $argarray misc. arguments
+ *
+ * @return boolean true
+ */
+ function prepare($argarray)
+ {
+ parent::prepare($argarray);
+ try {
+ $this->checkNotice();
+ } catch (Exception $e) {
+ $this->clientError($e->getMessage());
+ return false;
+ }
+ return true;
+ }
+
function handle($args)
{
parent::handle($args);
try {
- common_remove_magic_from_request();
- $req = OAuthRequest::from_request('POST', common_local_url('postnotice'));
- # Note: server-to-server function!
- $server = omb_oauth_server();
- list($consumer, $token) = $server->verify_request($req);
- if ($this->save_notice($req, $consumer, $token)) {
- print "omb_version=".OMB_VERSION_01;
- }
- } catch (OAuthException $e) {
+ $srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
+ omb_oauth_server());
+ $srv->handlePostNotice();
+ } catch (Exception $e) {
$this->serverError($e->getMessage());
return;
}
}
- function save_notice(&$req, &$consumer, &$token)
+ function checkNotice()
{
- $version = $req->get_parameter('omb_version');
- if ($version != OMB_VERSION_01) {
- $this->clientError(_('Unsupported OMB version'), 400);
- return false;
- }
- # First, check to see
- $listenee = $req->get_parameter('omb_listenee');
- $remote_profile = Remote_profile::staticGet('uri', $listenee);
- if (!$remote_profile) {
- $this->clientError(_('Profile unknown'), 403);
- return false;
- }
- $sub = Subscription::staticGet('token', $token->key);
- if (!$sub) {
- $this->clientError(_('No such subscription'), 403);
- return false;
- }
- $content = $req->get_parameter('omb_notice_content');
- $content_shortened = common_shorten_links($content);
- if (mb_strlen($content_shortened) > 140) {
+ $content = common_shorten_links($_POST['omb_notice_content']);
+ if (Notice::contentTooLong($content)) {
$this->clientError(_('Invalid notice content'), 400);
return false;
}
- $notice_uri = $req->get_parameter('omb_notice');
- if (!Validate::uri($notice_uri) &&
- !common_valid_tag($notice_uri)) {
- $this->clientError(_('Invalid notice uri'), 400);
- return false;
- }
- $notice_url = $req->get_parameter('omb_notice_url');
- if ($notice_url && !common_valid_http_url($notice_url)) {
- $this->clientError(_('Invalid notice url'), 400);
- return false;
+ $license = $_POST['omb_notice_license'];
+ $site_license = common_config('license', 'url');
+ if ($license && !common_compatible_license($license, $site_license)) {
+ throw new Exception(sprintf(_('Notice license ‘%s’ is not ' .
+ 'compatible with site license ‘%s’.'),
+ $license, $site_license));
}
- $notice = Notice::staticGet('uri', $notice_uri);
- if (!$notice) {
- $notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, null, $notice_uri);
- if (is_string($notice)) {
- common_server_serror($notice, 500);
- return false;
- }
- common_broadcast_notice($notice, true);
- }
- return true;
}
}
+?> \ No newline at end of file
diff --git a/actions/profilesettings.php b/actions/profilesettings.php
index f9c16351d..5445d9bb2 100644
--- a/actions/profilesettings.php
+++ b/actions/profilesettings.php
@@ -23,6 +23,7 @@
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
+ * @author Sarven Capadisli <csarven@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
@@ -41,6 +42,7 @@ require_once INSTALLDIR.'/lib/accountsettingsaction.php';
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
+ * @author Sarven Capadisli <csarven@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@@ -70,6 +72,12 @@ class ProfilesettingsAction extends AccountSettingsAction
'so people know more about you.');
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('nickname');
+ }
+
/**
* Content area of the page
*
@@ -109,9 +117,16 @@ class ProfilesettingsAction extends AccountSettingsAction
_('URL of your homepage, blog, or profile on another site'));
$this->elementEnd('li');
$this->elementStart('li');
+ $maxBio = Profile::maxBio();
+ if ($maxBio > 0) {
+ $bioInstr = sprintf(_('Describe yourself and your interests in %d chars'),
+ $maxBio);
+ } else {
+ $bioInstr = _('Describe yourself and your interests');
+ }
$this->textarea('bio', _('Bio'),
($this->arg('bio')) ? $this->arg('bio') : $profile->bio,
- _('Describe yourself and your interests in 140 chars'));
+ $bioInstr);
$this->elementEnd('li');
$this->elementStart('li');
$this->input('location', _('Location'),
@@ -202,8 +217,9 @@ class ProfilesettingsAction extends AccountSettingsAction
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
$this->showForm(_('Full name is too long (max 255 chars).'));
return;
- } else if (!is_null($bio) && mb_strlen($bio) > 140) {
- $this->showForm(_('Bio is too long (max 140 chars).'));
+ } else if (Profile::bioTooLong($bio)) {
+ $this->showForm(sprintf(_('Bio is too long (max %d chars).'),
+ Profile::maxBio()));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).'));
diff --git a/actions/public.php b/actions/public.php
index d426648f3..73fad182a 100644
--- a/actions/public.php
+++ b/actions/public.php
@@ -114,8 +114,6 @@ class PublicAction extends Action
{
parent::handle($args);
- header('X-XRDS-Location: '. common_local_url('publicxrds'));
-
$this->showPage();
}
@@ -157,22 +155,6 @@ class PublicAction extends Action
}
/**
- * Extra head elements
- *
- * We include a <meta> element linking to the publicxrds page, for OpenID
- * client-side authentication.
- *
- * @return void
- */
-
- function extraHead()
- {
- // for client side of OpenID authentication
- $this->element('meta', array('http-equiv' => 'X-XRDS-Location',
- 'content' => common_local_url('publicxrds')));
- }
-
- /**
* Show tabset for this page
*
* Uses the PublicGroupNav widget
@@ -196,8 +178,7 @@ class PublicAction extends Action
}
else {
if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
- $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and be the first to post!'),
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+ $message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
}
}
@@ -244,11 +225,10 @@ class PublicAction extends Action
function showAnonymousMessage()
{
if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
- $m = sprintf(_('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.%s%%%%) to share notices about yourself with friends, family, and colleagues! ' .
- '([Read more](%%%%doc.help%%%%))'),
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+ $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 {
$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.');
diff --git a/actions/publicrss.php b/actions/publicrss.php
index 593888b9f..0c5d061cb 100644
--- a/actions/publicrss.php
+++ b/actions/publicrss.php
@@ -50,8 +50,22 @@ require_once INSTALLDIR.'/lib/rssaction.php';
class PublicrssAction extends Rss10Action
{
/**
+ * Read arguments and initialize members
+ *
+ * @param array $args Arguments from $_REQUEST
+ * @return boolean success
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ $this->notices = $this->getNotices($this->limit);
+ return true;
+ }
+
+ /**
* Initialization.
- *
+ *
* @return boolean true
*/
function init()
@@ -73,7 +87,7 @@ class PublicrssAction extends Rss10Action
while ($notice->fetch()) {
$notices[] = clone($notice);
}
-
+
return $notices;
}
diff --git a/actions/publictagcloud.php b/actions/publictagcloud.php
index 60bb53e27..e7f6ee36c 100644
--- a/actions/publictagcloud.php
+++ b/actions/publictagcloud.php
@@ -72,8 +72,7 @@ class PublictagcloudAction extends Action
$message .= _('Be the first to post one!');
}
else {
- $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and be the first to post one!'),
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+ $message .= _('Why not [register an account](%%action.register%%) and be the first to post one!');
}
$this->elementStart('div', 'guide');
diff --git a/actions/register.php b/actions/register.php
index c431aeee3..100ab7424 100644
--- a/actions/register.php
+++ b/actions/register.php
@@ -116,8 +116,6 @@ class RegisterAction extends Action
*
* Checks if registration is closed and shows an error if so.
*
- * Checks if only OpenID is allowed and redirects to openidlogin if so.
- *
* @param array $args $_REQUEST data
*
* @return void
@@ -129,8 +127,6 @@ class RegisterAction extends Action
if (common_config('site', 'closed')) {
$this->clientError(_('Registration not allowed.'));
- } else if (common_config('site', 'openidonly')) {
- common_redirect(common_local_url('openidlogin'));
} else if (common_logged_in()) {
$this->clientError(_('Already logged in.'));
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
@@ -140,6 +136,12 @@ class RegisterAction extends Action
}
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('nickname');
+ }
+
/**
* Try to register a user
*
@@ -211,8 +213,9 @@ class RegisterAction extends Action
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
$this->showForm(_('Full name is too long (max 255 chars).'));
return;
- } else if (!is_null($bio) && mb_strlen($bio) > 140) {
- $this->showForm(_('Bio is too long (max 140 chars).'));
+ } else if (Profile::bioTooLong($bio)) {
+ $this->showForm(sprintf(_('Bio is too long (max %d chars).'),
+ Profile::maxBio()));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).'));
@@ -329,22 +332,11 @@ class RegisterAction extends Action
} else if ($this->error) {
$this->element('p', 'error', $this->error);
} else {
- if (common_config('openid', 'enabled')) {
- $instr =
- common_markup_to_html(_('With this form you can create '.
- ' a new account. ' .
- 'You can then post notices and '.
- 'link up to friends and colleagues. '.
- '(Have an [OpenID](http://openid.net/)? ' .
- 'Try our [OpenID registration]'.
- '(%%action.openidlogin%%)!)'));
- } else {
- $instr =
- common_markup_to_html(_('With this form you can create '.
- ' a new account. ' .
- 'You can then post notices and '.
- 'link up to friends and colleagues.'));
- }
+ $instr =
+ common_markup_to_html(_('With this form you can create '.
+ ' a new account. ' .
+ 'You can then post notices and '.
+ 'link up to friends and colleagues. '));
$this->elementStart('div', 'instructions');
$this->raw($instr);
@@ -457,10 +449,16 @@ class RegisterAction extends Action
'or profile on another site'));
$this->elementEnd('li');
$this->elementStart('li');
+ $maxBio = Profile::maxBio();
+ if ($maxBio > 0) {
+ $bioInstr = sprintf(_('Describe yourself and your interests in %d chars'),
+ $maxBio);
+ } else {
+ $bioInstr = _('Describe yourself and your interests');
+ }
$this->textarea('bio', _('Bio'),
$this->trimmed('bio'),
- _('Describe yourself and your '.
- 'interests in 140 chars'));
+ $bioInstr);
$this->elementEnd('li');
$this->elementStart('li');
$this->input('location', _('Location'),
diff --git a/actions/remotesubscribe.php b/actions/remotesubscribe.php
index 374392d4a..aee2a5d8e 100644
--- a/actions/remotesubscribe.php
+++ b/actions/remotesubscribe.php
@@ -1,5 +1,16 @@
<?php
-/*
+/**
+ * Handler for remote subscription
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@status.net>
+ * @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.
*
@@ -15,11 +26,24 @@
*
* 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.'/lib/omb.php');
+require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_consumer.php';
+require_once INSTALLDIR.'/extlib/libomb/profile.php';
+
+/**
+ * Handler for remote subscription
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ */
class RemotesubscribeAction extends Action
{
@@ -36,7 +60,7 @@ class RemotesubscribeAction extends Action
return false;
}
- $this->nickname = $this->trimmed('nickname');
+ $this->nickname = $this->trimmed('nickname');
$this->profile_url = $this->trimmed('profile_url');
return true;
@@ -47,7 +71,7 @@ class RemotesubscribeAction extends Action
parent::handle($args);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- # CSRF protection
+ /* Use a session token for CSRF protection. */
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->showForm(_('There was a problem with your session token. '.
@@ -71,13 +95,11 @@ class RemotesubscribeAction extends Action
if ($this->err) {
$this->element('div', 'error', $this->err);
} else {
- $inst = sprintf(_('To subscribe, you can [login](%%%%action.%s%%%%),' .
- ' or [register](%%%%action.%s%%%%) a new ' .
- ' account. If you already have an account ' .
- ' on a [compatible microblogging site](%%doc.openmublog%%), ' .
- ' enter your profile URL below.'),
- (!common_config('site','openidonly')) ? 'login' : 'openidlogin',
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+ $inst = _('To subscribe, you can [login](%%action.login%%),' .
+ ' or [register](%%action.register%%) a new ' .
+ ' account. If you already have an account ' .
+ ' on a [compatible microblogging site](%%doc.openmublog%%), ' .
+ ' enter your profile URL below.');
$output = common_markup_to_html($inst);
$this->elementStart('div', 'instructions');
$this->raw($output);
@@ -92,8 +114,8 @@ class RemotesubscribeAction extends Action
function showContent()
{
- # id = remotesubscribe conflicts with the
- # button on profile page
+ /* The id 'remotesubscribe' conflicts with the
+ button on profile page. */
$this->elementStart('form', array('id' => 'form_remote_subscribe',
'method' => 'post',
'class' => 'form_settings',
@@ -119,247 +141,50 @@ class RemotesubscribeAction extends Action
function remoteSubscription()
{
- $user = $this->getUser();
-
- if (!$user) {
+ if (!$this->nickname) {
$this->showForm(_('No such user.'));
return;
}
+ $user = User::staticGet('nickname', $this->nickname);
+
$this->profile_url = $this->trimmed('profile_url');
if (!$this->profile_url) {
- $this->showForm(_('No such user.'));
+ $this->showForm(_('No such user'));
return;
}
- if (!Validate::uri($this->profile_url, array('allowed_schemes' => array('http', 'https')))) {
+ if (!common_valid_http_url($this->profile_url)) {
$this->showForm(_('Invalid profile URL (bad format)'));
return;
}
- $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
- $yadis = Auth_Yadis_Yadis::discover($this->profile_url, $fetcher);
-
- if (!$yadis || $yadis->failed) {
- $this->showForm(_('Not a valid profile URL (no YADIS document).'));
- return;
- }
-
- # XXX: a little liberal for sites that accidentally put whitespace before the xml declaration
-
- $xrds =& Auth_Yadis_XRDS::parseXRDS(trim($yadis->response_text));
-
- if (!$xrds) {
- $this->showForm(_('Not a valid profile URL (no XRDS defined).'));
- return;
- }
-
- $omb = $this->getOmb($xrds);
-
- if (!$omb) {
- $this->showForm(_('Not a valid profile URL (incorrect services).'));
- return;
- }
-
- if (omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]) ==
- common_local_url('requesttoken'))
- {
- $this->showForm(_('That\'s a local profile! Login to subscribe.'));
+ try {
+ $service = new OMB_Service_Consumer($this->profile_url,
+ common_root_url(),
+ omb_oauth_datastore());
+ } catch (OMB_InvalidYadisException $e) {
+ $this->showForm(_('Not a valid profile URL (no YADIS document or ' .
+ 'no or invalid XRDS defined).'));
return;
}
- if (User::staticGet('uri', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]))) {
- $this->showForm(_('That\'s a local profile! Login to subscribe.'));
+ if ($service->getServiceURI(OAUTH_ENDPOINT_REQUEST) ==
+ common_local_url('requesttoken') ||
+ User::staticGet('uri', $service->getRemoteUserURI())) {
+ $this->showForm(_('That’s a local profile! Login to subscribe.'));
return;
}
- list($token, $secret) = $this->requestToken($omb);
-
- if (!$token || !$secret) {
- $this->showForm(_('Couldn\'t get a request token.'));
+ try {
+ $service->requestToken();
+ } catch (OMB_RemoteServiceException $e) {
+ $this->showForm(_('Couldn’t get a request token.'));
return;
}
- $this->requestAuthorization($user, $omb, $token, $secret);
- }
-
- function getUser()
- {
- $user = null;
- if ($this->nickname) {
- $user = User::staticGet('nickname', $this->nickname);
- }
- return $user;
- }
-
- function getOmb($xrds)
- {
- static $omb_endpoints = array(OMB_ENDPOINT_UPDATEPROFILE, OMB_ENDPOINT_POSTNOTICE);
- static $oauth_endpoints = array(OAUTH_ENDPOINT_REQUEST, OAUTH_ENDPOINT_AUTHORIZE,
- OAUTH_ENDPOINT_ACCESS);
- $omb = array();
-
- # XXX: the following code could probably be refactored to eliminate dupes
-
- $oauth_services = omb_get_services($xrds, OAUTH_DISCOVERY);
-
- if (!$oauth_services) {
- return null;
- }
-
- $oauth_service = $oauth_services[0];
-
- $oauth_xrd = $this->getXRD($oauth_service, $xrds);
-
- if (!$oauth_xrd) {
- return null;
- }
-
- if (!$this->addServices($oauth_xrd, $oauth_endpoints, $omb)) {
- return null;
- }
-
- $omb_services = omb_get_services($xrds, OMB_NAMESPACE);
-
- if (!$omb_services) {
- return null;
- }
-
- $omb_service = $omb_services[0];
-
- $omb_xrd = $this->getXRD($omb_service, $xrds);
-
- if (!$omb_xrd) {
- return null;
- }
-
- if (!$this->addServices($omb_xrd, $omb_endpoints, $omb)) {
- return null;
- }
-
- # XXX: check that we got all the services we needed
-
- foreach (array_merge($omb_endpoints, $oauth_endpoints) as $type) {
- if (!array_key_exists($type, $omb) || !$omb[$type]) {
- return null;
- }
- }
-
- if (!omb_local_id($omb[OAUTH_ENDPOINT_REQUEST])) {
- return null;
- }
-
- return $omb;
- }
-
- function getXRD($main_service, $main_xrds)
- {
- $uri = omb_service_uri($main_service);
- if (strpos($uri, "#") !== 0) {
- # FIXME: more rigorous handling of external service definitions
- return null;
- }
- $id = substr($uri, 1);
- $nodes = $main_xrds->allXrdNodes;
- $parser = $main_xrds->parser;
- foreach ($nodes as $node) {
- $attrs = $parser->attributes($node);
- if (array_key_exists('xml:id', $attrs) &&
- $attrs['xml:id'] == $id) {
- # XXX: trick the constructor into thinking this is the only node
- $bogus_nodes = array($node);
- return new Auth_Yadis_XRDS($parser, $bogus_nodes);
- }
- }
- return null;
- }
-
- function addServices($xrd, $types, &$omb)
- {
- foreach ($types as $type) {
- $matches = omb_get_services($xrd, $type);
- if ($matches) {
- $omb[$type] = $matches[0];
- } else {
- # no match for type
- return false;
- }
- }
- return true;
- }
-
- function requestToken($omb)
- {
- $con = omb_oauth_consumer();
-
- $url = omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]);
-
- # XXX: Is this the right thing to do? Strip off GET params and make them
- # POST params? Seems wrong to me.
-
- $parsed = parse_url($url);
- $params = array();
- parse_str($parsed['query'], $params);
-
- $req = OAuthRequest::from_consumer_and_token($con, null, "POST", $url, $params);
-
- $listener = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]);
-
- if (!$listener) {
- return null;
- }
-
- $req->set_parameter('omb_listener', $listener);
- $req->set_parameter('omb_version', OMB_VERSION_01);
-
- # XXX: test to see if endpoint accepts this signature method
-
- $req->sign_request(omb_hmac_sha1(), $con, null);
-
- # We re-use this tool's fetcher, since it's pretty good
-
- $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
-
- $result = $fetcher->post($req->get_normalized_http_url(),
- $req->to_postdata(),
- array('User-Agent: StatusNet/' . STATUSNET_VERSION));
- if ($result->status != 200) {
- return null;
- }
-
- parse_str($result->body, $return);
-
- return array($return['oauth_token'], $return['oauth_token_secret']);
- }
-
- function requestAuthorization($user, $omb, $token, $secret)
- {
- $con = omb_oauth_consumer();
- $tok = new OAuthToken($token, $secret);
-
- $url = omb_service_uri($omb[OAUTH_ENDPOINT_AUTHORIZE]);
-
- # XXX: Is this the right thing to do? Strip off GET params and make them
- # POST params? Seems wrong to me.
-
- $parsed = parse_url($url);
- $params = array();
- parse_str($parsed['query'], $params);
-
- $req = OAuthRequest::from_consumer_and_token($con, $tok, 'GET', $url, $params);
-
- # We send over a ton of information. This lets the other
- # server store info about our user, and it lets the current
- # user decide if they really want to authorize the subscription.
-
- $req->set_parameter('omb_version', OMB_VERSION_01);
- $req->set_parameter('omb_listener', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]));
- $req->set_parameter('omb_listenee', $user->uri);
- $req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname));
- $req->set_parameter('omb_listenee_nickname', $user->nickname);
- $req->set_parameter('omb_listenee_license', common_config('license', 'url'));
-
+ /* Create an OMB_Profile from $user. */
$profile = $user->getProfile();
if (!$profile) {
common_log_db_error($user, 'SELECT', __FILE__);
@@ -367,49 +192,16 @@ class RemotesubscribeAction extends Action
return;
}
- if (!is_null($profile->fullname)) {
- $req->set_parameter('omb_listenee_fullname', $profile->fullname);
- }
- if (!is_null($profile->homepage)) {
- $req->set_parameter('omb_listenee_homepage', $profile->homepage);
- }
- if (!is_null($profile->bio)) {
- $req->set_parameter('omb_listenee_bio', $profile->bio);
- }
- if (!is_null($profile->location)) {
- $req->set_parameter('omb_listenee_location', $profile->location);
- }
- $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
- if ($avatar) {
- $req->set_parameter('omb_listenee_avatar', $avatar->url);
- }
-
- # XXX: add a nonce to prevent replay attacks
-
- $req->set_parameter('oauth_callback', common_local_url('finishremotesubscribe'));
-
- # XXX: test to see if endpoint accepts this signature method
-
- $req->sign_request(omb_hmac_sha1(), $con, $tok);
-
- # store all our info here
-
- $omb['listenee'] = $user->nickname;
- $omb['listener'] = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]);
- $omb['token'] = $token;
- $omb['secret'] = $secret;
- # call doesn't work after bounce back so we cache; maybe serialization issue...?
- $omb['access_token_url'] = omb_service_uri($omb[OAUTH_ENDPOINT_ACCESS]);
- $omb['post_notice_url'] = omb_service_uri($omb[OMB_ENDPOINT_POSTNOTICE]);
- $omb['update_profile_url'] = omb_service_uri($omb[OMB_ENDPOINT_UPDATEPROFILE]);
+ $target_url = $service->requestAuthorization(
+ profile_to_omb_profile($user->uri, $profile),
+ common_local_url('finishremotesubscribe'));
common_ensure_session();
- $_SESSION['oauth_authorization_request'] = $omb;
-
- # Redirect to authorization service
+ $_SESSION['oauth_authorization_request'] = serialize($service);
- common_redirect($req->to_url(), 303);
- return;
+ /* Redirect to the remote service for authorization. */
+ common_redirect($target_url, 303);
}
}
+?>
diff --git a/actions/replies.php b/actions/replies.php
index cca430230..6003ad30b 100644
--- a/actions/replies.php
+++ b/actions/replies.php
@@ -192,9 +192,7 @@ class RepliesAction extends OwnerDesignAction
}
}
else {
- $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and then nudge %s or post a notice to his or her attention.'),
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
- $this->user->nickname);
+ $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
diff --git a/actions/repliesrss.php b/actions/repliesrss.php
index c71c9226f..76aae21ad 100644
--- a/actions/repliesrss.php
+++ b/actions/repliesrss.php
@@ -38,6 +38,7 @@ class RepliesrssAction extends Rss10Action
$this->clientError(_('No such user.'));
return false;
} else {
+ $this->notices = $this->getNotices($this->limit);
return true;
}
}
diff --git a/actions/requesttoken.php b/actions/requesttoken.php
index 48fe1db08..e095161a7 100644
--- a/actions/requesttoken.php
+++ b/actions/requesttoken.php
@@ -34,6 +34,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
}
require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
/**
* Request token action class.
@@ -49,17 +50,17 @@ class RequesttokenAction extends Action
{
/**
* Is read only?
- *
+ *
* @return boolean false
*/
- function isReadOnly($args)
+ function isReadOnly()
{
return false;
}
-
+
/**
* Class handler.
- *
+ *
* @param array $args array of arguments
*
* @return void
@@ -68,14 +69,12 @@ class RequesttokenAction extends Action
{
parent::handle($args);
try {
- common_remove_magic_from_request();
- $req = OAuthRequest::from_request('POST', common_local_url('requesttoken'));
- $server = omb_oauth_server();
- $token = $server->fetch_request_token($req);
- print $token;
- } catch (OAuthException $e) {
+ $srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
+ omb_oauth_server());
+ $srv->writeRequestToken();
+ } catch (Exception $e) {
$this->serverError($e->getMessage());
}
}
}
-
+?>
diff --git a/actions/showfavorites.php b/actions/showfavorites.php
index 0f7a66330..b96d2af37 100644
--- a/actions/showfavorites.php
+++ b/actions/showfavorites.php
@@ -196,9 +196,7 @@ class ShowfavoritesAction extends OwnerDesignAction
}
}
else {
- $message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Why not [register an account](%%%%action.%s%%%%) and then post something interesting they would add to their favorites :)'),
- $this->user->nickname,
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+ $message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Why not [register an account](%%%%action.register%%%%) and then post something interesting they would add to their favorites :)'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
diff --git a/actions/showgroup.php b/actions/showgroup.php
index 8157ee3c8..ff9949762 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -450,9 +450,8 @@ class ShowgroupAction extends GroupDesignAction
$m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [StatusNet](http://status.net/) tool. Its members share ' .
'short messages about their life and interests. '.
- '[Join now](%%%%action.%s%%%%) to become part of this group and many more! ([Read more](%%%%doc.help%%%%))'),
- $this->group->nickname,
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+ '[Join now](%%%%action.register%%%%) to become part of this group and many more! ([Read more](%%%%doc.help%%%%))'),
+ $this->group->nickname);
} else {
$m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [StatusNet](http://status.net/) tool. Its members share ' .
diff --git a/actions/shownotice.php b/actions/shownotice.php
index 3bc52b2db..41408c23c 100644
--- a/actions/shownotice.php
+++ b/actions/shownotice.php
@@ -84,7 +84,13 @@ class ShownoticeAction extends OwnerDesignAction
$this->notice = Notice::staticGet($id);
if (empty($this->notice)) {
- $this->clientError(_('No such notice.'), 404);
+ // Did we used to have it, and it got deleted?
+ $deleted = Deleted_notice::staticGet($id);
+ if (!empty($deleted)) {
+ $this->clientError(_('Notice deleted.'), 410);
+ } else {
+ $this->clientError(_('No such notice.'), 404);
+ }
return false;
}
diff --git a/actions/showstream.php b/actions/showstream.php
index 4d3067eed..cdac4f47b 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -358,9 +358,7 @@ class ShowstreamAction extends ProfileAction
}
}
else {
- $message .= sprintf(_('Why not [register an account](%%%%action.%s%%%%) and then nudge %s or post a notice to his or her attention.'),
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
- $this->user->nickname);
+ $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
@@ -380,8 +378,13 @@ class ShowstreamAction extends ProfileAction
$this->showEmptyListMessage();
}
+ $args = array('nickname' => $this->user->nickname);
+ if (!empty($this->tag))
+ {
+ $args['tag'] = $this->tag;
+ }
$this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
- 'showstream', array('nickname' => $this->user->nickname));
+ 'showstream', $args);
}
function showAnonymousMessage()
@@ -389,10 +392,8 @@ class ShowstreamAction extends ProfileAction
if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
$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.%s%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
- $this->user->nickname,
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin',
- $this->user->nickname);
+ '[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
+ $this->user->nickname, $this->user->nickname);
} else {
$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. '),
diff --git a/actions/smssettings.php b/actions/smssettings.php
index b956cceba..672abcef8 100644
--- a/actions/smssettings.php
+++ b/actions/smssettings.php
@@ -69,6 +69,12 @@ class SmssettingsAction extends ConnectSettingsAction
return _('You can receive SMS messages through email from %%site.name%%.');
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('sms');
+ }
+
/**
* Content area of the page
*
diff --git a/actions/subscribers.php b/actions/subscribers.php
index f7d08d9d0..df9ec9961 100644
--- a/actions/subscribers.php
+++ b/actions/subscribers.php
@@ -111,9 +111,7 @@ class SubscribersAction extends GalleryAction
}
}
else {
- $message = sprintf(_('%s has no subscribers. Why not [register an account](%%%%action.%s%%%%) and be the first?'),
- $this->user->nickname,
- (!common_config('site','openidonly')) ? 'register' : 'openidlogin');
+ $message = sprintf(_('%s has no subscribers. Why not [register an account](%%%%action.register%%%%) and be the first?'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
diff --git a/actions/subscriptions.php b/actions/subscriptions.php
index b1c668228..cc7b38ee4 100644
--- a/actions/subscriptions.php
+++ b/actions/subscriptions.php
@@ -107,6 +107,12 @@ class SubscriptionsAction extends GalleryAction
array('nickname' => $this->user->nickname));
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('tag');
+ }
+
function showEmptyListMessage()
{
if (common_logged_in()) {
diff --git a/actions/twitapidirect_messages.php b/actions/twitapidirect_messages.php
index dbe55804b..08b8f4e9c 100644
--- a/actions/twitapidirect_messages.php
+++ b/actions/twitapidirect_messages.php
@@ -141,9 +141,10 @@ class Twitapidirect_messagesAction extends TwitterapiAction
$code = 406, $apidata['content-type']);
} else {
$content_shortened = common_shorten_links($content);
- if (mb_strlen($content_shortened) > 140) {
- $this->clientError(_('That\'s too long. Max message size is 140 chars.'),
- $code = 406, $apidata['content-type']);
+ if (Message::contentTooLong($content_shortened)) {
+ $this->clientError(sprintf(_('That\'s too long. Max message size is %d chars.'),
+ Message::maxContent()),
+ $code = 406, $apidata['content-type']);
return;
}
}
diff --git a/actions/twitapigroups.php b/actions/twitapigroups.php
index 4deb1b764..493144e77 100644
--- a/actions/twitapigroups.php
+++ b/actions/twitapigroups.php
@@ -293,6 +293,105 @@ require_once INSTALLDIR.'/lib/twitterapi.php';
}
}
+ function join($args, $apidata)
+ {
+ parent::handle($args);
+
+ common_debug("in groups api action");
+
+ $this->auth_user = $apidata['user'];
+ $group = $this->get_group($apidata['api_arg'], $apidata);
+
+ if (empty($group)) {
+ $this->clientError('Not Found', 404, $apidata['content-type']);
+ return false;
+ }
+
+ if($this->auth_user->isMember($group)){
+ $this->clientError(_('You are already a member of that group'), $code = 403);
+ return false;
+ }
+
+ if (Group_block::isBlocked($group, $this->auth_user->getProfile())) {
+ $this->clientError(_('You have been blocked from that group by the admin.'), 403);
+ return false;
+ }
+
+ $member = new Group_member();
+
+ $member->group_id = $group->id;
+ $member->profile_id = $this->auth_user->id;
+ $member->created = common_sql_now();
+
+ $result = $member->insert();
+
+ if (!$result) {
+ common_log_db_error($member, 'INSERT', __FILE__);
+ $this->serverError(sprintf(_('Could not join user %s to group %s'),
+ $this->auth_user->nickname, $group->nickname));
+ }
+
+ switch($apidata['content-type']) {
+ case 'xml':
+ $this->show_single_xml_group($group);
+ break;
+ case 'json':
+ $this->show_single_json_group($group);
+ break;
+ default:
+ $this->clientError(_('API method not found!'), $code = 404);
+ }
+ }
+
+ function leave($args, $apidata)
+ {
+ parent::handle($args);
+
+ common_debug("in groups api action");
+
+ $this->auth_user = $apidata['user'];
+ $group = $this->get_group($apidata['api_arg'], $apidata);
+
+ if (empty($group)) {
+ $this->clientError('Not Found', 404, $apidata['content-type']);
+ return false;
+ }
+
+ if(! $this->auth_user->isMember($group)){
+ $this->clientError(_('You are not a member of that group'), $code = 403);
+ return false;
+ }
+
+ $member = new Group_member();
+
+ $member->group_id = $group->id;
+ $member->profile_id = $this->auth_user->id;
+
+ if (!$member->find(true)) {
+ $this->serverError(_('Could not find membership record.'));
+ return;
+ }
+
+ $result = $member->delete();
+
+ if (!$result) {
+ common_log_db_error($member, 'INSERT', __FILE__);
+ $this->serverError(sprintf(_('Could not remove user %s to group %s'),
+ $this->auth_user->nickname, $group->nickname));
+ }
+
+ switch($apidata['content-type']) {
+ case 'xml':
+ $this->show_single_xml_group($group);
+ break;
+ case 'json':
+ $this->show_single_json_group($group);
+ break;
+ default:
+ $this->clientError(_('API method not found!'), $code = 404);
+ }
+ }
+
function is_member($args, $apidata)
{
parent::handle($args);
@@ -326,4 +425,29 @@ require_once INSTALLDIR.'/lib/twitterapi.php';
$this->clientError(_('API method not found!'), $code = 404);
}
}
+
+ function create($args, $apidata)
+ {
+ die("todo");
+ }
+
+ function update($args, $apidata)
+ {
+ die("todo");
+ }
+
+ function update_group_logo($args, $apidata)
+ {
+ die("todo");
+ }
+
+ function destroy($args, $apidata)
+ {
+ die("todo");
+ }
+
+ function tag($args, $apidata)
+ {
+ die("todo");
+ }
}
diff --git a/actions/twitapistatuses.php b/actions/twitapistatuses.php
index 5e2867ea8..2f10ff966 100644
--- a/actions/twitapistatuses.php
+++ b/actions/twitapistatuses.php
@@ -247,14 +247,15 @@ class TwitapistatusesAction extends TwitterapiAction
$status_shortened = common_shorten_links($status);
- if (mb_strlen($status_shortened) > 140) {
+ if (Notice::contentTooLong($status_shortened)) {
// XXX: Twitter truncates anything over 140, flags the status
// as "truncated." Sending this error may screw up some clients
// that assume Twitter will truncate for them. Should we just
// truncate too? -- Zach
- $this->clientError(_('That\'s too long. Max notice size is 140 chars.'),
- $code = 406, $apidata['content-type']);
+ $this->clientError(sprintf(_('That\'s too long. Max notice size is %d chars.'),
+ Notice::maxContent()),
+ $code = 406, $apidata['content-type']);
return;
}
}
@@ -401,8 +402,14 @@ class TwitapistatusesAction extends TwitterapiAction
} else {
// XXX: Twitter just sets a 404 header and doens't bother
// to return an err msg
- $this->clientError(_('No status with that ID found.'),
- 404, $apidata['content-type']);
+ $deleted = Deleted_notice::staticGet($notice_id);
+ if (!empty($deleted)) {
+ $this->clientError(_('Status deleted.'),
+ 410, $apidata['content-type']);
+ } else {
+ $this->clientError(_('No status with that ID found.'),
+ 404, $apidata['content-type']);
+ }
}
}
diff --git a/actions/updateprofile.php b/actions/updateprofile.php
index 9a4cf8e46..3cec9523c 100644
--- a/actions/updateprofile.php
+++ b/actions/updateprofile.php
@@ -1,5 +1,16 @@
<?php
-/*
+/**
+ * Handle an updateprofile action
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@status.net>
+ * @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.
*
@@ -19,165 +30,54 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
+/**
+ * Handle an updateprofile action
+ *
+ * @category Action
+ * @package Laconica
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@controlyourself.ca>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://laconi.ca/
+ */
class UpdateprofileAction extends Action
{
-
- function handle($args)
- {
- parent::handle($args);
- try {
- common_remove_magic_from_request();
- $req = OAuthRequest::from_request('POST', common_local_url('updateprofile'));
- # Note: server-to-server function!
- $server = omb_oauth_server();
- list($consumer, $token) = $server->verify_request($req);
- if ($this->update_profile($req, $consumer, $token)) {
- header('HTTP/1.1 200 OK');
- header('Content-type: text/plain');
- print "omb_version=".OMB_VERSION_01;
- }
- } catch (OAuthException $e) {
- $this->serverError($e->getMessage());
- return;
- }
- }
- function update_profile($req, $consumer, $token)
+ /**
+ * For initializing members of the class.
+ *
+ * @param array $argarray misc. arguments
+ *
+ * @return boolean true
+ */
+ function prepare($argarray)
{
- $version = $req->get_parameter('omb_version');
- if ($version != OMB_VERSION_01) {
- $this->clientError(_('Unsupported OMB version'), 400);
+ parent::prepare($argarray);
+ $license = $_POST['omb_listenee_license'];
+ $site_license = common_config('license', 'url');
+ if (!common_compatible_license($license, $site_license)) {
+ $this->clientError(sprintf(_('Listenee stream license ‘%s’ is not '.
+ 'compatible with site license ‘%s’.'),
+ $license, $site_license));
return false;
}
- # First, check to see if listenee exists
- $listenee = $req->get_parameter('omb_listenee');
- $remote = Remote_profile::staticGet('uri', $listenee);
- if (!$remote) {
- $this->clientError(_('Profile unknown'), 404);
- return false;
- }
- # Second, check to see if they should be able to post updates!
- # We see if there are any subscriptions to that remote user with
- # the given token.
-
- $sub = new Subscription();
- $sub->subscribed = $remote->id;
- $sub->token = $token->key;
- if (!$sub->find(true)) {
- $this->clientError(_('You did not send us that profile'), 403);
- return false;
- }
-
- $profile = Profile::staticGet('id', $remote->id);
- if (!$profile) {
- # This one is our fault
- $this->serverError(_('Remote profile with no matching profile'), 500);
- return false;
- }
- $nickname = $req->get_parameter('omb_listenee_nickname');
- if ($nickname && !Validate::string($nickname, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
- $this->clientError(_('Nickname must have only lowercase letters and numbers and no spaces.'));
- return false;
- }
- $license = $req->get_parameter('omb_listenee_license');
- if ($license && !common_valid_http_url($license)) {
- $this->clientError(sprintf(_("Invalid license URL '%s'"), $license));
- return false;
- }
- $profile_url = $req->get_parameter('omb_listenee_profile');
- if ($profile_url && !common_valid_http_url($profile_url)) {
- $this->clientError(sprintf(_("Invalid profile URL '%s'."), $profile_url));
- return false;
- }
- # optional stuff
- $fullname = $req->get_parameter('omb_listenee_fullname');
- if ($fullname && mb_strlen($fullname) > 255) {
- $this->clientError(_("Full name is too long (max 255 chars)."));
- return false;
- }
- $homepage = $req->get_parameter('omb_listenee_homepage');
- if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) {
- $this->clientError(sprintf(_("Invalid homepage '%s'"), $homepage));
- return false;
- }
- $bio = $req->get_parameter('omb_listenee_bio');
- if ($bio && mb_strlen($bio) > 140) {
- $this->clientError(_("Bio is too long (max 140 chars)."));
- return false;
- }
- $location = $req->get_parameter('omb_listenee_location');
- if ($location && mb_strlen($location) > 255) {
- $this->clientError(_("Location is too long (max 255 chars)."));
- return false;
- }
- $avatar = $req->get_parameter('omb_listenee_avatar');
- if ($avatar) {
- if (!common_valid_http_url($avatar) || strlen($avatar) > 255) {
- $this->clientError(sprintf(_("Invalid avatar URL '%s'"), $avatar));
- return false;
- }
- $size = @getimagesize($avatar);
- if (!$size) {
- $this->clientError(sprintf(_("Can't read avatar URL '%s'"), $avatar));
- return false;
- }
- if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) {
- $this->clientError(sprintf(_("Wrong size image at '%s'"), $avatar));
- return false;
- }
- if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG,
- IMAGETYPE_PNG))) {
- $this->clientError(sprintf(_("Wrong image type for '%s'"), $avatar));
- return false;
- }
- }
-
- $orig_profile = clone($profile);
+ return true;
+ }
- /* Use values even if they are an empty string. Parsing an empty string in
- updateProfile is the specified way of clearing a parameter in OMB. */
- if (!is_null($nickname)) {
- $profile->nickname = $nickname;
- }
- if (!is_null($profile_url)) {
- $profile->profileurl = $profile_url;
- }
- if (!is_null($fullname)) {
- $profile->fullname = $fullname;
- }
- if (!is_null($homepage)) {
- $profile->homepage = $homepage;
- }
- if (!is_null($bio)) {
- $profile->bio = $bio;
- }
- if (!is_null($location)) {
- $profile->location = $location;
- }
+ function handle($args)
+ {
+ parent::handle($args);
- if (!$profile->update($orig_profile)) {
- $this->serverError(_('Could not save new profile info'), 500);
- return false;
- } else {
- if ($avatar) {
- $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar');
- copy($avatar, $temp_filename);
- $imagefile = new ImageFile($profile->id, $temp_filename);
- $filename = Avatar::filename($profile->id,
- image_type_to_extension($imagefile->type),
- null,
- common_timestamp());
- rename($temp_filename, Avatar::path($filename));
- if (!$profile->setOriginal($filename)) {
- $this->serverError(_('Could not save avatar info'), 500);
- return false;
- }
- }
- return true;
+ try {
+ $srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
+ omb_oauth_server());
+ $srv->handleUpdateProfile();
+ } catch (Exception $e) {
+ $this->serverError($e->getMessage());
+ return;
}
}
}
diff --git a/actions/userauthorization.php b/actions/userauthorization.php
index a9ac1f256..dc59e6c94 100644
--- a/actions/userauthorization.php
+++ b/actions/userauthorization.php
@@ -1,5 +1,16 @@
<?php
-/*
+/**
+ * Let the user authorize a remote subscription request
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@status.net>
+ * @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.
*
@@ -19,7 +30,9 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
+require_once INSTALLDIR.'/extlib/libomb/profile.php';
define('TIMESTAMP_THRESHOLD', 300);
class UserauthorizationAction extends Action
@@ -32,46 +45,58 @@ class UserauthorizationAction extends Action
parent::handle($args);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- # CSRF protection
+ /* Use a session token for CSRF protection. */
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
- $params = $this->getStoredParams();
- $this->showForm($params, _('There was a problem with your session token. '.
- 'Try again, please.'));
+ $srv = $this->getStoredParams();
+ $this->showForm($srv->getRemoteUser(), _('There was a problem ' .
+ 'with your session token. Try again, ' .
+ 'please.'));
return;
}
- # We've shown the form, now post user's choice
+ /* We've shown the form, now post user's choice. */
$this->sendAuthorization();
} else {
if (!common_logged_in()) {
- # Go log in, and then come back
+ /* Go log in, and then come back. */
common_set_returnto($_SERVER['REQUEST_URI']);
- if (!common_config('site', 'openidonly')) {
- common_redirect(common_local_url('login'));
- } else {
- common_redirect(common_local_url('openidlogin'));
- }
+ common_redirect(common_local_url('login'));
+ return;
+ }
+
+ $user = common_current_user();
+ $profile = $user->getProfile();
+ if (!$profile) {
+ common_log_db_error($user, 'SELECT', __FILE__);
+ $this->serverError(_('User without matching profile'));
return;
}
+ /* TODO: If no token is passed the user should get a prompt to enter
+ it according to OAuth Core 1.0. */
try {
- $this->validateRequest();
- $this->storeParams($_GET);
- $this->showForm($_GET);
- } catch (OAuthException $e) {
+ $this->validateOmb();
+ $srv = new OMB_Service_Provider(
+ profile_to_omb_profile($user->uri, $profile),
+ omb_oauth_datastore());
+
+ $remote_user = $srv->handleUserAuth();
+ } catch (Exception $e) {
$this->clearParams();
$this->clientError($e->getMessage());
return;
}
+ $this->storeParams($srv);
+ $this->showForm($remote_user);
}
}
function showForm($params, $error=null)
{
$this->params = $params;
- $this->error = $error;
+ $this->error = $error;
$this->showPage();
}
@@ -83,23 +108,24 @@ class UserauthorizationAction extends Action
function showPageNotice()
{
$this->element('p', null, _('Please check these details to make sure '.
- 'that you want to subscribe to this user\'s notices. '.
- 'If you didn\'t just ask to subscribe to someone\'s notices, '.
- 'click "Reject".'));
+ 'that you want to subscribe to this ' .
+ 'user’s notices. If you didn’t just ask ' .
+ 'to subscribe to someone’s notices, '.
+ 'click “Reject”.'));
}
function showContent()
{
$params = $this->params;
- $nickname = $params['omb_listenee_nickname'];
- $profile = $params['omb_listenee_profile'];
- $license = $params['omb_listenee_license'];
- $fullname = $params['omb_listenee_fullname'];
- $homepage = $params['omb_listenee_homepage'];
- $bio = $params['omb_listenee_bio'];
- $location = $params['omb_listenee_location'];
- $avatar = $params['omb_listenee_avatar'];
+ $nickname = $params->getNickname();
+ $profile = $params->getProfileURL();
+ $license = $params->getLicenseURL();
+ $fullname = $params->getFullname();
+ $homepage = $params->getHomepage();
+ $bio = $params->getBio();
+ $location = $params->getLocation();
+ $avatar = $params->getAvatarURL();
$this->elementStart('div', array('class' => 'profile'));
$this->elementStart('div', 'entity_profile vcard');
@@ -176,11 +202,14 @@ class UserauthorizationAction extends Action
'id' => 'userauthorization',
'class' => 'form_user_authorization',
'name' => 'userauthorization',
- 'action' => common_local_url('userauthorization')));
+ 'action' => common_local_url(
+ 'userauthorization')));
$this->hidden('token', common_session_token());
- $this->submit('accept', _('Accept'), 'submit accept', null, _('Subscribe to this user'));
- $this->submit('reject', _('Reject'), 'submit reject', null, _('Reject this subscription'));
+ $this->submit('accept', _('Accept'), 'submit accept', null,
+ _('Subscribe to this user'));
+ $this->submit('reject', _('Reject'), 'submit reject', null,
+ _('Reject this subscription'));
$this->elementEnd('form');
$this->elementEnd('li');
$this->elementEnd('ul');
@@ -190,191 +219,27 @@ class UserauthorizationAction extends Action
function sendAuthorization()
{
- $params = $this->getStoredParams();
+ $srv = $this->getStoredParams();
- if (!$params) {
+ if (is_null($srv)) {
$this->clientError(_('No authorization request!'));
return;
}
- $callback = $params['oauth_callback'];
-
- if ($this->arg('accept')) {
- if (!$this->authorizeToken($params)) {
- $this->clientError(_('Error authorizing token'));
- }
- if (!$this->saveRemoteProfile($params)) {
- $this->clientError(_('Error saving remote profile'));
- }
- if (!$callback) {
- $this->showAcceptMessage($params['oauth_token']);
- } else {
- $newparams = array();
- $newparams['oauth_token'] = $params['oauth_token'];
- $newparams['omb_version'] = OMB_VERSION_01;
- $user = User::staticGet('uri', $params['omb_listener']);
- $profile = $user->getProfile();
- if (!$profile) {
- common_log_db_error($user, 'SELECT', __FILE__);
- $this->serverError(_('User without matching profile'));
- return;
- }
- $newparams['omb_listener_nickname'] = $user->nickname;
- $newparams['omb_listener_profile'] = common_local_url('showstream',
- array('nickname' => $user->nickname));
- if (!is_null($profile->fullname)) {
- $newparams['omb_listener_fullname'] = $profile->fullname;
- }
- if (!is_null($profile->homepage)) {
- $newparams['omb_listener_homepage'] = $profile->homepage;
- }
- if (!is_null($profile->bio)) {
- $newparams['omb_listener_bio'] = $profile->bio;
- }
- if (!is_null($profile->location)) {
- $newparams['omb_listener_location'] = $profile->location;
- }
- $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
- if ($avatar) {
- $newparams['omb_listener_avatar'] = $avatar->url;
- }
- $parts = array();
- foreach ($newparams as $k => $v) {
- $parts[] = $k . '=' . OAuthUtil::urlencode_rfc3986($v);
- }
- $query_string = implode('&', $parts);
- $parsed = parse_url($callback);
- $url = $callback . (($parsed['query']) ? '&' : '?') . $query_string;
- common_redirect($url, 303);
- }
- } else {
- if (!$callback) {
- $this->showRejectMessage();
- } else {
- # XXX: not 100% sure how to signal failure... just redirect without token?
- common_redirect($callback, 303);
- }
- }
- }
-
- function authorizeToken(&$params)
- {
- $token_field = $params['oauth_token'];
- $rt = new Token();
- $rt->tok = $token_field;
- $rt->type = 0;
- $rt->state = 0;
- if ($rt->find(true)) {
- $orig_rt = clone($rt);
- $rt->state = 1; # Authorized but not used
- if ($rt->update($orig_rt)) {
- return true;
- }
- }
- return false;
- }
-
- # XXX: refactor with similar code in finishremotesubscribe.php
-
- function saveRemoteProfile(&$params)
- {
- # FIXME: we should really do this when the consumer comes
- # back for an access token. If they never do, we've got stuff in a
- # weird state.
-
- $nickname = $params['omb_listenee_nickname'];
- $fullname = $params['omb_listenee_fullname'];
- $profile_url = $params['omb_listenee_profile'];
- $homepage = $params['omb_listenee_homepage'];
- $bio = $params['omb_listenee_bio'];
- $location = $params['omb_listenee_location'];
- $avatar_url = $params['omb_listenee_avatar'];
-
- $listenee = $params['omb_listenee'];
- $remote = Remote_profile::staticGet('uri', $listenee);
-
- if ($remote) {
- $exists = true;
- $profile = Profile::staticGet($remote->id);
- $orig_remote = clone($remote);
- $orig_profile = clone($profile);
- } else {
- $exists = false;
- $remote = new Remote_profile();
- $remote->uri = $listenee;
- $profile = new Profile();
- }
-
- $profile->nickname = $nickname;
- $profile->profileurl = $profile_url;
-
- if (!is_null($fullname)) {
- $profile->fullname = $fullname;
- }
- if (!is_null($homepage)) {
- $profile->homepage = $homepage;
- }
- if (!is_null($bio)) {
- $profile->bio = $bio;
- }
- if (!is_null($location)) {
- $profile->location = $location;
+ $accepted = $this->arg('accept');
+ try {
+ list($val, $token) = $srv->continueUserAuth($accepted);
+ } catch (Exception $e) {
+ $this->clientError($e->getMessage());
+ return;
}
-
- if ($exists) {
- $profile->update($orig_profile);
+ if ($val !== false) {
+ common_redirect($val, 303);
+ } elseif ($accepted) {
+ $this->showAcceptMessage($token);
} else {
- $profile->created = DB_DataObject_Cast::dateTime(); # current time
- $id = $profile->insert();
- if (!$id) {
- return false;
- }
- $remote->id = $id;
+ $this->showRejectMessage();
}
-
- if ($exists) {
- if (!$remote->update($orig_remote)) {
- return false;
- }
- } else {
- $remote->created = DB_DataObject_Cast::dateTime(); # current time
- if (!$remote->insert()) {
- return false;
- }
- }
-
- if ($avatar_url) {
- if (!$this->addAvatar($profile, $avatar_url)) {
- return false;
- }
- }
-
- $user = common_current_user();
-
- $sub = new Subscription();
- $sub->subscriber = $user->id;
- $sub->subscribed = $remote->id;
- $sub->token = $params['oauth_token']; # NOTE: request token, not valid for use!
- $sub->created = DB_DataObject_Cast::dateTime(); # current time
-
- if (!$sub->insert()) {
- return false;
- }
-
- return true;
- }
-
- function addAvatar($profile, $url)
- {
- $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar');
- copy($url, $temp_filename);
- $imagefile = new ImageFile($profile->id, $temp_filename);
- $filename = Avatar::filename($profile->id,
- image_type_to_extension($imagefile->type),
- null,
- common_timestamp());
- rename($temp_filename, Avatar::path($filename));
- return $profile->setOriginal($filename);
}
function showAcceptMessage($tok)
@@ -382,26 +247,28 @@ class UserauthorizationAction extends Action
common_show_header(_('Subscription authorized'));
$this->element('p', null,
_('The subscription has been authorized, but no '.
- 'callback URL was passed. Check with the site\'s instructions for '.
- 'details on how to authorize the subscription. Your subscription token is:'));
+ 'callback URL was passed. Check with the site’s ' .
+ 'instructions for details on how to authorize the ' .
+ 'subscription. Your subscription token is:'));
$this->element('blockquote', 'token', $tok);
common_show_footer();
}
- function showRejectMessage($tok)
+ function showRejectMessage()
{
common_show_header(_('Subscription rejected'));
$this->element('p', null,
_('The subscription has been rejected, but no '.
- 'callback URL was passed. Check with the site\'s instructions for '.
- 'details on how to fully reject the subscription.'));
+ 'callback URL was passed. Check with the site’s ' .
+ 'instructions for details on how to fully reject ' .
+ 'the subscription.'));
common_show_footer();
}
function storeParams($params)
{
common_ensure_session();
- $_SESSION['userauthorizationparams'] = $params;
+ $_SESSION['userauthorizationparams'] = serialize($params);
}
function clearParams()
@@ -413,138 +280,74 @@ class UserauthorizationAction extends Action
function getStoredParams()
{
common_ensure_session();
- $params = $_SESSION['userauthorizationparams'];
+ $params = unserialize($_SESSION['userauthorizationparams']);
return $params;
}
- # Throws an OAuthException if anything goes wrong
-
- function validateRequest()
- {
- /* Find token.
- TODO: If no token is passed the user should get a prompt to enter it
- according to OAuth Core 1.0 */
- $t = new Token();
- $t->tok = $_GET['oauth_token'];
- $t->type = 0;
- if (!$t->find(true)) {
- throw new OAuthException("Invalid request token: " . $_GET['oauth_token']);
- }
-
- $this->validateOmb();
- return true;
- }
-
function validateOmb()
{
- foreach (array('omb_version', 'omb_listener', 'omb_listenee',
- 'omb_listenee_profile', 'omb_listenee_nickname',
- 'omb_listenee_license') as $param)
- {
- if (!isset($_GET[$param]) || is_null($_GET[$param])) {
- throw new OAuthException("Required parameter '$param' not found");
- }
- }
- # Now, OMB stuff
- $version = $_GET['omb_version'];
- if ($version != OMB_VERSION_01) {
- throw new OAuthException("OpenMicroBlogging version '$version' not supported");
- }
$listener = $_GET['omb_listener'];
+ $listenee = $_GET['omb_listenee'];
+ $nickname = $_GET['omb_listenee_nickname'];
+ $profile = $_GET['omb_listenee_profile'];
+
$user = User::staticGet('uri', $listener);
if (!$user) {
- throw new OAuthException("Listener URI '$listener' not found here");
- }
- $cur = common_current_user();
- if ($cur->id != $user->id) {
- throw new OAuthException("Can't add for another user!");
- }
- $listenee = $_GET['omb_listenee'];
- if (!Validate::uri($listenee) &&
- !common_valid_tag($listenee)) {
- throw new OAuthException("Listenee URI '$listenee' not a recognizable URI");
+ throw new Exception(sprintf(_('Listener URI ‘%s’ not found here'),
+ $listener));
}
+
if (strlen($listenee) > 255) {
- throw new OAuthException("Listenee URI '$listenee' too long");
+ throw new Exception(sprintf(_('Listenee URI ‘%s’ is too long.'),
+ $listenee));
}
$other = User::staticGet('uri', $listenee);
if ($other) {
- throw new OAuthException("Listenee URI '$listenee' is local user");
+ throw new Exception(sprintf(_('Listenee URI ‘%s’ is a local user.'),
+ $listenee));
}
$remote = Remote_profile::staticGet('uri', $listenee);
if ($remote) {
- $sub = new Subscription();
+ $sub = new Subscription();
$sub->subscriber = $user->id;
$sub->subscribed = $remote->id;
if ($sub->find(true)) {
- throw new OAuthException("Already subscribed to user!");
+ throw new Exception('You are already subscribed to this user.');
}
}
- $nickname = $_GET['omb_listenee_nickname'];
- if (!Validate::string($nickname, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
- throw new OAuthException('Nickname must have only letters and numbers and no spaces.');
- }
- $profile = $_GET['omb_listenee_profile'];
- if (!common_valid_http_url($profile)) {
- throw new OAuthException("Invalid profile URL '$profile'.");
- }
- if ($profile == common_local_url('showstream', array('nickname' => $nickname))) {
- throw new OAuthException("Profile URL '$profile' is for a local user.");
- }
+ if ($profile == common_profile_url($nickname)) {
+ throw new Exception(sprintf(_('Profile URL ‘%s’ is for a local user.'),
+ $profile));
- $license = $_GET['omb_listenee_license'];
- if (!common_valid_http_url($license)) {
- throw new OAuthException("Invalid license URL '$license'.");
}
+
+ $license = $_GET['omb_listenee_license'];
$site_license = common_config('license', 'url');
if (!common_compatible_license($license, $site_license)) {
- throw new OAuthException("Listenee stream license '$license' not compatible with site license '$site_license'.");
- }
- # optional stuff
- $fullname = $_GET['omb_listenee_fullname'];
- if ($fullname && mb_strlen($fullname) > 255) {
- throw new OAuthException("Full name '$fullname' too long.");
- }
- $homepage = $_GET['omb_listenee_homepage'];
- if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) {
- throw new OAuthException("Invalid homepage '$homepage'");
- }
- $bio = $_GET['omb_listenee_bio'];
- if ($bio && mb_strlen($bio) > 140) {
- throw new OAuthException("Bio too long '$bio'");
- }
- $location = $_GET['omb_listenee_location'];
- if ($location && mb_strlen($location) > 255) {
- throw new OAuthException("Location too long '$location'");
+ throw new Exception(sprintf(_('Listenee stream license ‘%s’ is not ' .
+ 'compatible with site license ‘%s’.'),
+ $license, $site_license));
}
+
$avatar = $_GET['omb_listenee_avatar'];
if ($avatar) {
if (!common_valid_http_url($avatar) || strlen($avatar) > 255) {
- throw new OAuthException("Invalid avatar URL '$avatar'");
+ throw new Exception(sprintf(_('Avatar URL ‘%s’ is not valid.'),
+ $avatar));
}
$size = @getimagesize($avatar);
if (!$size) {
- throw new OAuthException("Can't read avatar URL '$avatar'");
- }
- if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) {
- throw new OAuthException("Wrong size image at '$avatar'");
+ throw new Exception(sprintf(_('Can’t read avatar URL ‘%s’.'),
+ $avatar));
}
if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG,
IMAGETYPE_PNG))) {
- throw new OAuthException("Wrong image type for '$avatar'");
+ throw new Exception(sprintf(_('Wrong image type for avatar URL '.
+ '‘%s’.'), $avatar));
}
}
- $callback = $_GET['oauth_callback'];
- if ($callback && !common_valid_http_url($callback)) {
- throw new OAuthException("Invalid callback URL '$callback'");
- }
- if ($callback && $callback == common_local_url('finishremotesubscribe')) {
- throw new OAuthException("Callback URL '$callback' is for local site.");
- }
}
-}
+} \ No newline at end of file
diff --git a/actions/userrss.php b/actions/userrss.php
index fa6d588cd..19e610551 100644
--- a/actions/userrss.php
+++ b/actions/userrss.php
@@ -25,7 +25,6 @@ require_once(INSTALLDIR.'/lib/rssaction.php');
class UserrssAction extends Rss10Action
{
- var $user = null;
var $tag = null;
function prepare($args)
@@ -39,6 +38,7 @@ class UserrssAction extends Rss10Action
$this->clientError(_('No such user.'));
return false;
} else {
+ $this->notices = $this->getNotices($this->limit);
return true;
}
}
@@ -64,9 +64,8 @@ class UserrssAction extends Rss10Action
function getNotices($limit=0)
{
-
$user = $this->user;
-
+
if (is_null($user)) {
return null;
}
diff --git a/actions/xrds.php b/actions/xrds.php
index def10e4cf..8ba89fec0 100644
--- a/actions/xrds.php
+++ b/actions/xrds.php
@@ -1,7 +1,7 @@
<?php
/**
- * XRDS for OpenID
+ * XRDS for OpenMicroBlogging
*
* PHP version 5
*
@@ -34,9 +34,11 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
}
require_once INSTALLDIR.'/lib/omb.php';
+require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
+require_once INSTALLDIR.'/extlib/libomb/xrds_mapper.php';
/**
- * XRDS for OpenID
+ * XRDS for OpenMicroBlogging
*
* @category Action
* @package StatusNet
@@ -52,7 +54,7 @@ class XrdsAction extends Action
*
* @return boolean true
*/
- function isReadOnly($args)
+ function isReadOnly()
{
return true;
}
@@ -85,89 +87,31 @@ class XrdsAction extends Action
*/
function showXrds($user)
{
- header('Content-Type: application/xrds+xml');
- $this->startXML();
- $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds'));
+ $srv = new OMB_Service_Provider(profile_to_omb_profile($user->uri,
+ $user->getProfile()));
+ /* Use libomb’s default XRDS Writer. */
+ $xrds_writer = null;
+ $srv->writeXRDS(new Laconica_XRDS_Mapper(), $xrds_writer);
+ }
+}
- $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
- 'xml:id' => 'oauth',
- 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
- 'version' => '2.0'));
- $this->element('Type', null, 'xri://$xrds*simple');
- $this->showService(OAUTH_ENDPOINT_REQUEST,
- common_local_url('requesttoken'),
- array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
- array(OAUTH_HMAC_SHA1),
- $user->uri);
- $this->showService(OAUTH_ENDPOINT_AUTHORIZE,
- common_local_url('userauthorization'),
- array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
- array(OAUTH_HMAC_SHA1));
- $this->showService(OAUTH_ENDPOINT_ACCESS,
- common_local_url('accesstoken'),
- array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
- array(OAUTH_HMAC_SHA1));
- $this->showService(OAUTH_ENDPOINT_RESOURCE,
- null,
- array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY),
- array(OAUTH_HMAC_SHA1));
- $this->elementEnd('XRD');
+class Laconica_XRDS_Mapper implements OMB_XRDS_Mapper
+{
+ protected $urls;
- // XXX: decide whether to include user's ID/nickname in postNotice URL
- $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
- 'xml:id' => 'omb',
- 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
- 'version' => '2.0'));
- $this->element('Type', null, 'xri://$xrds*simple');
- $this->showService(OMB_ENDPOINT_POSTNOTICE,
- common_local_url('postnotice'));
- $this->showService(OMB_ENDPOINT_UPDATEPROFILE,
- common_local_url('updateprofile'));
- $this->elementEnd('XRD');
- $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
- 'version' => '2.0'));
- $this->element('Type', null, 'xri://$xrds*simple');
- $this->showService(OAUTH_DISCOVERY,
- '#oauth');
- $this->showService(OMB_NAMESPACE,
- '#omb');
- $this->elementEnd('XRD');
- $this->elementEnd('XRDS');
- $this->endXML();
+ public function __construct()
+ {
+ $this->urls = array(
+ OAUTH_ENDPOINT_REQUEST => 'requesttoken',
+ OAUTH_ENDPOINT_AUTHORIZE => 'userauthorization',
+ OAUTH_ENDPOINT_ACCESS => 'accesstoken',
+ OMB_ENDPOINT_POSTNOTICE => 'postnotice',
+ OMB_ENDPOINT_UPDATEPROFILE => 'updateprofile');
}
- /**
- * 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 showService($type, $uri, $params=null, $sigs=null, $localId=null)
+ public function getURL($action)
{
- $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');
+ return common_local_url($this->urls[$action]);
}
}
-
+?>
diff --git a/classes/Config.php b/classes/Config.php
new file mode 100644
index 000000000..92f237d7f
--- /dev/null
+++ b/classes/Config.php
@@ -0,0 +1,131 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, 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')) {
+ exit(1);
+}
+
+/**
+ * Table Definition for config
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Config extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'config'; // table name
+ public $section; // varchar(32) primary_key not_null
+ public $setting; // varchar(32) primary_key not_null
+ public $value; // varchar(255)
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Config',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ const settingsKey = 'config:settings';
+
+ static function loadSettings()
+ {
+ $settings = self::_getSettings();
+ if (!empty($settings)) {
+ self::_applySettings($settings);
+ }
+ }
+
+ static function _getSettings()
+ {
+ $c = self::memcache();
+
+ if (!empty($c)) {
+ $settings = $c->get(common_cache_key(self::settingsKey));
+ if (!empty($settings)) {
+ return $settings;
+ }
+ }
+
+ $settings = array();
+
+ $config = new Config();
+
+ $config->find();
+
+ while ($config->fetch()) {
+ $settings[] = array($config->section, $config->setting, $config->value);
+ }
+
+ $config->free();
+
+ if (!empty($c)) {
+ $c->set(common_cache_key(self::settingsKey), $settings);
+ }
+
+ return $settings;
+ }
+
+ static function _applySettings($settings)
+ {
+ global $config;
+
+ foreach ($settings as $s) {
+ list($section, $setting, $value) = $s;
+ $config[$section][$setting] = $value;
+ }
+ }
+
+ function insert()
+ {
+ $result = parent::insert();
+ if ($result) {
+ Config::_blowSettingsCache();
+ }
+ return $result;
+ }
+
+ function delete()
+ {
+ $result = parent::delete();
+ if ($result) {
+ Config::_blowSettingsCache();
+ }
+ return $result;
+ }
+
+ function update($orig=null)
+ {
+ $result = parent::update($orig);
+ if ($result) {
+ Config::_blowSettingsCache();
+ }
+ return $result;
+ }
+
+ function _blowSettingsCache()
+ {
+ $c = self::memcache();
+
+ if (!empty($c)) {
+ $c->delete(common_cache_key(self::settingsKey));
+ }
+ }
+}
diff --git a/classes/Deleted_notice.php b/classes/Deleted_notice.php
new file mode 100644
index 000000000..64dc85da6
--- /dev/null
+++ b/classes/Deleted_notice.php
@@ -0,0 +1,46 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, 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')) {
+ exit(1);
+}
+
+/**
+ * Table Definition for notice
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Deleted_notice extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'deleted_notice'; // table name
+ public $id; // int(4) primary_key not_null
+ public $profile_id; // int(4) not_null
+ public $uri; // varchar(255) unique_key
+ public $created; // datetime() not_null
+ public $deleted; // datetime() not_null
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Deleted_notice',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/classes/File.php b/classes/File.php
index f4d0a3a48..9758cf7f5 100644
--- a/classes/File.php
+++ b/classes/File.php
@@ -78,7 +78,7 @@ class File extends Memcached_DataObject
$file_id = $x->insert();
if (isset($redir_data['type'])
- && ('text/html' === substr($redir_data['type'], 0, 9))
+ && (('text/html' === substr($redir_data['type'], 0, 9) || 'application/xhtml+xml' === substr($redir_data['type'], 0, 21)))
&& ($oembed_data = File_oembed::_getOembed($given_url))) {
File_oembed::saveNew($oembed_data, $file_id);
}
@@ -197,17 +197,44 @@ class File extends Memcached_DataObject
return 'http://'.$server.$path.$filename;
}
- function isEnclosure(){
- if(isset($this->filename)){
- return true;
- }
- $notEnclosureMimeTypes = array('text/html','application/xhtml+xml');
- $mimetype = strtolower($this->mimetype);
- $semicolon = strpos($mimetype,';');
- if($semicolon){
- $mimetype = substr($mimetype,0,$semicolon);
+ function getEnclosure(){
+ $enclosure = (object) array();
+ $enclosure->title=$this->title;
+ $enclosure->url=$this->url;
+ $enclosure->title=$this->title;
+ $enclosure->date=$this->date;
+ $enclosure->modified=$this->modified;
+ $enclosure->size=$this->size;
+ $enclosure->mimetype=$this->mimetype;
+
+ if(! isset($this->filename)){
+ $notEnclosureMimeTypes = array('text/html','application/xhtml+xml');
+ $mimetype = strtolower($this->mimetype);
+ $semicolon = strpos($mimetype,';');
+ if($semicolon){
+ $mimetype = substr($mimetype,0,$semicolon);
+ }
+ if(in_array($mimetype,$notEnclosureMimeTypes)){
+ $oembed = File_oembed::staticGet('file_id',$this->id);
+ if($oembed){
+ $mimetype = strtolower($oembed->mimetype);
+ $semicolon = strpos($mimetype,';');
+ if($semicolon){
+ $mimetype = substr($mimetype,0,$semicolon);
+ }
+ if(in_array($mimetype,$notEnclosureMimeTypes)){
+ return false;
+ }else{
+ if($oembed->mimetype) $enclosure->mimetype=$oembed->mimetype;
+ if($oembed->url) $enclosure->url=$oembed->url;
+ if($oembed->title) $enclosure->title=$oembed->title;
+ if($oembed->modified) $enclosure->modified=$oembed->modified;
+ unset($oembed->size);
+ }
+ }
+ }
}
- return(! in_array($mimetype,$notEnclosureMimeTypes));
+ return $enclosure;
}
}
diff --git a/classes/File_oembed.php b/classes/File_oembed.php
index 6be651815..e41ccfd09 100644
--- a/classes/File_oembed.php
+++ b/classes/File_oembed.php
@@ -20,6 +20,7 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+require_once INSTALLDIR.'/classes/File_redirection.php';
/**
* Table Definition for file_oembed
@@ -34,6 +35,7 @@ class File_oembed extends Memcached_DataObject
public $file_id; // int(4) primary_key not_null
public $version; // varchar(20)
public $type; // varchar(20)
+ public $mimetype; // varchar(50)
public $provider; // varchar(50)
public $provider_url; // varchar(255)
public $width; // int(4)
@@ -93,7 +95,24 @@ class File_oembed extends Memcached_DataObject
if (!empty($data->title)) $file_oembed->title = $data->title;
if (!empty($data->author_name)) $file_oembed->author_name = $data->author_name;
if (!empty($data->author_url)) $file_oembed->author_url = $data->author_url;
- if (!empty($data->url)) $file_oembed->url = $data->url;
+ if (!empty($data->url)){
+ $file_oembed->url = $data->url;
+ $given_url = File_redirection::_canonUrl($file_oembed->url);
+ if (! empty($given_url)){
+ $file = File::staticGet('url', $given_url);
+ if (empty($file)) {
+ $file_redir = File_redirection::staticGet('url', $given_url);
+ if (empty($file_redir)) {
+ $redir_data = File_redirection::where($given_url);
+ $file_oembed->mimetype = $redir_data['type'];
+ } else {
+ $file_id = $file_redir->file_id;
+ }
+ } else {
+ $file_oembed->mimetype=$file->mimetype;
+ }
+ }
+ }
$file_oembed->insert();
if (!empty($data->thumbnail_url)) {
File_thumbnail::saveNew($data, $file_id);
diff --git a/classes/Message.php b/classes/Message.php
index 4806057b4..979e6e87c 100644
--- a/classes/Message.php
+++ b/classes/Message.php
@@ -4,7 +4,7 @@
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-class Message extends Memcached_DataObject
+class Message extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
@@ -14,58 +14,73 @@ class Message extends Memcached_DataObject
public $uri; // varchar(255) unique_key
public $from_profile; // int(4) not_null
public $to_profile; // int(4) not_null
- public $content; // varchar(140)
- public $rendered; // text()
- public $url; // varchar(255)
+ public $content; // text()
+ public $rendered; // text()
+ public $url; // varchar(255)
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
- public $source; // varchar(32)
+ public $source; // varchar(32)
/* Static get */
- function staticGet($k,$v=null)
- { return Memcached_DataObject::staticGet('Message',$k,$v); }
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Message',$k,$v); }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
-
+
function getFrom()
{
return Profile::staticGet('id', $this->from_profile);
}
-
+
function getTo()
{
return Profile::staticGet('id', $this->to_profile);
}
-
+
static function saveNew($from, $to, $content, $source) {
-
+
$msg = new Message();
-
+
$msg->from_profile = $from;
$msg->to_profile = $to;
$msg->content = common_shorten_links($content);
$msg->rendered = common_render_text($content);
$msg->created = common_sql_now();
$msg->source = $source;
-
+
$result = $msg->insert();
-
+
if (!$result) {
common_log_db_error($msg, 'INSERT', __FILE__);
return _('Could not insert message.');
}
-
+
$orig = clone($msg);
$msg->uri = common_local_url('showmessage', array('message' => $msg->id));
-
+
$result = $msg->update($orig);
-
+
if (!$result) {
common_log_db_error($msg, 'UPDATE', __FILE__);
return _('Could not update message with new URI.');
}
-
+
return $msg;
}
+
+ static function maxContent()
+ {
+ $desclimit = common_config('message', 'contentlimit');
+ // null => use global limit (distinct from 0!)
+ if (is_null($desclimit)) {
+ $desclimit = common_config('site', 'textlimit');
+ }
+ return $desclimit;
+ }
+
+ static function contentTooLong($content)
+ {
+ $contentlimit = self::maxContent();
+ return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
+ }
}
diff --git a/classes/Notice.php b/classes/Notice.php
index 7d0502626..f3fa9af78 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -40,7 +40,7 @@ class Notice extends Memcached_DataObject
public $id; // int(4) primary_key not_null
public $profile_id; // int(4) not_null
public $uri; // varchar(255) unique_key
- public $content; // varchar(140)
+ public $content; // text()
public $rendered; // text()
public $url; // varchar(255)
public $created; // datetime() not_null
@@ -51,9 +51,7 @@ class Notice extends Memcached_DataObject
public $conversation; // int(4)
/* Static get */
- function staticGet($k,$v=NULL) {
- return Memcached_DataObject::staticGet('Notice',$k,$v);
- }
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice',$k,$v); }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
@@ -75,7 +73,21 @@ class Notice extends Memcached_DataObject
$this->blowFavesCache(true);
$this->blowSubsCache(true);
+ // For auditing purposes, save a record that the notice
+ // was deleted.
+
+ $deleted = new Deleted_notice();
+
+ $deleted->id = $this->id;
+ $deleted->profile_id = $this->profile_id;
+ $deleted->uri = $this->uri;
+ $deleted->created = $this->created;
+ $deleted->deleted = common_sql_now();
+
$this->query('BEGIN');
+
+ $deleted->insert();
+
//Null any notices that are replies to this notice
$this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id));
$related = array('Reply',
@@ -140,7 +152,7 @@ class Notice extends Memcached_DataObject
$final = common_shorten_links($content);
- if (mb_strlen($final) > 140) {
+ if (Notice::contentTooLong($final)) {
common_log(LOG_INFO, 'Rejecting notice that is too long.');
return _('Problem saving notice. Too long.');
}
@@ -1181,10 +1193,11 @@ class Notice extends Memcached_DataObject
$attachments = $this->attachments();
if($attachments){
foreach($attachments as $attachment){
- if ($attachment->isEnclosure()) {
- $attributes = array('rel'=>'enclosure','href'=>$attachment->url,'type'=>$attachment->mimetype,'length'=>$attachment->size);
- if($attachment->title){
- $attributes['title']=$attachment->title;
+ $enclosure=$attachment->getEnclosure();
+ if ($enclosure) {
+ $attributes = array('rel'=>'enclosure','href'=>$enclosure->url,'type'=>$enclosure->mimetype,'length'=>$enclosure->size);
+ if($enclosure->title){
+ $attributes['title']=$enclosure->title;
}
$xs->element('link', $attributes, null);
}
@@ -1335,4 +1348,20 @@ class Notice extends Memcached_DataObject
return $last->id;
}
}
+
+ static function maxContent()
+ {
+ $contentlimit = common_config('notice', 'contentlimit');
+ // null => use global limit (distinct from 0!)
+ if (is_null($contentlimit)) {
+ $contentlimit = common_config('site', 'textlimit');
+ }
+ return $contentlimit;
+ }
+
+ static function contentTooLong($content)
+ {
+ $contentlimit = self::maxContent();
+ return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
+ }
}
diff --git a/classes/Profile.php b/classes/Profile.php
index 6ad0e7a3a..7f0d12758 100644
--- a/classes/Profile.php
+++ b/classes/Profile.php
@@ -35,14 +35,13 @@ class Profile extends Memcached_DataObject
public $fullname; // varchar(255) multiple_key
public $profileurl; // varchar(255)
public $homepage; // varchar(255) multiple_key
- public $bio; // varchar(140) multiple_key
+ public $bio; // text() multiple_key
public $location; // varchar(255) multiple_key
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
/* Static get */
- function staticGet($k,$v=null)
- { return Memcached_DataObject::staticGet('Profile',$k,$v); }
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Profile',$k,$v); }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
@@ -461,4 +460,20 @@ class Profile extends Memcached_DataObject
$c->delete(common_cache_key('profile:notice_count:'.$this->id));
}
}
+
+ static function maxBio()
+ {
+ $biolimit = common_config('profile', 'biolimit');
+ // null => use global limit (distinct from 0!)
+ if (is_null($biolimit)) {
+ $biolimit = common_config('site', 'textlimit');
+ }
+ return $biolimit;
+ }
+
+ static function bioTooLong($bio)
+ {
+ $biolimit = self::maxBio();
+ return ($biolimit > 0 && !empty($bio) && (mb_strlen($bio) > $biolimit));
+ }
}
diff --git a/classes/User.php b/classes/User.php
index 14d3cf54f..5e74c7fde 100644
--- a/classes/User.php
+++ b/classes/User.php
@@ -103,10 +103,7 @@ class User extends Memcached_DataObject
}
$toupdate = implode(', ', $parts);
- $table = $this->tableName();
- if(common_config('db','quote_identifiers')) {
- $table = '"' . $table . '"';
- }
+ $table = common_database_tablename($this->tableName());
$qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
' WHERE id = ' . $this->id;
$orig->decache();
@@ -120,11 +117,15 @@ class User extends Memcached_DataObject
function allowed_nickname($nickname)
{
// XXX: should already be validated for size, content, etc.
- static $blacklist = array('rss', 'xrds', 'doc', 'main',
- 'settings', 'notice', 'user',
- 'search', 'avatar', 'tag', 'tags',
- 'api', 'message', 'group', 'groups',
- 'local');
+
+ $blacklist = array();
+
+ //all directory and file names should be blacklisted
+ $d = dir(INSTALLDIR);
+ while (false !== ($entry = $d->read())) {
+ $blacklist[]=$entry;
+ }
+ $d->close();
$merged = array_merge($blacklist, common_config('nickname', 'blacklist'));
return !in_array($nickname, $merged);
}
@@ -630,11 +631,7 @@ class User extends Memcached_DataObject
'ORDER BY subscription.created DESC ';
if ($offset) {
- if (common_config('db','type') == 'pgsql') {
- $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
- } else {
- $qry .= ' LIMIT ' . $offset . ', ' . $limit;
- }
+ $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
}
$profile = new Profile();
@@ -657,11 +654,7 @@ class User extends Memcached_DataObject
'AND subscription.subscribed != subscription.subscriber ' .
'ORDER BY subscription.created DESC ';
- if (common_config('db','type') == 'pgsql') {
- $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
- } else {
- $qry .= ' LIMIT ' . $offset . ', ' . $limit;
- }
+ $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
$profile = new Profile();
@@ -670,19 +663,52 @@ class User extends Memcached_DataObject
return $profile;
}
- function hasOpenID()
+ function getDesign()
+ {
+ return Design::staticGet('id', $this->design_id);
+ }
+
+ function hasRole($name)
{
- $oid = new User_openid();
+ $role = User_role::pkeyGet(array('user_id' => $this->id,
+ 'role' => $name));
+ return (!empty($role));
+ }
+
+ function grantRole($name)
+ {
+ $role = new User_role();
- $oid->user_id = $this->id;
+ $role->user_id = $this->id;
+ $role->role = $name;
+ $role->created = common_sql_now();
- $cnt = $oid->find();
+ $result = $role->insert();
- return ($cnt > 0);
+ if (!$result) {
+ common_log_db_error($role, 'INSERT', __FILE__);
+ return false;
+ }
+
+ return true;
}
- function getDesign()
+ function revokeRole($name)
{
- return Design::staticGet('id', $this->design_id);
+ $role = User_role::pkeyGet(array('user_id' => $this->id,
+ 'role' => $name));
+
+ if (empty($role)) {
+ throw new Exception('Cannot revoke role "'.$name.'" for user #'.$this->id.'; does not exist.');
+ }
+
+ $result = $role->delete();
+
+ if (!$result) {
+ common_log_db_error($role, 'DELETE', __FILE__);
+ throw new Exception('Cannot revoke role "'.$name.'" for user #'.$this->id.'; database error.');
+ }
+
+ return true;
}
}
diff --git a/classes/User_group.php b/classes/User_group.php
index ea19cbb97..310ecff1e 100644
--- a/classes/User_group.php
+++ b/classes/User_group.php
@@ -13,7 +13,7 @@ class User_group extends Memcached_DataObject
public $nickname; // varchar(64) unique_key
public $fullname; // varchar(255)
public $homepage; // varchar(255)
- public $description; // varchar(140)
+ public $description; // text()
public $location; // varchar(255)
public $original_logo; // varchar(255)
public $homepage_logo; // varchar(255)
@@ -298,6 +298,22 @@ class User_group extends Memcached_DataObject
return $ids;
}
+ static function maxDescription()
+ {
+ $desclimit = common_config('group', 'desclimit');
+ // null => use global limit (distinct from 0!)
+ if (is_null($desclimit)) {
+ $desclimit = common_config('site', 'textlimit');
+ }
+ return $desclimit;
+ }
+
+ static function descriptionTooLong($desc)
+ {
+ $desclimit = self::maxDescription();
+ return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit));
+ }
+
function asAtomEntry($namespace=false, $source=false)
{
$xs = new XMLStringer(true);
diff --git a/classes/User_role.php b/classes/User_role.php
new file mode 100644
index 000000000..85ecfb422
--- /dev/null
+++ b/classes/User_role.php
@@ -0,0 +1,48 @@
+<?php
+/*
+ * 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); }
+
+/**
+ * Table Definition for user_role
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class User_role extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'user_role'; // table name
+ public $user_id; // int(4) primary_key not_null
+ public $role; // varchar(32) primary_key not_null
+ public $created; // datetime() not_null
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User_role',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function &pkeyGet($kv)
+ {
+ return Memcached_DataObject::pkeyGet('User_role', $kv);
+ }
+}
diff --git a/classes/statusnet.ini b/classes/statusnet.ini
index 766bed75d..453981cd6 100644
--- a/classes/statusnet.ini
+++ b/classes/statusnet.ini
@@ -1,4 +1,3 @@
-
[avatar]
profile_id = 129
original = 17
@@ -16,6 +15,15 @@ width = K
height = K
url = U
+[config]
+section = 130
+setting = 130
+value = 2
+
+[config__keys]
+section = K
+setting = K
+
[confirm_address]
code = 130
user_id = 129
@@ -38,6 +46,17 @@ modified = 384
[consumer__keys]
consumer_key = K
+[deleted_notice]
+id = 129
+profile_id = 129
+uri = 2
+created = 142
+deleted = 142
+
+[deleted_notice__keys]
+id = K
+uri = U
+
[design]
id = 129
backgroundcolor = 1
@@ -78,6 +97,7 @@ id = N
file_id = 129
version = 2
type = 2
+mimetype = 2
provider = 2
provider_url = 2
width = 1
@@ -228,7 +248,7 @@ id = 129
uri = 2
from_profile = 129
to_profile = 129
-content = 2
+content = 34
rendered = 34
url = 2
created = 142
@@ -255,7 +275,7 @@ ts = K
id = 129
profile_id = 129
uri = 2
-content = 2
+content = 34
rendered = 34
url = 2
created = 142
@@ -303,7 +323,7 @@ nickname = 130
fullname = 2
profileurl = 2
homepage = 2
-bio = 2
+bio = 34
location = 2
created = 142
modified = 384
@@ -475,7 +495,7 @@ id = 129
nickname = 2
fullname = 2
homepage = 2
-description = 2
+description = 34
location = 2
original_logo = 2
homepage_logo = 2
@@ -498,3 +518,12 @@ modified = 384
[user_openid__keys]
canonical = K
display = U
+
+[user_role]
+user_id = 129
+role = 130
+created = 142
+
+[user_role__keys]
+user_id = K
+role = K
diff --git a/config.php.sample b/config.php.sample
index bd3776a47..997c9d6b0 100644
--- a/config.php.sample
+++ b/config.php.sample
@@ -38,8 +38,6 @@ $config['site']['path'] = 'statusnet';
// $config['site']['closed'] = true;
// Only allow registration for people invited by another user
// $config['site']['inviteonly'] = true;
-// Only allow registrations and logins through OpenID
-// $config['site']['openidonly'] = true;
// Make the site invisible to non-logged-in users
// $config['site']['private'] = true;
@@ -99,9 +97,6 @@ $config['sphinx']['port'] = 3312;
// $config['xmpp']['public'][] = 'someindexer@example.net';
// $config['xmpp']['debug'] = false;
-// Disable OpenID
-// $config['openid']['enabled'] = false;
-
// Turn off invites
// $config['invite']['enabled'] = false;
diff --git a/db/08to09.sql b/db/08to09.sql
new file mode 100644
index 000000000..953e0e5f4
--- /dev/null
+++ b/db/08to09.sql
@@ -0,0 +1,34 @@
+alter table notice
+ modify column content text comment 'update content';
+
+alter table message
+ modify column content text comment 'message content';
+
+alter table profile
+ modify column bio text comment 'descriptive biography';
+
+alter table user_group
+ modify column description text comment 'group description';
+
+alter table file_oembed
+ add column mimetype varchar(50) comment 'mime type of resource';
+
+create table config (
+
+ section varchar(32) comment 'configuration section',
+ setting varchar(32) comment 'configuration setting',
+ value varchar(255) comment 'configuration value',
+
+ constraint primary key (section, setting)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table user_role (
+
+ user_id integer not null comment 'user having the role' references user (id),
+ role varchar(32) not null comment 'string representing the role',
+ created datetime not null comment 'date the role was granted',
+
+ constraint primary key (user_id, role)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
diff --git a/db/08to09_pg.sql b/db/08to09_pg.sql
new file mode 100644
index 000000000..9e37314aa
--- /dev/null
+++ b/db/08to09_pg.sql
@@ -0,0 +1,40 @@
+-- SQL commands to update an 0.8.x version of Laconica
+-- to 0.9.x.
+
+--these are just comments
+/*
+alter table notice
+ modify column content text comment 'update content';
+
+alter table message
+ modify column content text comment 'message content';
+
+alter table profile
+ modify column bio text comment 'descriptive biography';
+
+alter table user_group
+ modify column description text comment 'group description';
+*/
+
+alter table file_oembed
+ add column mimetype varchar(50) /*comment 'mime type of resource'*/;
+
+create table config (
+
+ section varchar(32) /* comment 'configuration section'*/,
+ setting varchar(32) /* comment 'configuration setting'*/,
+ value varchar(255) /* comment 'configuration value'*/,
+
+ primary key (section, setting)
+
+);
+
+create table user_role (
+
+ user_id integer not null /* comment 'user having the role'*/ references "user" (id),
+ role varchar(32) not null /* comment 'string representing the role'*/,
+ created timestamp /* not null comment 'date the role was granted'*/,
+
+ primary key (user_id, role)
+
+);
diff --git a/db/sms_carrier.sql b/db/sms_carrier.sql
index 055606f58..0e94df296 100644
--- a/db/sms_carrier.sql
+++ b/db/sms_carrier.sql
@@ -61,4 +61,5 @@ VALUES
(100113, 'T-Mobile Germany', '%s@t-mobile-sms.de', now()),
(100114, 'Vodafone Germany', '%s@vodafone-sms.de', now()),
(100115, 'E-Plus', '%s@smsmail.eplus.de', now()),
- (100116, 'Cellular South', '%s@csouth1.com', now());
+ (100116, 'Cellular South', '%s@csouth1.com', now()),
+ (100117, 'ChinaMobile (139)', '%s@139.com', now());
diff --git a/db/statusnet.sql b/db/statusnet.sql
index 2c04f680a..221d60ce3 100644
--- a/db/statusnet.sql
+++ b/db/statusnet.sql
@@ -6,7 +6,7 @@ create table profile (
fullname varchar(255) comment 'display name',
profileurl varchar(255) comment 'URL, cached so we dont regenerate',
homepage varchar(255) comment 'identifying URL',
- bio varchar(140) comment 'descriptive biography',
+ bio text comment 'descriptive biography',
location varchar(255) comment 'physical location',
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified',
@@ -110,7 +110,7 @@ create table notice (
id integer auto_increment primary key comment 'unique identifier',
profile_id integer not null comment 'who made the update' references profile (id),
uri varchar(255) unique key comment 'universally unique identifier, usually a tag URI',
- content varchar(140) comment 'update content',
+ content text comment 'update content',
rendered text comment 'HTML version of the content',
url varchar(255) comment 'URL of any attachment (image, video, bookmark, whatever)',
created datetime not null comment 'date this record was created',
@@ -331,7 +331,7 @@ create table message (
uri varchar(255) unique key comment 'universally unique identifier',
from_profile integer not null comment 'who the message is from' references profile (id),
to_profile integer not null comment 'who the message is to' references profile (id),
- content varchar(140) comment 'message content',
+ content text comment 'message content',
rendered text comment 'HTML version of the content',
url varchar(255) comment 'URL of any attachment (image, video, bookmark, whatever)',
created datetime not null comment 'date this record was created',
@@ -380,7 +380,7 @@ create table user_group (
nickname varchar(64) unique key comment 'nickname for addressing',
fullname varchar(255) comment 'display name',
homepage varchar(255) comment 'URL, cached so we dont regenerate',
- description varchar(140) comment 'descriptive biography',
+ description text comment 'group description',
location varchar(255) comment 'related physical location, if any',
original_logo varchar(255) comment 'original size logo',
@@ -450,6 +450,7 @@ create table file_oembed (
file_id integer primary key comment 'oEmbed for that URL/file' references file (id),
version varchar(20) comment 'oEmbed spec. version',
type varchar(20) comment 'oEmbed type: photo, video, link, rich',
+ mimetype varchar(50) comment 'mime type of resource',
provider varchar(50) comment 'name of this oEmbed provider',
provider_url varchar(255) comment 'URL of this oEmbed provider',
width integer comment 'width of oEmbed resource when available',
@@ -534,4 +535,36 @@ create table session (
index session_modified_idx (modified)
-) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; \ No newline at end of file
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table deleted_notice (
+
+ id integer primary key comment 'identity of notice',
+ profile_id integer not null comment 'author of the notice',
+ uri varchar(255) unique key comment 'universally unique identifier, usually a tag URI',
+ created datetime not null comment 'date the notice record was created',
+ deleted datetime not null comment 'date the notice record was created',
+
+ index deleted_notice_profile_id_idx (profile_id)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table config (
+
+ section varchar(32) comment 'configuration section',
+ setting varchar(32) comment 'configuration setting',
+ value varchar(255) comment 'configuration value',
+
+ constraint primary key (section, setting)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table user_role (
+
+ user_id integer not null comment 'user having the role' references user (id),
+ role varchar(32) not null comment 'string representing the role',
+ created datetime not null comment 'date the role was granted',
+
+ constraint primary key (user_id, role)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
diff --git a/db/statusnet_pg.sql b/db/statusnet_pg.sql
index ad34720a2..672877ddf 100644
--- a/db/statusnet_pg.sql
+++ b/db/statusnet_pg.sql
@@ -465,6 +465,7 @@ create table file_oembed (
file_id bigint default nextval('file_oembed_seq') primary key /* comment 'unique identifier' */,
version varchar(20),
type varchar(20),
+ mimetype varchar(50),
provider varchar(50),
provider_url varchar(255),
width integer,
@@ -529,6 +530,17 @@ create table session (
create index session_modified_idx on session (modified);
+create table deleted_notice (
+
+ id integer primary key /* comment 'identity of notice'*/ ,
+ profile_id integer /* not null comment 'author of the notice'*/,
+ uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI'*/,
+ created timestamp not null /* comment 'date the notice record was created'*/ ,
+ deleted timestamp not null DEFAULT CURRENT_TIMESTAMP /* comment 'date the notice record was created'*/
+);
+
+CREATE index deleted_notice_profile_id_idx on deleted_notice (profile_id);
+
/* Textsearch stuff */
@@ -537,3 +549,23 @@ create index noticecontent_idx on notice using gist(to_tsvector('english',conten
create trigger textsearchupdate before insert or update on profile for each row
execute procedure tsvector_update_trigger(textsearch, 'pg_catalog.english', nickname, fullname, location, bio, homepage);
+
+create table config (
+
+ section varchar(32) /* comment 'configuration section'*/,
+ setting varchar(32) /* comment 'configuration setting'*/,
+ value varchar(255) /* comment 'configuration value'*/,
+
+ primary key (section, setting)
+
+);
+
+create table user_role (
+
+ user_id integer not null /* comment 'user having the role'*/ references "user" (id),
+ role varchar(32) not null /* comment 'string representing the role'*/,
+ created timestamp /* not null comment 'date the role was granted'*/,
+
+ primary key (user_id, role)
+
+);
diff --git a/doc-src/bookmarklet b/doc-src/bookmarklet
index 6cd2c08f9..e5ded7702 100644
--- a/doc-src/bookmarklet
+++ b/doc-src/bookmarklet
@@ -2,6 +2,4 @@ A bookmarklet is a small piece of javascript code used as a bookmark. This one w
Drag-and-drop the following link to your bookmarks bar or right-click it and add it to your browser favorites to keep it handy.
-<MTMarkdownOptions output='raw'>
-<a href="javascript:var%20d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='http://%%site.server%%/%%site.path%%/index.php?action=newnotice',l=d.location,e=encodeURIComponent,g=f+'&status_textarea=%22'+((e(s))?e(s):e(document.title))+'%22 from '+l.href;function%20a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=800,height=570')){l.href=g;}}a();void(0);">Post to %%site.name%%</a>
-</MTMarkdownOptions>
+<a href="javascript:var%20d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='http://%%site.server%%/%%site.path%%/index.php?action=newnotice',l=d.location,e=encodeURIComponent,g=f+'&amp;status_textarea=%22'+((e(s))?e(s):e(document.title))+'%22 from '+l.href;function%20a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=800,height=570')){l.href=g;}}a();void(0);">Post to %%site.name%%</a>
diff --git a/doc-src/contact b/doc-src/contact
index 31f3a4d2b..c63fcd01a 100644
--- a/doc-src/contact
+++ b/doc-src/contact
@@ -13,7 +13,7 @@ Bugs
----
If you think you've found a bug in the [StatusNet](http://status.net/) software,
-or if there's a new feature you'd like to see, add it into the [StatusNet bug database](http://status.net/PITS/HomePage). Don't forget to check the list of
+or if there's a new feature you'd like to see, add it into the [StatusNet bug database](http://status.net/bugs/). Don't forget to check the list of
existing bugs to make sure it hasn't already been reported!
Email
diff --git a/doc-src/help b/doc-src/help
index 8d7acf63b..93300ab24 100644
--- a/doc-src/help
+++ b/doc-src/help
@@ -26,7 +26,6 @@ Here are some documents that you might find helpful in understanding
* [SMS](%%doc.sms%%) - tying your cellphone to %%site.name%%
* [tags](%%doc.tags%%) - different ways to use tagging
* [Groups](%%doc.groups%%) - joining together in groups
-* [OpenID](%%doc.openid%%) - what OpenID is and how to use it with this service
* [OpenMicroBlogging](%%doc.openmublog%%) - subscribing to remote users
* [Privacy](%%doc.privacy%%) - %%site.name%%'s privacy policy
* [Source](%%doc.source%%) - How to get the StatusNet source code
diff --git a/doc-src/im b/doc-src/im
index c722a4e2c..631f6d9bb 100644
--- a/doc-src/im
+++ b/doc-src/im
@@ -37,10 +37,10 @@ currently-implemented commands:
* **help**: Show this help. List available Jabber/XMPP commands
* **follow &lt;nickname&gt;**: Subscribe to &lt;nickname&gt;
* **sub &lt;nickname&gt;**: Same as follow
-* **leave &lt;nickname&gt;**: Subscribe to &lt;nickname&gt;
+* **leave &lt;nickname&gt;**: Unsubscribe from &lt;nickname&gt;
* **unsub &lt;nickname&gt;**: Same as leave
* **d &lt;nickname&gt; &lt;text&gt;**: Send direct message to &lt;nickname&gt; with message body &lt;text&gt;
* **get &lt;nickname&gt;**: Get last notice from &lt;nickname&gt;
* **last &lt;nickname&gt;**: Same as 'get'
* **whois &lt;nickname&gt;**: Get Profile info on &lt;nickname&gt;
-* **fav &lt;nickname&gt;**: Add user's last notice as a favorite \ No newline at end of file
+* **fav &lt;nickname&gt;**: Add user's last notice as a favorite
diff --git a/extlib/Auth/OpenID/BigMath.php b/extlib/Auth/OpenID/BigMath.php
index 45104947d..b5fc627a0 100644
--- a/extlib/Auth/OpenID/BigMath.php
+++ b/extlib/Auth/OpenID/BigMath.php
@@ -376,7 +376,7 @@ function Auth_OpenID_detectMathLibrary($exts)
// Try to load dynamic modules.
if (!$loaded) {
foreach ($extension['modules'] as $module) {
- if (@dl($module . "." . PHP_SHLIB_SUFFIX)) {
+ if (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode') && @dl($module . "." . PHP_SHLIB_SUFFIX)) {
$loaded = true;
break;
}
diff --git a/extlib/Auth/Yadis/XML.php b/extlib/Auth/Yadis/XML.php
index 4854f12bb..7232d6cbd 100644
--- a/extlib/Auth/Yadis/XML.php
+++ b/extlib/Auth/Yadis/XML.php
@@ -349,7 +349,7 @@ function &Auth_Yadis_getXMLParser()
foreach ($extensions as $name => $params) {
if (!extension_loaded($name)) {
foreach ($params['libname'] as $libname) {
- if (@dl($libname)) {
+ if (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode') && @dl($libname)) {
$classname = $params['classname'];
}
}
diff --git a/extlib/OAuth.php b/extlib/OAuth.php
index 029166175..648627b57 100644
--- a/extlib/OAuth.php
+++ b/extlib/OAuth.php
@@ -199,7 +199,8 @@ class OAuthRequest {/*{{{*/
} else {
// collect request parameters from query string (GET) and post-data (POST) if appropriate (note: POST vars have priority)
$req_parameters = $_GET;
- if ($http_method == "POST" && @strstr($request_headers["Content-Type"], "application/x-www-form-urlencoded") ) {
+ if ($http_method == "POST" &&
+ ( @strstr($request_headers["Content-Type"], "application/x-www-form-urlencoded") || @strstr($_ENV["CONTENT_TYPE"], "application/x-www-form-urlencoded") )) {
$req_parameters = array_merge($req_parameters, $_POST);
}
@@ -326,7 +327,7 @@ class OAuthRequest {/*{{{*/
public function get_normalized_http_url() {/*{{{*/
$parts = parse_url($this->http_url);
- $port = @$parts['port'];
+ $port = isset($parts['port']) ? $parts['port'] : null;
$scheme = $parts['scheme'];
$host = $parts['host'];
$path = @$parts['path'];
diff --git a/extlib/PEAR.php b/extlib/PEAR.php
index 4c24c6006..fcefa964a 100644
--- a/extlib/PEAR.php
+++ b/extlib/PEAR.php
@@ -746,7 +746,7 @@ class PEAR
{
if (!extension_loaded($ext)) {
// if either returns true dl() will produce a FATAL error, stop that
- if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
+ if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1) || !function_exists('dl')) {
return false;
}
if (OS_WINDOWS) {
diff --git a/extlib/Services/oEmbed.php b/extlib/Services/oEmbed.php
index 7d507b6f6..0dc8f01b2 100644
--- a/extlib/Services/oEmbed.php
+++ b/extlib/Services/oEmbed.php
@@ -256,7 +256,7 @@ class Services_oEmbed
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (substr($code, 0, 1) != '2') {
- throw new Services_oEmbed_Exception('Non-200 code returned');
+ throw new Services_oEmbed_Exception('Non-200 code returned. Got code ' . $code);
}
curl_close($ch);
@@ -302,8 +302,8 @@ class Services_oEmbed
// Find all <link /> tags that have a valid oembed type set. We then
// extract the href attribute for each type.
- $regexp = '#<link([^>]*)type="' .
- '(application/json|text/xml)\+oembed"([^>]*)>#i';
+ $regexp = '#<link([^>]*)type[\s\n]*=[\s\n]*"' .
+ '(application/json|text/xml)\+oembed"([^>]*)>#im';
$m = $ret = array();
if (!preg_match_all($regexp, $body, $m)) {
@@ -314,7 +314,7 @@ class Services_oEmbed
foreach ($m[0] as $i => $link) {
$h = array();
- if (preg_match('/href="([^"]+)"/i', $link, $h)) {
+ if (preg_match('/[\s\n]+href[\s\n]*=[\s\n]*"([^"]+)"/im', $link, $h)) {
$ret[$m[2][$i]] = $h[1];
}
}
@@ -347,7 +347,7 @@ class Services_oEmbed
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (substr($code, 0, 1) != '2') {
- throw new Services_oEmbed_Exception('Non-200 code returned');
+ throw new Services_oEmbed_Exception('Non-200 code returned. Got code ' . $code);
}
return $result;
diff --git a/extlib/libomb/base_url_xrds_mapper.php b/extlib/libomb/base_url_xrds_mapper.php
new file mode 100755
index 000000000..645459583
--- /dev/null
+++ b/extlib/libomb/base_url_xrds_mapper.php
@@ -0,0 +1,51 @@
+<?php
+
+require_once 'xrds_mapper.php';
+require_once 'constants.php';
+
+/**
+ * Map XRDS actions to URLs using base URLs.
+ *
+ * This interface specifies classes which write the XRDS file announcing
+ * the OMB server. An instance of an implementing class should be passed to
+ * OMB_Service_Provider->writeXRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Base_URL_XRDS_Mapper implements OMB_XRDS_Mapper {
+
+ protected $urls;
+
+ public function __construct($oauth_base, $omb_base) {
+ $this->urls = array(
+ OAUTH_ENDPOINT_REQUEST => $oauth_base . 'requesttoken',
+ OAUTH_ENDPOINT_AUTHORIZE => $oauth_base . 'userauthorization',
+ OAUTH_ENDPOINT_ACCESS => $oauth_base . 'accesstoken',
+ OMB_ENDPOINT_POSTNOTICE => $omb_base . 'postnotice',
+ OMB_ENDPOINT_UPDATEPROFILE => $omb_base . 'updateprofile');
+ }
+
+ public function getURL($action) {
+ return $this->urls[$action];
+ }
+}
+?>
diff --git a/extlib/libomb/constants.php b/extlib/libomb/constants.php
new file mode 100644
index 000000000..a097443ac
--- /dev/null
+++ b/extlib/libomb/constants.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Constants for libomb
+ *
+ * This file contains constant definitions for libomb. The defined constants
+ * are service and namespace URIs for OAuth and OMB as used in XRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+/**
+ * The OMB constants.
+ **/
+
+define('OMB_VERSION_01', 'http://openmicroblogging.org/protocol/0.1');
+
+/* The OMB version supported by this libomb version. */
+define('OMB_VERSION', OMB_VERSION_01);
+
+define('OMB_ENDPOINT_UPDATEPROFILE', OMB_VERSION . '/updateProfile');
+define('OMB_ENDPOINT_POSTNOTICE', OMB_VERSION . '/postNotice');
+
+/**
+ * The OAuth constants.
+ **/
+
+define('OAUTH_NAMESPACE', 'http://oauth.net/core/1.0/');
+
+define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request');
+define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize');
+define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access');
+define('OAUTH_ENDPOINT_RESOURCE', OAUTH_NAMESPACE.'endpoint/resource');
+
+define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header');
+define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body');
+
+define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1');
+
+define('OAUTH_DISCOVERY', 'http://oauth.net/discovery/1.0');
+?>
diff --git a/extlib/libomb/datastore.php b/extlib/libomb/datastore.php
new file mode 100755
index 000000000..ab52de547
--- /dev/null
+++ b/extlib/libomb/datastore.php
@@ -0,0 +1,200 @@
+<?php
+
+require_once 'OAuth.php';
+
+/**
+ * Data access interface
+ *
+ * This interface specifies data access methods libomb needs. It should be
+ * implemented by libomb users. OMB_Datastore is libomb’s main interface to the
+ * application’s data. Objects corresponding to this interface are used in
+ * OMB_Service_Provider and OMB_Service_Consumer.
+ *
+ * Note that it’s implemented as a class since OAuthDataStore is as well a
+ * class, though only declaring methods.
+ *
+ * OMB_Datastore extends OAuthDataStore with two OAuth-related methods for token
+ * revoking and authorizing and all OMB-related methods.
+ * Refer to OAuth.php for a complete specification of OAuth-related methods.
+ *
+ * It is the user’s duty to signal and handle errors. libomb does not check
+ * return values nor handle exceptions. It is suggested to use exceptions.
+ * Note that lookup_token and getProfile return null if the requested object
+ * is not available. This is NOT an error and should not raise an exception.
+ * Same applies for lookup_nonce which returns a boolean value. These methods
+ * may nevertheless throw an exception, for example in case of a storage errors.
+ *
+ * Most of the parameters passed to these methods are unescaped and unverified
+ * user input. Therefore they should be handled with extra care to avoid
+ * security problems like SQL injections.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Datastore extends OAuthDataStore {
+
+ /*********
+ * OAUTH *
+ *********/
+
+ /**
+ * Revoke specified OAuth token
+ *
+ * Revokes the authorization token specified by $token_key.
+ * Throws exceptions in case of error.
+ *
+ * @param string $token_key The key of the token to be revoked
+ *
+ * @access public
+ **/
+ public function revoke_token($token_key) {
+ throw new Exception();
+ }
+
+ /**
+ * Authorize specified OAuth token
+ *
+ * Authorizes the authorization token specified by $token_key.
+ * Throws exceptions in case of error.
+ *
+ * @param string $token_key The key of the token to be authorized
+ *
+ * @access public
+ **/
+ public function authorize_token($token_key) {
+ throw new Exception();
+ }
+
+ /*********
+ * OMB *
+ *********/
+
+ /**
+ * Get profile by identifying URI
+ *
+ * Returns an OMB_Profile object representing the OMB profile identified by
+ * $identifier_uri.
+ * Returns null if there is no such OMB profile.
+ * Throws exceptions in case of other error.
+ *
+ * @param string $identifier_uri The OMB identifier URI specifying the
+ * requested profile
+ *
+ * @access public
+ *
+ * @return OMB_Profile The corresponding profile
+ **/
+ public function getProfile($identifier_uri) {
+ throw new Exception();
+ }
+
+ /**
+ * Save passed profile
+ *
+ * Stores the OMB profile $profile. Overwrites an existing entry.
+ * Throws exceptions in case of error.
+ *
+ * @param OMB_Profile $profile The OMB profile which should be saved
+ *
+ * @access public
+ **/
+ public function saveProfile($profile) {
+ throw new Exception();
+ }
+
+ /**
+ * Save passed notice
+ *
+ * Stores the OMB notice $notice. The datastore may change the passed notice.
+ * This might by neccessary for URIs depending on a database key. Note that
+ * it is the user’s duty to present a mechanism for his OMB_Datastore to
+ * appropriately change his OMB_Notice. TODO: Ugly.
+ * Throws exceptions in case of error.
+ *
+ * @param OMB_Notice $notice The OMB notice which should be saved
+ *
+ * @access public
+ **/
+ public function saveNotice(&$notice) {
+ throw new Exception();
+ }
+
+ /**
+ * Get subscriptions of a given profile
+ *
+ * Returns an array containing subscription informations for the specified
+ * profile. Every array entry should in turn be an array with keys
+ * 'uri´: The identifier URI of the subscriber
+ * 'token´: The subscribe token
+ * 'secret´: The secret token
+ * Throws exceptions in case of error.
+ *
+ * @param string $subscribed_user_uri The OMB identifier URI specifying the
+ * subscribed profile
+ *
+ * @access public
+ *
+ * @return mixed An array containing the subscriptions or 0 if no
+ * subscription has been found.
+ **/
+ public function getSubscriptions($subscribed_user_uri) {
+ throw new Exception();
+ }
+
+ /**
+ * Delete a subscription
+ *
+ * Deletes the subscription from $subscriber_uri to $subscribed_user_uri.
+ * Throws exceptions in case of error.
+ *
+ * @param string $subscriber_uri The OMB identifier URI specifying the
+ * subscribing profile
+ *
+ * @param string $subscribed_user_uri The OMB identifier URI specifying the
+ * subscribed profile
+ *
+ * @access public
+ **/
+ public function deleteSubscription($subscriber_uri, $subscribed_user_uri) {
+ throw new Exception();
+ }
+
+ /**
+ * Save a subscription
+ *
+ * Saves the subscription from $subscriber_uri to $subscribed_user_uri.
+ * Throws exceptions in case of error.
+ *
+ * @param string $subscriber_uri The OMB identifier URI specifying
+ * the subscribing profile
+ *
+ * @param string $subscribed_user_uri The OMB identifier URI specifying
+ * the subscribed profile
+ * @param OAuthToken $token The access token
+ *
+ * @access public
+ **/
+ public function saveSubscription($subscriber_uri, $subscribed_user_uri,
+ $token) {
+ throw new Exception();
+ }
+}
+?>
diff --git a/extlib/libomb/helper.php b/extlib/libomb/helper.php
new file mode 100644
index 000000000..a1f21f268
--- /dev/null
+++ b/extlib/libomb/helper.php
@@ -0,0 +1,99 @@
+<?php
+
+require_once 'Validate.php';
+
+/**
+ * Helper functions for libomb
+ *
+ * This file contains helper functions for libomb.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Helper {
+
+ /**
+ * Non-scalar constants
+ *
+ * The set of OMB and OAuth Services an OMB Server has to implement.
+ */
+
+ public static $OMB_SERVICES =
+ array(OMB_ENDPOINT_UPDATEPROFILE, OMB_ENDPOINT_POSTNOTICE);
+ public static $OAUTH_SERVICES =
+ array(OAUTH_ENDPOINT_REQUEST, OAUTH_ENDPOINT_AUTHORIZE, OAUTH_ENDPOINT_ACCESS);
+
+ /**
+ * Validate URL
+ *
+ * Basic URL validation. Currently http, https, ftp and gopher are supported
+ * schemes.
+ *
+ * @param string $url The URL which is to be validated.
+ *
+ * @return bool Whether URL is valid.
+ *
+ * @access public
+ */
+ public static function validateURL($url) {
+ return Validate::uri($url, array('allowed_schemes' => array('http', 'https',
+ 'gopher', 'ftp')));
+ }
+
+ /**
+ * Validate Media type
+ *
+ * Basic Media type validation. Checks for valid maintype and correct format.
+ *
+ * @param string $mediatype The Media type which is to be validated.
+ *
+ * @return bool Whether media type is valid.
+ *
+ * @access public
+ */
+ public static function validateMediaType($mediatype) {
+ if (0 === preg_match('/^(\w+)\/([\w\d-+.]+)$/', $mediatype, $subtypes)) {
+ return false;
+ }
+ if (!in_array(strtolower($subtypes[1]), array('application', 'audio', 'image',
+ 'message', 'model', 'multipart', 'text', 'video'))) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Remove escaping from request parameters
+ *
+ * Neutralise the evil effects of magic_quotes_gpc in the current request.
+ * This is used before handing a request off to OAuthRequest::from_request.
+ * Many thanks to Ciaran Gultnieks for this fix.
+ *
+ * @access public
+ */
+ public static function removeMagicQuotesFromRequest() {
+ if(get_magic_quotes_gpc() == 1) {
+ $_POST = array_map('stripslashes', $_POST);
+ $_GET = array_map('stripslashes', $_GET);
+ }
+ }
+}
+?>
diff --git a/extlib/libomb/invalidparameterexception.php b/extlib/libomb/invalidparameterexception.php
new file mode 100755
index 000000000..163e1dd4c
--- /dev/null
+++ b/extlib/libomb/invalidparameterexception.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Exception stating that a passed parameter is invalid
+ *
+ * This exception is raised when a parameter does not obey the OMB standard.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+class OMB_InvalidParameterException extends Exception {
+ public function __construct($value, $type, $parameter) {
+ parent::__construct("Invalid value $value for parameter $parameter in $type");
+ }
+}
+?>
diff --git a/extlib/libomb/invalidyadisexception.php b/extlib/libomb/invalidyadisexception.php
new file mode 100755
index 000000000..797b7b95b
--- /dev/null
+++ b/extlib/libomb/invalidyadisexception.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Exception stating that a requested url does not resolve to a valid yadis
+ *
+ * This exception is raised when OMB_Service is not able to discover a valid
+ * yadis location with XRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+class OMB_InvalidYadisException extends Exception {
+
+}
+?>
diff --git a/extlib/libomb/notice.php b/extlib/libomb/notice.php
new file mode 100755
index 000000000..9ac36640a
--- /dev/null
+++ b/extlib/libomb/notice.php
@@ -0,0 +1,272 @@
+<?php
+require_once 'invalidparameterexception.php';
+require_once 'Validate.php';
+require_once 'helper.php';
+
+/**
+ * OMB Notice representation
+ *
+ * This class represents an OMB notice.
+ *
+ * Do not call the setters with null values. Instead, if you want to delete a
+ * field, pass an empty string. The getters will return null for empty fields.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Notice {
+ protected $author;
+ protected $uri;
+ protected $content;
+ protected $url;
+ protected $license_url; /* url is an own addition for clarification. */
+ protected $seealso_url; /* url is an own addition for clarification. */
+ protected $seealso_disposition;
+ protected $seealso_mediatype;
+ protected $seealso_license_url; /* url is an addition for clarification. */
+
+ /* The notice as OMB param array. Cached and rebuild on usage.
+ false while outdated. */
+ protected $param_array;
+
+ /**
+ * Constructor for OMB_Notice
+ *
+ * Initializes the OMB_Notice object with author, uri and content.
+ * These parameters are mandatory for postNotice.
+ *
+ * @param object $author An OMB_Profile object representing the author of the
+ * notice.
+ * @param string $uri The notice URI as defined by the OMB. A unique and
+ * unchanging identifier for a notice.
+ * @param string $content The content of the notice. 140 chars recommended,
+ * but there is no limit.
+ *
+ * @access public
+ */
+ public function __construct($author, $uri, $content) {
+ $this->content = $content;
+ if (is_null($author)) {
+ throw new OMB_InvalidParameterException('', 'notice', 'omb_listenee');
+ }
+ $this->author = $author;
+
+ if (!Validate::uri($uri)) {
+ throw new OMB_InvalidParameterException($uri, 'notice', 'omb_notice');
+ }
+ $this->uri = $uri;
+
+ $this->param_array = false;
+ }
+
+ /**
+ * Returns the notice as array
+ *
+ * The method returns an array which contains the whole notice as array. The
+ * array is cached and only rebuilt on changes of the notice.
+ * Empty optional values are not passed.
+ *
+ * @access public
+ * @returns array The notice as parameter array
+ */
+ public function asParameters() {
+ if ($this->param_array !== false) {
+ return $this->param_array;
+ }
+
+ $this->param_array = array(
+ 'omb_notice' => $this->uri,
+ 'omb_notice_content' => $this->content);
+
+ if (!is_null($this->url))
+ $this->param_array['omb_notice_url'] = $this->url;
+
+ if (!is_null($this->license_url))
+ $this->param_array['omb_notice_license'] = $this->license_url;
+
+ if (!is_null($this->seealso_url)) {
+ $this->param_array['omb_seealso'] = $this->seealso_url;
+
+ /* This is actually a free interpretation of the OMB standard. We assume
+ that additional seealso parameters are not of any use if seealso itself
+ is not set. */
+ if (!is_null($this->seealso_disposition))
+ $this->param_array['omb_seealso_disposition'] =
+ $this->seealso_disposition;
+
+ if (!is_null($this->seealso_mediatype))
+ $this->param_array['omb_seealso_mediatype'] = $this->seealso_mediatype;
+
+ if (!is_null($this->seealso_license_url))
+ $this->param_array['omb_seealso_license'] = $this->seealso_license_url;
+ }
+ return $this->param_array;
+ }
+
+ /**
+ * Builds an OMB_Notice object from array
+ *
+ * The method builds an OMB_Notice object from the passed parameters array.
+ * The array MUST provide a notice URI and content. The array fields HAVE TO
+ * be named according to the OMB standard, i. e. omb_notice_* and
+ * omb_seealso_*. Values are handled as not passed if the corresponding array
+ * fields are not set or the empty string.
+ *
+ * @param object $author An OMB_Profile object representing the author of
+ * the notice.
+ * @param string $parameters An array containing the notice parameters.
+ *
+ * @access public
+ *
+ * @returns OMB_Notice The built OMB_Notice.
+ */
+ public static function fromParameters($author, $parameters) {
+ $notice = new OMB_Notice($author, $parameters['omb_notice'],
+ $parameters['omb_notice_content']);
+
+ if (isset($parameters['omb_notice_url'])) {
+ $notice->setURL($parameters['omb_notice_url']);
+ }
+
+ if (isset($parameters['omb_notice_license'])) {
+ $notice->setLicenseURL($parameters['omb_notice_license']);
+ }
+
+ if (isset($parameters['omb_seealso'])) {
+ $notice->setSeealsoURL($parameters['omb_seealso']);
+ }
+
+ if (isset($parameters['omb_seealso_disposition'])) {
+ $notice->setSeealsoDisposition($parameters['omb_seealso_disposition']);
+ }
+
+ if (isset($parameters['omb_seealso_mediatype'])) {
+ $notice->setSeealsoMediatype($parameters['omb_seealso_mediatype']);
+ }
+
+ if (isset($parameters['omb_seealso_license'])) {
+ $notice->setSeealsoLicenseURL($parameters['omb_seealso_license']);
+ }
+ return $notice;
+ }
+
+ public function getAuthor() {
+ return $this->author;
+ }
+
+ public function getIdentifierURI() {
+ return $this->uri;
+ }
+
+ public function getContent() {
+ return $this->content;
+ }
+
+ public function getURL() {
+ return $this->url;
+ }
+
+ public function getLicenseURL() {
+ return $this->license_url;
+ }
+
+ public function getSeealsoURL() {
+ return $this->seealso_url;
+ }
+
+ public function getSeealsoDisposition() {
+ return $this->seealso_disposition;
+ }
+
+ public function getSeealsoMediatype() {
+ return $this->seealso_mediatype;
+ }
+
+ public function getSeealsoLicenseURL() {
+ return $this->seealso_license_url;
+ }
+
+ public function setURL($url) {
+ if ($url === '') {
+ $url = null;
+ } elseif (!OMB_Helper::validateURL($url)) {
+ throw new OMB_InvalidParameterException($url, 'notice', 'omb_notice_url');
+ }
+ $this->url = $url;
+ $this->param_array = false;
+ }
+
+ public function setLicenseURL($license_url) {
+ if ($license_url === '') {
+ $license_url = null;
+ } elseif (!OMB_Helper::validateURL($license_url)) {
+ throw new OMB_InvalidParameterException($license_url, 'notice',
+ 'omb_notice_license');
+ }
+ $this->license_url = $license_url;
+ $this->param_array = false;
+ }
+
+ public function setSeealsoURL($seealso_url) {
+ if ($seealso_url === '') {
+ $seealso_url = null;
+ } elseif (!OMB_Helper::validateURL($seealso_url)) {
+ throw new OMB_InvalidParameterException($seealso_url, 'notice',
+ 'omb_seealso');
+ }
+ $this->seealso_url = $seealso_url;
+ $this->param_array = false;
+ }
+
+ public function setSeealsoDisposition($seealso_disposition) {
+ if ($seealso_disposition === '') {
+ $seealso_disposition = null;
+ } elseif ($seealso_disposition !== 'link' && $seealso_disposition !== 'inline') {
+ throw new OMB_InvalidParameterException($seealso_disposition, 'notice',
+ 'omb_seealso_disposition');
+ }
+ $this->seealso_disposition = $seealso_disposition;
+ $this->param_array = false;
+ }
+
+ public function setSeealsoMediatype($seealso_mediatype) {
+ if ($seealso_mediatype === '') {
+ $seealso_mediatype = null;
+ } elseif (!OMB_Helper::validateMediaType($seealso_mediatype)) {
+ throw new OMB_InvalidParameterException($seealso_mediatype, 'notice',
+ 'omb_seealso_mediatype');
+ }
+ $this->seealso_mediatype = $seealso_mediatype;
+ $this->param_array = false;
+ }
+
+ public function setSeealsoLicenseURL($seealso_license_url) {
+ if ($seealso_license_url === '') {
+ $seealso_license_url = null;
+ } elseif (!OMB_Helper::validateURL($seealso_license_url)) {
+ throw new OMB_InvalidParameterException($seealso_license_url, 'notice',
+ 'omb_seealso_license');
+ }
+ $this->seealso_license_url = $seealso_license_url;
+ $this->param_array = false;
+ }
+}
+?>
diff --git a/extlib/libomb/omb_yadis_xrds.php b/extlib/libomb/omb_yadis_xrds.php
new file mode 100755
index 000000000..89921203b
--- /dev/null
+++ b/extlib/libomb/omb_yadis_xrds.php
@@ -0,0 +1,196 @@
+<?php
+
+require_once 'Auth/Yadis/Yadis.php';
+require_once 'unsupportedserviceexception.php';
+require_once 'invalidyadisexception.php';
+
+/**
+ * OMB XRDS representation
+ *
+ * This class represents a Yadis XRDS file for OMB. It adds some useful methods to
+ * Auth_Yadis_XRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Yadis_XRDS extends Auth_Yadis_XRDS {
+
+ protected $fetcher;
+
+ /**
+ * Create an instance from URL
+ *
+ * Constructs an OMB_Yadis_XRDS object from a given URL. A full Yadis
+ * discovery is performed on the URL and the XRDS is parsed.
+ * Throws an OMB_InvalidYadisException when no Yadis is discovered or the
+ * detected XRDS file is broken.
+ *
+ * @param string $url The URL on which Yadis discovery
+ * should be performed on
+ * @param Auth_Yadis_HTTPFetcher $fetcher A fetcher used to get HTTP
+ * resources
+ *
+ * @access public
+ *
+ * @return OMB_Yadis_XRDS The initialized object representing the given
+ * resource
+ **/
+ public static function fromYadisURL($url, $fetcher) {
+ /* Perform a Yadis discovery. */
+ $yadis = Auth_Yadis_Yadis::discover($url, $fetcher);
+ if ($yadis->failed) {
+ throw new OMB_InvalidYadisException($url);
+ }
+
+ /* Parse the XRDS file. */
+ $xrds = OMB_Yadis_XRDS::parseXRDS($yadis->response_text);
+ if ($xrds === null) {
+ throw new OMB_InvalidYadisException($url);
+ }
+ $xrds->fetcher = $fetcher;
+ return $xrds;
+ }
+
+ /**
+ * Get a specific service
+ *
+ * Returns the Auth_Yadis_Service object corresponding to the given service
+ * URI.
+ * Throws an OMB_UnsupportedServiceException if the service is not available.
+ *
+ * @param string $service URI specifier of the requested service
+ *
+ * @access public
+ *
+ * @return Auth_Yadis_Service The object representing the requested service
+ **/
+ public function getService($service) {
+ $match = $this->services(array( create_function('$s',
+ "return in_array('$service', \$s->getTypes());")));
+ if ($match === array()) {
+ throw new OMB_UnsupportedServiceException($service);
+ }
+ return $match[0];
+ }
+
+ /**
+ * Get a specific XRD
+ *
+ * Returns the OMB_Yadis_XRDS object corresponding to the given URI.
+ * Throws an OMB_UnsupportedServiceException if the XRD is not available.
+ * Note that getXRD tries to resolve external XRD parts as well.
+ *
+ * @param string $uri URI specifier of the requested XRD
+ *
+ * @access public
+ *
+ * @return OMB_Yadis_XRDS The object representing the requested XRD
+ **/
+ public function getXRD($uri) {
+ $nexthash = strpos($uri, '#');
+ if ($nexthash !== 0) {
+ if ($nexthash !== false) {
+ $cururi = substr($uri, 0, $nexthash);
+ $nexturi = substr($uri, $nexthash);
+ }
+ return
+ OMB_Yadis_XRDS::fromYadisURL($cururi, $this->fetcher)->getXRD($nexturi);
+ }
+
+ $id = substr($uri, 1);
+ foreach ($this->allXrdNodes as $node) {
+ $attrs = $this->parser->attributes($node);
+ if (array_key_exists('xml:id', $attrs) && $attrs['xml:id'] == $id) {
+ /* Trick the constructor into thinking this is the only node. */
+ $bogus_nodes = array($node);
+ return new OMB_Yadis_XRDS($this->parser, $bogus_nodes);
+ }
+ }
+ throw new OMB_UnsupportedServiceException($uri);
+ }
+
+ /**
+ * Parse an XML string containing a XRDS document
+ *
+ * Parse an XML string (XRDS document) and return either a
+ * Auth_Yadis_XRDS object or null, depending on whether the
+ * XRDS XML is valid.
+ * Copy and paste from parent to select correct constructor.
+ *
+ * @param string $xml_string An XRDS XML string.
+ *
+ * @access public
+ *
+ * @return mixed An instance of OMB_Yadis_XRDS or null,
+ * depending on the validity of $xml_string
+ **/
+
+ public function &parseXRDS($xml_string, $extra_ns_map = null) {
+ $_null = null;
+
+ if (!$xml_string) {
+ return $_null;
+ }
+
+ $parser = Auth_Yadis_getXMLParser();
+
+ $ns_map = Auth_Yadis_getNSMap();
+
+ if ($extra_ns_map && is_array($extra_ns_map)) {
+ $ns_map = array_merge($ns_map, $extra_ns_map);
+ }
+
+ if (!($parser && $parser->init($xml_string, $ns_map))) {
+ return $_null;
+ }
+
+ // Try to get root element.
+ $root = $parser->evalXPath('/xrds:XRDS[1]');
+ if (!$root) {
+ return $_null;
+ }
+
+ if (is_array($root)) {
+ $root = $root[0];
+ }
+
+ $attrs = $parser->attributes($root);
+
+ if (array_key_exists('xmlns:xrd', $attrs) &&
+ $attrs['xmlns:xrd'] != Auth_Yadis_XMLNS_XRDS) {
+ return $_null;
+ } else if (array_key_exists('xmlns', $attrs) &&
+ preg_match('/xri/', $attrs['xmlns']) &&
+ $attrs['xmlns'] != Auth_Yadis_XMLNS_XRD_2_0) {
+ return $_null;
+ }
+
+ // Get the last XRD node.
+ $xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD');
+
+ if (!$xrd_nodes) {
+ return $_null;
+ }
+
+ $xrds = new OMB_Yadis_XRDS($parser, $xrd_nodes);
+ return $xrds;
+ }
+}
diff --git a/extlib/libomb/plain_xrds_writer.php b/extlib/libomb/plain_xrds_writer.php
new file mode 100755
index 000000000..b4a6e990b
--- /dev/null
+++ b/extlib/libomb/plain_xrds_writer.php
@@ -0,0 +1,124 @@
+<?php
+
+require_once 'xrds_writer.php';
+
+/**
+ * Write OMB-specific XRDS using XMLWriter.
+ *
+ * This class writes the XRDS file announcing the OMB server. It uses
+ * OMB_XMLWriter, which is a subclass of XMLWriter. An instance of
+ * OMB_Plain_XRDS_Writer should be passed to OMB_Service_Provider->writeXRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Plain_XRDS_Writer implements OMB_XRDS_Writer {
+ public function writeXRDS($user, $mapper) {
+ header('Content-Type: application/xrds+xml');
+ $xw = new XMLWriter();
+ $xw->openURI('php://output');
+ $xw->setIndent(true);
+
+ $xw->startDocument('1.0', 'UTF-8');
+ $this->writeFullElement($xw, 'XRDS', array('xmlns' => 'xri://$xrds'), array(
+ array('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
+ 'xml:id' => 'oauth',
+ 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
+ 'version' => '2.0'), array(
+ array('Type', null, 'xri://$xrds*simple'),
+ array('Service', null, array(
+ array('Type', null, OAUTH_ENDPOINT_REQUEST),
+ array('URI', null, $mapper->getURL(OAUTH_ENDPOINT_REQUEST)),
+ array('Type', null, OAUTH_AUTH_HEADER),
+ array('Type', null, OAUTH_POST_BODY),
+ array('Type', null, OAUTH_HMAC_SHA1),
+ array('LocalID', null, $user->getIdentifierURI())
+ )),
+ array('Service', null, array(
+ array('Type', null, OAUTH_ENDPOINT_AUTHORIZE),
+ array('URI', null, $mapper->getURL(OAUTH_ENDPOINT_AUTHORIZE)),
+ array('Type', null, OAUTH_AUTH_HEADER),
+ array('Type', null, OAUTH_POST_BODY),
+ array('Type', null, OAUTH_HMAC_SHA1)
+ )),
+ array('Service', null, array(
+ array('Type', null, OAUTH_ENDPOINT_ACCESS),
+ array('URI', null, $mapper->getURL(OAUTH_ENDPOINT_ACCESS)),
+ array('Type', null, OAUTH_AUTH_HEADER),
+ array('Type', null, OAUTH_POST_BODY),
+ array('Type', null, OAUTH_HMAC_SHA1)
+ )),
+ array('Service', null, array(
+ array('Type', null, OAUTH_ENDPOINT_RESOURCE),
+ array('Type', null, OAUTH_AUTH_HEADER),
+ array('Type', null, OAUTH_POST_BODY),
+ array('Type', null, OAUTH_HMAC_SHA1)
+ ))
+ )),
+ array('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
+ 'xml:id' => 'omb',
+ 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
+ 'version' => '2.0'), array(
+ array('Type', null, 'xri://$xrds*simple'),
+ array('Service', null, array(
+ array('Type', null, OMB_ENDPOINT_POSTNOTICE),
+ array('URI', null, $mapper->getURL(OMB_ENDPOINT_POSTNOTICE))
+ )),
+ array('Service', null, array(
+ array('Type', null, OMB_ENDPOINT_UPDATEPROFILE),
+ array('URI', null, $mapper->getURL(OMB_ENDPOINT_UPDATEPROFILE))
+ ))
+ )),
+ array('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
+ 'version' => '2.0'), array(
+ array('Type', null, 'xri://$xrds*simple'),
+ array('Service', null, array(
+ array('Type', null, OAUTH_DISCOVERY),
+ array('URI', null, '#oauth')
+ )),
+ array('Service', null, array(
+ array('Type', null, OMB_VERSION),
+ array('URI', null, '#omb')
+ ))
+ ))
+ ));
+ $xw->endDocument();
+ $xw->flush();
+ }
+
+ public static function writeFullElement($xw, $tag, $attributes, $content) {
+ $xw->startElement($tag);
+ if (!is_null($attributes)) {
+ foreach ($attributes as $name => $value) {
+ $xw->writeAttribute($name, $value);
+ }
+ }
+ if (is_array($content)) {
+ foreach ($content as $values) {
+ OMB_Plain_XRDS_Writer::writeFullElement($xw, $values[0], $values[1], $values[2]);
+ }
+ } else {
+ $xw->text($content);
+ }
+ $xw->fullEndElement();
+ }
+}
+?>
diff --git a/extlib/libomb/profile.php b/extlib/libomb/profile.php
new file mode 100755
index 000000000..13314d3e8
--- /dev/null
+++ b/extlib/libomb/profile.php
@@ -0,0 +1,317 @@
+<?php
+require_once 'invalidparameterexception.php';
+require_once 'Validate.php';
+require_once 'helper.php';
+
+/**
+ * OMB profile representation
+ *
+ * This class represents an OMB profile.
+ *
+ * Do not call the setters with null values. Instead, if you want to delete a
+ * field, pass an empty string. The getters will return null for empty fields.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Profile {
+ protected $identifier_uri;
+ protected $profile_url;
+ protected $nickname;
+ protected $license_url;
+ protected $fullname;
+ protected $homepage;
+ protected $bio;
+ protected $location;
+ protected $avatar_url;
+
+ /* The profile as OMB param array. Cached and rebuild on usage.
+ false while outdated. */
+ protected $param_array;
+
+ /**
+ * Constructor for OMB_Profile
+ *
+ * Initializes the OMB_Profile object with an identifier uri.
+ *
+ * @param string $identifier_uri The profile URI as defined by the OMB. A unique
+ * and unchanging identifier for a profile.
+ *
+ * @access public
+ */
+ public function __construct($identifier_uri) {
+ if (!Validate::uri($identifier_uri)) {
+ throw new OMB_InvalidParameterException($identifier_uri, 'profile',
+ 'omb_listenee or omb_listener');
+ }
+ $this->identifier_uri = $identifier_uri;
+ $this->param_array = false;
+ }
+
+ /**
+ * Returns the profile as array
+ *
+ * The method returns an array which contains the whole profile as array. The
+ * array is cached and only rebuilt on changes of the profile.
+ *
+ * @param bool $force_all Specifies whether empty fields should be added to
+ * the array as well. This is neccessary to clear
+ * fields via updateProfile.
+ *
+ * @param string $prefix The common prefix to the key for all parameters.
+ *
+ * @access public
+ *
+ * @return array The profile as parameter array
+ */
+ public function asParameters($prefix, $force_all = false) {
+ if ($this->param_array === false) {
+ $this->param_array = array('' => $this->identifier_uri);
+
+ if ($force_all || !is_null($this->profile_url)) {
+ $this->param_array['_profile'] = $this->profile_url;
+ }
+
+ if ($force_all || !is_null($this->homepage)) {
+ $this->param_array['_homepage'] = $this->homepage;
+ }
+
+ if ($force_all || !is_null($this->nickname)) {
+ $this->param_array['_nickname'] = $this->nickname;
+ }
+
+ if ($force_all || !is_null($this->license_url)) {
+ $this->param_array['_license'] = $this->license_url;
+ }
+
+ if ($force_all || !is_null($this->fullname)) {
+ $this->param_array['_fullname'] = $this->fullname;
+ }
+
+ if ($force_all || !is_null($this->bio)) {
+ $this->param_array['_bio'] = $this->bio;
+ }
+
+ if ($force_all || !is_null($this->location)) {
+ $this->param_array['_location'] = $this->location;
+ }
+
+ if ($force_all || !is_null($this->avatar_url)) {
+ $this->param_array['_avatar'] = $this->avatar_url;
+ }
+
+ }
+ $ret = array();
+ foreach ($this->param_array as $k => $v) {
+ $ret[$prefix . $k] = $v;
+ }
+ return $ret;
+ }
+
+ /**
+ * Builds an OMB_Profile object from array
+ *
+ * The method builds an OMB_Profile object from the passed parameters array. The
+ * array MUST provide a profile URI. The array fields HAVE TO be named according
+ * to the OMB standard. The prefix (omb_listener or omb_listenee) is passed as a
+ * parameter.
+ *
+ * @param string $parameters An array containing the profile parameters.
+ * @param string $prefix The common prefix of the profile parameter keys.
+ *
+ * @access public
+ *
+ * @returns OMB_Profile The built OMB_Profile.
+ */
+ public static function fromParameters($parameters, $prefix) {
+ if (!isset($parameters[$prefix])) {
+ throw new OMB_InvalidParameterException('', 'profile', $prefix);
+ }
+
+ $profile = new OMB_Profile($parameters[$prefix]);
+ $profile->updateFromParameters($parameters, $prefix);
+ return $profile;
+ }
+
+ /**
+ * Update from array
+ *
+ * Updates from the passed parameters array. The array does not have to
+ * provide a profile URI. The array fields HAVE TO be named according to the
+ * OMB standard. The prefix (omb_listener or omb_listenee) is passed as a
+ * parameter.
+ *
+ * @param string $parameters An array containing the profile parameters.
+ * @param string $prefix The common prefix of the profile parameter keys.
+ *
+ * @access public
+ */
+ public function updateFromParameters($parameters, $prefix) {
+ if (isset($parameters[$prefix.'_profile'])) {
+ $this->setProfileURL($parameters[$prefix.'_profile']);
+ }
+
+ if (isset($parameters[$prefix.'_license'])) {
+ $this->setLicenseURL($parameters[$prefix.'_license']);
+ }
+
+ if (isset($parameters[$prefix.'_nickname'])) {
+ $this->setNickname($parameters[$prefix.'_nickname']);
+ }
+
+ if (isset($parameters[$prefix.'_fullname'])) {
+ $this->setFullname($parameters[$prefix.'_fullname']);
+ }
+
+ if (isset($parameters[$prefix.'_homepage'])) {
+ $this->setHomepage($parameters[$prefix.'_homepage']);
+ }
+
+ if (isset($parameters[$prefix.'_bio'])) {
+ $this->setBio($parameters[$prefix.'_bio']);
+ }
+
+ if (isset($parameters[$prefix.'_location'])) {
+ $this->setLocation($parameters[$prefix.'_location']);
+ }
+
+ if (isset($parameters[$prefix.'_avatar'])) {
+ $this->setAvatarURL($parameters[$prefix.'_avatar']);
+ }
+ }
+
+ public function getIdentifierURI() {
+ return $this->identifier_uri;
+ }
+
+ public function getProfileURL() {
+ return $this->profile_url;
+ }
+
+ public function getHomepage() {
+ return $this->homepage;
+ }
+
+ public function getNickname() {
+ return $this->nickname;
+ }
+
+ public function getLicenseURL() {
+ return $this->license_url;
+ }
+
+ public function getFullname() {
+ return $this->fullname;
+ }
+
+ public function getBio() {
+ return $this->bio;
+ }
+
+ public function getLocation() {
+ return $this->location;
+ }
+
+ public function getAvatarURL() {
+ return $this->avatar_url;
+ }
+
+ public function setProfileURL($profile_url) {
+ if (!OMB_Helper::validateURL($profile_url)) {
+ throw new OMB_InvalidParameterException($profile_url, 'profile',
+ 'omb_listenee_profile or omb_listener_profile');
+ }
+ $this->profile_url = $profile_url;
+ $this->param_array = false;
+ }
+
+ public function setNickname($nickname) {
+ if (!Validate::string($nickname,
+ array('min_length' => 1,
+ 'max_length' => 64,
+ 'format' => VALIDATE_NUM . VALIDATE_ALPHA))) {
+ throw new OMB_InvalidParameterException($nickname, 'profile', 'nickname');
+ }
+
+ $this->nickname = $nickname;
+ $this->param_array = false;
+ }
+
+ public function setLicenseURL($license_url) {
+ if (!OMB_Helper::validateURL($license_url)) {
+ throw new OMB_InvalidParameterException($license_url, 'profile',
+ 'omb_listenee_license or omb_listener_license');
+ }
+ $this->license_url = $license_url;
+ $this->param_array = false;
+ }
+
+ public function setFullname($fullname) {
+ if ($fullname === '') {
+ $fullname = null;
+ } elseif (!Validate::string($fullname, array('max_length' => 255))) {
+ throw new OMB_InvalidParameterException($fullname, 'profile', 'fullname');
+ }
+ $this->fullname = $fullname;
+ $this->param_array = false;
+ }
+
+ public function setHomepage($homepage) {
+ if ($homepage === '') {
+ $homepage = null;
+ }
+ $this->homepage = $homepage;
+ $this->param_array = false;
+ }
+
+ public function setBio($bio) {
+ if ($bio === '') {
+ $bio = null;
+ } elseif (!Validate::string($bio, array('max_length' => 140))) {
+ throw new OMB_InvalidParameterException($bio, 'profile', 'fullname');
+ }
+ $this->bio = $bio;
+ $this->param_array = false;
+ }
+
+ public function setLocation($location) {
+ if ($location === '') {
+ $location = null;
+ } elseif (!Validate::string($location, array('max_length' => 255))) {
+ throw new OMB_InvalidParameterException($location, 'profile', 'fullname');
+ }
+ $this->location = $location;
+ $this->param_array = false;
+ }
+
+ public function setAvatarURL($avatar_url) {
+ if ($avatar_url === '') {
+ $avatar_url = null;
+ } elseif (!OMB_Helper::validateURL($avatar_url)) {
+ throw new OMB_InvalidParameterException($avatar_url, 'profile',
+ 'omb_listenee_avatar or omb_listener_avatar');
+ }
+ $this->avatar_url = $avatar_url;
+ $this->param_array = false;
+ }
+
+}
+?>
diff --git a/extlib/libomb/remoteserviceexception.php b/extlib/libomb/remoteserviceexception.php
new file mode 100755
index 000000000..374d15973
--- /dev/null
+++ b/extlib/libomb/remoteserviceexception.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Exception stating that the remote service had a failure
+ *
+ * This exception is raised when a remote service failed to return a valid
+ * response to a request or send a valid request.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+class OMB_RemoteServiceException extends Exception {
+ public static function fromYadis($request_uri, $result) {
+ if ($result->status == 200) {
+ $err = 'Got wrong response ' . $result->body;
+ } else {
+ $err = 'Got error code ' . $result->status . ' with response ' . $result->body;
+ }
+ return new OMB_RemoteServiceException($request_uri . ': ' . $err);
+ }
+
+ public static function forRequest($action_uri, $failure) {
+ return new OMB_RemoteServiceException("Handler for $action_uri: " . $failure);
+ }
+}
+?>
diff --git a/extlib/libomb/service_consumer.php b/extlib/libomb/service_consumer.php
new file mode 100755
index 000000000..273fd052e
--- /dev/null
+++ b/extlib/libomb/service_consumer.php
@@ -0,0 +1,430 @@
+<?php
+
+require_once 'constants.php';
+require_once 'Validate.php';
+require_once 'Auth/Yadis/Yadis.php';
+require_once 'OAuth.php';
+require_once 'unsupportedserviceexception.php';
+require_once 'remoteserviceexception.php';
+require_once 'omb_yadis_xrds.php';
+require_once 'helper.php';
+
+/**
+ * OMB service representation
+ *
+ * This class represents a complete remote OMB service. It provides discovery
+ * and execution of the service’s methods.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Service_Consumer {
+ protected $url; /* The service URL */
+ protected $services; /* An array of strings mapping service URI to
+ service URL */
+
+ protected $token; /* An OAuthToken */
+
+ protected $listener_uri; /* The URI identifying the listener, i. e. the
+ remote user. */
+
+ protected $listenee_uri; /* The URI identifying the listenee, i. e. the
+ local user during an auth request. */
+
+ /**
+ * According to OAuth Core 1.0, an user authorization request is no full-blown
+ * OAuth request. nonce, timestamp, consumer_key and signature are not needed
+ * in this step. See http://laconi.ca/trac/ticket/827 for more informations.
+ *
+ * Since Laconica up to version 0.7.2 performs a full OAuth request check, a
+ * correct request would fail.
+ **/
+ public $performLegacyAuthRequest = true;
+
+ /* Helper stuff we are going to need. */
+ protected $fetcher;
+ protected $oauth_consumer;
+ protected $datastore;
+
+ /**
+ * Constructor for OMB_Service_Consumer
+ *
+ * Initializes an OMB_Service_Consumer object representing the OMB service
+ * specified by $service_url. Performs a complete service discovery using
+ * Yadis.
+ * Throws OMB_UnsupportedServiceException if XRDS file does not specify a
+ * complete OMB service.
+ *
+ * @param string $service_url The URL of the service
+ * @param string $consumer_url An URL representing the consumer
+ * @param OMB_Datastore $datastore An instance of a class implementing
+ * OMB_Datastore
+ *
+ * @access public
+ **/
+ public function __construct ($service_url, $consumer_url, $datastore) {
+ $this->url = $service_url;
+ $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+ $this->datastore = $datastore;
+ $this->oauth_consumer = new OAuthConsumer($consumer_url, '');
+
+ $xrds = OMB_Yadis_XRDS::fromYadisURL($service_url, $this->fetcher);
+
+ /* Detect our services. This performs a validation as well, since
+ getService und getXRD throw exceptions on failure. */
+ $this->services = array();
+
+ foreach (array(OAUTH_DISCOVERY => OMB_Helper::$OAUTH_SERVICES,
+ OMB_VERSION => OMB_Helper::$OMB_SERVICES)
+ as $service_root => $targetservices) {
+ $uris = $xrds->getService($service_root)->getURIs();
+ $xrd = $xrds->getXRD($uris[0]);
+ foreach ($targetservices as $targetservice) {
+ $yadis_service = $xrd->getService($targetservice);
+ if ($targetservice == OAUTH_ENDPOINT_REQUEST) {
+ $localid = $yadis_service->getElements('xrd:LocalID');
+ $this->listener_uri = $yadis_service->parser->content($localid[0]);
+ }
+ $uris = $yadis_service->getURIs();
+ $this->services[$targetservice] = $uris[0];
+ }
+ }
+ }
+
+ /**
+ * Get the handler URI for a service
+ *
+ * Returns the URI the remote web service has specified for the given
+ * service.
+ *
+ * @param string $service The URI identifying the service
+ *
+ * @access public
+ *
+ * @return string The service handler URI
+ **/
+ public function getServiceURI($service) {
+ return $this->services[$service];
+ }
+
+ /**
+ * Get the remote user’s URI
+ *
+ * Returns the URI of the remote user, i. e. the listener.
+ *
+ * @access public
+ *
+ * @return string The remote user’s URI
+ **/
+ public function getRemoteUserURI() {
+ return $this->listener_uri;
+ }
+
+ /**
+ * Get the listenee’s URI
+ *
+ * Returns the URI of the user being subscribed to, i. e. the local user.
+ *
+ * @access public
+ *
+ * @return string The local user’s URI
+ **/
+ public function getListeneeURI() {
+ return $this->listenee_uri;
+ }
+
+ /**
+ * Request a request token
+ *
+ * Performs a token request on the service. Returns an OAuthToken on success.
+ * Throws an exception if the request fails.
+ *
+ * @access public
+ *
+ * @return OAuthToken An unauthorized request token
+ **/
+ public function requestToken() {
+ /* Set the token to null just in case the user called setToken. */
+ $this->token = null;
+
+ $result = $this->performAction(OAUTH_ENDPOINT_REQUEST,
+ array('omb_listener' => $this->listener_uri));
+ if ($result->status != 200) {
+ throw OMB_RemoteServiceException::fromYadis(OAUTH_ENDPOINT_REQUEST,
+ $result);
+ }
+ parse_str($result->body, $return);
+ if (!isset($return['oauth_token']) || !isset($return['oauth_token_secret'])) {
+ throw OMB_RemoteServiceException::fromYadis(OAUTH_ENDPOINT_REQUEST,
+ $result);
+ }
+ $this->setToken($return['oauth_token'], $return['oauth_token_secret']);
+ return $this->token;
+ }
+
+ /**
+ *
+ * Request authorization
+ *
+ * Returns an URL which equals to an authorization request. The end user
+ * should be redirected to this location to perform authorization.
+ * The $finish_url should be a local resource which invokes
+ * OMB_Consumer::finishAuthorization on request.
+ *
+ * @param OMB_Profile $profile An OMB_Profile object representing the
+ * soon-to-be subscribed (i. e. local) user
+ * @param string $finish_url Target location after successful
+ * authorization
+ *
+ * @access public
+ *
+ * @return string An URL representing an authorization request
+ **/
+ public function requestAuthorization($profile, $finish_url) {
+ if ($this->performLegacyAuthRequest) {
+ $params = $profile->asParameters('omb_listenee', false);
+ $params['omb_listener'] = $this->listener_uri;
+ $params['oauth_callback'] = $finish_url;
+
+ $url = $this->prepareAction(OAUTH_ENDPOINT_AUTHORIZE, $params, 'GET')->to_url();
+ } else {
+
+ $params = array(
+ 'oauth_callback' => $finish_url,
+ 'oauth_token' => $this->token->key,
+ 'omb_version' => OMB_VERSION,
+ 'omb_listener' => $this->listener_uri);
+
+ $params = array_merge($profile->asParameters('omb_listenee', false). $params);
+
+ /* Build result URL. */
+ $url = $this->services[OAUTH_ENDPOINT_AUTHORIZE];
+ $url .= (strrpos($url, '?') === false ? '?' : '&');
+ foreach ($params as $k => $v) {
+ $url .= OAuthUtil::urlencode_rfc3986($k) . '=' . OAuthUtil::urlencode_rfc3986($v) . '&';
+ }
+ }
+
+ $this->listenee_uri = $profile->getIdentifierURI();
+
+ return $url;
+ }
+
+ /**
+ * Finish authorization
+ *
+ * Finish the subscription process by converting the received and authorized
+ * request token into an access token. After that, the subscriber’s profile
+ * and the subscription are stored in the database.
+ * Expects an OAuthRequest in query parameters.
+ * Throws exceptions on failure.
+ *
+ * @access public
+ **/
+ public function finishAuthorization() {
+ OMB_Helper::removeMagicQuotesFromRequest();
+ $req = OAuthRequest::from_request();
+ if ($req->get_parameter('oauth_token') !=
+ $this->token->key) {
+ /* That’s not the token I wanted to get authorized. */
+ throw new OAuthException('The authorized token does not equal the ' .
+ 'submitted token.');
+ }
+
+ if ($req->get_parameter('omb_version') != OMB_VERSION) {
+ throw new OMB_RemoteServiceException('The remote service uses an ' .
+ 'unsupported OMB version');
+ }
+
+ /* Construct the profile to validate it. */
+
+ /* Fix OMB bug. Listener URI is not passed. */
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ $params = $_POST;
+ } else {
+ $params = $_GET;
+ }
+ $params['omb_listener'] = $this->listener_uri;
+
+ require_once 'profile.php';
+ $listener = OMB_Profile::fromParameters($params, 'omb_listener');
+
+ /* Ask the remote service to convert the authorized request token into an
+ access token. */
+
+ $result = $this->performAction(OAUTH_ENDPOINT_ACCESS, array());
+ if ($result->status != 200) {
+ throw new OAuthException('Could not get access token');
+ }
+
+ parse_str($result->body, $return);
+ if (!isset($return['oauth_token']) || !isset($return['oauth_token_secret'])) {
+ throw new OAuthException('Could not get access token');
+ }
+ $this->setToken($return['oauth_token'], $return['oauth_token_secret']);
+
+ /* Subscription is finished and valid. Now store the new subscriber and the
+ subscription in the database. */
+
+ $this->datastore->saveProfile($listener);
+ $this->datastore->saveSubscription($this->listener_uri,
+ $this->listenee_uri,
+ $this->token);
+ }
+
+ /**
+ * Return the URI identifying the listener
+ *
+ * Returns the URI for the OMB user who tries to subscribe or already has
+ * subscribed our user. This method is a workaround for a serious OMB flaw:
+ * The Listener URI is not passed in the finishauthorization call.
+ *
+ * @access public
+ *
+ * @return string the listener’s URI
+ **/
+ public function getListenerURI() {
+ return $this->listener_uri;
+ }
+
+ /**
+ * Inform the service about a profile update
+ *
+ * Sends an updated profile to the service.
+ *
+ * @param OMB_Profile $profile The profile that has changed
+ *
+ * @access public
+ **/
+ public function updateProfile($profile) {
+ $params = $profile->asParameters('omb_listenee', true);
+ $this->performOMBAction(OMB_ENDPOINT_UPDATEPROFILE, $params, $profile->getIdentifierURI());
+ }
+
+ /**
+ * Inform the service about a new notice
+ *
+ * Sends a notice to the service.
+ *
+ * @param OMB_Notice $notice The notice
+ *
+ * @access public
+ **/
+ public function postNotice($notice) {
+ $params = $notice->asParameters();
+ $params['omb_listenee'] = $notice->getAuthor()->getIdentifierURI();
+ $this->performOMBAction(OMB_ENDPOINT_POSTNOTICE, $params, $params['omb_listenee']);
+ }
+
+ /**
+ * Set the token member variable
+ *
+ * Initializes the token based on given token and secret token.
+ *
+ * @param string $token The token
+ * @param string $secret The secret token
+ *
+ * @access public
+ **/
+ public function setToken($token, $secret) {
+ $this->token = new OAuthToken($token, $secret);
+ }
+
+ /**
+ * Prepare an OAuthRequest object
+ *
+ * Creates an OAuthRequest object mapping the request specified by the
+ * parameters.
+ *
+ * @param string $action_uri The URI specifying the target service
+ * @param array $params Additional parameters for the service call
+ * @param string $method The HTTP method used to call the service
+ * ('POST' or 'GET', usually)
+ *
+ * @access protected
+ *
+ * @return OAuthRequest the prepared request
+ **/
+ protected function prepareAction($action_uri, $params, $method) {
+ $url = $this->services[$action_uri];
+
+ $url_params = array();
+ parse_str(parse_url($url, PHP_URL_QUERY), $url_params);
+
+ /* Add OMB version. */
+ $url_params['omb_version'] = OMB_VERSION;
+
+ /* Add user-defined parameters. */
+ $url_params = array_merge($url_params, $params);
+
+ $req = OAuthRequest::from_consumer_and_token($this->oauth_consumer,
+ $this->token, $method, $url, $url_params);
+
+ /* Sign the request. */
+ $req->sign_request(new OAuthSignatureMethod_HMAC_SHA1(),
+ $this->oauth_consumer, $this->token);
+
+ return $req;
+ }
+
+ /**
+ * Perform a service call
+ *
+ * Creates an OAuthRequest object and execute the mapped call as POST request.
+ *
+ * @param string $action_uri The URI specifying the target service
+ * @param array $params Additional parameters for the service call
+ *
+ * @access protected
+ *
+ * @return Auth_Yadis_HTTPResponse The POST request response
+ **/
+ protected function performAction($action_uri, $params) {
+ $req = $this->prepareAction($action_uri, $params, 'POST');
+
+ /* Return result page. */
+ return $this->fetcher->post($req->get_normalized_http_url(), $req->to_postdata(), array());
+ }
+
+ /**
+ * Perform an OMB action
+ *
+ * Executes an OMB action – to date, it’s one of updateProfile or postNotice.
+ *
+ * @param string $action_uri The URI specifying the target service
+ * @param array $params Additional parameters for the service call
+ * @param string $listenee_uri The URI identifying the local user for whom
+ * the action is performed
+ *
+ * @access protected
+ **/
+ protected function performOMBAction($action_uri, $params, $listenee_uri) {
+ $result = $this->performAction($action_uri, $params);
+ if ($result->status == 403) {
+ /* The remote user unsubscribed us. */
+ $this->datastore->deleteSubscription($this->listener_uri, $listenee_uri);
+ } else if ($result->status != 200 ||
+ strpos($result->body, 'omb_version=' . OMB_VERSION) === false) {
+ /* The server signaled an error or sent an incorrect response. */
+ throw OMB_RemoteServiceException::fromYadis($action_uri, $result);
+ }
+ }
+}
diff --git a/extlib/libomb/service_provider.php b/extlib/libomb/service_provider.php
new file mode 100755
index 000000000..753152713
--- /dev/null
+++ b/extlib/libomb/service_provider.php
@@ -0,0 +1,425 @@
+<?php
+
+require_once 'constants.php';
+require_once 'remoteserviceexception.php';
+require_once 'helper.php';
+
+/**
+ * OMB service realization
+ *
+ * This class realizes a complete, simple OMB service.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+class OMB_Service_Provider {
+ protected $user; /* An OMB_Profile representing the user */
+ protected $datastore; /* AN OMB_Datastore */
+
+ protected $remote_user; /* An OMB_Profile representing the remote user during
+ the authorization process */
+
+ protected $oauth_server; /* An OAuthServer; should only be accessed via
+ getOAuthServer. */
+
+ /**
+ * Initialize an OMB_Service_Provider object
+ *
+ * Constructs an OMB_Service_Provider instance that provides OMB services
+ * referring to a particular user.
+ *
+ * @param OMB_Profile $user An OMB_Profile; mandatory for XRDS
+ * output, user auth handling and OMB
+ * action performing
+ * @param OMB_Datastore $datastore An OMB_Datastore; mandatory for
+ * everything but XRDS output
+ * @param OAuthServer $oauth_server An OAuthServer; used for token writing
+ * and OMB action handling; will use
+ * default value if not set
+ *
+ * @access public
+ **/
+ public function __construct ($user = null, $datastore = null, $oauth_server = null) {
+ $this->user = $user;
+ $this->datastore = $datastore;
+ $this->oauth_server = $oauth_server;
+ }
+
+ public function getRemoteUser() {
+ return $this->remote_user;
+ }
+
+ /**
+ * Write a XRDS document
+ *
+ * Writes a XRDS document specifying the OMB service. Optionally uses a
+ * given object of a class implementing OMB_XRDS_Writer for output. Else
+ * OMB_Plain_XRDS_Writer is used.
+ *
+ * @param OMB_XRDS_Mapper $xrds_mapper An object mapping actions to URLs
+ * @param OMB_XRDS_Writer $xrds_writer Optional; The OMB_XRDS_Writer used to
+ * write the XRDS document
+ *
+ * @access public
+ *
+ * @return mixed Depends on the used OMB_XRDS_Writer; OMB_Plain_XRDS_Writer
+ * returns nothing.
+ **/
+ public function writeXRDS($xrds_mapper, $xrds_writer = null) {
+ if ($xrds_writer == null) {
+ require_once 'plain_xrds_writer.php';
+ $xrds_writer = new OMB_Plain_XRDS_Writer();
+ }
+ return $xrds_writer->writeXRDS($this->user, $xrds_mapper);
+ }
+
+ /**
+ * Echo a request token
+ *
+ * Outputs an unauthorized request token for the query found in $_GET or
+ * $_POST.
+ *
+ * @access public
+ **/
+ public function writeRequestToken() {
+ OMB_Helper::removeMagicQuotesFromRequest();
+ echo $this->getOAuthServer()->fetch_request_token(OAuthRequest::from_request());
+ }
+
+ /**
+ * Handle an user authorization request.
+ *
+ * Parses an authorization request. This includes OAuth and OMB verification.
+ * Throws exceptions on failures. Returns an OMB_Profile object representing
+ * the remote user.
+ *
+ * The OMB_Profile passed to the constructor of OMB_Service_Provider should
+ * not represent the user specified in the authorization request, but the one
+ * currently logged in to the service. This condition being satisfied,
+ * handleUserAuth will check whether the listener specified in the request is
+ * identical to the logged in user.
+ *
+ * @access public
+ *
+ * @return OMB_Profile The profile of the soon-to-be subscribed, i. e. remote
+ * user
+ **/
+ public function handleUserAuth() {
+ OMB_Helper::removeMagicQuotesFromRequest();
+
+ /* Verify the request token. */
+
+ $this->token = $this->datastore->lookup_token(null, "request", $_GET['oauth_token']);
+ if (is_null($this->token)) {
+ throw new OAuthException('The given request token has not been issued ' .
+ 'by this service.');
+ }
+
+ /* Verify the OMB part. */
+
+ if ($_GET['omb_version'] !== OMB_VERSION) {
+ throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
+ 'Wrong OMB version ' . $_GET['omb_version']);
+ }
+
+ if ($_GET['omb_listener'] !== $this->user->getIdentifierURI()) {
+ throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
+ 'Wrong OMB listener ' . $_GET['omb_listener']);
+ }
+
+ foreach (array('omb_listenee', 'omb_listenee_profile',
+ 'omb_listenee_nickname', 'omb_listenee_license') as $param) {
+ if (!isset($_GET[$param]) || is_null($_GET[$param])) {
+ throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
+ "Required parameter '$param' not found");
+ }
+ }
+
+ /* Store given callback for later use. */
+ if (isset($_GET['oauth_callback']) && $_GET['oauth_callback'] !== '') {
+ $this->callback = $_GET['oauth_callback'];
+ if (!OMB_Helper::validateURL($this->callback)) {
+ throw OMB_RemoteServiceException::forRequest(OAUTH_ENDPOINT_AUTHORIZE,
+ 'Invalid callback URL specified');
+ }
+ }
+ $this->remote_user = OMB_Profile::fromParameters($_GET, 'omb_listenee');
+
+ return $this->remote_user;
+ }
+
+ /**
+ * Continue the OAuth dance after user authorization
+ *
+ * Performs the appropriate actions after user answered the authorization
+ * request.
+ *
+ * @param bool $accepted Whether the user granted authorization
+ *
+ * @access public
+ *
+ * @return array A two-component array with the values:
+ * - callback The callback URL or null if none given
+ * - token The authorized request token or null if not
+ * authorized.
+ **/
+ public function continueUserAuth($accepted) {
+ $callback = $this->callback;
+ if (!$accepted) {
+ $this->datastore->revoke_token($this->token->key);
+ $this->token = null;
+ /* TODO: The handling is probably wrong in terms of OAuth 1.0 but the way
+ laconica works. Moreover I don’t know the right way either. */
+
+ } else {
+ $this->datastore->authorize_token($this->token->key);
+ $this->datastore->saveProfile($this->remote_user);
+ $this->datastore->saveSubscription($this->user->getIdentifierURI(),
+ $this->remote_user->getIdentifierURI(), $this->token);
+
+ if (!is_null($this->callback)) {
+ /* Callback wants to get some informations as well. */
+ $params = $this->user->asParameters('omb_listener', false);
+
+ $params['oauth_token'] = $this->token->key;
+ $params['omb_version'] = OMB_VERSION;
+
+ $callback .= (parse_url($this->callback, PHP_URL_QUERY) ? '&' : '?');
+ foreach ($params as $k => $v) {
+ $callback .= OAuthUtil::urlencode_rfc3986($k) . '=' .
+ OAuthUtil::urlencode_rfc3986($v) . '&';
+ }
+ }
+ }
+ return array($callback, $this->token);
+ }
+
+ /**
+ * Echo an access token
+ *
+ * Outputs an access token for the query found in $_POST. OMB 0.1 specifies
+ * that the access token request has to be a POST even if OAuth allows GET as
+ * well.
+ *
+ * @access public
+ **/
+ public function writeAccessToken() {
+ OMB_Helper::removeMagicQuotesFromRequest();
+ echo $this->getOAuthServer()->fetch_access_token(
+ OAuthRequest::from_request('POST'));
+ }
+
+ /**
+ * Handle an updateprofile request
+ *
+ * Handles an updateprofile request posted to this service. Updates the
+ * profile through the OMB_Datastore.
+ *
+ * @access public
+ *
+ * @return OMB_Profile The updated profile
+ **/
+ public function handleUpdateProfile() {
+ list($req, $profile) = $this->handleOMBRequest(OMB_ENDPOINT_UPDATEPROFILE);
+ $profile->updateFromParameters($req->get_parameters(), 'omb_listenee');
+ $this->datastore->saveProfile($profile);
+ $this->finishOMBRequest();
+ return $profile;
+ }
+
+ /**
+ * Handle a postnotice request
+ *
+ * Handles a postnotice request posted to this service. Saves the notice
+ * through the OMB_Datastore.
+ *
+ * @access public
+ *
+ * @return OMB_Notice The received notice
+ **/
+ public function handlePostNotice() {
+ list($req, $profile) = $this->handleOMBRequest(OMB_ENDPOINT_POSTNOTICE);
+ require_once 'notice.php';
+ $notice = OMB_Notice::fromParameters($profile, $req->get_parameters());
+ $this->datastore->saveNotice($notice);
+ $this->finishOMBRequest();
+ return $notice;
+ }
+
+ /**
+ * Handle an OMB request
+ *
+ * Performs common OMB request handling.
+ *
+ * @param string $uri The URI defining the OMB endpoint being served
+ *
+ * @access protected
+ *
+ * @return array(OAuthRequest, OMB_Profile)
+ **/
+ protected function handleOMBRequest($uri) {
+
+ OMB_Helper::removeMagicQuotesFromRequest();
+ $req = OAuthRequest::from_request('POST');
+ $listenee = $req->get_parameter('omb_listenee');
+
+ try {
+ list($consumer, $token) = $this->getOAuthServer()->verify_request($req);
+ } catch (OAuthException $e) {
+ header('HTTP/1.1 403 Forbidden');
+ throw OMB_RemoteServiceException::forRequest($uri,
+ 'Revoked accesstoken for ' . $listenee);
+ }
+
+ $version = $req->get_parameter('omb_version');
+ if ($version !== OMB_VERSION) {
+ header('HTTP/1.1 400 Bad Request');
+ throw OMB_RemoteServiceException::forRequest($uri,
+ 'Wrong OMB version ' . $version);
+ }
+
+ $profile = $this->datastore->getProfile($listenee);
+ if (is_null($profile)) {
+ header('HTTP/1.1 400 Bad Request');
+ throw OMB_RemoteServiceException::forRequest($uri,
+ 'Unknown remote profile ' . $listenee);
+ }
+
+ $subscribers = $this->datastore->getSubscriptions($listenee);
+ if (count($subscribers) === 0) {
+ header('HTTP/1.1 403 Forbidden');
+ throw OMB_RemoteServiceException::forRequest($uri,
+ 'No subscriber for ' . $listenee);
+ }
+
+ return array($req, $profile);
+ }
+
+ /**
+ * Finishes an OMB request handling
+ *
+ * Performs common OMB request handling finishing.
+ *
+ * @access protected
+ **/
+ protected function finishOMBRequest() {
+ header('HTTP/1.1 200 OK');
+ header('Content-type: text/plain');
+ /* There should be no clutter but the version. */
+ echo "omb_version=" . OMB_VERSION;
+ }
+
+ /**
+ * Return an OAuthServer
+ *
+ * Checks whether the OAuthServer is null. If so, initializes it with a
+ * default value. Returns the OAuth server.
+ *
+ * @access protected
+ **/
+ protected function getOAuthServer() {
+ if (is_null($this->oauth_server)) {
+ $this->oauth_server = new OAuthServer($this->datastore);
+ $this->oauth_server->add_signature_method(
+ new OAuthSignatureMethod_HMAC_SHA1());
+ }
+ return $this->oauth_server;
+ }
+
+ /**
+ * Publish a notice
+ *
+ * Posts an OMB notice. This includes storing the notice and posting it to
+ * subscribed users.
+ *
+ * @param OMB_Notice $notice The new notice
+ *
+ * @access public
+ *
+ * @return array An array mapping subscriber URIs to the exception posting to
+ * them has raised; Empty array if no exception occured
+ **/
+ public function postNotice($notice) {
+ $uri = $this->user->getIdentifierURI();
+
+ /* $notice is passed by reference and may change. */
+ $this->datastore->saveNotice($notice);
+ $subscribers = $this->datastore->getSubscriptions($uri);
+
+ /* No one to post to. */
+ if (is_null($subscribers)) {
+ return array();
+ }
+
+ require_once 'service_consumer.php';
+
+ $err = array();
+ foreach($subscribers as $subscriber) {
+ try {
+ $service = new OMB_Service_Consumer($subscriber['uri'], $uri, $this->datastore);
+ $service->setToken($subscriber['token'], $subscriber['secret']);
+ $service->postNotice($notice);
+ } catch (Exception $e) {
+ $err[$subscriber['uri']] = $e;
+ continue;
+ }
+ }
+ return $err;
+ }
+
+ /**
+ * Publish a profile update
+ *
+ * Posts the current profile as an OMB profile update. This includes updating
+ * the stored profile and posting it to subscribed users.
+ *
+ * @access public
+ *
+ * @return array An array mapping subscriber URIs to the exception posting to
+ * them has raised; Empty array if no exception occured
+ **/
+ public function updateProfile() {
+ $uri = $this->user->getIdentifierURI();
+
+ $this->datastore->saveProfile($this->user);
+ $subscribers = $this->datastore->getSubscriptions($uri);
+
+ /* No one to post to. */
+ if (is_null($subscribers)) {
+ return array();
+ }
+
+ require_once 'service_consumer.php';
+
+ $err = array();
+ foreach($subscribers as $subscriber) {
+ try {
+ $service = new OMB_Service_Consumer($subscriber['uri'], $uri, $this->datastore);
+ $service->setToken($subscriber['token'], $subscriber['secret']);
+ $service->updateProfile($this->user);
+ } catch (Exception $e) {
+ $err[$subscriber['uri']] = $e;
+ continue;
+ }
+ }
+ return $err;
+ }
+}
diff --git a/extlib/libomb/unsupportedserviceexception.php b/extlib/libomb/unsupportedserviceexception.php
new file mode 100755
index 000000000..4dab63ebe
--- /dev/null
+++ b/extlib/libomb/unsupportedserviceexception.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Exception stating that a requested service is not available
+ *
+ * This exception is raised when OMB_Service is asked to call a service the remote
+ * server does not provide.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+class OMB_UnsupportedServiceException extends Exception {
+
+}
+?>
diff --git a/extlib/libomb/xrds_mapper.php b/extlib/libomb/xrds_mapper.php
new file mode 100755
index 000000000..7552154e5
--- /dev/null
+++ b/extlib/libomb/xrds_mapper.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Map XRDS actions to URLs
+ *
+ * This interface specifies classes which write the XRDS file announcing
+ * the OMB server. An instance of an implementing class should be passed to
+ * OMB_Service_Provider->writeXRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+interface OMB_XRDS_Mapper {
+ public function getURL($action);
+}
+?>
diff --git a/extlib/libomb/xrds_writer.php b/extlib/libomb/xrds_writer.php
new file mode 100755
index 000000000..31b451b9c
--- /dev/null
+++ b/extlib/libomb/xrds_writer.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Write OMB-specific XRDS
+ *
+ * This interface specifies classes which write the XRDS file announcing
+ * the OMB server. An instance of an implementing class should be passed to
+ * OMB_Service_Provider->writeXRDS.
+ *
+ * PHP version 5
+ *
+ * LICENSE: 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/>.
+ *
+ * @package OMB
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @copyright 2009 Adrian Lang
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL 3.0
+ **/
+
+interface OMB_XRDS_Writer {
+ public function writeXRDS($user, $mapper);
+}
+?>
diff --git a/index.php b/index.php
index 7669778f6..51e30f578 100644
--- a/index.php
+++ b/index.php
@@ -15,6 +15,22 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category StatusNet
+ * @package StatusNet
+ * @author Brenda Wallace <shiny@cpan.org>
+ * @author Christopher Vollick <psycotica0@gmail.com>
+ * @author CiaranG <ciaran@ciarang.com>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Evan Prodromou <evan@controlezvous.ca>
+ * @author Gina Haeussge <osd@foosel.net>
+ * @author Jeffery To <jeffery.to@gmail.com>
+ * @author Mike Cochrane <mikec@mikenz.geek.nz>
+ * @author Robin Millette <millette@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@controlyourself.ca>
+ * @author Tom Adams <tom@holizz.com>
+ *
+ * @license GNU Affero General Public License http://www.gnu.org/licenses/
*/
define('INSTALLDIR', dirname(__FILE__));
@@ -29,10 +45,17 @@ $action = null;
function getPath($req)
{
if ((common_config('site', 'fancy') || !array_key_exists('PATH_INFO', $_SERVER))
- && array_key_exists('p', $req)) {
+ && array_key_exists('p', $req)
+ ) {
return $req['p'];
} else if (array_key_exists('PATH_INFO', $_SERVER)) {
- return $_SERVER['PATH_INFO'];
+ $path = $_SERVER['PATH_INFO'];
+ $script = $_SERVER['SCRIPT_NAME'];
+ if (substr($path, 0, mb_strlen($script)) == $script) {
+ return substr($path, mb_strlen($script));
+ } else {
+ return $path;
+ }
} else {
return null;
}
@@ -45,28 +68,35 @@ function handleError($error)
}
$logmsg = "PEAR error: " . $error->getMessage();
- if(common_config('site', 'logdebug')) {
+ if (common_config('site', 'logdebug')) {
$logmsg .= " : ". $error->getDebugInfo();
}
common_log(LOG_ERR, $logmsg);
- if(common_config('site', 'logdebug')) {
+ if (common_config('site', 'logdebug')) {
$bt = $error->getBacktrace();
foreach ($bt as $line) {
common_log(LOG_ERR, $line);
}
}
- if ($error instanceof DB_DataObject_Error ||
- $error instanceof DB_Error) {
- $msg = sprintf(_('The database for %s isn\'t responding correctly, '.
- 'so the site won\'t work properly. '.
- 'The site admins probably know about the problem, '.
- 'but you can contact them at %s to make sure. '.
- 'Otherwise, wait a few minutes and try again.'),
- common_config('site', 'name'),
- common_config('site', 'email'));
+ if ($error instanceof DB_DataObject_Error
+ || $error instanceof DB_Error
+ ) {
+ $msg = sprintf(
+ _(
+ 'The database for %s isn\'t responding correctly, '.
+ 'so the site won\'t work properly. '.
+ 'The site admins probably know about the problem, '.
+ 'but you can contact them at %s to make sure. '.
+ 'Otherwise, wait a few minutes and try again.'
+ ),
+ common_config('site', 'name'),
+ common_config('site', 'email')
+ );
} else {
- $msg = _('An important error occured, probably related to email setup. '.
- 'Check logfiles for more info..');
+ $msg = _(
+ 'An important error occured, probably related to email setup. '.
+ 'Check logfiles for more info..'
+ );
}
$dac = new DBErrorAction($msg, 500);
@@ -106,6 +136,19 @@ function checkMirror($action_obj, $args)
}
}
+function isLoginAction($action)
+{
+ static $loginActions = array('login', 'recoverpassword', 'api', 'doc', 'register');
+
+ $login = null;
+
+ if (Event::handle('LoginAction', array($action, &$login))) {
+ $login = in_array($action, $loginActions);
+ }
+
+ return $login;
+}
+
function main()
{
// fake HTTP redirects using lighttpd's 404 redirects
@@ -114,10 +157,11 @@ function main()
$_lighty_url = @parse_url($_lighty_url);
if ($_lighty_url['path'] != '/index.php' && $_lighty_url['path'] != '/') {
- $_lighty_path = preg_replace('/^'.preg_quote(common_config('site','path')).'\//', '', substr($_lighty_url['path'], 1));
+ $_lighty_path = preg_replace('/^'.preg_quote(common_config('site', 'path')).'\//', '', substr($_lighty_url['path'], 1));
$_SERVER['QUERY_STRING'] = 'p='.$_lighty_path;
- if ($_lighty_url['query'])
+ if ($_lighty_url['query']) {
$_SERVER['QUERY_STRING'] .= '&'.$_lighty_url['query'];
+ }
parse_str($_lighty_url['query'], $_lighty_query);
foreach ($_lighty_query as $key => $val) {
$_GET[$key] = $_REQUEST[$key] = $val;
@@ -128,7 +172,7 @@ function main()
$_SERVER['REDIRECT_URL'] = preg_replace("/\?.+$/", "", $_SERVER['REQUEST_URI']);
// quick check for fancy URL auto-detection support in installer.
- if (isset($_SERVER['REDIRECT_URL']) && (preg_replace("/^\/$/","",(dirname($_SERVER['REQUEST_URI']))) . '/check-fancy') === $_SERVER['REDIRECT_URL']) {
+ if (isset($_SERVER['REDIRECT_URL']) && (preg_replace("/^\/$/", "", (dirname($_SERVER['REQUEST_URI']))) . '/check-fancy') === $_SERVER['REDIRECT_URL']) {
die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs.");
}
global $user, $action;
@@ -136,8 +180,12 @@ function main()
Snapshot::check();
if (!_have_config()) {
- $msg = sprintf(_("No configuration file found. Try running ".
- "the installation program first."));
+ $msg = sprintf(
+ _(
+ "No configuration file found. Try running ".
+ "the installation program first."
+ )
+ );
$sac = new ServerErrorAction($msg);
$sac->showPage();
return;
@@ -183,36 +231,12 @@ function main()
// If the site is private, and they're not on one of the "public"
// parts of the site, redirect to login
- if (!$user && common_config('site', 'private')) {
- $public_actions = array('openidlogin', 'finishopenidlogin',
- 'recoverpassword', 'api', 'doc',
- 'opensearch');
- $login_action = 'openidlogin';
- if (!common_config('site', 'openidonly')) {
- $public_actions[] = 'login';
- $public_actions[] = 'register';
- $login_action = 'login';
- }
- if (!in_array($action, $public_actions) &&
- !preg_match('/rss$/', $action)) {
-
- // set returnto
- $rargs =& common_copy_args($args);
- unset($rargs['action']);
- if (common_config('site', 'fancy')) {
- unset($rargs['p']);
- }
- if (array_key_exists('submit', $rargs)) {
- unset($rargs['submit']);
- }
- foreach (array_keys($_COOKIE) as $cookie) {
- unset($rargs[$cookie]);
- }
- common_set_returnto(common_local_url($action, $rargs));
-
- common_redirect(common_local_url($login_action));
- return;
- }
+ if (!$user && common_config('site', 'private')
+ && !isLoginAction($action)
+ && !preg_match('/rss$/', $action)
+ ) {
+ common_redirect(common_local_url('login'));
+ return;
}
$action_class = ucfirst($action).'Action';
diff --git a/install.php b/install.php
index a59b9469d..c2a5bb29e 100644
--- a/install.php
+++ b/install.php
@@ -15,6 +15,24 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Installation
+ * @package Installation
+ *
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @author Brenda Wallace <shiny@cpan.org>
+ * @author Brett Taylor <brett@webfroot.co.nz>
+ * @author Brion Vibber <brion@pobox.com>
+ * @author CiaranG <ciaran@ciarang.com>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Eric Helgeson <helfire@Erics-MBP.local>
+ * @author Evan Prodromou <evan@status.net>
+ * @author Robin Millette <millette@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@status.net>
+ * @author Tom Adams <tom@holizz.com>
+ * @license GNU Affero General Public License http://www.gnu.org/licenses/
+ * @version 0.9
+ * @link http://status.net
*/
define('INSTALLDIR', dirname(__FILE__));
@@ -181,17 +199,34 @@ $external_libraries=array(
'check_class'=>'Validate'
)
);
+$dbModules = array(
+ 'mysql' => array(
+ 'name' => 'MySQL',
+ 'check_module' => 'mysql', // mysqli?
+ 'installer' => 'mysql_db_installer',
+ ),
+ 'pgsql' => array(
+ 'name' => 'PostgreSQL',
+ 'check_module' => 'pgsql',
+ 'installer' => 'pgsql_db_installer',
+ ),
+);
+/**
+ * the actual installation.
+ * If call libraries are present, then install
+ *
+ * @return void
+ */
function main()
{
- if (!checkPrereqs())
- {
+ if (!checkPrereqs()) {
return;
}
-
- if( $_GET['checklibs'] ){
+
+ if (!empty($_GET['checklibs'])) {
showLibs();
- }else{
+ } else {
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
handlePost();
} else {
@@ -200,90 +235,137 @@ function main()
}
}
+/**
+ * checks if an external libary is present
+ *
+ * @param string $external_library Name of library
+ *
+ * @return boolean indicates if library present
+ */
function haveExternalLibrary($external_library)
{
- if(isset($external_library['include']) && ! include_once($external_library['include'])){
+ if (isset($external_library['include']) && !haveIncludeFile($external_library['include'])) {
return false;
}
- if(isset($external_library['check_function']) && ! function_exists($external_library['check_function'])){
+ if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
return false;
}
- if(isset($external_library['check_class']) && ! class_exists($external_library['check_class'])){
+ if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
return false;
}
return true;
}
+// Attempt to include a PHP file and report if it worked, while
+// suppressing the annoying warning messages on failure.
+function haveIncludeFile($filename) {
+ $old = error_reporting(error_reporting() & ~E_WARNING);
+ $ok = include_once($filename);
+ error_reporting($old);
+ return $ok;
+}
+
+/**
+ * Check if all is ready for installation
+ *
+ * @return void
+ */
function checkPrereqs()
{
- $pass = true;
+ $pass = true;
if (file_exists(INSTALLDIR.'/config.php')) {
- ?><p class="error">Config file &quot;config.php&quot; already exists.</p>
- <?php
+ printf('<p class="error">Config file &quot;config.php&quot; already exists.</p>');
$pass = false;
}
if (version_compare(PHP_VERSION, '5.2.3', '<')) {
- ?><p class="error">Require PHP version 5.2.3 or greater.</p><?php
- $pass = false;
+ printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
+ $pass = false;
}
$reqs = array('gd', 'curl',
- 'xmlwriter', 'mbstring');
+ 'xmlwriter', 'mbstring','tidy');
foreach ($reqs as $req) {
if (!checkExtension($req)) {
- ?><p class="error">Cannot load required extension: <code><?php echo $req; ?></code></p><?php
- $pass = false;
+ printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
+ $pass = false;
}
}
- if (!checkExtension('pgsql') && !checkExtension('mysql')) {
- ?><p class="error">Cannot find mysql or pgsql extension. You need one or the other: <code><?php echo $req; ?></code></p><?php
- $pass = false;
- }
-
- if (!is_writable(INSTALLDIR)) {
- ?><p class="error">Cannot write config file to: <code><?php echo INSTALLDIR; ?></code></p>
- <p>On your server, try this command: <code>chmod a+w <?php echo INSTALLDIR; ?></code>
- <?php
- $pass = false;
- }
-
- // Check the subdirs used for file uploads
- $fileSubdirs = array('avatar', 'background', 'file');
- foreach ($fileSubdirs as $fileSubdir) {
- $fileFullPath = INSTALLDIR."/$fileSubdir/";
- if (!is_writable($fileFullPath)) {
- ?><p class="error">Cannot write <?php echo $fileSubdir; ?> directory: <code><?php echo $fileFullPath; ?></code></p>
- <p>On your server, try this command: <code>chmod a+w <?php echo $fileFullPath; ?></code></p>
- <?php
- $pass = false;
- }
- }
-
- return $pass;
+ // Make sure we have at least one database module available
+ global $dbModules;
+ $missingExtensions = array();
+ foreach ($dbModules as $type => $info) {
+ if (!checkExtension($info['check_module'])) {
+ $missingExtensions[] = $info['check_module'];
+ }
+ }
+
+ if (count($missingExtensions) == count($dbModules)) {
+ $req = implode(', ', $missingExtensions);
+ printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other.');
+ $pass = false;
+ }
+
+ if (!is_writable(INSTALLDIR)) {
+ printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
+ printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
+ $pass = false;
+ }
+
+ // Check the subdirs used for file uploads
+ $fileSubdirs = array('avatar', 'background', 'file');
+ foreach ($fileSubdirs as $fileSubdir) {
+ $fileFullPath = INSTALLDIR."/$fileSubdir/";
+ if (!is_writable($fileFullPath)) {
+ printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
+ printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
+ $pass = false;
+ }
+ }
+
+ return $pass;
}
+/**
+ * Checks if a php extension is both installed and loaded
+ *
+ * @param string $name of extension to check
+ *
+ * @return boolean whether extension is installed and loaded
+ */
function checkExtension($name)
{
- if (!extension_loaded($name)) {
- if (!@dl($name.'.so')) {
- return false;
- }
+ if (extension_loaded($name)) {
+ return true;
+ } elseif (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode')) {
+ // dl will throw a fatal error if it's disabled or we're in safe mode.
+ // More fun, it may not even exist under some SAPIs in 5.3.0 or later...
+ $soname = $name . '.' . PHP_SHLIB_SUFFIX;
+ if (PHP_SHLIB_SUFFIX == 'dll') {
+ $soname = "php_" . $soname;
+ }
+ return @dl($soname);
+ } else {
+ return false;
}
- return true;
}
+/**
+ * Show list of libraries
+ *
+ * @return void
+ */
function showLibs()
{
global $external_libraries;
$present_libraries=array();
$absent_libraries=array();
- foreach($external_libraries as $external_library){
- if(haveExternalLibrary($external_library)){
+ foreach ($external_libraries as $external_library) {
+ if (haveExternalLibrary($external_library)) {
$present_libraries[]=$external_library;
- }else{
+ } else {
$absent_libraries[]=$external_library;
}
}
@@ -298,22 +380,21 @@ function showLibs()
<h2>Absent Libraries</h2>
<ul id="absent_libraries">
E_O_T;
- foreach($absent_libraries as $library)
- {
+ foreach ($absent_libraries as $library) {
echo '<li>';
- if($library['url']){
+ if (isset($library['url'])) {
echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
- }else{
+ } else {
echo htmlentities($library['name']);
}
echo '<ul>';
- if($library['deb']){
+ if (isset($library['deb'])) {
echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
}
- if($library['rpm']){
+ if (isset($library['rpm'])) {
echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
}
- if($library['pear']){
+ if (isset($library['pear'])) {
echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
}
echo '</ul>';
@@ -323,12 +404,11 @@ E_O_T;
<h2>Installed Libraries</h2>
<ul id="present_libraries">
E_O_T;
- foreach($present_libraries as $library)
- {
+ foreach ($present_libraries as $library) {
echo '<li>';
- if($library['url']){
+ if (isset($library['url'])) {
echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
- }else{
+ } else {
echo htmlentities($library['name']);
}
echo '</li>';
@@ -340,6 +420,15 @@ E_O_T;
function showForm()
{
+ global $dbModules;
+ $dbRadios = '';
+ $checked = 'checked="checked" '; // Check the first one which exists
+ foreach ($dbModules as $type => $info) {
+ if (checkExtension($info['check_module'])) {
+ $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
+ $checked = '';
+ }
+ }
echo<<<E_O_T
</ul>
</dd>
@@ -376,8 +465,7 @@ function showForm()
<li>
<label for="dbtype">Type</label>
- <input type="radio" name="dbtype" id="fancy-mysql" value="mysql" checked='checked' /> MySQL<br />
- <input type="radio" name="dbtype" id="dbtype-pgsql" value="pgsql" /> PostgreSQL<br />
+ $dbRadios
<p class="form_guide">Database type</p>
</li>
@@ -406,17 +494,11 @@ E_O_T;
function updateStatus($status, $error=false)
{
-?>
- <li <?php echo ($error) ? 'class="error"': ''; ?>><?php echo $status;?></li>
-
-<?php
+ echo '<li' . ($error ? ' class="error"': '' ) . ">$status</li>";
}
function handlePost()
{
-?>
-
-<?php
$host = $_POST['host'];
$dbtype = $_POST['dbtype'];
$database = $_POST['database'];
@@ -427,55 +509,41 @@ function handlePost()
$server = $_SERVER['HTTP_HOST'];
$path = substr(dirname($_SERVER['PHP_SELF']), 1);
-?>
+ echo <<<STR
<dl class="system_notice">
<dt>Page notice</dt>
<dd>
<ul>
-<?php
- $fail = false;
+STR;
+ $fail = false;
if (empty($host)) {
updateStatus("No hostname specified.", true);
- $fail = true;
+ $fail = true;
}
if (empty($database)) {
updateStatus("No database specified.", true);
- $fail = true;
+ $fail = true;
}
if (empty($username)) {
updateStatus("No username specified.", true);
- $fail = true;
+ $fail = true;
}
-// if (empty($password)) {
-// updateStatus("No password specified.", true);
-// $fail = true;
-// }
-
if (empty($sitename)) {
updateStatus("No sitename specified.", true);
- $fail = true;
+ $fail = true;
}
- if($fail){
- showForm();
+ if ($fail) {
+ showForm();
return;
}
- // FIXME: use PEAR::DB or PDO instead of our own switch
-
- switch($dbtype) {
- case 'mysql':
- $db = mysql_db_installer($host, $database, $username, $password);
- break;
- case 'pgsql':
- $db = pgsql_db_installer($host, $database, $username, $password);
- break;
- default:
- }
+ global $dbModules;
+ $db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
if (!$db) {
// database connection failed, do not move on to create config file.
@@ -498,112 +566,110 @@ function handlePost()
updateStatus("StatusNet has been installed at $link");
updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
-?>
-
-<?php
}
-function pgsql_db_installer($host, $database, $username, $password) {
- $connstring = "dbname=$database host=$host user=$username";
-
- //No password would mean trust authentication used.
- if (!empty($password)) {
- $connstring .= " password=$password";
- }
- updateStatus("Starting installation...");
- updateStatus("Checking database...");
- $conn = pg_connect($connstring);
-
- if ($conn ===false) {
- updateStatus("Failed to connect to database: $connstring");
- showForm();
- return false;
- }
-
- //ensure database encoding is UTF8
- $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
- if ($record->server_encoding != 'UTF8') {
- updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
- showForm();
- return false;
- }
-
- updateStatus("Running database script...");
- //wrap in transaction;
- pg_query($conn, 'BEGIN');
- $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
-
- if ($res === false) {
- updateStatus("Can't run database script.", true);
- showForm();
- return false;
- }
- foreach (array('sms_carrier' => 'SMS carrier',
+function Pgsql_Db_installer($host, $database, $username, $password)
+{
+ $connstring = "dbname=$database host=$host user=$username";
+
+ //No password would mean trust authentication used.
+ if (!empty($password)) {
+ $connstring .= " password=$password";
+ }
+ updateStatus("Starting installation...");
+ updateStatus("Checking database...");
+ $conn = pg_connect($connstring);
+
+ if ($conn ===false) {
+ updateStatus("Failed to connect to database: $connstring");
+ showForm();
+ return false;
+ }
+
+ //ensure database encoding is UTF8
+ $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
+ if ($record->server_encoding != 'UTF8') {
+ updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
+ showForm();
+ return false;
+ }
+
+ updateStatus("Running database script...");
+ //wrap in transaction;
+ pg_query($conn, 'BEGIN');
+ $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
+
+ if ($res === false) {
+ updateStatus("Can't run database script.", true);
+ showForm();
+ return false;
+ }
+ foreach (array('sms_carrier' => 'SMS carrier',
'notice_source' => 'notice source',
'foreign_services' => 'foreign service')
as $scr => $name) {
- updateStatus(sprintf("Adding %s data to database...", $name));
- $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
- if ($res === false) {
- updateStatus(sprintf("Can't run %d script.", $name), true);
- showForm();
- return false;
- }
- }
- pg_query($conn, 'COMMIT');
-
- if (empty($password)) {
- $sqlUrl = "pgsql://$username@$host/$database";
- }
- else {
- $sqlUrl = "pgsql://$username:$password@$host/$database";
- }
-
- $db = array('type' => 'pgsql', 'database' => $sqlUrl);
-
- return $db;
+ updateStatus(sprintf("Adding %s data to database...", $name));
+ $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
+ if ($res === false) {
+ updateStatus(sprintf("Can't run %d script.", $name), true);
+ showForm();
+ return false;
+ }
+ }
+ pg_query($conn, 'COMMIT');
+
+ if (empty($password)) {
+ $sqlUrl = "pgsql://$username@$host/$database";
+ } else {
+ $sqlUrl = "pgsql://$username:$password@$host/$database";
+ }
+
+ $db = array('type' => 'pgsql', 'database' => $sqlUrl);
+
+ return $db;
}
-function mysql_db_installer($host, $database, $username, $password) {
- updateStatus("Starting installation...");
- updateStatus("Checking database...");
-
- $conn = mysql_connect($host, $username, $password);
- if (!$conn) {
- updateStatus("Can't connect to server '$host' as '$username'.", true);
- showForm();
- return false;
- }
- updateStatus("Changing to database...");
- $res = mysql_select_db($database, $conn);
- if (!$res) {
- updateStatus("Can't change to database.", true);
- showForm();
- return false;
- }
- updateStatus("Running database script...");
- $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
- if ($res === false) {
- updateStatus("Can't run database script.", true);
- showForm();
- return false;
- }
- foreach (array('sms_carrier' => 'SMS carrier',
+function Mysql_Db_installer($host, $database, $username, $password)
+{
+ updateStatus("Starting installation...");
+ updateStatus("Checking database...");
+
+ $conn = mysql_connect($host, $username, $password);
+ if (!$conn) {
+ updateStatus("Can't connect to server '$host' as '$username'.", true);
+ showForm();
+ return false;
+ }
+ updateStatus("Changing to database...");
+ $res = mysql_select_db($database, $conn);
+ if (!$res) {
+ updateStatus("Can't change to database.", true);
+ showForm();
+ return false;
+ }
+ updateStatus("Running database script...");
+ $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
+ if ($res === false) {
+ updateStatus("Can't run database script.", true);
+ showForm();
+ return false;
+ }
+ foreach (array('sms_carrier' => 'SMS carrier',
'notice_source' => 'notice source',
'foreign_services' => 'foreign service')
as $scr => $name) {
- updateStatus(sprintf("Adding %s data to database...", $name));
- $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
- if ($res === false) {
- updateStatus(sprintf("Can't run %d script.", $name), true);
- showForm();
- return false;
- }
- }
-
- $sqlUrl = "mysqli://$username:$password@$host/$database";
- $db = array('type' => 'mysql', 'database' => $sqlUrl);
- return $db;
+ updateStatus(sprintf("Adding %s data to database...", $name));
+ $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
+ if ($res === false) {
+ updateStatus(sprintf("Can't run %d script.", $name), true);
+ showForm();
+ return false;
+ }
+ }
+
+ $sqlUrl = "mysqli://$username:$password@$host/$database";
+ $db = array('type' => 'mysql', 'database' => $sqlUrl);
+ return $db;
}
function writeConf($sitename, $server, $path, $fancy, $db)
@@ -634,7 +700,16 @@ function writeConf($sitename, $server, $path, $fancy, $db)
return $res;
}
-function runDbScript($filename, $conn, $type = 'mysql')
+/**
+ * Install schema into the database
+ *
+ * @param string $filename location of database schema file
+ * @param dbconn $conn connection to database
+ * @param string $type type of database, currently mysql or pgsql
+ *
+ * @return boolean - indicating success or failure
+ */
+function runDbScript($filename, $conn, $type = 'mysqli')
{
$sql = trim(file_get_contents($filename));
$stmts = explode(';', $sql);
@@ -645,7 +720,7 @@ function runDbScript($filename, $conn, $type = 'mysql')
}
// FIXME: use PEAR::DB or PDO instead of our own switch
switch ($type) {
- case 'mysql':
+ case 'mysqli':
$res = mysql_query($stmt, $conn);
if ($res === false) {
$error = mysql_error();
@@ -670,7 +745,9 @@ function runDbScript($filename, $conn, $type = 'mysql')
?>
<?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
-<!DOCTYPE html>
+<!DOCTYPE html
+PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
<head>
<title>Install StatusNet</title>
diff --git a/js/util.js b/js/util.js
index 2165957c3..0a943512f 100644
--- a/js/util.js
+++ b/js/util.js
@@ -21,7 +21,9 @@ $(document).ready(function(){
// count character on keyup
function counter(event){
- var maxLength = 140;
+ if (maxLength <= 0) {
+ return;
+ }
var currentLength = $("#notice_data-text").val().length;
var remaining = maxLength - currentLength;
var counter = $("#notice_text-count");
@@ -67,12 +69,20 @@ $(document).ready(function(){
return true;
}
+ // define maxLength if it wasn't defined already
+
+ if (typeof(maxLength) == "undefined") {
+ maxLength = 140;
+ }
+
if ($("#notice_data-text").length) {
- $("#notice_data-text").bind("keyup", counter);
- $("#notice_data-text").bind("keydown", submitonreturn);
+ if (maxLength > 0) {
+ $("#notice_data-text").bind("keyup", counter);
+ // run once in case there's something in there
+ counter();
+ }
- // run once in case there's something in there
- counter();
+ $("#notice_data-text").bind("keydown", submitonreturn);
if($('body')[0].id != 'conversation') {
$("#notice_data-text").focus();
@@ -218,7 +228,9 @@ $(document).ready(function(){
}
else {
$("#notice_data-text").val("");
- counter();
+ if (maxLength > 0) {
+ counter();
+ }
}
}
}
@@ -258,7 +270,9 @@ $(document).ready(function(){
$("#notice_data-attach").val("");
$("#notice_in-reply-to").val("");
$('#notice_data-attach_selected').remove();
- counter();
+ if (maxLength > 0) {
+ counter();
+ }
}
$("#form_notice").removeClass("processing");
$("#notice_action-submit").removeAttr("disabled");
diff --git a/lib/Shorturl_api.php b/lib/Shorturl_api.php
index 6402dbc09..18ae7719b 100644
--- a/lib/Shorturl_api.php
+++ b/lib/Shorturl_api.php
@@ -19,7 +19,7 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-class ShortUrlApi
+abstract class ShortUrlApi
{
protected $service_url;
protected $long_limit = 27;
@@ -35,11 +35,9 @@ class ShortUrlApi
return $url;
}
- protected function shorten_imp($url) {
- return "To Override";
- }
+ protected abstract function shorten_imp($url);
- private function is_long($url) {
+ protected function is_long($url) {
return strlen($url) >= common_config('site', 'shorturllength');
}
@@ -71,61 +69,3 @@ class ShortUrlApi
}
}
-class LilUrl extends ShortUrlApi
-{
- function __construct()
- {
- parent::__construct('http://ur1.ca/');
- }
-
- protected function shorten_imp($url) {
- $data['longurl'] = $url;
- $response = $this->http_post($data);
- if (!$response) return $url;
- $y = @simplexml_load_string($response);
- if (!isset($y->body)) return $url;
- $x = $y->body->p[0]->a->attributes();
- if (isset($x['href'])) return $x['href'];
- return $url;
- }
-}
-
-
-class PtitUrl extends ShortUrlApi
-{
- function __construct()
- {
- parent::__construct('http://ptiturl.com/?creer=oui&action=Reduire&url=');
- }
-
- protected function shorten_imp($url) {
- $response = $this->http_get($url);
- if (!$response) return $url;
- $response = $this->tidy($response);
- $y = @simplexml_load_string($response);
- if (!isset($y->body)) return $url;
- $xml = $y->body->center->table->tr->td->pre->a->attributes();
- if (isset($xml['href'])) return $xml['href'];
- return $url;
- }
-}
-
-class TightUrl extends ShortUrlApi
-{
- function __construct()
- {
- parent::__construct('http://2tu.us/?save=y&url=');
- }
-
- protected function shorten_imp($url) {
- $response = $this->http_get($url);
- if (!$response) return $url;
- $response = $this->tidy($response);
- $y = @simplexml_load_string($response);
- if (!isset($y->body)) return $url;
- $xml = $y->body->p[0]->code[0]->a->attributes();
- if (isset($xml['href'])) return $xml['href'];
- return $url;
- }
-}
-
diff --git a/lib/accountsettingsaction.php b/lib/accountsettingsaction.php
index 798116163..a004a3ed9 100644
--- a/lib/accountsettingsaction.php
+++ b/lib/accountsettingsaction.php
@@ -98,42 +98,39 @@ class AccountSettingsNav extends Widget
function show()
{
- # action => array('prompt', 'title')
- $menu =
- array('profilesettings' =>
- array(_('Profile'),
- _('Change your profile settings')),
- 'avatarsettings' =>
- array(_('Avatar'),
- _('Upload an avatar')),
- 'passwordsettings' =>
- array(_('Password'),
- _('Change your password')),
- 'emailsettings' =>
- array(_('Email'),
- _('Change email handling')),
- 'openidsettings' =>
- array(_('OpenID'),
- _('Add or remove OpenIDs')),
- 'userdesignsettings' =>
- array(_('Design'),
- _('Design your profile')),
- 'othersettings' =>
- array(_('Other'),
- _('Other options')));
-
$action_name = $this->action->trimmed('action');
$this->action->elementStart('ul', array('class' => 'nav'));
- foreach ($menu as $menuaction => $menudesc) {
- if ($menuaction == 'openidsettings' &&
- !common_config('openid', 'enabled')) {
- continue;
+ if (Event::handle('StartAccountSettingsNav', array(&$this->action))) {
+
+ $menu =
+ array('profilesettings' =>
+ array(_('Profile'),
+ _('Change your profile settings')),
+ 'avatarsettings' =>
+ array(_('Avatar'),
+ _('Upload an avatar')),
+ 'passwordsettings' =>
+ array(_('Password'),
+ _('Change your password')),
+ 'emailsettings' =>
+ array(_('Email'),
+ _('Change email handling')),
+ 'userdesignsettings' =>
+ array(_('Design'),
+ _('Design your profile')),
+ 'othersettings' =>
+ array(_('Other'),
+ _('Other options')));
+
+ foreach ($menu as $menuaction => $menudesc) {
+ $this->action->menuItem(common_local_url($menuaction),
+ $menudesc[0],
+ $menudesc[1],
+ $action_name === $menuaction);
}
- $this->action->menuItem(common_local_url($menuaction),
- $menudesc[0],
- $menudesc[1],
- $action_name === $menuaction);
+
+ Event::handle('EndAccountSettingsNav', array(&$this->action));
}
$this->action->elementEnd('ul');
diff --git a/lib/action.php b/lib/action.php
index fafb2c6fc..02793f069 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -442,17 +442,12 @@ class Action extends HTMLOutputter // lawsuit
_('Logout'), _('Logout from the site'), false, 'nav_logout');
}
else {
- if (!common_config('site', 'openidonly')) {
- if (!common_config('site', 'closed')) {
- $this->menuItem(common_local_url('register'),
- _('Register'), _('Create an account'), false, 'nav_register');
- }
- $this->menuItem(common_local_url('login'),
- _('Login'), _('Login to the site'), false, 'nav_login');
- } else {
- $this->menuItem(common_local_url('openidlogin'),
- _('OpenID'), _('Login with OpenID'), false, 'nav_openid');
+ if (!common_config('site', 'closed')) {
+ $this->menuItem(common_local_url('register'),
+ _('Register'), _('Create an account'), false, 'nav_register');
}
+ $this->menuItem(common_local_url('login'),
+ _('Login'), _('Login to the site'), false, 'nav_login');
}
$this->menuItem(common_local_url('doc', array('title' => 'help')),
_('Help'), _('Help me!'), false, 'nav_help');
@@ -881,6 +876,7 @@ class Action extends HTMLOutputter // lawsuit
*/
function handle($argarray=null)
{
+ header('Vary: Accept-Encoding,Cookie');
$lm = $this->lastModified();
$etag = $this->etag();
if ($etag) {
diff --git a/lib/command.php b/lib/command.php
index 91a20b810..11d40b8e1 100644
--- a/lib/command.php
+++ b/lib/command.php
@@ -114,7 +114,6 @@ class StatsCommand extends Command
class FavCommand extends Command
{
-
var $other = null;
function __construct($user, $other)
@@ -158,6 +157,108 @@ class FavCommand extends Command
$channel->output($this->user, _('Notice marked as fave.'));
}
+
+}
+class JoinCommand extends Command
+{
+ var $other = null;
+
+ function __construct($user, $other)
+ {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+
+ function execute($channel)
+ {
+
+ $nickname = common_canonical_nickname($this->other);
+ $group = User_group::staticGet('nickname', $nickname);
+ $cur = $this->user;
+
+ if (!$group) {
+ $channel->error($cur, _('No such group.'));
+ return;
+ }
+
+ if ($cur->isMember($group)) {
+ $channel->error($cur, _('You are already a member of that group'));
+ return;
+ }
+ if (Group_block::isBlocked($group, $cur->getProfile())) {
+ $channel->error($cur, _('You have been blocked from that group by the admin.'));
+ return;
+ }
+
+ $member = new Group_member();
+
+ $member->group_id = $group->id;
+ $member->profile_id = $cur->id;
+ $member->created = common_sql_now();
+
+ $result = $member->insert();
+ if (!$result) {
+ common_log_db_error($member, 'INSERT', __FILE__);
+ $channel->error($cur, sprintf(_('Could not join user %s to group %s'),
+ $cur->nickname, $group->nickname));
+ return;
+ }
+
+ $channel->output($cur, sprintf(_('%s joined group %s'),
+ $cur->nickname,
+ $group->nickname));
+ }
+
+}
+class DropCommand extends Command
+{
+ var $other = null;
+
+ function __construct($user, $other)
+ {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+
+ function execute($channel)
+ {
+
+ $nickname = common_canonical_nickname($this->other);
+ $group = User_group::staticGet('nickname', $nickname);
+ $cur = $this->user;
+
+ if (!$group) {
+ $channel->error($cur, _('No such group.'));
+ return;
+ }
+
+ if (!$cur->isMember($group)) {
+ $channel->error($cur, _('You are not a member of that group.'));
+ return;
+ }
+
+ $member = new Group_member();
+
+ $member->group_id = $group->id;
+ $member->profile_id = $cur->id;
+
+ if (!$member->find(true)) {
+ $channel->error($cur,_('Could not find membership record.'));
+ return;
+ }
+ $result = $member->delete();
+ if (!$result) {
+ common_log_db_error($member, 'INSERT', __FILE__);
+ $channel->error($cur, sprintf(_('Could not remove user %s to group %s'),
+ $cur->nickname, $group->nickname));
+ return;
+ }
+
+ $channel->output($cur, sprintf(_('%s left group %s'),
+ $cur->nickname,
+ $group->nickname));
+ }
+
}
class WhoisCommand extends Command
@@ -211,16 +312,20 @@ class MessageCommand extends Command
function execute($channel)
{
$other = User::staticGet('nickname', common_canonical_nickname($this->other));
+
$len = mb_strlen($this->text);
+
if ($len == 0) {
$channel->error($this->user, _('No content!'));
return;
- } else if ($len > 140) {
- $content = common_shorten_links($content);
- if (mb_strlen($content) > 140) {
- $channel->error($this->user, sprintf(_('Message too long - maximum is 140 characters, you sent %d'), $len));
- return;
- }
+ }
+
+ $this->text = common_shorten_links($this->text);
+
+ if (Message::contentTooLong($this->text)) {
+ $channel->error($this->user, sprintf(_('Message too long - maximum is %d characters, you sent %d'),
+ Message::maxContent(), mb_strlen($this->text)));
+ return;
}
if (!$other) {
@@ -392,6 +497,8 @@ class HelpCommand extends Command
"get <nickname> - get last notice from user\n".
"whois <nickname> - get profile info on user\n".
"fav <nickname> - add user's last notice as a 'fave'\n".
+ "join <group> - join group\n".
+ "drop <group> - leave group\n".
"stats - get your stats\n".
"stop - same as 'off'\n".
"quit - same as 'off'\n".
diff --git a/lib/commandinterpreter.php b/lib/commandinterpreter.php
index ac6bf73c8..60fc4c3c4 100644
--- a/lib/commandinterpreter.php
+++ b/lib/commandinterpreter.php
@@ -28,7 +28,7 @@ class CommandInterpreter
# XXX: localise
$text = preg_replace('/\s+/', ' ', trim($text));
- list($cmd, $arg) = explode(' ', $text, 2);
+ list($cmd, $arg) = $this->split_arg($text);
# We try to support all the same commands as Twitter, see
# http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands
@@ -43,7 +43,7 @@ class CommandInterpreter
return new HelpCommand($user);
case 'on':
if ($arg) {
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -54,7 +54,7 @@ class CommandInterpreter
}
case 'off':
if ($arg) {
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -70,12 +70,32 @@ class CommandInterpreter
} else {
return new OffCommand($user);
}
+ case 'join':
+ if (!$arg) {
+ return null;
+ }
+ list($other, $extra) = $this->split_arg($arg);
+ if ($extra) {
+ return null;
+ } else {
+ return new JoinCommand($user, $other);
+ }
+ case 'drop':
+ if (!$arg) {
+ return null;
+ }
+ list($other, $extra) = $this->split_arg($arg);
+ if ($extra) {
+ return null;
+ } else {
+ return new DropCommand($user, $other);
+ }
case 'follow':
case 'sub':
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -86,7 +106,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -97,7 +117,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -108,7 +128,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if (!$extra) {
return null;
} else {
@@ -118,7 +138,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -128,7 +148,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -138,7 +158,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -153,7 +173,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -163,7 +183,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($word, $extra) = explode(' ', $arg, 2);
+ list($word, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else if ($word == 'off') {
@@ -175,7 +195,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($word, $extra) = explode(' ', $arg, 2);
+ list($word, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else if ($word == 'all') {
@@ -193,5 +213,17 @@ class CommandInterpreter
return false;
}
}
+
+ /**
+ * Split arguments without triggering a PHP notice warning
+ */
+ function split_arg($text)
+ {
+ $pieces = explode(' ', $text, 2);
+ if (count($pieces) == 1) {
+ $pieces[] = null;
+ }
+ return $pieces;
+ }
}
diff --git a/lib/common.php b/lib/common.php
index 39d4ffc9b..194eb568f 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -19,10 +19,10 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-define('STATUSNET_VERSION', '0.8.1');
+define('STATUSNET_VERSION', '0.9.0dev');
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
-define('STATUSNET_CODENAME', 'Second Guessing');
+define('STATUSNET_CODENAME', 'Stand');
define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48);
@@ -115,12 +115,13 @@ $config =
'broughtbyurl' => null,
'closed' => false,
'inviteonly' => false,
- 'openidonly' => false,
'private' => false,
'ssl' => 'never',
'sslserver' => null,
'shorturllength' => 30,
- 'dupelimit' => 60), # default for same person saying the same thing
+ 'dupelimit' => 60, # default for same person saying the same thing
+ 'textlimit' => 140,
+ ),
'syslog' =>
array('appname' => 'statusnet', # for syslog
'priority' => 'debug', # XXX: currently ignored
@@ -144,7 +145,8 @@ $config =
array('blacklist' => array(),
'featured' => array()),
'profile' =>
- array('banned' => array()),
+ array('banned' => array(),
+ 'biolimit' => null),
'avatar' =>
array('server' => null,
'dir' => INSTALLDIR . '/avatar/',
@@ -176,8 +178,6 @@ $config =
'host' => null, # only set if != server
'debug' => false, # print extra debug info
'public' => array()), # JIDs of users who want to receive the public stream
- 'openid' =>
- array('enabled' => true),
'invite' =>
array('enabled' => true),
'sphinx' =>
@@ -264,7 +264,8 @@ $config =
'filecommand' => '/usr/bin/file',
),
'group' =>
- array('maxaliases' => 3),
+ array('maxaliases' => 3,
+ 'desclimit' => null),
'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
'search' =>
array('type' => 'fulltext'),
@@ -279,6 +280,12 @@ $config =
'linkcolor' => null,
'backgroundimage' => null,
'disposition' => null),
+ 'notice' =>
+ array('contentlimit' => null),
+ 'message' =>
+ array('contentlimit' => null),
+ 'http' =>
+ array('client' => 'curl'), // XXX: should this be the default?
);
$config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
@@ -383,13 +390,25 @@ if ($_db_name != 'statusnet' && !array_key_exists('ini_'.$_db_name, $config['db'
$config['db']['ini_'.$_db_name] = INSTALLDIR.'/classes/statusnet.ini';
}
-// Ignore openidonly if OpenID is disabled
-
-if (!$config['openid']['enabled']) {
- $config['site']['openidonly'] = false;
+function __autoload($cls)
+{
+ if (file_exists(INSTALLDIR.'/classes/' . $cls . '.php')) {
+ require_once(INSTALLDIR.'/classes/' . $cls . '.php');
+ } else if (file_exists(INSTALLDIR.'/lib/' . strtolower($cls) . '.php')) {
+ require_once(INSTALLDIR.'/lib/' . strtolower($cls) . '.php');
+ } else if (mb_substr($cls, -6) == 'Action' &&
+ file_exists(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php')) {
+ require_once(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
+ } else if ($cls == 'OAuthRequest') {
+ require_once('OAuth.php');
+ } else {
+ Event::handle('Autoload', array(&$cls));
+ }
}
// XXX: how many of these could be auto-loaded on use?
+// XXX: note that these files should not use config options
+// at compile time since DB config options are not yet loaded.
require_once 'Validate.php';
require_once 'markdown.php';
@@ -405,24 +424,14 @@ require_once INSTALLDIR.'/lib/twitter.php';
require_once INSTALLDIR.'/lib/clientexception.php';
require_once INSTALLDIR.'/lib/serverexception.php';
+// Load settings from database; note we need autoload for this
+
+Config::loadSettings();
+
// XXX: other formats here
define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER);
-function __autoload($class)
-{
- if ($class == 'OAuthRequest') {
- require_once('OAuth.php');
- } else if (file_exists(INSTALLDIR.'/classes/' . $class . '.php')) {
- require_once(INSTALLDIR.'/classes/' . $class . '.php');
- } else if (file_exists(INSTALLDIR.'/lib/' . strtolower($class) . '.php')) {
- require_once(INSTALLDIR.'/lib/' . strtolower($class) . '.php');
- } else if (mb_substr($class, -6) == 'Action' &&
- file_exists(INSTALLDIR.'/actions/' . strtolower(mb_substr($class, 0, -6)) . '.php')) {
- require_once(INSTALLDIR.'/actions/' . strtolower(mb_substr($class, 0, -6)) . '.php');
- }
-}
-
// Give plugins a chance to initialize in a fully-prepared environment
Event::handle('InitializePlugin');
diff --git a/lib/curlclient.php b/lib/curlclient.php
new file mode 100644
index 000000000..36fc7d157
--- /dev/null
+++ b/lib/curlclient.php
@@ -0,0 +1,179 @@
+n<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Utility class for wrapping Curl
+ *
+ * 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 HTTP
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+define(CURLCLIENT_VERSION, "0.1");
+
+/**
+ * Wrapper for Curl
+ *
+ * Makes Curl HTTP client calls within our HTTPClient framework
+ *
+ * @category HTTP
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class CurlClient extends HTTPClient
+{
+ function __construct()
+ {
+ }
+
+ function head($url, $headers=null)
+ {
+ $ch = curl_init($url);
+
+ $this->setup($ch);
+
+ curl_setopt_array($ch,
+ array(CURLOPT_NOBODY => true));
+
+ if (!is_null($headers)) {
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ }
+
+ $result = curl_exec($ch);
+
+ curl_close($ch);
+
+ return $this->parseResults($result);
+ }
+
+ function get($url, $headers=null)
+ {
+ $ch = curl_init($url);
+
+ $this->setup($ch);
+
+ if (!is_null($headers)) {
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ }
+
+ $result = curl_exec($ch);
+
+ curl_close($ch);
+
+ return $this->parseResults($result);
+ }
+
+ function post($url, $headers=null, $body=null)
+ {
+ $ch = curl_init($url);
+
+ $this->setup($ch);
+
+ curl_setopt($ch, CURLOPT_POST, true);
+
+ if (!is_null($body)) {
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ }
+
+ if (!is_null($headers)) {
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ }
+
+ $result = curl_exec($ch);
+
+ curl_close($ch);
+
+ return $this->parseResults($result);
+ }
+
+ function setup($ch)
+ {
+ curl_setopt_array($ch,
+ array(CURLOPT_USERAGENT => $this->userAgent(),
+ CURLOPT_HEADER => true,
+ CURLOPT_RETURNTRANSFER => true));
+ }
+
+ function userAgent()
+ {
+ $version = curl_version();
+ return parent::userAgent() . " CurlClient/".CURLCLIENT_VERSION . " cURL/" . $version['version'];
+ }
+
+ function parseResults($results)
+ {
+ $resp = new HTTPResponse();
+
+ $lines = explode("\r\n", $results);
+
+ if (preg_match("#^HTTP/1.[01] (\d\d\d) .+$#", $lines[0], $match)) {
+ $resp->code = $match[1];
+ } else {
+ throw Exception("Bad format: initial line is not HTTP status line");
+ }
+
+ $lastk = null;
+
+ for ($i = 1; $i < count($lines); $i++) {
+ $l =& $lines[$i];
+ if (mb_strlen($l) == 0) {
+ $resp->body = implode("\r\n", array_slice($lines, $i + 1));
+ break;
+ }
+ if (preg_match("#^(\S+):\s+(.*)$#", $l, $match)) {
+ $k = $match[1];
+ $v = $match[2];
+
+ if (array_key_exists($k, $resp->headers)) {
+ if (is_array($resp->headers[$k])) {
+ $resp->headers[$k][] = $v;
+ } else {
+ $resp->headers[$k] = array($resp->headers[$k], $v);
+ }
+ } else {
+ $resp->headers[$k] = $v;
+ }
+ $lastk = $k;
+ } else if (preg_match("#^\s+(.*)$#", $l, $match)) {
+ // continuation line
+ if (is_null($lastk)) {
+ throw Exception("Bad format: initial whitespace in headers");
+ }
+ $h =& $resp->headers[$lastk];
+ if (is_array($h)) {
+ $n = count($h);
+ $h[$n-1] .= $match[1];
+ } else {
+ $h .= $match[1];
+ }
+ }
+ }
+
+ return $resp;
+ }
+}
diff --git a/lib/designsettings.php b/lib/designsettings.php
index fe4222597..820d534f2 100644
--- a/lib/designsettings.php
+++ b/lib/designsettings.php
@@ -325,8 +325,9 @@ class DesignSettingsAction extends AccountSettingsAction
parent::showScripts();
$this->script('js/farbtastic/farbtastic.js');
- $this->script('js/farbtastic/farbtastic.go.js');
$this->script('js/userdesign.go.js');
+
+ $this->autofocus('design_background-image_file');
}
/**
diff --git a/lib/facebookaction.php b/lib/facebookaction.php
index 5cbb9be53..411f79594 100644
--- a/lib/facebookaction.php
+++ b/lib/facebookaction.php
@@ -35,7 +35,6 @@ if (!defined('STATUSNET') && !defined('LACONICA'))
require_once INSTALLDIR.'/lib/facebookutil.php';
require_once INSTALLDIR.'/lib/noticeform.php';
-
class FacebookAction extends Action
{
@@ -181,7 +180,6 @@ class FacebookAction extends Action
}
-
// Make this into a widget later
function showLocalNav()
{
@@ -241,7 +239,6 @@ class FacebookAction extends Action
$this->endHTML();
}
-
function showInstructions()
{
@@ -257,13 +254,8 @@ class FacebookAction extends Action
$this->elementStart('dd');
$this->elementStart('p');
$this->text(sprintf($loginmsg_part1, common_config('site', 'name')));
- if (!common_config('site', 'openidonly')) {
- $this->element('a',
- array('href' => common_local_url('register')), _('Register'));
- } else {
- $this->element('a',
- array('href' => common_local_url('openidlogin')), _('Register'));
- }
+ $this->element('a',
+ array('href' => common_local_url('register')), _('Register'));
$this->text($loginmsg_part2);
$this->elementEnd('p');
$this->elementEnd('dd');
@@ -272,7 +264,6 @@ class FacebookAction extends Action
$this->elementEnd('div');
}
-
function showLoginForm($msg = null)
{
@@ -317,7 +308,6 @@ class FacebookAction extends Action
}
-
function updateProfileBox($notice)
{
@@ -399,7 +389,6 @@ class FacebookAction extends Action
$this->xw->openURI('php://output');
}
-
/**
* Generate pagination links
*
@@ -458,8 +447,9 @@ class FacebookAction extends Action
} else {
$content_shortened = common_shorten_links($content);
- if (mb_strlen($content_shortened) > 140) {
- $this->showPage(_('That\'s too long. Max notice size is 140 chars.'));
+ if (Notice::contentTooLong($content_shortened)) {
+ $this->showPage(sprintf(_('That\'s too long. Max notice size is %d chars.'),
+ Notice::maxContent()));
return;
}
}
diff --git a/lib/facebookutil.php b/lib/facebookutil.php
index ad61b6f0a..f5121609d 100644
--- a/lib/facebookutil.php
+++ b/lib/facebookutil.php
@@ -109,7 +109,6 @@ function facebookBroadcastNotice($notice)
$can_update = $facebook->api_client->users_hasAppPermission('status_update',
$fbuid);
-
if (!empty($attachments) && $can_publish == 1) {
$fbattachment = format_attachments($attachments);
$facebook->api_client->stream_publish($status, $fbattachment,
@@ -180,7 +179,11 @@ function format_attachments($attachments)
foreach($attachments as $attachment)
{
- $fbmedia = get_fbmedia_for_attachment($attachment);
+ if($enclosure = $attachment->getEnclosure()){
+ $fbmedia = get_fbmedia_for_attachment($enclosure);
+ }else{
+ $fbmedia = get_fbmedia_for_attachment($attachment);
+ }
if($fbmedia){
$fbattachment['media'][]=$fbmedia;
}else{
diff --git a/lib/galleryaction.php b/lib/galleryaction.php
index 18cc7b929..31e36803a 100644
--- a/lib/galleryaction.php
+++ b/lib/galleryaction.php
@@ -132,13 +132,16 @@ class GalleryAction extends OwnerDesignAction
$this->elementEnd('li');
$this->elementStart('li', array('id'=>'filter_tags_item'));
$this->elementStart('form', array('name' => 'bytag',
- 'id' => 'bytag',
+ 'id' => 'form_filter_bytag',
'action' => common_path('?action=' . $this->trimmed('action')),
'method' => 'post'));
+ $this->elementStart('fieldset');
+ $this->element('legend', null, _('Select tag to filter'));
$this->dropdown('tag', _('Tag'), $content,
_('Choose a tag to narrow list'), false, $tag);
$this->hidden('nickname', $this->user->nickname);
$this->submit('submit', _('Go'));
+ $this->elementEnd('fieldset');
$this->elementEnd('form');
$this->elementEnd('li');
$this->elementEnd('ul');
diff --git a/lib/groupeditform.php b/lib/groupeditform.php
index a649c2ee3..433f6a138 100644
--- a/lib/groupeditform.php
+++ b/lib/groupeditform.php
@@ -150,27 +150,33 @@ class GroupEditForm extends Form
$this->out->elementStart('li');
$this->out->hidden('groupid', $id);
$this->out->input('nickname', _('Nickname'),
- ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
- _('1-64 lowercase letters or numbers, no punctuation or spaces'));
+ ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
+ _('1-64 lowercase letters or numbers, no punctuation or spaces'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('fullname', _('Full name'),
- ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
+ ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('homepage', _('Homepage'),
- ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
- _('URL of the homepage or blog of the group or topic'));
+ ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
+ _('URL of the homepage or blog of the group or topic'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
+ $desclimit = User_group::maxDescription();
+ if ($desclimit == 0) {
+ $descinstr = _('Describe the group or topic');
+ } else {
+ $descinstr = sprintf(_('Describe the group or topic in %d characters'), $desclimit);
+ }
$this->out->textarea('description', _('Description'),
- ($this->out->arg('description')) ? $this->out->arg('description') : $description,
- _('Describe the group or topic in 140 chars'));
+ ($this->out->arg('description')) ? $this->out->arg('description') : $description,
+ $descinstr);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('location', _('Location'),
- ($this->out->arg('location')) ? $this->out->arg('location') : $location,
- _('Location for the group, if any, like "City, State (or Region), Country"'));
+ ($this->out->arg('location')) ? $this->out->arg('location') : $location,
+ _('Location for the group, if any, like "City, State (or Region), Country"'));
$this->out->elementEnd('li');
if (common_config('group', 'maxaliases') > 0) {
$aliases = (empty($this->group)) ? array() : $this->group->getAliases();
diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php
index 8ad7dc20f..aa01f6b1d 100644
--- a/lib/htmloutputter.php
+++ b/lib/htmloutputter.php
@@ -412,4 +412,29 @@ class HTMLOutputter extends XMLOutputter
$this->element('p', 'form_guide', $instructions);
}
}
+
+
+ /**
+ * Internal script to autofocus the given element on page onload.
+ *
+ * @param string $id element ID, must refer to an existing element
+ *
+ * @return void
+ *
+ */
+ function autofocus($id)
+ {
+ $this->elementStart('script', array('type' => 'text/javascript'));
+ $this->raw('
+ <!--
+ $(document).ready(function() {
+ var el = $("#' . $id . '");
+ if (el.length) {
+ el.focus();
+ }
+ });
+ -->
+ ');
+ $this->elementEnd('script');
+ }
}
diff --git a/lib/httpclient.php b/lib/httpclient.php
new file mode 100644
index 000000000..c8c8ae5b2
--- /dev/null
+++ b/lib/httpclient.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Utility for doing HTTP-related things
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * Useful structure for HTTP responses
+ *
+ * We make HTTP calls in several places, and we have several different
+ * ways of doing them. This class hides the specifics of what underlying
+ * library (curl or PHP-HTTP or whatever) that's used.
+ *
+ * @category HTTP
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class HTTPResponse
+{
+ public $code = null;
+ public $headers = null;
+ public $body = null;
+}
+
+/**
+ * Utility class for doing HTTP client stuff
+ *
+ * We make HTTP calls in several places, and we have several different
+ * ways of doing them. This class hides the specifics of what underlying
+ * library (curl or PHP-HTTP or whatever) that's used.
+ *
+ * @category HTTP
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class HTTPClient
+{
+ static $_client = null;
+
+ static function start()
+ {
+ if (!is_null(self::$_client)) {
+ return self::$_client;
+ }
+
+ $type = common_config('http', 'client');
+
+ switch ($type) {
+ case 'curl':
+ self::$_client = new CurlClient();
+ break;
+ default:
+ throw new Exception("Unknown HTTP client type '$type'");
+ break;
+ }
+
+ return self::$_client;
+ }
+
+ function head($url, $headers)
+ {
+ throw new Exception("HEAD method unimplemented");
+ }
+
+ function get($url, $headers)
+ {
+ throw new Exception("GET method unimplemented");
+ }
+
+ function post($url, $headers, $body)
+ {
+ throw new Exception("POST method unimplemented");
+ }
+
+ function put($url, $headers, $body)
+ {
+ throw new Exception("PUT method unimplemented");
+ }
+
+ function delete($url, $headers)
+ {
+ throw new Exception("DELETE method unimplemented");
+ }
+
+ function userAgent()
+ {
+ return "StatusNet/".STATUSNET_VERSION." (".STATUSNET_CODENAME.")";
+ }
+}
diff --git a/lib/logingroupnav.php b/lib/logingroupnav.php
index f740e329a..b545fbf26 100644
--- a/lib/logingroupnav.php
+++ b/lib/logingroupnav.php
@@ -69,30 +69,25 @@ class LoginGroupNav extends Widget
function show()
{
- // action => array('prompt', 'title')
- $menu = array();
+ $action_name = $this->action->trimmed('action');
+
+ $this->action->elementStart('ul', array('class' => 'nav'));
+
+ if (Event::handle('StartLoginGroupNav', array(&$this->action))) {
+
+ $this->action->menuItem(common_local_url('login'),
+ _('Login'),
+ _('Login with a username and password'),
+ $action_name === 'login');
- if (!common_config('site','openidonly')) {
- $menu['login'] = array(_('Login'),
- _('Login with a username and password'));
if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
- $menu['register'] = array(_('Register'),
- _('Sign up for a new account'));
+ $this->action->menuItem(common_local_url('register'),
+ _('Register'),
+ _('Sign up for a new account'),
+ $action_name === 'register');
}
- }
- if (common_config('openid', 'enabled')) {
- $menu['openidlogin'] = array(_('OpenID'),
- _('Login or register with OpenID'));
- }
-
- $action_name = $this->action->trimmed('action');
- $this->action->elementStart('ul', array('class' => 'nav'));
- foreach ($menu as $menuaction => $menudesc) {
- $this->action->menuItem(common_local_url($menuaction),
- $menudesc[0],
- $menudesc[1],
- $action_name === $menuaction);
+ Event::handle('EndLoginGroupNav', array(&$this->action));
}
$this->action->elementEnd('ul');
diff --git a/lib/mail.php b/lib/mail.php
index df585406c..5bf4d7425 100644
--- a/lib/mail.php
+++ b/lib/mail.php
@@ -551,9 +551,9 @@ function mail_notify_fave($other, $user, $notice)
common_init_locale($other->language);
- $subject = sprintf(_('%s added your notice as a favorite'), $bestname);
+ $subject = sprintf(_('%s (@%s) added your notice as a favorite'), $bestname, $user->nickname);
- $body = sprintf(_("%1\$s just added your notice from %2\$s".
+ $body = sprintf(_("%1\$s (@%7\$s) just added your notice from %2\$s".
" as one of their favorites.\n\n" .
"The URL of your notice is:\n\n" .
"%3\$s\n\n" .
@@ -570,7 +570,8 @@ function mail_notify_fave($other, $user, $notice)
$notice->content,
common_local_url('showfavorites',
array('nickname' => $user->nickname)),
- common_config('site', 'name'));
+ common_config('site', 'name'),
+ $user->nickname);
common_init_locale();
mail_to_user($other, $subject, $body);
@@ -607,9 +608,9 @@ function mail_notify_attn($user, $notice)
$conversationUrl = null;
}
- $subject = sprintf(_('%s sent a notice to your attention'), $bestname);
+ $subject = sprintf(_('%s (@%s) sent a notice to your attention'), $bestname, $sender->nickname);
- $body = sprintf(_("%1\$s just sent a notice to your attention (an '@-reply') on %2\$s.\n\n".
+ $body = sprintf(_("%1\$s (@%9\$s) just sent a notice to your attention (an '@-reply') on %2\$s.\n\n".
"The notice is here:\n\n".
"\t%3\$s\n\n" .
"It reads:\n\n".
@@ -629,10 +630,11 @@ function mail_notify_attn($user, $notice)
$notice->content,//%4
$conversationUrl,//%5
common_local_url('newnotice',
- array('replyto' => $sender->nickname)),//%6
+ array('replyto' => $sender->nickname, 'inreplyto' => $notice->id)),//%6
common_local_url('replies',
array('nickname' => $user->nickname)),//%7
- common_local_url('emailsettings'));//%8
+ common_local_url('emailsettings'), //%8
+ $sender->nickname); //%9
common_init_locale();
mail_to_user($user, $subject, $body);
diff --git a/lib/messageform.php b/lib/messageform.php
index 6431bfdcc..e25ebfa08 100644
--- a/lib/messageform.php
+++ b/lib/messageform.php
@@ -140,12 +140,19 @@ class MessageForm extends Form
'rows' => 4,
'name' => 'content'),
($this->content) ? $this->content : '');
- $this->out->elementStart('dl', 'form_note');
- $this->out->element('dt', null, _('Available characters'));
- $this->out->element('dd', array('id' => 'notice_text-count'),
- '140');
- $this->out->elementEnd('dl');
+ $contentLimit = Message::maxContent();
+
+ $this->out->element('script', array('type' => 'text/javascript'),
+ 'maxLength = ' . $contentLimit . ';');
+
+ if ($contentLimit > 0) {
+ $this->out->elementStart('dl', 'form_note');
+ $this->out->element('dt', null, _('Available characters'));
+ $this->out->element('dd', array('id' => 'notice_text-count'),
+ $contentLimit);
+ $this->out->elementEnd('dl');
+ }
}
/**
diff --git a/lib/noticeform.php b/lib/noticeform.php
index 1e3a45142..186330bf1 100644
--- a/lib/noticeform.php
+++ b/lib/noticeform.php
@@ -70,6 +70,12 @@ class NoticeForm extends Form
var $user = null;
/**
+ * The notice being replied to
+ */
+
+ var $inreplyto = null;
+
+ /**
* Constructor
*
* @param HTMLOutputter $out output channel
@@ -77,13 +83,14 @@ class NoticeForm extends Form
* @param string $content content to pre-fill
*/
- function __construct($out=null, $action=null, $content=null, $user=null)
+ function __construct($out=null, $action=null, $content=null, $user=null, $inreplyto=null)
{
parent::__construct($out);
$this->action = $action;
$this->content = $content;
-
+ $this->inreplyto = $inreplyto;
+
if ($user) {
$this->user = $user;
} else {
@@ -117,7 +124,6 @@ class NoticeForm extends Form
return common_local_url('newnotice');
}
-
/**
* Legend of the Form
*
@@ -128,7 +134,6 @@ class NoticeForm extends Form
$this->out->element('legend', null, _('Send a notice'));
}
-
/**
* Data elements
*
@@ -145,11 +150,20 @@ class NoticeForm extends Form
'rows' => 4,
'name' => 'status_textarea'),
($this->content) ? $this->content : '');
- $this->out->elementStart('dl', 'form_note');
- $this->out->element('dt', null, _('Available characters'));
- $this->out->element('dd', array('id' => 'notice_text-count'),
- '140');
- $this->out->elementEnd('dl');
+
+ $contentLimit = Notice::maxContent();
+
+ $this->out->element('script', array('type' => 'text/javascript'),
+ 'maxLength = ' . $contentLimit . ';');
+
+ if ($contentLimit > 0) {
+ $this->out->elementStart('dl', 'form_note');
+ $this->out->element('dt', null, _('Available characters'));
+ $this->out->element('dd', array('id' => 'notice_text-count'),
+ $contentLimit);
+ $this->out->elementEnd('dl');
+ }
+
if (common_config('attachments', 'uploads')) {
$this->out->element('label', array('for' => 'notice_data-attach'),_('Attach'));
$this->out->element('input', array('id' => 'notice_data-attach',
@@ -161,7 +175,7 @@ class NoticeForm extends Form
if ($this->action) {
$this->out->hidden('notice_return-to', $this->action, 'returnto');
}
- $this->out->hidden('notice_in-reply-to', $this->action, 'inreplyto');
+ $this->out->hidden('notice_in-reply-to', $this->inreplyto, 'inreplyto');
}
/**
diff --git a/lib/noticelist.php b/lib/noticelist.php
index ec85e4a5c..c2ff7c26b 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -178,9 +178,12 @@ class NoticeListItem extends Widget
function show()
{
$this->showStart();
- $this->showNotice();
- $this->showNoticeInfo();
- $this->showNoticeOptions();
+ if (Event::handle('StartShowNoticeItem', array($this))) {
+ $this->showNotice();
+ $this->showNoticeInfo();
+ $this->showNoticeOptions();
+ Event::handle('EndShowNoticeItem', array($this));
+ }
$this->showEnd();
}
@@ -261,7 +264,7 @@ class NoticeListItem extends Widget
$attrs = array('href' => $this->profile->profileurl,
'class' => 'url');
if (!empty($this->profile->fullname)) {
- $attrs['title'] = $this->profile->fullname . ' (' . $this->profile->nickname . ') ';
+ $attrs['title'] = $this->profile->fullname . ' (' . $this->profile->nickname . ')';
}
$this->out->elementStart('a', $attrs);
$this->showAvatar();
@@ -418,9 +421,17 @@ class NoticeListItem extends Widget
function showContext()
{
- // XXX: also show context if there are replies to this notice
- if (!empty($this->notice->conversation)
- && $this->notice->conversation != $this->notice->id) {
+ $hasConversation = false;
+ if( !empty($this->notice->conversation)
+ && $this->notice->conversation != $this->notice->id){
+ $hasConversation = true;
+ }else{
+ $conversation = Notice::conversationStream($this->notice->id, 1, 1);
+ if($conversation->N > 0){
+ $hasConversation = true;
+ }
+ }
+ if ($hasConversation){
$convurl = common_local_url('conversation',
array('id' => $this->notice->conversation));
$this->out->element('a', array('href' => $convurl.'#notice-'.$this->notice->id,
@@ -442,7 +453,7 @@ class NoticeListItem extends Widget
{
if (common_logged_in()) {
$reply_url = common_local_url('newnotice',
- array('replyto' => $this->profile->nickname));
+ array('replyto' => $this->profile->nickname, 'inreplyto' => $this->notice->id));
$this->out->elementStart('a', array('href' => $reply_url,
'class' => 'notice_reply',
'title' => _('Reply to this notice')));
diff --git a/lib/oauthstore.php b/lib/oauthstore.php
index 6db07b20f..e69a00f55 100644
--- a/lib/oauthstore.php
+++ b/lib/oauthstore.php
@@ -19,13 +19,12 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once 'libomb/datastore.php';
class StatusNetOAuthDataStore extends OAuthDataStore
{
// We keep a record of who's contacted us
-
function lookup_consumer($consumer_key)
{
$con = Consumer::staticGet('consumer_key', $consumer_key);
@@ -44,7 +43,9 @@ class StatusNetOAuthDataStore extends OAuthDataStore
function lookup_token($consumer, $token_type, $token_key)
{
$t = new Token();
- $t->consumer_key = $consumer->key;
+ if (!is_null($consumer)) {
+ $t->consumer_key = $consumer->key;
+ }
$t->tok = $token_key;
$t->type = ($token_type == 'access') ? 1 : 0;
if ($t->find(true)) {
@@ -154,4 +155,348 @@ class StatusNetOAuthDataStore extends OAuthDataStore
{
return $this->new_access_token($consumer);
}
+
+
+ /**
+ * Revoke specified OAuth token
+ *
+ * Revokes the authorization token specified by $token_key.
+ * Throws exceptions in case of error.
+ *
+ * @param string $token_key The token to be revoked
+ *
+ * @access public
+ **/
+ public function revoke_token($token_key) {
+ $rt = new Token();
+ $rt->tok = $token_key;
+ $rt->type = 0;
+ $rt->state = 0;
+ if (!$rt->find(true)) {
+ throw new Exception('Tried to revoke unknown token');
+ }
+ if (!$rt->delete()) {
+ throw new Exception('Failed to delete revoked token');
+ }
+ }
+
+ /**
+ * Authorize specified OAuth token
+ *
+ * Authorizes the authorization token specified by $token_key.
+ * Throws exceptions in case of error.
+ *
+ * @param string $token_key The token to be authorized
+ *
+ * @access public
+ **/
+ public function authorize_token($token_key) {
+ $rt = new Token();
+ $rt->tok = $token_key;
+ $rt->type = 0;
+ $rt->state = 0;
+ if (!$rt->find(true)) {
+ throw new Exception('Tried to authorize unknown token');
+ }
+ $orig_rt = clone($rt);
+ $rt->state = 1; # Authorized but not used
+ if (!$rt->update($orig_rt)) {
+ throw new Exception('Failed to authorize token');
+ }
+ }
+
+ /**
+ * Get profile by identifying URI
+ *
+ * Returns an OMB_Profile object representing the OMB profile identified by
+ * $identifier_uri.
+ * Returns null if there is no such OMB profile.
+ * Throws exceptions in case of other error.
+ *
+ * @param string $identifier_uri The OMB identifier URI specifying the
+ * requested profile
+ *
+ * @access public
+ *
+ * @return OMB_Profile The corresponding profile
+ **/
+ public function getProfile($identifier_uri) {
+ /* getProfile is only used for remote profiles by libomb.
+ TODO: Make it work with local ones anyway. */
+ $remote = Remote_profile::staticGet('uri', $identifier_uri);
+ if (!$remote) throw new Exception('No such remote profile');
+ $profile = Profile::staticGet('id', $remote->id);
+ if (!$profile) throw new Exception('No profile for remote user');
+
+ require_once INSTALLDIR.'/lib/omb.php';
+ return profile_to_omb_profile($identifier_uri, $profile);
+ }
+
+ /**
+ * Save passed profile
+ *
+ * Stores the OMB profile $profile. Overwrites an existing entry.
+ * Throws exceptions in case of error.
+ *
+ * @param OMB_Profile $profile The OMB profile which should be saved
+ *
+ * @access public
+ **/
+ public function saveProfile($omb_profile) {
+ if (common_profile_url($omb_profile->getNickname()) ==
+ $omb_profile->getProfileURL()) {
+ throw new Exception('Not implemented');
+ } else {
+ $remote = Remote_profile::staticGet('uri', $omb_profile->getIdentifierURI());
+
+ if ($remote) {
+ $exists = true;
+ $profile = Profile::staticGet($remote->id);
+ $orig_remote = clone($remote);
+ $orig_profile = clone($profile);
+ # XXX: compare current postNotice and updateProfile URLs to the ones
+ # stored in the DB to avoid (possibly...) above attack
+ } else {
+ $exists = false;
+ $remote = new Remote_profile();
+ $remote->uri = $omb_profile->getIdentifierURI();
+ $profile = new Profile();
+ }
+
+ $profile->nickname = $omb_profile->getNickname();
+ $profile->profileurl = $omb_profile->getProfileURL();
+
+ $fullname = $omb_profile->getFullname();
+ $profile->fullname = is_null($fullname) ? '' : $fullname;
+ $homepage = $omb_profile->getHomepage();
+ $profile->homepage = is_null($homepage) ? '' : $homepage;
+ $bio = $omb_profile->getBio();
+ $profile->bio = is_null($bio) ? '' : $bio;
+ $location = $omb_profile->getLocation();
+ $profile->location = is_null($location) ? '' : $location;
+
+ if ($exists) {
+ $profile->update($orig_profile);
+ } else {
+ $profile->created = DB_DataObject_Cast::dateTime(); # current time
+ $id = $profile->insert();
+ if (!$id) {
+ throw new Exception(_('Error inserting new profile'));
+ }
+ $remote->id = $id;
+ }
+
+ $avatar_url = $omb_profile->getAvatarURL();
+ if ($avatar_url) {
+ if (!$this->add_avatar($profile, $avatar_url)) {
+ throw new Exception(_('Error inserting avatar'));
+ }
+ } else {
+ $avatar = $profile->getOriginalAvatar();
+ if($avatar) $avatar->delete();
+ $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
+ if($avatar) $avatar->delete();
+ $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
+ if($avatar) $avatar->delete();
+ $avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
+ if($avatar) $avatar->delete();
+ }
+
+ if ($exists) {
+ if (!$remote->update($orig_remote)) {
+ throw new Exception(_('Error updating remote profile'));
+ }
+ } else {
+ $remote->created = DB_DataObject_Cast::dateTime(); # current time
+ if (!$remote->insert()) {
+ throw new Exception(_('Error inserting remote profile'));
+ }
+ }
+ }
+ }
+
+ function add_avatar($profile, $url)
+ {
+ $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
+ copy($url, $temp_filename);
+ $imagefile = new ImageFile($profile->id, $temp_filename);
+ $filename = Avatar::filename($profile->id,
+ image_type_to_extension($imagefile->type),
+ null,
+ common_timestamp());
+ rename($temp_filename, Avatar::path($filename));
+ return $profile->setOriginal($filename);
+ }
+
+ /**
+ * Save passed notice
+ *
+ * Stores the OMB notice $notice. The datastore may change the passed notice.
+ * This might by neccessary for URIs depending on a database key. Note that
+ * it is the user’s duty to present a mechanism for his OMB_Datastore to
+ * appropriately change his OMB_Notice.
+ * Throws exceptions in case of error.
+ *
+ * @param OMB_Notice $notice The OMB notice which should be saved
+ *
+ * @access public
+ **/
+ public function saveNotice(&$omb_notice) {
+ if (Notice::staticGet('uri', $omb_notice->getIdentifierURI())) {
+ throw new Exception(_('Duplicate notice'));
+ }
+ $author_uri = $omb_notice->getAuthor()->getIdentifierURI();
+ common_log(LOG_DEBUG, $author_uri, __FILE__);
+ $author = Remote_profile::staticGet('uri', $author_uri);
+ if (!$author) {
+ $author = User::staticGet('uri', $author_uri);
+ }
+ if (!$author) {
+ throw new Exception('No such user');
+ }
+
+ common_log(LOG_DEBUG, print_r($author, true), __FILE__);
+
+ $notice = Notice::saveNew($author->id,
+ $omb_notice->getContent(),
+ 'omb',
+ false,
+ null,
+ $omb_notice->getIdentifierURI());
+ if (is_string($notice)) {
+ throw new Exception($notice);
+ }
+ common_broadcast_notice($notice, true);
+ }
+
+ /**
+ * Get subscriptions of a given profile
+ *
+ * Returns an array containing subscription informations for the specified
+ * profile. Every array entry should in turn be an array with keys
+ * 'uri´: The identifier URI of the subscriber
+ * 'token´: The subscribe token
+ * 'secret´: The secret token
+ * Throws exceptions in case of error.
+ *
+ * @param string $subscribed_user_uri The OMB identifier URI specifying the
+ * subscribed profile
+ *
+ * @access public
+ *
+ * @return mixed An array containing the subscriptions or 0 if no
+ * subscription has been found.
+ **/
+ public function getSubscriptions($subscribed_user_uri) {
+ $sub = new Subscription();
+
+ $user = $this->_getAnyProfile($subscribed_user_uri);
+
+ $sub->subscribed = $user->id;
+
+ if (!$sub->find(true)) {
+ return 0;
+ }
+
+ /* Since we do not use OMB_Service_Provider’s action methods, there
+ is no need to actually return the subscriptions. */
+ return 1;
+ }
+
+ private function _getAnyProfile($uri)
+ {
+ $user = Remote_profile::staticGet('uri', $uri);
+ if (!$user) {
+ $user = User::staticGet('uri', $uri);
+ }
+ if (!$user) {
+ throw new Exception('No such user');
+ }
+ return $user;
+ }
+
+ /**
+ * Delete a subscription
+ *
+ * Deletes the subscription from $subscriber_uri to $subscribed_user_uri.
+ * Throws exceptions in case of error.
+ *
+ * @param string $subscriber_uri The OMB identifier URI specifying the
+ * subscribing profile
+ *
+ * @param string $subscribed_user_uri The OMB identifier URI specifying the
+ * subscribed profile
+ *
+ * @access public
+ **/
+ public function deleteSubscription($subscriber_uri, $subscribed_user_uri)
+ {
+ $sub = new Subscription();
+
+ $subscribed = $this->_getAnyProfile($subscribed_user_uri);
+ $subscriber = $this->_getAnyProfile($subscriber_uri);
+
+ $sub->subscribed = $subscribed->id;
+ $sub->subscriber = $subscriber->id;
+
+ $sub->delete();
+ }
+
+ /**
+ * Save a subscription
+ *
+ * Saves the subscription from $subscriber_uri to $subscribed_user_uri.
+ * Throws exceptions in case of error.
+ *
+ * @param string $subscriber_uri The OMB identifier URI specifying
+ * the subscribing profile
+ *
+ * @param string $subscribed_user_uri The OMB identifier URI specifying
+ * the subscribed profile
+ * @param OAuthToken $token The access token
+ *
+ * @access public
+ **/
+ public function saveSubscription($subscriber_uri, $subscribed_user_uri,
+ $token)
+ {
+ $sub = new Subscription();
+
+ $subscribed = $this->_getAnyProfile($subscribed_user_uri);
+ $subscriber = $this->_getAnyProfile($subscriber_uri);
+
+ $sub->subscribed = $subscribed->id;
+ $sub->subscriber = $subscriber->id;
+
+ $sub_exists = $sub->find(true);
+
+ if ($sub_exists) {
+ $orig_sub = clone($sub);
+ } else {
+ $sub->created = DB_DataObject_Cast::dateTime();
+ }
+
+ $sub->token = $token->key;
+ $sub->secret = $token->secret;
+
+ if ($sub_exists) {
+ $result = $sub->update($orig_sub);
+ } else {
+ $result = $sub->insert();
+ }
+
+ if (!$result) {
+ common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
+ throw new Exception(_('Couldn\'t insert new subscription.'));
+ return;
+ }
+
+ /* Notify user, if necessary. */
+
+ if ($subscribed instanceof User) {
+ mail_subscribe_notify_profile($subscribed,
+ Profile::staticGet($subscriber->id));
+ }
+ }
}
+?>
diff --git a/lib/omb.php b/lib/omb.php
index 0d6244599..0566701ff 100644
--- a/lib/omb.php
+++ b/lib/omb.php
@@ -19,34 +19,18 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once('OAuth.php');
-require_once(INSTALLDIR.'/lib/oauthstore.php');
-
-require_once(INSTALLDIR.'/classes/Consumer.php');
-require_once(INSTALLDIR.'/classes/Nonce.php');
-require_once(INSTALLDIR.'/classes/Token.php');
-
-require_once('Auth/Yadis/Yadis.php');
-
-define('OAUTH_NAMESPACE', 'http://oauth.net/core/1.0/');
-define('OMB_NAMESPACE', 'http://openmicroblogging.org/protocol/0.1');
-define('OMB_VERSION_01', 'http://openmicroblogging.org/protocol/0.1');
-define('OAUTH_DISCOVERY', 'http://oauth.net/discovery/1.0');
-
-define('OMB_ENDPOINT_UPDATEPROFILE', OMB_NAMESPACE.'/updateProfile');
-define('OMB_ENDPOINT_POSTNOTICE', OMB_NAMESPACE.'/postNotice');
-define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request');
-define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize');
-define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access');
-define('OAUTH_ENDPOINT_RESOURCE', OAUTH_NAMESPACE.'endpoint/resource');
-define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header');
-define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body');
-define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1');
+require_once INSTALLDIR.'/lib/oauthstore.php';
+require_once 'OAuth.php';
+require_once 'libomb/constants.php';
+require_once 'libomb/service_consumer.php';
+require_once 'libomb/notice.php';
+require_once 'libomb/profile.php';
+require_once 'Auth/Yadis/Yadis.php';
function omb_oauth_consumer()
{
static $con = null;
- if (!$con) {
+ if (is_null($con)) {
$con = new OAuthConsumer(common_root_url(), '');
}
return $con;
@@ -55,7 +39,7 @@ function omb_oauth_consumer()
function omb_oauth_server()
{
static $server = null;
- if (!$server) {
+ if (is_null($server)) {
$server = new OAuthServer(omb_oauth_datastore());
$server->add_signature_method(omb_hmac_sha1());
}
@@ -65,7 +49,7 @@ function omb_oauth_server()
function omb_oauth_datastore()
{
static $store = null;
- if (!$store) {
+ if (is_null($store)) {
$store = new StatusNetOAuthDataStore();
}
return $store;
@@ -74,57 +58,18 @@ function omb_oauth_datastore()
function omb_hmac_sha1()
{
static $hmac_method = null;
- if (!$hmac_method) {
+ if (is_null($hmac_method)) {
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
}
return $hmac_method;
}
-function omb_get_services($xrd, $type)
+function omb_broadcast_notice($notice)
{
- return $xrd->services(array(omb_service_filter($type)));
-}
-
-function omb_service_filter($type)
-{
- return create_function('$s',
- 'return omb_match_service($s, \''.$type.'\');');
-}
-
-function omb_match_service($service, $type)
-{
- return in_array($type, $service->getTypes());
-}
-
-function omb_service_uri($service)
-{
- if (!$service) {
- return null;
- }
- $uris = $service->getURIs();
- if (!$uris) {
- return null;
- }
- return $uris[0];
-}
-
-function omb_local_id($service)
-{
- if (!$service) {
- return null;
- }
- $els = $service->getElements('xrd:LocalID');
- if (!$els) {
- return null;
- }
- $el = $els[0];
- return $service->parser->content($el);
-}
-function omb_broadcast_remote_subscribers($notice)
-{
+ $omb_notice = notice_to_omb_notice($notice);
- # First, get remote users subscribed to this profile
+ /* Get remote users subscribed to this profile. */
$rp = new Remote_profile();
$rp->query('SELECT postnoticeurl, token, secret ' .
@@ -135,170 +80,148 @@ function omb_broadcast_remote_subscribers($notice)
$posted = array();
while ($rp->fetch()) {
- if (!$posted[$rp->postnoticeurl]) {
- common_log(LOG_DEBUG, 'Posting to ' . $rp->postnoticeurl);
- if (omb_post_notice_keys($notice, $rp->postnoticeurl, $rp->token, $rp->secret)) {
- common_log(LOG_DEBUG, 'Finished to ' . $rp->postnoticeurl);
- $posted[$rp->postnoticeurl] = true;
- } else {
- common_log(LOG_DEBUG, 'Failed posting to ' . $rp->postnoticeurl);
- }
+ if (isset($posted[$rp->postnoticeurl])) {
+ /* We already posted to this url. */
+ continue;
}
- }
-
- $rp->free();
- unset($rp);
+ common_debug('Posting to ' . $rp->postnoticeurl, __FILE__);
+
+ /* Post notice. */
+ $service = new Laconica_OMB_Service_Consumer(
+ array(OMB_ENDPOINT_POSTNOTICE => $rp->postnoticeurl));
+ try {
+ $service->setToken($rp->token, $rp->secret);
+ $service->postNotice($omb_notice);
+ } catch (Exception $e) {
+ common_log(LOG_ERR, 'Failed posting to ' . $rp->postnoticeurl);
+ common_log(LOG_ERR, 'Error status '.$e);
+ continue;
+ }
+ $posted[$rp->postnoticeurl] = true;
- return true;
-}
+ common_debug('Finished to ' . $rp->postnoticeurl, __FILE__);
+ }
-function omb_post_notice($notice, $remote_profile, $subscription)
-{
- return omb_post_notice_keys($notice, $remote_profile->postnoticeurl, $subscription->token, $subscription->secret);
+ return;
}
-function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret)
+function omb_broadcast_profile($profile)
{
- $user = User::staticGet('id', $notice->profile_id);
+ $user = User::staticGet('id', $profile->id);
if (!$user) {
return false;
}
- $con = omb_oauth_consumer();
+ $profile = $user->getProfile();
- $token = new OAuthToken($tk, $secret);
-
- $url = $postnoticeurl;
- $parsed = parse_url($url);
- $params = array();
- parse_str($parsed['query'], $params);
-
- $req = OAuthRequest::from_consumer_and_token($con, $token,
- 'POST', $url, $params);
-
- $req->set_parameter('omb_version', OMB_VERSION_01);
- $req->set_parameter('omb_listenee', $user->uri);
- $req->set_parameter('omb_notice', $notice->uri);
- $req->set_parameter('omb_notice_content', $notice->content);
- $req->set_parameter('omb_notice_url', common_local_url('shownotice',
- array('notice' =>
- $notice->id)));
- $req->set_parameter('omb_notice_license', common_config('license', 'url'));
+ $omb_profile = profile_to_omb_profile($user->uri, $profile, true);
- $user->free();
- unset($user);
+ /* Get remote users subscribed to this profile. */
+ $rp = new Remote_profile();
- $req->sign_request(omb_hmac_sha1(), $con, $token);
+ $rp->query('SELECT updateprofileurl, token, secret ' .
+ 'FROM subscription JOIN remote_profile ' .
+ 'ON subscription.subscriber = remote_profile.id ' .
+ 'WHERE subscription.subscribed = ' . $profile->id . ' ');
- # We re-use this tool's fetcher, since it's pretty good
+ $posted = array();
- $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+ while ($rp->fetch()) {
+ if (isset($posted[$rp->updateprofileurl])) {
+ /* We already posted to this url. */
+ continue;
+ }
+ common_debug('Posting to ' . $rp->updateprofileurl, __FILE__);
+
+ /* Update profile. */
+ $service = new StatusNet_OMB_Service_Consumer(
+ array(OMB_ENDPOINT_UPDATEPROFILE => $rp->updateprofileurl));
+ try {
+ $service->setToken($rp->token, $rp->secret);
+ $service->updateProfile($omb_profile);
+ } catch (Exception $e) {
+ common_log(LOG_ERR, 'Failed posting to ' . $rp->updateprofileurl);
+ common_log(LOG_ERR, 'Error status '.$e);
+ continue;
+ }
+ $posted[$rp->updateprofileurl] = true;
- if (!$fetcher) {
- common_log(LOG_WARNING, 'Failed to initialize Yadis fetcher.', __FILE__);
- return false;
+ common_debug('Finished to ' . $rp->updateprofileurl, __FILE__);
}
- $result = $fetcher->post($req->get_normalized_http_url(),
- $req->to_postdata(),
- array('User-Agent: StatusNet/' . STATUSNET_VERSION));
-
- if ($result->status == 403) { # not authorized, don't send again
- common_debug('403 result, deleting subscription', __FILE__);
- # FIXME: figure out how to delete this
- # $subscription->delete();
- return false;
- } else if ($result->status != 200) {
- common_debug('Error status '.$result->status, __FILE__);
- return false;
- } else { # success!
- parse_str($result->body, $return);
- if ($return['omb_version'] == OMB_VERSION_01) {
- return true;
- } else {
- return false;
- }
- }
+ return;
}
-function omb_broadcast_profile($profile)
-{
- # First, get remote users subscribed to this profile
- # XXX: use a join here rather than looping through results
- $sub = new Subscription();
- $sub->subscribed = $profile->id;
- if ($sub->find()) {
- $updated = array();
- while ($sub->fetch()) {
- $rp = Remote_profile::staticGet('id', $sub->subscriber);
- if ($rp) {
- if (!array_key_exists($rp->updateprofileurl, $updated)) {
- if (omb_update_profile($profile, $rp, $sub)) {
- $updated[$rp->updateprofileurl] = true;
- }
- }
- }
- }
+class StatusNet_OMB_Service_Consumer extends OMB_Service_Consumer {
+ public function __construct($urls)
+ {
+ $this->services = $urls;
+ $this->datastore = omb_oauth_datastore();
+ $this->oauth_consumer = omb_oauth_consumer();
+ $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
}
+
}
-function omb_update_profile($profile, $remote_profile, $subscription)
+function profile_to_omb_profile($uri, $profile, $force = false)
{
- $user = User::staticGet($profile->id);
- $con = omb_oauth_consumer();
- $token = new OAuthToken($subscription->token, $subscription->secret);
- $url = $remote_profile->updateprofileurl;
- $parsed = parse_url($url);
- $params = array();
- parse_str($parsed['query'], $params);
- $req = OAuthRequest::from_consumer_and_token($con, $token,
- "POST", $url, $params);
- $req->set_parameter('omb_version', OMB_VERSION_01);
- $req->set_parameter('omb_listenee', $user->uri);
- $req->set_parameter('omb_listenee_profile', common_profile_url($profile->nickname));
- $req->set_parameter('omb_listenee_nickname', $profile->nickname);
-
- # We use blanks to force emptying any existing values in these optional fields
-
- $req->set_parameter('omb_listenee_fullname',
- ($profile->fullname) ? $profile->fullname : '');
- $req->set_parameter('omb_listenee_homepage',
- ($profile->homepage) ? $profile->homepage : '');
- $req->set_parameter('omb_listenee_bio',
- ($profile->bio) ? $profile->bio : '');
- $req->set_parameter('omb_listenee_location',
- ($profile->location) ? $profile->location : '');
+ $omb_profile = new OMB_Profile($uri);
+ $omb_profile->setNickname($profile->nickname);
+ $omb_profile->setLicenseURL(common_config('license', 'url'));
+ if (!is_null($profile->fullname)) {
+ $omb_profile->setFullname($profile->fullname);
+ } elseif ($force) {
+ $omb_profile->setFullname('');
+ }
+ if (!is_null($profile->homepage)) {
+ $omb_profile->setHomepage($profile->homepage);
+ } elseif ($force) {
+ $omb_profile->setHomepage('');
+ }
+ if (!is_null($profile->bio)) {
+ $omb_profile->setBio($profile->bio);
+ } elseif ($force) {
+ $omb_profile->setBio('');
+ }
+ if (!is_null($profile->location)) {
+ $omb_profile->setLocation($profile->location);
+ } elseif ($force) {
+ $omb_profile->setLocation('');
+ }
+ if (!is_null($profile->profileurl)) {
+ $omb_profile->setProfileURL($profile->profileurl);
+ } elseif ($force) {
+ $omb_profile->setProfileURL('');
+ }
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
- $req->set_parameter('omb_listenee_avatar',
- ($avatar) ? $avatar->url : '');
+ if ($avatar) {
+ $omb_profile->setAvatarURL($avatar->url);
+ } elseif ($force) {
+ $omb_profile->setAvatarURL('');
+ }
+ return $omb_profile;
+}
- $req->sign_request(omb_hmac_sha1(), $con, $token);
+function notice_to_omb_notice($notice)
+{
+ /* Create an OMB_Notice for $notice. */
+ $user = User::staticGet('id', $notice->profile_id);
- # We re-use this tool's fetcher, since it's pretty good
+ if (!$user) {
+ return null;
+ }
- $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+ $profile = $user->getProfile();
- $result = $fetcher->post($req->get_normalized_http_url(),
- $req->to_postdata(),
- array('User-Agent: StatusNet/' . STATUSNET_VERSION));
+ $omb_notice = new OMB_Notice(profile_to_omb_profile($user->uri, $profile),
+ $notice->uri,
+ $notice->content);
+ $omb_notice->setURL(common_local_url('shownotice', array('notice' =>
+ $notice->id)));
+ $omb_notice->setLicenseURL(common_config('license', 'url'));
- if (empty($result) || !$result) {
- common_debug("Unable to contact " . $req->get_normalized_http_url());
- } else if ($result->status == 403) { # not authorized, don't send again
- common_debug('403 result, deleting subscription', __FILE__);
- $subscription->delete();
- return false;
- } else if ($result->status != 200) {
- common_debug('Error status '.$result->status, __FILE__);
- return false;
- } else { # success!
- parse_str($result->body, $return);
- if (isset($return['omb_version']) && $return['omb_version'] === OMB_VERSION_01) {
- return true;
- } else {
- return false;
- }
- }
+ return $omb_notice;
}
+?>
diff --git a/lib/plugin.php b/lib/plugin.php
index 87d7be5a7..59bf3ba9d 100644
--- a/lib/plugin.php
+++ b/lib/plugin.php
@@ -76,4 +76,18 @@ class Plugin
{
return true;
}
+
+ /*
+ * the name of the shortener
+ * shortenerInfo associative array with additional information. One possible element is 'freeService' which can be true or false
+ * shortener array, first element is the name of the class, second element is an array to be passed as constructor parameters to the class
+ */
+ function registerUrlShortener($name, $shortenerInfo, $shortener)
+ {
+ global $_shorteners;
+ if(!is_array($_shorteners)){
+ $_shorteners=array();
+ }
+ $_shorteners[$name]=array('info'=>$shortenerInfo, 'callInfo'=>$shortener);
+ }
}
diff --git a/lib/router.php b/lib/router.php
index 5309fe753..c18f273ed 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -50,8 +50,7 @@ class Router
var $m = null;
static $inst = null;
static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
- 'postnotice', 'updateprofile', 'finishremotesubscribe',
- 'finishopenidlogin', 'finishaddopenid');
+ 'postnotice', 'updateprofile', 'finishremotesubscribe');
static function get()
{
@@ -76,7 +75,6 @@ class Router
$m->connect('', array('action' => 'public'));
$m->connect('rss', array('action' => 'publicrss'));
- $m->connect('xrds', array('action' => 'publicxrds'));
$m->connect('featuredrss', array('action' => 'featuredrss'));
$m->connect('favoritedrss', array('action' => 'favoritedrss'));
$m->connect('opensearch/people', array('action' => 'opensearch',
@@ -128,7 +126,6 @@ class Router
// exceptional
- $m->connect('main/openid', array('action' => 'openidlogin'));
$m->connect('main/remote', array('action' => 'remotesubscribe'));
$m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
@@ -138,7 +135,7 @@ class Router
// settings
- foreach (array('profile', 'avatar', 'password', 'openid', 'im',
+ foreach (array('profile', 'avatar', 'password', 'im',
'email', 'sms', 'twitter', 'userdesign', 'other') as $s) {
$m->connect('settings/'.$s, array('action' => $s.'settings'));
}
@@ -175,6 +172,10 @@ class Router
$m->connect('notice/new?replyto=:replyto',
array('action' => 'newnotice'),
array('replyto' => '[A-Za-z0-9_-]+'));
+ $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
+ array('action' => 'newnotice'),
+ array('replyto' => '[A-Za-z0-9_-]+'),
+ array('inreplyto' => '[0-9]+'));
$m->connect('notice/:notice/file',
array('action' => 'file'),
@@ -459,7 +460,7 @@ class Router
// user stuff
foreach (array('subscriptions', 'subscribers',
- 'nudge', 'xrds', 'all', 'foaf',
+ 'nudge', 'all', 'foaf', 'xrds',
'replies', 'inbox', 'outbox', 'microsummary') as $a) {
$m->connect(':nickname/'.$a,
array('action' => $a),
diff --git a/lib/rssaction.php b/lib/rssaction.php
index 60611e48d..faf6bec7d 100644
--- a/lib/rssaction.php
+++ b/lib/rssaction.php
@@ -78,25 +78,12 @@ class Rss10Action extends Action
function prepare($args)
{
parent::prepare($args);
+
$this->limit = (int) $this->trimmed('limit');
+
if ($this->limit == 0) {
$this->limit = DEFAULT_RSS_LIMIT;
}
- return true;
- }
-
- /**
- * Handle a request
- *
- * @param array $args Arguments from $_REQUEST
- *
- * @return void
- */
-
- function handle($args)
- {
- // Parent handling, including cache check
- parent::handle($args);
if (common_config('site', 'private')) {
if (!isset($_SERVER['PHP_AUTH_USER'])) {
@@ -122,8 +109,21 @@ class Rss10Action extends Action
}
}
- // Get the list of notices
- $this->notices = $this->getNotices($this->limit);
+ return true;
+ }
+
+ /**
+ * Handle a request
+ *
+ * @param array $args Arguments from $_REQUEST
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ // Parent handling, including cache check
+ parent::handle($args);
$this->showRss();
}
@@ -140,7 +140,7 @@ class Rss10Action extends Action
}
/**
- * Get the notices to output in this stream
+ * Get the notices to output in this stream.
*
* @return array an array of Notice objects sorted in reverse chron
*/
@@ -258,26 +258,27 @@ class Rss10Action extends Action
$attachments = $notice->attachments();
if($attachments){
foreach($attachments as $attachment){
- if ($attachment->isEnclosure()) {
+ $enclosure=$attachment->getEnclosure();
+ if ($enclosure) {
// DO NOT move xmlns declaration to root element. Making it
// the default namespace here improves compatibility with
// real-world feed readers.
$attribs = array(
- 'rdf:resource' => $attachment->url,
- 'url' => $attachment->url,
+ 'rdf:resource' => $enclosure->url,
+ 'url' => $enclosure->url,
'xmlns' => 'http://purl.oclc.org/net/rss_2.0/enc#'
);
- if ($attachment->title) {
- $attribs['dc:title'] = $attachment->title;
+ if ($enclosure->title) {
+ $attribs['dc:title'] = $enclosure->title;
}
- if ($attachment->modified) {
- $attribs['dc:date'] = common_date_w3dtf($attachment->modified);
+ if ($enclosure->modified) {
+ $attribs['dc:date'] = common_date_w3dtf($enclosure->modified);
}
- if ($attachment->size) {
- $attribs['length'] = $attachment->size;
+ if ($enclosure->size) {
+ $attribs['length'] = $enclosure->size;
}
- if ($attachment->mimetype) {
- $attribs['type'] = $attachment->mimetype;
+ if ($enclosure->mimetype) {
+ $attribs['type'] = $enclosure->mimetype;
}
$this->element('enclosure', $attribs);
}
diff --git a/lib/settingsaction.php b/lib/settingsaction.php
index a1f305f5b..c3669868d 100644
--- a/lib/settingsaction.php
+++ b/lib/settingsaction.php
@@ -77,9 +77,7 @@ class SettingsAction extends CurrentUserDesignAction
// _all_ our settings are important
common_set_returnto($this->selfUrl());
$user = common_current_user();
- if ($user->hasOpenID()) {
- common_redirect(common_local_url('openidlogin'), 303);
- } else {
+ if (Event::handle('RedirectToLogin', array($this, $user))) {
common_redirect(common_local_url('login'), 303);
}
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
diff --git a/lib/twitter.php b/lib/twitter.php
index 676c9b20a..b49e2e119 100644
--- a/lib/twitter.php
+++ b/lib/twitter.php
@@ -165,9 +165,10 @@ function broadcast_twitter($notice)
}
function broadcast_oauth($notice, $flink) {
-
$user = $flink->getUser();
$statustxt = format_status($notice);
+ // Convert !groups to #hashes
+ $statustxt = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/', "\\1#\\2", $statustxt);
$token = TwitterOAuthClient::unpackToken($flink->credentials);
$client = new TwitterOAuthClient($token->key, $token->secret);
$status = null;
@@ -222,6 +223,10 @@ function broadcast_basicauth($notice, $flink)
$user->nickname, $user->id);
common_log(LOG_WARNING, $errmsg);
+ $errmsg = sprintf('No data returned by Twitter API when ' .
+ 'trying to send update for %1$s (user id %2$s).',
+ $user->nickname, $user->id);
+ common_log(LOG_WARNING, $errmsg);
return false;
}
diff --git a/lib/twitterapi.php b/lib/twitterapi.php
index 638efba24..d199e4dee 100644
--- a/lib/twitterapi.php
+++ b/lib/twitterapi.php
@@ -274,11 +274,12 @@ class TwitterapiAction extends Action
$enclosures = array();
foreach ($attachments as $attachment) {
- if ($attachment->isEnclosure()) {
+ $enclosure_o=$attachment->getEnclosure();
+ if ($enclosure_o) {
$enclosure = array();
- $enclosure['url'] = $attachment->url;
- $enclosure['mimetype'] = $attachment->mimetype;
- $enclosure['size'] = $attachment->size;
+ $enclosure['url'] = $enclosure_o->url;
+ $enclosure['mimetype'] = $enclosure_o->mimetype;
+ $enclosure['size'] = $enclosure_o->size;
$enclosures[] = $enclosure;
}
}
@@ -594,7 +595,6 @@ class TwitterapiAction extends Action
$this->init_document('rss');
- $this->elementStart('channel');
$this->element('title', null, $title);
$this->element('link', null, $link);
if (!is_null($suplink)) {
@@ -620,7 +620,6 @@ class TwitterapiAction extends Action
}
}
- $this->elementEnd('channel');
$this->end_twitter_rss();
}
@@ -667,7 +666,6 @@ class TwitterapiAction extends Action
$this->init_document('rss');
- $this->elementStart('channel');
$this->element('title', null, $title);
$this->element('link', null, $link);
$this->element('description', null, $subtitle);
@@ -686,7 +684,6 @@ class TwitterapiAction extends Action
}
}
- $this->elementEnd('channel');
$this->end_twitter_rss();
}
@@ -989,11 +986,14 @@ class TwitterapiAction extends Action
function init_twitter_rss()
{
$this->startXML();
- $this->elementStart('rss', array('version' => '2.0'));
+ $this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
+ $this->elementStart('channel');
+ Event::handle('StartApiRss', array($this));
}
function end_twitter_rss()
{
+ $this->elementEnd('channel');
$this->elementEnd('rss');
$this->endXML();
}
@@ -1005,6 +1005,7 @@ class TwitterapiAction extends Action
$this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
'xml:lang' => 'en-US',
'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
+ Event::handle('StartApiAtom', array($this));
}
function end_twitter_atom()
diff --git a/lib/twitteroauthclient.php b/lib/twitteroauthclient.php
index e37fa05f0..bad2b74ca 100644
--- a/lib/twitteroauthclient.php
+++ b/lib/twitteroauthclient.php
@@ -118,7 +118,7 @@ class TwitterOAuthClient extends OAuthClient
}
/**
- * Calls Twitter's /stutuses/update API method
+ * Calls Twitter's /statuses/update API method
*
* @param string $status text of the status
* @param int $in_reply_to_status_id optional id of the status it's
@@ -137,7 +137,7 @@ class TwitterOAuthClient extends OAuthClient
}
/**
- * Calls Twitter's /stutuses/friends_timeline API method
+ * Calls Twitter's /statuses/friends_timeline API method
*
* @param int $since_id show statuses after this id
* @param int $max_id show statuses before this id
@@ -167,7 +167,7 @@ class TwitterOAuthClient extends OAuthClient
}
/**
- * Calls Twitter's /stutuses/friends API method
+ * Calls Twitter's /statuses/friends API method
*
* @param int $id id of the user whom you wish to see friends of
* @param int $user_id numerical user id
@@ -197,7 +197,7 @@ class TwitterOAuthClient extends OAuthClient
}
/**
- * Calls Twitter's /stutuses/friends/ids API method
+ * Calls Twitter's /statuses/friends/ids API method
*
* @param int $id id of the user whom you wish to see friends of
* @param int $user_id numerical user id
diff --git a/lib/unqueuemanager.php b/lib/unqueuemanager.php
index 3cdad0b54..6cfe5bcbd 100644
--- a/lib/unqueuemanager.php
+++ b/lib/unqueuemanager.php
@@ -39,7 +39,7 @@ class UnQueueManager
case 'omb':
if ($this->_isLocal($notice)) {
require_once(INSTALLDIR.'/lib/omb.php');
- omb_broadcast_remote_subscribers($notice);
+ omb_broadcast_notice($notice);
}
break;
case 'public':
@@ -72,8 +72,13 @@ class UnQueueManager
require_once(INSTALLDIR.'/lib/jabber.php');
jabber_broadcast_notice($notice);
break;
+ case 'plugin':
+ Event::handle('HandleQueuedNotice', array(&$notice));
+ break;
default:
- throw ServerException("UnQueueManager: Unknown queue: $type");
+ if (Event::handle('UnqueueHandleNotice', array(&$notice, $queue))) {
+ throw ServerException("UnQueueManager: Unknown queue: $queue");
+ }
}
}
diff --git a/lib/util.php b/lib/util.php
index 0b696662c..56753debe 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -59,7 +59,7 @@ function common_init_language()
textdomain("statusnet");
setlocale(LC_CTYPE, 'C');
if(!$locale_set) {
- common_log(LOG_INFO,'Language requested:'.$language.' - locale could not be set:',__FILE__);
+ common_log(LOG_INFO, 'Language requested:' . $language . ' - locale could not be set. Perhaps that system locale is not installed.', __FILE__);
}
}
@@ -391,10 +391,10 @@ function common_render_content($text, $notice)
{
$r = common_render_text($text);
$id = $notice->profile_id;
- $r = preg_replace('/(^|\s+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r);
+ $r = preg_replace('/(^|[\s\.\,\:\;]+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r);
$r = preg_replace('/^T ([A-Z0-9]{1,64}) /e', "'T '.common_at_link($id, '\\1').' '", $r);
- $r = preg_replace('/(^|\s+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r);
- $r = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r);
+ $r = preg_replace('/(^|[\s\.\,\:\;]+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r);
+ $r = preg_replace('/(^|[\s\.\,\:\;]+)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r);
return $r;
}
@@ -421,7 +421,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
'|'.
'(?:(?:mailto|aim|tel|xmpp):)'.
')'.
- '(?:[\pN\pL\-\_\+]+(?::[\pN\pL\-\_\+]+)?\@)?'. //user:pass@
+ '(?:[\pN\pL\-\_\+\%\~]+(?::[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@
'(?:'.
'(?:'.
'\[[\pN\pL\-\_\:\.]+(?<![\.\:])\]'. //[dns]
@@ -432,9 +432,9 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
')'.
'|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'. //IPv4
'|(?:'. //IPv6
- '\[?(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}(?:(?:[0-9A-Fa-f]{1,4})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){6}(?::|(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(?::[0-9A-Fa-f]{1,4})))|(?:(?:[0-9A-Fa-f]{1,4}:){5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){4}(?::[0-9A-Fa-f]{1,4}){0,1}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){3}(?::[0-9A-Fa-f]{1,4}){0,2}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){2}(?::[0-9A-Fa-f]{1,4}){0,3}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:)(?::[0-9A-Fa-f]{1,4}){0,4}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?::(?::[0-9A-Fa-f]{1,4}){0,5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))\]?'.
+ '\[?(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}(?:(?:[0-9A-Fa-f]{1,4})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){6}(?::|(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(?::[0-9A-Fa-f]{1,4})))|(?:(?:[0-9A-Fa-f]{1,4}:){5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){4}(?::[0-9A-Fa-f]{1,4}){0,1}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){3}(?::[0-9A-Fa-f]{1,4}){0,2}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){2}(?::[0-9A-Fa-f]{1,4}){0,3}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:)(?::[0-9A-Fa-f]{1,4}){0,4}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?::(?::[0-9A-Fa-f]{1,4}){0,5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))\]?(?<!:)'.
')|(?:'. //DNS
- '(?:[\pN\pL\-\_\+]+(?:\:[\pN\pL\-\_\+]+)?\@)?'. //user:pass@
+ '(?:[\pN\pL\-\_\+\%\~]+(?:\:[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@
'[\pN\pL\-\_]+(?:\.[\pN\pL\-\_]+)*\.'.
//tld list from http://data.iana.org/TLD/tlds-alpha-by-domain.txt, also added local, loc, and onion
'(?:AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XN--0ZWM56D|测试|XN--11B5BS3A9AJ6G|परीक्षा|XN--80AKHBYKNJ4F|испытание|XN--9T4B11YI5A|테스트|XN--DEBA0AD|טעסט|XN--G6W251D|測試|XN--HGBK6AJ7F53BBA|آزمایشی|XN--HLCJ6AYA9ESC7A|பரிட்சை|XN--JXALPDLP|δοκιμή|XN--KGBECHTV|إختبار|XN--ZCKZAH|テスト|YE|YT|YU|ZA|ZM|ZW|local|loc|onion)'.
@@ -442,13 +442,13 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
')'.
'(?:'.
'(?:\:\d+)?'. //:port
- '(?:/[\pN\pL$\[\]\,\!\(\)\.\-\_\+\/\=\&\;]*)?'. // /path
- '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\-\_\+\/\=\&\;\/]*)?'. // ?query string
- '(?:\#[\pN\pL$\[\]\,\!\(\)\.\-\_\+\/\=\&\;\/\?\#]*)?'. // #fragment
+ '(?:/[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"@]*)?'. // /path
+ '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"@\/]*)?'. // ?query string
+ '(?:\#[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"\@/\?\#]*)?'. // #fragment
')(?<![\?\.\,\#\,])'.
')'.
'#ixu';
- preg_match_all($regex,$text,$matches);
+ //preg_match_all($regex,$text,$matches);
//print_r($matches);
return preg_replace_callback($regex, curry('callback_helper',$callback,$notice_id) ,$text);
}
@@ -493,7 +493,7 @@ function callback_helper($matches, $callback, $notice_id) {
}while($original_url!=$url);
if(empty($notice_id)){
- $result = call_user_func_array($callback,$url);
+ $result = call_user_func_array($callback, array($url));
}else{
$result = call_user_func_array($callback, array(array($url,$notice_id)) );
}
@@ -536,7 +536,7 @@ function common_linkify($url) {
throw new ServerException("Can't linkify url '$url'");
}
- $attrs = array('href' => $canon, 'rel' => 'external');
+ $attrs = array('href' => $canon, 'title' => $longurl, 'rel' => 'external');
$is_attachment = false;
$attachment_id = null;
@@ -552,12 +552,13 @@ function common_linkify($url) {
}
if (!empty($f)) {
- if (isset($f->filename)) {
+ if ($f->isEnclosure()) {
$is_attachment = true;
$attachment_id = $f->id;
- } else { // if it has OEmbed info, it's an attachment, too
+ } else {
$foe = File_oembed::staticGet('file_id', $f->id);
if (!empty($foe)) {
+ // if it has OEmbed info, it's an attachment, too
$is_attachment = true;
$attachment_id = $f->id;
@@ -583,7 +584,8 @@ function common_linkify($url) {
function common_shorten_links($text)
{
- if (mb_strlen($text) <= 140) return $text;
+ $maxLength = Notice::maxContent();
+ if ($maxLength == 0 || mb_strlen($text) <= $maxLength) return $text;
return common_replace_urls_callback($text, array('File_redirection', 'makeShort'));
}
@@ -728,14 +730,10 @@ function common_relative_profile($sender, $nickname, $dt=null)
function common_local_url($action, $args=null, $params=null, $fragment=null)
{
- static $sensitive = array('login', 'register', 'passwordsettings',
- 'twittersettings', 'finishopenidlogin',
- 'finishaddopenid', 'api');
-
$r = Router::get();
$path = $r->build($action, $args, $params, $fragment);
- $ssl = in_array($action, $sensitive);
+ $ssl = common_is_sensitive($action);
if (common_config('site','fancy')) {
$url = common_path(mb_substr($path, 1), $ssl);
@@ -749,6 +747,19 @@ function common_local_url($action, $args=null, $params=null, $fragment=null)
return $url;
}
+function common_is_sensitive($action)
+{
+ static $sensitive = array('login', 'register', 'passwordsettings',
+ 'twittersettings', 'api');
+ $ssl = null;
+
+ if (Event::handle('SensitiveAction', array($action, &$ssl))) {
+ $ssl = in_array($action, $sensitive);
+ }
+
+ return $ssl;
+}
+
function common_path($relative, $ssl=false)
{
$pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
@@ -887,7 +898,8 @@ function common_enqueue_notice($notice)
'twitter',
'facebook',
'ping');
- static $allTransports = array('sms');
+
+ static $allTransports = array('sms', 'plugin');
$transports = $allTransports;
@@ -905,11 +917,16 @@ function common_enqueue_notice($notice)
}
}
- $qm = QueueManager::get();
+ if (Event::handle('StartEnqueueNotice', array($notice, &$transports))) {
+
+ $qm = QueueManager::get();
- foreach ($transports as $transport)
- {
- $qm->enqueue($notice, $transport);
+ foreach ($transports as $transport)
+ {
+ $qm->enqueue($notice, $transport);
+ }
+
+ Event::handle('EndEnqueueNotice', array($notice, $transports));
}
return true;
@@ -1155,7 +1172,8 @@ function common_negotiate_type($cprefs, $sprefs)
function common_config($main, $sub)
{
global $config;
- return isset($config[$main][$sub]) ? $config[$main][$sub] : false;
+ return (array_key_exists($main, $config) &&
+ array_key_exists($sub, $config[$main])) ? $config[$main][$sub] : false;
}
function common_copy_args($from)
@@ -1362,57 +1380,19 @@ function common_shorten_url($long_url)
} else {
$svc = $user->urlshorteningservice;
}
-
- $curlh = curl_init();
- curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
- curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet');
- curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
-
- switch($svc) {
- case 'ur1.ca':
- require_once INSTALLDIR.'/lib/Shorturl_api.php';
- $short_url_service = new LilUrl;
- $short_url = $short_url_service->shorten($long_url);
- break;
-
- case '2tu.us':
- $short_url_service = new TightUrl;
- require_once INSTALLDIR.'/lib/Shorturl_api.php';
- $short_url = $short_url_service->shorten($long_url);
- break;
-
- case 'ptiturl.com':
- require_once INSTALLDIR.'/lib/Shorturl_api.php';
- $short_url_service = new PtitUrl;
- $short_url = $short_url_service->shorten($long_url);
- break;
-
- case 'bit.ly':
- curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url));
- $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl;
- break;
-
- case 'is.gd':
- curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url));
- $short_url = curl_exec($curlh);
- break;
- case 'snipr.com':
- curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url));
- $short_url = curl_exec($curlh);
- break;
- case 'metamark.net':
- curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url));
- $short_url = curl_exec($curlh);
- break;
- case 'tinyurl.com':
- curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url));
- $short_url = curl_exec($curlh);
- break;
- default:
- $short_url = false;
+ global $_shorteners;
+ if (!isset($_shorteners[$svc])) {
+ //the user selected service doesn't exist, so default to ur1.ca
+ $svc = 'ur1.ca';
+ }
+ if (!isset($_shorteners[$svc])) {
+ // no shortener plugins installed.
+ return $long_url;
}
- curl_close($curlh);
+ $reflectionObj = new ReflectionClass($_shorteners[$svc]['callInfo'][0]);
+ $short_url_service = $reflectionObj->newInstanceArgs($_shorteners[$svc]['callInfo'][1]);
+ $short_url = $short_url_service->shorten($long_url);
return $short_url;
}
diff --git a/plugins/Autocomplete/Autocomplete.js b/plugins/Autocomplete/Autocomplete.js
index dfadea004..3eff685a8 100644
--- a/plugins/Autocomplete/Autocomplete.js
+++ b/plugins/Autocomplete/Autocomplete.js
@@ -1,38 +1,37 @@
$(document).ready(function(){
- $.getJSON($('address .url')[0].href+'/api/statuses/friends.json?user_id=' + current_user['id'] + '&lite=true&callback=?',
- function(friends){
- $('#notice_data-text').autocomplete(friends, {
+ $('#notice_data-text').autocomplete($('address .url')[0].href+'/plugins/Autocomplete/autocomplete.json', {
multiple: true,
multipleSeparator: " ",
minChars: 1,
formatItem: function(row, i, max){
- return '@' + row.screen_name + ' (' + row.name + ')';
+ row = eval("(" + row + ")");
+ switch(row.type)
+ {
+ case 'user':
+ return row.nickname + ' (' + row.fullname + ')';
+ case 'group':
+ return row.nickname + ' (' + row.fullname + ')';
+ }
},
formatMatch: function(row, i, max){
- return '@' + row.screen_name;
+ row = eval("(" + row + ")");
+ switch(row.type)
+ {
+ case 'user':
+ return row.nickname;
+ case 'group':
+ return row.nickname;
+ }
},
formatResult: function(row){
- return '@' + row.screen_name;
+ row = eval("(" + row + ")");
+ switch(row.type)
+ {
+ case 'user':
+ return '@' + row.nickname;
+ case 'group':
+ return '!' + row.nickname;
+ }
}
});
- }
- );
- $.getJSON($('address .url')[0].href+'/api/statusnet/groups/list.json?user_id=' + current_user['id'] + '&callback=?',
- function(groups){
- $('#notice_data-text').autocomplete(groups, {
- multiple: true,
- multipleSeparator: " ",
- minChars: 1,
- formatItem: function(row, i, max){
- return '!' + row.nickname + ' (' + row.fullname + ')';
- },
- formatMatch: function(row, i, max){
- return '!' + row.nickname;
- },
- formatResult: function(row){
- return '!' + row.nickname;
- }
- });
- }
- );
});
diff --git a/plugins/Autocomplete/AutocompletePlugin.php b/plugins/Autocomplete/AutocompletePlugin.php
index b75397270..baaec73c1 100644
--- a/plugins/Autocomplete/AutocompletePlugin.php
+++ b/plugins/Autocomplete/AutocompletePlugin.php
@@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
+require_once(INSTALLDIR.'/plugins/Autocomplete/autocomplete.php');
+
class AutocompletePlugin extends Plugin
{
function __construct()
@@ -40,13 +42,6 @@ class AutocompletePlugin extends Plugin
function onEndShowScripts($action){
if (common_logged_in()) {
- $current_user = common_current_user();
- $js_string = <<<EOT
-<script type="text/javascript">
-var current_user = { id: '$current_user->id' };
-</script>
-EOT;
- $action->raw($js_string);
$action->script('plugins/Autocomplete/jquery-autocomplete/jquery.autocomplete.pack.js');
$action->script('plugins/Autocomplete/Autocomplete.js');
}
@@ -59,5 +54,12 @@ EOT;
}
}
+ function onRouterInitialized($m)
+ {
+ if (common_logged_in()) {
+ $m->connect('plugins/Autocomplete/autocomplete.json', array('action'=>'autocomplete'));
+ }
+ }
+
}
?>
diff --git a/plugins/Autocomplete/autocomplete.php b/plugins/Autocomplete/autocomplete.php
new file mode 100644
index 000000000..aa57b3915
--- /dev/null
+++ b/plugins/Autocomplete/autocomplete.php
@@ -0,0 +1,136 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * List users for autocompletion
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * List users for autocompletion
+ *
+ * This is the form for adding a new g
+ *
+ * @category Plugin
+ * @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/
+ */
+
+class AutocompleteAction extends Action
+{
+ private $result;
+
+ /**
+ * Last-modified date for page
+ *
+ * When was the content of this page last modified? Based on notice,
+ * profile, avatar.
+ *
+ * @return int last-modified date as unix timestamp
+ */
+ function lastModified()
+ {
+ $max=0;
+ foreach($this->users as $user){
+ $max = max($max,strtotime($user->modified),strtotime($user->profile->modified));
+ }
+ foreach($this->groups as $group){
+ $max = max($max,strtotime($group->modified));
+ }
+ return $max;
+ }
+
+ /**
+ * An entity tag for this page
+ *
+ * Shows the ETag for the page, based on the notice ID and timestamps
+ * for the notice, profile, and avatar. It's weak, since we change
+ * the date text "one hour ago", etc.
+ *
+ * @return string etag
+ */
+ function etag()
+ {
+ return '"' . implode(':', array($this->arg('action'),
+ crc32($this->arg('q')), //the actual string can have funny characters in we don't want showing up in the etag
+ $this->arg('limit'),
+ $this->lastModified())) . '"';
+ }
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ $this->groups=array();
+ $this->users=array();
+ $q = $this->arg('q');
+ $limit = $this->arg('limit');
+ if($limit > 200) $limit=200; //prevent DOS attacks
+ if(substr($q,0,1)=='@'){
+ //user search
+ $q=substr($q,1);
+ $user = new User();
+ $user->limit($limit);
+ $user->whereAdd('nickname like \'' . trim($user->escape($q), '\'') . '%\'');
+ $user->find();
+ while($user->fetch()) {
+ $profile = Profile::staticGet($user->id);
+ $user->profile=$profile;
+ $this->users[]=$user;
+ }
+ }
+ if(substr($q,0,1)=='!'){
+ //group search
+ $q=substr($q,1);
+ $group = new User_group();
+ $group->limit($limit);
+ $group->whereAdd('nickname like \'' . trim($group->escape($q), '\'') . '%\'');
+ $group->find();
+ while($group->fetch()) {
+ $this->groups[]=$group;
+ }
+ }
+ return true;
+ }
+
+ function handle($args)
+ {
+ parent::handle($args);
+ $results = array();
+ foreach($this->users as $user){
+ $results[]=array('nickname' => $user->nickname, 'fullname'=> $user->profile->fullname, 'type'=>'user');
+ }
+ foreach($this->groups as $group){
+ $results[]=array('nickname' => $group->nickname, 'fullname'=> $group->fullname, 'type'=>'group');
+ }
+ foreach($results as $result) {
+ print json_encode($result) . "\n";
+ }
+ }
+}
diff --git a/plugins/Autocomplete/readme.txt b/plugins/Autocomplete/readme.txt
index 3272aa1ee..1db4c6565 100644
--- a/plugins/Autocomplete/readme.txt
+++ b/plugins/Autocomplete/readme.txt
@@ -1,5 +1,7 @@
Autocomplete allows users to autocomplete screen names in @ replies. When an "@" is typed into the notice text area, an autocomplete box is displayed populated with the user's friends' screen names.
+Note: This plugin doesn't work if the site is in Private mode, i.e. when $config['site']['private'] is set to true.
+
Installation
============
Add "addPlugin('Autocomplete');" to the bottom of your config.php
diff --git a/plugins/InfiniteScroll/InfiniteScrollPlugin.php b/plugins/InfiniteScroll/InfiniteScrollPlugin.php
index c955298cb..5928c007f 100644
--- a/plugins/InfiniteScroll/InfiniteScrollPlugin.php
+++ b/plugins/InfiniteScroll/InfiniteScrollPlugin.php
@@ -40,7 +40,7 @@ class InfiniteScrollPlugin extends Plugin
function onEndShowScripts($action)
{
- $action->script('plugins/InfiniteScroll/jquery.infinitescroll.min.js');
+ $action->script('plugins/InfiniteScroll/jquery.infinitescroll.js');
$action->script('plugins/InfiniteScroll/infinitescroll.js');
}
}
diff --git a/plugins/InfiniteScroll/infinitescroll.js b/plugins/InfiniteScroll/infinitescroll.js
index 6513072d0..ae4d53d09 100644
--- a/plugins/InfiniteScroll/infinitescroll.js
+++ b/plugins/InfiniteScroll/infinitescroll.js
@@ -1,6 +1,7 @@
jQuery(document).ready(function($){
$('notices_primary').infinitescroll({
debug: true,
+ infiniteScroll : false,
nextSelector : "li.nav_next a",
loadingImg : $('address .url')[0].href+'plugins/InfiniteScroll/ajax-loader.gif',
text : "<em>Loading the next set of posts...</em>",
@@ -12,4 +13,3 @@ jQuery(document).ready(function($){
NoticeAttachments();
});
});
-
diff --git a/plugins/InfiniteScroll/jquery.infinitescroll.js b/plugins/InfiniteScroll/jquery.infinitescroll.js
index 670686b0e..ec31bb086 100644
--- a/plugins/InfiniteScroll/jquery.infinitescroll.js
+++ b/plugins/InfiniteScroll/jquery.infinitescroll.js
@@ -92,14 +92,14 @@
if (props.isDuringAjax || props.isInvalidPage || props.isDone) return;
- if ( !isNearBottom(opts,props) ) return;
+ if ( opts.infiniteScroll && !isNearBottom(opts,props) ) return;
// we dont want to fire the ajax multiple times
props.isDuringAjax = true;
// show the loading message and hide the previous/next links
props.loadingMsg.appendTo( opts.contentSelector ).show();
- $( opts.navSelector ).hide();
+ if(opts.infiniteScroll) $( opts.navSelector ).hide();
// increment the URL bit. e.g. /page/3/
props.currPage++;
@@ -205,10 +205,19 @@
}
});
- // bind scroll handler to element (if its a local scroll) or window
- $(opts.localMode ? this : window)
- .bind('scroll.infscr', function(){ infscrSetup(path,opts,props,callback); } )
- .trigger('scroll.infscr'); // trigger the event, in case it's a short page
+ if(opts.infiniteScroll){
+ // bind scroll handler to element (if its a local scroll) or window
+ $(opts.localMode ? this : window)
+ .bind('scroll.infscr', function(){ infscrSetup(path,opts,props,callback); } )
+ .trigger('scroll.infscr'); // trigger the event, in case it's a short page
+ }else{
+ $(opts.nextSelector).click(
+ function(){
+ infscrSetup(path,opts,props,callback);
+ return false;
+ }
+ );
+ }
return this;
@@ -222,6 +231,7 @@
$.infinitescroll = {
defaults : {
debug : false,
+ infiniteScroll : true,
preload : false,
nextSelector : "div.navigation a:first",
loadingImg : "http://www.infinite-scroll.com/loading.gif",
diff --git a/plugins/LilUrl/LilUrlPlugin.php b/plugins/LilUrl/LilUrlPlugin.php
new file mode 100644
index 000000000..7665b6c1e
--- /dev/null
+++ b/plugins/LilUrl/LilUrlPlugin.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once(INSTALLDIR.'/lib/Shorturl_api.php');
+
+class LilUrlPlugin extends Plugin
+{
+ function __construct()
+ {
+ parent::__construct();
+ }
+
+ function onInitializePlugin(){
+ $this->registerUrlShortener(
+ 'ur1.ca',
+ array('freeService'=>true),
+ array('LilUrl',array('http://ur1.ca/'))
+ );
+ }
+}
+
+class LilUrl extends ShortUrlApi
+{
+ protected function shorten_imp($url) {
+ $data['longurl'] = $url;
+ $response = $this->http_post($data);
+ if (!$response) return $url;
+ $y = @simplexml_load_string($response);
+ if (!isset($y->body)) return $url;
+ $x = $y->body->p[0]->a->attributes();
+ if (isset($x['href'])) return $x['href'];
+ return $url;
+ }
+}
diff --git a/plugins/LinkbackPlugin.php b/plugins/LinkbackPlugin.php
index c49f70de0..60f7a60c7 100644
--- a/plugins/LinkbackPlugin.php
+++ b/plugins/LinkbackPlugin.php
@@ -75,6 +75,8 @@ class LinkbackPlugin extends Plugin
function linkbackUrl($url)
{
+ common_log(LOG_DEBUG,"Attempting linkback for " . $url);
+
$orig = $url;
$url = htmlspecialchars_decode($orig);
$scheme = parse_url($url, PHP_URL_SCHEME);
@@ -134,15 +136,20 @@ class LinkbackPlugin extends Plugin
"User-Agent: " . $this->userAgent(),
'content' => $request)));
$file = file_get_contents($endpoint, false, $context);
- $response = xmlrpc_decode($file);
- if (xmlrpc_is_fault($response)) {
+ if (!$file) {
common_log(LOG_WARNING,
+ "Pingback request failed for '$url' ($endpoint)");
+ } else {
+ $response = xmlrpc_decode($file);
+ if (xmlrpc_is_fault($response)) {
+ common_log(LOG_WARNING,
"Pingback error for '$url' ($endpoint): ".
"$response[faultString] ($response[faultCode])");
- } else {
- common_log(LOG_INFO,
+ } else {
+ common_log(LOG_INFO,
"Pingback success for '$url' ($endpoint): ".
"'$response'");
+ }
}
}
diff --git a/plugins/Meteor/meteorupdater.js b/plugins/Meteor/meteorupdater.js
index 2e688336f..9ce68775b 100644
--- a/plugins/Meteor/meteorupdater.js
+++ b/plugins/Meteor/meteorupdater.js
@@ -1,5 +1,6 @@
-// update the local timeline from a Meteor server
-//
+// Update the local timeline from a Meteor server
+// XXX: If @a is subscribed to @b, @a should get @b's notices in @a's Personal timeline.
+// Do Replies timeline.
var MeteorUpdater = function()
{
diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php
new file mode 100644
index 000000000..91bddf381
--- /dev/null
+++ b/plugins/OpenID/OpenIDPlugin.php
@@ -0,0 +1,225 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * Plugin for OpenID authentication and identity
+ *
+ * This class enables consumer support for OpenID, the distributed authentication
+ * and identity system.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ * @link http://openid.net/
+ */
+
+class OpenIDPlugin extends Plugin
+{
+ /**
+ * Initializer for the plugin.
+ */
+
+ function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Add OpenID-related paths to the router table
+ *
+ * Hook for RouterInitialized event.
+ *
+ * @return boolean hook return
+ */
+
+ function onRouterInitialized(&$m)
+ {
+ $m->connect('main/openid', array('action' => 'openidlogin'));
+ $m->connect('settings/openid', array('action' => 'openidsettings'));
+ $m->connect('xrds', array('action' => 'publicxrds'));
+ $m->connect('index.php?action=finishopenidlogin', array('action' => 'finishopenidlogin'));
+ $m->connect('index.php?action=finishaddopenid', array('action' => 'finishaddopenid'));
+
+ return true;
+ }
+
+ function onEndLoginGroupNav(&$action)
+ {
+ $action_name = $action->trimmed('action');
+
+ $action->menuItem(common_local_url('openidlogin'),
+ _('OpenID'),
+ _('Login or register with OpenID'),
+ $action_name === 'openidlogin');
+
+ return true;
+ }
+
+ function onEndAccountSettingsNav(&$action)
+ {
+ $action_name = $action->trimmed('action');
+
+ $action->menuItem(common_local_url('openidsettings'),
+ _('OpenID'),
+ _('Add or remove OpenIDs'),
+ $action_name === 'openidsettings');
+
+ return true;
+ }
+
+ function onAutoload($cls)
+ {
+ switch ($cls)
+ {
+ case 'OpenidloginAction':
+ case 'FinishopenidloginAction':
+ case 'FinishaddopenidAction':
+ case 'XrdsAction':
+ case 'PublicxrdsAction':
+ case 'OpenidsettingsAction':
+ require_once(INSTALLDIR.'/plugins/OpenID/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
+ return false;
+ case 'User_openid':
+ require_once(INSTALLDIR.'/plugins/OpenID/User_openid.php');
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ function onSensitiveAction($action, &$ssl)
+ {
+ switch ($action)
+ {
+ case 'finishopenidlogin':
+ case 'finishaddopenid':
+ $ssl = true;
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ function onLoginAction($action, &$login)
+ {
+ switch ($action)
+ {
+ case 'openidlogin':
+ case 'finishopenidlogin':
+ $login = true;
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * We include a <meta> element linking to the publicxrds page, for OpenID
+ * client-side authentication.
+ *
+ * @return void
+ */
+
+ function onEndHeadChildren($action)
+ {
+ // for client side of OpenID authentication
+ $action->element('meta', array('http-equiv' => 'X-XRDS-Location',
+ 'content' => common_local_url('publicxrds')));
+ }
+
+ /**
+ * Redirect to OpenID login if they have an OpenID
+ *
+ * @return boolean whether to continue
+ */
+
+ function onRedirectToLogin($action, $user)
+ {
+ if (!empty($user) && User_openid::hasOpenID($user->id)) {
+ common_redirect(common_local_url('openidlogin'), 303);
+ return false;
+ }
+ return true;
+ }
+
+ function onEndShowPageNotice($action)
+ {
+ $name = $action->trimmed('action');
+
+ switch ($name)
+ {
+ case 'register':
+ $instr = '(Have an [OpenID](http://openid.net/)? ' .
+ 'Try our [OpenID registration]'.
+ '(%%action.openidlogin%%)!)';
+ break;
+ case 'login':
+ $instr = '(Have an [OpenID](http://openid.net/)? ' .
+ 'Try our [OpenID login]'.
+ '(%%action.openidlogin%%)!)';
+ break;
+ default:
+ return true;
+ }
+
+ $output = common_markup_to_html($instr);
+ $action->raw($output);
+ return true;
+ }
+
+ function onStartLoadDoc(&$title, &$output)
+ {
+ if ($title == 'openid')
+ {
+ $filename = INSTALLDIR.'/plugins/OpenID/doc-src/openid';
+
+ $c = file_get_contents($filename);
+ $output = common_markup_to_html($c);
+ return false; // success!
+ }
+
+ return true;
+ }
+
+ function onEndLoadDoc($title, &$output)
+ {
+ if ($title == 'help')
+ {
+ $menuitem = '* [OpenID](%%doc.openid%%) - what OpenID is and how to use it with this service';
+
+ $output .= common_markup_to_html($menuitem);
+ }
+
+ return true;
+ }
+}
diff --git a/classes/User_openid.php b/plugins/OpenID/User_openid.php
index f4fda1c72..338e0f6e9 100644
--- a/classes/User_openid.php
+++ b/plugins/OpenID/User_openid.php
@@ -4,7 +4,7 @@
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-class User_openid extends Memcached_DataObject
+class User_openid extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
@@ -22,4 +22,15 @@ class User_openid extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
+
+ static function hasOpenID($user_id)
+ {
+ $oid = new User_openid();
+
+ $oid->user_id = $user_id;
+
+ $cnt = $oid->find();
+
+ return ($cnt > 0);
+ }
}
diff --git a/doc-src/openid b/plugins/OpenID/doc-src/openid
index c741e3674..c741e3674 100644
--- a/doc-src/openid
+++ b/plugins/OpenID/doc-src/openid
diff --git a/actions/finishaddopenid.php b/plugins/OpenID/finishaddopenid.php
index b6de4f244..6e889205d 100644
--- a/actions/finishaddopenid.php
+++ b/plugins/OpenID/finishaddopenid.php
@@ -31,7 +31,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
-require_once INSTALLDIR.'/lib/openid.php';
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
/**
* Complete adding an OpenID
diff --git a/actions/finishopenidlogin.php b/plugins/OpenID/finishopenidlogin.php
index 9ac036985..50a9c15c8 100644
--- a/actions/finishopenidlogin.php
+++ b/plugins/OpenID/finishopenidlogin.php
@@ -19,7 +19,7 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once(INSTALLDIR.'/lib/openid.php');
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
class FinishopenidloginAction extends Action
{
@@ -30,9 +30,7 @@ class FinishopenidloginAction extends Action
function handle($args)
{
parent::handle($args);
- if (!common_config('openid', 'enabled')) {
- common_redirect(common_local_url('login'));
- } else if (common_is_real_login()) {
+ if (common_is_real_login()) {
$this->clientError(_('Already logged in.'));
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$token = $this->trimmed('token');
diff --git a/lib/openid.php b/plugins/OpenID/openid.php
index 7a2c46f00..0944117c0 100644
--- a/lib/openid.php
+++ b/plugins/OpenID/openid.php
@@ -19,7 +19,7 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once(INSTALLDIR.'/classes/User_openid.php');
+require_once(INSTALLDIR.'/plugins/OpenID/User_openid.php');
require_once('Auth/OpenID.php');
require_once('Auth/OpenID/Consumer.php');
diff --git a/actions/openidlogin.php b/plugins/OpenID/openidlogin.php
index 4b5338694..29e89234e 100644
--- a/actions/openidlogin.php
+++ b/plugins/OpenID/openidlogin.php
@@ -19,16 +19,14 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once(INSTALLDIR.'/lib/openid.php');
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
class OpenidloginAction extends Action
{
function handle($args)
{
parent::handle($args);
- if (!common_config('openid', 'enabled')) {
- common_redirect(common_local_url('login'));
- } else if (common_is_real_login()) {
+ if (common_is_real_login()) {
$this->clientError(_('Already logged in.'));
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$openid_url = $this->trimmed('openid_url');
@@ -86,6 +84,12 @@ class OpenidloginAction extends Action
}
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('openid_url');
+ }
+
function title()
{
return _('OpenID Login');
diff --git a/actions/openidsettings.php b/plugins/OpenID/openidsettings.php
index 13da64a4f..3ad46f5f5 100644
--- a/actions/openidsettings.php
+++ b/plugins/OpenID/openidsettings.php
@@ -32,7 +32,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
}
require_once INSTALLDIR.'/lib/accountsettingsaction.php';
-require_once INSTALLDIR.'/lib/openid.php';
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
/**
* Settings for OpenID
@@ -72,6 +72,12 @@ class OpenidsettingsAction extends AccountSettingsAction
' Manage your associated OpenIDs from here.');
}
+ function showScripts()
+ {
+ parent::showScripts();
+ $this->autofocus('openid_url');
+ }
+
/**
* Show the form for OpenID management
*
@@ -82,12 +88,6 @@ class OpenidsettingsAction extends AccountSettingsAction
function showContent()
{
- if (!common_config('openid', 'enabled')) {
- $this->element('div', array('class' => 'error'),
- _('OpenID is not available.'));
- return;
- }
-
$user = common_current_user();
$this->elementStart('form', array('method' => 'post',
diff --git a/actions/publicxrds.php b/plugins/OpenID/publicxrds.php
index 209a10e3d..1b2b359ca 100644
--- a/actions/publicxrds.php
+++ b/plugins/OpenID/publicxrds.php
@@ -33,7 +33,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
-require_once INSTALLDIR.'/lib/openid.php';
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
/**
* Public XRDS for OpenID
diff --git a/plugins/PiwikAnalyticsPlugin.php b/plugins/PiwikAnalyticsPlugin.php
index e36bd1c5c..8191f5181 100644
--- a/plugins/PiwikAnalyticsPlugin.php
+++ b/plugins/PiwikAnalyticsPlugin.php
@@ -73,7 +73,7 @@ class PiwikAnalyticsPlugin extends Plugin
function __construct($root=null, $id=null)
{
$this->piwikroot = $root;
- $this->piwikid = $id;
+ $this->piwikId = $id;
parent::__construct();
}
diff --git a/plugins/PtitUrl/PtitUrlPlugin.php b/plugins/PtitUrl/PtitUrlPlugin.php
new file mode 100644
index 000000000..f00d3e2f2
--- /dev/null
+++ b/plugins/PtitUrl/PtitUrlPlugin.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+class PtitUrlPlugin extends Plugin
+{
+ function __construct()
+ {
+ parent::__construct();
+ }
+
+ function onInitializePlugin(){
+ $this->registerUrlShortener(
+ 'ptiturl.com',
+ array(),
+ array('PtitUrl',array('http://ptiturl.com/?creer=oui&action=Reduire&url='))
+ );
+ }
+}
+
+class PtitUrl extends ShortUrlApi
+{
+ protected function shorten_imp($url) {
+ $response = $this->http_get($url);
+ if (!$response) return $url;
+ $response = $this->tidy($response);
+ $y = @simplexml_load_string($response);
+ if (!isset($y->body)) return $url;
+ $xml = $y->body->center->table->tr->td->pre->a->attributes();
+ if (isset($xml['href'])) return $xml['href'];
+ return $url;
+ }
+}
diff --git a/plugins/PubSubHubBub/PubSubHubBubPlugin.php b/plugins/PubSubHubBub/PubSubHubBubPlugin.php
new file mode 100644
index 000000000..e1e82e352
--- /dev/null
+++ b/plugins/PubSubHubBub/PubSubHubBubPlugin.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+define('DEFAULT_HUB','http://pubsubhubbub.appspot.com');
+
+require_once(INSTALLDIR.'/plugins/PubSubHubBub/publisher.php');
+
+class PubSubHubBubPlugin extends Plugin
+{
+ private $hub;
+
+ function __construct()
+ {
+ parent::__construct();
+ }
+
+ function onInitializePlugin(){
+ $this->hub = common_config('PubSubHubBub', 'hub');
+ if(empty($this->hub)){
+ $this->hub = DEFAULT_HUB;
+ }
+ }
+
+ function onStartApiAtom($action){
+ $action->element('link',array('rel'=>'hub','href'=>$this->hub),null);
+ }
+
+ function onStartApiRss($action){
+ $action->element('atom:link',array('rel'=>'hub','href'=>$this->hub),null);
+ }
+
+ function onHandleQueuedNotice($notice){
+ $publisher = new Publisher($this->hub);
+
+ $feeds = array();
+
+ //public timeline feeds
+ $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'public_timeline.rss'));
+ $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'public_timeline.atom'));
+
+ //author's own feeds
+ $user = User::staticGet('id',$notice->profile_id);
+ $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss'));
+ $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom'));
+
+ //tag feeds
+ $tag = new Notice_tag();
+ $tag->notice_id = $notice->id;
+ if ($tag->find()) {
+ while ($tag->fetch()) {
+ $feeds[]=common_local_url('api',array('apiaction' => 'tags','method' => 'timeline', 'argument'=>$tag->tag.'.atom'));
+ $feeds[]=common_local_url('api',array('apiaction' => 'tags','method' => 'timeline', 'argument'=>$tag->tag.'.rss'));
+ }
+ }
+
+ //group feeds
+ $group_inbox = new Group_inbox();
+ $group_inbox->notice_id = $notice->id;
+ if ($group_inbox->find()) {
+ while ($group_inbox->fetch()) {
+ $group = User_group::staticGet('id',$group_inbox->group_id);
+ $feeds[]=common_local_url('api',array('apiaction' => 'groups','method' => 'timeline','argument' => $group->nickname.'.rss'));
+ $feeds[]=common_local_url('api',array('apiaction' => 'groups','method' => 'timeline','argument' => $group->nickname.'.atom'));
+ }
+ }
+
+ //feed of each user that subscribes to the notice's author
+ $notice_inbox = new Notice_inbox();
+ $notice_inbox->notice_id = $notice->id;
+ if ($notice_inbox->find()) {
+ while ($notice_inbox->fetch()) {
+ $user = User::staticGet('id',$notice_inbox->user_id);
+ $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss'));
+ $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom'));
+ }
+ }
+
+ /* TODO: when the reply page gets RSS and ATOM feeds, implement this
+ //feed of user replied to
+ if($notice->reply_to){
+ $user = User::staticGet('id',$notice->reply_to);
+ $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss'));
+ $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom'));
+ }*/
+
+ foreach(array_unique($feeds) as $feed){
+ if(! $publisher->publish_update($feed)){
+ common_log_line(LOG_WARNING,$feed.' was not published to hub at '.$this->hub.':'.$publisher->last_response());
+ }
+ }
+ }
+}
diff --git a/plugins/PubSubHubBub/publisher.php b/plugins/PubSubHubBub/publisher.php
new file mode 100644
index 000000000..f176a9b8a
--- /dev/null
+++ b/plugins/PubSubHubBub/publisher.php
@@ -0,0 +1,86 @@
+<?php
+
+// a PHP client library for pubsubhubbub
+// as defined at http://code.google.com/p/pubsubhubbub/
+// written by Josh Fraser | joshfraser.com | josh@eventvue.com
+// Released under Apache License 2.0
+
+class Publisher {
+
+ protected $hub_url;
+ protected $last_response;
+
+ // create a new Publisher
+ public function __construct($hub_url) {
+
+ if (!isset($hub_url))
+ throw new Exception('Please specify a hub url');
+
+ if (!preg_match("|^https?://|i",$hub_url))
+ throw new Exception('The specified hub url does not appear to be valid: '.$hub_url);
+
+ $this->hub_url = $hub_url;
+ }
+
+ // accepts either a single url or an array of urls
+ public function publish_update($topic_urls, $http_function = false) {
+ if (!isset($topic_urls))
+ throw new Exception('Please specify a topic url');
+
+ // check that we're working with an array
+ if (!is_array($topic_urls)) {
+ $topic_urls = array($topic_urls);
+ }
+
+ // set the mode to publish
+ $post_string = "hub.mode=publish";
+ // loop through each topic url
+ foreach ($topic_urls as $topic_url) {
+
+ // lightweight check that we're actually working w/ a valid url
+ if (!preg_match("|^https?://|i",$topic_url))
+ throw new Exception('The specified topic url does not appear to be valid: '.$topic_url);
+
+ // append the topic url parameters
+ $post_string .= "&hub.url=".urlencode($topic_url);
+ }
+
+ // make the http post request and return true/false
+ // easy to over-write to use your own http function
+ if ($http_function)
+ return $http_function($this->hub_url,$post_string);
+ else
+ return $this->http_post($this->hub_url,$post_string);
+ }
+
+ // returns any error message from the latest request
+ public function last_response() {
+ return $this->last_response;
+ }
+
+ // default http function that uses curl to post to the hub endpoint
+ private function http_post($url, $post_string) {
+
+ // add any additional curl options here
+ $options = array(CURLOPT_URL => $url,
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => $post_string,
+ CURLOPT_USERAGENT => "PubSubHubbub-Publisher-PHP/1.0");
+
+ $ch = curl_init();
+ curl_setopt_array($ch, $options);
+
+ $response = curl_exec($ch);
+ $this->last_response = $response;
+ $info = curl_getinfo($ch);
+
+ curl_close($ch);
+
+ // all good
+ if ($info['http_code'] == 204)
+ return true;
+ return false;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/plugins/Realtime/RealtimePlugin.php b/plugins/Realtime/RealtimePlugin.php
index 82eca3d08..e30c41156 100644
--- a/plugins/Realtime/RealtimePlugin.php
+++ b/plugins/Realtime/RealtimePlugin.php
@@ -50,6 +50,11 @@ class RealtimePlugin extends Plugin
protected $favorurl = null;
protected $deleteurl = null;
+ /**
+ * When it's time to initialize the plugin, calculate and
+ * pass the URLs we need.
+ */
+
function onInitializePlugin()
{
$this->replyurl = common_local_url('newnotice');
@@ -57,29 +62,26 @@ class RealtimePlugin extends Plugin
// FIXME: need to find a better way to pass this pattern in
$this->deleteurl = common_local_url('deletenotice',
array('notice' => '0000000000'));
+ return true;
}
function onEndShowScripts($action)
{
- $path = null;
+ $timeline = $this->_getTimeline($action);
- switch ($action->trimmed('action')) {
- case 'public':
- $path = array('public');
- break;
- case 'tag':
- $tag = $action->trimmed('tag');
- if (!empty($tag)) {
- $path = array('tag', $tag);
- } else {
- return true;
- }
- break;
- default:
+ // If there's not a timeline on this page,
+ // just return true
+
+ if (empty($timeline)) {
return true;
}
- $timeline = $this->_pathToChannel($path);
+ $base = $action->selfUrl();
+ if (mb_strstr($base, '?')) {
+ $url = $base . '&realtime=1';
+ } else {
+ $url = $base . '?realtime=1';
+ }
$scripts = $this->_getScripts();
@@ -95,10 +97,22 @@ class RealtimePlugin extends Plugin
$user_id = 0;
}
+ if ($action->boolean('realtime')) {
+ $realtimeUI = ' RealtimeUpdate.initPopupWindow();';
+ }
+ else {
+ $iconurl = common_path('plugins/Realtime/icon_external.gif');
+ $realtimeUI = ' RealtimeUpdate.addPopup("'.$url.'", "'.$timeline.'", "'. $iconurl .'");';
+ }
+
$action->elementStart('script', array('type' => 'text/javascript'));
- $action->raw("$(document).ready(function() { ");
- $action->raw($this->_updateInitialize($timeline, $user_id));
- $action->raw(" });");
+
+ $script = ' $(document).ready(function() { '.
+ $realtimeUI.
+ $this->_updateInitialize($timeline, $user_id).
+ '}); ';
+ $action->raw($script);
+
$action->elementEnd('script');
return true;
@@ -108,13 +122,23 @@ class RealtimePlugin extends Plugin
{
$paths = array();
- // XXX: Add other timelines; this is just for the public one
+ // Add to the author's timeline
+
+ $user = User::staticGet('id', $notice->profile_id);
+
+ if (!empty($user)) {
+ $paths[] = array('showstream', $user->nickname);
+ }
+
+ // Add to the public timeline
if ($notice->is_local ||
($notice->is_local == 0 && !common_config('public', 'localonly'))) {
$paths[] = array('public');
}
+ // Add to the tags timeline
+
$tags = $this->getNoticeTags($notice);
if (!empty($tags)) {
@@ -123,6 +147,46 @@ class RealtimePlugin extends Plugin
}
}
+ // Add to inbox timelines
+ // XXX: do a join
+
+ $inbox = new Notice_inbox();
+ $inbox->notice_id = $notice->id;
+
+ if ($inbox->find()) {
+ while ($inbox->fetch()) {
+ $user = User::staticGet('id', $inbox->user_id);
+ $paths[] = array('all', $user->nickname);
+ }
+ }
+
+ // Add to the replies timeline
+
+ $reply = new Reply();
+ $reply->notice_id = $notice->id;
+
+ if ($reply->find()) {
+ while ($reply->fetch()) {
+ $user = User::staticGet('id', $reply->profile_id);
+ if (!empty($user)) {
+ $paths[] = array('replies', $user->nickname);
+ }
+ }
+ }
+
+ // Add to the group timeline
+ // XXX: join
+
+ $gi = new Group_inbox();
+ $gi->notice_id = $notice->id;
+
+ if ($gi->find()) {
+ while ($gi->fetch()) {
+ $ug = User_group::staticGet('id', $gi->group_id);
+ $paths[] = array('showgroup', $ug->nickname);
+ }
+ }
+
if (count($paths) > 0) {
$json = $this->noticeAsJson($notice);
@@ -140,6 +204,39 @@ class RealtimePlugin extends Plugin
return true;
}
+ function onStartShowBody($action)
+ {
+ $realtime = $action->boolean('realtime');
+ if (!$realtime) {
+ return true;
+ }
+
+ $action->elementStart('body',
+ (common_current_user()) ? array('id' => $action->trimmed('action'),
+ 'class' => 'user_in')
+ : array('id' => $action->trimmed('action')));
+
+ $action->elementStart('div', array('id' => 'header'));
+
+ // XXX hack to deal with JS that tries to get the
+ // root url from page output
+
+ $action->elementStart('address');
+ $action->element('a', array('class' => 'url',
+ 'href' => common_local_url('public')),
+ '');
+ $action->elementEnd('address');
+
+ if (common_logged_in()) {
+ $action->showNoticeForm();
+ }
+ $action->elementEnd('div');
+
+ $action->showContentBlock();
+ $action->elementEnd('body');
+ return false; // No default processing
+ }
+
function noticeAsJson($notice)
{
// FIXME: this code should be abstracted to a neutral third
@@ -224,4 +321,41 @@ class RealtimePlugin extends Plugin
{
return '';
}
+
+ function _getTimeline($action)
+ {
+ $path = null;
+ $timeline = null;
+
+ $action_name = $action->trimmed('action');
+
+ switch ($action_name) {
+ case 'public':
+ $path = array('public');
+ break;
+ case 'tag':
+ $tag = $action->trimmed('tag');
+ if (!empty($tag)) {
+ $path = array('tag', $tag);
+ }
+ break;
+ case 'showstream':
+ case 'all':
+ case 'replies':
+ case 'showgroup':
+ $nickname = common_canonical_nickname($action->trimmed('nickname'));
+ if (!empty($nickname)) {
+ $path = array($action_name, $nickname);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!empty($path)) {
+ $timeline = $this->_pathToChannel($path);
+ }
+
+ return $timeline;
+ }
}
diff --git a/plugins/Realtime/icon_external.gif b/plugins/Realtime/icon_external.gif
new file mode 100644
index 000000000..c4118d53b
--- /dev/null
+++ b/plugins/Realtime/icon_external.gif
Binary files differ
diff --git a/plugins/Realtime/jquery.getUrlParam.js b/plugins/Realtime/jquery.getUrlParam.js
new file mode 100644
index 000000000..e8f73eb47
--- /dev/null
+++ b/plugins/Realtime/jquery.getUrlParam.js
@@ -0,0 +1,72 @@
+/* Copyright (c) 2006-2007 Mathias Bank (http://www.mathias-bank.de)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * Version 2.1
+ *
+ * Thanks to
+ * Hinnerk Ruemenapf - http://hinnerk.ruemenapf.de/ for bug reporting and fixing.
+ * Tom Leonard for some improvements
+ *
+ */
+jQuery.fn.extend({
+/**
+* Returns get parameters.
+*
+* If the desired param does not exist, null will be returned
+*
+* To get the document params:
+* @example value = $(document).getUrlParam("paramName");
+*
+* To get the params of a html-attribut (uses src attribute)
+* @example value = $('#imgLink').getUrlParam("paramName");
+*/
+ getUrlParam: function(strParamName){
+ strParamName = escape(unescape(strParamName));
+
+ var returnVal = new Array();
+ var qString = null;
+
+ if ($(this).attr("nodeName")=="#document") {
+ //document-handler
+
+ if (window.location.search.search(strParamName) > -1 ){
+
+ qString = window.location.search.substr(1,window.location.search.length).split("&");
+ }
+
+ } else if ($(this).attr("src")!="undefined") {
+
+ var strHref = $(this).attr("src")
+ if ( strHref.indexOf("?") > -1 ){
+ var strQueryString = strHref.substr(strHref.indexOf("?")+1);
+ qString = strQueryString.split("&");
+ }
+ } else if ($(this).attr("href")!="undefined") {
+
+ var strHref = $(this).attr("href")
+ if ( strHref.indexOf("?") > -1 ){
+ var strQueryString = strHref.substr(strHref.indexOf("?")+1);
+ qString = strQueryString.split("&");
+ }
+ } else {
+ return null;
+ }
+
+
+ if (qString==null) return null;
+
+
+ for (var i=0;i<qString.length; i++){
+ if (escape(unescape(qString[i].split("=")[0])) == strParamName){
+ returnVal.push(qString[i].split("=")[1]);
+ }
+
+ }
+
+
+ if (returnVal.length==0) return null;
+ else if (returnVal.length==1) return returnVal[0];
+ else return returnVal;
+ }
+}); \ No newline at end of file
diff --git a/plugins/Realtime/realtimeupdate.js b/plugins/Realtime/realtimeupdate.js
index d55db5859..57fe0a843 100644
--- a/plugins/Realtime/realtimeupdate.js
+++ b/plugins/Realtime/realtimeupdate.js
@@ -1,8 +1,8 @@
// add a notice encoded as JSON into the current timeline
//
+// TODO: i18n
RealtimeUpdate = {
-
_userid: 0,
_replyurl: '',
_favorurl: '',
@@ -10,10 +10,10 @@ RealtimeUpdate = {
init: function(userid, replyurl, favorurl, deleteurl)
{
- RealtimeUpdate._userid = userid;
- RealtimeUpdate._replyurl = replyurl;
- RealtimeUpdate._favorurl = favorurl;
- RealtimeUpdate._deleteurl = deleteurl;
+ RealtimeUpdate._userid = userid;
+ RealtimeUpdate._replyurl = replyurl;
+ RealtimeUpdate._favorurl = favorurl;
+ RealtimeUpdate._deleteurl = deleteurl;
},
receive: function(data)
@@ -21,7 +21,7 @@ RealtimeUpdate = {
id = data.id;
// Don't add it if it already exists
-
+ //
if ($("#notice-"+id).length > 0) {
return;
}
@@ -50,30 +50,19 @@ RealtimeUpdate = {
"<p class=\"entry-content\">"+html+"</p>"+
"</div>"+
"<div class=\"entry-content\">"+
- "<dl class=\"timestamp\">"+
- "<dt>Published</dt>"+
- "<dd>"+
- "<a rel=\"bookmark\" href=\""+data['url']+"\" >"+
+ "<a class=\"timestamp\" rel=\"bookmark\" href=\""+data['url']+"\" >"+
"<abbr class=\"published\" title=\""+data['created_at']+"\">a few seconds ago</abbr>"+
"</a> "+
- "</dd>"+
- "</dl>"+
- "<dl class=\"device\">"+
- "<dt>From</dt> "+
- "<dd>"+source+"</dd>"+ // may have a link, I think
- "</dl>";
-
+ "<span class=\"source\">"+
+ "from "+
+ "<span class=\"device\">"+source+"</span>"+ // may have a link
+ "</span>";
if (data['in_reply_to_status_id']) {
- ni = ni+" <dl class=\"response\">"+
- "<dt>To</dt>"+
- "<dd>"+
- "<a href=\""+data['in_reply_to_status_url']+"\" rel=\"in-reply-to\">in reply to</a>"+
- "</dd>"+
- "</dl>";
+ ni = ni+" <a class=\"response\" href=\""+data['in_reply_to_status_url']+"\">in context</a>";
}
ni = ni+"</div>"+
- "<div class=\"notice-options\">";
+ "<div class=\"notice-options\">";
if (RealtimeUpdate._userid != 0) {
var input = $("form#form_notice fieldset input#token");
@@ -95,12 +84,12 @@ RealtimeUpdate = {
var ff;
ff = "<form id=\"favor-"+id+"\" class=\"form_favor\" method=\"post\" action=\""+RealtimeUpdate._favorurl+"\">"+
- "<fieldset>"+
- "<legend>Favor this notice</legend>"+ // XXX: i18n
+ "<fieldset>"+
+ "<legend>Favor this notice</legend>"+
"<input name=\"token-"+id+"\" type=\"hidden\" id=\"token-"+id+"\" value=\""+session_key+"\"/>"+
"<input name=\"notice\" type=\"hidden\" id=\"notice-n"+id+"\" value=\""+id+"\"/>"+
"<input type=\"submit\" id=\"favor-submit-"+id+"\" name=\"favor-submit-"+id+"\" class=\"submit\" value=\"Favor\" title=\"Favor this notice\"/>"+
- "</fieldset>"+
+ "</fieldset>"+
"</form>";
return ff;
},
@@ -108,28 +97,51 @@ RealtimeUpdate = {
makeReplyLink: function(id, nickname)
{
var rl;
- rl = "<dl class=\"notice_reply\">"+
- "<dt>Reply to this notice</dt>"+
- "<dd>"+
- "<a href=\""+RealtimeUpdate._replyurl+"?replyto="+nickname+"\" title=\"Reply to this notice\">Reply <span class=\"notice_id\">"+id+"</span>"+
- "</a>"+
- "</dd>"+
- "</dl>";
+ rl = "<a class=\"notice_reply\" href=\""+RealtimeUpdate._replyurl+"?replyto="+nickname+"\" title=\"Reply to this notice\">Reply <span class=\"notice_id\">"+id+"</span></a>";
return rl;
- },
+ },
makeDeleteLink: function(id)
{
var dl, delurl;
delurl = RealtimeUpdate._deleteurl.replace("0000000000", id);
- dl = "<dl class=\"notice_delete\">"+
- "<dt>Delete this notice</dt>"+
- "<dd>"+
- "<a href=\""+delurl+"\" title=\"Delete this notice\">Delete</a>"+
- "</dd>"+
- "</dl>";
+ dl = "<a class=\"notice_delete\" href=\""+delurl+"\" title=\"Delete this notice\">Delete</a>";
return dl;
},
+
+ addPopup: function(url, timeline, iconurl)
+ {
+ $('#site_nav_local_views .current a').append('<button id="realtime_timeline" title="Real-time pop window">&#8599;</button>');
+
+ $('#realtime_timeline').css({
+ 'margin':'2px 0 0 11px',
+ 'background':'transparent url('+ iconurl + ') no-repeat 45% 45%',
+ 'text-indent':'-9999px',
+ 'width':'16px',
+ 'height':'16px',
+ 'padding':'0',
+ 'display':'block',
+ 'float':'right',
+ 'border':'none',
+ 'cursor':'pointer'
+ });
+
+ $('#realtime_timeline').click(function() {
+ window.open(url,
+ timeline,
+ 'toolbar=no,resizable=yes,scrollbars=yes,status=yes');
+
+ return false;
+ });
+ },
+
+ initPopupWindow: function()
+ {
+ window.resizeTo(575, 640);
+ $('address').hide();
+ $('#content').css({'width':'92%'});
+ }
}
+
diff --git a/plugins/SimpleUrl/SimpleUrlPlugin.php b/plugins/SimpleUrl/SimpleUrlPlugin.php
new file mode 100644
index 000000000..82d772048
--- /dev/null
+++ b/plugins/SimpleUrl/SimpleUrlPlugin.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+class SimpleUrlPlugin extends Plugin
+{
+ function __construct()
+ {
+ parent::__construct();
+ }
+
+ function onInitializePlugin(){
+ $this->registerUrlShortener(
+ 'is.gd',
+ array(),
+ array('SimpleUrl',array('http://is.gd/api.php?longurl='))
+ );
+ $this->registerUrlShortener(
+ 'snipr.com',
+ array(),
+ array('SimpleUrl',array('http://snipr.com/site/snip?r=simple&link='))
+ );
+ $this->registerUrlShortener(
+ 'metamark.net',
+ array(),
+ array('SimpleUrl',array('http://metamark.net/api/rest/simple?long_url='))
+ );
+ $this->registerUrlShortener(
+ 'tinyurl.com',
+ array(),
+ array('SimpleUrl',array('http://tinyurl.com/api-create.php?url='))
+ );
+ }
+}
+
+class SimpleUrl extends ShortUrlApi
+{
+ protected function shorten_imp($url) {
+ $curlh = curl_init();
+ curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
+ curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet');
+ curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
+
+ curl_setopt($curlh, CURLOPT_URL, $this->service_url.urlencode($url));
+ $short_url = curl_exec($curlh);
+
+ curl_close($curlh);
+ return $short_url;
+ }
+}
diff --git a/plugins/TightUrl/TightUrlPlugin.php b/plugins/TightUrl/TightUrlPlugin.php
new file mode 100644
index 000000000..48efb355f
--- /dev/null
+++ b/plugins/TightUrl/TightUrlPlugin.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to push RSS/Atom updates to a PubSubHubBub hub
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Craig Andrews http://candrews.integralblue.com
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+class TightUrlPlugin extends Plugin
+{
+ function __construct()
+ {
+ parent::__construct();
+ }
+
+ function onInitializePlugin(){
+ $this->registerUrlShortener(
+ '2tu.us',
+ array('freeService'=>true),
+ array('TightUrl',array('http://2tu.us/?save=y&url='))
+ );
+ }
+}
+
+class TightUrl extends ShortUrlApi
+{
+ protected function shorten_imp($url) {
+ $response = $this->http_get($url);
+ if (!$response) return $url;
+ $response = $this->tidy($response);
+ $y = @simplexml_load_string($response);
+ if (!isset($y->body)) return $url;
+ $xml = $y->body->p[0]->code[0]->a->attributes();
+ if (isset($xml['href'])) return $xml['href'];
+ return $url;
+ }
+}
diff --git a/plugins/recaptcha/README b/plugins/recaptcha/README
index ce23a2695..b996f96cc 100644
--- a/plugins/recaptcha/README
+++ b/plugins/recaptcha/README
@@ -6,7 +6,7 @@ Use:
1. Get an API key from http://recaptcha.net
2. In config.php add:
-include_once('plugins/recaptcha.php');
+include_once('plugins/recaptcha/recaptcha.php');
$captcha = new recaptcha(publickey, privatekey, showErrors);
Changelog
diff --git a/scripts/getvaliddaemons.php b/scripts/getvaliddaemons.php
index 8f48e8e6f..6dd019712 100755
--- a/scripts/getvaliddaemons.php
+++ b/scripts/getvaliddaemons.php
@@ -35,20 +35,36 @@ ENDOFHELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
+$daemons = array();
+
+$daemons[] = INSTALLDIR.'/scripts/pluginqueuehandler.php';
+$daemons[] = INSTALLDIR.'/scripts/ombqueuehandler.php';
+$daemons[] = INSTALLDIR.'/scripts/facebookqueuehandler.php';
+$daemons[] = INSTALLDIR.'/scripts/pingqueuehandler.php';
+
if(common_config('xmpp','enabled')) {
- echo "xmppdaemon.php jabberqueuehandler.php publicqueuehandler.php ";
- echo "xmppconfirmhandler.php ";
+ $daemons[] = INSTALLDIR.'/scripts/xmppdaemon.php';
+ $daemons[] = INSTALLDIR.'/scripts/jabberqueuehandler.php';
+ $daemons[] = INSTALLDIR.'/scripts/publicqueuehandler.php';
+ $daemons[] = INSTALLDIR.'/scripts/xmppconfirmhandler.php';
}
+
if(common_config('twitterbridge','enabled')) {
- echo "twitterstatusfetcher.php ";
+ $daemons[] = INSTALLDIR.'/scripts/twitterstatusfetcher.php';
}
-echo "ombqueuehandler.php ";
+
if (common_config('twitter', 'enabled')) {
- echo "twitterqueuehandler.php ";
- echo "synctwitterfriends.php ";
+ $daemons[] = INSTALLDIR.'/scripts/twitterqueuehandler.php';
+ $daemons[] = INSTALLDIR.'/scripts/synctwitterfriends.php';
}
-echo "facebookqueuehandler.php ";
-echo "pingqueuehandler.php ";
+
if (common_config('sms', 'enabled')) {
- echo "smsqueuehandler.php ";
+ $daemons[] = INSTALLDIR.'/scripts/smsqueuehandler.php';
+}
+
+if (Event::handle('GetValidDaemons', array(&$daemons))) {
+ foreach ($daemons as $daemon) {
+ print $daemon . ' ';
+ }
+ print "\n";
}
diff --git a/scripts/maildaemon.php b/scripts/maildaemon.php
index 11911dcbd..5705cfd50 100755
--- a/scripts/maildaemon.php
+++ b/scripts/maildaemon.php
@@ -66,9 +66,10 @@ class MailerDaemon
}
$msg = $this->cleanup_msg($msg);
$msg = common_shorten_links($msg);
- if (mb_strlen($msg) > 140) {
- $this->error($from,_('That\'s too long. '.
- 'Max notice size is 140 chars.'));
+ if (Notice::contentTooLong($msg)) {
+ $this->error($from, sprintf(_('That\'s too long. '.
+ 'Max notice size is %d chars.'),
+ Notice::maxContent()));
}
$fileRecords = array();
foreach($attachments as $attachment){
@@ -78,9 +79,9 @@ class MailerDaemon
die('error() should trigger an exception before reaching here.');
}
$filename = $this->saveFile($user, $attachment,$mimetype);
-
+
fclose($attachment);
-
+
if (empty($filename)) {
$this->error($from,_('Couldn\'t save file.'));
}
@@ -96,9 +97,10 @@ class MailerDaemon
$short_fileurl = common_shorten_url($fileurl);
$msg .= ' ' . $short_fileurl;
- if (mb_strlen($msg) > 140) {
+ if (Notice::contentTooLong($msg)) {
$this->deleteFile($filename);
- $this->error($from,_('Max notice size is 140 chars, including attachment URL.'));
+ $this->error($from, sprintf(_('Max notice size is %d chars, including attachment URL.'),
+ Notice::maxContent()));
}
// Also, not sure this is necessary -- Zach
@@ -123,7 +125,7 @@ class MailerDaemon
$stream = stream_get_meta_data($attachment);
if (copy($stream['uri'], $filepath) && chmod($filepath,0664)) {
return $filename;
- } else {
+ } else {
$this->error(null,_('File could not be moved to destination directory.' . $stream['uri'] . ' ' . $filepath));
}
}
@@ -152,7 +154,7 @@ class MailerDaemon
}
function maybeAddRedir($file_id, $url)
- {
+ {
$file_redir = File_redirection::staticGet('url', $url);
if (empty($file_redir)) {
@@ -273,7 +275,7 @@ class MailerDaemon
}
function attachFile($notice, $filerec)
- {
+ {
File_to_post::processNew($filerec->id, $notice->id);
$this->maybeAddRedir($filerec->id,
diff --git a/scripts/ombqueuehandler.php b/scripts/ombqueuehandler.php
index 8e685f1c8..be33b9821 100755
--- a/scripts/ombqueuehandler.php
+++ b/scripts/ombqueuehandler.php
@@ -57,7 +57,7 @@ class OmbQueueHandler extends QueueHandler
$this->log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id);
return true;
} else {
- return omb_broadcast_remote_subscribers($notice);
+ return omb_broadcast_notice($notice);
}
}
diff --git a/scripts/pluginqueuehandler.php b/scripts/pluginqueuehandler.php
new file mode 100755
index 000000000..ae807db6a
--- /dev/null
+++ b/scripts/pluginqueuehandler.php
@@ -0,0 +1,58 @@
+#!/usr/bin/env php
+<?php
+/*
+ * 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/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_OMB_HELP
+Daemon script for letting plugins handle stuff at queue time
+
+ -i --id Identity (default none)
+
+END_OF_OMB_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+require_once INSTALLDIR . '/lib/queuehandler.php';
+
+class PluginQueueHandler extends QueueHandler
+{
+
+ function transport()
+ {
+ return 'plugin';
+ }
+
+ function handle_notice($notice)
+ {
+ Event::handle('HandleQueuedNotice', array(&$notice));
+ return true;
+ }
+}
+
+if (have_option('i', 'id')) {
+ $id = get_option_value('i', 'id');
+} else {
+ $id = null;
+}
+
+$handler = new PluginQueueHandler($id);
+$handler->runOnce();
diff --git a/scripts/startdaemons.sh b/scripts/startdaemons.sh
index 298162673..5fb75414d 100755
--- a/scripts/startdaemons.sh
+++ b/scripts/startdaemons.sh
@@ -40,7 +40,7 @@ DAEMONS=`php $DIR/getvaliddaemons.php $ARGSG`
for f in $DAEMONS; do
printf "Starting $f...";
- php $DIR/$f $ARGSD
+ php $f $ARGSD
printf "DONE.\n"
done
diff --git a/scripts/synctwitterfriends.php b/scripts/synctwitterfriends.php
index 2cb7525ea..b30e700a1 100755
--- a/scripts/synctwitterfriends.php
+++ b/scripts/synctwitterfriends.php
@@ -19,8 +19,6 @@
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('STATUSNET', true);
-define('LACONICA', true); // compatibility
$shortoptions = 'di::';
$longoptions = array('id::', 'debug');
diff --git a/scripts/twitterqueuehandler.php b/scripts/twitterqueuehandler.php
index 992141f9d..ce4d824d0 100755
--- a/scripts/twitterqueuehandler.php
+++ b/scripts/twitterqueuehandler.php
@@ -19,8 +19,6 @@
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('STATUSNET', true);
-define('LACONICA', true); // compatibility
$shortoptions = 'i::';
$longoptions = array('id::');
diff --git a/scripts/twitterstatusfetcher.php b/scripts/twitterstatusfetcher.php
index 6dca6f75b..3cdf1867a 100755
--- a/scripts/twitterstatusfetcher.php
+++ b/scripts/twitterstatusfetcher.php
@@ -19,8 +19,6 @@
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('STATUSNET', true);
-define('LACONICA', true); // compatibility
// Tune number of processes and how often to poll Twitter
// XXX: Should these things be in config.php?
diff --git a/scripts/xmppdaemon.php b/scripts/xmppdaemon.php
index 9e621e725..1b1aec3e6 100755
--- a/scripts/xmppdaemon.php
+++ b/scripts/xmppdaemon.php
@@ -316,9 +316,11 @@ class XMPPDaemon extends Daemon
{
$body = trim($pl['body']);
$content_shortened = common_shorten_links($body);
- if (mb_strlen($content_shortened) > 140) {
+ if (Notice::contentTooLong($content_shortened)) {
$from = jabber_normalize_jid($pl['from']);
- $this->from_site($from, "Message too long - maximum is 140 characters, you sent ".mb_strlen($content_shortened));
+ $this->from_site($from, sprintf(_("Message too long - maximum is %d characters, you sent %d"),
+ Notice::maxContent(),
+ mb_strlen($content_shortened)));
return;
}
$notice = Notice::saveNew($user->id, $content_shortened, 'xmpp');
diff --git a/tests/URLDetectionTest.php b/tests/URLDetectionTest.php
index 767f895bb..a7cdcaa24 100644
--- a/tests/URLDetectionTest.php
+++ b/tests/URLDetectionTest.php
@@ -25,48 +25,72 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase
static public function provider()
{
return array(
+ array('not a link :: no way',
+ 'not a link :: no way'),
+ array('link http://www.somesite.com/xyz/35637563@N00/52803365/ link',
+ 'link <a href="http://www.somesite.com/xyz/35637563@N00/52803365/" title="http://www.somesite.com/xyz/35637563@N00/52803365/" rel="external">http://www.somesite.com/xyz/35637563@N00/52803365/</a> link'),
array('http://127.0.0.1',
- '<a href="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'),
+ '<a href="http://127.0.0.1/" title="http://127.0.0.1" rel="external">http://127.0.0.1</a>'),
array('127.0.0.1',
- '<a href="http://127.0.0.1/" rel="external">127.0.0.1</a>'),
+ '<a href="http://127.0.0.1/" title="http://127.0.0.1" rel="external">127.0.0.1</a>'),
array('127.0.0.1:99',
- '<a href="http://127.0.0.1:99/" rel="external">127.0.0.1:99</a>'),
- array('127.0.0.1/test.php',
- '<a href="http://127.0.0.1/test.php" rel="external">127.0.0.1/test.php</a>'),
+ '<a href="http://127.0.0.1:99/" title="http://127.0.0.1:99" rel="external">127.0.0.1:99</a>'),
+ array('127.0.0.1/Name:test.php',
+ '<a href="http://127.0.0.1/Name:test.php" title="http://127.0.0.1/Name:test.php" rel="external">127.0.0.1/Name:test.php</a>'),
+ array('127.0.0.1/~test',
+ '<a href="http://127.0.0.1/~test" title="http://127.0.0.1/~test" rel="external">127.0.0.1/~test</a>'),
+ array('127.0.0.1/+test',
+ '<a href="http://127.0.0.1/+test" title="http://127.0.0.1/+test" rel="external">127.0.0.1/+test</a>'),
+ array('127.0.0.1/$test',
+ '<a href="http://127.0.0.1/$test" title="http://127.0.0.1/$test" rel="external">127.0.0.1/$test</a>'),
+ array('127.0.0.1/\'test',
+ '<a href="http://127.0.0.1/\'test" title="http://127.0.0.1/\'test" rel="external">127.0.0.1/\'test</a>'),
+ array('127.0.0.1/"test',
+ '<a href="http://127.0.0.1/&quot;test" title="http://127.0.0.1/&quot;test" rel="external">127.0.0.1/&quot;test</a>'),
+ array('127.0.0.1/-test',
+ '<a href="http://127.0.0.1/-test" title="http://127.0.0.1/-test" rel="external">127.0.0.1/-test</a>'),
+ array('127.0.0.1/_test',
+ '<a href="http://127.0.0.1/_test" title="http://127.0.0.1/_test" rel="external">127.0.0.1/_test</a>'),
+ array('127.0.0.1/!test',
+ '<a href="http://127.0.0.1/!test" title="http://127.0.0.1/!test" rel="external">127.0.0.1/!test</a>'),
+ array('127.0.0.1/*test',
+ '<a href="http://127.0.0.1/*test" title="http://127.0.0.1/*test" rel="external">127.0.0.1/*test</a>'),
+ array('127.0.0.1/test%20stuff',
+ '<a href="http://127.0.0.1/test%20stuff" title="http://127.0.0.1/test%20stuff" rel="external">127.0.0.1/test%20stuff</a>'),
array('http://[::1]:99/test.php',
- '<a href="http://[::1]:99/test.php" rel="external">http://[::1]:99/test.php</a>'),
+ '<a href="http://[::1]:99/test.php" title="http://[::1]:99/test.php" rel="external">http://[::1]:99/test.php</a>'),
array('http://::1/test.php',
- '<a href="http://::1/test.php" rel="external">http://::1/test.php</a>'),
+ '<a href="http://::1/test.php" title="http://::1/test.php" rel="external">http://::1/test.php</a>'),
array('http://::1',
- '<a href="http://::1/" rel="external">http://::1</a>'),
+ '<a href="http://::1/" title="http://::1" rel="external">http://::1</a>'),
array('2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php',
- '<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php</a>'),
+ '<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" title="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php</a>'),
array('[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php',
- '<a href="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" rel="external">[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php</a>'),
+ '<a href="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" title="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" rel="external">[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php</a>'),
array('2001:4978:1b5:0:21d:e0ff:fe66:59ab',
- '<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab</a>'),
+ '<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/" title="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab</a>'),
array('http://127.0.0.1',
- '<a href="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'),
+ '<a href="http://127.0.0.1/" title="http://127.0.0.1" rel="external">http://127.0.0.1</a>'),
array('example.com',
- '<a href="http://example.com/" rel="external">example.com</a>'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>'),
array('example.com',
- '<a href="http://example.com/" rel="external">example.com</a>'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>'),
array('http://example.com',
- '<a href="http://example.com/" rel="external">http://example.com</a>'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>'),
array('http://example.com.',
- '<a href="http://example.com/" rel="external">http://example.com</a>.'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>.'),
array('/var/lib/example.so',
'/var/lib/example.so'),
array('example',
'example'),
array('user@example.com',
- '<a href="mailto:user@example.com" rel="external">user@example.com</a>'),
+ '<a href="mailto:user@example.com" title="mailto:user@example.com" rel="external">user@example.com</a>'),
array('user_name+other@example.com',
- '<a href="mailto:user_name+other@example.com" rel="external">user_name+other@example.com</a>'),
+ '<a href="mailto:user_name+other@example.com" title="mailto:user_name+other@example.com" rel="external">user_name+other@example.com</a>'),
array('mailto:user@example.com',
- '<a href="mailto:user@example.com" rel="external">mailto:user@example.com</a>'),
+ '<a href="mailto:user@example.com" title="mailto:user@example.com" rel="external">mailto:user@example.com</a>'),
array('mailto:user@example.com?subject=test',
- '<a href="mailto:user@example.com?subject=test" rel="external">mailto:user@example.com?subject=test</a>'),
+ '<a href="mailto:user@example.com?subject=test" title="mailto:user@example.com?subject=test" rel="external">mailto:user@example.com?subject=test</a>'),
array('#example',
'#<span class="tag"><a href="' . common_local_url('tag', array('tag' => common_canonical_tag('example'))) . '" rel="tag">example</a></span>'),
array('#example.com',
@@ -74,165 +98,165 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase
array('#.net',
'#<span class="tag"><a href="' . common_local_url('tag', array('tag' => common_canonical_tag('.net'))) . '" rel="tag">.net</a></span>'),
array('http://example',
- '<a href="http://example/" rel="external">http://example</a>'),
+ '<a href="http://example/" title="http://example" rel="external">http://example</a>'),
array('http://3xampl3',
- '<a href="http://3xampl3/" rel="external">http://3xampl3</a>'),
+ '<a href="http://3xampl3/" title="http://3xampl3" rel="external">http://3xampl3</a>'),
array('http://example/',
- '<a href="http://example/" rel="external">http://example/</a>'),
+ '<a href="http://example/" title="http://example/" rel="external">http://example/</a>'),
array('http://example/path',
- '<a href="http://example/path" rel="external">http://example/path</a>'),
+ '<a href="http://example/path" title="http://example/path" rel="external">http://example/path</a>'),
array('http://example.com',
- '<a href="http://example.com/" rel="external">http://example.com</a>'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>'),
array('https://example.com',
- '<a href="https://example.com/" rel="external">https://example.com</a>'),
+ '<a href="https://example.com/" title="https://example.com" rel="external">https://example.com</a>'),
array('ftp://example.com',
- '<a href="ftp://example.com/" rel="external">ftp://example.com</a>'),
+ '<a href="ftp://example.com/" title="ftp://example.com" rel="external">ftp://example.com</a>'),
array('ftps://example.com',
- '<a href="ftps://example.com/" rel="external">ftps://example.com</a>'),
+ '<a href="ftps://example.com/" title="ftps://example.com" rel="external">ftps://example.com</a>'),
array('http://user@example.com',
- '<a href="http://user@example.com/" rel="external">http://user@example.com</a>'),
+ '<a href="http://user@example.com/" title="http://user@example.com" rel="external">http://user@example.com</a>'),
array('http://user:pass@example.com',
- '<a href="http://user:pass@example.com/" rel="external">http://user:pass@example.com</a>'),
+ '<a href="http://user:pass@example.com/" title="http://user:pass@example.com" rel="external">http://user:pass@example.com</a>'),
array('http://example.com:8080',
- '<a href="http://example.com:8080/" rel="external">http://example.com:8080</a>'),
+ '<a href="http://example.com:8080/" title="http://example.com:8080" rel="external">http://example.com:8080</a>'),
array('http://example.com:8080/test.php',
- '<a href="http://example.com:8080/test.php" rel="external">http://example.com:8080/test.php</a>'),
+ '<a href="http://example.com:8080/test.php" title="http://example.com:8080/test.php" rel="external">http://example.com:8080/test.php</a>'),
array('example.com:8080/test.php',
- '<a href="http://example.com:8080/test.php" rel="external">example.com:8080/test.php</a>'),
+ '<a href="http://example.com:8080/test.php" title="http://example.com:8080/test.php" rel="external">example.com:8080/test.php</a>'),
array('http://www.example.com',
- '<a href="http://www.example.com/" rel="external">http://www.example.com</a>'),
+ '<a href="http://www.example.com/" title="http://www.example.com" rel="external">http://www.example.com</a>'),
array('http://example.com/',
- '<a href="http://example.com/" rel="external">http://example.com/</a>'),
+ '<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com/</a>'),
array('http://example.com/path',
- '<a href="http://example.com/path" rel="external">http://example.com/path</a>'),
+ '<a href="http://example.com/path" title="http://example.com/path" rel="external">http://example.com/path</a>'),
array('http://example.com/path.html',
- '<a href="http://example.com/path.html" rel="external">http://example.com/path.html</a>'),
+ '<a href="http://example.com/path.html" title="http://example.com/path.html" rel="external">http://example.com/path.html</a>'),
array('http://example.com/path.html#fragment',
- '<a href="http://example.com/path.html#fragment" rel="external">http://example.com/path.html#fragment</a>'),
+ '<a href="http://example.com/path.html#fragment" title="http://example.com/path.html#fragment" rel="external">http://example.com/path.html#fragment</a>'),
array('http://example.com/path.php?foo=bar&bar=foo',
- '<a href="http://example.com/path.php?foo=bar&amp;bar=foo" rel="external">http://example.com/path.php?foo=bar&amp;bar=foo</a>'),
+ '<a href="http://example.com/path.php?foo=bar&amp;bar=foo" title="http://example.com/path.php?foo=bar&amp;bar=foo" rel="external">http://example.com/path.php?foo=bar&amp;bar=foo</a>'),
array('http://example.com.',
- '<a href="http://example.com/" rel="external">http://example.com</a>.'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>.'),
array('http://müllärör.de',
- '<a href="http://m&#xFC;ll&#xE4;r&#xF6;r.de/" rel="external">http://müllärör.de</a>'),
+ '<a href="http://m&#xFC;ll&#xE4;r&#xF6;r.de/" title="http://m&#xFC;ll&#xE4;r&#xF6;r.de" rel="external">http://müllärör.de</a>'),
array('http://ﺱﺲﺷ.com',
- '<a href="http://&#xFEB1;&#xFEB2;&#xFEB7;.com/" rel="external">http://ﺱﺲﺷ.com</a>'),
+ '<a href="http://&#xFEB1;&#xFEB2;&#xFEB7;.com/" title="http://&#xFEB1;&#xFEB2;&#xFEB7;.com" rel="external">http://ﺱﺲﺷ.com</a>'),
array('http://сделаткартинки.com',
- '<a href="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com/" rel="external">http://сделаткартинки.com</a>'),
+ '<a href="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com/" title="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com" rel="external">http://сделаткартинки.com</a>'),
array('http://tūdaliņ.lv',
- '<a href="http://t&#x16B;dali&#x146;.lv/" rel="external">http://tūdaliņ.lv</a>'),
+ '<a href="http://t&#x16B;dali&#x146;.lv/" title="http://t&#x16B;dali&#x146;.lv" rel="external">http://tūdaliņ.lv</a>'),
array('http://brændendekærlighed.com',
- '<a href="http://br&#xE6;ndendek&#xE6;rlighed.com/" rel="external">http://brændendekærlighed.com</a>'),
+ '<a href="http://br&#xE6;ndendek&#xE6;rlighed.com/" title="http://br&#xE6;ndendek&#xE6;rlighed.com" rel="external">http://brændendekærlighed.com</a>'),
array('http://あーるいん.com',
- '<a href="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com/" rel="external">http://あーるいん.com</a>'),
+ '<a href="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com/" title="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com" rel="external">http://あーるいん.com</a>'),
array('http://예비교사.com',
- '<a href="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com/" rel="external">http://예비교사.com</a>'),
+ '<a href="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com/" title="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com" rel="external">http://예비교사.com</a>'),
array('http://example.com.',
- '<a href="http://example.com/" rel="external">http://example.com</a>.'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>.'),
array('http://example.com?',
- '<a href="http://example.com/" rel="external">http://example.com</a>?'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>?'),
array('http://example.com!',
- '<a href="http://example.com/" rel="external">http://example.com</a>!'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>!'),
array('http://example.com,',
- '<a href="http://example.com/" rel="external">http://example.com</a>,'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>,'),
array('http://example.com;',
- '<a href="http://example.com/" rel="external">http://example.com</a>;'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>;'),
array('http://example.com:',
- '<a href="http://example.com/" rel="external">http://example.com</a>:'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>:'),
array('\'http://example.com\'',
- '\'<a href="http://example.com/" rel="external">http://example.com</a>\''),
+ '\'<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>\''),
array('"http://example.com"',
- '&quot;<a href="http://example.com/" rel="external">http://example.com</a>&quot;'),
+ '&quot;<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>&quot;'),
array('http://example.com',
- '<a href="http://example.com/" rel="external">http://example.com</a>'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>'),
array('(http://example.com)',
- '(<a href="http://example.com/" rel="external">http://example.com</a>)'),
+ '(<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>)'),
array('[http://example.com]',
- '[<a href="http://example.com/" rel="external">http://example.com</a>]'),
+ '[<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>]'),
array('<http://example.com>',
- '&lt;<a href="http://example.com/" rel="external">http://example.com</a>&gt;'),
+ '&lt;<a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a>&gt;'),
array('http://example.com/path/(foo)/bar',
- '<a href="http://example.com/path/(foo)/bar" rel="external">http://example.com/path/(foo)/bar</a>'),
+ '<a href="http://example.com/path/(foo)/bar" title="http://example.com/path/(foo)/bar" rel="external">http://example.com/path/(foo)/bar</a>'),
array('http://example.com/path/[foo]/bar',
- '<a href="http://example.com/path/[foo]/bar" rel="external">http://example.com/path/[foo]/bar</a>'),
+ '<a href="http://example.com/path/[foo]/bar" title="http://example.com/path/[foo]/bar" rel="external">http://example.com/path/[foo]/bar</a>'),
array('http://example.com/path/foo/(bar)',
- '<a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>'),
+ '<a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>'),
//Not a valid url - urls cannot contain unencoded square brackets
array('http://example.com/path/foo/[bar]',
- '<a href="http://example.com/path/foo/[bar]" rel="external">http://example.com/path/foo/[bar]</a>'),
+ '<a href="http://example.com/path/foo/[bar]" title="http://example.com/path/foo/[bar]" rel="external">http://example.com/path/foo/[bar]</a>'),
array('Hey, check out my cool site http://example.com okay?',
- 'Hey, check out my cool site <a href="http://example.com/" rel="external">http://example.com</a> okay?'),
+ 'Hey, check out my cool site <a href="http://example.com/" title="http://example.com" rel="external">http://example.com</a> okay?'),
array('What about parens (e.g. http://example.com/path/foo/(bar))?',
- 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)?'),
+ 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)?'),
array('What about parens (e.g. http://example.com/path/foo/(bar)?',
- 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>?'),
+ 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>?'),
array('What about parens (e.g. http://example.com/path/foo/(bar).)?',
- 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>.)?'),
+ 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>.)?'),
//Not a valid url - urls cannot contain unencoded commas
array('What about parens (e.g. http://example.com/path/(foo,bar)?',
- 'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" rel="external">http://example.com/path/(foo,bar)</a>?'),
+ 'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" title="http://example.com/path/(foo,bar)" rel="external">http://example.com/path/(foo,bar)</a>?'),
array('Unbalanced too (e.g. http://example.com/path/((((foo)/bar)?',
- 'Unbalanced too (e.g. <a href="http://example.com/path/((((foo)/bar)" rel="external">http://example.com/path/((((foo)/bar)</a>?'),
+ 'Unbalanced too (e.g. <a href="http://example.com/path/((((foo)/bar)" title="http://example.com/path/((((foo)/bar)" rel="external">http://example.com/path/((((foo)/bar)</a>?'),
array('Unbalanced too (e.g. http://example.com/path/(foo))))/bar)?',
- 'Unbalanced too (e.g. <a href="http://example.com/path/(foo))))/bar" rel="external">http://example.com/path/(foo))))/bar</a>)?'),
+ 'Unbalanced too (e.g. <a href="http://example.com/path/(foo))))/bar" title="http://example.com/path/(foo))))/bar" rel="external">http://example.com/path/(foo))))/bar</a>)?'),
array('Unbalanced too (e.g. http://example.com/path/foo/((((bar)?',
- 'Unbalanced too (e.g. <a href="http://example.com/path/foo/((((bar)" rel="external">http://example.com/path/foo/((((bar)</a>?'),
+ 'Unbalanced too (e.g. <a href="http://example.com/path/foo/((((bar)" title="http://example.com/path/foo/((((bar)" rel="external">http://example.com/path/foo/((((bar)</a>?'),
array('Unbalanced too (e.g. http://example.com/path/foo/(bar))))?',
- 'Unbalanced too (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)))?'),
+ 'Unbalanced too (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)))?'),
array('example.com',
- '<a href="http://example.com/" rel="external">example.com</a>'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>'),
array('example.org',
- '<a href="http://example.org/" rel="external">example.org</a>'),
+ '<a href="http://example.org/" title="http://example.org" rel="external">example.org</a>'),
array('example.co.uk',
- '<a href="http://example.co.uk/" rel="external">example.co.uk</a>'),
+ '<a href="http://example.co.uk/" title="http://example.co.uk" rel="external">example.co.uk</a>'),
array('www.example.co.uk',
- '<a href="http://www.example.co.uk/" rel="external">www.example.co.uk</a>'),
+ '<a href="http://www.example.co.uk/" title="http://www.example.co.uk" rel="external">www.example.co.uk</a>'),
array('farm1.images.example.co.uk',
- '<a href="http://farm1.images.example.co.uk/" rel="external">farm1.images.example.co.uk</a>'),
+ '<a href="http://farm1.images.example.co.uk/" title="http://farm1.images.example.co.uk" rel="external">farm1.images.example.co.uk</a>'),
array('example.museum',
- '<a href="http://example.museum/" rel="external">example.museum</a>'),
+ '<a href="http://example.museum/" title="http://example.museum" rel="external">example.museum</a>'),
array('example.travel',
- '<a href="http://example.travel/" rel="external">example.travel</a>'),
+ '<a href="http://example.travel/" title="http://example.travel" rel="external">example.travel</a>'),
array('example.com.',
- '<a href="http://example.com/" rel="external">example.com</a>.'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>.'),
array('example.com?',
- '<a href="http://example.com/" rel="external">example.com</a>?'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>?'),
array('example.com!',
- '<a href="http://example.com/" rel="external">example.com</a>!'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>!'),
array('example.com,',
- '<a href="http://example.com/" rel="external">example.com</a>,'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>,'),
array('example.com;',
- '<a href="http://example.com/" rel="external">example.com</a>;'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>;'),
array('example.com:',
- '<a href="http://example.com/" rel="external">example.com</a>:'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>:'),
array('\'example.com\'',
- '\'<a href="http://example.com/" rel="external">example.com</a>\''),
+ '\'<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>\''),
array('"example.com"',
- '&quot;<a href="http://example.com/" rel="external">example.com</a>&quot;'),
+ '&quot;<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>&quot;'),
array('example.com',
- '<a href="http://example.com/" rel="external">example.com</a>'),
+ '<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>'),
array('(example.com)',
- '(<a href="http://example.com/" rel="external">example.com</a>)'),
+ '(<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>)'),
array('[example.com]',
- '[<a href="http://example.com/" rel="external">example.com</a>]'),
+ '[<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>]'),
array('<example.com>',
- '&lt;<a href="http://example.com/" rel="external">example.com</a>&gt;'),
+ '&lt;<a href="http://example.com/" title="http://example.com" rel="external">example.com</a>&gt;'),
array('Hey, check out my cool site example.com okay?',
- 'Hey, check out my cool site <a href="http://example.com/" rel="external">example.com</a> okay?'),
+ 'Hey, check out my cool site <a href="http://example.com/" title="http://example.com" rel="external">example.com</a> okay?'),
array('Hey, check out my cool site example.com.I made it.',
- 'Hey, check out my cool site <a href="http://example.com/" rel="external">example.com</a>.I made it.'),
+ 'Hey, check out my cool site <a href="http://example.com/" title="http://example.com" rel="external">example.com</a>.I made it.'),
array('Hey, check out my cool site example.com.Funny thing...',
- 'Hey, check out my cool site <a href="http://example.com/" rel="external">example.com</a>.Funny thing...'),
+ 'Hey, check out my cool site <a href="http://example.com/" title="http://example.com" rel="external">example.com</a>.Funny thing...'),
array('Hey, check out my cool site example.com.You will love it.',
- 'Hey, check out my cool site <a href="http://example.com/" rel="external">example.com</a>.You will love it.'),
+ 'Hey, check out my cool site <a href="http://example.com/" title="http://example.com" rel="external">example.com</a>.You will love it.'),
array('What about parens (e.g. example.com/path/foo/(bar))?',
- 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>)?'),
+ 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>)?'),
array('What about parens (e.g. example.com/path/foo/(bar)?',
- 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>?'),
+ 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>?'),
array('What about parens (e.g. example.com/path/foo/(bar).)?',
- 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>.)?'),
+ 'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>.)?'),
array('What about parens (e.g. example.com/path/(foo,bar)?',
- 'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" rel="external">example.com/path/(foo,bar)</a>?'),
+ 'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" title="http://example.com/path/(foo,bar)" rel="external">example.com/path/(foo,bar)</a>?'),
array('file.ext',
'file.ext'),
array('file.html',
diff --git a/theme/base/css/display.css b/theme/base/css/display.css
index 6f1a29f4a..1f37a7637 100644
--- a/theme/base/css/display.css
+++ b/theme/base/css/display.css
@@ -156,7 +156,8 @@ font-weight:bold;
#form_notice_delete legend,
#form_password_recover legend,
#form_password_change legend,
-.form_entity_block legend {
+.form_entity_block legend,
+#form_filter_bytag legend {
display:none;
}
@@ -510,6 +511,7 @@ margin-top:7px;
margin-bottom:7px;
margin-left:18px;
float:left;
+max-width:322px;
}
#form_notice .error,
#form_notice .success {
@@ -1049,36 +1051,37 @@ display:none;
#filter_tags ul {
list-style-type:none;
}
-#filter_tags ul li {
+#filter_tags li {
float:left;
margin-left:7px;
padding-left:7px;
border-left-width:1px;
border-left-style:solid;
}
-#filter_tags ul li.child_1 {
+#filter_tags #filter_tags_all {
margin-left:0;
border-left:0;
padding-left:0;
}
-#filter_tags ul li#filter_tags_all a {
+#filter_tags_all a {
font-weight:bold;
margin-top:7px;
float:left;
}
-#filter_tags ul li#filter_tags_item label {
+#filter_tags_item label {
margin-right:7px;
}
-#filter_tags ul li#filter_tags_item label,
-#filter_tags ul li#filter_tags_item select {
-display:inline;
+#filter_tags_item label,
+#filter_tags_item select {
+float:left;
}
-#filter_tags ul li#filter_tags_item p {
+#filter_tags_item p {
float:left;
+clear:both;
margin-left:38px;
}
-#filter_tags ul li#filter_tags_item input {
+#filter_tags_item .submit {
position:relative;
top:3px;
left:3px;
diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css
index 5245ea5d2..6357e55b4 100644
--- a/theme/biz/css/base.css
+++ b/theme/biz/css/base.css
@@ -849,6 +849,10 @@ float:left;
font-size:1.025em;
}
+.notice div.entry-content .timestamp {
+display:inline-block;
+}
+
.notice div.entry-content dl,
.notice div.entry-content dt,
.notice div.entry-content dd {
@@ -866,15 +870,12 @@ display:inline-block;
text-transform:lowercase;
}
-
.notice-options {
-padding-left:2%;
-float:left;
-width:50%;
position:relative;
font-size:0.95em;
-width:12.5%;
+width:90px;
float:right;
+margin-right:11px;
}
.notice-options a {
@@ -897,38 +898,28 @@ left:29px;
.notice-options .notice_delete {
right:0;
}
-.notice-options .notice_reply dt {
-display:none;
-}
-
.notice-options input,
.notice-options a {
text-indent:-9999px;
outline:none;
}
-
-.notice-options .notice_reply a,
.notice-options input.submit {
display:block;
border:0;
}
-.notice-options .notice_reply a,
-.notice-options .notice_delete a {
+.notice-options .notice_reply,
+.notice-options .notice_delete {
text-decoration:none;
padding-left:16px;
}
-
.notice-options form input.submit {
width:16px;
padding:2px 0;
}
-
-.notice-options .notice_delete dt,
.notice-options .form_favor legend,
.notice-options .form_disfavor legend {
display:none;
}
-.notice-options .notice_delete fieldset,
.notice-options .form_favor fieldset,
.notice-options .form_disfavor fieldset {
border:0;
diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css
index 240060b10..7ea451576 100644
--- a/theme/biz/css/display.css
+++ b/theme/biz/css/display.css
@@ -30,10 +30,10 @@ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
}
input, textarea, select,
.entity_remote_subscribe {
-border-color:#aaa;
+border-color:#AAAAAA;
}
#filter_tags ul li {
-border-color:#ddd;
+border-color:#DDDDDD;
}
.form_settings input.form_action-primary {
@@ -50,11 +50,14 @@ background-color:#9BB43E;
input:focus, textarea:focus, select:focus,
#form_notice.warning #notice_data-text {
border-color:#9BB43E;
+box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
}
input.submit,
.entity_remote_subscribe,
#site_nav_local_views a {
-color:#fff;
+color:#FFFFFF;
}
a,
@@ -62,10 +65,13 @@ a,
div.notice-options input,
.form_user_block input.submit,
.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
.entity_send-a-message a,
.form_user_nudge input.submit,
.entity_nudge p,
-.form_settings input.form_action-primary {
+.form_settings input.form_action-primary,
+.form_make_admin input.submit {
color:#002E6E;
}
@@ -82,13 +88,6 @@ border-top-color:#CEE1E9;
border-top-color:#87B4C8;
}
-#content .notice p.entry-content a:visited {
-background-color:#fcfcfc;
-}
-#content .notice p.entry-content .vcard a {
-background-color:#fcfffc;
-}
-
.aside .section {
background-color:#F1F5F8;
background-position:100% 0;
@@ -97,10 +96,10 @@ background-repeat:no-repeat;
}
#notice_text-count {
-color:#333;
+color:#333333;
}
#form_notice.warning #notice_text-count {
-color:#000;
+color:#000000;
}
#form_notice label[for=notice_data-attach] {
background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
@@ -109,28 +108,43 @@ background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no
opacity:0;
}
-#form_notice.processing #notice_action-submit {
-background:#fff url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
+#wrap form.processing input.submit {
+background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
cursor:wait;
text-indent:-9999px;
+outline:none;
}
+#content {
+box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+}
#content,
#site_nav_local_views a,
.aside .section {
-border-color:#fff;
+border-color:#FFFFFF;
}
#content,
#site_nav_local_views .current a {
-background-color:#fff;
+background-color:#FFFFFF;
}
+#site_nav_local_views li {
+box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+-webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+}
#site_nav_local_views a {
-background-color:rgba(135, 180, 200, 0.3);
+background-color:rgba(194, 194, 194, 0.5);
}
#site_nav_local_views a:hover {
background-color:rgba(255, 255, 255, 0.7);
}
+#site_nav_local_views .current a {
+text-shadow: rgba(194,194,194,0.5) 1px 1px 1px;
+}
+
.error {
background-color:#F7E8E8;
@@ -140,10 +154,7 @@ background-color:#EFF3DC;
}
#anon_notice {
-color:#fff;
-}
-
-#showstream #anon_notice {
+color:#FFFFFF;
}
#export_data li a {
@@ -165,7 +176,10 @@ background-image:url(../../base/images/icons/icon_foaf.gif);
.form_user_nudge input.submit,
.form_user_block input.submit,
.form_user_unblock input.submit,
-.entity_nudge p {
+.form_group_block input.submit,
+.form_group_unblock input.submit,
+.entity_nudge p,
+.form_make_admin input.submit {
background-position: 0 40%;
background-repeat: no-repeat;
background-color:transparent;
@@ -175,7 +189,7 @@ background-color:transparent;
.form_user_subscribe input.submit,
.form_user_unsubscribe input.submit {
background-color:#9BB43E;
-color:#fff;
+color:#FFFFFF;
}
.form_user_unsubscribe input.submit,
.form_group_leave input.submit,
@@ -194,20 +208,23 @@ background-image:url(../../base/images/icons/twotone/green/quote.gif);
background-image:url(../../base/images/icons/twotone/green/mail.gif);
}
.form_user_block input.submit,
-.form_user_unblock input.submit {
+.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit {
background-image:url(../../base/images/icons/twotone/green/shield.gif);
}
+.form_make_admin input.submit {
+background-image:url(../../base/images/icons/twotone/green/admin.gif);
+}
/* NOTICES */
-.notices li.over {
-background-color:#fcfcfc;
+.notice .attachment {
+background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%;
}
-
-.notice-options .notice_reply a,
-.notice-options form input.submit {
-background-color:transparent;
+#attachments .attachment {
+background:none;
}
-.notice-options .notice_reply a {
+.notice-options .notice_reply {
background:transparent url(../../base/images/icons/twotone/green/reply.gif) no-repeat 0 45%;
}
.notice-options form.form_favor input.submit {
@@ -216,7 +233,7 @@ background:transparent url(../../base/images/icons/twotone/green/favourite.gif)
.notice-options form.form_disfavor input.submit {
background:transparent url(../../base/images/icons/twotone/green/disfavourite.gif) no-repeat 0 45%;
}
-.notice-options .notice_delete a {
+.notice-options .notice_delete {
background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%;
}
@@ -224,19 +241,32 @@ background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-r
.notices div.notice-options {
opacity:0.4;
}
-.notices li.hover div.entry-content,
-.notices li.hover div.notice-options {
+.notices li:hover div.entry-content,
+.notices li:hover div.notice-options {
opacity:1;
}
-div.entry-content {
-color:#333;
-}
div.notice-options a,
div.notice-options input {
font-family:sans-serif;
}
-.notices li.hover {
-background-color:#fcfcfc;
+#content .notices li:hover {
+background-color:rgba(240, 240, 240, 0.2);
+}
+#conversation .notices li:hover {
+background-color:transparent;
+}
+
+.notices .notices {
+background-color:rgba(200, 200, 200, 0.050);
+}
+.notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.100);
+}
+.notices .notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.150);
+}
+.notices .notices .notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.300);
}
/*END: NOTICES */
diff --git a/theme/biz/logo.png b/theme/biz/logo.png
index fdead6c4a..550d373fe 100644
--- a/theme/biz/logo.png
+++ b/theme/biz/logo.png
Binary files differ
diff --git a/theme/cloudy/css/display.css b/theme/cloudy/css/display.css
index 3851bc057..3fe05eb3d 100644
--- a/theme/cloudy/css/display.css
+++ b/theme/cloudy/css/display.css
@@ -120,6 +120,10 @@ float:left;
margin-left:11px;
float:left;
}
+.form_settings .form_data textarea {
+width:325px;
+}
+
.form_settings .form_data input.submit {
margin-left:0;
}
@@ -968,9 +972,6 @@ right:7px;
top:47px;
right:7px;
}
-.notice-options .notice_reply dt {
-display:none;
-}
.notice-options input,
.notice-options a {
@@ -978,13 +979,13 @@ text-indent:-9999px;
outline:none;
}
-.notice-options .notice_reply a,
+.notice-options .notice_reply,
.notice-options input.submit {
display:block;
border:0;
}
-.notice-options .notice_reply a,
-.notice-options .notice_delete a {
+.notice-options .notice_reply,
+.notice-options .notice_delete {
text-decoration:none;
padding-left:16px;
}
@@ -1375,6 +1376,12 @@ padding-top:160px;
#smssettings #form_notice,
#twittersettings #form_notice,
#imsettings #form_notice,
+#userdesignsettings #form_notice,
+#groupdesignsettings #form_notice,
+#grouplogo #form_notice,
+#editgroup #form_notice,
+#blockedfromgroup #form_notice,
+#groupmembers #form_notice,
#doc #form_notice,
#usergroups #form_notice,
#invite #form_notice,
@@ -1584,11 +1591,11 @@ background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no
background:none;
}
-.notice-options .notice_reply a,
+.notice-options .notice_reply,
.notice-options form input.submit {
background-color:transparent;
}
-.notice-options .notice_reply a {
+.notice-options .notice_reply {
background:transparent url(../images/icons/icon_reply.gif) no-repeat 0 45%;
}
.notice-options form.form_favor input.submit {
@@ -1597,7 +1604,7 @@ background:transparent url(../images/icons/icon_favourite.gif) no-repeat 0 45%;
.notice-options form.form_disfavor input.submit {
background:transparent url(../images/icons/icon_disfavourite.gif) no-repeat 0 45%;
}
-.notice-options .notice_delete a {
+.notice-options .notice_delete {
background:transparent url(../images/icons/icon_trash.gif) no-repeat 0 45%;
}
diff --git a/theme/cloudy/logo.png b/theme/cloudy/logo.png
index fdead6c4a..550d373fe 100644
--- a/theme/cloudy/logo.png
+++ b/theme/cloudy/logo.png
Binary files differ
diff --git a/theme/default/css/display.css b/theme/default/css/display.css
index a1c4a2171..86369cb99 100644
--- a/theme/default/css/display.css
+++ b/theme/default/css/display.css
@@ -94,10 +94,11 @@ background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no
opacity:0;
}
-#form_notice.processing #notice_action-submit {
+#wrap form.processing input.submit {
background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
cursor:wait;
text-indent:-9999px;
+outline:none;
}
#content {
@@ -223,10 +224,6 @@ background:transparent url(../../base/images/icons/twotone/green/favourite.gif)
.notice-options form.form_disfavor input.submit {
background:transparent url(../../base/images/icons/twotone/green/disfavourite.gif) no-repeat 0 45%;
}
-.notice-options form.form_favor.processing input.submit,
-.notice-options form.form_disfavor.processing input.submit {
-background:transparent url(../../base/images/icons/icon_processing.gif) no-repeat 0 45%;
-}
.notice-options .notice_delete {
background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%;
}
diff --git a/theme/h4ck3r/logo.png b/theme/h4ck3r/logo.png
index fdead6c4a..550d373fe 100644
--- a/theme/h4ck3r/logo.png
+++ b/theme/h4ck3r/logo.png
Binary files differ
diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css
index 51286657e..9fc97180d 100644
--- a/theme/identica/css/display.css
+++ b/theme/identica/css/display.css
@@ -94,10 +94,11 @@ background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no
opacity:0;
}
-#form_notice.processing #notice_action-submit {
+#wrap form.processing input.submit {
background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
cursor:wait;
text-indent:-9999px;
+outline:none;
}
#content {
diff --git a/theme/pigeonthoughts/logo.png b/theme/pigeonthoughts/logo.png
index fdead6c4a..550d373fe 100644
--- a/theme/pigeonthoughts/logo.png
+++ b/theme/pigeonthoughts/logo.png
Binary files differ
diff --git a/theme/readme.txt b/theme/readme.txt
index 151b1fb71..d030f2db4 100644
--- a/theme/readme.txt
+++ b/theme/readme.txt
@@ -1,7 +1,7 @@
/** Howto: create a statusnet theme
*
* @package StatusNet
- * @author Sarven Capadisli <csarven@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@status.net>
* @copyright 2009 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/