Merge branch 'testing' of gitorious.org:statusnet/mainline into 0.9.x

Conflicts:
	plugins/OpenID/openidserver.php (cleaned up mismatched comment)
This commit is contained in:
Brion Vibber 2010-06-26 10:17:36 -04:00
commit 696e4ba393
20 changed files with 452 additions and 58 deletions

View File

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

View File

@ -22,7 +22,7 @@
* @category Search
* @package StatusNet
* @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
* @link http://status.net/
*/
@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/apiprivateauth.php';
/**
* Action for outputting search results in Twitter compatible Atom
* 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
* @link http://status.net/
*
* @see ApiAction
* @see ApiPrivateAuthAction
*/
class TwitapisearchatomAction extends ApiAction
class ApiSearchAtomAction extends ApiPrivateAuthAction
{
var $cnt;
@ -96,8 +98,11 @@ class TwitapisearchatomAction extends ApiAction
function prepare($args)
{
common_debug("in apisearchatom prepare()");
parent::prepare($args);
$this->query = $this->trimmed('q');
$this->lang = $this->trimmed('lang');
$this->rpp = $this->trimmed('rpp');
@ -138,6 +143,7 @@ class TwitapisearchatomAction extends ApiAction
function handle($args)
{
parent::handle($args);
common_debug("In apisearchatom handle()");
$this->showAtom();
}

View File

@ -22,7 +22,7 @@
* @category Search
* @package StatusNet
* @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
* @link http://status.net/
*/
@ -31,6 +31,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/apiprivateauth.php';
require_once INSTALLDIR.'/lib/jsonsearchresultslist.php';
/**
@ -44,7 +45,7 @@ require_once INSTALLDIR.'/lib/jsonsearchresultslist.php';
* @see ApiAction
*/
class TwitapisearchjsonAction extends ApiAction
class ApiSearchJSONAction extends ApiPrivateAuthAction
{
var $query;
var $lang;
@ -64,6 +65,8 @@ class TwitapisearchjsonAction extends ApiAction
function prepare($args)
{
common_debug("apisearchjson prepare()");
parent::prepare($args);
$this->query = $this->trimmed('q');

View File

@ -206,7 +206,8 @@ class ApiSubscriptionsAction extends ApiBareAuthAction
{
switch ($this->format) {
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) {
$this->showProfile(
$profile,

View File

@ -22,7 +22,7 @@
* @category Search
* @package StatusNet
* @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
* @link http://status.net/
*/
@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/apiprivateauth.php';
/**
* Returns the top ten queries that are currently trending
*
@ -43,7 +45,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @see ApiAction
*/
class TwitapitrendsAction extends ApiAction
class ApiTrendsAction extends ApiPrivateAuthAction
{
var $callback;
@ -82,7 +84,7 @@ class TwitapitrendsAction extends ApiAction
*/
function showTrends()
{
$this->serverError(_('API method under construction.'), $code = 501);
$this->serverError(_('API method under construction.'), 501);
}
}

View File

@ -430,14 +430,6 @@ class ShowgroupAction extends GroupDesignAction
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',
'class' => 'section'));
@ -451,7 +443,7 @@ class ShowgroupAction extends GroupDesignAction
$this->elementStart('dl', 'entity_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('div');

View File

@ -1192,7 +1192,7 @@ class Notice extends Memcached_DataObject
'xmlns:media' => 'http://purl.org/syndication/atommedia',
'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
'xmlns:ostatus' => 'http://ostatus.org/schema/1.0',
'xmlns:statusnet' => 'http://status.net/ont/');
'xmlns:statusnet' => 'http://status.net/schema/api/1/');
} else {
$attrs = array();
}
@ -1227,7 +1227,7 @@ class Notice extends Memcached_DataObject
$xs->element('title', null, common_xml_safe_str($this->content));
if ($author) {
$xs->raw($profile->asAtomAuthor());
$xs->raw($profile->asAtomAuthor($cur));
$xs->raw($profile->asActivityActor());
}
@ -1240,9 +1240,25 @@ class Notice extends Memcached_DataObject
$xs->element('published', 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' => $this->source, // the client name (source attribution)
'local_id' => $this->id, // local notice ID (useful to clients for ordering)
'source' => $source, // the client name (source attribution)
);
$ns = $this->getSource();
@ -1254,8 +1270,8 @@ class Notice extends Memcached_DataObject
if (!empty($cur)) {
$noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false";
$profile = $cur->getProfile();
$noticeInfoAttr['repeated'] = ($profile->hasRepeated($this->id)) ? "true" : "false";
$profile = $cur->getProfile();
$noticeInfoAttr['repeated'] = ($profile->hasRepeated($this->id)) ? "true" : "false";
}
if (!empty($this->repeat_of)) {

View File

@ -849,15 +849,23 @@ class Profile extends Memcached_DataObject
*
* Assumes that Atom has been previously set up as the base namespace.
*
* @param Profile $cur the current authenticated user
*
* @return string
*/
function asAtomAuthor()
function asAtomAuthor($cur = null)
{
$xs = new XMLStringer(true);
$xs->elementStart('author');
$xs->element('name', null, $this->nickname);
$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');
return $xs->getString();

View File

@ -144,6 +144,35 @@ class Status_network extends Safe_DataObject
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()
{
$this->decache(); # while we still have the values!

View File

@ -154,6 +154,21 @@ class User_group extends Memcached_DataObject
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)
{
$qry =

View File

@ -271,11 +271,13 @@ class ApiAction extends Action
// Is the requesting user following this user?
$twitter_user['following'] = false;
$twitter_user['statusnet:blocking'] = false;
$twitter_user['notifications'] = false;
if (isset($this->auth_user)) {
$twitter_user['following'] = $this->auth_user->isSubscribed($profile);
$twitter_user['statusnet:blocking'] = $this->auth_user->hasBlocked($profile);
// Notifications on?
$sub = Subscription::pkeyGet(array('subscriber' =>
@ -409,20 +411,32 @@ class ApiAction extends Action
function twitterGroupArray($group)
{
$twitter_group=array();
$twitter_group['id']=$group->id;
$twitter_group['url']=$group->permalink();
$twitter_group['nickname']=$group->nickname;
$twitter_group['fullname']=$group->fullname;
$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);
$twitter_group = array();
$twitter_group['id'] = $group->id;
$twitter_group['url'] = $group->permalink();
$twitter_group['nickname'] = $group->nickname;
$twitter_group['fullname'] = $group->fullname;
if (isset($this->auth_user)) {
$twitter_group['member'] = $this->auth_user->isMember($group);
$twitter_group['blocked'] = Group_block::isBlocked(
$group,
$this->auth_user->getProfile()
);
}
$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;
}

View File

@ -96,4 +96,23 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed
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

@ -95,7 +95,7 @@ class AtomNoticeFeed extends Atom10Feed
$this->addNamespace(
'statusnet',
'http://status.net/ont/'
'http://status.net/schema/api/1/'
);
}

View File

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

View File

@ -447,7 +447,7 @@ abstract class Installer
case 'mysqli':
$res = $conn->query($stmt);
if ($res === false) {
$error = $conn->error();
$error = $conn->error;
}
break;
case 'pgsql':

View File

@ -263,7 +263,7 @@ class Router
$m->connect('tag', array('action' => 'publictagcloud'));
$m->connect('tag/:tag/rss',
array('action' => 'tagrss'),
array('tag' => '[a-zA-Z0-9]+'));
array('tag' => '[\pL\pN_\-\.]{1,64}'));
$m->connect('tag/:tag',
array('action' => 'tag'),
array('tag' => '[\pL\pN_\-\.]{1,64}'));
@ -667,9 +667,9 @@ class Router
);
// search
$m->connect('api/search.atom', array('action' => 'twitapisearchatom'));
$m->connect('api/search.json', array('action' => 'twitapisearchjson'));
$m->connect('api/trends.json', array('action' => 'twitapitrends'));
$m->connect('api/search.atom', array('action' => 'ApiSearchAtom'));
$m->connect('api/search.json', array('action' => 'ApiSearchJSON'));
$m->connect('api/trends.json', array('action' => 'ApiTrends'));
$m->connect('api/oauth/request_token',
array('action' => 'apioauthrequesttoken'));
@ -749,12 +749,12 @@ class Router
$m->connect('tag/:tag/rss',
array('action' => 'userrss',
'nickname' => $nickname),
array('tag' => '[a-zA-Z0-9]+'));
array('tag' => '[\pL\pN_\-\.]{1,64}'));
$m->connect('tag/:tag',
array('action' => 'showstream',
'nickname' => $nickname),
array('tag' => '[a-zA-Z0-9]+'));
array('tag' => '[\pL\pN_\-\.]{1,64}'));
$m->connect('rsd.xml',
array('action' => 'rsd',
@ -815,12 +815,12 @@ class Router
$m->connect(':nickname/tag/:tag/rss',
array('action' => 'userrss'),
array('nickname' => '[a-zA-Z0-9]{1,64}'),
array('tag' => '[a-zA-Z0-9]+'));
array('tag' => '[\pL\pN_\-\.]{1,64}'));
$m->connect(':nickname/tag/:tag',
array('action' => 'showstream'),
array('nickname' => '[a-zA-Z0-9]{1,64}'),
array('tag' => '[a-zA-Z0-9]+'));
array('tag' => '[\pL\pN_\-\.]{1,64}'));
$m->connect(':nickname/rsd.xml',
array('action' => 'rsd'),

View File

@ -71,7 +71,11 @@ class OpenidserverAction extends Action
//cannot prompt the user to login in immediate mode, so answer false
$response = $this->generateDenyResponse($request);
}else{
/* Go log in, and then come back. */
// Go log in, and then come back.
//
// Note: 303 redirect rather than 307 to avoid
// prompting user for form resubmission if we
// were POSTed here.
common_set_returnto($_SERVER['REQUEST_URI']);
common_redirect(common_local_url('login'), 303);
return;
@ -92,7 +96,12 @@ class OpenidserverAction extends Action
$this->oserver->encodeResponse($denyResponse); //sign the response
$_SESSION['openid_allow_url'] = $allowResponse->encodeToUrl();
$_SESSION['openid_deny_url'] = $denyResponse->encodeToUrl();
//ask the user to trust this trust root
// Ask the user to trust this trust root...
//
// Note: 303 redirect rather than 307 to avoid
// prompting user for form resubmission if we
// were POSTed here.
common_redirect(common_local_url('openidtrust'), 303);
return;
}

View File

@ -62,12 +62,32 @@ class RecaptchaPlugin extends Plugin
{
$action->elementStart('li');
$action->raw('<label for="recaptcha">Captcha</label>');
if($this->checkssl() === true) {
$action->raw(recaptcha_get_html($this->public_key), null, true);
} else {
$action->raw(recaptcha_get_html($this->public_key));
}
// AJAX API will fill this div out.
// We're calling that instead of the regular one so we stay compatible
// with application/xml+xhtml output as for mobile.
$action->element('div', array('id' => 'recaptcha'));
$action->elementEnd('li');
$action->recaptchaPluginNeedsOutput = true;
return true;
}
function onEndShowScripts($action)
{
if (isset($action->recaptchaPluginNeedsOutput) && $action->recaptchaPluginNeedsOutput) {
// Load the AJAX API
if ($this->checkssl()) {
$url = "https://api-secure.recaptcha.net/js/recaptcha_ajax.js";
} else {
$url = "http://api.recaptcha.net/js/recaptcha_ajax.js";
}
$action->script($url);
// And when we're ready, fill out the captcha!
$key = json_encode($this->public_key);
$action->inlinescript("\$(function(){Recaptcha.create($key, 'recaptcha');});");
}
return true;
}

View File

@ -71,6 +71,7 @@ class SitemapPlugin extends Plugin
case 'SitemapindexAction':
case 'NoticesitemapAction':
case 'UsersitemapAction':
case 'SitemapadminpanelAction':
require_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
return false;
case 'SitemapAction':
@ -124,6 +125,49 @@ class SitemapPlugin extends Plugin
'month' => '[01][0-9]',
'day' => '[0123][0-9]',
'index' => '[1-9][0-9]*'));
$m->connect('admin/sitemap',
array('action' => 'sitemapadminpanel'));
return true;
}
/**
* Meta tags for "claiming" a site
*
* We add extra meta tags that search engines like Yahoo!, Google, and Bing
* require to let you claim your site.
*
* @param Action $action Action being executed
*
* @return boolean hook value.
*/
function onStartShowHeadElements($action)
{
$actionName = $action->trimmed('action');
$singleUser = common_config('singleuser', 'enabled');
// Different "top" pages if it's single user or not
if (($singleUser && $actionName == 'showstream') ||
(!$singleUser && $actionName == 'public')) {
$keys = array('googlekey' => 'google-site-verification',
'yahookey' => 'y_key',
'bingkey' => 'msvalidate.01'); // XXX: is this the same for all sites?
foreach ($keys as $config => $metaname) {
$content = common_config('sitemap', $config);
if (!empty($content)) {
$action->element('meta', array('name' => $metaname,
'content' => $content));
}
}
}
return true;
}
@ -160,4 +204,15 @@ class SitemapPlugin extends Plugin
return true;
}
function onEndAdminPanelNav($menu) {
if (AdminPanelAction::canAdmin('sitemap')) {
// TRANS: Menu item title/tooltip
$menu_title = _('Sitemap configuration');
// TRANS: Menu item for site administration
$menu->out->menuItem(common_local_url('sitemapadminpanel'), _('Sitemap'),
$menu_title, $action_name == 'sitemapadminpanel', 'nav_sitemap_admin_panel');
}
return true;
}
}

View File

@ -0,0 +1,205 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Sitemap administration panel
*
* 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 Sitemap
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Administer sitemap settings
*
* @category Sitemap
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class SitemapadminpanelAction extends AdminPanelAction
{
/**
* Returns the page title
*
* @return string page title
*/
function title()
{
return _('Sitemap');
}
/**
* Instructions for using this form.
*
* @return string instructions
*/
function getInstructions()
{
return _('Sitemap settings for this StatusNet site');
}
/**
* Show the site admin panel form
*
* @return void
*/
function showForm()
{
$form = new SitemapAdminPanelForm($this);
$form->show();
return;
}
/**
* Save settings from the form
*
* @return void
*/
function saveSettings()
{
static $settings = array('sitemap' => array('googlekey', 'yahookey', 'bingkey'));
$values = array();
foreach ($settings as $section => $parts) {
foreach ($parts as $setting) {
$values[$section][$setting] = $this->trimmed($setting);
}
}
// This throws an exception on validation errors
$this->validate($values);
// assert(all values are valid);
$config = new Config();
$config->query('BEGIN');
foreach ($settings as $section => $parts) {
foreach ($parts as $setting) {
Config::save($section, $setting, $values[$section][$setting]);
}
}
$config->query('COMMIT');
return;
}
function validate(&$values)
{
}
}
/**
* Form for the sitemap admin panel
*/
class SitemapAdminPanelForm extends AdminForm
{
/**
* ID of the form
*
* @return int ID of the form
*/
function id()
{
return 'form_sitemap_admin_panel';
}
/**
* class of the form
*
* @return string class of the form
*/
function formClass()
{
return 'form_sitemap';
}
/**
* Action of the form
*
* @return string URL of the action
*/
function action()
{
return common_local_url('sitemapadminpanel');
}
/**
* Data elements of the form
*
* @return void
*/
function formData()
{
$this->out->elementStart('fieldset', array('id' => 'sitemap_admin'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$this->input('googlekey',
_('Google key'),
_('Google Webmaster Tools verification key'),
'sitemap');
$this->unli();
$this->li();
$this->input('yahookey',
_('Yahoo key'),
_('Yahoo! Site Explorer verification key'),
'sitemap');
$this->unli();
$this->li();
$this->input('bingkey',
_('Bing key'),
_('Bing Webmaster Tools verification key'),
'sitemap');
$this->unli();
$this->out->elementEnd('ul');
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
$this->out->submit('submit', _('Save'), 'submit', null, _('Save sitemap settings'));
}
}