diff --git a/.gitignore b/.gitignore
index 1cde3a6254..217622c84d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,4 +24,4 @@ config-*.php
good-config.php
lac08.log
php.log
-
+.DS_Store
diff --git a/EVENTS.txt b/EVENTS.txt
index 9de2f8bc6b..a8a77390f6 100644
--- a/EVENTS.txt
+++ b/EVENTS.txt
@@ -129,6 +129,9 @@ StartSubGroupNav: Showing the subscriptions group nav menu
EndSubGroupNav: At the end of the subscriptions group nav menu
- $action: the current action
+StartInitializeRouter: Before the router instance has been initialized; good place to add routes
+- $m: the Net_URL_Mapper that has just been set up
+
RouterInitialized: After the router instance has been initialized
- $m: the Net_URL_Mapper that has just been set up
@@ -390,3 +393,84 @@ EndProfilePageProfileTags: after showing the tags on the profile page
- $action: the current action
- &$profile: the profile being shown
+StartProfileList: when starting a list of profiles (before
)
+- $profilelist: ProfileList widget, with $profile, $action, and $out
+
+EndProfileList: when ending a list of profiles (after
)
+- $profilelist: ProfileList widget
+
+StartProfileListItem: when starting to show a profile list item
+- $item: ProfileListItem widget
+
+EndProfileListItem: after showing a profile list item
+- $item: ProfileListItem widget
+
+StartProfileListItemProfile: the profile data part of the item
+- $item: ProfileListItem widget
+
+EndProfileListItemProfile: the profile data part of the item
+- $item: ProfileListItem widget
+
+StartProfileListItemActions: the actions (buttons) for an item
+- $item: ProfileListItem widget
+
+EndProfileListItemActions: the actions (buttons) for an item
+- $item: ProfileListItem widget
+
+StartProfileListItemProfileElements: inside the
+- $item: ProfileListItem widget
+
+EndProfileListItemProfileElements: inside the
+- $item: ProfileListItem widget
+
+StartProfileListItemAvatar: Showing a profile list avatar
+- $item: ProfileListItem widget
+
+EndProfileListItemAvatar: Showing a profile list avatar
+- $item: ProfileListItem widget
+
+StartProfileListItemFullName: Showing the profile list full name
+- $item: ProfileListItem widget
+
+EndProfileListItemFullName: Showing the profile list full name
+- $item: ProfileListItem widget
+
+StartProfileListItemLocation: Showing the profile list location
+- $item: ProfileListItem widget
+
+EndProfileListItemLocation: Showing the profile list location
+- $item: ProfileListItem widget
+
+StartProfileListItemHomepage: Showing the profile list homepage
+- $item: ProfileListItem widget
+
+EndProfileListItemHomepage: Showing the profile list homepage
+- $item: ProfileListItem widget
+
+StartProfileListItemBio: Showing the profile list bio
+- $item: ProfileListItem widget
+
+EndProfileListItemBio: Showing the profile list bio
+- $item: ProfileListItem widget
+
+StartProfileListItemActionElements: Showing the profile list actions (prepend a button here, or replace all buttons)
+- $item: ProfileListItem widget
+
+EndProfileListItemActionElements: Showing profile list actions (append a button here)
+- $item: ProfileListItem widget
+
+StartUserXRDS: Start XRDS output (right after the opening XRDS tag)
+- $action: the current action
+- &$xrdsoutputter - XRDSOutputter object to write to
+
+EndUserXRDS: End XRDS output (right before the closing XRDS tag)
+- $action: the current action
+- &$xrdsoutputter - XRDSOutputter object to write to
+
+StartPublicXRDS: Start XRDS output (right after the opening XRDS tag)
+- $action: the current action
+- &$xrdsoutputter - XRDSOutputter object to write to
+
+EndPublicXRDS: End XRDS output (right before the closing XRDS tag)
+- $action: the current action
+- &$xrdsoutputter - XRDSOutputter object to write to
diff --git a/README b/README
index 037027d431..952c914fec 100644
--- a/README
+++ b/README
@@ -2,8 +2,8 @@
README
------
-StatusNet 0.8.1 ("Second Guessing")
-26 Aug 2009
+StatusNet 0.8.2 ("Life and How to Live It")
+1 Nov 2009
This is the README file for StatusNet (formerly Laconica), the Open
Source microblogging platform. It includes installation instructions,
@@ -77,49 +77,80 @@ for additional terms.
New this version
================
-This is a minor feature and bugfix release since version 0.8.0,
-released Jul 15 2009. Notable changes this version:
+This is a minor feature and bugfix release since version 0.8.1,
+released Aug 26 2009. Notable changes this version:
-- Laconica has been renamed StatusNet. With a few minor compatibility
- exceptions, all references to "Laconica" in code, documentation
- and comments were changed to "StatusNet".
-- A new plugin to support "infinite scroll".
-- A new plugin to support reCaptcha .
-- Better logging of server errors.
-- Add an Openid-only mode for authentication.
-- 'lite' parameter for some Twitter API methods.
-- A new plugin to auto-complete nicknames for @-replies.
-- Configuration options to disable OpenID, SMS, Twitter, post-by-email, and IM.
-- Support for lighttpd using 404-based
- rewrites.
-- Support for using Twitter's OAuth authentication as a client.
-- First version of the groups API.
-- Can configure a site-wide design, including background image and
- colors.
-- Improved algorithm for replies and conversations, making
- conversation trees more accurate and useful.
-- Add a script to create a simulation database for testing/debugging.
-- Sanitize HTML for OEmbed.
-- Improved queue management for DB-based queuing.
-- More complete URL detection.
-- Hashtags now support full Unicode character set.
-- Notice inboxes are now garbage-collected on a regular basis
- at notice-write time.
-- PiwikAnalyticsPlugin updated for latest Piwik interface.
-- Attachment and notice pages can be embedded with OEmbed
- .
-- Failed authentication is logged.
-- PostgreSQL schema and support brought up-to-date with 0.8.x features.
-- The installer works with PostgreSQL as well as MySQL.
-- RSS 1.0 feeds use HTTP Basic authentication in private mode.
-- Many, many bug fixes, particularly with performance.
-- Better (=working) garbage collection for old sessions.
-- Better (=working) search queries.
-- Some cleanup of HTML output.
-- Better error handling when updating Facebook.
-- Considerably better performance when using replication for API
- calls.
-- Initial unit tests.
+- New script for deleting user accounts. Not particularly safe or
+ community-friendly. Better for deleting abusive accounts than for
+ users who are 'retiring'.
+- Improved detection of URLs in notices, specifically for punctuation
+ chars like ~, :, $, _, -, +, !, @, and %.
+- Removed some extra
semantic HTML code.
+- Correct error in status-network database ini file (having multiple
+ statusnet sites with a single codebase)
+- Fixed error output for Twitter posting failures.
+- Fixed bug in Twitter queue handler that requeued inapplicable
+ notices ad infinitum.
+- Improve FOAF output for remote users.
+- new commands to join and leave groups.
+- Fixed bug in which you cannot turn off importing friends timelines
+ flag.
+- Better error handling in Twitter posting.
+- Show oEmbed data for XHTML files as well as plain HTML.
+- Updated bug database link in README.
+- require HTML tidy extension.
+- add support for HTTP Basic Auth in PHP CGI or FastCGI (e.g. GoDaddy).
+- autofocus input to selected entry elements depending on page.
+- updated layout for filter-by-tag form.
+- better layout for inbox and outbox pages.
+- fix highlighting search terms in attributes of notice list elements.
+- Correctly handle errors in linkback plugin.
+- Updated biz theme.
+- Updated cloudy theme.
+- Don't match '::' as an IPv6 address.
+- Use the same decision logic for deciding whether to mark an
+ attachment as an enclosure in RSS or as a paperclip item in Web
+ output.
+- Fixed a bug in the Piwik plugin that hard-coded the site ID.
+- Add a param, inreplyto, to notice/new to allow an explicit response
+ to another notice.
+- Show username in subject of emails.
+- Check if avatar exists before trying to delete it.
+- Correctly add omb_version to response for request token in OMB.
+- Add a few more SMS carriers.
+- Add a few more notice sources.
+- Vary: header.
+- Improvements to the AutoCompletePlugin.
+- Check for 'dl' before using it.
+- Make it impossible to delete self-subscriptions via the API.
+- Fix pagination of tagged user pages.
+- Make PiwikAnalyticsPlugin work with addPlugin().
+- Removed trailing single space in user nicknames in notice lists.
+- Show context link if a notice starts a conversation.
+- blacklist all files and directories in install dir.
+- handle GoDaddy-style PATH_INFO, including script name.
+- add home_timeline synonym for friends_timeline.
+- Add a popup window for the realtime plugin.
+- Add some more streams for the realtime plugin.
+- Fix a bug that overwrote group creation timestamp on every edit.
+- Moved HTTP error code strings to a class variable.
+- The Twitter API now returns server errors in the correct format.
+- Reset the doctype for HTML output.
+- Fixed a number of notices.
+- Don't show search suggestions for private sites.
+- Some corrections to FBConnect nav overrides.
+- Slightly less database-intensive session management.
+- Updated name of software in installer script.
+- Include long-form attachment URLs if url-shortener is disabled.
+- Include updated localisations for Polish, Greek, Hebrew, Icelandic,
+ Norwegian, and Chinese.
+- Include upstream fixes to gettext.php.
+- Correct for regression in Facebook API for updates.
+- Ignore "Sent from my iPhone" (and similar) in mail updates.
+- Use the NICKNAME_FMT constant for detecting nicknames.
+- Check for site servername config'd.
+- Compatibility fix for empty status updates with Twitter API.
+- Option to show files privately (EXPERIMENTAL! Use with caution.)
Prerequisites
=============
@@ -225,9 +256,9 @@ especially if you've previously installed PHP/MySQL packages.
1. Unpack the tarball you downloaded on your Web server. Usually a
command like this will work:
- tar zxf statusnet-0.8.1.tar.gz
+ tar zxf statusnet-0.8.2.tar.gz
- ...which will make a statusnet-0.8.1 subdirectory in your current
+ ...which will make a statusnet-0.8.2 subdirectory in your current
directory. (If you don't have shell access on your Web server, you
may have to unpack the tarball on your local computer and FTP the
files to the server.)
@@ -235,7 +266,7 @@ especially if you've previously installed PHP/MySQL packages.
2. Move the tarball to a directory of your choosing in your Web root
directory. Usually something like this will work:
- mv statusnet-0.8.1 /var/www/mublog
+ mv statusnet-0.8.2 /var/www/mublog
This will make your StatusNet instance available in the mublog path of
your server, like "http://example.net/mublog". "microblog" or
@@ -526,10 +557,6 @@ This will run eight (for now) queue handlers:
of registered users.
* xmppconfirmhandler.php - sends confirmation messages to registered
users.
-* twitterqueuehandler.php - sends queued notices to Twitter for user
- who have opted to set up Twitter bridging.
-* facebookqueuehandler.php - sends queued notices to Facebook for users
- of the built-in Facebook application.
Note that these queue daemons are pretty raw, and need your care. In
particular, they leak memory, and you may want to restart them on a
@@ -547,101 +574,6 @@ our kind of hacky home-grown DB-based queue solution. See the "queues"
config section below for how to configure to use STOMP. As of this
writing, the software has been tested with ActiveMQ (
-Twitter Bridge
---------------
-
-* OAuth
-
-As of 0.8.1, OAuth is used to to access protected resources on Twitter
-instead of HTTP Basic Auth. To use Twitter bridging you will need
-to register your instance of StatusNet as an application on Twitter
-(http://twitter.com/apps), and update the following variables in your
-config.php with the consumer key and secret Twitter generates for you:
-
- $config['twitter']['consumer_key'] = 'YOURKEY';
- $config['twitter']['consumer_secret'] = 'YOURSECRET';
-
-When registering your application with Twitter set the type to "Browser"
-and your Callback URL to:
-
- http://example.org/mublog/twitter/authorization
-
-The default access type should be, "Read & Write".
-
-* Importing statuses from Twitter
-
-To allow your users to import their friends' Twitter statuses, you will
-need to enable the bidirectional Twitter bridge in config.php:
-
- $config['twitterbridge']['enabled'] = true;
-
-and run the TwitterStatusFetcher daemon (scripts/twitterstatusfetcher.php).
-Additionally, you will want to set the integration source variable,
-which will keep notices posted to Twitter via StatusNet from looping
-back. The integration source should be set to the name of your
-application, exactly as you specified it on the settings page for your
-StatusNet application on Twitter, e.g.:
-
- $config['integration']['source'] = 'YourApp';
-
-* Twitter Friends Syncing
-
-Users may set a flag in their settings ("Subscribe to my Twitter friends
-here" under the Twitter tab) to have StatusNet attempt to locate and
-subscribe to "friends" (people they "follow") on Twitter who also have
-accounts on your StatusNet system, and who have previously set up a link
-for automatically posting notices to Twitter.
-
-As of 0.8.0, this is no longer accomplished via a cron job. Instead you
-must run the SyncTwitterFriends daemon (scripts/synctwitterfreinds.php).
-
-Built-in Facebook Application
------------------------------
-
-StatusNet's Facebook application allows your users to automatically
-update their Facebook statuses with their latest notices, invite
-their friends to use the app (and thus your site), view their notice
-timelines, and post notices -- all from within Facebook. The application
-is built into StatusNet and runs on your host. For automatic Facebook
-status updating to work you will need to enable queuing and run the
-facebookqueuehandler.php daemon (see the "Queues and daemons" section
-above).
-
-Quick setup instructions*:
-
-Install the Facebook Developer application on Facebook:
-
- http://www.facebook.com/developers/
-
-Use it to create a new application and generate an API key and secret.
-Uncomment the Facebook app section of your config.php and copy in the
-key and secret, e.g.:
-
- # Config section for the built-in Facebook application
- $config['facebook']['apikey'] = 'APIKEY';
- $config['facebook']['secret'] = 'SECRET';
-
-In Facebook's application editor, specify the following URLs for your app:
-
-- Canvas Callback URL: http://example.net/mublog/facebook/
-- Post-Remove Callback URL: http://example.net/mublog/facebook/remove
-- Post-Add Redirect URL: http://apps.facebook.com/yourapp/
-- Canvas Page URL: http://apps.facebook.com/yourapp/
-
-(Replace 'example.net' with your host's URL, 'mublog' with the path
-to your StatusNet installation, and 'yourapp' with the name of the
-Facebook application you created.)
-
-Additionally, Choose "Web" for Application type in the Advanced tab.
-In the "Canvas setting" section, choose the "FBML" for Render Method,
-"Smart Size" for IFrame size, and "Full width (760px)" for Canvas Width.
-Everything else can be left with default values.
-
-*For more detailed instructions please see the installation guide on the
-StatusNet wiki:
-
- http://status.net/trac/wiki/FacebookApplication
-
Sitemaps
--------
@@ -755,6 +687,16 @@ private site, but users of the private site may be able to subscribe
to users on a remote site. (Or not... it's not well tested.) The
"proper behaviour" hasn't been defined here, so handle with care.
+If fancy URLs is enabled, access to file attachments can also be
+restricted to logged-in users only. Uncomment the appropriate rewrite
+<<<<<<< HEAD:README
+rule in .htaccess or your server's httpd.conf. (This most likely will
+not work if you are using a virtual server for attachments, so consider
+the performance/security tradeoff.)
+=======
+rule in .htaccess or your server's httpd.conf.
+>>>>>>> 446de62... Revert "Added some explanatory text to README":README
+
Upgrading
=========
@@ -768,7 +710,7 @@ with this situation.
If you've been using StatusNet 0.7, 0.6, 0.5 or lower, or if you've
been tracking the "git" version of the software, you will probably
want to upgrade and keep your existing data. There is no automated
-upgrade procedure in StatusNet 0.8.1. Try these step-by-step
+upgrade procedure in StatusNet 0.8.2. Try these step-by-step
instructions; read to the end first before trying them.
0. Download StatusNet and set up all the prerequisites as if you were
@@ -789,7 +731,7 @@ instructions; read to the end first before trying them.
5. Once all writing processes to your site are turned off, make a
final backup of the Web directory and database.
6. Move your StatusNet directory to a backup spot, like "mublog.bak".
-7. Unpack your StatusNet 0.8.1 tarball and move it to "mublog" or
+7. Unpack your StatusNet 0.8.2 tarball and move it to "mublog" or
wherever your code used to be.
8. Copy the config.php file and avatar directory from your old
directory to your new directory.
@@ -1251,24 +1193,11 @@ For SMS integration.
enabled: Whether to enable SMS integration. Defaults to true. Queues
should also be enabled.
-twitter
--------
-
-For Twitter integration
-
-enabled: Whether to enable Twitter integration. Defaults to true.
- Queues should also be enabled.
-
integration
-----------
A catch-all for integration with other systems.
-source: The name to use for the source of posts to Twitter. Defaults
- to 'statusnet', but if you request your own source name from
- Twitter , you can use
- that here instead. Status updates on Twitter will then have
- links to your site.
taguri: base for tag:// URIs. Defaults to site-server + ',2009'.
inboxes
@@ -1446,15 +1375,6 @@ dir: directory to write backgrounds too. Default is '/background/'
path: path to backgrounds. Default is sub-path of install path; note
that you may need to change this if you change site-path too.
-twitterbridge
--------------
-
-A bi-direction bridge to Twitter (http://twitter.com/).
-
-enabled: default false. If true, will show user's Twitter friends'
- notices in their inbox and faves pages, only to the user. You
- must also run the twitterstatusfetcher.php script.
-
ping
----
@@ -1553,7 +1473,7 @@ repository (see below), and you get a compilation error ("unexpected
T_STRING") in the browser, check to see that you don't have any
conflicts in your code.
-If you upgraded to StatusNet 0.8.1 without reading the "Notice
+If you upgraded to StatusNet 0.8.2 without reading the "Notice
inboxes" section above, and all your users' 'Personal' tabs are empty,
read the "Notice inboxes" section above.
@@ -1661,6 +1581,7 @@ if anyone's been overlooked in error.
* Jeffery To
* Federico Marani
* Craig Andrews
+* mEDI
Thanks also to the developers of our upstream library code and to the
thousands of people who have tried out Identi.ca, installed StatusNet,
diff --git a/actions/all.php b/actions/all.php
index f1786462e1..61cedce749 100644
--- a/actions/all.php
+++ b/actions/all.php
@@ -99,19 +99,17 @@ class AllAction extends ProfileAction
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'
+ 'ApiTimelineFriends', array(
+ 'format' => 'rss',
+ 'id' => $this->user->nickname
)
),
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'
+ 'ApiTimelineFriends', array(
+ 'format' => 'atom',
+ 'id' => $this->user->nickname
)
),
sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))
diff --git a/actions/apidirectmessagenew.php b/actions/apidirectmessagenew.php
index fa6cafbe8d..ca1ee70dde 100644
--- a/actions/apidirectmessagenew.php
+++ b/actions/apidirectmessagenew.php
@@ -157,7 +157,7 @@ class ApiDirectMessageNewAction extends ApiAuthAction
// Note: sending msgs to yourself is allowed by Twitter
$errmsg = 'Don\'t send a message to yourself; ' .
- 'just say it to yourself quietly instead.'
+ 'just say it to yourself quietly instead.';
$this->clientError(_($errmsg), 403, $this->format);
return;
diff --git a/actions/apifavoritecreate.php b/actions/apifavoritecreate.php
index a80a6492e9..4367397707 100644
--- a/actions/apifavoritecreate.php
+++ b/actions/apifavoritecreate.php
@@ -127,7 +127,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction
if (empty($fave)) {
$this->clientError(
- _('Could not create favorite.')
+ _('Could not create favorite.'),
403,
$this->format
);
diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php
index 0d71e15128..898a4bd723 100644
--- a/actions/apistatusesupdate.php
+++ b/actions/apistatusesupdate.php
@@ -38,6 +38,7 @@ if (!defined('STATUSNET')) {
}
require_once INSTALLDIR . '/lib/apiauth.php';
+require_once INSTALLDIR . '/lib/mediafile.php';
/**
* Updates the authenticating user's status (posts a notice).
@@ -60,7 +61,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
var $source = null;
var $status = null;
var $in_reply_to_status_id = null;
-
static $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
/**
@@ -76,25 +76,8 @@ class ApiStatusesUpdateAction extends ApiAuthAction
{
parent::prepare($args);
- $this->user = $this->auth_user;
-
- if (empty($this->user)) {
- $this->clientError(_('No such user!'), 404, $this->format);
- return false;
- }
-
+ $this->user = $this->auth_user;
$this->status = $this->trimmed('status');
-
- if (empty($this->status)) {
- $this->clientError(
- 'Client must provide a \'status\' parameter with a value.',
- 400,
- $this->format
- );
-
- return false;
- }
-
$this->source = $this->trimmed('source');
if (empty($this->source) || in_array($source, $this->reserved_sources)) {
@@ -129,6 +112,27 @@ class ApiStatusesUpdateAction extends ApiAuthAction
return;
}
+ if (empty($this->status)) {
+ $this->clientError(
+ 'Client must provide a \'status\' parameter with a value.',
+ 400,
+ $this->format
+ );
+ return;
+ }
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user!'), 404, $this->format);
+ return;
+ }
+
+ // Workaround for PHP returning empty $_FILES when POST length > PHP settings
+
+ if (empty($_POST) && ($_SERVER['CONTENT_LENGTH'] > 0)) {
+ $this->clientError(_('Unable to handle that much POST data!'));
+ return;
+ }
+
$status_shortened = common_shorten_links($this->status);
if (Notice::contentTooLong($status_shortened)) {
@@ -187,14 +191,40 @@ class ApiStatusesUpdateAction extends ApiAuthAction
}
}
+ $upload = null;
+
+ try {
+ $upload = MediaFile::fromUpload('media', $this->user);
+ } catch (ClientException $ce) {
+ $this->clientError($ce->getMessage());
+ return;
+ }
+
+ if (isset($upload)) {
+ $status_shortened .= ' ' . $upload->shortUrl();
+
+ if (Notice::contentTooLong($status_shortened)) {
+ $upload->delete();
+ $msg = _(
+ 'Max notice size is %d chars, ' .
+ 'including attachment URL.'
+ );
+ $this->clientError(sprintf($msg, Notice::maxContent()));
+ }
+ }
+
$this->notice = Notice::saveNew(
$this->user->id,
- html_entity_decode($this->status, ENT_NOQUOTES, 'UTF-8'),
+ html_entity_decode($status_shortened, ENT_NOQUOTES, 'UTF-8'),
$this->source,
1,
$reply_to
);
+ if (isset($upload)) {
+ $upload->attachToNotice($this->notice);
+ }
+
common_broadcast_notice($this->notice);
}
diff --git a/actions/bookmarklet.php b/actions/bookmarklet.php
new file mode 100644
index 0000000000..0603a74561
--- /dev/null
+++ b/actions/bookmarklet.php
@@ -0,0 +1,75 @@
+.
+ *
+ * @category Bookmarklet
+ * @package StatusNet
+ * @author Sarven Capadisli
+ * @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);
+}
+
+require_once INSTALLDIR . '/actions/newnotice.php';
+
+/**
+ * Action for posting a notice
+ *
+ * @category Bookmarklet
+ * @package StatusNet
+ * @author Sarven Capadisli
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class BookmarkletAction extends NewnoticeAction
+{
+ function showTitle()
+ {
+ $this->element('title', null, _('Post to ').common_config('site', 'name'));
+ }
+
+ function showHeader()
+ {
+ $this->elementStart('div', array('id' => 'header'));
+ $this->elementStart('address');
+ $this->element('a', array('class' => 'url',
+ 'href' => common_local_url('public')),
+ '');
+ $this->elementEnd('address');
+ if (common_logged_in()) {
+ $this->showNoticeForm();
+ }
+ $this->elementEnd('div');
+ }
+
+ function showCore()
+ {
+ }
+
+ function showFooter()
+ {
+ }
+}
+
diff --git a/actions/emailsettings.php b/actions/emailsettings.php
index 6eff06c0d6..67b991cdc8 100644
--- a/actions/emailsettings.php
+++ b/actions/emailsettings.php
@@ -326,7 +326,7 @@ class EmailsettingsAction extends AccountSettingsAction
$this->showForm(_('Cannot normalize that email address'));
return;
}
- if (!Validate::email($email, true)) {
+ if (!Validate::email($email, common_config('email', 'check_domain'))) {
$this->showForm(_('Not a valid email address'));
return;
} else if ($user->email == $email) {
diff --git a/actions/getfile.php b/actions/getfile.php
new file mode 100644
index 0000000000..ecda34c0f6
--- /dev/null
+++ b/actions/getfile.php
@@ -0,0 +1,145 @@
+.
+ *
+ * @category Personal
+ * @package StatusNet
+ * @author Jeffery To
+ * @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);
+}
+
+require_once 'MIME/Type.php';
+
+/**
+ * Action for getting a file attachment
+ *
+ * @category Personal
+ * @package StatusNet
+ * @author Jeffery To
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class GetfileAction extends Action
+{
+ /**
+ * Path of file to return
+ */
+
+ var $path = null;
+
+ /**
+ * Get file name
+ *
+ * @param array $args $_REQUEST array
+ *
+ * @return success flag
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $filename = $this->trimmed('filename');
+ $path = null;
+
+ if ($filename) {
+ $path = common_config('attachments', 'dir') . $filename;
+ }
+
+ if (empty($path) or !file_exists($path)) {
+ $this->clientError(_('No such file.'), 404);
+ return false;
+ }
+ if (!is_readable($path)) {
+ $this->clientError(_('Cannot read file.'), 403);
+ return false;
+ }
+
+ $this->path = $path;
+ return true;
+ }
+
+ /**
+ * Is this page read-only?
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * Last-modified date for file
+ *
+ * @return int last-modified date as unix timestamp
+ */
+
+ function lastModified()
+ {
+ return filemtime($this->path);
+ }
+
+ /**
+ * etag for file
+ *
+ * This returns the same data (inode, size, mtime) as Apache would,
+ * but in decimal instead of hex.
+ *
+ * @return string etag http header
+ */
+ function etag()
+ {
+ $stat = stat($this->path);
+ return '"' . $stat['ino'] . '-' . $stat['size'] . '-' . $stat['mtime'] . '"';
+ }
+
+ /**
+ * Handle input, produce output
+ *
+ * @param array $args $_REQUEST contents
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ // undo headers set by PHP sessions
+ $sec = session_cache_expire() * 60;
+ header('Expires: ' . date(DATE_RFC1123, time() + $sec));
+ header('Cache-Control: public, max-age=' . $sec);
+ header('Pragma: public');
+
+ parent::handle($args);
+
+ $path = $this->path;
+ header('Content-Type: ' . MIME_Type::autoDetect($path));
+ readfile($path);
+ }
+}
diff --git a/actions/groupmembers.php b/actions/groupmembers.php
index dcbdd37597..b326a0df75 100644
--- a/actions/groupmembers.php
+++ b/actions/groupmembers.php
@@ -179,9 +179,12 @@ class GroupMemberListItem extends ProfileListItem
function showActions()
{
$this->startActions();
- $this->showSubscribeButton();
- $this->showMakeAdminForm();
- $this->showGroupBlockForm();
+ if (Event::handle('StartProfileListItemActionElements', array($this))) {
+ $this->showSubscribeButton();
+ $this->showMakeAdminForm();
+ $this->showGroupBlockForm();
+ Event::handle('EndProfileListItemActionElements', array($this));
+ }
$this->endActions();
}
diff --git a/actions/invite.php b/actions/invite.php
index 788130c582..3015202e9e 100644
--- a/actions/invite.php
+++ b/actions/invite.php
@@ -68,7 +68,7 @@ class InviteAction extends CurrentUserDesignAction
foreach ($addresses as $email) {
$email = trim($email);
- if (!Validate::email($email, true)) {
+ if (!Validate::email($email, common_config('email', 'check_domain'))) {
$this->showForm(sprintf(_('Invalid email address: %s'), $email));
return;
}
diff --git a/actions/newmessage.php b/actions/newmessage.php
index a0b17fc18a..37fca1ca2b 100644
--- a/actions/newmessage.php
+++ b/actions/newmessage.php
@@ -99,7 +99,9 @@ class NewmessageAction extends Action
$user = common_current_user();
if (!$user) {
- $this->clientError(_('Only logged-in users can send direct messages.'), 403);
+ /* Go log in, and then come back. */
+ common_set_returnto($_SERVER['REQUEST_URI']);
+ common_redirect(common_local_url('login'));
return false;
}
@@ -221,7 +223,22 @@ class NewmessageAction extends Action
}
$this->msg = $msg;
- $this->showPage();
+ if ($this->trimmed('ajax')) {
+ $this->startHTML('text/xml;charset=UTF-8');
+ $this->elementStart('head');
+ $this->element('title', null, _('New message'));
+ $this->elementEnd('head');
+ $this->elementStart('body');
+ if (common_logged_in()) {
+ $this->showNoticeForm();
+ }
+ $this->elementEnd('div');
+ $this->elementEnd('body');
+ $this->endHTML();
+ }
+ else {
+ $this->showPage();
+ }
}
function showPageNotice()
diff --git a/actions/newnotice.php b/actions/newnotice.php
index 9ee031f936..fbd7ab6bce 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -33,7 +33,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
-require_once INSTALLDIR.'/lib/noticelist.php';
+require_once INSTALLDIR . '/lib/noticelist.php';
+require_once INSTALLDIR . '/lib/mediafile.php';
/**
* Action for posting new notices
@@ -113,33 +114,6 @@ class NewnoticeAction extends Action
}
}
- function getUploadedFileType() {
- require_once 'MIME/Type.php';
-
- $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd');
- $cmd = common_config('attachments', 'filecommand');
-
- $filetype = MIME_Type::autoDetect($_FILES['attach']['tmp_name']);
- if (in_array($filetype, common_config('attachments', 'supported'))) {
- return $filetype;
- }
- $media = MIME_Type::getMedia($filetype);
- if ('application' !== $media) {
- $hint = sprintf(_(' Try using another %s format.'), $media);
- } else {
- $hint = '';
- }
- $this->clientError(sprintf(
- _('%s is not a supported filetype on this server.'), $filetype) . $hint);
- }
-
- function isRespectsQuota($user) {
- $file = new File;
- $ret = $file->isRespectsQuota($user,$_FILES['attach']['size']);
- if (true === $ret) return true;
- $this->clientError($ret);
- }
-
/**
* Save a new notice, based on arguments
*
@@ -160,18 +134,12 @@ class NewnoticeAction extends Action
if (!$content) {
$this->clientError(_('No content!'));
- } else {
- $content_shortened = common_shorten_links($content);
- if (Notice::contentTooLong($content_shortened)) {
- $this->clientError(sprintf(_('That\'s too long. '.
- 'Max notice size is %d chars.'),
- Notice::maxContent()));
- }
+ return;
}
$inter = new CommandInterpreter();
- $cmd = $inter->handle_command($user, $content_shortened);
+ $cmd = $inter->handle_command($user, $content);
if ($cmd) {
if ($this->boolean('ajax')) {
@@ -182,6 +150,13 @@ class NewnoticeAction extends Action
return;
}
+ $content_shortened = common_shorten_links($content);
+ if (Notice::contentTooLong($content_shortened)) {
+ $this->clientError(sprintf(_('That\'s too long. '.
+ 'Max notice size is %d chars.'),
+ Notice::maxContent()));
+ }
+
$replyto = $this->trimmed('inreplyto');
#If an ID of 0 is wrongly passed here, it will cause a database error,
#so override it...
@@ -189,84 +164,37 @@ class NewnoticeAction extends Action
$replyto = 'false';
}
- if (isset($_FILES['attach']['error'])) {
- switch ($_FILES['attach']['error']) {
- case UPLOAD_ERR_NO_FILE:
- // no file uploaded, nothing to do
- break;
+ $upload = null;
+ $upload = MediaFile::fromUpload('attach');
- case UPLOAD_ERR_OK:
- $mimetype = $this->getUploadedFileType();
- if (!$this->isRespectsQuota($user)) {
- die('clientError() should trigger an exception before reaching here.');
- }
- break;
+ if (isset($upload)) {
- case UPLOAD_ERR_INI_SIZE:
- $this->clientError(_('The uploaded file exceeds the upload_max_filesize directive in php.ini.'));
-
- case UPLOAD_ERR_FORM_SIZE:
- $this->clientError(_('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.'));
-
- case UPLOAD_ERR_PARTIAL:
- $this->clientError(_('The uploaded file was only partially uploaded.'));
-
- case UPLOAD_ERR_NO_TMP_DIR:
- $this->clientError(_('Missing a temporary folder.'));
-
- case UPLOAD_ERR_CANT_WRITE:
- $this->clientError(_('Failed to write file to disk.'));
-
- case UPLOAD_ERR_EXTENSION:
- $this->clientError(_('File upload stopped by extension.'));
-
- default:
- die('Should never reach here.');
- }
- }
-
- if (isset($mimetype)) {
- $filename = $this->saveFile($mimetype);
- if (empty($filename)) {
- $this->clientError(_('Couldn\'t save file.'));
- }
-
- $fileRecord = $this->storeFile($filename, $mimetype);
-
- $fileurl = common_local_url('attachment',
- array('attachment' => $fileRecord->id));
-
- // not sure this is necessary -- Zach
- $this->maybeAddRedir($fileRecord->id, $fileurl);
-
- $short_fileurl = common_shorten_url($fileurl);
- if (!$short_fileurl) {
- // todo -- Consider forcing default shortener if none selected?
- $short_fileurl = $fileurl;
- }
- $content_shortened .= ' ' . $short_fileurl;
+ $content_shortened .= ' ' . $upload->shortUrl();
if (Notice::contentTooLong($content_shortened)) {
- $this->deleteFile($filename);
- $this->clientError(sprintf(_('Max notice size is %d chars, including attachment URL.'),
- Notice::maxContent()));
+ $upload->delete();
+ $this->clientError(
+ sprintf(
+ _('Max notice size is %d chars, including attachment URL.'),
+ Notice::maxContent()
+ )
+ );
}
-
- // Also, not sure this is necessary -- Zach
- $this->maybeAddRedir($fileRecord->id, $short_fileurl);
}
$notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
($replyto == 'false') ? null : $replyto);
- if (isset($mimetype)) {
- $this->attachFile($notice, $fileRecord);
+ if (isset($upload)) {
+ $upload->attachToNotice($notice);
}
common_broadcast_notice($notice);
if ($this->boolean('ajax')) {
- $this->startHTML('text/xml;charset=utf-8');
+ header('Content-Type: text/xml;charset=utf-8');
+ $this->xw->startDocument('1.0', 'UTF-8');
+ $this->elementStart('html');
$this->elementStart('head');
$this->element('title', null, _('Notice posted'));
$this->elementEnd('head');
@@ -288,87 +216,6 @@ class NewnoticeAction extends Action
}
}
- function saveFile($mimetype) {
-
- $cur = common_current_user();
-
- if (empty($cur)) {
- $this->serverError(_('Somehow lost the login in saveFile'));
- }
-
- $basename = basename($_FILES['attach']['name']);
-
- $filename = File::filename($cur->getProfile(), $basename, $mimetype);
-
- $filepath = File::path($filename);
-
- if (move_uploaded_file($_FILES['attach']['tmp_name'], $filepath)) {
- return $filename;
- } else {
- $this->clientError(_('File could not be moved to destination directory.'));
- }
- }
-
- function deleteFile($filename)
- {
- $filepath = File::path($filename);
- @unlink($filepath);
- }
-
- function storeFile($filename, $mimetype) {
-
- $file = new File;
- $file->filename = $filename;
-
- $file->url = File::url($filename);
-
- $filepath = File::path($filename);
-
- $file->size = filesize($filepath);
- $file->date = time();
- $file->mimetype = $mimetype;
-
- $file_id = $file->insert();
-
- if (!$file_id) {
- common_log_db_error($file, "INSERT", __FILE__);
- $this->clientError(_('There was a database error while saving your file. Please try again.'));
- }
-
- return $file;
- }
-
- function rememberFile($file, $short)
- {
- $this->maybeAddRedir($file->id, $short);
- }
-
- function maybeAddRedir($file_id, $url)
- {
- $file_redir = File_redirection::staticGet('url', $url);
-
- if (empty($file_redir)) {
- $file_redir = new File_redirection;
- $file_redir->url = $url;
- $file_redir->file_id = $file_id;
-
- $result = $file_redir->insert();
-
- if (!$result) {
- common_log_db_error($file_redir, "INSERT", __FILE__);
- $this->clientError(_('There was a database error while saving your file. Please try again.'));
- }
- }
- }
-
- function attachFile($notice, $filerec)
- {
- File_to_post::processNew($filerec->id, $notice->id);
-
- $this->maybeAddRedir($filerec->id,
- common_local_url('file', array('notice' => $notice->id)));
- }
-
/**
* Show an Ajax-y error message
*
diff --git a/actions/othersettings.php b/actions/othersettings.php
index 011b4fc838..d32a2d651c 100644
--- a/actions/othersettings.php
+++ b/actions/othersettings.php
@@ -103,7 +103,7 @@ class OthersettingsAction extends AccountSettingsAction
foreach($_shorteners as $name=>$value)
{
$services[$name]=$name;
- if($value['info']['freeService']){
+ if(!empty($value['info']['freeService'])){
// I18N
$services[$name].=' (free service)';
}
diff --git a/actions/profilesettings.php b/actions/profilesettings.php
index 5445d9bb25..0a0cc59973 100644
--- a/actions/profilesettings.php
+++ b/actions/profilesettings.php
@@ -306,6 +306,16 @@ class ProfilesettingsAction extends AccountSettingsAction
$profile->homepage = $homepage;
$profile->bio = $bio;
$profile->location = $location;
+
+ $loc = Location::fromName($location);
+
+ if (!empty($loc)) {
+ $profile->lat = $loc->lat;
+ $profile->lon = $loc->lon;
+ $profile->location_id = $loc->location_id;
+ $profile->location_ns = $loc->location_ns;
+ }
+
$profile->profileurl = common_profile_url($nickname);
common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__);
diff --git a/actions/public.php b/actions/public.php
index 73fad182a3..982dfde157 100644
--- a/actions/public.php
+++ b/actions/public.php
@@ -131,6 +131,13 @@ class PublicAction extends Action
return _('Public timeline');
}
}
+
+ function extraHead()
+ {
+ parent::extraHead();
+ $this->element('meta', array('http-equiv' => 'X-XRDS-Location',
+ 'content' => common_local_url('publicxrds')));
+ }
/**
* Output elements for RSS and Atom feeds
@@ -143,14 +150,12 @@ class PublicAction extends Action
return array(new Feed(Feed::RSS1, common_local_url('publicrss'),
_('Public Stream Feed (RSS 1.0)')),
new Feed(Feed::RSS2,
- common_local_url('api',
- array('apiaction' => 'statuses',
- 'method' => 'public_timeline.rss')),
+ common_local_url('ApiTimelinePublic',
+ array('format' => 'rss')),
_('Public Stream Feed (RSS 2.0)')),
new Feed(Feed::ATOM,
- common_local_url('api',
- array('apiaction' => 'statuses',
- 'method' => 'public_timeline.atom')),
+ common_local_url('ApiTimelinePublic',
+ array('format' => 'atom')),
_('Public Stream Feed (Atom)')));
}
diff --git a/actions/publicxrds.php b/actions/publicxrds.php
new file mode 100644
index 0000000000..5fd4eead7d
--- /dev/null
+++ b/actions/publicxrds.php
@@ -0,0 +1,81 @@
+
+ * @author Robin Millette
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+require_once INSTALLDIR.'/lib/xrdsoutputter.php';
+
+/**
+ * Public XRDS
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @author Robin Millette
+ * @author Craig Andrews
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * @todo factor out similarities with XrdsAction
+ */
+class PublicxrdsAction extends Action
+{
+ /**
+ * Is read only?
+ *
+ * @return boolean true
+ */
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
+ /**
+ * Class handler.
+ *
+ * @param array $args array of arguments
+ *
+ * @return nothing
+ */
+ function handle($args)
+ {
+ parent::handle($args);
+ $xrdsOutputter = new XRDSOutputter();
+ $xrdsOutputter->startXRDS();
+ Event::handle('StartPublicXRDS', array($this,&$xrdsOutputter));
+ Event::handle('EndPublicXRDS', array($this,&$xrdsOutputter));
+ $xrdsOutputter->endXRDS();
+ }
+}
+
diff --git a/actions/register.php b/actions/register.php
index 100ab74242..a6c1a903aa 100644
--- a/actions/register.php
+++ b/actions/register.php
@@ -191,7 +191,7 @@ class RegisterAction extends Action
if (!$this->boolean('license')) {
$this->showForm(_('You can\'t register if you don\'t '.
'agree to the license.'));
- } else if ($email && !Validate::email($email, true)) {
+ } else if ($email && !Validate::email($email, common_config('email', 'check_domain'))) {
$this->showForm(_('Not a valid email address.'));
} else if (!Validate::string($nickname, array('min_length' => 1,
'max_length' => 64,
diff --git a/actions/replies.php b/actions/replies.php
index 6003ad30bd..a13b5a2273 100644
--- a/actions/replies.php
+++ b/actions/replies.php
@@ -138,11 +138,25 @@ class RepliesAction extends OwnerDesignAction
function getFeeds()
{
- $rssurl = common_local_url('repliesrss',
- array('nickname' => $this->user->nickname));
- $rsstitle = sprintf(_('Feed for replies to %s'), $this->user->nickname);
-
- return array(new Feed(Feed::RSS1, $rssurl, $rsstitle));
+ return array(new Feed(Feed::RSS1,
+ common_local_url('repliesrss',
+ array('nickname' => $this->user->nickname)),
+ sprintf(_('Replies feed for %s (RSS 1.0)'),
+ $this->user->nickname)),
+ new Feed(Feed::RSS2,
+ common_local_url('ApiTimelineMentions',
+ array(
+ 'id' => $this->user->nickname,
+ 'format' => 'rss')),
+ sprintf(_('Replies feed for %s (RSS 2.0)'),
+ $this->user->nickname)),
+ new Feed(Feed::ATOM,
+ common_local_url('ApiTimelineMentions',
+ array(
+ 'id' => $this->user->nickname,
+ 'format' => 'atom')),
+ sprintf(_('Replies feed for %s (Atom)'),
+ $this->user->nickname)));
}
/**
diff --git a/actions/showfavorites.php b/actions/showfavorites.php
index b96d2af37f..b12fcdd9a0 100644
--- a/actions/showfavorites.php
+++ b/actions/showfavorites.php
@@ -164,13 +164,25 @@ class ShowfavoritesAction extends OwnerDesignAction
function getFeeds()
{
- $feedurl = common_local_url('favoritesrss',
- array('nickname' =>
- $this->user->nickname));
- $feedtitle = sprintf(_('Feed for favorites of %s'),
- $this->user->nickname);
-
- return array(new Feed(Feed::RSS1, $feedurl, $feedtitle));
+ return array(new Feed(Feed::RSS1,
+ common_local_url('favoritesrss',
+ array('nickname' => $this->user->nickname)),
+ sprintf(_('Feed for favorites of %s (RSS 1.0)'),
+ $this->user->nickname)),
+ new Feed(Feed::RSS2,
+ common_local_url('ApiTimelineFavorites',
+ array(
+ 'id' => $this->user->nickname,
+ 'format' => 'rss')),
+ sprintf(_('Feed for favorites of %s (RSS 2.0)'),
+ $this->user->nickname)),
+ new Feed(Feed::ATOM,
+ common_local_url('ApiTimelineFavorites',
+ array(
+ 'id' => $this->user->nickname,
+ 'format' => 'atom')),
+ sprintf(_('Feed for favorites of %s (Atom)'),
+ $this->user->nickname)));
}
/**
diff --git a/actions/showgroup.php b/actions/showgroup.php
index bfe45ddad7..a4af29391d 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -328,17 +328,15 @@ class ShowgroupAction extends GroupDesignAction
sprintf(_('Notice feed for %s group (RSS 1.0)'),
$this->group->nickname)),
new Feed(Feed::RSS2,
- common_local_url('api',
- array('apiaction' => 'groups',
- 'method' => 'timeline',
- 'argument' => $this->group->nickname.'.rss')),
+ common_local_url('ApiTimelineGroup',
+ array('format' => 'rss',
+ 'id' => $this->group->nickname)),
sprintf(_('Notice feed for %s group (RSS 2.0)'),
$this->group->nickname)),
new Feed(Feed::ATOM,
- common_local_url('api',
- array('apiaction' => 'groups',
- 'method' => 'timeline',
- 'argument' => $this->group->nickname.'.atom')),
+ common_local_url('ApiTimelineGroup',
+ array('format' => 'atom',
+ 'id' => $this->group->nickname)),
sprintf(_('Notice feed for %s group (Atom)'),
$this->group->nickname)),
new Feed(Feed::FOAF,
diff --git a/actions/shownotice.php b/actions/shownotice.php
index 41408c23cc..5d16fdad9e 100644
--- a/actions/shownotice.php
+++ b/actions/shownotice.php
@@ -172,9 +172,9 @@ class ShownoticeAction extends OwnerDesignAction
function title()
{
if (!empty($this->profile->fullname)) {
- $base = $this->profile->fullname . ' (' . $this->user->nickname . ') ';
+ $base = $this->profile->fullname . ' (' . $this->profile->nickname . ') ';
} else {
- $base = $this->user->nickname;
+ $base = $this->profile->nickname;
}
return sprintf(_('%1$s\'s status on %2$s'),
diff --git a/actions/showstream.php b/actions/showstream.php
index b3a9b1f05c..4f48060378 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -128,17 +128,17 @@ class ShowstreamAction extends ProfileAction
sprintf(_('Notice feed for %s (RSS 1.0)'),
$this->user->nickname)),
new Feed(Feed::RSS2,
- common_local_url('api',
- array('apiaction' => 'statuses',
- 'method' => 'user_timeline',
- 'argument' => $this->user->nickname.'.rss')),
+ common_local_url('ApiTimelineUser',
+ array(
+ 'id' => $this->user->nickname,
+ 'format' => 'rss')),
sprintf(_('Notice feed for %s (RSS 2.0)'),
$this->user->nickname)),
new Feed(Feed::ATOM,
- common_local_url('api',
- array('apiaction' => 'statuses',
- 'method' => 'user_timeline',
- 'argument' => $this->user->nickname.'.atom')),
+ common_local_url('ApiTimelineUser',
+ array(
+ 'id' => $this->user->nickname,
+ 'format' => 'atom')),
sprintf(_('Notice feed for %s (Atom)'),
$this->user->nickname)),
new Feed(Feed::FOAF,
@@ -348,6 +348,8 @@ class ShowstreamAction extends ProfileAction
{
if (Event::handle('StartProfilePageActionsSection', array(&$this, $this->profile))) {
+ $cur = common_current_user();
+
$this->elementStart('div', 'entity_actions');
$this->element('h2', null, _('User actions'));
$this->elementStart('ul');
@@ -379,21 +381,21 @@ class ShowstreamAction extends ProfileAction
}
$this->elementEnd('li');
- if ($cur->mutuallySubscribed($user)) {
+ if ($cur->mutuallySubscribed($this->user)) {
// message
$this->elementStart('li', 'entity_send-a-message');
- $this->element('a', array('href' => common_local_url('newmessage', array('to' => $user->id)),
+ $this->element('a', array('href' => common_local_url('newmessage', array('to' => $this->user->id)),
'title' => _('Send a direct message to this user')),
_('Message'));
$this->elementEnd('li');
// nudge
- if ($user->email && $user->emailnotifynudge) {
+ if ($this->user->email && $this->user->emailnotifynudge) {
$this->elementStart('li', 'entity_nudge');
- $nf = new NudgeForm($this, $user);
+ $nf = new NudgeForm($this, $this->user);
$nf->show();
$this->elementEnd('li');
}
diff --git a/actions/tag.php b/actions/tag.php
index f0ab30308c..3a88c1229d 100644
--- a/actions/tag.php
+++ b/actions/tag.php
@@ -86,17 +86,15 @@ class TagAction extends Action
sprintf(_('Notice feed for tag %s (RSS 1.0)'),
$this->tag)),
new Feed(Feed::RSS2,
- common_local_url('api',
- array('apiaction' => 'tags',
- 'method' => 'timeline',
- 'argument' => $this->tag.'.rss')),
- sprintf(_('Notice feed for %s group (RSS 2.0)'),
+ common_local_url('ApiTimelineTag',
+ array('format' => 'rss',
+ 'tag' => $this->tag)),
+ sprintf(_('Notice feed for tag %s (RSS 2.0)'),
$this->tag)),
new Feed(Feed::ATOM,
- common_local_url('api',
- array('apiaction' => 'tags',
- 'method' => 'timeline',
- 'argument' => $this->tag.'.atom')),
+ common_local_url('ApiTimelineTag',
+ array('format' => 'atom',
+ 'tag' => $this->tag)),
sprintf(_('Notice feed for tag %s (Atom)'),
$this->tag)));
}
diff --git a/actions/xrds.php b/actions/xrds.php
index 8ba89fec0f..8f09557d18 100644
--- a/actions/xrds.php
+++ b/actions/xrds.php
@@ -36,6 +36,7 @@ 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';
+require_once INSTALLDIR.'/lib/xrdsoutputter.php';
/**
* XRDS for OpenMicroBlogging
@@ -49,6 +50,8 @@ require_once INSTALLDIR.'/extlib/libomb/xrds_mapper.php';
*/
class XrdsAction extends Action
{
+ var $user;
+
/**
* Is read only?
*
@@ -58,6 +61,18 @@ class XrdsAction extends Action
{
return true;
}
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ $nickname = $this->trimmed('nickname');
+ $this->user = User::staticGet('nickname', $nickname);
+ if (!$this->user) {
+ $this->clientError(_('No such user.'));
+ return;
+ }
+ return true;
+ }
/**
* Class handler.
@@ -69,49 +84,64 @@ class XrdsAction extends Action
function handle($args)
{
parent::handle($args);
- $nickname = $this->trimmed('nickname');
- $user = User::staticGet('nickname', $nickname);
- if (!$user) {
- $this->clientError(_('No such user.'));
- return;
- }
- $this->showXrds($user);
- }
+ $xrdsOutputter = new XRDSOutputter();
+ $xrdsOutputter->startXRDS();
- /**
- * Show XRDS for a user.
- *
- * @param class $user XRDS for this user.
- *
- * @return void
- */
- function showXrds($user)
- {
- $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);
- }
-}
+ Event::handle('StartUserXRDS', array($this,&$xrdsOutputter));
-class Laconica_XRDS_Mapper implements OMB_XRDS_Mapper
-{
- protected $urls;
+ //oauth
+ $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
+ 'xml:id' => 'oauth',
+ 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
+ 'version' => '2.0'));
+ $xrdsOutputter->element('Type', null, 'xri://$xrds*simple');
+ $xrdsOutputter->showXrdsService(OAUTH_ENDPOINT_REQUEST,
+ common_local_url('requesttoken'),
+ array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY, OAUTH_HMAC_SHA1));
+ $xrdsOutputter->showXrdsService( OAUTH_ENDPOINT_AUTHORIZE,
+ common_local_url('userauthorization'),
+ array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY, OAUTH_HMAC_SHA1),
+ null,
+ $this->user->getIdentifierURI());
+ $xrdsOutputter->showXrdsService(OAUTH_ENDPOINT_ACCESS,
+ common_local_url('accesstoken'),
+ array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY, OAUTH_HMAC_SHA1),
+ null,
+ $this->user->getIdentifierURI());
+ $xrdsOutputter->showXrdsService(OAUTH_ENDPOINT_RESOURCE,
+ null,
+ array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY, OAUTH_HMAC_SHA1),
+ null,
+ $this->user->getIdentifierURI());
+ $xrdsOutputter->elementEnd('XRD');
+
+ //omb
+ $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
+ 'xml:id' => 'oauth',
+ 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
+ 'version' => '2.0'));
+ $xrdsOutputter->element('Type', null, 'xri://$xrds*simple');
+ $xrdsOutputter->showXrdsService(OMB_ENDPOINT_POSTNOTICE,
+ common_local_url('postnotice'));
+ $xrdsOutputter->showXrdsService(OMB_ENDPOINT_UPDATEPROFILE,
+ common_local_url('updateprofile'));
+ $xrdsOutputter->elementEnd('XRD');
+
+ //misc
+ $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
+ 'xml:id' => 'oauth',
+ 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
+ 'version' => '2.0'));
+ $xrdsOutputter->showXrdsService(OAUTH_DISCOVERY,
+ '#oauth');
+ $xrdsOutputter->showXrdsService(OMB_VERSION,
+ '#omb');
+ $xrdsOutputter->elementEnd('XRD');
- 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');
- }
+ Event::handle('EndUserXRDS', array($this,&$xrdsOutputter));
- public function getURL($action)
- {
- return common_local_url($this->urls[$action]);
+ $xrdsOutputter->endXRDS();
+
}
}
?>
diff --git a/classes/Avatar.php b/classes/Avatar.php
index 5e8b315fe6..64f105179c 100644
--- a/classes/Avatar.php
+++ b/classes/Avatar.php
@@ -81,7 +81,7 @@ class Avatar extends Memcached_DataObject
if (empty($server)) {
$server = common_config('site', 'server');
}
-
+ common_debug('path = ' . $path);
// XXX: protocol
return 'http://'.$server.$path.$filename;
diff --git a/classes/Location_namespace.php b/classes/Location_namespace.php
new file mode 100644
index 0000000000..5ab95d9ef2
--- /dev/null
+++ b/classes/Location_namespace.php
@@ -0,0 +1,46 @@
+.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+/**
+ * Table Definition for location_namespace
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Location_namespace extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'location_namespace'; // table name
+ public $id; // int(4) primary_key not_null
+ public $description; // varchar(255)
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) {
+ return Memcached_DataObject::staticGet('Location_namespace',$k,$v);
+ }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/classes/Notice.php b/classes/Notice.php
index cd3906ca17..a9dbaa461b 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -66,9 +66,15 @@ class Notice extends Memcached_DataObject
public $is_local; // tinyint(1)
public $source; // varchar(32)
public $conversation; // int(4)
+ public $lat; // decimal(10,7)
+ public $lon; // decimal(10,7)
+ public $location_id; // int(4)
+ public $location_ns; // 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
@@ -162,7 +168,8 @@ class Notice extends Memcached_DataObject
}
static function saveNew($profile_id, $content, $source=null,
- $is_local=Notice::LOCAL_PUBLIC, $reply_to=null, $uri=null, $created=null) {
+ $is_local=Notice::LOCAL_PUBLIC, $reply_to=null, $uri=null, $created=null,
+ $lat=null, $lon=null, $location_id=null, $location_ns=null) {
$profile = Profile::staticGet($profile_id);
@@ -172,7 +179,7 @@ class Notice extends Memcached_DataObject
throw new ClientException(_('Problem saving notice. Too long.'));
}
- if (!$profile) {
+ if (empty($profile)) {
throw new ClientException(_('Problem saving notice. Unknown user.'));
}
@@ -228,6 +235,26 @@ class Notice extends Memcached_DataObject
$notice->conversation = $reply->conversation;
}
+ if (!empty($lat) && !empty($lon)) {
+ $notice->lat = $lat;
+ $notice->lon = $lon;
+ $notice->location_id = $location_id;
+ $notice->location_ns = $location_ns;
+ } else if (!empty($location_ns) && !empty($location_id)) {
+ $location = Location::fromId($location_id, $location_ns);
+ if (!empty($location)) {
+ $notice->lat = $location->lat;
+ $notice->lon = $location->lon;
+ $notice->location_id = $location_id;
+ $notice->location_ns = $location_ns;
+ }
+ } else {
+ $notice->lat = $profile->lat;
+ $notice->lon = $profile->lon;
+ $notice->location_id = $profile->location_id;
+ $notice->location_ns = $profile->location_ns;
+ }
+
if (Event::handle('StartNoticeSave', array(&$notice))) {
// XXX: some of these functions write to the DB
@@ -269,7 +296,6 @@ class Notice extends Memcached_DataObject
// XXX: do we need to change this for remote users?
- $notice->saveReplies();
$notice->saveTags();
$notice->addToInboxes();
@@ -307,11 +333,11 @@ class Notice extends Memcached_DataObject
static function checkDupes($profile_id, $content) {
$profile = Profile::staticGet($profile_id);
- if (!$profile) {
+ if (empty($profile)) {
return false;
}
$notice = $profile->getNotices(0, NOTICE_CACHE_WINDOW);
- if ($notice) {
+ if (!empty($notice)) {
$last = 0;
while ($notice->fetch()) {
if (time() - strtotime($notice->created) >= common_config('site', 'dupelimit')) {
@@ -337,7 +363,7 @@ class Notice extends Memcached_DataObject
static function checkEditThrottle($profile_id) {
$profile = Profile::staticGet($profile_id);
- if (!$profile) {
+ if (empty($profile)) {
return false;
}
# Get the Nth notice
@@ -658,7 +684,7 @@ class Notice extends Memcached_DataObject
$cache = common_memcache();
- if (!$cache) {
+ if (empty($cache)) {
return Notice::getStreamDirect($qry, $offset, $limit, null, null, $order, null);
}
@@ -719,7 +745,7 @@ class Notice extends Memcached_DataObject
# If there are no hits, just return the value
- if (!$notice) {
+ if (empty($notice)) {
return $notice;
}
@@ -909,6 +935,18 @@ class Notice extends Memcached_DataObject
}
}
+ $recipients = $this->saveReplies();
+
+ foreach ($recipients as $recipient) {
+
+ if (!array_key_exists($recipient, $ni)) {
+ $recipientUser = User::staticGet('id', $recipient);
+ if (!empty($recipientUser)) {
+ $ni[$recipient] = NOTICE_INBOX_SOURCE_REPLY;
+ }
+ }
+ }
+
$cnt = 0;
$qryhdr = 'INSERT INTO notice_inbox (user_id, notice_id, source, created) VALUES ';
@@ -1061,12 +1099,12 @@ class Notice extends Memcached_DataObject
for ($i=0; $icreated);
- if (!$recipient) {
+ if (empty($recipient)) {
continue;
}
// Don't save replies from blocked profile to local user
$recipient_user = User::staticGet('id', $recipient->id);
- if ($recipient_user && $recipient_user->hasBlocked($sender)) {
+ if (!empty($recipient_user) && $recipient_user->hasBlocked($sender)) {
continue;
}
$reply = new Reply();
@@ -1077,7 +1115,7 @@ class Notice extends Memcached_DataObject
$last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
common_log(LOG_ERR, 'DB error inserting reply: ' . $last_error->message);
common_server_error(sprintf(_('DB error inserting reply: %s'), $last_error->message));
- return;
+ return array();
} else {
$replied[$recipient->id] = 1;
}
@@ -1101,7 +1139,7 @@ class Notice extends Memcached_DataObject
$id = $reply->insert();
if (!$id) {
common_log_db_error($reply, 'INSERT', __FILE__);
- return;
+ return array();
} else {
$replied[$recipient->id] = 1;
}
@@ -1110,12 +1148,16 @@ class Notice extends Memcached_DataObject
}
}
- foreach (array_keys($replied) as $recipient) {
+ $recipientIds = array_keys($replied);
+
+ foreach ($recipientIds as $recipient) {
$user = User::staticGet('id', $recipient);
if ($user) {
mail_notify_attn($user, $this);
}
}
+
+ return $recipientIds;
}
function asAtomEntry($namespace=false, $source=false)
@@ -1139,10 +1181,9 @@ class Notice extends Memcached_DataObject
$xs->element('link', array('href' => $profile->profileurl));
$user = User::staticGet('id', $profile->id);
if (!empty($user)) {
- $atom_feed = common_local_url('api',
- array('apiaction' => 'statuses',
- 'method' => 'user_timeline',
- 'argument' => $profile->nickname.'.atom'));
+ $atom_feed = common_local_url('ApiTimelineUser',
+ array('format' => 'atom',
+ 'id' => $profile->nickname));
$xs->element('link', array('rel' => 'self',
'type' => 'application/atom+xml',
'href' => $profile->profileurl));
@@ -1370,4 +1411,21 @@ class Notice extends Memcached_DataObject
$contentlimit = self::maxContent();
return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
}
+
+ function getLocation()
+ {
+ $location = null;
+
+ if (!empty($this->location_id) && !empty($this->location_ns)) {
+ $location = Location::fromId($this->location_id, $this->location_ns);
+ }
+
+ if (is_null($location)) { // no ID, or Location::fromId() failed
+ if (!empty($this->lat) && !empty($this->lon)) {
+ $location = Location::fromLatLon($this->lat, $this->lon);
+ }
+ }
+
+ return $location;
+ }
}
diff --git a/classes/Profile.php b/classes/Profile.php
index 4a069ee84e..7c1e9db332 100644
--- a/classes/Profile.php
+++ b/classes/Profile.php
@@ -37,15 +37,26 @@ class Profile extends Memcached_DataObject
public $homepage; // varchar(255) multiple_key
public $bio; // text() multiple_key
public $location; // varchar(255) multiple_key
+ public $lat; // decimal(10,7)
+ public $lon; // decimal(10,7)
+ public $location_id; // int(4)
+ public $location_ns; // int(4)
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
+ function getUser()
+ {
+ return User::staticGet('id', $this->id);
+ }
+
function getAvatar($width, $height=null)
{
if (is_null($height)) {
@@ -551,4 +562,29 @@ class Profile extends Memcached_DataObject
$block->blocked = $this->id;
$block->delete();
}
+
+ // XXX: identical to Notice::getLocation.
+
+ function getLocation()
+ {
+ $location = null;
+
+ if (!empty($this->location_id) && !empty($this->location_ns)) {
+ $location = Location::fromId($this->location_id, $this->location_ns);
+ }
+
+ if (is_null($location)) { // no ID, or Location::fromId() failed
+ if (!empty($this->lat) && !empty($this->lon)) {
+ $location = Location::fromLatLon($this->lat, $this->lon);
+ }
+ }
+
+ if (is_null($location)) { // still haven't found it!
+ if (!empty($this->location)) {
+ $location = Location::fromName($this->location);
+ }
+ }
+
+ return $location;
+ }
}
diff --git a/classes/User.php b/classes/User.php
index 0a70c98014..3fa9cc1526 100644
--- a/classes/User.php
+++ b/classes/User.php
@@ -198,6 +198,15 @@ class User extends Memcached_DataObject
}
if (!empty($location)) {
$profile->location = $location;
+
+ $loc = Location::fromName($location);
+
+ if (!empty($loc)) {
+ $profile->lat = $loc->lat;
+ $profile->lon = $loc->lon;
+ $profile->location_id = $loc->location_id;
+ $profile->location_ns = $loc->location_ns;
+ }
}
$profile->created = common_sql_now();
@@ -319,6 +328,7 @@ class User extends Memcached_DataObject
common_config('site', 'name'),
$user->nickname),
'system');
+ common_broadcast_notice($notice);
}
}
diff --git a/classes/statusnet.ini b/classes/statusnet.ini
index 453981cd64..623790b100 100644
--- a/classes/statusnet.ini
+++ b/classes/statusnet.ini
@@ -1,3 +1,4 @@
+
[avatar]
profile_id = 129
original = 17
@@ -243,6 +244,15 @@ created = 142
[invitation__keys]
code = K
+[location_namespace]
+id = 129
+description = 2
+created = 142
+modified = 384
+
+[location_namespace__keys]
+id = K
+
[message]
id = 129
uri = 2
@@ -284,6 +294,10 @@ reply_to = 1
is_local = 17
source = 2
conversation = 1
+lat = 1
+lon = 1
+location_id = 1
+location_ns = 1
[notice__keys]
id = N
@@ -325,6 +339,10 @@ profileurl = 2
homepage = 2
bio = 34
location = 2
+lat = 1
+lon = 1
+location_id = 1
+location_ns = 1
created = 142
modified = 384
@@ -519,6 +537,16 @@ modified = 384
canonical = K
display = U
+[user_openid_trustroot]
+trustroot = 130
+user_id = 129
+created = 142
+modified = 384
+
+[user_openid__keys]
+trustroot = K
+user_id = K
+
[user_role]
user_id = 129
role = 130
diff --git a/config.php.sample b/config.php.sample
index 997c9d6b0b..9fccb84f3b 100644
--- a/config.php.sample
+++ b/config.php.sample
@@ -104,6 +104,10 @@ $config['sphinx']['port'] = 3312;
// $config['site']['timezone'] = 'Pacific/Auckland';
// $config['site']['language'] = 'en_NZ';
+// When validating user supplied email addresses, validate if the domain
+// is running an SMTP server.
+// $config['mail']['check_domain'] = true;
+
// Email info, used for all outbound email
// $config['mail']['notifyfrom'] = 'microblog@example.net';
// $config['mail']['domain'] = 'microblog.example.net';
diff --git a/db/location_namespace.sql b/db/location_namespace.sql
new file mode 100644
index 0000000000..59b2ce67c3
--- /dev/null
+++ b/db/location_namespace.sql
@@ -0,0 +1,5 @@
+insert into location_namespace
+ (id, description, created)
+values
+ (1, 'Geonames', now()),
+ (2, 'Where on Earth', now());
diff --git a/db/statusnet.sql b/db/statusnet.sql
index dfcddb643c..1524d83957 100644
--- a/db/statusnet.sql
+++ b/db/statusnet.sql
@@ -1,6 +1,7 @@
/* local and remote users have profiles */
create table profile (
+
id integer auto_increment primary key comment 'unique identifier',
nickname varchar(64) not null comment 'nickname or username',
fullname varchar(255) comment 'display name',
@@ -8,6 +9,11 @@ create table profile (
homepage varchar(255) comment 'identifying URL',
bio text comment 'descriptive biography',
location varchar(255) comment 'physical location',
+ lat decimal(10,7) comment 'latitude',
+ lon decimal(10,7) comment 'longitude',
+ location_id integer comment 'location id if possible',
+ location_ns integer comment 'namespace for location',
+
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified',
@@ -119,6 +125,10 @@ create table notice (
is_local tinyint default 0 comment 'notice was generated by a user',
source varchar(32) comment 'source of comment, like "web", "im", or "clientname"',
conversation integer comment 'id of root notice in this conversation' references notice (id),
+ lat decimal(10,7) comment 'latitude',
+ lon decimal(10,7) comment 'longitude',
+ location_id integer comment 'location id if possible',
+ location_ns integer comment 'namespace for location',
index notice_profile_id_idx (profile_id),
index notice_conversation_idx (conversation),
@@ -556,3 +566,12 @@ create table user_role (
constraint primary key (user_id, role)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table location_namespace (
+
+ id integer primary key comment 'identity for this namespace',
+ description varchar(255) comment 'description of the namespace',
+ created datetime not null comment 'date the record was created',
+ modified timestamp comment 'date this record was modified'
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
diff --git a/doc-src/bookmarklet b/doc-src/bookmarklet
index e5ded77023..6ce23bd0b6 100644
--- a/doc-src/bookmarklet
+++ b/doc-src/bookmarklet
@@ -2,4 +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.
-Post to %%site.name%%
+Post to %%site.name%%
diff --git a/extlib/Auth/OpenID.php b/extlib/Auth/OpenID.php
index 6556b5b01e..db6164256f 100644
--- a/extlib/Auth/OpenID.php
+++ b/extlib/Auth/OpenID.php
@@ -20,7 +20,7 @@
/**
* The library version string
*/
-define('Auth_OpenID_VERSION', '2.1.2');
+define('Auth_OpenID_VERSION', '2.1.3');
/**
* Require the fetcher code.
diff --git a/extlib/Auth/OpenID/BigMath.php b/extlib/Auth/OpenID/BigMath.php
index b5fc627a09..45104947d6 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 (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode') && @dl($module . "." . PHP_SHLIB_SUFFIX)) {
+ if (@dl($module . "." . PHP_SHLIB_SUFFIX)) {
$loaded = true;
break;
}
diff --git a/extlib/Auth/OpenID/Consumer.php b/extlib/Auth/OpenID/Consumer.php
index a72684c6b8..500890b656 100644
--- a/extlib/Auth/OpenID/Consumer.php
+++ b/extlib/Auth/OpenID/Consumer.php
@@ -1295,7 +1295,8 @@ class Auth_OpenID_GenericConsumer {
Auth_OpenID_OPENID2_NS => array_merge($basic_sig_fields,
array('response_nonce',
'claimed_id',
- 'assoc_handle')),
+ 'assoc_handle',
+ 'op_endpoint')),
Auth_OpenID_OPENID1_NS => array_merge($basic_sig_fields,
array('nonce'))
);
diff --git a/extlib/Auth/OpenID/Message.php b/extlib/Auth/OpenID/Message.php
index fd23e67a3c..5ab115a86e 100644
--- a/extlib/Auth/OpenID/Message.php
+++ b/extlib/Auth/OpenID/Message.php
@@ -887,6 +887,11 @@ class Auth_OpenID_Message {
function getAliasedArg($aliased_key, $default = null)
{
+ if ($aliased_key == 'ns') {
+ // Return the namespace URI for the OpenID namespace
+ return $this->getOpenIDNamespace();
+ }
+
$parts = explode('.', $aliased_key, 2);
if (count($parts) != 2) {
diff --git a/extlib/Auth/Yadis/HTTPFetcher.php b/extlib/Auth/Yadis/HTTPFetcher.php
index a1825403d6..963b9a49a4 100644
--- a/extlib/Auth/Yadis/HTTPFetcher.php
+++ b/extlib/Auth/Yadis/HTTPFetcher.php
@@ -138,7 +138,7 @@ class Auth_Yadis_HTTPFetcher {
* pass the URLHasAllowedScheme check or if the server's response
* is malformed.
*/
- function get($url, $headers)
+ function get($url, $headers = null)
{
trigger_error("not implemented", E_USER_ERROR);
}
diff --git a/extlib/Auth/Yadis/ParanoidHTTPFetcher.php b/extlib/Auth/Yadis/ParanoidHTTPFetcher.php
index 8975d7f89e..6a418260ee 100644
--- a/extlib/Auth/Yadis/ParanoidHTTPFetcher.php
+++ b/extlib/Auth/Yadis/ParanoidHTTPFetcher.php
@@ -127,8 +127,6 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
Auth_OpenID_USER_AGENT.' '.$curl_user_agent);
curl_setopt($c, CURLOPT_TIMEOUT, $off);
curl_setopt($c, CURLOPT_URL, $url);
- curl_setopt($c, CURLOPT_RANGE,
- "0-".(1024 * Auth_OpenID_FETCHER_MAX_RESPONSE_KB));
curl_exec($c);
diff --git a/extlib/Auth/Yadis/PlainHTTPFetcher.php b/extlib/Auth/Yadis/PlainHTTPFetcher.php
index 8882e3cef7..3e0ca2bb0c 100644
--- a/extlib/Auth/Yadis/PlainHTTPFetcher.php
+++ b/extlib/Auth/Yadis/PlainHTTPFetcher.php
@@ -83,8 +83,6 @@ class Auth_Yadis_PlainHTTPFetcher extends Auth_Yadis_HTTPFetcher {
"User-Agent: $user_agent",
"Host: ".$parts['host'].
($specify_port ? ":".$parts['port'] : ""),
- "Range: 0-".
- (1024*Auth_OpenID_FETCHER_MAX_RESPONSE_KB),
"Port: ".$parts['port']);
$errno = 0;
diff --git a/extlib/Auth/Yadis/XML.php b/extlib/Auth/Yadis/XML.php
index 7232d6cbdd..81b2ce2210 100644
--- a/extlib/Auth/Yadis/XML.php
+++ b/extlib/Auth/Yadis/XML.php
@@ -91,7 +91,7 @@ class Auth_Yadis_XMLParser {
* @return array $node_list An array of matching opaque node
* objects to be used with other methods of this parser class.
*/
- function evalXPath($xpath, $node = null)
+ function &evalXPath($xpath, $node = null)
{
// Not implemented.
}
@@ -349,7 +349,7 @@ function &Auth_Yadis_getXMLParser()
foreach ($extensions as $name => $params) {
if (!extension_loaded($name)) {
foreach ($params['libname'] as $libname) {
- if (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode') && @dl($libname)) {
+ if (@dl($libname)) {
$classname = $params['classname'];
}
}
diff --git a/extlib/DB/DataObject.php b/extlib/DB/DataObject.php
index 8e226b8fa9..60ff1441b5 100644
--- a/extlib/DB/DataObject.php
+++ b/extlib/DB/DataObject.php
@@ -235,7 +235,7 @@ class DB_DataObject extends DB_DataObject_Overload
* @access private
* @var string
*/
- var $_DB_DataObject_version = "1.8.11";
+ var $_DB_DataObject_version = "1.8.12";
/**
* The Database table (used by table extends)
diff --git a/extlib/DB/DataObject/Cast.php b/extlib/DB/DataObject/Cast.php
index 095d2a4d25..2049beb581 100644
--- a/extlib/DB/DataObject/Cast.php
+++ b/extlib/DB/DataObject/Cast.php
@@ -6,9 +6,9 @@
*
* PHP versions 4 and 5
*
- * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
* that is available through the world-wide-web at the following URI:
- * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * http://www.php.net/license/3_01.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
@@ -16,8 +16,8 @@
* @package DB_DataObject
* @author Alan Knowles
* @copyright 1997-2008 The PHP Group
- * @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version CVS: $Id: Cast.php 264148 2008-08-04 03:44:59Z alan_k $
+ * @license http://www.php.net/license/3_01.txt PHP License 3.01
+ * @version CVS: $Id: Cast.php 287158 2009-08-12 13:58:31Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
diff --git a/extlib/DB/DataObject/Error.php b/extlib/DB/DataObject/Error.php
index 3821154537..cdb863d83c 100644
--- a/extlib/DB/DataObject/Error.php
+++ b/extlib/DB/DataObject/Error.php
@@ -7,9 +7,9 @@
*
* PHP versions 4 and 5
*
- * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
* that is available through the world-wide-web at the following URI:
- * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * http://www.php.net/license/3_01.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
@@ -17,8 +17,8 @@
* @package DB_DataObject
* @author Alan Knowles
* @copyright 1997-2006 The PHP Group
- * @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version CVS: $Id: Error.php 277015 2009-03-12 05:51:03Z alan_k $
+ * @license http://www.php.net/license/3_01.txt PHP License 3.01
+ * @version CVS: $Id: Error.php 287158 2009-08-12 13:58:31Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
diff --git a/extlib/README b/extlib/README
new file mode 100644
index 0000000000..cfc2f9c8c4
--- /dev/null
+++ b/extlib/README
@@ -0,0 +1,58 @@
+DO NOT "FIX" CODE IN THIS DIRECTORY.
+
+ONLY UPSTREAM VERSIONS OF SOFTWARE GO IN THIS DIRECTORY.
+
+This directory is provided as a courtesy to our users who might be
+unable or unwilling to find and install libraries we depend on.
+
+If we "fix" software in this directory, we hamstring users who do the
+right thing and keep a single version of upstream libraries in a
+system-wide library. We introduce subtle and maddening bugs where
+our code is "accidentally" using the "wrong" library version. We may
+unwittingly interfere with other software that depends on the
+canonical release versions of those same libraries!
+
+Forking upstream software for trivial reasons makes us bad citizens in
+the Open Source community and adds unnecessary heartache for our
+users. Don't make us "that" project.
+
+FAQ:
+
+Q: What should we do when we find a bug in upstream software?
+
+A: First and foremost, REPORT THE BUG, and if possible send in a patch.
+
+ Watch for a release of the upstream software and integrate with it
+ when it's released.
+
+ In the meantime, work around the bug, if at all possible. Usually,
+ it's quite possible, if slightly harder or less efficient.
+
+Q: What if the bug can't be worked around?
+
+A: If the upstream developers have accepted a bug patch, it's
+ undesirable but acceptable to apply that patch to the library in
+ the extlib dir. Ideally, use a release version for upstream or a
+ version control system snapshot.
+
+ Note that this is a last resort.
+
+Q: What if upstream is unresponsive or won't accept a patch?
+
+A: Try again.
+
+Q: I tried again, and upstream is still unresponsive and nobody's
+ checked on my patch. Now what?
+
+A: If the upstream project is moribund and there's a way to adopt it,
+ propose having the StatusNet dev team adopt the project. Or, adopt
+ it yourself.
+
+Q: What if there's no upstream authority and it can't be adopted?
+
+A: Then we fork it. Make a new name and a new version. Include it in
+ lib/ instead of extlib/, and use the StatusNet_* prefix to change
+ the namespace to avoid collisions.
+
+ This is a last resort; consult with the rest of the dev group
+ before taking this radical step.
diff --git a/extlib/php-gettext/ChangeLog b/extlib/php-gettext/ChangeLog
index 5e0949dfd7..ab77d80810 100644
--- a/extlib/php-gettext/ChangeLog
+++ b/extlib/php-gettext/ChangeLog
@@ -1,3 +1,19 @@
+2006-02-28 Danilo Šegan
+
+ * gettext.php: Added some comments about these workarounds for
+ different PHP versions and architectures.
+
+2006-02-28 Danilo Šegan
+
+ Fixes bug #15923.
+
+ * gettext.php (gettext_reader): make magic check work on 64-bit
+ platforms as well (by Steffen Pingel).
+
+2006-02-20 Danilo Šegan
+
+ * gettext.inc (_bindtextdomain): Use php_uname to detect Windows.
+
2006-02-07 Danilo Šegan
* examples/pigs_dropin.php: comment-out bind_textdomain_codeset
diff --git a/extlib/php-gettext/gettext.inc b/extlib/php-gettext/gettext.inc
index eb94b256a6..7c95e40ec2 100644
--- a/extlib/php-gettext/gettext.inc
+++ b/extlib/php-gettext/gettext.inc
@@ -129,7 +129,7 @@ function _setlocale($category, $locale) {
$ret = 0;
if (function_exists('setlocale')) // I don't know if this ever happens ;)
$ret = setlocale($category, $locale);
- if (($ret and $locale == '') or ($ret == $locale)) {
+ if ($ret and ($locale == '' or $ret == $locale)) {
$EMULATEGETTEXT = 0;
$CURRENTLOCALE = $ret;
} else {
@@ -148,9 +148,9 @@ function _setlocale($category, $locale) {
*/
function _bindtextdomain($domain, $path) {
global $text_domains;
- // ensure $path ends with a slash
- if ($path[strlen($path) - 1] != '/') $path .= '/';
- elseif ($path[strlen($path) - 1] != '\\') $path .= '\\';
+ // ensure $path ends with a slash
+ if ($path[strlen($path) - 1] != '/') $path .= '/';
+ elseif ($path[strlen($path) - 1] != '\\') $path .= '\\';
$text_domains[$domain]->path = $path;
}
diff --git a/extlib/php-gettext/gettext.php b/extlib/php-gettext/gettext.php
index ad94a987b7..cd080444ca 100644
--- a/extlib/php-gettext/gettext.php
+++ b/extlib/php-gettext/gettext.php
@@ -102,16 +102,16 @@ class gettext_reader {
// Caching can be turned off
$this->enable_cache = $enable_cache;
- // $MAGIC1 = (int)0x950412de; //bug in PHP 5
+ // $MAGIC1 = (int)0x950412de; //bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565
$MAGIC1 = (int) - 1794895138;
// $MAGIC2 = (int)0xde120495; //bug
$MAGIC2 = (int) - 569244523;
$this->STREAM = $Reader;
$magic = $this->readint();
- if ($magic == $MAGIC1) {
+ if ($magic == ($MAGIC1 & 0xFFFFFFFF)) { // to make sure it works for 64-bit platforms
$this->BYTEORDER = 0;
- } elseif ($magic == $MAGIC2) {
+ } elseif ($magic == ($MAGIC2 & 0xFFFFFFFF)) {
$this->BYTEORDER = 1;
} else {
$this->error = 1; // not MO file
diff --git a/htaccess.sample b/htaccess.sample
index 37eb8e01ec..91ae9da9be 100644
--- a/htaccess.sample
+++ b/htaccess.sample
@@ -5,6 +5,14 @@
RewriteBase /mublog/
+ # If your site is private and want to only allow logged-in users to
+ # be able to download file attachments, uncomment this rule.
+ #
+ # If you have a custom attachment path
+ # ($config['attachments']['path']), change "file/" to match.
+ #
+ #RewriteRule ^file/(.*) getfile/$1
+
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php?p=$1 [L,QSA]
diff --git a/index.php b/index.php
index 644812bd55..3acdba3754 100644
--- a/index.php
+++ b/index.php
@@ -143,7 +143,7 @@ function checkMirror($action_obj, $args)
function isLoginAction($action)
{
- static $loginActions = array('login', 'recoverpassword', 'api', 'doc', 'register');
+ static $loginActions = array('login', 'recoverpassword', 'api', 'doc', 'register', 'publicxrds');
$login = null;
diff --git a/install.php b/install.php
index 319c261e41..6bfc4c2e21 100644
--- a/install.php
+++ b/install.php
@@ -692,9 +692,7 @@ function writeConf($sitename, $server, $path, $fancy, $db)
// database
"\$config['db']['database'] = '{$db['database']}';\n\n".
($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
- "\$config['db']['type'] = '{$db['type']}';\n\n".
-
- "?>";
+ "\$config['db']['type'] = '{$db['type']}';\n\n";
// write configuration file out to install directory
$res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
diff --git a/js/util.js b/js/util.js
index 0a943512f2..663ec89863 100644
--- a/js/util.js
+++ b/js/util.js
@@ -14,370 +14,377 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
+ *
+ * @category UI interaction
+ * @package StatusNet
+ * @author Sarven Capadisli
+ * @author Evan Prodromou
+ * @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/
*/
$(document).ready(function(){
- var counterBlackout = false;
-
- // count character on keyup
- function counter(event){
- if (maxLength <= 0) {
- return;
- }
- var currentLength = $("#notice_data-text").val().length;
- var remaining = maxLength - currentLength;
- var counter = $("#notice_text-count");
-
- if (remaining.toString() != counter.text()) {
- if (!counterBlackout || remaining == 0) {
- if (counter.text() != String(remaining)) {
- counter.text(remaining);
- }
+ if ($('body.user_in').length > 0) {
+ $('.'+SN.C.S.FormNotice).each(function() { SN.U.FormNoticeEnhancements($(this)); });
- if (remaining < 0) {
- $("#form_notice").addClass("warning");
- } else {
- $("#form_notice").removeClass("warning");
- }
- // Skip updates for the next 500ms.
- // On slower hardware, updating on every keypress is unpleasant.
- if (!counterBlackout) {
- counterBlackout = true;
- window.setTimeout(clearCounterBlackout, 500);
- }
- }
- }
- }
-
- function clearCounterBlackout() {
- // Allow keyup events to poke the counter again
- counterBlackout = false;
- // Check if the string changed since we last looked
- counter(null);
- }
+ $('.form_user_subscribe').each(function() { SN.U.FormXHR($(this)); });
+ $('.form_user_unsubscribe').each(function() { SN.U.FormXHR($(this)); });
+ $('.form_favor').each(function() { SN.U.FormXHR($(this)); });
+ $('.form_disfavor').each(function() { SN.U.FormXHR($(this)); });
+ $('.form_group_join').each(function() { SN.U.FormXHR($(this)); });
+ $('.form_group_leave').each(function() { SN.U.FormXHR($(this)); });
+ $('.form_user_nudge').each(function() { SN.U.FormXHR($(this)); });
- function submitonreturn(event) {
- if (event.keyCode == 13 || event.keyCode == 10) {
- // iPhone sends \n not \r for 'return'
- $("#form_notice").submit();
- event.preventDefault();
- event.stopPropagation();
- $("#notice_data-text").blur();
- $("body").focus();
- return false;
- }
- return true;
- }
+ SN.U.NoticeReply();
- // define maxLength if it wasn't defined already
+ SN.U.NoticeDataAttach();
- if (typeof(maxLength) == "undefined") {
- maxLength = 140;
+ SN.U.NewDirectMessage();
}
- if ($("#notice_data-text").length) {
- if (maxLength > 0) {
- $("#notice_data-text").bind("keyup", 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();
- }
- }
-
- // XXX: refactor this code
-
- var favoptions = { dataType: 'xml',
- beforeSubmit: function(data, target, options) {
- $(target).addClass('processing');
- return true;
- },
- success: function(xml) { var new_form = document._importNode($('form', xml).get(0), true);
- var dis = new_form.id;
- var fav = dis.replace('disfavor', 'favor');
- $('form#'+fav).replaceWith(new_form);
- $('form#'+dis).ajaxForm(disoptions).each(addAjaxHidden);
- }
- };
-
- var disoptions = { dataType: 'xml',
- beforeSubmit: function(data, target, options) {
- $(target).addClass('processing');
- return true;
- },
- success: function(xml) { var new_form = document._importNode($('form', xml).get(0), true);
- var fav = new_form.id;
- var dis = fav.replace('favor', 'disfavor');
- $('form#'+dis).replaceWith(new_form);
- $('form#'+fav).ajaxForm(favoptions).each(addAjaxHidden);
- }
- };
-
- var joinoptions = { dataType: 'xml',
- success: function(xml) { var new_form = document._importNode($('form', xml).get(0), true);
- var leave = new_form.id;
- var join = leave.replace('leave', 'join');
- $('form#'+join).replaceWith(new_form);
- $('form#'+leave).ajaxForm(leaveoptions).each(addAjaxHidden);
- }
- };
-
- var leaveoptions = { dataType: 'xml',
- success: function(xml) { var new_form = document._importNode($('form', xml).get(0), true);
- var join = new_form.id;
- var leave = join.replace('join', 'leave');
- $('form#'+leave).replaceWith(new_form);
- $('form#'+join).ajaxForm(joinoptions).each(addAjaxHidden);
- }
- };
-
- function addAjaxHidden() {
- var ajax = document.createElement('input');
- ajax.setAttribute('type', 'hidden');
- ajax.setAttribute('name', 'ajax');
- ajax.setAttribute('value', 1);
- this.appendChild(ajax);
- }
-
- $("form.form_favor").ajaxForm(favoptions);
- $("form.form_disfavor").ajaxForm(disoptions);
- $("form.form_group_join").ajaxForm(joinoptions);
- $("form.form_group_leave").ajaxForm(leaveoptions);
- $("form.form_favor").each(addAjaxHidden);
- $("form.form_disfavor").each(addAjaxHidden);
- $("form.form_group_join").each(addAjaxHidden);
- $("form.form_group_leave").each(addAjaxHidden);
-
- $("#form_user_nudge").ajaxForm ({ dataType: 'xml',
- beforeSubmit: function(xml) { $("#form_user_nudge input[type=submit]").attr("disabled", "disabled");
- $("#form_user_nudge input[type=submit]").addClass("disabled");
- },
- success: function(xml) { $("#form_user_nudge").replaceWith(document._importNode($("#nudge_response", xml).get(0),true));
- $("#form_user_nudge input[type=submit]").removeAttr("disabled");
- $("#form_user_nudge input[type=submit]").removeClass("disabled");
- }
- });
- $("#form_user_nudge").each(addAjaxHidden);
-
- var Subscribe = { dataType: 'xml',
- beforeSubmit: function(formData, jqForm, options) { $(".form_user_subscribe input[type=submit]").attr("disabled", "disabled");
- $(".form_user_subscribe input[type=submit]").addClass("disabled");
- },
- success: function(xml) { var form_unsubscribe = document._importNode($('form', xml).get(0), true);
- var form_unsubscribe_id = form_unsubscribe.id;
- var form_subscribe_id = form_unsubscribe_id.replace('unsubscribe', 'subscribe');
- $("form#"+form_subscribe_id).replaceWith(form_unsubscribe);
- $("form#"+form_unsubscribe_id).ajaxForm(UnSubscribe).each(addAjaxHidden);
- $("dd.subscribers").text(parseInt($("dd.subscribers").text())+1);
- $(".form_user_subscribe input[type=submit]").removeAttr("disabled");
- $(".form_user_subscribe input[type=submit]").removeClass("disabled");
- }
- };
-
- var UnSubscribe = { dataType: 'xml',
- beforeSubmit: function(formData, jqForm, options) { $(".form_user_unsubscribe input[type=submit]").attr("disabled", "disabled");
- $(".form_user_unsubscribe input[type=submit]").addClass("disabled");
- },
- success: function(xml) { var form_subscribe = document._importNode($('form', xml).get(0), true);
- var form_subscribe_id = form_subscribe.id;
- var form_unsubscribe_id = form_subscribe_id.replace('subscribe', 'unsubscribe');
- $("form#"+form_unsubscribe_id).replaceWith(form_subscribe);
- $("form#"+form_subscribe_id).ajaxForm(Subscribe).each(addAjaxHidden);
- $("#profile_send_a_new_message").remove();
- $("#profile_nudge").remove();
- $("dd.subscribers").text(parseInt($("dd.subscribers").text())-1);
- $(".form_user_unsubscribe input[type=submit]").removeAttr("disabled");
- $(".form_user_unsubscribe input[type=submit]").removeClass("disabled");
- }
- };
-
- $(".form_user_subscribe").ajaxForm(Subscribe);
- $(".form_user_unsubscribe").ajaxForm(UnSubscribe);
- $(".form_user_subscribe").each(addAjaxHidden);
- $(".form_user_unsubscribe").each(addAjaxHidden);
-
- var PostNotice = { dataType: 'xml',
- beforeSubmit: function(formData, jqForm, options) { if ($("#notice_data-text").get(0).value.length == 0) {
- $("#form_notice").addClass("warning");
- return false;
- }
- $("#form_notice").addClass("processing");
- $("#notice_action-submit").attr("disabled", "disabled");
- $("#notice_action-submit").addClass("disabled");
- return true;
- },
- timeout: '60000',
- error: function (xhr, textStatus, errorThrown) { $("#form_notice").removeClass("processing");
- $("#notice_action-submit").removeAttr("disabled");
- $("#notice_action-submit").removeClass("disabled");
- if (textStatus == "timeout") {
- alert ("Sorry! We had trouble sending your notice. The servers are overloaded. Please try again, and contact the site administrator if this problem persists");
- }
- else {
- if ($(".error", xhr.responseXML).length > 0) {
- $('#form_notice').append(document._importNode($(".error", xhr.responseXML).get(0), true));
- }
- else {
- var HTTP20x30x = [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307];
- if(jQuery.inArray(parseInt(xhr.status), HTTP20x30x) < 0) {
- alert("Sorry! We had trouble sending your notice ("+xhr.status+" "+xhr.statusText+"). Please report the problem to the site administrator if this happens again.");
- }
- else {
- $("#notice_data-text").val("");
- if (maxLength > 0) {
- counter();
- }
- }
- }
- }
- },
- success: function(xml) { if ($("#error", xml).length > 0) {
- var result = document._importNode($("p", xml).get(0), true);
- result = result.textContent || result.innerHTML;
- alert(result);
- }
- else {
- if ($("#command_result", xml).length > 0) {
- var result = document._importNode($("p", xml).get(0), true);
- result = result.textContent || result.innerHTML;
- alert(result);
- }
- else {
- li = $("li", xml).get(0);
- if ($("#"+li.id).length == 0) {
- var notice_irt_value = $('#notice_in-reply-to').val();
- var notice_irt = '#notices_primary #notice-'+notice_irt_value;
- if($('body')[0].id == 'conversation') {
- if(notice_irt_value.length > 0 && $(notice_irt+' .notices').length < 1) {
- $(notice_irt).append('
',
- $title, $logout_url, $text);
-
- $action->raw($html);
-
- } else {
- $action->menuItem(common_local_url('logout'),
- _('Logout'), _('Logout from the site'), false, 'nav_logout');
- }
- }
- else {
- if (!common_config('site', 'openidonly')) {
- if (!common_config('site', 'closed')) {
- $action->menuItem(common_local_url('register'),
- _('Register'), _('Create an account'), false, 'nav_register');
- }
- $action->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');
- }
- }
-
- $action->menuItem(common_local_url('doc', array('title' => 'help')),
- _('Help'), _('Help me!'), false, 'nav_help');
- if ($user || !common_config('site', 'private')) {
- $action->menuItem(common_local_url('peoplesearch'),
- _('Search'), _('Search for people or text'), false, 'nav_search');
- }
-
- // We are replacing the primary nav entirely; give other
- // plugins a chance to handle it here.
-
- Event::handle('EndPrimaryNav', array($action));
-
- return false;
+ return true;
}
+ /**
+ * Alter the local nav menu to have a Facebook Connect login and
+ * settings pages
+ *
+ * @param Action $action the current action
+ *
+ * @return void
+ *
+ */
+
function onStartShowLocalNavBlock($action)
{
- $action_name = get_class($action);
+ $action_name = get_class($action);
$login_actions = array('LoginAction', 'RegisterAction',
'OpenidloginAction', 'FBConnectLoginAction');
@@ -356,8 +429,16 @@ class FBConnectPlugin extends Plugin
return true;
}
+ /**
+ * Have the logout process do some Facebook Connect cookie cleanup
+ *
+ * @param Action $action the current action
+ *
+ * @return void
+ */
+
function onStartLogout($action)
-{
+ {
$action->logout();
$fbuid = $this->loggedIn();
@@ -375,9 +456,16 @@ class FBConnectPlugin extends Plugin
return true;
}
+ /**
+ * Get the URL of the user's Facebook avatar
+ *
+ * @param int $fbuid the Facebook user ID
+ *
+ * @return string $url the url for the user's Facebook avatar
+ */
+
function getProfilePicURL($fbuid)
{
-
$facebook = getFacebook();
$url = null;
@@ -396,8 +484,70 @@ class FBConnectPlugin extends Plugin
"Facebook client failure requesting profile pic!");
}
- return $url;
+ return $url;
+ }
+ /**
+ * Add a Facebook queue item for each notice
+ *
+ * @param Notice $notice the notice
+ * @param array &$transports the list of transports (queues)
+ *
+ * @return boolean hook return
+ */
+
+ function onStartEnqueueNotice($notice, &$transports)
+ {
+ array_push($transports, 'facebook');
+ return true;
+ }
+
+ /**
+ * broadcast the message when not using queuehandler
+ *
+ * @param Notice &$notice the notice
+ * @param array $queue destination queue
+ *
+ * @return boolean hook return
+ */
+
+ function onUnqueueHandleNotice(&$notice, $queue)
+ {
+ if (($queue == 'facebook') && ($this->_isLocal($notice))) {
+ facebookBroadcastNotice($notice);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Determine whether the notice was locally created
+ *
+ * @param Notice $notice the notice
+ *
+ * @return boolean locality
+ */
+
+ function _isLocal($notice)
+ {
+ return ($notice->is_local == Notice::LOCAL_PUBLIC ||
+ $notice->is_local == Notice::LOCAL_NONPUBLIC);
+ }
+
+ /**
+ * Add Facebook queuehandler to the list of daemons to start
+ *
+ * @param array $daemons the list fo daemons to run
+ *
+ * @return boolean hook return
+ *
+ */
+
+ function onGetValidDaemons($daemons)
+ {
+ array_push($daemons, INSTALLDIR .
+ '/plugins/Facebook/facebookqueuehandler.php');
+ return true;
}
}
diff --git a/plugins/Facebook/README b/plugins/Facebook/README
new file mode 100644
index 0000000000..bf2f4a1800
--- /dev/null
+++ b/plugins/Facebook/README
@@ -0,0 +1,129 @@
+This plugin allows you to use Facebook Connect with StatusNet, provides a
+Facebook application for your users, and allows them to update their
+Facebook statuses from StatusNet.
+
+Facebook Connect
+----------------
+
+Facebook connect allows users to register and login using nothing but their
+Facebook credentials. With Facebook Connect, your users can:
+
+- Authenticate (register/login/logout -- works similar to OpenID)
+- Associate an existing StatusNet account with a Facebook account
+- Disconnect a Facebook account from a StatusNet account
+
+Built-in Facebook Application
+-----------------------------
+
+The plugin also installs a StatusNet Facebook application that allows your
+users to automatically update their Facebook statuses with their latest
+notices, invite their friends to use the app (and thus your site), view
+their notice timelines, and post notices -- all from within Facebook. The
+application is built into the StatusNet Facebook plugin and runs on your
+host.
+
+Quick setup instructions*
+-------------------------
+
+Install the Facebook Developer application on Facebook:
+
+ http://www.facebook.com/developers/
+
+Use it to create a new application and generate an API key and secret. Add a
+Facebook app section of your config.php and copy in the key and secret,
+e.g.:
+
+ // Config section for the built-in Facebook application
+ $config['facebook']['apikey'] = 'APIKEY';
+ $config['facebook']['secret'] = 'SECRET';
+
+In Facebook's application editor, specify the following URLs for your app:
+
+- Canvas Callback URL : http://example.net/mublog/facebook/app/
+- Post-Remove Callback URL: http://example.net/mublog/facebook/app/remove
+- Post-Add Redirect URL : http://apps.facebook.com/yourapp/
+- Canvas Page URL : http://apps.facebook.com/yourapp/
+- Connect URL : http://example.net/mublog/
+
+ *** ATTENTION ***
+ These URLs have changed slightly since StatusNet version 0.8.1,
+ so if you have been using the Facebook app previously, you will
+ need to update your configuration!
+
+Replace "example.net" with your host's URL, "mublog" with the path to your
+StatusNet installation, and 'yourapp' with the name of the Facebook
+application you created. (If you don't have "Fancy URLs" on, you'll need to
+change http://example.net/mublog/ to http://example.net/mublog/index.php/).
+
+Additionally, Choose "Web" for Application type in the Advanced tab. In the
+"Canvas setting" section, choose the "FBML" for Render Method, "Smart Size"
+for IFrame size, and "Full width (760px)" for Canvas Width. Everything else
+can be left with default values.
+
+* NOTE: For more under-the-hood detailed instructions about setting up a
+ Facebook application and getting an API key, check out the
+ following pages on the Facebook wiki:
+
+ http://wiki.developers.facebook.com/index.php/Connect/Setting_Up_Your_Site
+ http://wiki.developers.facebook.com/index.php/Creating_your_first_application
+
+Finally you must activate the plugin by adding the following line to your
+config.php:
+
+ addPlugin('Facebook');
+
+Testing It Out
+--------------
+
+If the Facebook plugin is enabled and working, there will be a new Facebook
+Connect Settings tab under each user's Connect menu. Users can connect and
+disconnect* to their Facebook accounts from it.
+
+To try out the plugin, fire up your browser and connect to:
+
+ http://SITE/PATH_TO_STATUSNET/main/facebooklogin
+
+or, if you do not have fancy URLs turned on:
+
+ http://SITE/PATH_TO_STATUSNET/index.php/main/facebooklogin
+
+You should see a page with a blue button that says: "Connect with Facebook"
+and you should be able to login or register.
+
+From within Facebook, you should also be able to get to the Facebook
+application, and run it by hitting the link you specified above when
+configuring it:
+
+ http://apps.facebook.com/yourapp/
+
+That link should be present you with a login screen. After logging in to
+the app, you are given the option to update their Facebook status via
+StatusNet.
+
+* Note: Before a user can disconnect from Facebook, she must set a normal
+ StatusNet password. Otherwise, she might not be able to login in to her
+ account in the future. This is usually only required for users who have
+ used Facebook Connect to register their StatusNet account, and therefore
+ haven't already set a local password.
+
+Offline Queue Handling
+----------------------
+
+For larger sites needing better performance it's possible to enable queuing
+and have users' notices posted to Facebook via a separate "offline"
+FacebookQueueHandler (facebookqueuhandler.php in the Facebook plugin
+directory), which will be started by the plugin along with their other
+daemons when you run scripts/startdaemons.sh. See the StatusNet README for
+more about queuing and daemons.
+
+TODO
+----
+
+- Invite Facebook friends to use your StatusNet installation via Facebook
+ Connect
+- Auto-subscribe Facebook friends already using StatusNet
+- Share StatusNet favorite notices to your Facebook stream
+- Allow users to update their Facebook statuses once they have authenticated
+ with Facebook Connect (no need for them to use the Facebook app if they
+ don't want to).
+- Re-design the whole thing to support multiple instances of StatusNet
diff --git a/extlib/facebook/facebook.php b/plugins/Facebook/facebook/facebook.php
similarity index 100%
rename from extlib/facebook/facebook.php
rename to plugins/Facebook/facebook/facebook.php
diff --git a/extlib/facebook/facebook_desktop.php b/plugins/Facebook/facebook/facebook_desktop.php
similarity index 100%
rename from extlib/facebook/facebook_desktop.php
rename to plugins/Facebook/facebook/facebook_desktop.php
diff --git a/extlib/facebook/facebookapi_php5_restlib.php b/plugins/Facebook/facebook/facebookapi_php5_restlib.php
similarity index 100%
rename from extlib/facebook/facebookapi_php5_restlib.php
rename to plugins/Facebook/facebook/facebookapi_php5_restlib.php
diff --git a/extlib/facebook/jsonwrapper/JSON/JSON.php b/plugins/Facebook/facebook/jsonwrapper/JSON/JSON.php
similarity index 100%
rename from extlib/facebook/jsonwrapper/JSON/JSON.php
rename to plugins/Facebook/facebook/jsonwrapper/JSON/JSON.php
diff --git a/extlib/facebook/jsonwrapper/JSON/LICENSE b/plugins/Facebook/facebook/jsonwrapper/JSON/LICENSE
similarity index 100%
rename from extlib/facebook/jsonwrapper/JSON/LICENSE
rename to plugins/Facebook/facebook/jsonwrapper/JSON/LICENSE
diff --git a/extlib/facebook/jsonwrapper/jsonwrapper.php b/plugins/Facebook/facebook/jsonwrapper/jsonwrapper.php
similarity index 100%
rename from extlib/facebook/jsonwrapper/jsonwrapper.php
rename to plugins/Facebook/facebook/jsonwrapper/jsonwrapper.php
diff --git a/extlib/facebook/jsonwrapper/jsonwrapper_inner.php b/plugins/Facebook/facebook/jsonwrapper/jsonwrapper_inner.php
similarity index 100%
rename from extlib/facebook/jsonwrapper/jsonwrapper_inner.php
rename to plugins/Facebook/facebook/jsonwrapper/jsonwrapper_inner.php
diff --git a/lib/facebookaction.php b/plugins/Facebook/facebookaction.php
similarity index 93%
rename from lib/facebookaction.php
rename to plugins/Facebook/facebookaction.php
index 3f3a8d3b09..a10fdf90d4 100644
--- a/lib/facebookaction.php
+++ b/plugins/Facebook/facebookaction.php
@@ -2,7 +2,7 @@
/**
* StatusNet, the distributed open-source microblogging tool
*
- * Low-level generator for HTML
+ * Base Facebook Action
*
* PHP version 5
*
@@ -22,18 +22,17 @@
* @category Faceboook
* @package StatusNet
* @author Zach Copley
- * @copyright 2008 StatusNet, Inc.
+ * @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'))
-{
+if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
-require_once INSTALLDIR.'/lib/facebookutil.php';
-require_once INSTALLDIR.'/lib/noticeform.php';
+require_once INSTALLDIR . '/plugins/Facebook/facebookutil.php';
+require_once INSTALLDIR . '/lib/noticeform.php';
class FacebookAction extends Action
{
@@ -45,17 +44,6 @@ class FacebookAction extends Action
var $app_uri = null;
var $app_name = null;
- /**
- * Constructor
- *
- * Just wraps the HTMLOutputter constructor.
- *
- * @param string $output URI to output to, default = stdout
- * @param boolean $indent Whether to indent output, default true
- *
- * @see XMLOutputter::__construct
- * @see HTMLOutputter::__construct
- */
function __construct($output='php://output', $indent=true, $facebook=null, $flink=null)
{
parent::__construct($output, $indent);
@@ -95,8 +83,8 @@ class FacebookAction extends Action
function showStylesheets()
{
$this->cssLink('css/display.css', 'base');
- $this->cssLink('css/display.css',null,'screen, projection, tv');
- $this->cssLink('css/facebookapp.css', 'base');
+ $this->cssLink('css/display.css', null, 'screen, projection, tv');
+ $this->cssLink('plugins/Facebook/facebookapp.css');
}
function showScripts()
@@ -107,10 +95,8 @@ class FacebookAction extends Action
/**
* Start an Facebook ready HTML document
*
- * For Facebook we don't want to actually output any headers,
- * DTD info, etc. Just Stylesheet and JavaScript links.
- *
- * If $type isn't specified, will attempt to do content negotiation.
+ * For Facebook we don't want to actually output any headers,
+ * DTD info, etc. Just Stylesheet and JavaScript links.
*
* @param string $type MIME type to use; default is to do negotation.
*
@@ -139,8 +125,6 @@ class FacebookAction extends Action
/**
* Show notice form.
*
- * MAY overload if no notice form needed... or direct message box????
- *
* @return nothing
*/
function showNoticeForm()
@@ -157,10 +141,6 @@ class FacebookAction extends Action
$this->elementEnd('div');
}
- function showAside()
- {
- }
-
function showHead($error, $success)
{
@@ -214,8 +194,6 @@ class FacebookAction extends Action
/**
* Show header of the page.
*
- * Calls template methods
- *
* @return nothing
*/
function showHeader()
@@ -257,7 +235,7 @@ class FacebookAction extends Action
$this->element('a',
array('href' => common_local_url('register')), _('Register'));
$this->text($loginmsg_part2);
- $this->elementEnd('p');
+ $this->elementEnd('p');
$this->elementEnd('dd');
$this->elementEnd('dl');
@@ -295,7 +273,7 @@ class FacebookAction extends Action
$this->elementEnd('ul');
$this->submit('submit', _('Login'));
- $this->elementEnd('fieldset');
+ $this->elementEnd('fieldset');
$this->elementEnd('form');
$this->elementStart('p');
@@ -313,8 +291,8 @@ class FacebookAction extends Action
// Need to include inline CSS for styling the Profile box
- $app_props = $this->facebook->api_client->Admin_getAppProperties(array('icon_url'));
- $icon_url = $app_props['icon_url'];
+ $app_props = $this->facebook->api_client->Admin_getAppProperties(array('icon_url'));
+ $icon_url = $app_props['icon_url'];
$style = '