Merge remote branch 'gitorious/1.0.x' into 1.0.x

This commit is contained in:
Evan Prodromou 2010-08-03 16:04:54 -07:00
commit 004e42e3e0
326 changed files with 31953 additions and 19653 deletions

62
README
View File

@ -2,8 +2,8 @@
README README
------ ------
StatusNet 0.9.1 ("Everybody Hurts") StatusNet 0.9.2 ("King of Birds")
28 Mar 2010 3 May 2010
This is the README file for StatusNet, the Open Source microblogging This is the README file for StatusNet, the Open Source microblogging
platform. It includes installation instructions, descriptions of platform. It includes installation instructions, descriptions of
@ -77,7 +77,7 @@ for additional terms.
New this version New this version
================ ================
This is a minor bug and feature release since version 0.9.0 released 4 This is a minor bug and feature release since version 0.9.1 released 28
March 2010. March 2010.
Because of fixes to OStatus bugs, it is highly recommended that all Because of fixes to OStatus bugs, it is highly recommended that all
@ -85,26 +85,32 @@ public sites upgrade to the new version immediately.
Notable changes this version: Notable changes this version:
- Twitter bridge truncates and links back to original for long - Installer no longer fails with a PHP fatal error when trying to set up the
notices. subscription to update@status.net
- Changed "Home" link in main menu to "Personal". - Fixed email notifications for @-replies that come in via OStatus
- A new memcached plugin (using pecl/memcached versus pecl/memcache) - OStatus related Fixes to the cloudy theme
- Opt-in subscription to update@status.net - Pass geo locations over Twitter bridge (will only be used if enabled on the
- Script to run commands on behalf of a user. Twitter side)
- Better Web UI for long notices. - scripts/showplugins.php - script to dump the list of activated plugins and
- A plugin to open external links in their own window or tab their settings
- Fixes to Salmon protocol for compatibility with other systems. - scripts/fixup_blocks.php - script to finds any stray subscriptions in
- Updates to latest ActivityStreams definition. violation of blocks, and removes them
- Twitpic-compatible API for image upload. - Allow blocking someone who's not currently subscribed to you (prevents
- Background deletion of user accounts. seeing @-replies from them, or them subbing to you in future)
- Better support for HTTP basic authentication with CGI/FastCGI - Default 2-second timeout on Geonames web service lookups
- Better discovery on OStatus - Improved localization for plugins
- Support for PuSH-enabled RSS 2.0 feeds - New anti-spam measures: added nofollow rels to group members list,
- OpenID-only mode subscribers list
- OpenID blacklist/whitelist - Shared cache key option for Geonames plugin (lets multi-instance sites
- OStatus unit tests share their cached geoname lookups)
- Stability fixes to the TwitterStatusFetcher
- If user allows location sharing but turned off browser location use profile
location
- Improved group listing via the API
- Improved FOAF output
- Several other bugfixes
A full changelog is available at http://status.net/wiki/StatusNet_0.9.1. A full changelog is available at http://status.net/wiki/StatusNet_0.9.2.
Prerequisites Prerequisites
============= =============
@ -216,9 +222,9 @@ especially if you've previously installed PHP/MySQL packages.
1. Unpack the tarball you downloaded on your Web server. Usually a 1. Unpack the tarball you downloaded on your Web server. Usually a
command like this will work: command like this will work:
tar zxf statusnet-0.9.1.tar.gz tar zxf statusnet-0.9.2.tar.gz
...which will make a statusnet-0.9.1 subdirectory in your current ...which will make a statusnet-0.9.2 subdirectory in your current
directory. (If you don't have shell access on your Web server, you 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 may have to unpack the tarball on your local computer and FTP the
files to the server.) files to the server.)
@ -226,7 +232,7 @@ especially if you've previously installed PHP/MySQL packages.
2. Move the tarball to a directory of your choosing in your Web root 2. Move the tarball to a directory of your choosing in your Web root
directory. Usually something like this will work: directory. Usually something like this will work:
mv statusnet-0.9.1 /var/www/statusnet mv statusnet-0.9.2 /var/www/statusnet
This will make your StatusNet instance available in the statusnet path of This will make your StatusNet instance available in the statusnet path of
your server, like "http://example.net/statusnet". "microblog" or your server, like "http://example.net/statusnet". "microblog" or
@ -641,7 +647,7 @@ with this situation.
If you've been using StatusNet 0.7, 0.6, 0.5 or lower, or if you've 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 been tracking the "git" version of the software, you will probably
want to upgrade and keep your existing data. There is no automated want to upgrade and keep your existing data. There is no automated
upgrade procedure in StatusNet 0.9.1. Try these step-by-step upgrade procedure in StatusNet 0.9.2. Try these step-by-step
instructions; read to the end first before trying them. instructions; read to the end first before trying them.
0. Download StatusNet and set up all the prerequisites as if you were 0. Download StatusNet and set up all the prerequisites as if you were
@ -662,7 +668,7 @@ instructions; read to the end first before trying them.
5. Once all writing processes to your site are turned off, make a 5. Once all writing processes to your site are turned off, make a
final backup of the Web directory and database. final backup of the Web directory and database.
6. Move your StatusNet directory to a backup spot, like "statusnet.bak". 6. Move your StatusNet directory to a backup spot, like "statusnet.bak".
7. Unpack your StatusNet 0.9.1 tarball and move it to "statusnet" or 7. Unpack your StatusNet 0.9.2 tarball and move it to "statusnet" or
wherever your code used to be. wherever your code used to be.
8. Copy the config.php file and avatar directory from your old 8. Copy the config.php file and avatar directory from your old
directory to your new directory. directory to your new directory.
@ -1539,7 +1545,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 T_STRING") in the browser, check to see that you don't have any
conflicts in your code. conflicts in your code.
If you upgraded to StatusNet 0.9.1 without reading the "Notice If you upgraded to StatusNet 0.9.2 without reading the "Notice
inboxes" section above, and all your users' 'Personal' tabs are empty, inboxes" section above, and all your users' 'Personal' tabs are empty,
read the "Notice inboxes" section above. read the "Notice inboxes" section above.

View File

@ -18,15 +18,19 @@
* *
* @category Actions * @category Actions
* @package Actions * @package Actions
* @author Evan Prodromou <evan@status.net>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <millette@controlyourself.ca>
* @author Adrian Lang <mail@adrianlang.de> * @author Adrian Lang <mail@adrianlang.de>
* @author Meitar Moscovitz <meitarm@gmail.com> * @author Brenda Wallace <shiny@cpan.org>
* @author Sarven Capadisli <csarven@status.net> * @author Brion Vibber <brion@pobox.com>
* @author Craig Andrews <candrews@integralblue.com> * @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@controlyourself.ca> * @author Meitar Moscovitz <meitarm@gmail.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <millette@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
* @author Zach Copley <zach@status.net>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license GNU Affero General Public License http://www.gnu.org/licenses/ * @license GNU Affero General Public License http://www.gnu.org/licenses/
* @link http://status.net * @link http://status.net
*/ */

View File

@ -21,8 +21,10 @@
* *
* @category API * @category API
* @package StatusNet * @package StatusNet
* @author Brion Vibber <brion@pobox.com>
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Robin Millette <robin@millette.info> * @author Robin Millette <robin@millette.info>
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0

View File

@ -21,6 +21,7 @@
* *
* @category API * @category API
* @package StatusNet * @package StatusNet
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0

View File

@ -75,7 +75,7 @@ class ApiAccountVerifyCredentialsAction extends ApiAuthAction
if ($this->format == 'xml') { if ($this->format == 'xml') {
$this->initDocument('xml'); $this->initDocument('xml');
$this->showTwitterXmlUser($twitter_user); $this->showTwitterXmlUser($twitter_user, 'user', true);
$this->endDocument('xml'); $this->endDocument('xml');
} elseif ($this->format == 'json') { } elseif ($this->format == 'json') {
$this->initDocument('json'); $this->initDocument('json');

View File

@ -232,7 +232,8 @@ class ApiDirectMessageAction extends ApiAuthAction
function showXmlDirectMessages() function showXmlDirectMessages()
{ {
$this->initDocument('xml'); $this->initDocument('xml');
$this->elementStart('direct-messages', array('type' => 'array')); $this->elementStart('direct-messages', array('type' => 'array',
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
foreach ($this->messages as $m) { foreach ($this->messages as $m) {
$dm_array = $this->directMessageArray($m); $dm_array = $this->directMessageArray($m);

View File

@ -52,7 +52,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
class ApiDirectMessageNewAction extends ApiAuthAction class ApiDirectMessageNewAction extends ApiAuthAction
{ {
var $source = null;
var $other = null; var $other = null;
var $content = null; var $content = null;
@ -76,13 +75,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction
return; return;
} }
$this->source = $this->trimmed('source'); // Not supported by Twitter.
$reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
if (empty($this->source) || in_array($this->source, $reserved_sources)) {
$source = 'api';
}
$this->content = $this->trimmed('text'); $this->content = $this->trimmed('text');
$this->user = $this->auth_user; $this->user = $this->auth_user;

View File

@ -25,6 +25,7 @@
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -25,6 +25,7 @@
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -0,0 +1,367 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Update a group's profile
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR . '/lib/apiauth.php';
/**
* API analog to the group edit page
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupProfileUpdateAction extends ApiAuthAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
$this->nickname = common_canonical_nickname($this->trimmed('nickname'));
$this->fullname = $this->trimmed('fullname');
$this->homepage = $this->trimmed('homepage');
$this->description = $this->trimmed('description');
$this->location = $this->trimmed('location');
$this->aliasstring = $this->trimmed('aliases');
$this->user = $this->auth_user;
$this->group = $this->getTargetGroup($this->arg('id'));
return true;
}
/**
* Handle the request
*
* See which request params have been set, and update the profile
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
{
parent::handle($args);
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
$this->clientError(
_('This method requires a POST.'),
400, $this->format
);
return;
}
if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(
_('API method not found.'),
404,
$this->format
);
return;
}
if (empty($this->user)) {
$this->clientError(_('No such user.'), 404, $this->format);
return;
}
if (empty($this->group)) {
$this->clientError(_('Group not found.'), 404, $this->format);
return false;
}
if (!$this->user->isAdmin($this->group)) {
$this->clientError(_('You must be an admin to edit the group.'), 403);
return false;
}
$this->group->query('BEGIN');
$orig = clone($this->group);
try {
if (!empty($this->nickname)) {
if ($this->validateNickname()) {
$this->group->nickname = $this->nickname;
$this->group->mainpage = common_local_url(
'showgroup',
array('nickname' => $this->nickname)
);
}
}
if (!empty($this->fullname)) {
$this->validateFullname();
$this->group->fullname = $this->fullname;
}
if (!empty($this->homepage)) {
$this->validateHomepage();
$this->group->homepage = $this->hompage;
}
if (!empty($this->description)) {
$this->validateDescription();
$this->group->description = $this->decription;
}
if (!empty($this->location)) {
$this->validateLocation();
$this->group->location = $this->location;
}
} catch (ApiValidationException $ave) {
$this->clientError(
$ave->getMessage(),
403,
$this->format
);
return;
}
$result = $this->group->update($orig);
if (!$result) {
common_log_db_error($this->group, 'UPDATE', __FILE__);
$this->serverError(_('Could not update group.'));
}
$aliases = array();
try {
if (!empty($this->aliasstring)) {
$aliases = $this->validateAliases();
}
} catch (ApiValidationException $ave) {
$this->clientError(
$ave->getMessage(),
403,
$this->format
);
return;
}
$result = $this->group->setAliases($aliases);
if (!$result) {
$this->serverError(_('Could not create aliases.'));
}
if (!empty($this->nickname) && ($this->nickname != $orig->nickname)) {
common_log(LOG_INFO, "Saving local group info.");
$local = Local_group::staticGet('group_id', $this->group->id);
$local->setNickname($this->nickname);
}
$this->group->query('COMMIT');
switch($this->format) {
case 'xml':
$this->showSingleXmlGroup($this->group);
break;
case 'json':
$this->showSingleJsonGroup($this->group);
break;
default:
$this->clientError(_('API method not found.'), 404, $this->format);
break;
}
}
function nicknameExists($nickname)
{
$group = Local_group::staticGet('nickname', $nickname);
if (!empty($group) &&
$group->group_id != $this->group->id) {
return true;
}
$alias = Group_alias::staticGet('alias', $nickname);
if (!empty($alias) &&
$alias->group_id != $this->group->id) {
return true;
}
return false;
}
function validateNickname()
{
if (!Validate::string(
$this->nickname, array(
'min_length' => 1,
'max_length' => 64,
'format' => NICKNAME_FMT
)
)
) {
throw new ApiValidationException(
_(
'Nickname must have only lowercase letters ' .
'and numbers and no spaces.'
)
);
} else if ($this->nicknameExists($this->nickname)) {
throw new ApiValidationException(
_('Nickname already in use. Try another one.')
);
} else if (!User_group::allowedNickname($this->nickname)) {
throw new ApiValidationException(
_('Not a valid nickname.')
);
}
return true;
}
function validateHomepage()
{
if (!is_null($this->homepage)
&& (strlen($this->homepage) > 0)
&& !Validate::uri(
$this->homepage,
array('allowed_schemes' => array('http', 'https')
)
)
) {
throw new ApiValidationException(
_('Homepage is not a valid URL.')
);
}
}
function validateFullname()
{
if (!is_null($this->fullname) && mb_strlen($this->fullname) > 255) {
throw new ApiValidationException(
_('Full name is too long (max 255 chars).')
);
}
}
function validateDescription()
{
if (User_group::descriptionTooLong($this->description)) {
throw new ApiValidationException(
sprintf(
_('description is too long (max %d chars).'),
User_group::maxDescription()
)
);
}
}
function validateLocation()
{
if (!is_null($this->location) && mb_strlen($this->location) > 255) {
throw new ApiValidationException(
_('Location is too long (max 255 chars).')
);
}
}
function validateAliases()
{
$aliases = array_map(
'common_canonical_nickname',
array_unique(
preg_split('/[\s,]+/',
$this->aliasstring
)
)
);
if (count($aliases) > common_config('group', 'maxaliases')) {
throw new ApiValidationException(
sprintf(
_('Too many aliases! Maximum %d.'),
common_config('group', 'maxaliases')
)
);
}
foreach ($aliases as $alias) {
if (!Validate::string(
$alias, array(
'min_length' => 1,
'max_length' => 64,
'format' => NICKNAME_FMT)
)
) {
throw new ApiValidationException(
sprintf(
_('Invalid alias: "%s"'),
$alias
)
);
}
if ($this->nicknameExists($alias)) {
throw new ApiValidationException(
sprintf(
_('Alias "%s" already in use. Try another one.'),
$alias)
);
}
// XXX assumes alphanum nicknames
if (strcmp($alias, $this->nickname) == 0) {
throw new ApiValidationException(
_('Alias can\'t be the same as nickname.')
);
}
}
return $aliases;
}
}

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -22,7 +22,7 @@
* @category Search * @category Search
* @package StatusNet * @package StatusNet
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2008-2009 StatusNet, Inc. * @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1); exit(1);
} }
require_once INSTALLDIR.'/lib/apiprivateauth.php';
/** /**
* Action for outputting search results in Twitter compatible Atom * Action for outputting search results in Twitter compatible Atom
* format. * format.
@ -44,10 +46,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
* *
* @see ApiAction * @see ApiPrivateAuthAction
*/ */
class TwitapisearchatomAction extends ApiAction class ApiSearchAtomAction extends ApiPrivateAuthAction
{ {
var $cnt; var $cnt;
@ -96,8 +98,11 @@ class TwitapisearchatomAction extends ApiAction
function prepare($args) function prepare($args)
{ {
common_debug("in apisearchatom prepare()");
parent::prepare($args); parent::prepare($args);
$this->query = $this->trimmed('q'); $this->query = $this->trimmed('q');
$this->lang = $this->trimmed('lang'); $this->lang = $this->trimmed('lang');
$this->rpp = $this->trimmed('rpp'); $this->rpp = $this->trimmed('rpp');
@ -138,6 +143,7 @@ class TwitapisearchatomAction extends ApiAction
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
common_debug("In apisearchatom handle()");
$this->showAtom(); $this->showAtom();
} }
@ -342,10 +348,24 @@ class TwitapisearchatomAction extends ApiAction
'rel' => 'related', 'rel' => 'related',
'href' => $profile->avatarUrl())); 'href' => $profile->avatarUrl()));
// TODO: Here is where we'd put in a link to an atom feed for threads // @todo: Here is where we'd put in a link to an atom feed for threads
$this->element("twitter:source", null, $source = null;
htmlentities($this->sourceLink($notice->source)));
$ns = $notice->getSource();
if ($ns) {
if (!empty($ns->name) && !empty($ns->url)) {
$source = '<a href="'
. htmlspecialchars($ns->url)
. '" rel="nofollow">'
. htmlspecialchars($ns->name)
. '</a>';
} else {
$source = $ns->code;
}
}
$this->element("twitter:source", null, $source);
$this->elementStart('author'); $this->elementStart('author');

View File

@ -22,7 +22,7 @@
* @category Search * @category Search
* @package StatusNet * @package StatusNet
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2008-2009 StatusNet, Inc. * @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -31,6 +31,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1); exit(1);
} }
require_once INSTALLDIR.'/lib/apiprivateauth.php';
require_once INSTALLDIR.'/lib/jsonsearchresultslist.php'; require_once INSTALLDIR.'/lib/jsonsearchresultslist.php';
/** /**
@ -44,7 +45,7 @@ require_once INSTALLDIR.'/lib/jsonsearchresultslist.php';
* @see ApiAction * @see ApiAction
*/ */
class TwitapisearchjsonAction extends ApiAction class ApiSearchJSONAction extends ApiPrivateAuthAction
{ {
var $query; var $query;
var $lang; var $lang;
@ -64,6 +65,8 @@ class TwitapisearchjsonAction extends ApiAction
function prepare($args) function prepare($args)
{ {
common_debug("apisearchjson prepare()");
parent::prepare($args); parent::prepare($args);
$this->query = $this->trimmed('q'); $this->query = $this->trimmed('q');

View File

@ -29,6 +29,7 @@
* @author Robin Millette <robin@millette.info> * @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -57,7 +58,7 @@ require_once INSTALLDIR . '/lib/apiauth.php';
class ApiStatusesDestroyAction extends ApiAuthAction class ApiStatusesDestroyAction extends ApiAuthAction
{ {
var $status = null; var $status = null;
/** /**
* Take arguments for running * Take arguments for running
@ -99,39 +100,43 @@ class ApiStatusesDestroyAction extends ApiAuthAction
parent::handle($args); parent::handle($args);
if (!in_array($this->format, array('xml', 'json'))) { if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(_('API method not found.'), $code = 404); $this->clientError(
return; _('API method not found.'),
404
);
return;
} }
if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
$this->clientError(_('This method requires a POST or DELETE.'), $this->clientError(
400, $this->format); _('This method requires a POST or DELETE.'),
return; 400,
} $this->format
);
return;
}
if (empty($this->notice)) { if (empty($this->notice)) {
$this->clientError(_('No status found with that ID.'), $this->clientError(
404, $this->format); _('No status found with that ID.'),
return; 404, $this->format
} );
return;
}
if ($this->user->id == $this->notice->profile_id) { if ($this->user->id == $this->notice->profile_id) {
$replies = new Reply; $replies = new Reply;
$replies->get('notice_id', $this->notice_id); $replies->get('notice_id', $this->notice_id);
$replies->delete(); $replies->delete();
$this->notice->delete(); $this->notice->delete();
$this->showNotice();
if ($this->format == 'xml') { } else {
$this->showSingleXmlStatus($this->notice); $this->clientError(
} elseif ($this->format == 'json') { _('You may not delete another user\'s status.'),
$this->show_single_json_status($this->notice); 403,
} $this->format
} else { );
$this->clientError(_('You may not delete another user\'s status.'), }
403, $this->format);
}
$this->showNotice();
} }
/** /**

View File

@ -79,7 +79,7 @@ class ApiStatusesRetweetAction extends ApiAuthAction
$this->user = $this->auth_user; $this->user = $this->auth_user;
if ($this->user->id == $notice->profile_id) { if ($this->user->id == $this->original->profile_id) {
$this->clientError(_('Cannot repeat your own notice.'), $this->clientError(_('Cannot repeat your own notice.'),
400, $this->format); 400, $this->format);
return false; return false;

View File

@ -29,6 +29,7 @@
* @author Robin Millette <robin@millette.info> * @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -29,10 +29,102 @@
* @author Robin Millette <robin@millette.info> * @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc. * @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
/* External API usage documentation. Please update when you change how this method works. */
/*! @page statusesupdate statuses/update
@section Description
Updates the authenticating user's status. Requires the status parameter specified below.
Request must be a POST.
@par URL pattern
/api/statuses/update.:format
@par Formats (:format)
xml, json
@par HTTP Method(s)
POST
@par Requires Authentication
Yes
@param status (Required) The URL-encoded text of the status update.
@param source (Optional) The source of the status.
@param in_reply_to_status_id (Optional) The ID of an existing status that the update is in reply to.
@param lat (Optional) The latitude the status refers to.
@param long (Optional) The longitude the status refers to.
@param media (Optional) a media upload, such as an image or movie file.
@sa @ref authentication
@sa @ref apiroot
@subsection usagenotes Usage notes
@li The URL pattern is relative to the @ref apiroot.
@li If the @e source parameter is not supplied the source of the status will default to 'api'.
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
to encode the latitude and longitude (see example response below <georss:point>).
@li Data uploaded via the @e media parameter should be multipart/form-data encoded.
@subsection exampleusage Example usage
@verbatim
curl -u username:password http://example.com/api/statuses/update.xml -d status='Howdy!' -d lat='30.468' -d long='-94.743'
@endverbatim
@subsection exampleresponse Example response
@verbatim
<?xml version="1.0" encoding="UTF-8"?>
<status>
<text>Howdy!</text>
<truncated>false</truncated>
<created_at>Tue Mar 30 23:28:05 +0000 2010</created_at>
<in_reply_to_status_id/>
<source>api</source>
<id>26668724</id>
<in_reply_to_user_id/>
<in_reply_to_screen_name/>
<geo xmlns:georss="http://www.georss.org/georss">
<georss:point>30.468 -94.743</georss:point>
</geo>
<favorited>false</favorited>
<user>
<id>25803</id>
<name>Jed Sanders</name>
<screen_name>jedsanders</screen_name>
<location>Hoop and Holler, Texas</location>
<description>I like to think of myself as America's Favorite.</description>
<profile_image_url>http://avatar.example.com/25803-48-20080924200604.png</profile_image_url>
<url>http://jedsanders.net</url>
<protected>false</protected>
<followers_count>5</followers_count>
<profile_background_color/>
<profile_text_color/>
<profile_link_color/>
<profile_sidebar_fill_color/>
<profile_sidebar_border_color/>
<friends_count>2</friends_count>
<created_at>Wed Sep 24 20:04:00 +0000 2008</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url/>
<profile_background_tile>false</profile_background_tile>
<statuses_count>70</statuses_count>
<following>true</following>
<notifications>true</notifications>
</user>
</status>
@endverbatim
*/
if (!defined('STATUSNET')) { if (!defined('STATUSNET')) {
exit(1); exit(1);
} }
@ -64,8 +156,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
var $lat = null; var $lat = null;
var $lon = null; var $lon = null;
static $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
/** /**
* Take arguments for running * Take arguments for running
* *
@ -80,19 +170,9 @@ class ApiStatusesUpdateAction extends ApiAuthAction
parent::prepare($args); parent::prepare($args);
$this->status = $this->trimmed('status'); $this->status = $this->trimmed('status');
$this->source = $this->trimmed('source');
$this->lat = $this->trimmed('lat'); $this->lat = $this->trimmed('lat');
$this->lon = $this->trimmed('long'); $this->lon = $this->trimmed('long');
// try to set the source attr from OAuth app
if (empty($this->source)) {
$this->source = $this->oauth_source;
}
if (empty($this->source) || in_array($this->source, self::$reserved_sources)) {
$this->source = 'api';
}
$this->in_reply_to_status_id $this->in_reply_to_status_id
= intval($this->trimmed('in_reply_to_status_id')); = intval($this->trimmed('in_reply_to_status_id'));

View File

@ -206,7 +206,8 @@ class ApiSubscriptionsAction extends ApiBareAuthAction
{ {
switch ($this->format) { switch ($this->format) {
case 'xml': case 'xml':
$this->elementStart('users', array('type' => 'array')); $this->elementStart('users', array('type' => 'array',
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
foreach ($this->profiles as $profile) { foreach ($this->profiles as $profile) {
$this->showProfile( $this->showProfile(
$profile, $profile,

View File

@ -25,6 +25,7 @@
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc. * @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -150,7 +151,7 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction
header('Content-Type: application/atom+xml; charset=utf-8'); header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed(); $atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id); $atom->setId($id);
$atom->setTitle($title); $atom->setTitle($title);
@ -185,17 +186,23 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction
{ {
$notices = array(); $notices = array();
common_debug("since id = " . $this->since_id . " max id = " . $this->max_id);
if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) { if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) {
$notice = $this->user->favoriteNotices( $notice = $this->user->favoriteNotices(
true,
($this->page-1) * $this->count, ($this->page-1) * $this->count,
$this->count, $this->count,
true $this->since_id,
$this->max_id
); );
} else { } else {
$notice = $this->user->favoriteNotices( $notice = $this->user->favoriteNotices(
false,
($this->page-1) * $this->count, ($this->page-1) * $this->count,
$this->count, $this->count,
false $this->since_id,
$this->max_id
); );
} }

View File

@ -28,11 +28,107 @@
* @author Mike Cochrane <mikec@mikenz.geek.nz> * @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info> * @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
/* External API usage documentation. Please update when you change how this method works. */
/*! @page friendstimeline statuses/friends_timeline
@section Description
Returns the 20 most recent statuses posted by the authenticating
user and that user's friends. This is the equivalent of "You and
friends" page in the web interface.
@par URL patterns
@li /api/statuses/friends_timeline.:format
@li /api/statuses/friends_timeline/:id.:format
@par Formats (:format)
xml, json, rss, atom
@par ID (:id)
username, user id
@par HTTP Method(s)
GET
@par Requires Authentication
Sometimes (see: @ref authentication)
@param user_id (Optional) Specifies a user by ID
@param screen_name (Optional) Specifies a user by screename (nickname)
@param since_id (Optional) Returns only statuses with an ID greater
than (that is, more recent than) the specified ID.
@param max_id (Optional) Returns only statuses with an ID less than
(that is, older than) or equal to the specified ID.
@param count (Optional) Specifies the number of statuses to retrieve.
@param page (Optional) Specifies the page of results to retrieve.
@sa @ref authentication
@sa @ref apiroot
@subsection usagenotes Usage notes
@li The URL pattern is relative to the @ref apiroot.
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
to encode the latitude and longitude (see example response below <georss:point>).
@subsection exampleusage Example usage
@verbatim
curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2
@endverbatim
@subsection exampleresponse Example response
@verbatim
<?xml version="1.0"?>
<statuses type="array">
<status>
<text>back from the !yul !drupal meet with Evolving Web folk, @anarcat, @webchick and others, and an interesting refresher on SQL indexing</text>
<truncated>false</truncated>
<created_at>Wed Mar 31 01:33:02 +0000 2010</created_at>
<in_reply_to_status_id/>
<source>&lt;a href="http://code.google.com/p/microblog-purple/"&gt;mbpidgin&lt;/a&gt;</source>
<id>26674201</id>
<in_reply_to_user_id/>
<in_reply_to_screen_name/>
<geo/>
<favorited>false</favorited>
<user>
<id>246</id>
<name>Mark</name>
<screen_name>lambic</screen_name>
<location>Montreal, Canada</location>
<description>Geek</description>
<profile_image_url>http://avatar.identi.ca/246-48-20080702141545.png</profile_image_url>
<url>http://lambic.co.uk</url>
<protected>false</protected>
<followers_count>73</followers_count>
<profile_background_color>#F0F2F5</profile_background_color>
<profile_text_color/>
<profile_link_color>#002E6E</profile_link_color>
<profile_sidebar_fill_color>#CEE1E9</profile_sidebar_fill_color>
<profile_sidebar_border_color/>
<friends_count>58</friends_count>
<created_at>Wed Jul 02 14:12:15 +0000 2008</created_at>
<favourites_count>2</favourites_count>
<utc_offset>-14400</utc_offset>
<time_zone>US/Eastern</time_zone>
<profile_background_image_url/>
<profile_background_tile>false</profile_background_tile>
<statuses_count>933</statuses_count>
<following>false</following>
<notifications>false</notifications>
</user>
</status>
</statuses>
@endverbatim
*/
if (!defined('STATUSNET')) { if (!defined('STATUSNET')) {
exit(1); exit(1);
} }
@ -153,7 +249,7 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
header('Content-Type: application/atom+xml; charset=utf-8'); header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed(); $atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id); $atom->setId($id);
$atom->setTitle($title); $atom->setTitle($title);

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -105,7 +106,7 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
function showTimeline() function showTimeline()
{ {
// We'll pull common formatting out of this for other formats // We'll pull common formatting out of this for other formats
$atom = new AtomGroupNoticeFeed($this->group); $atom = new AtomGroupNoticeFeed($this->group, $this->auth_user);
$self = $this->getSelfUri(); $self = $this->getSelfUri();

View File

@ -29,6 +29,7 @@
* @author Robin Millette <robin@millette.info> * @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -152,7 +153,7 @@ class ApiTimelineHomeAction extends ApiBareAuthAction
header('Content-Type: application/atom+xml; charset=utf-8'); header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed(); $atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id); $atom->setId($id);
$atom->setTitle($title); $atom->setTitle($title);

View File

@ -29,6 +29,7 @@
* @author Robin Millette <robin@millette.info> * @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -151,7 +152,7 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction
header('Content-Type: application/atom+xml; charset=utf-8'); header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed(); $atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id); $atom->setId($id);
$atom->setTitle($title); $atom->setTitle($title);

View File

@ -29,6 +29,7 @@
* @author Robin Millette <robin@millette.info> * @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -55,6 +56,95 @@ require_once INSTALLDIR . '/lib/apiprivateauth.php';
* @link http://status.net/ * @link http://status.net/
*/ */
/* External API usage documentation. Please update when you change how this method works. */
/*! @page publictimeline statuses/public_timeline
@section Description
Returns the 20 most recent notices from users throughout the system who have
uploaded their own avatars. Depending on configuration, it may or may not
not include notices from automatic posting services.
@par URL patterns
@li /api/statuses/public_timeline.:format
@par Formats (:format)
xml, json, rss, atom
@par HTTP Method(s)
GET
@par Requires Authentication
No
@param since_id (Optional) Returns only statuses with an ID greater
than (that is, more recent than) the specified ID.
@param max_id (Optional) Returns only statuses with an ID less than
(that is, older than) or equal to the specified ID.
@param count (Optional) Specifies the number of statuses to retrieve.
@param page (Optional) Specifies the page of results to retrieve.
@sa @ref apiroot
@subsection usagenotes Usage notes
@li The URL pattern is relative to the @ref apiroot.
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
to encode the latitude and longitude (see example response below <georss:point>).
@subsection exampleusage Example usage
@verbatim
curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2
@endverbatim
@subsection exampleresponse Example response
@verbatim
<?xml version="1.0" encoding="UTF-8"?>
<statuses type="array">
<status>
<text>@skwashd oh, commbank reenabled me super quick both times. but disconcerting when you don't expect it though</text>
<truncated>false</truncated>
<created_at>Sat Apr 17 00:49:12 +0000 2010</created_at>
<in_reply_to_status_id>28838393</in_reply_to_status_id>
<source>xmpp</source>
<id>28838456</id>
<in_reply_to_user_id>39303</in_reply_to_user_id>
<in_reply_to_screen_name>skwashd</in_reply_to_screen_name>
<geo></geo>
<favorited>false</favorited>
<user>
<id>44517</id>
<name>joshua may</name>
<screen_name>notjosh</screen_name>
<location></location>
<description></description>
<profile_image_url>http://avatar.identi.ca/44517-48-20090321004106.jpeg</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>17</followers_count>
<profile_background_color></profile_background_color>
<profile_text_color></profile_text_color>
<profile_link_color></profile_link_color>
<profile_sidebar_fill_color></profile_sidebar_fill_color>
<profile_sidebar_border_color></profile_sidebar_border_color>
<friends_count>20</friends_count>
<created_at>Sat Mar 21 00:40:25 +0000 2009</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url></profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>100</statuses_count>
<following>false</following>
<notifications>false</notifications>
</user>
</status>
[....]
</statuses>
@endverbatim
*/
class ApiTimelinePublicAction extends ApiPrivateAuthAction class ApiTimelinePublicAction extends ApiPrivateAuthAction
{ {
@ -130,7 +220,7 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction
header('Content-Type: application/atom+xml; charset=utf-8'); header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed(); $atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id); $atom->setId($id);
$atom->setTitle($title); $atom->setTitle($title);

View File

@ -117,7 +117,7 @@ class ApiTimelineRetweetsOfMeAction extends ApiAuthAction
header('Content-Type: application/atom+xml; charset=utf-8'); header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed(); $atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id); $atom->setId($id);
$atom->setTitle($title); $atom->setTitle($title);

View File

@ -26,6 +26,7 @@
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc. * @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -138,7 +139,7 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
header('Content-Type: application/atom+xml; charset=utf-8'); header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed(); $atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id); $atom->setId($id);
$atom->setTitle($title); $atom->setTitle($title);

View File

@ -29,6 +29,7 @@
* @author Robin Millette <robin@millette.info> * @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -115,7 +116,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
// We'll use the shared params from the Atom stub // We'll use the shared params from the Atom stub
// for other feed types. // for other feed types.
$atom = new AtomUserNoticeFeed($this->user); $atom = new AtomUserNoticeFeed($this->user, $this->auth_user);
$link = common_local_url( $link = common_local_url(
'showstream', 'showstream',

View File

@ -22,7 +22,7 @@
* @category Search * @category Search
* @package StatusNet * @package StatusNet
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2008-2009 StatusNet, Inc. * @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1); exit(1);
} }
require_once INSTALLDIR.'/lib/apiprivateauth.php';
/** /**
* Returns the top ten queries that are currently trending * Returns the top ten queries that are currently trending
* *
@ -43,7 +45,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @see ApiAction * @see ApiAction
*/ */
class TwitapitrendsAction extends ApiAction class ApiTrendsAction extends ApiPrivateAuthAction
{ {
var $callback; var $callback;
@ -82,7 +84,7 @@ class TwitapitrendsAction extends ApiAction
*/ */
function showTrends() function showTrends()
{ {
$this->serverError(_('API method under construction.'), $code = 501); $this->serverError(_('API method under construction.'), 501);
} }
} }

View File

@ -113,7 +113,7 @@ class ApiUserShowAction extends ApiPrivateAuthAction
if ($this->format == 'xml') { if ($this->format == 'xml') {
$this->initDocument('xml'); $this->initDocument('xml');
$this->showTwitterXmlUser($twitter_user); $this->showTwitterXmlUser($twitter_user, 'user', true);
$this->endDocument('xml'); $this->endDocument('xml');
} elseif ($this->format == 'json') { } elseif ($this->format == 'json') {
$this->initDocument('json'); $this->initDocument('json');

View File

@ -87,13 +87,15 @@ class BlockAction extends ProfileFormAction
{ {
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) { if ($this->arg('no')) {
$this->returnToArgs(); $this->returnToPrevious();
} elseif ($this->arg('yes')) { } elseif ($this->arg('yes')) {
$this->handlePost(); $this->handlePost();
$this->returnToArgs(); $this->returnToPrevious();
} else { } else {
$this->showPage(); $this->showPage();
} }
} else {
$this->showPage();
} }
} }
@ -118,6 +120,12 @@ class BlockAction extends ProfileFormAction
*/ */
function areYouSureForm() function areYouSureForm()
{ {
// @fixme if we ajaxify the confirmation form, skip the preview on ajax hits
$profile = new ArrayWrapper(array($this->profile));
$preview = new ProfileList($profile, $this);
$preview->show();
$id = $this->profile->id; $id = $this->profile->id;
$this->elementStart('form', array('id' => 'block-' . $id, $this->elementStart('form', array('id' => 'block-' . $id,
'method' => 'post', 'method' => 'post',
@ -187,4 +195,38 @@ class BlockAction extends ProfileFormAction
$this->autofocus('form_action-yes'); $this->autofocus('form_action-yes');
} }
/**
* Override for form session token checks; on our first hit we're just
* requesting confirmation, which doesn't need a token. We need to be
* able to take regular GET requests from email!
*
* @throws ClientException if token is bad on POST request or if we have
* confirmation parameters which could trigger something.
*/
function checkSessionToken()
{
if ($_SERVER['REQUEST_METHOD'] == 'POST' ||
$this->arg('yes') ||
$this->arg('no')) {
return parent::checkSessionToken();
}
}
/**
* If we reached this form without returnto arguments, return to the
* current user's subscription list.
*
* @return string URL
*/
function defaultReturnTo()
{
$user = common_current_user();
if ($user) {
return common_local_url('subscribers',
array('nickname' => $user->nickname));
} else {
return common_local_url('public');
}
}
} }

View File

@ -92,10 +92,10 @@ class DeleteuserAction extends ProfileFormAction
{ {
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) { if ($this->arg('no')) {
$this->returnToArgs(); $this->returnToPrevious();
} elseif ($this->arg('yes')) { } elseif ($this->arg('yes')) {
$this->handlePost(); $this->handlePost();
$this->returnToArgs(); $this->returnToPrevious();
} else { } else {
$this->showPage(); $this->showPage();
} }

View File

@ -126,9 +126,19 @@ class DesignadminpanelAction extends AdminPanelAction
return; return;
} }
// check for an image upload // check for file uploads
$bgimage = $this->saveBackgroundImage(); $bgimage = $this->saveBackgroundImage();
$customTheme = $this->saveCustomTheme();
$oldtheme = common_config('site', 'theme');
if ($customTheme) {
// This feels pretty hacky :D
$this->args['theme'] = $customTheme;
$themeChanged = true;
} else {
$themeChanged = ($this->trimmed('theme') != $oldtheme);
}
static $settings = array('theme', 'logo'); static $settings = array('theme', 'logo');
@ -140,15 +150,13 @@ class DesignadminpanelAction extends AdminPanelAction
$this->validate($values); $this->validate($values);
$oldtheme = common_config('site', 'theme');
$config = new Config(); $config = new Config();
$config->query('BEGIN'); $config->query('BEGIN');
// Only update colors if the theme has not changed. // Only update colors if the theme has not changed.
if ($oldtheme == $values['theme']) { if (!$themeChanged) {
$bgcolor = new WebColor($this->trimmed('design_background')); $bgcolor = new WebColor($this->trimmed('design_background'));
$ccolor = new WebColor($this->trimmed('design_content')); $ccolor = new WebColor($this->trimmed('design_content'));
@ -190,6 +198,13 @@ class DesignadminpanelAction extends AdminPanelAction
Config::save('design', 'backgroundimage', $bgimage); Config::save('design', 'backgroundimage', $bgimage);
} }
if (common_config('custom_css', 'enabled')) {
$css = $this->arg('css');
if ($css != common_config('custom_css', 'css')) {
Config::save('custom_css', 'css', $css);
}
}
$config->query('COMMIT'); $config->query('COMMIT');
} }
@ -263,6 +278,33 @@ class DesignadminpanelAction extends AdminPanelAction
} }
} }
/**
* Save the custom theme if the user uploaded one.
*
* @return mixed custom theme name, if succesful, or null if no theme upload.
* @throws ClientException for invalid theme archives
* @throws ServerException if trouble saving the theme files
*/
function saveCustomTheme()
{
if (common_config('theme_upload', 'enabled') &&
$_FILES['design_upload_theme']['error'] == UPLOAD_ERR_OK) {
$upload = ThemeUploader::fromUpload('design_upload_theme');
$basedir = common_config('local', 'dir');
if (empty($basedir)) {
$basedir = INSTALLDIR . '/local';
}
$name = 'custom'; // @todo allow multiples, custom naming?
$outdir = $basedir . '/theme/' . $name;
$upload->extract($outdir);
return $name;
} else {
return null;
}
}
/** /**
* Attempt to validate setting values * Attempt to validate setting values
* *
@ -371,7 +413,15 @@ class DesignAdminPanelForm extends AdminForm
function formData() function formData()
{ {
$this->showLogo();
$this->showTheme();
$this->showBackground();
$this->showColors();
$this->showAdvanced();
}
function showLogo()
{
$this->out->elementStart('fieldset', array('id' => 'settings_design_logo')); $this->out->elementStart('fieldset', array('id' => 'settings_design_logo'));
$this->out->element('legend', null, _('Change logo')); $this->out->element('legend', null, _('Change logo'));
@ -384,6 +434,11 @@ class DesignAdminPanelForm extends AdminForm
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
$this->out->elementEnd('fieldset'); $this->out->elementEnd('fieldset');
}
function showTheme()
{
$this->out->elementStart('fieldset', array('id' => 'settings_design_theme')); $this->out->elementStart('fieldset', array('id' => 'settings_design_theme'));
$this->out->element('legend', null, _('Change theme')); $this->out->element('legend', null, _('Change theme'));
@ -407,10 +462,23 @@ class DesignAdminPanelForm extends AdminForm
false, $this->value('theme')); false, $this->value('theme'));
$this->unli(); $this->unli();
if (common_config('theme_upload', 'enabled')) {
$this->li();
$this->out->element('label', array('for' => 'design_upload_theme'), _('Custom theme'));
$this->out->element('input', array('id' => 'design_upload_theme',
'name' => 'design_upload_theme',
'type' => 'file'));
$this->out->element('p', 'form_guide', _('You can upload a custom StatusNet theme as a .ZIP archive.'));
$this->unli();
}
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
$this->out->elementEnd('fieldset'); $this->out->elementEnd('fieldset');
}
function showBackground()
{
$design = $this->out->design; $design = $this->out->design;
$this->out->elementStart('fieldset', array('id' => $this->out->elementStart('fieldset', array('id' =>
@ -486,6 +554,11 @@ class DesignAdminPanelForm extends AdminForm
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
$this->out->elementEnd('fieldset'); $this->out->elementEnd('fieldset');
}
function showColors()
{
$design = $this->out->design;
$this->out->elementStart('fieldset', array('id' => 'settings_design_color')); $this->out->elementStart('fieldset', array('id' => 'settings_design_color'));
$this->out->element('legend', null, _('Change colours')); $this->out->element('legend', null, _('Change colours'));
@ -493,6 +566,7 @@ class DesignAdminPanelForm extends AdminForm
$this->out->elementStart('ul', 'form_data'); $this->out->elementStart('ul', 'form_data');
try { try {
// @fixme avoid loop unrolling in non-performance-critical contexts like this
$bgcolor = new WebColor($design->backgroundcolor); $bgcolor = new WebColor($design->backgroundcolor);
@ -560,6 +634,7 @@ class DesignAdminPanelForm extends AdminForm
$this->unli(); $this->unli();
} catch (WebColorException $e) { } catch (WebColorException $e) {
// @fixme normalize them individually!
common_log(LOG_ERR, 'Bad color values in site design: ' . common_log(LOG_ERR, 'Bad color values in site design: ' .
$e->getMessage()); $e->getMessage());
} }
@ -569,6 +644,27 @@ class DesignAdminPanelForm extends AdminForm
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
} }
function showAdvanced()
{
if (common_config('custom_css', 'enabled')) {
$this->out->elementStart('fieldset', array('id' => 'settings_design_advanced'));
$this->out->element('legend', null, _('Advanced'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$this->out->element('label', array('for' => 'css'), _('Custom CSS'));
$this->out->element('textarea', array('name' => 'css',
'id' => 'css',
'cols' => '50',
'rows' => '10'),
strval(common_config('custom_css', 'css')));
$this->unli();
$this->out->elementEnd('fieldset');
$this->out->elementEnd('ul');
}
}
/** /**
* Action elements * Action elements
* *

View File

@ -89,7 +89,7 @@ class FavoritesrssAction extends Rss10Action
function getNotices($limit=0) function getNotices($limit=0)
{ {
$user = $this->user; $user = $this->user;
$notice = $user->favoriteNotices(0, $limit); $notice = $user->favoriteNotices(false, 0, $limit);
$notices = array(); $notices = array();
while ($notice->fetch()) { while ($notice->fetch()) {
$notices[] = clone($notice); $notices[] = clone($notice);

View File

@ -95,7 +95,9 @@ class FoafAction extends Action
// Would be nice to tell if they were a Person or not (e.g. a #person usertag?) // Would be nice to tell if they were a Person or not (e.g. a #person usertag?)
$this->elementStart('Agent', array('rdf:about' => $this->elementStart('Agent', array('rdf:about' =>
$this->user->uri)); $this->user->uri));
$this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email)); if ($this->user->email) {
$this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email));
}
if ($this->profile->fullname) { if ($this->profile->fullname) {
$this->element('name', null, $this->profile->fullname); $this->element('name', null, $this->profile->fullname);
} }
@ -152,7 +154,9 @@ class FoafAction extends Action
} }
$person = $this->showMicrobloggingAccount($this->profile, $person = $this->showMicrobloggingAccount($this->profile,
common_root_url(), $this->user->uri, false); common_root_url(), $this->user->uri,
/*$fetchSubscriptions*/true,
/*$isSubscriber*/false);
// Get people who subscribe to user // Get people who subscribe to user
@ -207,7 +211,8 @@ class FoafAction extends Action
$this->showMicrobloggingAccount($profile, $this->showMicrobloggingAccount($profile,
($local == 'local') ? common_root_url() : null, ($local == 'local') ? common_root_url() : null,
$uri, $uri,
true); /*$fetchSubscriptions*/false,
/*$isSubscriber*/($type == LISTENER || $type == BOTH));
if ($foaf_url) { if ($foaf_url) {
$this->element('rdfs:seeAlso', array('rdf:resource' => $foaf_url)); $this->element('rdfs:seeAlso', array('rdf:resource' => $foaf_url));
} }
@ -232,7 +237,21 @@ class FoafAction extends Action
$this->elementEnd('PersonalProfileDocument'); $this->elementEnd('PersonalProfileDocument');
} }
function showMicrobloggingAccount($profile, $service=null, $useruri=null, $isSubscriber=false) /**
* Output FOAF <account> bit for the given profile.
*
* @param Profile $profile
* @param mixed $service Root URL of this StatusNet instance for a local
* user, otherwise null.
* @param mixed $useruri URI string for the referenced profile..
* @param boolean $fetchSubscriptions Should we load and list all their subscriptions?
* @param boolean $isSubscriber if not fetching subs, we can still mark the user as following the current page.
*
* @return array if $fetchSubscribers is set, return a list of info on those
* subscriptions.
*/
function showMicrobloggingAccount($profile, $service=null, $useruri=null, $fetchSubscriptions=false, $isSubscriber=false)
{ {
$attr = array(); $attr = array();
if ($useruri) { if ($useruri) {
@ -254,9 +273,7 @@ class FoafAction extends Action
$person = array(); $person = array();
if ($isSubscriber) { if ($fetchSubscriptions) {
$this->element('sioc:follows', array('rdf:resource'=>$this->user->uri . '#acct'));
} else {
// Get people user is subscribed to // Get people user is subscribed to
$sub = new Subscription(); $sub = new Subscription();
$sub->subscriber = $profile->id; $sub->subscriber = $profile->id;
@ -281,6 +298,9 @@ class FoafAction extends Action
} }
unset($sub); unset($sub);
} else if ($isSubscriber) {
// Just declare that they follow the user whose FOAF we're showing.
$this->element('sioc:follows', array('rdf:resource' => $this->user->uri . '#acct'));
} }
$this->elementEnd('OnlineAccount'); $this->elementEnd('OnlineAccount');

View File

@ -37,6 +37,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @category Action * @category Action
* @package StatusNet * @package StatusNet
* @author Craig Andrews <candrews@integralblue.com> * @author Craig Andrews <candrews@integralblue.com>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -117,7 +117,7 @@ class GroupblockAction extends RedirectingAction
parent::handle($args); parent::handle($args);
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) { if ($this->arg('no')) {
$this->returnToArgs(); $this->returnToPrevious();
} elseif ($this->arg('yes')) { } elseif ($this->arg('yes')) {
$this->blockProfile(); $this->blockProfile();
} elseif ($this->arg('blockto')) { } elseif ($this->arg('blockto')) {
@ -207,7 +207,7 @@ class GroupblockAction extends RedirectingAction
return false; return false;
} }
$this->returnToArgs(); $this->returnToPrevious();
} }
/** /**

View File

@ -133,8 +133,7 @@ class ImsettingsAction extends ConnectSettingsAction
'message with further instructions. '. 'message with further instructions. '.
'(Did you add %s to your buddy list?)'), '(Did you add %s to your buddy list?)'),
$transport_info['display'], $transport_info['display'],
$transport_info['daemon_screenname'], $transport_info['daemon_screenname']));
jabber_daemon_address()));
$this->hidden('screenname', $confirm->address); $this->hidden('screenname', $confirm->address);
// TRANS: Button label to cancel an IM address confirmation procedure. // TRANS: Button label to cancel an IM address confirmation procedure.
$this->submit('cancel', _m('BUTTON','Cancel')); $this->submit('cancel', _m('BUTTON','Cancel'));
@ -163,12 +162,11 @@ class ImsettingsAction extends ConnectSettingsAction
'action' => 'action' =>
common_local_url('imsettings'))); common_local_url('imsettings')));
$this->elementStart('fieldset', array('id' => 'settings_im_preferences')); $this->elementStart('fieldset', array('id' => 'settings_im_preferences'));
$this->element('legend', null, _('Preferences')); // TRANS: Header for IM preferences form.
$this->element('legend', null, _('IM Preferences'));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
$this->elementStart('table'); $this->elementStart('table');
$this->elementStart('tr'); $this->elementStart('tr');
// TRANS: Header for IM preferences form.
$this->element('th', null, _('IM Preferences'));
foreach($user_im_prefs_by_transport as $transport=>$user_im_prefs) foreach($user_im_prefs_by_transport as $transport=>$user_im_prefs)
{ {
$this->element('th', null, $transports[$transport]['display']); $this->element('th', null, $transports[$transport]['display']);
@ -278,19 +276,20 @@ class ImsettingsAction extends ConnectSettingsAction
$user = common_current_user(); $user = common_current_user();
$user_im_prefs = new User_im_prefs(); $user_im_prefs = new User_im_prefs();
$user_im_prefs->query('BEGIN');
$user_im_prefs->user_id = $user->id; $user_im_prefs->user_id = $user->id;
if($user_im_prefs->find() && $user_im_prefs->fetch()) if($user_im_prefs->find() && $user_im_prefs->fetch())
{ {
$preferences = array('notify', 'updatefrompresence', 'replies', 'microid'); $preferences = array('notify', 'updatefrompresence', 'replies', 'microid');
$user_im_prefs->query('BEGIN');
do do
{ {
$original = clone($user_im_prefs); $original = clone($user_im_prefs);
$new = clone($user_im_prefs);
foreach($preferences as $preference) foreach($preferences as $preference)
{ {
$user_im_prefs->$preference = $this->boolean($user_im_prefs->transport . '_' . $preference); $new->$preference = $this->boolean($new->transport . '_' . $preference);
} }
$result = $user_im_prefs->update($original); $result = $new->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
@ -299,8 +298,8 @@ class ImsettingsAction extends ConnectSettingsAction
return; return;
} }
}while($user_im_prefs->fetch()); }while($user_im_prefs->fetch());
$user_im_prefs->query('COMMIT');
} }
$user_im_prefs->query('COMMIT');
// TRANS: Confirmation message for successful IM preferences save. // TRANS: Confirmation message for successful IM preferences save.
$this->showForm(_('Preferences saved.'), true); $this->showForm(_('Preferences saved.'), true);
} }

View File

@ -62,6 +62,28 @@ class LoginAction extends Action
return false; return false;
} }
/**
* Prepare page to run
*
*
* @param $args
* @return string title
*/
function prepare($args)
{
parent::prepare($args);
// @todo this check should really be in index.php for all sensitive actions
$ssl = common_config('site', 'ssl');
if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) {
common_redirect(common_local_url('login'));
// exit
}
return true;
}
/** /**
* Handle input, produce output * Handle input, produce output
* *
@ -267,9 +289,13 @@ class LoginAction extends Action
'user name and password ' . 'user name and password ' .
'before changing your settings.'); 'before changing your settings.');
} else { } else {
return _('Login with your username and password. ' . $prompt = _('Login with your username and password.');
'Don\'t have a username yet? ' . if (!common_config('site', 'closed') && !common_config('site', 'inviteonly')) {
'[Register](%%action.register%%) a new account.'); $prompt .= ' ';
$prompt .= _('Don\'t have a username yet? ' .
'[Register](%%action.register%%) a new account.');
}
return $prompt;
} }
} }

View File

@ -23,6 +23,7 @@
* @package StatusNet * @package StatusNet
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @copyright 2008 StatusNet, Inc. * @copyright 2008 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -8,7 +8,9 @@
* @category Action * @category Action
* @package StatusNet * @package StatusNet
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Craig Andrews <candrews@integralblue.com>
* @author Robin Millette <millette@status.net> * @author Robin Millette <millette@status.net>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
* *
@ -44,6 +46,7 @@ require_once INSTALLDIR.'/lib/xrdsoutputter.php';
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Robin Millette <millette@status.net> * @author Robin Millette <millette@status.net>
* @author Craig Andrews <candrews@integralblue.com> * @author Craig Andrews <candrews@integralblue.com>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
* *

View File

@ -74,6 +74,13 @@ class RegisterAction extends Action
parent::prepare($args); parent::prepare($args);
$this->code = $this->trimmed('code'); $this->code = $this->trimmed('code');
// @todo this check should really be in index.php for all sensitive actions
$ssl = common_config('site', 'ssl');
if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) {
common_redirect(common_local_url('register'));
// exit
}
if (empty($this->code)) { if (empty($this->code)) {
common_ensure_session(); common_ensure_session();
if (array_key_exists('invitecode', $_SESSION)) { if (array_key_exists('invitecode', $_SESSION)) {
@ -491,6 +498,45 @@ class RegisterAction extends Action
$this->elementStart('li'); $this->elementStart('li');
$this->element('input', $attrs); $this->element('input', $attrs);
$this->elementStart('label', array('class' => 'checkbox', 'for' => 'license')); $this->elementStart('label', array('class' => 'checkbox', 'for' => 'license'));
$this->raw($this->licenseCheckbox());
$this->elementEnd('label');
$this->elementEnd('li');
}
$this->elementEnd('ul');
$this->submit('submit', _('Register'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
function licenseCheckbox()
{
$out = '';
switch (common_config('license', 'type')) {
case 'private':
// TRANS: Copyright checkbox label in registration dialog, for private sites.
$out .= htmlspecialchars(sprintf(
_('I understand that content and data of %1$s are private and confidential.'),
common_config('site', 'name')));
// fall through
case 'allrightsreserved':
if ($out != '') {
$out .= ' ';
}
if (common_config('license', 'owner')) {
// TRANS: Copyright checkbox label in registration dialog, for all rights reserved with a specified copyright owner.
$out .= htmlspecialchars(sprintf(
_('My text and files are copyright by %1$s.'),
common_config('license', 'owner')));
} else {
// TRANS: Copyright checkbox label in registration dialog, for all rights reserved with ownership left to contributors.
$out .= htmlspecialchars(_('My text and files remain under my own copyright.'));
}
// TRANS: Copyright checkbox label in registration dialog, for all rights reserved.
$out .= ' ' . _('All rights reserved.');
break;
case 'cc': // fall through
default:
// TRANS: Copyright checkbox label in registration dialog, for Creative Commons-style licenses.
$message = _('My text and files are available under %s ' . $message = _('My text and files are available under %s ' .
'except this private data: password, ' . 'except this private data: password, ' .
'email address, IM address, and phone number.'); 'email address, IM address, and phone number.');
@ -499,14 +545,9 @@ class RegisterAction extends Action
'">' . '">' .
htmlspecialchars(common_config('license', 'title')) . htmlspecialchars(common_config('license', 'title')) .
'</a>'; '</a>';
$this->raw(sprintf(htmlspecialchars($message), $link)); $out .= sprintf(htmlspecialchars($message), $link);
$this->elementEnd('label');
$this->elementEnd('li');
} }
$this->elementEnd('ul'); return $out;
$this->submit('submit', _('Register'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
} }
/** /**

View File

@ -121,11 +121,11 @@ class ShowfavoritesAction extends OwnerDesignAction
// Show imported/gateway notices as well as local if // Show imported/gateway notices as well as local if
// the user is looking at his own favorites // the user is looking at his own favorites
$this->notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE, $this->notice = $this->user->favoriteNotices(true, ($this->page-1)*NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1, true); NOTICES_PER_PAGE + 1);
} else { } else {
$this->notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE, $this->notice = $this->user->favoriteNotices(false, ($this->page-1)*NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1, false); NOTICES_PER_PAGE + 1);
} }
if (empty($this->notice)) { if (empty($this->notice)) {

View File

@ -430,14 +430,6 @@ class ShowgroupAction extends GroupDesignAction
function showStatistics() function showStatistics()
{ {
// XXX: WORM cache this
$members = $this->group->getMembers();
$members_count = 0;
/** $member->count() doesn't work. */
while ($members->fetch()) {
$members_count++;
}
$this->elementStart('div', array('id' => 'entity_statistics', $this->elementStart('div', array('id' => 'entity_statistics',
'class' => 'section')); 'class' => 'section'));
@ -451,7 +443,7 @@ class ShowgroupAction extends GroupDesignAction
$this->elementStart('dl', 'entity_members'); $this->elementStart('dl', 'entity_members');
$this->element('dt', null, _('Members')); $this->element('dt', null, _('Members'));
$this->element('dd', null, (is_int($members_count)) ? $members_count : '0'); $this->element('dd', null, $this->group->getMemberCount());
$this->elementEnd('dl'); $this->elementEnd('dl');
$this->elementEnd('div'); $this->elementEnd('div');

View File

@ -185,7 +185,9 @@ class SubscriptionsListItem extends SubscriptionListItem
return; return;
} }
if (!common_config('xmpp', 'enabled') && !common_config('sms', 'enabled')) { $transports = array();
Event::handle('GetImTransports', array(&$transports));
if (!$transports && !common_config('sms', 'enabled')) {
return; return;
} }
@ -195,7 +197,7 @@ class SubscriptionsListItem extends SubscriptionListItem
'action' => common_local_url('subedit'))); 'action' => common_local_url('subedit')));
$this->out->hidden('token', common_session_token()); $this->out->hidden('token', common_session_token());
$this->out->hidden('profile', $this->profile->id); $this->out->hidden('profile', $this->profile->id);
if (common_config('xmpp', 'enabled')) { if ($transports) {
$attrs = array('name' => 'jabber', $attrs = array('name' => 'jabber',
'type' => 'checkbox', 'type' => 'checkbox',
'class' => 'checkbox', 'class' => 'checkbox',
@ -205,7 +207,7 @@ class SubscriptionsListItem extends SubscriptionListItem
} }
$this->out->element('input', $attrs); $this->out->element('input', $attrs);
$this->out->element('label', array('for' => 'jabber-'.$this->profile->id), _('Jabber')); $this->out->element('label', array('for' => 'jabber-'.$this->profile->id), _('IM'));
} else { } else {
$this->out->hidden('jabber', $sub->jabber); $this->out->hidden('jabber', $sub->jabber);
} }

View File

@ -41,6 +41,8 @@ if (!defined('STATUSNET')) {
* @category Info * @category Info
* @package StatusNet * @package StatusNet
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Craig Andrews <candrews@integralblue.com>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */

0
avatar/.gitignore vendored Normal file → Executable file
View File

View File

@ -75,13 +75,13 @@ class Fave extends Memcached_DataObject
return Memcached_DataObject::pkeyGet('Fave', $kv); return Memcached_DataObject::pkeyGet('Fave', $kv);
} }
function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false) function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false, $since_id=0, $max_id=0)
{ {
$ids = Notice::stream(array('Fave', '_streamDirect'), $ids = Notice::stream(array('Fave', '_streamDirect'),
array($user_id, $own), array($user_id, $own),
($own) ? 'fave:ids_by_user_own:'.$user_id : ($own) ? 'fave:ids_by_user_own:'.$user_id :
'fave:ids_by_user:'.$user_id, 'fave:ids_by_user:'.$user_id,
$offset, $limit); $offset, $limit, $since_id, $max_id);
return $ids; return $ids;
} }

View File

@ -116,7 +116,11 @@ class File extends Memcached_DataObject
return false; return false;
} }
function processNew($given_url, $notice_id=null) { /**
* @fixme refactor this mess, it's gotten pretty scary.
* @param bool $followRedirects
*/
function processNew($given_url, $notice_id=null, $followRedirects=true) {
if (empty($given_url)) return -1; // error, no url to process if (empty($given_url)) return -1; // error, no url to process
$given_url = File_redirection::_canonUrl($given_url); $given_url = File_redirection::_canonUrl($given_url);
if (empty($given_url)) return -1; // error, no url to process if (empty($given_url)) return -1; // error, no url to process
@ -124,6 +128,10 @@ class File extends Memcached_DataObject
if (empty($file)) { if (empty($file)) {
$file_redir = File_redirection::staticGet('url', $given_url); $file_redir = File_redirection::staticGet('url', $given_url);
if (empty($file_redir)) { if (empty($file_redir)) {
// @fixme for new URLs this also looks up non-redirect data
// such as target content type, size, etc, which we need
// for File::saveNew(); so we call it even if not following
// new redirects.
$redir_data = File_redirection::where($given_url); $redir_data = File_redirection::where($given_url);
if (is_array($redir_data)) { if (is_array($redir_data)) {
$redir_url = $redir_data['url']; $redir_url = $redir_data['url'];
@ -134,11 +142,19 @@ class File extends Memcached_DataObject
throw new ServerException("Can't process url '$given_url'"); throw new ServerException("Can't process url '$given_url'");
} }
// TODO: max field length // TODO: max field length
if ($redir_url === $given_url || strlen($redir_url) > 255) { if ($redir_url === $given_url || strlen($redir_url) > 255 || !$followRedirects) {
$x = File::saveNew($redir_data, $given_url); $x = File::saveNew($redir_data, $given_url);
$file_id = $x->id; $file_id = $x->id;
} else { } else {
$x = File::processNew($redir_url, $notice_id); // This seems kind of messed up... for now skipping this part
// if we're already under a redirect, so we don't go into
// horrible infinite loops if we've been given an unstable
// redirect (where the final destination of the first request
// doesn't match what we get when we ask for it again).
//
// Seen in the wild with clojure.org, which redirects through
// wikispaces for auth and appends session data in the URL params.
$x = File::processNew($redir_url, $notice_id, /*followRedirects*/false);
$file_id = $x->id; $file_id = $x->id;
File_redirection::saveNew($redir_data, $file_id, $given_url); File_redirection::saveNew($redir_data, $file_id, $given_url);
} }

View File

@ -115,9 +115,12 @@ class Inbox extends Memcached_DataObject
*/ */
static function insertNotice($user_id, $notice_id) static function insertNotice($user_id, $notice_id)
{ {
$inbox = DB_DataObject::staticGet('inbox', 'user_id', $user_id); // Going straight to the DB rather than trusting our caching
// during an update. Note: not using DB_DataObject::staticGet,
if (empty($inbox)) { // which is unsafe to use directly (in-process caching causes
// memory leaks, which accumulate in queue processes).
$inbox = new Inbox();
if (!$inbox->get('user_id', $user_id)) {
$inbox = Inbox::initialize($user_id); $inbox = Inbox::initialize($user_id);
} }

View File

@ -128,12 +128,13 @@ class Memcached_DataObject extends Safe_DataObject
} }
static function cacheKey($cls, $k, $v) { static function cacheKey($cls, $k, $v) {
if (is_object($cls) || is_object($k) || is_object($v)) { if (is_object($cls) || is_object($k) || (is_object($v) && !($v instanceof DB_DataObject_Cast))) {
$e = new Exception(); $e = new Exception();
common_log(LOG_ERR, __METHOD__ . ' object in param: ' . common_log(LOG_ERR, __METHOD__ . ' object in param: ' .
str_replace("\n", " ", $e->getTraceAsString())); str_replace("\n", " ", $e->getTraceAsString()));
} }
return common_cache_key(strtolower($cls).':'.$k.':'.$v); $vstr = self::valueString($v);
return common_cache_key(strtolower($cls).':'.$k.':'.$vstr);
} }
static function getcached($cls, $k, $v) { static function getcached($cls, $k, $v) {
@ -229,10 +230,10 @@ class Memcached_DataObject extends Safe_DataObject
if (empty($this->$key)) { if (empty($this->$key)) {
continue; continue;
} }
$ckeys[] = $this->cacheKey($this->tableName(), $key, $this->$key); $ckeys[] = $this->cacheKey($this->tableName(), $key, self::valueString($this->$key));
} else if ($type == 'K' || $type == 'N') { } else if ($type == 'K' || $type == 'N') {
$pkey[] = $key; $pkey[] = $key;
$pval[] = $this->$key; $pval[] = self::valueString($this->$key);
} else { } else {
throw new Exception("Unknown key type $key => $type for " . $this->tableName()); throw new Exception("Unknown key type $key => $type for " . $this->tableName());
} }
@ -351,7 +352,7 @@ class Memcached_DataObject extends Safe_DataObject
* low-level database function and add a comment to the * low-level database function and add a comment to the
* query string. This should then be visible in process lists * query string. This should then be visible in process lists
* and slow query logs, to help identify problem areas. * and slow query logs, to help identify problem areas.
* *
* Also marks whether this was a web GET/POST or which daemon * Also marks whether this was a web GET/POST or which daemon
* was running it. * was running it.
* *
@ -604,5 +605,30 @@ class Memcached_DataObject extends Safe_DataObject
return $c->set($cacheKey, $value); return $c->set($cacheKey, $value);
} }
static function valueString($v)
{
$vstr = null;
if (is_object($v) && $v instanceof DB_DataObject_Cast) {
switch ($v->type) {
case 'date':
$vstr = $v->year . '-' . $v->month . '-' . $v->day;
break;
case 'blob':
case 'string':
case 'sql':
case 'datetime':
case 'time':
throw new ServerException("Unhandled DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
break;
default:
throw new ServerException("Unknown DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
break;
}
} else {
$vstr = strval($v);
}
return $vstr;
}
} }

View File

@ -29,6 +29,7 @@
* @author Robin Millette <millette@controlyourself.ca> * @author Robin Millette <millette@controlyourself.ca>
* @author Sarven Capadisli <csarven@controlyourself.ca> * @author Sarven Capadisli <csarven@controlyourself.ca>
* @author Tom Adams <tom@holizz.com> * @author Tom Adams <tom@holizz.com>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license GNU Affero General Public License http://www.gnu.org/licenses/ * @license GNU Affero General Public License http://www.gnu.org/licenses/
*/ */
@ -97,15 +98,20 @@ class Notice extends Memcached_DataObject
// For auditing purposes, save a record that the notice // For auditing purposes, save a record that the notice
// was deleted. // was deleted.
$deleted = new Deleted_notice(); // @fixme we have some cases where things get re-run and so the
// insert fails.
$deleted = Deleted_notice::staticGet('id', $this->id);
if (!$deleted) {
$deleted = new Deleted_notice();
$deleted->id = $this->id; $deleted->id = $this->id;
$deleted->profile_id = $this->profile_id; $deleted->profile_id = $this->profile_id;
$deleted->uri = $this->uri; $deleted->uri = $this->uri;
$deleted->created = $this->created; $deleted->created = $this->created;
$deleted->deleted = common_sql_now(); $deleted->deleted = common_sql_now();
$deleted->insert(); $deleted->insert();
}
// Clear related records // Clear related records
@ -704,7 +710,7 @@ class Notice extends Memcached_DataObject
/** /**
* Is this notice part of an active conversation? * Is this notice part of an active conversation?
* *
* @return boolean true if other messages exist in the same * @return boolean true if other messages exist in the same
* conversation, false if this is the only one * conversation, false if this is the only one
*/ */
@ -981,8 +987,7 @@ class Notice extends Memcached_DataObject
* messages, we won't deliver to any remote targets as that's the * messages, we won't deliver to any remote targets as that's the
* source service's responsibility. * source service's responsibility.
* *
* @fixme Unlike saveReplies() there's no mail notification here. * Mail notifications etc will be handled later.
* Move that to distrib queue handler?
* *
* @param array of unique identifier URIs for recipients * @param array of unique identifier URIs for recipients
*/ */
@ -1008,8 +1013,6 @@ class Notice extends Memcached_DataObject
$reply->profile_id = $user->id; $reply->profile_id = $user->id;
$id = $reply->insert(); $id = $reply->insert();
self::blow('reply:stream:%d', $user->id);
} }
} }
@ -1021,8 +1024,7 @@ class Notice extends Memcached_DataObject
* and save reply records indicating that this message needs to be * and save reply records indicating that this message needs to be
* delivered to those users. * delivered to those users.
* *
* Side effect: local recipients get e-mail notifications here. * Mail notifications to local profiles will be sent later.
* @fixme move mail notifications to distrib?
* *
* @return array of integer profile IDs * @return array of integer profile IDs
*/ */
@ -1076,23 +1078,21 @@ class Notice extends Memcached_DataObject
throw new ServerException("Couldn't save reply for {$this->id}, {$mentioned->id}"); throw new ServerException("Couldn't save reply for {$this->id}, {$mentioned->id}");
} else { } else {
$replied[$mentioned->id] = 1; $replied[$mentioned->id] = 1;
self::blow('reply:stream:%d', $mentioned->id);
} }
} }
} }
$recipientIds = array_keys($replied); $recipientIds = array_keys($replied);
foreach ($recipientIds as $recipientId) {
$user = User::staticGet('id', $recipientId);
if (!empty($user)) {
self::blow('reply:stream:%d', $reply->profile_id);
mail_notify_attn($user, $this);
}
}
return $recipientIds; return $recipientIds;
} }
/**
* Pull the complete list of @-reply targets for this notice.
*
* @return array of integer profile ids
*/
function getReplies() function getReplies()
{ {
// XXX: cache me // XXX: cache me
@ -1115,6 +1115,30 @@ class Notice extends Memcached_DataObject
return $ids; return $ids;
} }
/**
* Send e-mail notifications to local @-reply targets.
*
* Replies must already have been saved; this is expected to be run
* from the distrib queue handler.
*/
function sendReplyNotifications()
{
// Don't send reply notifications for repeats
if (!empty($this->repeat_of)) {
return array();
}
$recipientIds = $this->getReplies();
foreach ($recipientIds as $recipientId) {
$user = User::staticGet('id', $recipientId);
if (!empty($user)) {
mail_notify_attn($user, $this);
}
}
}
/** /**
* Pull list of groups this notice needs to be delivered to, * Pull list of groups this notice needs to be delivered to,
* as previously recorded by saveGroups() or saveKnownGroups(). * as previously recorded by saveGroups() or saveKnownGroups().
@ -1154,7 +1178,7 @@ class Notice extends Memcached_DataObject
return $groups; return $groups;
} }
function asAtomEntry($namespace=false, $source=false, $author=true) function asAtomEntry($namespace=false, $source=false, $author=true, $cur=null)
{ {
$profile = $this->getProfile(); $profile = $this->getProfile();
@ -1167,7 +1191,8 @@ class Notice extends Memcached_DataObject
'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/',
'xmlns:media' => 'http://purl.org/syndication/atommedia', 'xmlns:media' => 'http://purl.org/syndication/atommedia',
'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
'xmlns:ostatus' => 'http://ostatus.org/schema/1.0'); 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0',
'xmlns:statusnet' => 'http://status.net/schema/api/1/');
} else { } else {
$attrs = array(); $attrs = array();
} }
@ -1202,7 +1227,7 @@ class Notice extends Memcached_DataObject
$xs->element('title', null, common_xml_safe_str($this->content)); $xs->element('title', null, common_xml_safe_str($this->content));
if ($author) { if ($author) {
$xs->raw($profile->asAtomAuthor()); $xs->raw($profile->asAtomAuthor($cur));
$xs->raw($profile->asActivityActor()); $xs->raw($profile->asActivityActor());
} }
@ -1215,6 +1240,46 @@ class Notice extends Memcached_DataObject
$xs->element('published', null, common_date_w3dtf($this->created)); $xs->element('published', null, common_date_w3dtf($this->created));
$xs->element('updated', null, common_date_w3dtf($this->created)); $xs->element('updated', null, common_date_w3dtf($this->created));
$source = null;
$ns = $this->getSource();
if ($ns) {
if (!empty($ns->name) && !empty($ns->url)) {
$source = '<a href="'
. htmlspecialchars($ns->url)
. '" rel="nofollow">'
. htmlspecialchars($ns->name)
. '</a>';
} else {
$source = $ns->code;
}
}
$noticeInfoAttr = array(
'local_id' => $this->id, // local notice ID (useful to clients for ordering)
'source' => $source, // the client name (source attribution)
);
$ns = $this->getSource();
if ($ns) {
if (!empty($ns->url)) {
$noticeInfoAttr['source_link'] = $ns->url;
}
}
if (!empty($cur)) {
$noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false";
$profile = $cur->getProfile();
$noticeInfoAttr['repeated'] = ($profile->hasRepeated($this->id)) ? "true" : "false";
}
if (!empty($this->repeat_of)) {
$noticeInfoAttr['repeat_of'] = $this->repeat_of;
}
$xs->element('statusnet:notice_info', $noticeInfoAttr, null);
if ($this->reply_to) { if ($this->reply_to) {
$reply_notice = Notice::staticGet('id', $this->reply_to); $reply_notice = Notice::staticGet('id', $this->reply_to);
if (!empty($reply_notice)) { if (!empty($reply_notice)) {
@ -1781,4 +1846,53 @@ class Notice extends Memcached_DataObject
return $result; return $result;
} }
/**
* Get the source of the notice
*
* @return Notice_source $ns A notice source object. 'code' is the only attribute
* guaranteed to be populated.
*/
function getSource()
{
$ns = new Notice_source();
if (!empty($this->source)) {
switch ($this->source) {
case 'web':
case 'xmpp':
case 'mail':
case 'omb':
case 'system':
case 'api':
$ns->code = $this->source;
break;
default:
$ns = Notice_source::staticGet($this->source);
if (!$ns) {
$ns = new Notice_source();
$ns->code = $this->source;
$app = Oauth_application::staticGet('name', $this->source);
if ($app) {
$ns->name = $app->name;
$ns->url = $app->source_url;
}
}
break;
}
}
return $ns;
}
/**
* Determine whether the notice was locally created
*
* @return boolean locality
*/
public function isLocal()
{
return ($this->is_local == Notice::LOCAL_PUBLIC ||
$this->is_local == Notice::LOCAL_NONPUBLIC);
}
} }

View File

@ -849,15 +849,23 @@ class Profile extends Memcached_DataObject
* *
* Assumes that Atom has been previously set up as the base namespace. * Assumes that Atom has been previously set up as the base namespace.
* *
* @param Profile $cur the current authenticated user
*
* @return string * @return string
*/ */
function asAtomAuthor() function asAtomAuthor($cur = null)
{ {
$xs = new XMLStringer(true); $xs = new XMLStringer(true);
$xs->elementStart('author'); $xs->elementStart('author');
$xs->element('name', null, $this->nickname); $xs->element('name', null, $this->nickname);
$xs->element('uri', null, $this->getUri()); $xs->element('uri', null, $this->getUri());
if ($cur != null) {
$attrs = Array();
$attrs['following'] = $cur->isSubscribed($this) ? 'true' : 'false';
$attrs['blocking'] = $cur->hasBlocked($this) ? 'true' : 'false';
$xs->element('statusnet:profile_info', $attrs, null);
}
$xs->elementEnd('author'); $xs->elementEnd('author');
return $xs->getString(); return $xs->getString();

View File

@ -64,4 +64,17 @@ class Queue_item extends Memcached_DataObject
$qi = null; $qi = null;
return null; return null;
} }
/**
* Release a claimed item.
*/
function releaseCLaim()
{
// DB_DataObject doesn't let us save nulls right now
$sql = sprintf("UPDATE queue_item SET claimed=NULL WHERE id=%d", $this->id);
$this->query($sql);
$this->claimed = null;
$this->encache();
}
} }

View File

@ -22,6 +22,20 @@ class Reply extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###END_AUTOCODE
/**
* Wrapper for record insertion to update related caches
*/
function insert()
{
$result = parent::insert();
if ($result) {
self::blow('reply:stream:%d', $this->profile_id);
}
return $result;
}
function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0) function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{ {
$ids = Notice::stream(array('Reply', '_streamDirect'), $ids = Notice::stream(array('Reply', '_streamDirect'),

View File

@ -144,26 +144,49 @@ class Status_network extends Safe_DataObject
return parent::update($orig); return parent::update($orig);
} }
/**
* DB_DataObject doesn't allow updating keys (even non-primary)
*/
function updateKeys(&$orig)
{
$this->_connect();
foreach (array('hostname', 'pathname') as $k) {
if (strcmp($this->$k, $orig->$k) != 0) {
$parts[] = $k . ' = ' . $this->_quote($this->$k);
}
}
if (count($parts) == 0) {
// No changes
return true;
}
$toupdate = implode(', ', $parts);
$table = common_database_tablename($this->tableName());
$qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
' WHERE nickname = ' . $this->_quote($this->nickname);
$orig->decache();
$result = $this->query($qry);
if ($result) {
$this->encache();
}
return $result;
}
function delete() function delete()
{ {
$this->decache(); # while we still have the values! $this->decache(); # while we still have the values!
return parent::delete(); return parent::delete();
} }
/** /**
* @param string $servername hostname * @param string $servername hostname
* @param string $pathname URL base path
* @param string $wildcard hostname suffix to match wildcard config * @param string $wildcard hostname suffix to match wildcard config
* @return mixed Status_network or null
*/ */
static function setupSite($servername, $pathname, $wildcard) static function getFromHostname($servername, $wildcard)
{ {
global $config;
$sn = null; $sn = null;
// XXX I18N, probably not crucial for hostnames
// XXX This probably needs a tune up
if (0 == strncasecmp(strrev($wildcard), strrev($servername), strlen($wildcard))) { if (0 == strncasecmp(strrev($wildcard), strrev($servername), strlen($wildcard))) {
// special case for exact match // special case for exact match
if (0 == strcasecmp($servername, $wildcard)) { if (0 == strcasecmp($servername, $wildcard)) {
@ -182,6 +205,23 @@ class Status_network extends Safe_DataObject
} }
} }
} }
return $sn;
}
/**
* @param string $servername hostname
* @param string $pathname URL base path
* @param string $wildcard hostname suffix to match wildcard config
*/
static function setupSite($servername, $pathname, $wildcard)
{
global $config;
$sn = null;
// XXX I18N, probably not crucial for hostnames
// XXX This probably needs a tune up
$sn = self::getFromHostname($servername, $wildcard);
if (!empty($sn)) { if (!empty($sn)) {

View File

@ -459,9 +459,9 @@ class User extends Memcached_DataObject
return $profile->getNotices($offset, $limit, $since_id, $before_id); return $profile->getNotices($offset, $limit, $since_id, $before_id);
} }
function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE, $own=false) function favoriteNotices($own=false, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{ {
$ids = Fave::stream($this->id, $offset, $limit, $own); $ids = Fave::stream($this->id, $offset, $limit, $own, $since_id, $max_id);
return Notice::getStreamByIds($ids); return Notice::getStreamByIds($ids);
} }

View File

@ -154,6 +154,21 @@ class User_group extends Memcached_DataObject
return $members; return $members;
} }
function getMemberCount()
{
// XXX: WORM cache this
$members = $this->getMembers();
$member_count = 0;
/** $member->count() doesn't work. */
while ($members->fetch()) {
$member_count++;
}
return $member_count;
}
function getAdmins($offset=0, $limit=null) function getAdmins($offset=0, $limit=null)
{ {
$qry = $qry =

View File

@ -68,4 +68,27 @@ class User_im_prefs extends Memcached_DataObject
{ {
return array(false,false); return array(false,false);
} }
/**
* We have two compound keys with unique constraints:
* (transport, user_id) which is our primary key, and
* (transport, screenname) which is an additional constraint.
*
* Currently there's not a way to represent that second key
* in the general keys list, so we're adding it here to the
* list of keys to use for caching, ensuring that it gets
* cleared as well when we change.
*
* @return array of cache keys
*/
function _allCacheKeys()
{
$ukeys = 'transport,screenname';
$uvals = $this->transport . ',' . $this->screenname;
$ckeys = parent::_allCacheKeys();
$ckeys[] = $this->cacheKey($this->tableName(), $ukeys, $uvals);
return $ckeys;
}
} }

View File

@ -647,8 +647,10 @@ modified = 384
[user_im_prefs__keys] [user_im_prefs__keys]
user_id = K user_id = K
transport = K transport = K
transport = U ; There's another unique index on (transport, screenname)
screenname = U ; but we have no way to represent a compound index other than
; the primary key in here. To ensure proper cache purging,
; we need to tweak the class.
[user_urlshortener_prefs] [user_urlshortener_prefs]
user_id = 129 user_id = 129

View File

@ -45,7 +45,7 @@ $config['site']['path'] = 'statusnet';
// lighttpd, nginx), you can enable X-Sendfile support for better // lighttpd, nginx), you can enable X-Sendfile support for better
// performance. Presently, only attachment serving when the site is // performance. Presently, only attachment serving when the site is
// in private mode will use X-Sendfile. // in private mode will use X-Sendfile.
// $config['site']['X-Sendfile'] = false; // $config['site']['use_x_sendfile'] = false;
// You may also need to enable X-Sendfile support for your web server and // You may also need to enable X-Sendfile support for your web server and
// allow it to access files outside of the web root. For Apache with // allow it to access files outside of the web root. For Apache with
// mod_xsendfile, you can add these to your .htaccess or server config: // mod_xsendfile, you can add these to your .htaccess or server config:

View File

@ -81,3 +81,42 @@ ALTER TABLE profile ADD COLUMN lon decimal(10,7) /*comment 'longitude'*/;
ALTER TABLE profile ADD COLUMN location_id integer /* comment 'location id if possible'*/; ALTER TABLE profile ADD COLUMN location_id integer /* comment 'location id if possible'*/;
ALTER TABLE profile ADD COLUMN location_ns integer /* comment 'namespace for location'*/; ALTER TABLE profile ADD COLUMN location_ns integer /* comment 'namespace for location'*/;
ALTER TABLE consumer add COLUMN consumer_secret varchar(255) not null ; /*comment 'secret value'*/
ALTER TABLE token ADD COLUMN verifier varchar(255); /* comment 'verifier string for OAuth 1.0a',*/
ALTER TABLE token ADD COLUMN verified_callback varchar(255); /* comment 'verified callback URL for OAuth 1.0a',*/
create table queue_item_new (
id serial /* comment 'unique identifier'*/,
frame bytea not null /* comment 'data: object reference or opaque string'*/,
transport varchar(8) not null /*comment 'queue for what? "email", "jabber", "sms", "irc", ...'*/,
created timestamp not null default CURRENT_TIMESTAMP /*comment 'date this record was created'*/,
claimed timestamp /*comment 'date this item was claimed'*/,
PRIMARY KEY (id)
);
insert into queue_item_new (frame,transport,created,claimed)
select ('0x' || notice_id::text)::bytea,transport,created,claimed from queue_item;
alter table queue_item rename to queue_item_old;
alter table queue_item_new rename to queue_item;
ALTER TABLE confirm_address ALTER column sent set default CURRENT_TIMESTAMP;
create table user_location_prefs (
user_id integer not null /*comment 'user who has the preference'*/ references "user" (id),
share_location int default 1 /* comment 'Whether to share location data'*/,
created timestamp not null /*comment 'date this record was created'*/,
modified timestamp /* comment 'date this record was modified'*/,
primary key (user_id)
);
create table inbox (
user_id integer not null /* comment 'user receiving the notice' */ references "user" (id),
notice_ids bytea /* comment 'packed list of notice ids' */,
primary key (user_id)
);

View File

@ -9,13 +9,16 @@ VALUES
('bti','bti','http://gregkh.github.com/bti/', now()), ('bti','bti','http://gregkh.github.com/bti/', now()),
('choqok', 'Choqok', 'http://choqok.gnufolks.org/', now()), ('choqok', 'Choqok', 'http://choqok.gnufolks.org/', now()),
('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()), ('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()),
('DarterosStatus', 'Darteros Status', 'http://www.darteros.com/doc/Darteros_Status', now()),
('deskbar','Deskbar-Applet','http://www.gnome.org/projects/deskbar-applet/', now()), ('deskbar','Deskbar-Applet','http://www.gnome.org/projects/deskbar-applet/', now()),
('Do','Gnome Do','http://do.davebsd.com/wiki/index.php?title=Microblog_Plugin', now()), ('Do','Gnome Do','http://do.davebsd.com/wiki/index.php?title=Microblog_Plugin', now()),
('drupal','Drupal','http://drupal.org/', now()), ('drupal','Drupal','http://drupal.org/', now()),
('eventbox','EventBox','http://thecosmicmachine.com/eventbox/ ', now()), ('eventbox','EventBox','http://thecosmicmachine.com/eventbox/', now()),
('identica-mode','Emacs Identica-mode','http://nongnu.org/identica-mode/', now()),
('Facebook','Facebook','http://apps.facebook.com/identica/', now()), ('Facebook','Facebook','http://apps.facebook.com/identica/', now()),
('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', now()), ('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', now()),
('get2gnow', 'get2gnow', 'http://uberchicgeekchick.com/?projects=get2gnow', now()), ('get2gnow', 'get2gnow', 'http://uberchicgeekchick.com/?projects=get2gnow', now()),
('gNewBook', 'gNewBook', 'http://www.gnewbook.org/', now()),
('gravity', 'Gravity', 'http://mobileways.de/gravity', now()), ('gravity', 'Gravity', 'http://mobileways.de/gravity', now()),
('Gwibber','Gwibber','http://launchpad.net/gwibber', now()), ('Gwibber','Gwibber','http://launchpad.net/gwibber', now()),
('HelloTxt','HelloTxt','http://hellotxt.com/', now()), ('HelloTxt','HelloTxt','http://hellotxt.com/', now()),
@ -53,6 +56,7 @@ VALUES
('tr.im','tr.im','http://tr.im/', now()), ('tr.im','tr.im','http://tr.im/', now()),
('triklepost', 'Tricklepost', 'http://github.com/zcopley/tricklepost/tree/master', now()), ('triklepost', 'Tricklepost', 'http://github.com/zcopley/tricklepost/tree/master', now()),
('tweenky','Tweenky','http://beta.tweenky.com/', now()), ('tweenky','Tweenky','http://beta.tweenky.com/', now()),
('TweetDeck', 'TweetDeck', 'http://www.tweetdeck.com/', now()),
('twhirl','Twhirl','http://www.twhirl.org/', now()), ('twhirl','Twhirl','http://www.twhirl.org/', now()),
('twibble','twibble','http://www.twibble.de/', now()), ('twibble','twibble','http://www.twibble.de/', now()),
('Twidge','Twidge','http://software.complete.org/twidge', now()), ('Twidge','Twidge','http://software.complete.org/twidge', now()),

View File

@ -8,6 +8,10 @@ create table profile (
homepage varchar(255) /* comment 'identifying URL' */, homepage varchar(255) /* comment 'identifying URL' */,
bio varchar(140) /* comment 'descriptive biography' */, bio varchar(140) /* comment 'descriptive biography' */,
location varchar(255) /* comment 'physical location' */, 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 timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */, modified timestamp /* comment 'date this record was modified' */,
@ -132,6 +136,7 @@ create table notice (
is_local integer default 0 /* comment 'notice was generated by a user' */, is_local integer default 0 /* comment 'notice was generated by a user' */,
source varchar(32) /* comment 'source of comment, like "web", "im", or "clientname"' */, source varchar(32) /* comment 'source of comment, like "web", "im", or "clientname"' */,
conversation integer /*id of root notice in this conversation' */ references notice (id), conversation integer /*id of root notice in this conversation' */ references notice (id),
location varchar(255) /* comment 'physical location' */,
lat decimal(10,7) /* comment 'latitude'*/ , lat decimal(10,7) /* comment 'latitude'*/ ,
lon decimal(10,7) /* comment 'longitude'*/ , lon decimal(10,7) /* comment 'longitude'*/ ,
location_id integer /* comment 'location id if possible'*/ , location_id integer /* comment 'location id if possible'*/ ,
@ -182,6 +187,7 @@ create index fave_modified_idx on fave using btree(modified);
create table consumer ( create table consumer (
consumer_key varchar(255) primary key /* comment 'unique identifier, root URL' */, consumer_key varchar(255) primary key /* comment 'unique identifier, root URL' */,
consumer_secret varchar(255) not null /* comment 'secret value', */,
seed char(32) not null /* comment 'seed for new tokens by this consumer' */, seed char(32) not null /* comment 'seed for new tokens by this consumer' */,
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
@ -195,6 +201,9 @@ create table token (
type integer not null default 0 /* comment 'request or access' */, type integer not null default 0 /* comment 'request or access' */,
state integer default 0 /* comment 'for requests 0 = initial, 1 = authorized, 2 = used' */, state integer default 0 /* comment 'for requests 0 = initial, 1 = authorized, 2 = used' */,
verifier varchar(255) /*comment 'verifier string for OAuth 1.0a'*/,
verified_callback varchar(255) /*comment 'verified callback URL for OAuth 1.0a'*/,
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */, modified timestamp /* comment 'date this record was modified' */,
@ -213,17 +222,33 @@ create table nonce (
primary key (consumer_key, ts, nonce) primary key (consumer_key, ts, nonce)
); );
/* One-to-many relationship of user to openid_url */ create sequence oauth_application_seq;
create table oauth_application (
create table user_openid ( id bigint default nextval('oauth_application_seq') primary key /* comment 'unique identifier' */,
canonical varchar(255) primary key /* comment 'Canonical true URL' */, owner integer not null /* comment 'owner of the application' */ references profile (id),
display varchar(255) not null unique /* comment 'URL for viewing, may be different from canonical' */, consumer_key varchar(255) not null /* comment 'application consumer key' */ references consumer (consumer_key),
user_id integer not null /* comment 'user owning this URL' */ references "user" (id) , name varchar(255) unique not null /* comment 'name of the application' */,
description varchar(255) /* comment 'description of the application' */,
icon varchar(255) not null /* comment 'application icon' */,
source_url varchar(255) /* comment 'application homepage - used for source link' */,
organization varchar(255) /* comment 'name of the organization running the application' */,
homepage varchar(255) /* comment 'homepage for the organization' */,
callback_url varchar(255) /* comment 'url to redirect to after authentication' */,
"type" integer default 0 /* comment 'type of app, 1 = browser, 2 = desktop' */,
access_type integer default 0 /* comment 'default access type, bit 1 = read, bit 2 = write' */,
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */ modified timestamp /* comment 'date this record was modified' */
); );
create index user_openid_user_id_idx on user_openid using btree(user_id);
create table oauth_application_user (
profile_id integer not null /* 'user of the application' */ references profile (id),
application_id integer not null /* 'id of the application' */ references oauth_application (id),
access_type integer default 0 /* 'access type, bit 1 = read, bit 2 = write' */,
token varchar(255) /* 'request or access token' */,
created timestamp not null default CURRENT_TIMESTAMP /* 'date this record was created' */,
modified timestamp /* 'date this record was modified' */,
primary key (profile_id, application_id)
);
/* These are used by JanRain OpenID library */ /* These are used by JanRain OpenID library */
@ -251,7 +276,7 @@ create table confirm_address (
address_extra varchar(255) not null default '' /* comment 'carrier ID, for SMS' */, address_extra varchar(255) not null default '' /* comment 'carrier ID, for SMS' */,
address_type varchar(8) not null /* comment 'address type ("email", "jabber", "sms")' */, address_type varchar(8) not null /* comment 'address type ("email", "jabber", "sms")' */,
claimed timestamp /* comment 'date this was claimed for queueing' */, claimed timestamp /* comment 'date this was claimed for queueing' */,
sent timestamp /* comment 'date this was sent for queueing' */, sent timestamp default CURRENT_TIMESTAMP /* comment 'date this was sent for queueing' */,
modified timestamp /* comment 'date this record was modified' */ modified timestamp /* comment 'date this record was modified' */
); );
@ -262,14 +287,12 @@ create table remember_me (
); );
create table queue_item ( create table queue_item (
id serial /* comment 'unique identifier'*/,
notice_id integer not null /* comment 'notice queued' */ references notice (id) , frame bytea not null /* comment 'data: object reference or opaque string'*/,
transport varchar(8) not null /* comment 'queue for what? "email", "jabber", "sms", "irc", ...' */, transport varchar(8) not null /*comment 'queue for what? "email", "jabber", "sms", "irc", ...'*/,
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, created timestamp not null default CURRENT_TIMESTAMP /*comment 'date this record was created'*/,
claimed timestamp /* comment 'date this item was claimed' */, claimed timestamp /*comment 'date this item was claimed'*/,
PRIMARY KEY (id)
primary key (notice_id, transport)
); );
create index queue_item_created_idx on queue_item using btree(created); create index queue_item_created_idx on queue_item using btree(created);
@ -589,3 +612,39 @@ create table login_token (
primary key (user_id) primary key (user_id)
); );
create table user_location_prefs (
user_id integer not null /* comment 'user who has the preference' */ references "user" (id),
share_location integer default 1 /* comment 'Whether to share location data' */,
created timestamp not null DEFAULT CURRENT_TIMESTAMP /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */,
primary key (user_id)
);
create table inbox (
user_id integer not null /* comment 'user receiving the notice' */ references "user" (id),
notice_ids bytea /* comment 'packed list of notice ids' */,
primary key (user_id)
);
create sequence conversation_seq;
create table conversation (
id bigint default nextval('conversation_seq') primary key /* comment 'unique identifier' */,
uri varchar(225) unique /* comment 'URI of the conversation' */,
created timestamp not null DEFAULT CURRENT_TIMESTAMP /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */
);
create table local_group (
group_id integer primary key /* comment 'group represented' */ references user_group (id),
nickname varchar(64) unique /* comment 'group represented' */,
created timestamp not null DEFAULT CURRENT_TIMESTAMP /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */
);

View File

@ -1,4 +1,4 @@
You can post messages to %%site.name%% using a many kinds of cell You can post messages to %%site.name%% using many kinds of cell
phones that support SMS messaging. This site does not support SMS phones that support SMS messaging. This site does not support SMS
directly; rather, it uses your carrier's email gateway to send and directly; rather, it uses your carrier's email gateway to send and
receive messages. receive messages.

82
extlib/Mail.php Normal file → Executable file
View File

@ -1,22 +1,47 @@
<?php <?php
// /**
// +----------------------------------------------------------------------+ * PEAR's Mail:: interface.
// | PHP Version 4 | *
// +----------------------------------------------------------------------+ * PHP versions 4 and 5
// | Copyright (c) 1997-2003 The PHP Group | *
// +----------------------------------------------------------------------+ * LICENSE:
// | This source file is subject to version 2.02 of the PHP license, | *
// | that is bundled with this package in the file LICENSE, and is | * Copyright (c) 2002-2007, Richard Heyes
// | available at through the world-wide-web at | * All rights reserved.
// | http://www.php.net/license/2_02.txt. | *
// | If you did not receive a copy of the PHP license and are unable to | * Redistribution and use in source and binary forms, with or without
// | obtain it through the world-wide-web, please send a note to | * modification, are permitted provided that the following conditions
// | license@php.net so we can mail you a copy immediately. | * are met:
// +----------------------------------------------------------------------+ *
// | Author: Chuck Hagenbuch <chuck@horde.org> | * o Redistributions of source code must retain the above copyright
// +----------------------------------------------------------------------+ * notice, this list of conditions and the following disclaimer.
// * o Redistributions in binary form must reproduce the above copyright
// $Id: Mail.php,v 1.17 2006/09/15 03:41:18 jon Exp $ * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Mail
* @package Mail
* @author Chuck Hagenbuch <chuck@horde.org>
* @copyright 1997-2010 Chuck Hagenbuch
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: Mail.php 294747 2010-02-08 08:18:33Z clockwerx $
* @link http://pear.php.net/package/Mail/
*/
require_once 'PEAR.php'; require_once 'PEAR.php';
@ -26,7 +51,7 @@ require_once 'PEAR.php';
* useful in multiple mailer backends. * useful in multiple mailer backends.
* *
* @access public * @access public
* @version $Revision: 1.17 $ * @version $Revision: 294747 $
* @package Mail * @package Mail
*/ */
class Mail class Mail
@ -82,12 +107,20 @@ class Mail
* @return mixed Returns true on success, or a PEAR_Error * @return mixed Returns true on success, or a PEAR_Error
* containing a descriptive error message on * containing a descriptive error message on
* failure. * failure.
*
* @access public * @access public
* @deprecated use Mail_mail::send instead * @deprecated use Mail_mail::send instead
*/ */
function send($recipients, $headers, $body) function send($recipients, $headers, $body)
{ {
$this->_sanitizeHeaders($headers); if (!is_array($headers)) {
return PEAR::raiseError('$headers must be an array');
}
$result = $this->_sanitizeHeaders($headers);
if (is_a($result, 'PEAR_Error')) {
return $result;
}
// if we're passed an array of recipients, implode it. // if we're passed an array of recipients, implode it.
if (is_array($recipients)) { if (is_array($recipients)) {
@ -103,10 +136,9 @@ class Mail
} }
// flatten the headers out. // flatten the headers out.
list(,$text_headers) = Mail::prepareHeaders($headers); list(, $text_headers) = Mail::prepareHeaders($headers);
return mail($recipients, $subject, $body, $text_headers); return mail($recipients, $subject, $body, $text_headers);
} }
/** /**
@ -151,9 +183,9 @@ class Mail
foreach ($headers as $key => $value) { foreach ($headers as $key => $value) {
if (strcasecmp($key, 'From') === 0) { if (strcasecmp($key, 'From') === 0) {
include_once 'Mail/RFC822.php'; include_once 'Mail/RFC822.php';
$parser = &new Mail_RFC822(); $parser = new Mail_RFC822();
$addresses = $parser->parseAddressList($value, 'localhost', false); $addresses = $parser->parseAddressList($value, 'localhost', false);
if (PEAR::isError($addresses)) { if (is_a($addresses, 'PEAR_Error')) {
return $addresses; return $addresses;
} }
@ -221,7 +253,7 @@ class Mail
$addresses = Mail_RFC822::parseAddressList($recipients, 'localhost', false); $addresses = Mail_RFC822::parseAddressList($recipients, 'localhost', false);
// If parseAddressList() returned a PEAR_Error object, just return it. // If parseAddressList() returned a PEAR_Error object, just return it.
if (PEAR::isError($addresses)) { if (is_a($addresses, 'PEAR_Error')) {
return $addresses; return $addresses;
} }

83
extlib/Mail/RFC822.php Normal file → Executable file
View File

@ -1,37 +1,48 @@
<?php <?php
// +-----------------------------------------------------------------------+ /**
// | Copyright (c) 2001-2002, Richard Heyes | * RFC 822 Email address list validation Utility
// | All rights reserved. | *
// | | * PHP versions 4 and 5
// | Redistribution and use in source and binary forms, with or without | *
// | modification, are permitted provided that the following conditions | * LICENSE:
// | are met: | *
// | | * Copyright (c) 2001-2010, Richard Heyes
// | o Redistributions of source code must retain the above copyright | * All rights reserved.
// | notice, this list of conditions and the following disclaimer. | *
// | o Redistributions in binary form must reproduce the above copyright | * Redistribution and use in source and binary forms, with or without
// | notice, this list of conditions and the following disclaimer in the | * modification, are permitted provided that the following conditions
// | documentation and/or other materials provided with the distribution.| * are met:
// | o The names of the authors may not be used to endorse or promote | *
// | products derived from this software without specific prior written | * o Redistributions of source code must retain the above copyright
// | permission. | * notice, this list of conditions and the following disclaimer.
// | | * o Redistributions in binary form must reproduce the above copyright
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | * notice, this list of conditions and the following disclaimer in the
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | * documentation and/or other materials provided with the distribution.
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | * o The names of the authors may not be used to endorse or promote
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | * products derived from this software without specific prior written
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | * permission.
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | *
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// +-----------------------------------------------------------------------+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// | Authors: Richard Heyes <richard@phpguru.org> | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// | Chuck Hagenbuch <chuck@horde.org> | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// +-----------------------------------------------------------------------+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Mail
* @package Mail
* @author Richard Heyes <richard@phpguru.org>
* @author Chuck Hagenbuch <chuck@horde.org
* @copyright 2001-2010 Richard Heyes
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: RFC822.php 294749 2010-02-08 08:22:25Z clockwerx $
* @link http://pear.php.net/package/Mail/
*/
/** /**
* RFC 822 Email address list validation Utility * RFC 822 Email address list validation Utility
@ -52,7 +63,7 @@
* *
* @author Richard Heyes <richard@phpguru.org> * @author Richard Heyes <richard@phpguru.org>
* @author Chuck Hagenbuch <chuck@horde.org> * @author Chuck Hagenbuch <chuck@horde.org>
* @version $Revision: 1.24 $ * @version $Revision: 294749 $
* @license BSD * @license BSD
* @package Mail * @package Mail
*/ */
@ -635,8 +646,8 @@ class Mail_RFC822 {
$comment = $this->_splitCheck($parts, ')'); $comment = $this->_splitCheck($parts, ')');
$comments[] = $comment; $comments[] = $comment;
// +1 is for the trailing ) // +2 is for the brackets
$_mailbox = substr($_mailbox, strpos($_mailbox, $comment)+strlen($comment)+1); $_mailbox = substr($_mailbox, strpos($_mailbox, '('.$comment)+strlen($comment)+2);
} else { } else {
break; break;
} }

63
extlib/Mail/mail.php Normal file → Executable file
View File

@ -1,27 +1,52 @@
<?php <?php
// /**
// +----------------------------------------------------------------------+ * internal PHP-mail() implementation of the PEAR Mail:: interface.
// | PHP Version 4 | *
// +----------------------------------------------------------------------+ * PHP versions 4 and 5
// | Copyright (c) 1997-2003 The PHP Group | *
// +----------------------------------------------------------------------+ * LICENSE:
// | This source file is subject to version 2.02 of the PHP license, | *
// | that is bundled with this package in the file LICENSE, and is | * Copyright (c) 2010 Chuck Hagenbuch
// | available at through the world-wide-web at | * All rights reserved.
// | http://www.php.net/license/2_02.txt. | *
// | If you did not receive a copy of the PHP license and are unable to | * Redistribution and use in source and binary forms, with or without
// | obtain it through the world-wide-web, please send a note to | * modification, are permitted provided that the following conditions
// | license@php.net so we can mail you a copy immediately. | * are met:
// +----------------------------------------------------------------------+ *
// | Author: Chuck Hagenbuch <chuck@horde.org> | * o Redistributions of source code must retain the above copyright
// +----------------------------------------------------------------------+ * notice, this list of conditions and the following disclaimer.
// * o Redistributions in binary form must reproduce the above copyright
// $Id: mail.php,v 1.20 2007/10/06 17:00:00 chagenbu Exp $ * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Mail
* @package Mail
* @author Chuck Hagenbuch <chuck@horde.org>
* @copyright 2010 Chuck Hagenbuch
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: mail.php 294747 2010-02-08 08:18:33Z clockwerx $
* @link http://pear.php.net/package/Mail/
*/
/** /**
* internal PHP-mail() implementation of the PEAR Mail:: interface. * internal PHP-mail() implementation of the PEAR Mail:: interface.
* @package Mail * @package Mail
* @version $Revision: 1.20 $ * @version $Revision: 294747 $
*/ */
class Mail_mail extends Mail { class Mail_mail extends Mail {

64
extlib/Mail/mock.php Normal file → Executable file
View File

@ -1,29 +1,53 @@
<?php <?php
// /**
// +----------------------------------------------------------------------+ * Mock implementation
// | PHP Version 4 | *
// +----------------------------------------------------------------------+ * PHP versions 4 and 5
// | Copyright (c) 1997-2003 The PHP Group | *
// +----------------------------------------------------------------------+ * LICENSE:
// | This source file is subject to version 2.02 of the PHP license, | *
// | that is bundled with this package in the file LICENSE, and is | * Copyright (c) 2010 Chuck Hagenbuch
// | available at through the world-wide-web at | * All rights reserved.
// | http://www.php.net/license/2_02.txt. | *
// | If you did not receive a copy of the PHP license and are unable to | * Redistribution and use in source and binary forms, with or without
// | obtain it through the world-wide-web, please send a note to | * modification, are permitted provided that the following conditions
// | license@php.net so we can mail you a copy immediately. | * are met:
// +----------------------------------------------------------------------+ *
// | Author: Chuck Hagenbuch <chuck@horde.org> | * o Redistributions of source code must retain the above copyright
// +----------------------------------------------------------------------+ * notice, this list of conditions and the following disclaimer.
// * o Redistributions in binary form must reproduce the above copyright
// $Id: mock.php,v 1.1 2007/12/08 17:57:54 chagenbu Exp $ * notice, this list of conditions and the following disclaimer in the
// * documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Mail
* @package Mail
* @author Chuck Hagenbuch <chuck@horde.org>
* @copyright 2010 Chuck Hagenbuch
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: mock.php 294747 2010-02-08 08:18:33Z clockwerx $
* @link http://pear.php.net/package/Mail/
*/
/** /**
* Mock implementation of the PEAR Mail:: interface for testing. * Mock implementation of the PEAR Mail:: interface for testing.
* @access public * @access public
* @package Mail * @package Mail
* @version $Revision: 1.1 $ * @version $Revision: 294747 $
*/ */
class Mail_mock extends Mail { class Mail_mock extends Mail {

64
extlib/Mail/null.php Normal file → Executable file
View File

@ -1,29 +1,53 @@
<?php <?php
// /**
// +----------------------------------------------------------------------+ * Null implementation of the PEAR Mail interface
// | PHP Version 4 | *
// +----------------------------------------------------------------------+ * PHP versions 4 and 5
// | Copyright (c) 1997-2003 The PHP Group | *
// +----------------------------------------------------------------------+ * LICENSE:
// | This source file is subject to version 2.02 of the PHP license, | *
// | that is bundled with this package in the file LICENSE, and is | * Copyright (c) 2010 Phil Kernick
// | available at through the world-wide-web at | * All rights reserved.
// | http://www.php.net/license/2_02.txt. | *
// | If you did not receive a copy of the PHP license and are unable to | * Redistribution and use in source and binary forms, with or without
// | obtain it through the world-wide-web, please send a note to | * modification, are permitted provided that the following conditions
// | license@php.net so we can mail you a copy immediately. | * are met:
// +----------------------------------------------------------------------+ *
// | Author: Phil Kernick <philk@rotfl.com.au> | * o Redistributions of source code must retain the above copyright
// +----------------------------------------------------------------------+ * notice, this list of conditions and the following disclaimer.
// * o Redistributions in binary form must reproduce the above copyright
// $Id: null.php,v 1.2 2004/04/06 05:19:03 jon Exp $ * notice, this list of conditions and the following disclaimer in the
// * documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Mail
* @package Mail
* @author Phil Kernick <philk@rotfl.com.au>
* @copyright 2010 Phil Kernick
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: null.php 294747 2010-02-08 08:18:33Z clockwerx $
* @link http://pear.php.net/package/Mail/
*/
/** /**
* Null implementation of the PEAR Mail:: interface. * Null implementation of the PEAR Mail:: interface.
* @access public * @access public
* @package Mail * @package Mail
* @version $Revision: 1.2 $ * @version $Revision: 294747 $
*/ */
class Mail_null extends Mail { class Mail_null extends Mail {

7
extlib/Mail/sendmail.php Normal file → Executable file
View File

@ -20,7 +20,7 @@
* Sendmail implementation of the PEAR Mail:: interface. * Sendmail implementation of the PEAR Mail:: interface.
* @access public * @access public
* @package Mail * @package Mail
* @version $Revision: 1.19 $ * @version $Revision: 294744 $
*/ */
class Mail_sendmail extends Mail { class Mail_sendmail extends Mail {
@ -117,7 +117,7 @@ class Mail_sendmail extends Mail {
if (is_a($recipients, 'PEAR_Error')) { if (is_a($recipients, 'PEAR_Error')) {
return $recipients; return $recipients;
} }
$recipients = escapeShellCmd(implode(' ', $recipients)); $recipients = implode(' ', array_map('escapeshellarg', $recipients));
$headerElements = $this->prepareHeaders($headers); $headerElements = $this->prepareHeaders($headers);
if (is_a($headerElements, 'PEAR_Error')) { if (is_a($headerElements, 'PEAR_Error')) {
@ -141,7 +141,8 @@ class Mail_sendmail extends Mail {
return PEAR::raiseError('From address specified with dangerous characters.'); return PEAR::raiseError('From address specified with dangerous characters.');
} }
$from = escapeShellCmd($from); $from = escapeshellarg($from); // Security bug #16200
$mail = @popen($this->sendmail_path . (!empty($this->sendmail_args) ? ' ' . $this->sendmail_args : '') . " -f$from -- $recipients", 'w'); $mail = @popen($this->sendmail_path . (!empty($this->sendmail_args) ? ' ' . $this->sendmail_args : '') . " -f$from -- $recipients", 'w');
if (!$mail) { if (!$mail) {
return PEAR::raiseError('Failed to open sendmail [' . $this->sendmail_path . '] for execution.'); return PEAR::raiseError('Failed to open sendmail [' . $this->sendmail_path . '] for execution.');

73
extlib/Mail/smtp.php Normal file → Executable file
View File

@ -1,21 +1,48 @@
<?php <?php
// /**
// +----------------------------------------------------------------------+ * SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class.
// | PHP Version 4 | *
// +----------------------------------------------------------------------+ * PHP versions 4 and 5
// | Copyright (c) 1997-2003 The PHP Group | *
// +----------------------------------------------------------------------+ * LICENSE:
// | This source file is subject to version 2.02 of the PHP license, | *
// | that is bundled with this package in the file LICENSE, and is | * Copyright (c) 2010, Chuck Hagenbuch
// | available at through the world-wide-web at | * All rights reserved.
// | http://www.php.net/license/2_02.txt. | *
// | If you did not receive a copy of the PHP license and are unable to | * Redistribution and use in source and binary forms, with or without
// | obtain it through the world-wide-web, please send a note to | * modification, are permitted provided that the following conditions
// | license@php.net so we can mail you a copy immediately. | * are met:
// +----------------------------------------------------------------------+ *
// | Authors: Chuck Hagenbuch <chuck@horde.org> | * o Redistributions of source code must retain the above copyright
// | Jon Parise <jon@php.net> | * notice, this list of conditions and the following disclaimer.
// +----------------------------------------------------------------------+ * o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category HTTP
* @package HTTP_Request
* @author Jon Parise <jon@php.net>
* @author Chuck Hagenbuch <chuck@horde.org>
* @copyright 2010 Chuck Hagenbuch
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: smtp.php 294747 2010-02-08 08:18:33Z clockwerx $
* @link http://pear.php.net/package/Mail/
*/
/** Error: Failed to create a Net_SMTP object */ /** Error: Failed to create a Net_SMTP object */
define('PEAR_MAIL_SMTP_ERROR_CREATE', 10000); define('PEAR_MAIL_SMTP_ERROR_CREATE', 10000);
@ -42,7 +69,7 @@ define('PEAR_MAIL_SMTP_ERROR_DATA', 10006);
* SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class. * SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class.
* @access public * @access public
* @package Mail * @package Mail
* @version $Revision: 1.33 $ * @version $Revision: 294747 $
*/ */
class Mail_smtp extends Mail { class Mail_smtp extends Mail {
@ -278,6 +305,16 @@ class Mail_smtp extends Mail {
/* Send the message's headers and the body as SMTP data. */ /* Send the message's headers and the body as SMTP data. */
$res = $this->_smtp->data($textHeaders . "\r\n\r\n" . $body); $res = $this->_smtp->data($textHeaders . "\r\n\r\n" . $body);
list(,$args) = $this->_smtp->getResponse();
if (preg_match("/Ok: queued as (.*)/", $args, $queued)) {
$this->queued_as = $queued[1];
}
/* we need the greeting; from it we can extract the authorative name of the mail server we've really connected to.
* ideal if we're connecting to a round-robin of relay servers and need to track which exact one took the email */
$this->greeting = $this->_smtp->getGreeting();
if (is_a($res, 'PEAR_Error')) { if (is_a($res, 'PEAR_Error')) {
$error = $this->_error('Failed to send data', $res); $error = $this->_error('Failed to send data', $res);
$this->_smtp->rset(); $this->_smtp->rset();

44
extlib/Mail/smtpmx.php Normal file → Executable file
View File

@ -8,19 +8,43 @@
* *
* PHP versions 4 and 5 * PHP versions 4 and 5
* *
* LICENSE: This source file is subject to version 3.0 of the PHP license * 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 * Copyright (c) 2010, gERD Schaufelberger
* the PHP License and are unable to obtain it through the web, please * All rights reserved.
* send a note to license@php.net so we can mail you a copy immediately. *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* o The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @category Mail * @category Mail
* @package Mail_smtpmx * @package Mail_smtpmx
* @author gERD Schaufelberger <gerd@php-tools.net> * @author gERD Schaufelberger <gerd@php-tools.net>
* @copyright 1997-2005 The PHP Group * @copyright 2010 gERD Schaufelberger
* @license http://www.php.net/license/3_0.txt PHP License 3.0 * @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: smtpmx.php,v 1.2 2007/10/06 17:00:00 chagenbu Exp $ * @version CVS: $Id: smtpmx.php 294747 2010-02-08 08:18:33Z clockwerx $
* @see Mail * @link http://pear.php.net/package/Mail/
*/ */
require_once 'Net/SMTP.php'; require_once 'Net/SMTP.php';
@ -32,7 +56,7 @@ require_once 'Net/SMTP.php';
* @access public * @access public
* @author gERD Schaufelberger <gerd@php-tools.net> * @author gERD Schaufelberger <gerd@php-tools.net>
* @package Mail * @package Mail
* @version $Revision: 1.2 $ * @version $Revision: 294747 $
*/ */
class Mail_smtpmx extends Mail { class Mail_smtpmx extends Mail {

View File

@ -18,7 +18,7 @@
// | Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> | // | Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> |
// +----------------------------------------------------------------------+ // +----------------------------------------------------------------------+
// //
// $Id: SMTP.php,v 1.63 2008/06/10 05:39:12 jon Exp $ // $Id: SMTP.php 293948 2010-01-24 21:46:00Z jon $
require_once 'PEAR.php'; require_once 'PEAR.php';
require_once 'Net/Socket.php'; require_once 'Net/Socket.php';
@ -91,6 +91,13 @@ class Net_SMTP
*/ */
var $_debug = false; var $_debug = false;
/**
* Debug output handler.
* @var callback
* @access private
*/
var $_debug_handler = null;
/** /**
* The socket resource being used to connect to the SMTP server. * The socket resource being used to connect to the SMTP server.
* @var resource * @var resource
@ -112,6 +119,13 @@ class Net_SMTP
*/ */
var $_arguments = array(); var $_arguments = array();
/**
* Stores the SMTP server's greeting string.
* @var string
* @access private
*/
var $_greeting = null;
/** /**
* Stores detected features of the SMTP server. * Stores detected features of the SMTP server.
* @var array * @var array
@ -172,9 +186,30 @@ class Net_SMTP
* @access public * @access public
* @since 1.1.0 * @since 1.1.0
*/ */
function setDebug($debug) function setDebug($debug, $handler = null)
{ {
$this->_debug = $debug; $this->_debug = $debug;
$this->_debug_handler = $handler;
}
/**
* Write the given debug text to the current debug output handler.
*
* @param string $message Debug mesage text.
*
* @access private
* @since 1.3.3
*/
function _debug($message)
{
if ($this->_debug) {
if ($this->_debug_handler) {
call_user_func_array($this->_debug_handler,
array(&$this, $message));
} else {
echo "DEBUG: $message\n";
}
}
} }
/** /**
@ -189,13 +224,12 @@ class Net_SMTP
*/ */
function _send($data) function _send($data)
{ {
if ($this->_debug) { $this->_debug("Send: $data");
echo "DEBUG: Send: $data\n";
}
if (PEAR::isError($error = $this->_socket->write($data))) { $error = $this->_socket->write($data);
return PEAR::raiseError('Failed to write to socket: ' . if ($error === false || PEAR::isError($error)) {
$error->getMessage()); $msg = ($error) ? $error->getMessage() : "unknown error";
return PEAR::raiseError("Failed to write to socket: $msg");
} }
return true; return true;
@ -262,9 +296,7 @@ class Net_SMTP
for ($i = 0; $i <= $this->_pipelined_commands; $i++) { for ($i = 0; $i <= $this->_pipelined_commands; $i++) {
while ($line = $this->_socket->readLine()) { while ($line = $this->_socket->readLine()) {
if ($this->_debug) { $this->_debug("Recv: $line");
echo "DEBUG: Recv: $line\n";
}
/* If we receive an empty line, the connection has been closed. */ /* If we receive an empty line, the connection has been closed. */
if (empty($line)) { if (empty($line)) {
@ -319,6 +351,20 @@ class Net_SMTP
return array($this->_code, join("\n", $this->_arguments)); return array($this->_code, join("\n", $this->_arguments));
} }
/**
* Return the SMTP server's greeting string.
*
* @return string A string containing the greeting string, or null if a
* greeting has not been received.
*
* @access public
* @since 1.3.3
*/
function getGreeting()
{
return $this->_greeting;
}
/** /**
* Attempt to connect to the SMTP server. * Attempt to connect to the SMTP server.
* *
@ -334,6 +380,7 @@ class Net_SMTP
*/ */
function connect($timeout = null, $persistent = false) function connect($timeout = null, $persistent = false)
{ {
$this->_greeting = null;
$result = $this->_socket->connect($this->host, $this->port, $result = $this->_socket->connect($this->host, $this->port,
$persistent, $timeout); $persistent, $timeout);
if (PEAR::isError($result)) { if (PEAR::isError($result)) {
@ -344,6 +391,10 @@ class Net_SMTP
if (PEAR::isError($error = $this->_parseResponse(220))) { if (PEAR::isError($error = $this->_parseResponse(220))) {
return $error; return $error;
} }
/* Extract and store a copy of the server's greeting string. */
list(, $this->_greeting) = $this->getResponse();
if (PEAR::isError($error = $this->_negotiate())) { if (PEAR::isError($error = $this->_negotiate())) {
return $error; return $error;
} }
@ -452,40 +503,43 @@ class Net_SMTP
* @param string The password to authenticate with. * @param string The password to authenticate with.
* @param string The requested authentication method. If none is * @param string The requested authentication method. If none is
* specified, the best supported method will be used. * specified, the best supported method will be used.
* @param bool Flag indicating whether or not TLS should be attempted.
* *
* @return mixed Returns a PEAR_Error with an error message on any * @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success. * kind of failure, or true on success.
* @access public * @access public
* @since 1.0 * @since 1.0
*/ */
function auth($uid, $pwd , $method = '') function auth($uid, $pwd , $method = '', $tls = true)
{ {
if (empty($this->_esmtp['AUTH'])) { /* We can only attempt a TLS connection if one has been requested,
if (version_compare(PHP_VERSION, '5.1.0', '>=')) { * we're running PHP 5.1.0 or later, have access to the OpenSSL
if (!isset($this->_esmtp['STARTTLS'])) { * extension, are connected to an SMTP server which supports the
return PEAR::raiseError('SMTP server does not support authentication'); * STARTTLS extension, and aren't already connected over a secure
} * (SSL) socket connection. */
if (PEAR::isError($result = $this->_put('STARTTLS'))) { if ($tls && version_compare(PHP_VERSION, '5.1.0', '>=') &&
return $result; extension_loaded('openssl') && isset($this->_esmtp['STARTTLS']) &&
} strncasecmp($this->host, 'ssl://', 6) !== 0) {
if (PEAR::isError($result = $this->_parseResponse(220))) { /* Start the TLS connection attempt. */
return $result; if (PEAR::isError($result = $this->_put('STARTTLS'))) {
} return $result;
if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
return $result;
} elseif ($result !== true) {
return PEAR::raiseError('STARTTLS failed');
}
/* Send EHLO again to recieve the AUTH string from the
* SMTP server. */
$this->_negotiate();
if (empty($this->_esmtp['AUTH'])) {
return PEAR::raiseError('SMTP server does not support authentication');
}
} else {
return PEAR::raiseError('SMTP server does not support authentication');
} }
if (PEAR::isError($result = $this->_parseResponse(220))) {
return $result;
}
if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
return $result;
} elseif ($result !== true) {
return PEAR::raiseError('STARTTLS failed');
}
/* Send EHLO again to recieve the AUTH string from the
* SMTP server. */
$this->_negotiate();
}
if (empty($this->_esmtp['AUTH'])) {
return PEAR::raiseError('SMTP server does not support authentication');
} }
/* If no method has been specified, get the name of the best /* If no method has been specified, get the name of the best
@ -844,30 +898,51 @@ class Net_SMTP
/** /**
* Send the DATA command. * Send the DATA command.
* *
* @param string $data The message body to send. * @param mixed $data The message data, either as a string or an open
* file resource.
* @param string $headers The message headers. If $headers is provided,
* $data is assumed to contain only body data.
* *
* @return mixed Returns a PEAR_Error with an error message on any * @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success. * kind of failure, or true on success.
* @access public * @access public
* @since 1.0 * @since 1.0
*/ */
function data($data) function data($data, $headers = null)
{ {
/* Verify that $data is a supported type. */
if (!is_string($data) && !is_resource($data)) {
return PEAR::raiseError('Expected a string or file resource');
}
/* RFC 1870, section 3, subsection 3 states "a value of zero /* RFC 1870, section 3, subsection 3 states "a value of zero
* indicates that no fixed maximum message size is in force". * indicates that no fixed maximum message size is in force".
* Furthermore, it says that if "the parameter is omitted no * Furthermore, it says that if "the parameter is omitted no
* information is conveyed about the server's fixed maximum * information is conveyed about the server's fixed maximum
* message size". */ * message size". */
if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) { if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) {
if (strlen($data) >= $this->_esmtp['SIZE']) { /* Start by considering the size of the optional headers string.
* We also account for the addition 4 character "\r\n\r\n"
* separator sequence. */
$size = (is_null($headers)) ? 0 : strlen($headers) + 4;
if (is_resource($data)) {
$stat = fstat($data);
if ($stat === false) {
return PEAR::raiseError('Failed to get file size');
}
$size += $stat['size'];
} else {
$size += strlen($data);
}
if ($size >= $this->_esmtp['SIZE']) {
$this->disconnect(); $this->disconnect();
return PEAR::raiseError('Message size excedes the server limit'); return PEAR::raiseError('Message size exceeds server limit');
} }
} }
/* Quote the data based on the SMTP standards. */ /* Initiate the DATA command. */
$this->quotedata($data);
if (PEAR::isError($error = $this->_put('DATA'))) { if (PEAR::isError($error = $this->_put('DATA'))) {
return $error; return $error;
} }
@ -875,9 +950,40 @@ class Net_SMTP
return $error; return $error;
} }
if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) { /* If we have a separate headers string, send it first. */
return $result; if (!is_null($headers)) {
$this->quotedata($headers);
if (PEAR::isError($result = $this->_send($headers . "\r\n\r\n"))) {
return $result;
}
} }
/* Now we can send the message body data. */
if (is_resource($data)) {
/* Stream the contents of the file resource out over our socket
* connection, line by line. Each line must be run through the
* quoting routine. */
while ($line = fgets($data, 1024)) {
$this->quotedata($line);
if (PEAR::isError($result = $this->_send($line))) {
return $result;
}
}
/* Finally, send the DATA terminator sequence. */
if (PEAR::isError($result = $this->_send("\r\n.\r\n"))) {
return $result;
}
} else {
/* Just send the entire quoted string followed by the DATA
* terminator. */
$this->quotedata($data);
if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) {
return $result;
}
}
/* Verify that the data was successfully received by the server. */
if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
return $error; return $error;
} }

View File

@ -19,16 +19,20 @@
* @category StatusNet * @category StatusNet
* @package StatusNet * @package StatusNet
* @author Brenda Wallace <shiny@cpan.org> * @author Brenda Wallace <shiny@cpan.org>
* @author Brion Vibber <brion@pobox.com>
* @author Christopher Vollick <psycotica0@gmail.com> * @author Christopher Vollick <psycotica0@gmail.com>
* @author CiaranG <ciaran@ciarang.com> * @author CiaranG <ciaran@ciarang.com>
* @author Craig Andrews <candrews@integralblue.com> * @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@controlezvous.ca> * @author Evan Prodromou <evan@controlezvous.ca>
* @author Gina Haeussge <osd@foosel.net> * @author Gina Haeussge <osd@foosel.net>
* @author James Walker <walkah@walkah.net>
* @author Jeffery To <jeffery.to@gmail.com> * @author Jeffery To <jeffery.to@gmail.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz> * @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <millette@controlyourself.ca> * @author Robin Millette <millette@controlyourself.ca>
* @author Sarven Capadisli <csarven@controlyourself.ca> * @author Sarven Capadisli <csarven@controlyourself.ca>
* @author Tom Adams <tom@holizz.com> * @author Tom Adams <tom@holizz.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* *
* @license GNU Affero General Public License http://www.gnu.org/licenses/ * @license GNU Affero General Public License http://www.gnu.org/licenses/
*/ */
@ -200,7 +204,7 @@ function checkMirror($action_obj, $args)
function isLoginAction($action) function isLoginAction($action)
{ {
static $loginActions = array('login', 'recoverpassword', 'api', 'doc', 'register', 'publicxrds', 'otp', 'opensearch'); static $loginActions = array('login', 'recoverpassword', 'api', 'doc', 'register', 'publicxrds', 'otp', 'opensearch', 'rsd');
$login = null; $login = null;

View File

@ -31,6 +31,7 @@
* @author Sarven Capadisli <csarven@status.net> * @author Sarven Capadisli <csarven@status.net>
* @author Tom Adams <tom@holizz.com> * @author Tom Adams <tom@holizz.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license GNU Affero General Public License http://www.gnu.org/licenses/ * @license GNU Affero General Public License http://www.gnu.org/licenses/
* @version 0.9.x * @version 0.9.x
* @link http://status.net * @link http://status.net

View File

@ -84,7 +84,7 @@ var SN = { // StatusNet
form.find('#'+SN.C.S.NoticeTextCount).text(jQuery.data(form[0], 'ElementData').MaxLength); form.find('#'+SN.C.S.NoticeTextCount).text(jQuery.data(form[0], 'ElementData').MaxLength);
} }
if ($('body')[0].id != 'conversation' && window.location.hash.length === 0) { if ($('body')[0].id != 'conversation' && window.location.hash.length === 0 && $(window).scrollTop() == 0) {
form.find('textarea').focus(); form.find('textarea').focus();
} }
}, },

View File

@ -235,6 +235,16 @@ class Action extends HTMLOutputter // lawsuit
Event::handle('EndShowDesign', array($this)); Event::handle('EndShowDesign', array($this));
} }
Event::handle('EndShowStyles', array($this)); Event::handle('EndShowStyles', array($this));
if (common_config('custom_css', 'enabled')) {
$css = common_config('custom_css', 'css');
if (Event::handle('StartShowCustomCss', array($this, &$css))) {
if (trim($css) != '') {
$this->style($css);
}
Event::handle('EndShowCustomCss', array($this));
}
}
} }
} }
@ -467,7 +477,7 @@ class Action extends HTMLOutputter // lawsuit
_m('MENU', 'Logout'), $tooltip, false, 'nav_logout'); _m('MENU', 'Logout'), $tooltip, false, 'nav_logout');
} }
else { else {
if (!common_config('site', 'closed')) { if (!common_config('site', 'closed') && !common_config('site', 'inviteonly')) {
// TRANS: Tooltip for main menu option "Register" // TRANS: Tooltip for main menu option "Register"
$tooltip = _m('TOOLTIP', 'Create an account'); $tooltip = _m('TOOLTIP', 'Create an account');
$this->menuItem(common_local_url('register'), $this->menuItem(common_local_url('register'),

View File

@ -83,6 +83,7 @@ class Activity
const CREATOR = 'creator'; const CREATOR = 'creator';
const CONTENTNS = 'http://purl.org/rss/1.0/modules/content/'; const CONTENTNS = 'http://purl.org/rss/1.0/modules/content/';
const ENCODED = 'encoded';
public $actor; // an ActivityObject public $actor; // an ActivityObject
public $verb; // a string (the URL) public $verb; // a string (the URL)
@ -269,14 +270,21 @@ class Activity
$this->title = ActivityUtils::childContent($item, ActivityObject::TITLE, self::RSS); $this->title = ActivityUtils::childContent($item, ActivityObject::TITLE, self::RSS);
$contentEl = ActivityUtils::child($item, ActivityUtils::CONTENT, self::CONTENTNS); $contentEl = ActivityUtils::child($item, self::ENCODED, self::CONTENTNS);
if (!empty($contentEl)) { if (!empty($contentEl)) {
$this->content = htmlspecialchars_decode($contentEl->textContent, ENT_QUOTES); // <content:encoded> XML node's text content is HTML; no further processing needed.
$this->content = $contentEl->textContent;
} else { } else {
$descriptionEl = ActivityUtils::child($item, self::DESCRIPTION, self::RSS); $descriptionEl = ActivityUtils::child($item, self::DESCRIPTION, self::RSS);
if (!empty($descriptionEl)) { if (!empty($descriptionEl)) {
$this->content = htmlspecialchars_decode($descriptionEl->textContent, ENT_QUOTES); // Per spec, <description> must be plaintext.
// In practice, often there's HTML... but these days good
// feeds are using <content:encoded> which is explicitly
// real HTML.
// We'll treat this following spec, and do HTML escaping
// to convert from plaintext to HTML.
$this->content = htmlspecialchars($descriptionEl->textContent);
} }
} }

View File

@ -213,11 +213,19 @@ class ActivityUtils
// slavishly following http://atompub.org/rfc4287.html#rfc.section.4.1.3.3 // slavishly following http://atompub.org/rfc4287.html#rfc.section.4.1.3.3
if (empty($type) || $type == 'text') { if (empty($type) || $type == 'text') {
return $el->textContent; // We have plaintext saved as the XML text content.
// Since we want HTML, we need to escape any special chars.
return htmlspecialchars($el->textContent);
} else if ($type == 'html') { } else if ($type == 'html') {
// We have HTML saved as the XML text content.
// No additional processing required once we've got it.
$text = $el->textContent; $text = $el->textContent;
return htmlspecialchars_decode($text, ENT_QUOTES); return $text;
} else if ($type == 'xhtml') { } else if ($type == 'xhtml') {
// Per spec, the <content type="xhtml"> contains a single
// HTML <div> with XHTML namespace on it as a child node.
// We need to pull all of that <div>'s child nodes and
// serialize them back to an (X)HTML source fragment.
$divEl = ActivityUtils::child($el, 'div', 'http://www.w3.org/1999/xhtml'); $divEl = ActivityUtils::child($el, 'div', 'http://www.w3.org/1999/xhtml');
if (empty($divEl)) { if (empty($divEl)) {
return null; return null;

View File

@ -284,9 +284,10 @@ class AdminPanelAction extends Action
$this->clientError(_("Unable to delete design setting.")); $this->clientError(_("Unable to delete design setting."));
return null; return null;
} }
return $result;
} }
return $result; return null;
} }
function canAdmin($name) function canAdmin($name)

View File

@ -28,14 +28,78 @@
* @author Toby Inkster <mail@tobyinkster.co.uk> * @author Toby Inkster <mail@tobyinkster.co.uk>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
/* External API usage documentation. Please update when you change how the API works. */
/*! @mainpage StatusNet REST API
@section Introduction
Some explanatory text about the API would be nice.
@section API Methods
@subsection timelinesmethods_sec Timeline Methods
@li @ref publictimeline
@li @ref friendstimeline
@subsection statusmethods_sec Status Methods
@li @ref statusesupdate
@subsection usermethods_sec User Methods
@subsection directmessagemethods_sec Direct Message Methods
@subsection friendshipmethods_sec Friendship Methods
@subsection socialgraphmethods_sec Social Graph Methods
@subsection accountmethods_sec Account Methods
@subsection favoritesmethods_sec Favorites Methods
@subsection blockmethods_sec Block Methods
@subsection oauthmethods_sec OAuth Methods
@subsection helpmethods_sec Help Methods
@subsection groupmethods_sec Group Methods
@page apiroot API Root
The URLs for methods referred to in this API documentation are
relative to the StatusNet API root. The API root is determined by the
site's @b server and @b path variables, which are generally specified
in config.php. For example:
@code
$config['site']['server'] = 'example.org';
$config['site']['path'] = 'statusnet'
@endcode
The pattern for a site's API root is: @c protocol://server/path/api E.g:
@c http://example.org/statusnet/api
The @b path can be empty. In that case the API root would simply be:
@c http://example.org/api
*/
if (!defined('STATUSNET')) { if (!defined('STATUSNET')) {
exit(1); exit(1);
} }
class ApiValidationException extends Exception { }
/** /**
* Contains most of the Twitter-compatible API output functions. * Contains most of the Twitter-compatible API output functions.
* *
@ -63,9 +127,12 @@ class ApiAction extends Action
var $count = null; var $count = null;
var $max_id = null; var $max_id = null;
var $since_id = null; var $since_id = null;
var $source = null;
var $access = self::READ_ONLY; // read (default) or read-write var $access = self::READ_ONLY; // read (default) or read-write
static $reserved_sources = array('web', 'omb', 'ostatus', 'mail', 'xmpp', 'api');
/** /**
* Initialization. * Initialization.
* *
@ -89,6 +156,12 @@ class ApiAction extends Action
header('X-StatusNet-Warning: since parameter is disabled; use since_id'); header('X-StatusNet-Warning: since parameter is disabled; use since_id');
} }
$this->source = $this->trimmed('source');
if (empty($this->source) || in_array($this->source, self::$reserved_sources)) {
$this->source = 'api';
}
return true; return true;
} }
@ -200,11 +273,13 @@ class ApiAction extends Action
// Is the requesting user following this user? // Is the requesting user following this user?
$twitter_user['following'] = false; $twitter_user['following'] = false;
$twitter_user['statusnet:blocking'] = false;
$twitter_user['notifications'] = false; $twitter_user['notifications'] = false;
if (isset($this->auth_user)) { if (isset($this->auth_user)) {
$twitter_user['following'] = $this->auth_user->isSubscribed($profile); $twitter_user['following'] = $this->auth_user->isSubscribed($profile);
$twitter_user['statusnet:blocking'] = $this->auth_user->hasBlocked($profile);
// Notifications on? // Notifications on?
$sub = Subscription::pkeyGet(array('subscriber' => $sub = Subscription::pkeyGet(array('subscriber' =>
@ -224,6 +299,10 @@ class ApiAction extends Action
} }
} }
// StatusNet-specific
$twitter_user['statusnet:profile_url'] = $profile->profileurl;
return $twitter_user; return $twitter_user;
} }
@ -252,7 +331,23 @@ class ApiAction extends Action
$twitter_status['created_at'] = $this->dateTwitter($notice->created); $twitter_status['created_at'] = $this->dateTwitter($notice->created);
$twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ? $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ?
intval($notice->reply_to) : null; intval($notice->reply_to) : null;
$twitter_status['source'] = $this->sourceLink($notice->source);
$source = null;
$ns = $notice->getSource();
if ($ns) {
if (!empty($ns->name) && !empty($ns->url)) {
$source = '<a href="'
. htmlspecialchars($ns->url)
. '" rel="nofollow">'
. htmlspecialchars($ns->name)
. '</a>';
} else {
$source = $ns->code;
}
}
$twitter_status['source'] = $source;
$twitter_status['id'] = intval($notice->id); $twitter_status['id'] = intval($notice->id);
$replier_profile = null; $replier_profile = null;
@ -309,25 +404,41 @@ class ApiAction extends Action
$twitter_status['user'] = $twitter_user; $twitter_status['user'] = $twitter_user;
} }
// StatusNet-specific
$twitter_status['statusnet:html'] = $notice->rendered;
return $twitter_status; return $twitter_status;
} }
function twitterGroupArray($group) function twitterGroupArray($group)
{ {
$twitter_group=array(); $twitter_group = array();
$twitter_group['id']=$group->id;
$twitter_group['url']=$group->permalink(); $twitter_group['id'] = $group->id;
$twitter_group['nickname']=$group->nickname; $twitter_group['url'] = $group->permalink();
$twitter_group['fullname']=$group->fullname; $twitter_group['nickname'] = $group->nickname;
$twitter_group['original_logo']=$group->original_logo; $twitter_group['fullname'] = $group->fullname;
$twitter_group['homepage_logo']=$group->homepage_logo;
$twitter_group['stream_logo']=$group->stream_logo; if (isset($this->auth_user)) {
$twitter_group['mini_logo']=$group->mini_logo; $twitter_group['member'] = $this->auth_user->isMember($group);
$twitter_group['homepage']=$group->homepage; $twitter_group['blocked'] = Group_block::isBlocked(
$twitter_group['description']=$group->description; $group,
$twitter_group['location']=$group->location; $this->auth_user->getProfile()
$twitter_group['created']=$this->dateTwitter($group->created); );
$twitter_group['modified']=$this->dateTwitter($group->modified); }
$twitter_group['member_count'] = $group->getMemberCount();
$twitter_group['original_logo'] = $group->original_logo;
$twitter_group['homepage_logo'] = $group->homepage_logo;
$twitter_group['stream_logo'] = $group->stream_logo;
$twitter_group['mini_logo'] = $group->mini_logo;
$twitter_group['homepage'] = $group->homepage;
$twitter_group['description'] = $group->description;
$twitter_group['location'] = $group->location;
$twitter_group['created'] = $this->dateTwitter($group->created);
$twitter_group['modified'] = $this->dateTwitter($group->modified);
return $twitter_group; return $twitter_group;
} }
@ -476,9 +587,13 @@ class ApiAction extends Action
} }
} }
function showTwitterXmlStatus($twitter_status, $tag='status') function showTwitterXmlStatus($twitter_status, $tag='status', $namespaces=false)
{ {
$this->elementStart($tag); $attrs = array();
if ($namespaces) {
$attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
}
$this->elementStart($tag, $attrs);
foreach($twitter_status as $element => $value) { foreach($twitter_status as $element => $value) {
switch ($element) { switch ($element) {
case 'user': case 'user':
@ -512,9 +627,13 @@ class ApiAction extends Action
$this->elementEnd('group'); $this->elementEnd('group');
} }
function showTwitterXmlUser($twitter_user, $role='user') function showTwitterXmlUser($twitter_user, $role='user', $namespaces=false)
{ {
$this->elementStart($role); $attrs = array();
if ($namespaces) {
$attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
}
$this->elementStart($role, $attrs);
foreach($twitter_user as $element => $value) { foreach($twitter_user as $element => $value) {
if ($element == 'status') { if ($element == 'status') {
$this->showTwitterXmlStatus($twitter_user['status']); $this->showTwitterXmlStatus($twitter_user['status']);
@ -596,7 +715,7 @@ class ApiAction extends Action
{ {
$this->initDocument('xml'); $this->initDocument('xml');
$twitter_status = $this->twitterStatusArray($notice); $twitter_status = $this->twitterStatusArray($notice);
$this->showTwitterXmlStatus($twitter_status); $this->showTwitterXmlStatus($twitter_status, 'status', true);
$this->endDocument('xml'); $this->endDocument('xml');
} }
@ -612,7 +731,8 @@ class ApiAction extends Action
{ {
$this->initDocument('xml'); $this->initDocument('xml');
$this->elementStart('statuses', array('type' => 'array')); $this->elementStart('statuses', array('type' => 'array',
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
if (is_array($notice)) { if (is_array($notice)) {
foreach ($notice as $n) { foreach ($notice as $n) {
@ -779,9 +899,13 @@ class ApiAction extends Action
$this->elementEnd('entry'); $this->elementEnd('entry');
} }
function showXmlDirectMessage($dm) function showXmlDirectMessage($dm, $namespaces=false)
{ {
$this->elementStart('direct_message'); $attrs = array();
if ($namespaces) {
$attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
}
$this->elementStart('direct_message', $attrs);
foreach($dm as $element => $value) { foreach($dm as $element => $value) {
switch ($element) { switch ($element) {
case 'sender': case 'sender':
@ -858,7 +982,7 @@ class ApiAction extends Action
{ {
$this->initDocument('xml'); $this->initDocument('xml');
$dmsg = $this->directMessageArray($message); $dmsg = $this->directMessageArray($message);
$this->showXmlDirectMessage($dmsg); $this->showXmlDirectMessage($dmsg, true);
$this->endDocument('xml'); $this->endDocument('xml');
} }
@ -975,7 +1099,8 @@ class ApiAction extends Action
{ {
$this->initDocument('xml'); $this->initDocument('xml');
$this->elementStart('users', array('type' => 'array')); $this->elementStart('users', array('type' => 'array',
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
if (is_array($user)) { if (is_array($user)) {
foreach ($user as $u) { foreach ($user as $u) {
@ -1293,43 +1418,6 @@ class ApiAction extends Action
} }
} }
function sourceLink($source)
{
$source_name = _($source);
switch ($source) {
case 'web':
case 'xmpp':
case 'mail':
case 'omb':
case 'api':
break;
default:
$name = null;
$url = null;
$ns = Notice_source::staticGet($source);
if ($ns) {
$name = $ns->name;
$url = $ns->url;
} else {
$app = Oauth_application::staticGet('name', $source);
if ($app) {
$name = $app->name;
$url = $app->source_url;
}
}
if (!empty($name) && !empty($url)) {
$source_name = '<a href="' . $url . '">' . $name . '</a>';
}
break;
}
return $source_name;
}
/** /**
* Returns query argument or default value if not found. Certain * Returns query argument or default value if not found. Certain
* parameters used throughout the API are lightly scrubbed and * parameters used throughout the API are lightly scrubbed and

View File

@ -30,10 +30,29 @@
* @author Sarven Capadisli <csarven@status.net> * @author Sarven Capadisli <csarven@status.net>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc. * @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
/* External API usage documentation. Please update when you change how this method works. */
/*! @page authentication Authentication
StatusNet supports HTTP Basic Authentication and OAuth for API calls.
@warning Currently, users who have created accounts without setting a
password via OpenID, Facebook Connect, etc., cannot use the API until
they set a password with their account settings panel.
@section HTTP Basic Auth
@section OAuth
*/
if (!defined('STATUSNET')) { if (!defined('STATUSNET')) {
exit(1); exit(1);
} }
@ -54,7 +73,6 @@ class ApiAuthAction extends ApiAction
{ {
var $auth_user_nickname = null; var $auth_user_nickname = null;
var $auth_user_password = null; var $auth_user_password = null;
var $oauth_source = null;
/** /**
* Take arguments for running, looks for an OAuth request, * Take arguments for running, looks for an OAuth request,
@ -163,7 +181,7 @@ class ApiAuthAction extends ApiAction
// set the source attr // set the source attr
$this->oauth_source = $app->name; $this->source = $app->name;
$appUser = Oauth_application_user::staticGet('token', $access_token); $appUser = Oauth_application_user::staticGet('token', $access_token);

View File

@ -32,6 +32,7 @@
* @author Sarven Capadisli <csarven@status.net> * @author Sarven Capadisli <csarven@status.net>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -106,4 +107,4 @@ class ApiBareAuthAction extends ApiAuthAction
return false; return false;
} }
} }

View File

@ -31,6 +31,7 @@
* @author Sarven Capadisli <csarven@status.net> * @author Sarven Capadisli <csarven@status.net>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc. * @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -50,12 +50,13 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed
* Constructor * Constructor
* *
* @param Group $group the group for the feed * @param Group $group the group for the feed
* @param User $cur the current authenticated user, if any
* @param boolean $indent flag to turn indenting on or off * @param boolean $indent flag to turn indenting on or off
* *
* @return void * @return void
*/ */
function __construct($group, $indent = true) { function __construct($group, $cur = null, $indent = true) {
parent::__construct($indent); parent::__construct($cur, $indent);
$this->group = $group; $this->group = $group;
// TRANS: Title in atom group notice feed. %s is a group name. // TRANS: Title in atom group notice feed. %s is a group name.
@ -95,4 +96,23 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed
return $this->group; return $this->group;
} }
function initFeed()
{
parent::initFeed();
$attrs = array();
if (!empty($this->cur)) {
$attrs['member'] = $this->cur->isMember($this->group)
? 'true' : 'false';
$attrs['blocked'] = Group_block::isBlocked(
$this->group,
$this->cur->getProfile()
) ? 'true' : 'false';
}
$attrs['member_count'] = $this->group->getMemberCount();
$this->element('statusnet:group_info', $attrs, null);
}
} }

View File

@ -44,9 +44,22 @@ if (!defined('STATUSNET'))
*/ */
class AtomNoticeFeed extends Atom10Feed class AtomNoticeFeed extends Atom10Feed
{ {
function __construct($indent = true) { var $cur;
/**
* Constructor - adds a bunch of XML namespaces we need in our
* notice-specific Atom feeds, and allows setting the current
* authenticated user (useful for API methods).
*
* @param User $cur the current authenticated user (optional)
* @param boolean $indent Whether to indent XML output
*
*/
function __construct($cur = null, $indent = true) {
parent::__construct($indent); parent::__construct($indent);
$this->cur = $cur;
// Feeds containing notice info use these namespaces // Feeds containing notice info use these namespaces
$this->addNamespace( $this->addNamespace(
@ -79,6 +92,11 @@ class AtomNoticeFeed extends Atom10Feed
'ostatus', 'ostatus',
'http://ostatus.org/schema/1.0' 'http://ostatus.org/schema/1.0'
); );
$this->addNamespace(
'statusnet',
'http://status.net/schema/api/1/'
);
} }
/** /**
@ -110,7 +128,9 @@ class AtomNoticeFeed extends Atom10Feed
$source = $this->showSource(); $source = $this->showSource();
$author = $this->showAuthor(); $author = $this->showAuthor();
$this->addEntryRaw($notice->asAtomEntry(false, $source, $author)); $cur = empty($this->cur) ? common_current_user() : $this->cur;
$this->addEntryRaw($notice->asAtomEntry(false, $source, $author, $cur));
} }
function showSource() function showSource()

View File

@ -50,13 +50,14 @@ class AtomUserNoticeFeed extends AtomNoticeFeed
* Constructor * Constructor
* *
* @param User $user the user for the feed * @param User $user the user for the feed
* @param User $cur the current authenticated user, if any
* @param boolean $indent flag to turn indenting on or off * @param boolean $indent flag to turn indenting on or off
* *
* @return void * @return void
*/ */
function __construct($user, $indent = true) { function __construct($user, $cur = null, $indent = true) {
parent::__construct($indent); parent::__construct($cur, $indent);
$this->user = $user; $this->user = $user;
if (!empty($user)) { if (!empty($user)) {
$profile = $user->getProfile(); $profile = $user->getProfile();

View File

@ -22,6 +22,7 @@
* @category Plugin * @category Plugin
* @package StatusNet * @package StatusNet
* @author Craig Andrews <candrews@integralblue.com> * @author Craig Andrews <candrews@integralblue.com>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -22,6 +22,7 @@
* @category Plugin * @category Plugin
* @package StatusNet * @package StatusNet
* @author Craig Andrews <candrews@integralblue.com> * @author Craig Andrews <candrews@integralblue.com>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -76,8 +76,8 @@ class AvatarLink
$alink = new AvatarLink(); $alink = new AvatarLink();
$alink->url = $filename; $alink->url = $filename;
$alink->height = $size; $alink->height = $size;
$alink->width = $size;
if (!empty($filename)) { if (!empty($filename)) {
$alink->width = $size;
$alink->type = self::mediatype($filename); $alink->type = self::mediatype($filename);
} else { } else {
$alink->url = User_group::defaultLogo($size); $alink->url = User_group::defaultLogo($size);

View File

@ -22,10 +22,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
//exit with 200 response, if this is checking fancy from the installer //exit with 200 response, if this is checking fancy from the installer
if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; } if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; }
define('STATUSNET_VERSION', '0.9.1'); define('STATUSNET_VERSION', '0.9.2');
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
define('STATUSNET_CODENAME', 'Everybody Hurts'); define('STATUSNET_CODENAME', 'King of Birds');
define('AVATAR_PROFILE_SIZE', 96); define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48); define('AVATAR_STREAM_SIZE', 48);
@ -132,6 +132,12 @@ require_once INSTALLDIR.'/lib/serverexception.php';
//set PEAR error handling to use regular PHP exceptions //set PEAR error handling to use regular PHP exceptions
function PEAR_ErrorToPEAR_Exception($err) function PEAR_ErrorToPEAR_Exception($err)
{ {
//DB_DataObject throws error when an empty set would be returned
//That behavior is weird, and not how the rest of StatusNet works.
//So just ignore those errors.
if ($err->getCode() == DB_DATAOBJECT_ERROR_NODATA) {
return;
}
if ($err->getCode()) { if ($err->getCode()) {
throw new PEAR_Exception($err->getMessage(), $err->getCode()); throw new PEAR_Exception($err->getMessage(), $err->getCode());
} }

View File

@ -105,7 +105,9 @@ class ConnectSettingsNav extends Widget
# action => array('prompt', 'title') # action => array('prompt', 'title')
$menu = array(); $menu = array();
if (common_config('xmpp', 'enabled')) { $transports = array();
Event::handle('GetImTransports', array(&$transports));
if ($transports) {
$menu['imsettings'] = $menu['imsettings'] =
array(_('IM'), array(_('IM'),
_('Updates by instant messenger (IM)')); _('Updates by instant messenger (IM)'));

View File

@ -135,9 +135,7 @@ class DBQueueManager extends QueueManager
if (empty($qi->claimed)) { if (empty($qi->claimed)) {
$this->_log(LOG_WARNING, "[$queue:item $qi->id] Ignoring failure for unclaimed queue item"); $this->_log(LOG_WARNING, "[$queue:item $qi->id] Ignoring failure for unclaimed queue item");
} else { } else {
$orig = clone($qi); $qi->releaseClaim();
$qi->claimed = null;
$qi->update($orig);
} }
$this->stats('error', $queue); $this->stats('error', $queue);

Some files were not shown because too many files have changed in this diff Show More