forked from GNUsocial/gnu-social
Merge branch 'testing' into 0.9.x
Conflicts: actions/apioauthauthorize.php
This commit is contained in:
commit
779204b194
15
EVENTS.txt
15
EVENTS.txt
@ -699,3 +699,18 @@ StartShowContentLicense: Showing the default license for content
|
|||||||
|
|
||||||
EndShowContentLicense: Showing the default license for content
|
EndShowContentLicense: Showing the default license for content
|
||||||
- $action: the current action
|
- $action: the current action
|
||||||
|
|
||||||
|
StartUserRegister: When a new user is being registered
|
||||||
|
- &$profile: new profile data (no ID)
|
||||||
|
- &$user: new user account (no ID or URI)
|
||||||
|
|
||||||
|
EndUserRegister: When a new user has been registered
|
||||||
|
- &$profile: new profile data
|
||||||
|
- &$user: new user account
|
||||||
|
|
||||||
|
StartRobotsTxt: Before outputting the robots.txt page
|
||||||
|
- &$action: RobotstxtAction being shown
|
||||||
|
|
||||||
|
EndRobotsTxt: After the default robots.txt page (good place for customization)
|
||||||
|
- &$action: RobotstxtAction being shown
|
||||||
|
|
||||||
|
51
README
51
README
@ -2,8 +2,8 @@
|
|||||||
README
|
README
|
||||||
------
|
------
|
||||||
|
|
||||||
StatusNet 0.9.0 ("Stand") Beta 3
|
StatusNet 0.9.0 ("Stand") Beta 4
|
||||||
20 Jan 2010
|
27 Jan 2010
|
||||||
|
|
||||||
This is the README file for StatusNet (formerly Laconica), the Open
|
This is the README file for StatusNet (formerly Laconica), the Open
|
||||||
Source microblogging platform. It includes installation instructions,
|
Source microblogging platform. It includes installation instructions,
|
||||||
@ -597,26 +597,19 @@ server is probably a good idea for high-volume sites.
|
|||||||
needs as a parameter the install path; if you run it from the
|
needs as a parameter the install path; if you run it from the
|
||||||
StatusNet dir, "." should suffice.
|
StatusNet dir, "." should suffice.
|
||||||
|
|
||||||
This will run eight (for now) queue handlers:
|
This will run the queue handlers:
|
||||||
|
|
||||||
|
* queuedaemon.php - polls for queued items for inbox processing and
|
||||||
|
pushing out to OMB, SMS, XMPP, etc.
|
||||||
* xmppdaemon.php - listens for new XMPP messages from users and stores
|
* xmppdaemon.php - listens for new XMPP messages from users and stores
|
||||||
them as notices in the database.
|
them as notices in the database; also pulls queued XMPP output from
|
||||||
* jabberqueuehandler.php - sends queued notices in the database to
|
queuedaemon.php to push out to clients.
|
||||||
registered users who should receive them.
|
|
||||||
* publicqueuehandler.php - sends queued notices in the database to
|
|
||||||
public feed listeners.
|
|
||||||
* ombqueuehandler.php - sends queued notices to OpenMicroBlogging
|
|
||||||
recipients on foreign servers.
|
|
||||||
* smsqueuehandler.php - sends queued notices to SMS-over-email addresses
|
|
||||||
of registered users.
|
|
||||||
* xmppconfirmhandler.php - sends confirmation messages to registered
|
|
||||||
users.
|
|
||||||
|
|
||||||
Note that these queue daemons are pretty raw, and need your care. In
|
These two daemons will automatically restart in most cases of failure
|
||||||
particular, they leak memory, and you may want to restart them on a
|
including memory leaks (if a memory_limit is set), but may still die
|
||||||
regular (daily or so) basis with a cron job. Also, if they lose
|
or behave oddly if they lose connections to the XMPP or queue servers.
|
||||||
the connection to the XMPP server for too long, they'll simply die. It
|
|
||||||
may be a good idea to use a daemon-monitoring service, like 'monit',
|
It may be a good idea to use a daemon-monitoring service, like 'monit',
|
||||||
to check their status and keep them running.
|
to check their status and keep them running.
|
||||||
|
|
||||||
All the daemons write their process IDs (pids) to /var/run/ by
|
All the daemons write their process IDs (pids) to /var/run/ by
|
||||||
@ -626,7 +619,7 @@ daemons.
|
|||||||
Since version 0.8.0, it's now possible to use a STOMP server instead of
|
Since version 0.8.0, it's now possible to use a STOMP server instead of
|
||||||
our kind of hacky home-grown DB-based queue solution. See the "queues"
|
our kind of hacky home-grown DB-based queue solution. See the "queues"
|
||||||
config section below for how to configure to use STOMP. As of this
|
config section below for how to configure to use STOMP. As of this
|
||||||
writing, the software has been tested with ActiveMQ (
|
writing, the software has been tested with ActiveMQ.
|
||||||
|
|
||||||
Sitemaps
|
Sitemaps
|
||||||
--------
|
--------
|
||||||
@ -712,10 +705,12 @@ subdirectory to add a new language to your system. You'll need to
|
|||||||
compile the ".po" files into ".mo" files, however.
|
compile the ".po" files into ".mo" files, however.
|
||||||
|
|
||||||
Contributions of translation information to StatusNet are very easy:
|
Contributions of translation information to StatusNet are very easy:
|
||||||
you can use the Web interface at http://status.net/pootle/ to add one
|
you can use the Web interface at TranslateWiki.net to add one
|
||||||
or a few or lots of new translations -- or even new languages. You can
|
or a few or lots of new translations -- or even new languages. You can
|
||||||
also download more up-to-date .po files there, if you so desire.
|
also download more up-to-date .po files there, if you so desire.
|
||||||
|
|
||||||
|
For info on helping with translations, see http://status.net/wiki/Translations
|
||||||
|
|
||||||
Backups
|
Backups
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@ -1501,6 +1496,20 @@ interface. It also makes the user's profile the root URL.
|
|||||||
enabled: Whether to run in "single user mode". Default false.
|
enabled: Whether to run in "single user mode". Default false.
|
||||||
nickname: nickname of the single user.
|
nickname: nickname of the single user.
|
||||||
|
|
||||||
|
robotstxt
|
||||||
|
---------
|
||||||
|
|
||||||
|
We put out a default robots.txt file to guide the processing of
|
||||||
|
Web crawlers. See http://www.robotstxt.org/ for more information
|
||||||
|
on the format of this file.
|
||||||
|
|
||||||
|
crawldelay: if non-empty, this value is provided as the Crawl-Delay:
|
||||||
|
for the robots.txt file. see http://ur1.ca/l5a0
|
||||||
|
for more information. Default is zero, no explicit delay.
|
||||||
|
disallow: Array of (virtual) directories to disallow. Default is 'main',
|
||||||
|
'search', 'message', 'settings', 'admin'. Ignored when site
|
||||||
|
is private, in which case the entire site ('/') is disallowed.
|
||||||
|
|
||||||
Plugins
|
Plugins
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
@ -303,8 +303,9 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
|
|||||||
$access = ($this->app->access_type & Oauth_application::$writeAccess) ?
|
$access = ($this->app->access_type & Oauth_application::$writeAccess) ?
|
||||||
'access and update' : 'access';
|
'access and update' : 'access';
|
||||||
|
|
||||||
$msg = _("The application <strong>%1$s</strong> by <strong>%2$s</strong> would like " .
|
$msg = _('The application <strong>%1$s</strong> by ' .
|
||||||
"the ability to <strong>%3$s</strong> your account data.");
|
'<strong>%2$s</strong> would like the ability ' .
|
||||||
|
'to <strong>%3$s</strong> your account data.');
|
||||||
|
|
||||||
$this->raw(sprintf($msg,
|
$this->raw(sprintf($msg,
|
||||||
$this->app->name,
|
$this->app->name,
|
||||||
|
@ -42,6 +42,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||||||
*/
|
*/
|
||||||
class GeocodeAction extends Action
|
class GeocodeAction extends Action
|
||||||
{
|
{
|
||||||
|
var $lat = null;
|
||||||
|
var $lon = null;
|
||||||
|
var $location = null;
|
||||||
|
|
||||||
function prepare($args)
|
function prepare($args)
|
||||||
{
|
{
|
||||||
parent::prepare($args);
|
parent::prepare($args);
|
||||||
@ -52,12 +56,7 @@ class GeocodeAction extends Action
|
|||||||
}
|
}
|
||||||
$this->lat = $this->trimmed('lat');
|
$this->lat = $this->trimmed('lat');
|
||||||
$this->lon = $this->trimmed('lon');
|
$this->lon = $this->trimmed('lon');
|
||||||
$location = Location::fromLatLon($this->lat, $this->lon);
|
$this->location = Location::fromLatLon($this->lat, $this->lon);
|
||||||
if ($location) {
|
|
||||||
$this->location = Location::fromId($location->location_id, $location->location_ns);
|
|
||||||
$this->lat = $this->location->lat;
|
|
||||||
$this->lon = $this->location->lon;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,4 +94,3 @@ class GeocodeAction extends Action
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
|
@ -131,12 +131,20 @@ class PublicAction extends Action
|
|||||||
return _('Public timeline');
|
return _('Public timeline');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function extraHead()
|
function extraHead()
|
||||||
{
|
{
|
||||||
parent::extraHead();
|
parent::extraHead();
|
||||||
$this->element('meta', array('http-equiv' => 'X-XRDS-Location',
|
$this->element('meta', array('http-equiv' => 'X-XRDS-Location',
|
||||||
'content' => common_local_url('publicxrds')));
|
'content' => common_local_url('publicxrds')));
|
||||||
|
|
||||||
|
$rsd = common_local_url('rsd');
|
||||||
|
|
||||||
|
// RSD, http://tales.phrasewise.com/rfc/rsd
|
||||||
|
|
||||||
|
$this->element('link', array('rel' => 'EditURI',
|
||||||
|
'type' => 'application/rsd+xml',
|
||||||
|
'href' => $rsd));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
100
actions/robotstxt.php
Normal file
100
actions/robotstxt.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2010, StatusNet, Inc.
|
||||||
|
*
|
||||||
|
* robots.txt generator
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* @category Action
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints out a static robots.txt
|
||||||
|
*
|
||||||
|
* @category Action
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class RobotstxtAction extends Action
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handles requests
|
||||||
|
*
|
||||||
|
* Since this is a relatively static document, we
|
||||||
|
* don't do a prepare()
|
||||||
|
*
|
||||||
|
* @param array $args GET, POST, and URL params; unused.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
if (Event::handle('StartRobotsTxt', array($this))) {
|
||||||
|
|
||||||
|
header('Content-Type: text/plain');
|
||||||
|
|
||||||
|
print "User-Agent: *\n";
|
||||||
|
|
||||||
|
if (common_config('site', 'private')) {
|
||||||
|
|
||||||
|
print "Disallow: /\n";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$disallow = common_config('robotstxt', 'disallow');
|
||||||
|
|
||||||
|
foreach ($disallow as $dir) {
|
||||||
|
print "Disallow: /$dir/\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$crawldelay = common_config('robotstxt', 'crawldelay');
|
||||||
|
|
||||||
|
if (!empty($crawldelay)) {
|
||||||
|
print "Crawl-delay: " . $crawldelay . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::handle('EndRobotsTxt', array($this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true; this page doesn't touch the DB.
|
||||||
|
*
|
||||||
|
* @param array $args other arguments
|
||||||
|
*
|
||||||
|
* @return boolean is read only action?
|
||||||
|
*/
|
||||||
|
|
||||||
|
function isReadOnly($args)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
226
actions/rsd.php
Normal file
226
actions/rsd.php
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2008-2010, StatusNet, Inc.
|
||||||
|
*
|
||||||
|
* Really Simple Discovery (RSD) for API access
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* 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 Evan Prodromou <evan@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSD action class
|
||||||
|
*
|
||||||
|
* Really Simple Discovery (RSD) is a simple (to a fault, maybe)
|
||||||
|
* discovery tool for blog APIs.
|
||||||
|
*
|
||||||
|
* http://tales.phrasewise.com/rfc/rsd
|
||||||
|
*
|
||||||
|
* Anil Dash suggested that RSD be used for services that implement
|
||||||
|
* the Twitter API:
|
||||||
|
*
|
||||||
|
* http://dashes.com/anil/2009/12/the-twitter-api-is-finished.html
|
||||||
|
*
|
||||||
|
* It's in use now for WordPress.com blogs:
|
||||||
|
*
|
||||||
|
* http://matt.wordpress.com/xmlrpc.php?rsd
|
||||||
|
*
|
||||||
|
* I (evan@status.net) have tried to stay faithful to the premise of
|
||||||
|
* RSD, while adding information useful to StatusNet client developers.
|
||||||
|
* In particular:
|
||||||
|
*
|
||||||
|
* - There is a link from each user's profile page to their personal
|
||||||
|
* RSD feed. A personal rsd.xml includes a 'blogID' element that is
|
||||||
|
* their username.
|
||||||
|
* - There is a link from the public root to '/rsd.xml', a public RSD
|
||||||
|
* feed. It's identical to the personal rsd except it doesn't include
|
||||||
|
* a blogId.
|
||||||
|
* - I've added a setting to the API to indicate that OAuth support is
|
||||||
|
* available.
|
||||||
|
*
|
||||||
|
* @category API
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class RsdAction extends Action
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Optional attribute for the personal rsd.xml file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var $user = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the action for use.
|
||||||
|
*
|
||||||
|
* Check for a nickname; redirect if non-canonical; if
|
||||||
|
* not provided, assume public rsd.xml.
|
||||||
|
*
|
||||||
|
* @param array $args GET, POST, and URI arguments.
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
function prepare($args)
|
||||||
|
{
|
||||||
|
parent::prepare($args);
|
||||||
|
|
||||||
|
// optional argument
|
||||||
|
|
||||||
|
$nickname_arg = $this->arg('nickname');
|
||||||
|
|
||||||
|
if (empty($nickname_arg)) {
|
||||||
|
$this->user = null;
|
||||||
|
} else {
|
||||||
|
$nickname = common_canonical_nickname($nickname_arg);
|
||||||
|
|
||||||
|
// Permanent redirect on non-canonical nickname
|
||||||
|
|
||||||
|
if ($nickname_arg != $nickname) {
|
||||||
|
common_redirect(common_local_url('rsd',
|
||||||
|
array('nickname' => $nickname)),
|
||||||
|
301);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->user = User::staticGet('nickname', $nickname);
|
||||||
|
|
||||||
|
if (empty($this->user)) {
|
||||||
|
$this->clientError(_('No such user.'), 404);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action handler.
|
||||||
|
*
|
||||||
|
* Outputs the XML format for an RSD file. May include
|
||||||
|
* personal information if this is a personal file
|
||||||
|
* (based on whether $user attribute is set).
|
||||||
|
*
|
||||||
|
* @param array $args array of arguments
|
||||||
|
*
|
||||||
|
* @return nothing
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
header('Content-Type: application/rsd+xml');
|
||||||
|
|
||||||
|
$this->startXML();
|
||||||
|
|
||||||
|
$rsdNS = 'http://archipelago.phrasewise.com/rsd';
|
||||||
|
$this->elementStart('rsd', array('version' => '1.0',
|
||||||
|
'xmlns' => $rsdNS));
|
||||||
|
$this->elementStart('service');
|
||||||
|
$this->element('engineName', null, _('StatusNet'));
|
||||||
|
$this->element('engineLink', null, 'http://status.net/');
|
||||||
|
$this->elementStart('apis');
|
||||||
|
if (Event::handle('StartRsdListApis', array($this, $this->user))) {
|
||||||
|
|
||||||
|
$blogID = (empty($this->user)) ? '' : $this->user->nickname;
|
||||||
|
$apiAttrs = array('name' => 'Twitter',
|
||||||
|
'preferred' => 'true',
|
||||||
|
'apiLink' => $this->_apiRoot(),
|
||||||
|
'blogID' => $blogID);
|
||||||
|
|
||||||
|
$this->elementStart('api', $apiAttrs);
|
||||||
|
$this->elementStart('settings');
|
||||||
|
$this->element('docs', null,
|
||||||
|
'http://status.net/wiki/TwitterCompatibleAPI');
|
||||||
|
$this->element('setting', array('name' => 'OAuth'),
|
||||||
|
'true');
|
||||||
|
$this->elementEnd('settings');
|
||||||
|
$this->elementEnd('api');
|
||||||
|
Event::handle('EndRsdListApis', array($this, $this->user));
|
||||||
|
}
|
||||||
|
$this->elementEnd('apis');
|
||||||
|
$this->elementEnd('service');
|
||||||
|
$this->elementEnd('rsd');
|
||||||
|
|
||||||
|
$this->endXML();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns last-modified date for use in caching
|
||||||
|
*
|
||||||
|
* Per-user rsd.xml is dated to last change of user
|
||||||
|
* (in case of nickname change); public has no date.
|
||||||
|
*
|
||||||
|
* @return string date of last change of this page
|
||||||
|
*/
|
||||||
|
|
||||||
|
function lastModified()
|
||||||
|
{
|
||||||
|
if (!empty($this->user)) {
|
||||||
|
return $this->user->modified;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag to indicate if this action is read-only
|
||||||
|
*
|
||||||
|
* It is; it doesn't change the DB.
|
||||||
|
*
|
||||||
|
* @param array $args ignored
|
||||||
|
*
|
||||||
|
* @return boolean true
|
||||||
|
*/
|
||||||
|
|
||||||
|
function isReadOnly($args)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return current site's API root
|
||||||
|
*
|
||||||
|
* Varies based on URL parameters, like if fancy URLs are
|
||||||
|
* turned on.
|
||||||
|
*
|
||||||
|
* @return string API root URI for this site
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function _apiRoot()
|
||||||
|
{
|
||||||
|
if (common_config('site', 'fancy')) {
|
||||||
|
return common_path('api/', true);
|
||||||
|
} else {
|
||||||
|
return common_path('index.php/api/', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -178,6 +178,15 @@ class ShowstreamAction extends ProfileAction
|
|||||||
$this->element('link', array('rel' => 'microsummary',
|
$this->element('link', array('rel' => 'microsummary',
|
||||||
'href' => common_local_url('microsummary',
|
'href' => common_local_url('microsummary',
|
||||||
array('nickname' => $this->profile->nickname))));
|
array('nickname' => $this->profile->nickname))));
|
||||||
|
|
||||||
|
$rsd = common_local_url('rsd',
|
||||||
|
array('nickname' => $this->profile->nickname));
|
||||||
|
|
||||||
|
// RSD, http://tales.phrasewise.com/rfc/rsd
|
||||||
|
$this->element('link', array('rel' => 'EditURI',
|
||||||
|
'type' => 'application/rsd+xml',
|
||||||
|
'href' => $rsd));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showProfile()
|
function showProfile()
|
||||||
|
@ -147,6 +147,7 @@ class Memcached_DataObject extends DB_DataObject
|
|||||||
{
|
{
|
||||||
$result = parent::insert();
|
$result = parent::insert();
|
||||||
if ($result) {
|
if ($result) {
|
||||||
|
$this->fixupTimestamps();
|
||||||
$this->encache(); // in case of cached negative lookups
|
$this->encache(); // in case of cached negative lookups
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
@ -159,6 +160,7 @@ class Memcached_DataObject extends DB_DataObject
|
|||||||
}
|
}
|
||||||
$result = parent::update($orig);
|
$result = parent::update($orig);
|
||||||
if ($result) {
|
if ($result) {
|
||||||
|
$this->fixupTimestamps();
|
||||||
$this->encache();
|
$this->encache();
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
@ -366,7 +368,7 @@ class Memcached_DataObject extends DB_DataObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sends query to database - this is the private one that must work
|
* sends query to database - this is the private one that must work
|
||||||
* - internal functions use this rather than $this->query()
|
* - internal functions use this rather than $this->query()
|
||||||
*
|
*
|
||||||
* Overridden to do logging.
|
* Overridden to do logging.
|
||||||
@ -428,7 +430,7 @@ class Memcached_DataObject extends DB_DataObject
|
|||||||
//
|
//
|
||||||
// WARNING WARNING if we end up actually using multiple DBs at a time
|
// WARNING WARNING if we end up actually using multiple DBs at a time
|
||||||
// we'll need some fancier logic here.
|
// we'll need some fancier logic here.
|
||||||
if (!$exists && !empty($_DB_DATAOBJECT['CONNECTIONS'])) {
|
if (!$exists && !empty($_DB_DATAOBJECT['CONNECTIONS']) && php_sapi_name() == 'cli') {
|
||||||
foreach ($_DB_DATAOBJECT['CONNECTIONS'] as $index => $conn) {
|
foreach ($_DB_DATAOBJECT['CONNECTIONS'] as $index => $conn) {
|
||||||
if (!empty($conn)) {
|
if (!empty($conn)) {
|
||||||
$conn->disconnect();
|
$conn->disconnect();
|
||||||
@ -529,4 +531,51 @@ class Memcached_DataObject extends DB_DataObject
|
|||||||
|
|
||||||
return $c->delete($cacheKey);
|
return $c->delete($cacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fixupTimestamps()
|
||||||
|
{
|
||||||
|
// Fake up timestamp columns
|
||||||
|
$columns = $this->table();
|
||||||
|
foreach ($columns as $name => $type) {
|
||||||
|
if ($type & DB_DATAOBJECT_MYSQLTIMESTAMP) {
|
||||||
|
$this->$name = common_sql_now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugDump()
|
||||||
|
{
|
||||||
|
common_debug("debugDump: " . common_log_objstring($this));
|
||||||
|
}
|
||||||
|
|
||||||
|
function raiseError($message, $type = null, $behaviour = null)
|
||||||
|
{
|
||||||
|
throw new ServerException("DB_DataObject error [$type]: $message");
|
||||||
|
}
|
||||||
|
|
||||||
|
static function cacheGet($keyPart)
|
||||||
|
{
|
||||||
|
$c = self::memcache();
|
||||||
|
|
||||||
|
if (empty($c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheKey = common_cache_key($keyPart);
|
||||||
|
|
||||||
|
return $c->get($cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function cacheSet($keyPart, $value)
|
||||||
|
{
|
||||||
|
$c = self::memcache();
|
||||||
|
|
||||||
|
if (empty($c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheKey = common_cache_key($keyPart);
|
||||||
|
|
||||||
|
return $c->set($cacheKey, $value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ class Notice extends Memcached_DataObject
|
|||||||
foreach(array_unique($hashtags) as $hashtag) {
|
foreach(array_unique($hashtags) as $hashtag) {
|
||||||
/* elide characters we don't want in the tag */
|
/* elide characters we don't want in the tag */
|
||||||
$this->saveTag($hashtag);
|
$this->saveTag($hashtag);
|
||||||
self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, $tag->tag);
|
self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, $hashtag);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -326,9 +326,7 @@ class Notice extends Memcached_DataObject
|
|||||||
# XXX: someone clever could prepend instead of clearing the cache
|
# XXX: someone clever could prepend instead of clearing the cache
|
||||||
$notice->blowOnInsert();
|
$notice->blowOnInsert();
|
||||||
|
|
||||||
$qm = QueueManager::get();
|
$notice->distribute();
|
||||||
|
|
||||||
$qm->enqueue($notice, 'distrib');
|
|
||||||
|
|
||||||
return $notice;
|
return $notice;
|
||||||
}
|
}
|
||||||
@ -1374,8 +1372,6 @@ class Notice extends Memcached_DataObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
$reply->free();
|
$reply->free();
|
||||||
|
|
||||||
return $ids;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearRepeats()
|
function clearRepeats()
|
||||||
@ -1445,4 +1441,31 @@ class Notice extends Memcached_DataObject
|
|||||||
|
|
||||||
$gi->free();
|
$gi->free();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function distribute()
|
||||||
|
{
|
||||||
|
if (common_config('queue', 'inboxes')) {
|
||||||
|
// If there's a failure, we want to _force_
|
||||||
|
// distribution at this point.
|
||||||
|
try {
|
||||||
|
$qm = QueueManager::get();
|
||||||
|
$qm->enqueue($this, 'distrib');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// If the exception isn't transient, this
|
||||||
|
// may throw more exceptions as DQH does
|
||||||
|
// its own enqueueing. So, we ignore them!
|
||||||
|
try {
|
||||||
|
$handler = new DistribQueueHandler();
|
||||||
|
$handler->handle($this);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
common_log(LOG_ERR, "emergency redistribution resulted in " . $e->getMessage());
|
||||||
|
}
|
||||||
|
// Re-throw so somebody smarter can handle it.
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$handler = new DistribQueueHandler();
|
||||||
|
$handler->handle($this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ class Profile_role extends Memcached_DataObject
|
|||||||
return Memcached_DataObject::pkeyGet('Profile_role', $kv);
|
return Memcached_DataObject::pkeyGet('Profile_role', $kv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const OWNER = 'owner';
|
||||||
const MODERATOR = 'moderator';
|
const MODERATOR = 'moderator';
|
||||||
const ADMINISTRATOR = 'administrator';
|
const ADMINISTRATOR = 'administrator';
|
||||||
const SANDBOXED = 'sandboxed';
|
const SANDBOXED = 'sandboxed';
|
||||||
|
@ -64,8 +64,12 @@ class Session extends Memcached_DataObject
|
|||||||
$session = Session::staticGet('id', $id);
|
$session = Session::staticGet('id', $id);
|
||||||
|
|
||||||
if (empty($session)) {
|
if (empty($session)) {
|
||||||
|
self::logdeb("Couldn't find '$id'");
|
||||||
return '';
|
return '';
|
||||||
} else {
|
} else {
|
||||||
|
self::logdeb("Found '$id', returning " .
|
||||||
|
strlen($session->session_data) .
|
||||||
|
" chars of data");
|
||||||
return (string)$session->session_data;
|
return (string)$session->session_data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,14 +81,24 @@ class Session extends Memcached_DataObject
|
|||||||
$session = Session::staticGet('id', $id);
|
$session = Session::staticGet('id', $id);
|
||||||
|
|
||||||
if (empty($session)) {
|
if (empty($session)) {
|
||||||
|
self::logdeb("'$id' doesn't yet exist; inserting.");
|
||||||
$session = new Session();
|
$session = new Session();
|
||||||
|
|
||||||
$session->id = $id;
|
$session->id = $id;
|
||||||
$session->session_data = $session_data;
|
$session->session_data = $session_data;
|
||||||
$session->created = common_sql_now();
|
$session->created = common_sql_now();
|
||||||
|
|
||||||
return $session->insert();
|
$result = $session->insert();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
common_log_db_error($session, 'INSERT', __FILE__);
|
||||||
|
self::logdeb("Failed to insert '$id'.");
|
||||||
|
} else {
|
||||||
|
self::logdeb("Successfully inserted '$id' (result = $result).");
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
} else {
|
} else {
|
||||||
|
self::logdeb("'$id' already exists; updating.");
|
||||||
if (strcmp($session->session_data, $session_data) == 0) {
|
if (strcmp($session->session_data, $session_data) == 0) {
|
||||||
self::logdeb("Not writing session '$id'; unchanged");
|
self::logdeb("Not writing session '$id'; unchanged");
|
||||||
return true;
|
return true;
|
||||||
@ -95,7 +109,16 @@ class Session extends Memcached_DataObject
|
|||||||
|
|
||||||
$session->session_data = $session_data;
|
$session->session_data = $session_data;
|
||||||
|
|
||||||
return $session->update($orig);
|
$result = $session->update($orig);
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
common_log_db_error($session, 'UPDATE', __FILE__);
|
||||||
|
self::logdeb("Failed to update '$id'.");
|
||||||
|
} else {
|
||||||
|
self::logdeb("Successfully updated '$id' (result = $result).");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,8 +129,17 @@ class Session extends Memcached_DataObject
|
|||||||
|
|
||||||
$session = Session::staticGet('id', $id);
|
$session = Session::staticGet('id', $id);
|
||||||
|
|
||||||
if (!empty($session)) {
|
if (empty($session)) {
|
||||||
return $session->delete();
|
self::logdeb("Can't find '$id' to delete.");
|
||||||
|
} else {
|
||||||
|
$result = $session->delete();
|
||||||
|
if (!$result) {
|
||||||
|
common_log_db_error($session, 'DELETE', __FILE__);
|
||||||
|
self::logdeb("Failed to delete '$id'.");
|
||||||
|
} else {
|
||||||
|
self::logdeb("Successfully deleted '$id' (result = $result).");
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +164,10 @@ class Session extends Memcached_DataObject
|
|||||||
|
|
||||||
$session->free();
|
$session->free();
|
||||||
|
|
||||||
|
self::logdeb("Found " . count($ids) . " ids to delete.");
|
||||||
|
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
|
self::logdeb("Destroying session '$id'.");
|
||||||
self::destroy($id);
|
self::destroy($id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
225
classes/User.php
225
classes/User.php
@ -209,8 +209,6 @@ class User extends Memcached_DataObject
|
|||||||
|
|
||||||
$profile = new Profile();
|
$profile = new Profile();
|
||||||
|
|
||||||
$profile->query('BEGIN');
|
|
||||||
|
|
||||||
if(!empty($email))
|
if(!empty($email))
|
||||||
{
|
{
|
||||||
$email = common_canonical_email($email);
|
$email = common_canonical_email($email);
|
||||||
@ -220,7 +218,7 @@ class User extends Memcached_DataObject
|
|||||||
$profile->nickname = $nickname;
|
$profile->nickname = $nickname;
|
||||||
if(! User::allowed_nickname($nickname)){
|
if(! User::allowed_nickname($nickname)){
|
||||||
common_log(LOG_WARNING, sprintf("Attempted to register a nickname that is not allowed: %s", $profile->nickname),
|
common_log(LOG_WARNING, sprintf("Attempted to register a nickname that is not allowed: %s", $profile->nickname),
|
||||||
__FILE__);
|
__FILE__);
|
||||||
}
|
}
|
||||||
$profile->profileurl = common_profile_url($nickname);
|
$profile->profileurl = common_profile_url($nickname);
|
||||||
|
|
||||||
@ -248,16 +246,8 @@ class User extends Memcached_DataObject
|
|||||||
|
|
||||||
$profile->created = common_sql_now();
|
$profile->created = common_sql_now();
|
||||||
|
|
||||||
$id = $profile->insert();
|
|
||||||
|
|
||||||
if (empty($id)) {
|
|
||||||
common_log_db_error($profile, 'INSERT', __FILE__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = new User();
|
$user = new User();
|
||||||
|
|
||||||
$user->id = $id;
|
|
||||||
$user->nickname = $nickname;
|
$user->nickname = $nickname;
|
||||||
|
|
||||||
if (!empty($password)) { // may not have a password for OpenID users
|
if (!empty($password)) { // may not have a password for OpenID users
|
||||||
@ -282,109 +272,126 @@ class User extends Memcached_DataObject
|
|||||||
$user->inboxed = 1;
|
$user->inboxed = 1;
|
||||||
|
|
||||||
$user->created = common_sql_now();
|
$user->created = common_sql_now();
|
||||||
$user->uri = common_user_uri($user);
|
|
||||||
|
|
||||||
$result = $user->insert();
|
if (Event::handle('StartUserRegister', array(&$user, &$profile))) {
|
||||||
|
|
||||||
if (!$result) {
|
$profile->query('BEGIN');
|
||||||
common_log_db_error($user, 'INSERT', __FILE__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everyone gets an inbox
|
$id = $profile->insert();
|
||||||
|
|
||||||
$inbox = new Inbox();
|
if (empty($id)) {
|
||||||
|
common_log_db_error($profile, 'INSERT', __FILE__);
|
||||||
$inbox->user_id = $user->id;
|
|
||||||
$inbox->notice_ids = '';
|
|
||||||
|
|
||||||
$result = $inbox->insert();
|
|
||||||
|
|
||||||
if (!$result) {
|
|
||||||
common_log_db_error($inbox, 'INSERT', __FILE__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everyone is subscribed to themself
|
|
||||||
|
|
||||||
$subscription = new Subscription();
|
|
||||||
$subscription->subscriber = $user->id;
|
|
||||||
$subscription->subscribed = $user->id;
|
|
||||||
$subscription->created = $user->created;
|
|
||||||
|
|
||||||
$result = $subscription->insert();
|
|
||||||
|
|
||||||
if (!$result) {
|
|
||||||
common_log_db_error($subscription, 'INSERT', __FILE__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($email) && !$user->email) {
|
|
||||||
|
|
||||||
$confirm = new Confirm_address();
|
|
||||||
$confirm->code = common_confirmation_code(128);
|
|
||||||
$confirm->user_id = $user->id;
|
|
||||||
$confirm->address = $email;
|
|
||||||
$confirm->address_type = 'email';
|
|
||||||
|
|
||||||
$result = $confirm->insert();
|
|
||||||
if (!$result) {
|
|
||||||
common_log_db_error($confirm, 'INSERT', __FILE__);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($code) && $user->email) {
|
$user->id = $id;
|
||||||
$user->emailChanged();
|
$user->uri = common_user_uri($user);
|
||||||
}
|
|
||||||
|
|
||||||
// Default system subscription
|
$result = $user->insert();
|
||||||
|
|
||||||
$defnick = common_config('newuser', 'default');
|
if (!$result) {
|
||||||
|
common_log_db_error($user, 'INSERT', __FILE__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($defnick)) {
|
// Everyone gets an inbox
|
||||||
$defuser = User::staticGet('nickname', $defnick);
|
|
||||||
if (empty($defuser)) {
|
|
||||||
common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick),
|
|
||||||
__FILE__);
|
|
||||||
} else {
|
|
||||||
$defsub = new Subscription();
|
|
||||||
$defsub->subscriber = $user->id;
|
|
||||||
$defsub->subscribed = $defuser->id;
|
|
||||||
$defsub->created = $user->created;
|
|
||||||
|
|
||||||
$result = $defsub->insert();
|
$inbox = new Inbox();
|
||||||
|
|
||||||
|
$inbox->user_id = $user->id;
|
||||||
|
$inbox->notice_ids = '';
|
||||||
|
|
||||||
|
$result = $inbox->insert();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
common_log_db_error($inbox, 'INSERT', __FILE__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everyone is subscribed to themself
|
||||||
|
|
||||||
|
$subscription = new Subscription();
|
||||||
|
$subscription->subscriber = $user->id;
|
||||||
|
$subscription->subscribed = $user->id;
|
||||||
|
$subscription->created = $user->created;
|
||||||
|
|
||||||
|
$result = $subscription->insert();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
common_log_db_error($subscription, 'INSERT', __FILE__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($email) && !$user->email) {
|
||||||
|
|
||||||
|
$confirm = new Confirm_address();
|
||||||
|
$confirm->code = common_confirmation_code(128);
|
||||||
|
$confirm->user_id = $user->id;
|
||||||
|
$confirm->address = $email;
|
||||||
|
$confirm->address_type = 'email';
|
||||||
|
|
||||||
|
$result = $confirm->insert();
|
||||||
|
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
common_log_db_error($defsub, 'INSERT', __FILE__);
|
common_log_db_error($confirm, 'INSERT', __FILE__);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$profile->query('COMMIT');
|
|
||||||
|
|
||||||
if (!empty($email) && !$user->email) {
|
|
||||||
mail_confirm_address($user, $confirm->code, $profile->nickname, $email);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Welcome message
|
|
||||||
|
|
||||||
$welcome = common_config('newuser', 'welcome');
|
|
||||||
|
|
||||||
if (!empty($welcome)) {
|
|
||||||
$welcomeuser = User::staticGet('nickname', $welcome);
|
|
||||||
if (empty($welcomeuser)) {
|
|
||||||
common_log(LOG_WARNING, sprintf("Welcome user %s does not exist.", $defnick),
|
|
||||||
__FILE__);
|
|
||||||
} else {
|
|
||||||
$notice = Notice::saveNew($welcomeuser->id,
|
|
||||||
sprintf(_('Welcome to %1$s, @%2$s!'),
|
|
||||||
common_config('site', 'name'),
|
|
||||||
$user->nickname),
|
|
||||||
'system');
|
|
||||||
|
|
||||||
|
if (!empty($code) && $user->email) {
|
||||||
|
$user->emailChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default system subscription
|
||||||
|
|
||||||
|
$defnick = common_config('newuser', 'default');
|
||||||
|
|
||||||
|
if (!empty($defnick)) {
|
||||||
|
$defuser = User::staticGet('nickname', $defnick);
|
||||||
|
if (empty($defuser)) {
|
||||||
|
common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick),
|
||||||
|
__FILE__);
|
||||||
|
} else {
|
||||||
|
$defsub = new Subscription();
|
||||||
|
$defsub->subscriber = $user->id;
|
||||||
|
$defsub->subscribed = $defuser->id;
|
||||||
|
$defsub->created = $user->created;
|
||||||
|
|
||||||
|
$result = $defsub->insert();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
common_log_db_error($defsub, 'INSERT', __FILE__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile->query('COMMIT');
|
||||||
|
|
||||||
|
if (!empty($email) && !$user->email) {
|
||||||
|
mail_confirm_address($user, $confirm->code, $profile->nickname, $email);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Welcome message
|
||||||
|
|
||||||
|
$welcome = common_config('newuser', 'welcome');
|
||||||
|
|
||||||
|
if (!empty($welcome)) {
|
||||||
|
$welcomeuser = User::staticGet('nickname', $welcome);
|
||||||
|
if (empty($welcomeuser)) {
|
||||||
|
common_log(LOG_WARNING, sprintf("Welcome user %s does not exist.", $defnick),
|
||||||
|
__FILE__);
|
||||||
|
} else {
|
||||||
|
$notice = Notice::saveNew($welcomeuser->id,
|
||||||
|
sprintf(_('Welcome to %1$s, @%2$s!'),
|
||||||
|
common_config('site', 'name'),
|
||||||
|
$user->nickname),
|
||||||
|
'system');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::handle('EndUserRegister', array(&$profile, &$user));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $user;
|
return $user;
|
||||||
@ -925,4 +932,30 @@ class User extends Memcached_DataObject
|
|||||||
return $share;
|
return $share;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function siteOwner()
|
||||||
|
{
|
||||||
|
$owner = self::cacheGet('user:site_owner');
|
||||||
|
|
||||||
|
if ($owner === false) { // cache miss
|
||||||
|
|
||||||
|
$pr = new Profile_role();
|
||||||
|
|
||||||
|
$pr->role = Profile_role::OWNER;
|
||||||
|
|
||||||
|
$pr->orderBy('created');
|
||||||
|
|
||||||
|
$pr->limit(0, 1);
|
||||||
|
|
||||||
|
if ($pr->fetch($true)) {
|
||||||
|
$owner = User::staticGet('id', $pr->profile_id);
|
||||||
|
} else {
|
||||||
|
$owner = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::cacheSet('user:site_owner', $owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $owner;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ theme = 2
|
|||||||
logo = 2
|
logo = 2
|
||||||
created = 142
|
created = 142
|
||||||
modified = 384
|
modified = 384
|
||||||
|
tags = 34
|
||||||
|
|
||||||
[status_network__keys]
|
[status_network__keys]
|
||||||
nickname = K
|
nickname = K
|
||||||
|
@ -15,7 +15,9 @@ alter table queue_item rename to queue_item_old;
|
|||||||
alter table queue_item_new rename to queue_item;
|
alter table queue_item_new rename to queue_item;
|
||||||
|
|
||||||
alter table consumer
|
alter table consumer
|
||||||
add consumer_secret varchar(255) not null comment 'secret value',
|
add consumer_secret varchar(255) not null comment 'secret value';
|
||||||
|
|
||||||
|
alter table token
|
||||||
add verifier varchar(255) comment 'verifier string for OAuth 1.0a',
|
add verifier varchar(255) comment 'verifier string for OAuth 1.0a',
|
||||||
add verified_callback varchar(255) comment 'verified callback URL for OAuth 1.0a';
|
add verified_callback varchar(255) comment 'verified callback URL for OAuth 1.0a';
|
||||||
|
|
||||||
|
38
index.php
38
index.php
@ -146,12 +146,27 @@ function formatBacktraceLine($n, $line)
|
|||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkMirror($action_obj, $args)
|
function setupRW()
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
static $alwaysRW = array('session', 'remember_me');
|
static $alwaysRW = array('session', 'remember_me');
|
||||||
|
|
||||||
|
// We ensure that these tables always are used
|
||||||
|
// on the master DB
|
||||||
|
|
||||||
|
$config['db']['database_rw'] = $config['db']['database'];
|
||||||
|
$config['db']['ini_rw'] = INSTALLDIR.'/classes/statusnet.ini';
|
||||||
|
|
||||||
|
foreach ($alwaysRW as $table) {
|
||||||
|
$config['db']['table_'.$table] = 'rw';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkMirror($action_obj, $args)
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
|
||||||
if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) {
|
if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) {
|
||||||
if (is_array(common_config('db', 'mirror'))) {
|
if (is_array(common_config('db', 'mirror'))) {
|
||||||
// "load balancing", ha ha
|
// "load balancing", ha ha
|
||||||
@ -162,16 +177,6 @@ function checkMirror($action_obj, $args)
|
|||||||
$mirror = common_config('db', 'mirror');
|
$mirror = common_config('db', 'mirror');
|
||||||
}
|
}
|
||||||
|
|
||||||
// We ensure that these tables always are used
|
|
||||||
// on the master DB
|
|
||||||
|
|
||||||
$config['db']['database_rw'] = $config['db']['database'];
|
|
||||||
$config['db']['ini_rw'] = INSTALLDIR.'/classes/statusnet.ini';
|
|
||||||
|
|
||||||
foreach ($alwaysRW as $table) {
|
|
||||||
$config['db']['table_'.$table] = 'rw';
|
|
||||||
}
|
|
||||||
|
|
||||||
// everyone else uses the mirror
|
// everyone else uses the mirror
|
||||||
|
|
||||||
$config['db']['database'] = $mirror;
|
$config['db']['database'] = $mirror;
|
||||||
@ -237,9 +242,13 @@ function main()
|
|||||||
|
|
||||||
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError');
|
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError');
|
||||||
|
|
||||||
|
// Make sure RW database is setup
|
||||||
|
|
||||||
|
setupRW();
|
||||||
|
|
||||||
// XXX: we need a little more structure in this script
|
// XXX: we need a little more structure in this script
|
||||||
|
|
||||||
// get and cache current user
|
// get and cache current user (may hit RW!)
|
||||||
|
|
||||||
$user = common_current_user();
|
$user = common_current_user();
|
||||||
|
|
||||||
@ -276,8 +285,9 @@ function main()
|
|||||||
if (!$user && common_config('site', 'private')
|
if (!$user && common_config('site', 'private')
|
||||||
&& !isLoginAction($action)
|
&& !isLoginAction($action)
|
||||||
&& !preg_match('/rss$/', $action)
|
&& !preg_match('/rss$/', $action)
|
||||||
&& !preg_match('/^Api/', $action)
|
&& $action != 'robotstxt'
|
||||||
) {
|
&& !preg_match('/^Api/', $action)) {
|
||||||
|
|
||||||
// set returnto
|
// set returnto
|
||||||
$rargs =& common_copy_args($args);
|
$rargs =& common_copy_args($args);
|
||||||
unset($rargs['action']);
|
unset($rargs['action']);
|
||||||
|
@ -299,7 +299,7 @@ class ApiAction extends Action
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($include_user) {
|
if ($include_user && $profile) {
|
||||||
# Don't get notice (recursive!)
|
# Don't get notice (recursive!)
|
||||||
$twitter_user = $this->twitterUserArray($profile, false);
|
$twitter_user = $this->twitterUserArray($profile, false);
|
||||||
$twitter_status['user'] = $twitter_user;
|
$twitter_status['user'] = $twitter_user;
|
||||||
|
@ -22,7 +22,7 @@ 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.0beta3');
|
define('STATUSNET_VERSION', '0.9.0beta4');
|
||||||
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
|
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
|
||||||
|
|
||||||
define('STATUSNET_CODENAME', 'Stand');
|
define('STATUSNET_CODENAME', 'Stand');
|
||||||
@ -115,6 +115,10 @@ function __autoload($cls)
|
|||||||
require_once 'Validate.php';
|
require_once 'Validate.php';
|
||||||
require_once 'markdown.php';
|
require_once 'markdown.php';
|
||||||
|
|
||||||
|
// XXX: other formats here
|
||||||
|
|
||||||
|
define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER);
|
||||||
|
|
||||||
require_once INSTALLDIR.'/lib/util.php';
|
require_once INSTALLDIR.'/lib/util.php';
|
||||||
require_once INSTALLDIR.'/lib/action.php';
|
require_once INSTALLDIR.'/lib/action.php';
|
||||||
require_once INSTALLDIR.'/lib/mail.php';
|
require_once INSTALLDIR.'/lib/mail.php';
|
||||||
@ -136,6 +140,3 @@ try {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: other formats here
|
|
||||||
|
|
||||||
define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER);
|
|
||||||
|
@ -84,9 +84,12 @@ $default =
|
|||||||
'control_channel' => '/topic/statusnet-control', // broadcasts to all queue daemons
|
'control_channel' => '/topic/statusnet-control', // broadcasts to all queue daemons
|
||||||
'stomp_username' => null,
|
'stomp_username' => null,
|
||||||
'stomp_password' => null,
|
'stomp_password' => null,
|
||||||
|
'stomp_persistent' => true, // keep items across queue server restart, if persistence is enabled
|
||||||
|
'stomp_manual_failover' => true, // if multiple servers are listed, treat them as separate (enqueue on one randomly, listen on all)
|
||||||
'monitor' => null, // URL to monitor ping endpoint (work in progress)
|
'monitor' => null, // URL to monitor ping endpoint (work in progress)
|
||||||
'softlimit' => '90%', // total size or % of memory_limit at which to restart queue threads gracefully
|
'softlimit' => '90%', // total size or % of memory_limit at which to restart queue threads gracefully
|
||||||
'debug_memory' => false, // true to spit memory usage to log
|
'debug_memory' => false, // true to spit memory usage to log
|
||||||
|
'inboxes' => true, // true to do inbox distribution & output queueing from in background via 'distrib' queue
|
||||||
),
|
),
|
||||||
'license' =>
|
'license' =>
|
||||||
array('type' => 'cc', # can be 'cc', 'allrightsreserved', 'private'
|
array('type' => 'cc', # can be 'cc', 'allrightsreserved', 'private'
|
||||||
@ -269,4 +272,8 @@ $default =
|
|||||||
'singleuser' =>
|
'singleuser' =>
|
||||||
array('enabled' => false,
|
array('enabled' => false,
|
||||||
'nickname' => null),
|
'nickname' => null),
|
||||||
|
'robotstxt' =>
|
||||||
|
array('crawldelay' => 0,
|
||||||
|
'disallow' => array('main', 'settings', 'admin', 'search', 'message')
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
@ -62,23 +62,60 @@ class DistribQueueHandler
|
|||||||
{
|
{
|
||||||
// XXX: do we need to change this for remote users?
|
// XXX: do we need to change this for remote users?
|
||||||
|
|
||||||
$notice->saveTags();
|
try {
|
||||||
|
$notice->saveTags();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logit($notice, $e);
|
||||||
|
}
|
||||||
|
|
||||||
$groups = $notice->saveGroups();
|
try {
|
||||||
|
$groups = $notice->saveGroups();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logit($notice, $e);
|
||||||
|
}
|
||||||
|
|
||||||
$recipients = $notice->saveReplies();
|
try {
|
||||||
|
$recipients = $notice->saveReplies();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logit($notice, $e);
|
||||||
|
}
|
||||||
|
|
||||||
$notice->addToInboxes($groups, $recipients);
|
try {
|
||||||
|
$notice->addToInboxes($groups, $recipients);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logit($notice, $e);
|
||||||
|
}
|
||||||
|
|
||||||
$notice->saveUrls();
|
try {
|
||||||
|
$notice->saveUrls();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logit($notice, $e);
|
||||||
|
}
|
||||||
|
|
||||||
Event::handle('EndNoticeSave', array($notice));
|
try {
|
||||||
|
Event::handle('EndNoticeSave', array($notice));
|
||||||
|
// Enqueue for other handlers
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logit($notice, $e);
|
||||||
|
}
|
||||||
|
|
||||||
// Enqueue for other handlers
|
try {
|
||||||
|
common_enqueue_notice($notice);
|
||||||
common_enqueue_notice($notice);
|
} catch (Exception $e) {
|
||||||
|
$this->logit($notice, $e);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function logit($notice, $e)
|
||||||
|
{
|
||||||
|
common_log(LOG_ERR, "Distrib queue exception saving notice $notice->id: " .
|
||||||
|
$e->getMessage() . ' ' .
|
||||||
|
str_replace("\n", " ", $e->getTraceAsString()));
|
||||||
|
|
||||||
|
// We'll still return true so we don't get stuck in a loop
|
||||||
|
// trying to run a bad insert over and over...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,7 +358,7 @@ function jabber_broadcast_notice($notice)
|
|||||||
common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
|
common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
|
||||||
'unknown profile ' . common_log_objstring($notice),
|
'unknown profile ' . common_log_objstring($notice),
|
||||||
__FILE__);
|
__FILE__);
|
||||||
return false;
|
return true; // not recoverable; discard.
|
||||||
}
|
}
|
||||||
|
|
||||||
$msg = jabber_format_notice($profile, $notice);
|
$msg = jabber_format_notice($profile, $notice);
|
||||||
@ -437,7 +437,7 @@ function jabber_public_notice($notice)
|
|||||||
common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
|
common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
|
||||||
'unknown profile ' . common_log_objstring($notice),
|
'unknown profile ' . common_log_objstring($notice),
|
||||||
__FILE__);
|
__FILE__);
|
||||||
return false;
|
return true; // not recoverable; discard.
|
||||||
}
|
}
|
||||||
|
|
||||||
$msg = jabber_format_notice($profile, $notice);
|
$msg = jabber_format_notice($profile, $notice);
|
||||||
|
@ -40,7 +40,7 @@ class JabberQueueHandler extends QueueHandler
|
|||||||
try {
|
try {
|
||||||
return jabber_broadcast_notice($notice);
|
return jabber_broadcast_notice($notice);
|
||||||
} catch (XMPPHP_Exception $e) {
|
} catch (XMPPHP_Exception $e) {
|
||||||
$this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
|
common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,22 @@ class LiberalStomp extends Stomp
|
|||||||
return $this->_socket;
|
return $this->_socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the host we're currently connected to.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function getServer()
|
||||||
|
{
|
||||||
|
$idx = $this->_currentHost;
|
||||||
|
if ($idx >= 0) {
|
||||||
|
$host = $this->_hosts[$idx];
|
||||||
|
return "$host[0]:$host[1]";
|
||||||
|
} else {
|
||||||
|
return '[unconnected]';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make socket connection to the server
|
* Make socket connection to the server
|
||||||
* We also set the stream to non-blocking mode, since we'll be
|
* We also set the stream to non-blocking mode, since we'll be
|
||||||
@ -71,10 +87,12 @@ class LiberalStomp extends Stomp
|
|||||||
// @fixme this sometimes hangs in blocking mode...
|
// @fixme this sometimes hangs in blocking mode...
|
||||||
// shouldn't we have been idle until we found there's more data?
|
// shouldn't we have been idle until we found there's more data?
|
||||||
$read = fread($this->_socket, $rb);
|
$read = fread($this->_socket, $rb);
|
||||||
if ($read === false) {
|
if ($read === false || ($read === '' && feof($this->_socket))) {
|
||||||
$this->_reconnect();
|
// @fixme possibly attempt an auto reconnect as old code?
|
||||||
|
throw new StompException("Error reading");
|
||||||
|
//$this->_reconnect();
|
||||||
// @fixme this will lose prior items
|
// @fixme this will lose prior items
|
||||||
return $this->readFrames();
|
//return $this->readFrames();
|
||||||
}
|
}
|
||||||
$data .= $read;
|
$data .= $read;
|
||||||
if (strpos($data, "\x00") !== false) {
|
if (strpos($data, "\x00") !== false) {
|
||||||
|
537
lib/mysqlschema.php
Normal file
537
lib/mysqlschema.php
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Database schema utilities
|
||||||
|
*
|
||||||
|
* 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 Database
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2009 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing the database schema
|
||||||
|
*
|
||||||
|
* A class representing the database schema. Can be used to
|
||||||
|
* manipulate the schema -- especially for plugins and upgrade
|
||||||
|
* utilities.
|
||||||
|
*
|
||||||
|
* @category Database
|
||||||
|
* @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 MysqlSchema extends Schema
|
||||||
|
{
|
||||||
|
static $_single = null;
|
||||||
|
protected $conn = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Only run once for singleton object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function __construct()
|
||||||
|
{
|
||||||
|
// XXX: there should be an easier way to do this.
|
||||||
|
$user = new User();
|
||||||
|
|
||||||
|
$this->conn = $user->getDatabaseConnection();
|
||||||
|
|
||||||
|
$user->free();
|
||||||
|
|
||||||
|
unset($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main public entry point. Use this to get
|
||||||
|
* the singleton object.
|
||||||
|
*
|
||||||
|
* @return Schema the (single) Schema object
|
||||||
|
*/
|
||||||
|
|
||||||
|
static function get()
|
||||||
|
{
|
||||||
|
if (empty(self::$_single)) {
|
||||||
|
self::$_single = new Schema();
|
||||||
|
}
|
||||||
|
return self::$_single;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a TableDef object for the table
|
||||||
|
* in the schema with the given name.
|
||||||
|
*
|
||||||
|
* Throws an exception if the table is not found.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the table to get
|
||||||
|
*
|
||||||
|
* @return TableDef tabledef for that table.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function getTableDef($name)
|
||||||
|
{
|
||||||
|
$res = $this->conn->query('DESCRIBE ' . $name);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$td = new TableDef();
|
||||||
|
|
||||||
|
$td->name = $name;
|
||||||
|
$td->columns = array();
|
||||||
|
|
||||||
|
$row = array();
|
||||||
|
|
||||||
|
while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
|
||||||
|
|
||||||
|
$cd = new ColumnDef();
|
||||||
|
|
||||||
|
$cd->name = $row['Field'];
|
||||||
|
|
||||||
|
$packed = $row['Type'];
|
||||||
|
|
||||||
|
if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) {
|
||||||
|
$cd->type = $match[1];
|
||||||
|
$cd->size = $match[2];
|
||||||
|
} else {
|
||||||
|
$cd->type = $packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cd->nullable = ($row['Null'] == 'YES') ? true : false;
|
||||||
|
$cd->key = $row['Key'];
|
||||||
|
$cd->default = $row['Default'];
|
||||||
|
$cd->extra = $row['Extra'];
|
||||||
|
|
||||||
|
$td->columns[] = $cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $td;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a ColumnDef object for a single column.
|
||||||
|
*
|
||||||
|
* Throws an exception if the table is not found.
|
||||||
|
*
|
||||||
|
* @param string $table name of the table
|
||||||
|
* @param string $column name of the column
|
||||||
|
*
|
||||||
|
* @return ColumnDef definition of the column or null
|
||||||
|
* if not found.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function getColumnDef($table, $column)
|
||||||
|
{
|
||||||
|
$td = $this->getTableDef($table);
|
||||||
|
|
||||||
|
foreach ($td->columns as $cd) {
|
||||||
|
if ($cd->name == $column) {
|
||||||
|
return $cd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a table with the given names and columns.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the table
|
||||||
|
* @param array $columns Array of ColumnDef objects
|
||||||
|
* for new table.
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function createTable($name, $columns)
|
||||||
|
{
|
||||||
|
$uniques = array();
|
||||||
|
$primary = array();
|
||||||
|
$indices = array();
|
||||||
|
|
||||||
|
$sql = "CREATE TABLE $name (\n";
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($columns); $i++) {
|
||||||
|
|
||||||
|
$cd =& $columns[$i];
|
||||||
|
|
||||||
|
if ($i > 0) {
|
||||||
|
$sql .= ",\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= $this->_columnSql($cd);
|
||||||
|
|
||||||
|
switch ($cd->key) {
|
||||||
|
case 'UNI':
|
||||||
|
$uniques[] = $cd->name;
|
||||||
|
break;
|
||||||
|
case 'PRI':
|
||||||
|
$primary[] = $cd->name;
|
||||||
|
break;
|
||||||
|
case 'MUL':
|
||||||
|
$indices[] = $cd->name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($primary) > 0) { // it really should be...
|
||||||
|
$sql .= ",\nconstraint primary key (" . implode(',', $primary) . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($uniques as $u) {
|
||||||
|
$sql .= ",\nunique index {$name}_{$u}_idx ($u)";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($indices as $i) {
|
||||||
|
$sql .= ",\nindex {$name}_{$i}_idx ($i)";
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= "); ";
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops a table from the schema
|
||||||
|
*
|
||||||
|
* Throws an exception if the table is not found.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the table to drop
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function dropTable($name)
|
||||||
|
{
|
||||||
|
$res = $this->conn->query("DROP TABLE $name");
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an index to a table.
|
||||||
|
*
|
||||||
|
* If no name is provided, a name will be made up based
|
||||||
|
* on the table name and column names.
|
||||||
|
*
|
||||||
|
* Throws an exception on database error, esp. if the table
|
||||||
|
* does not exist.
|
||||||
|
*
|
||||||
|
* @param string $table Name of the table
|
||||||
|
* @param array $columnNames Name of columns to index
|
||||||
|
* @param string $name (Optional) name of the index
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function createIndex($table, $columnNames, $name=null)
|
||||||
|
{
|
||||||
|
if (!is_array($columnNames)) {
|
||||||
|
$columnNames = array($columnNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($name)) {
|
||||||
|
$name = "$table_".implode("_", $columnNames)."_idx";
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $this->conn->query("ALTER TABLE $table ".
|
||||||
|
"ADD INDEX $name (".
|
||||||
|
implode(",", $columnNames).")");
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops a named index from a table.
|
||||||
|
*
|
||||||
|
* @param string $table name of the table the index is on.
|
||||||
|
* @param string $name name of the index
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function dropIndex($table, $name)
|
||||||
|
{
|
||||||
|
$res = $this->conn->query("ALTER TABLE $table DROP INDEX $name");
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a column to a table
|
||||||
|
*
|
||||||
|
* @param string $table name of the table
|
||||||
|
* @param ColumnDef $columndef Definition of the new
|
||||||
|
* column.
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function addColumn($table, $columndef)
|
||||||
|
{
|
||||||
|
$sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef);
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies a column in the schema.
|
||||||
|
*
|
||||||
|
* The name must match an existing column and table.
|
||||||
|
*
|
||||||
|
* @param string $table name of the table
|
||||||
|
* @param ColumnDef $columndef new definition of the column.
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function modifyColumn($table, $columndef)
|
||||||
|
{
|
||||||
|
$sql = "ALTER TABLE $table MODIFY COLUMN " .
|
||||||
|
$this->_columnSql($columndef);
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops a column from a table
|
||||||
|
*
|
||||||
|
* The name must match an existing column.
|
||||||
|
*
|
||||||
|
* @param string $table name of the table
|
||||||
|
* @param string $columnName name of the column to drop
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function dropColumn($table, $columnName)
|
||||||
|
{
|
||||||
|
$sql = "ALTER TABLE $table DROP COLUMN $columnName";
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that a table exists with the given
|
||||||
|
* name and the given column definitions.
|
||||||
|
*
|
||||||
|
* If the table does not yet exist, it will
|
||||||
|
* create the table. If it does exist, it will
|
||||||
|
* alter the table to match the column definitions.
|
||||||
|
*
|
||||||
|
* @param string $tableName name of the table
|
||||||
|
* @param array $columns array of ColumnDef
|
||||||
|
* objects for the table
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function ensureTable($tableName, $columns)
|
||||||
|
{
|
||||||
|
// XXX: DB engine portability -> toilet
|
||||||
|
|
||||||
|
try {
|
||||||
|
$td = $this->getTableDef($tableName);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (preg_match('/no such table/', $e->getMessage())) {
|
||||||
|
return $this->createTable($tableName, $columns);
|
||||||
|
} else {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cur = $this->_names($td->columns);
|
||||||
|
$new = $this->_names($columns);
|
||||||
|
|
||||||
|
$toadd = array_diff($new, $cur);
|
||||||
|
$todrop = array_diff($cur, $new);
|
||||||
|
$same = array_intersect($new, $cur);
|
||||||
|
$tomod = array();
|
||||||
|
|
||||||
|
foreach ($same as $m) {
|
||||||
|
$curCol = $this->_byName($td->columns, $m);
|
||||||
|
$newCol = $this->_byName($columns, $m);
|
||||||
|
|
||||||
|
if (!$newCol->equals($curCol)) {
|
||||||
|
$tomod[] = $newCol->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($toadd) + count($todrop) + count($tomod) == 0) {
|
||||||
|
// nothing to do
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For efficiency, we want this all in one
|
||||||
|
// query, instead of using our methods.
|
||||||
|
|
||||||
|
$phrase = array();
|
||||||
|
|
||||||
|
foreach ($toadd as $columnName) {
|
||||||
|
$cd = $this->_byName($columns, $columnName);
|
||||||
|
|
||||||
|
$phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($todrop as $columnName) {
|
||||||
|
$phrase[] = 'DROP COLUMN ' . $columnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($tomod as $columnName) {
|
||||||
|
$cd = $this->_byName($columns, $columnName);
|
||||||
|
|
||||||
|
$phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the array of names from an array of
|
||||||
|
* ColumnDef objects.
|
||||||
|
*
|
||||||
|
* @param array $cds array of ColumnDef objects
|
||||||
|
*
|
||||||
|
* @return array strings for name values
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function _names($cds)
|
||||||
|
{
|
||||||
|
$names = array();
|
||||||
|
|
||||||
|
foreach ($cds as $cd) {
|
||||||
|
$names[] = $cd->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $names;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a ColumnDef from an array matching
|
||||||
|
* name.
|
||||||
|
*
|
||||||
|
* @param array $cds Array of ColumnDef objects
|
||||||
|
* @param string $name Name of the column
|
||||||
|
*
|
||||||
|
* @return ColumnDef matching item or null if no match.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function _byName($cds, $name)
|
||||||
|
{
|
||||||
|
foreach ($cds as $cd) {
|
||||||
|
if ($cd->name == $name) {
|
||||||
|
return $cd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the proper SQL for creating or
|
||||||
|
* altering a column.
|
||||||
|
*
|
||||||
|
* Appropriate for use in CREATE TABLE or
|
||||||
|
* ALTER TABLE statements.
|
||||||
|
*
|
||||||
|
* @param ColumnDef $cd column to create
|
||||||
|
*
|
||||||
|
* @return string correct SQL for that column
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function _columnSql($cd)
|
||||||
|
{
|
||||||
|
$sql = "{$cd->name} ";
|
||||||
|
|
||||||
|
if (!empty($cd->size)) {
|
||||||
|
$sql .= "{$cd->type}({$cd->size}) ";
|
||||||
|
} else {
|
||||||
|
$sql .= "{$cd->type} ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($cd->default)) {
|
||||||
|
$sql .= "default {$cd->default} ";
|
||||||
|
} else {
|
||||||
|
$sql .= ($cd->nullable) ? "null " : "not null ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($cd->auto_increment)) {
|
||||||
|
$sql .= " auto_increment ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($cd->extra)) {
|
||||||
|
$sql .= "{$cd->extra} ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sql;
|
||||||
|
}
|
||||||
|
}
|
@ -39,7 +39,7 @@ class OmbQueueHandler extends QueueHandler
|
|||||||
function handle($notice)
|
function handle($notice)
|
||||||
{
|
{
|
||||||
if ($this->is_remote($notice)) {
|
if ($this->is_remote($notice)) {
|
||||||
$this->log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id);
|
common_log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
require_once(INSTALLDIR.'/lib/omb.php');
|
require_once(INSTALLDIR.'/lib/omb.php');
|
||||||
|
503
lib/pgsqlschema.php
Normal file
503
lib/pgsqlschema.php
Normal file
@ -0,0 +1,503 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Database schema utilities
|
||||||
|
*
|
||||||
|
* 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 Database
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2009 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing the database schema
|
||||||
|
*
|
||||||
|
* A class representing the database schema. Can be used to
|
||||||
|
* manipulate the schema -- especially for plugins and upgrade
|
||||||
|
* utilities.
|
||||||
|
*
|
||||||
|
* @category Database
|
||||||
|
* @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 PgsqlSchema extends Schema
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a TableDef object for the table
|
||||||
|
* in the schema with the given name.
|
||||||
|
*
|
||||||
|
* Throws an exception if the table is not found.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the table to get
|
||||||
|
*
|
||||||
|
* @return TableDef tabledef for that table.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function getTableDef($name)
|
||||||
|
{
|
||||||
|
$res = $this->conn->query("select *, column_default as default, is_nullable as Null, udt_name as Type, column_name AS Field from INFORMATION_SCHEMA.COLUMNS where table_name = '$name'");
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$td = new TableDef();
|
||||||
|
|
||||||
|
$td->name = $name;
|
||||||
|
$td->columns = array();
|
||||||
|
|
||||||
|
$row = array();
|
||||||
|
|
||||||
|
while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
|
||||||
|
// var_dump($row);
|
||||||
|
$cd = new ColumnDef();
|
||||||
|
|
||||||
|
$cd->name = $row['field'];
|
||||||
|
|
||||||
|
$packed = $row['type'];
|
||||||
|
|
||||||
|
if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) {
|
||||||
|
$cd->type = $match[1];
|
||||||
|
$cd->size = $match[2];
|
||||||
|
} else {
|
||||||
|
$cd->type = $packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cd->nullable = ($row['null'] == 'YES') ? true : false;
|
||||||
|
$cd->key = $row['Key'];
|
||||||
|
$cd->default = $row['default'];
|
||||||
|
$cd->extra = $row['Extra'];
|
||||||
|
|
||||||
|
$td->columns[] = $cd;
|
||||||
|
}
|
||||||
|
return $td;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a ColumnDef object for a single column.
|
||||||
|
*
|
||||||
|
* Throws an exception if the table is not found.
|
||||||
|
*
|
||||||
|
* @param string $table name of the table
|
||||||
|
* @param string $column name of the column
|
||||||
|
*
|
||||||
|
* @return ColumnDef definition of the column or null
|
||||||
|
* if not found.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function getColumnDef($table, $column)
|
||||||
|
{
|
||||||
|
$td = $this->getTableDef($table);
|
||||||
|
|
||||||
|
foreach ($td->columns as $cd) {
|
||||||
|
if ($cd->name == $column) {
|
||||||
|
return $cd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a table with the given names and columns.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the table
|
||||||
|
* @param array $columns Array of ColumnDef objects
|
||||||
|
* for new table.
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function createTable($name, $columns)
|
||||||
|
{
|
||||||
|
$uniques = array();
|
||||||
|
$primary = array();
|
||||||
|
$indices = array();
|
||||||
|
|
||||||
|
$sql = "CREATE TABLE $name (\n";
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($columns); $i++) {
|
||||||
|
|
||||||
|
$cd =& $columns[$i];
|
||||||
|
|
||||||
|
if ($i > 0) {
|
||||||
|
$sql .= ",\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= $this->_columnSql($cd);
|
||||||
|
|
||||||
|
switch ($cd->key) {
|
||||||
|
case 'UNI':
|
||||||
|
$uniques[] = $cd->name;
|
||||||
|
break;
|
||||||
|
case 'PRI':
|
||||||
|
$primary[] = $cd->name;
|
||||||
|
break;
|
||||||
|
case 'MUL':
|
||||||
|
$indices[] = $cd->name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($primary) > 0) { // it really should be...
|
||||||
|
$sql .= ",\nconstraint primary key (" . implode(',', $primary) . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($uniques as $u) {
|
||||||
|
$sql .= ",\nunique index {$name}_{$u}_idx ($u)";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($indices as $i) {
|
||||||
|
$sql .= ",\nindex {$name}_{$i}_idx ($i)";
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= "); ";
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops a table from the schema
|
||||||
|
*
|
||||||
|
* Throws an exception if the table is not found.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the table to drop
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function dropTable($name)
|
||||||
|
{
|
||||||
|
$res = $this->conn->query("DROP TABLE $name");
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an index to a table.
|
||||||
|
*
|
||||||
|
* If no name is provided, a name will be made up based
|
||||||
|
* on the table name and column names.
|
||||||
|
*
|
||||||
|
* Throws an exception on database error, esp. if the table
|
||||||
|
* does not exist.
|
||||||
|
*
|
||||||
|
* @param string $table Name of the table
|
||||||
|
* @param array $columnNames Name of columns to index
|
||||||
|
* @param string $name (Optional) name of the index
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function createIndex($table, $columnNames, $name=null)
|
||||||
|
{
|
||||||
|
if (!is_array($columnNames)) {
|
||||||
|
$columnNames = array($columnNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($name)) {
|
||||||
|
$name = "$table_".implode("_", $columnNames)."_idx";
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $this->conn->query("ALTER TABLE $table ".
|
||||||
|
"ADD INDEX $name (".
|
||||||
|
implode(",", $columnNames).")");
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops a named index from a table.
|
||||||
|
*
|
||||||
|
* @param string $table name of the table the index is on.
|
||||||
|
* @param string $name name of the index
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function dropIndex($table, $name)
|
||||||
|
{
|
||||||
|
$res = $this->conn->query("ALTER TABLE $table DROP INDEX $name");
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a column to a table
|
||||||
|
*
|
||||||
|
* @param string $table name of the table
|
||||||
|
* @param ColumnDef $columndef Definition of the new
|
||||||
|
* column.
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function addColumn($table, $columndef)
|
||||||
|
{
|
||||||
|
$sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef);
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies a column in the schema.
|
||||||
|
*
|
||||||
|
* The name must match an existing column and table.
|
||||||
|
*
|
||||||
|
* @param string $table name of the table
|
||||||
|
* @param ColumnDef $columndef new definition of the column.
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function modifyColumn($table, $columndef)
|
||||||
|
{
|
||||||
|
$sql = "ALTER TABLE $table MODIFY COLUMN " .
|
||||||
|
$this->_columnSql($columndef);
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops a column from a table
|
||||||
|
*
|
||||||
|
* The name must match an existing column.
|
||||||
|
*
|
||||||
|
* @param string $table name of the table
|
||||||
|
* @param string $columnName name of the column to drop
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function dropColumn($table, $columnName)
|
||||||
|
{
|
||||||
|
$sql = "ALTER TABLE $table DROP COLUMN $columnName";
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that a table exists with the given
|
||||||
|
* name and the given column definitions.
|
||||||
|
*
|
||||||
|
* If the table does not yet exist, it will
|
||||||
|
* create the table. If it does exist, it will
|
||||||
|
* alter the table to match the column definitions.
|
||||||
|
*
|
||||||
|
* @param string $tableName name of the table
|
||||||
|
* @param array $columns array of ColumnDef
|
||||||
|
* objects for the table
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function ensureTable($tableName, $columns)
|
||||||
|
{
|
||||||
|
// XXX: DB engine portability -> toilet
|
||||||
|
|
||||||
|
try {
|
||||||
|
$td = $this->getTableDef($tableName);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (preg_match('/no such table/', $e->getMessage())) {
|
||||||
|
return $this->createTable($tableName, $columns);
|
||||||
|
} else {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cur = $this->_names($td->columns);
|
||||||
|
$new = $this->_names($columns);
|
||||||
|
|
||||||
|
$toadd = array_diff($new, $cur);
|
||||||
|
$todrop = array_diff($cur, $new);
|
||||||
|
$same = array_intersect($new, $cur);
|
||||||
|
$tomod = array();
|
||||||
|
|
||||||
|
foreach ($same as $m) {
|
||||||
|
$curCol = $this->_byName($td->columns, $m);
|
||||||
|
$newCol = $this->_byName($columns, $m);
|
||||||
|
|
||||||
|
if (!$newCol->equals($curCol)) {
|
||||||
|
$tomod[] = $newCol->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($toadd) + count($todrop) + count($tomod) == 0) {
|
||||||
|
// nothing to do
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For efficiency, we want this all in one
|
||||||
|
// query, instead of using our methods.
|
||||||
|
|
||||||
|
$phrase = array();
|
||||||
|
|
||||||
|
foreach ($toadd as $columnName) {
|
||||||
|
$cd = $this->_byName($columns, $columnName);
|
||||||
|
|
||||||
|
$phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($todrop as $columnName) {
|
||||||
|
$phrase[] = 'DROP COLUMN ' . $columnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($tomod as $columnName) {
|
||||||
|
$cd = $this->_byName($columns, $columnName);
|
||||||
|
|
||||||
|
$phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
|
||||||
|
|
||||||
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
throw new Exception($res->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the array of names from an array of
|
||||||
|
* ColumnDef objects.
|
||||||
|
*
|
||||||
|
* @param array $cds array of ColumnDef objects
|
||||||
|
*
|
||||||
|
* @return array strings for name values
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function _names($cds)
|
||||||
|
{
|
||||||
|
$names = array();
|
||||||
|
|
||||||
|
foreach ($cds as $cd) {
|
||||||
|
$names[] = $cd->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $names;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a ColumnDef from an array matching
|
||||||
|
* name.
|
||||||
|
*
|
||||||
|
* @param array $cds Array of ColumnDef objects
|
||||||
|
* @param string $name Name of the column
|
||||||
|
*
|
||||||
|
* @return ColumnDef matching item or null if no match.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function _byName($cds, $name)
|
||||||
|
{
|
||||||
|
foreach ($cds as $cd) {
|
||||||
|
if ($cd->name == $name) {
|
||||||
|
return $cd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the proper SQL for creating or
|
||||||
|
* altering a column.
|
||||||
|
*
|
||||||
|
* Appropriate for use in CREATE TABLE or
|
||||||
|
* ALTER TABLE statements.
|
||||||
|
*
|
||||||
|
* @param ColumnDef $cd column to create
|
||||||
|
*
|
||||||
|
* @return string correct SQL for that column
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function _columnSql($cd)
|
||||||
|
{
|
||||||
|
$sql = "{$cd->name} ";
|
||||||
|
|
||||||
|
if (!empty($cd->size)) {
|
||||||
|
$sql .= "{$cd->type}({$cd->size}) ";
|
||||||
|
} else {
|
||||||
|
$sql .= "{$cd->type} ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($cd->default)) {
|
||||||
|
$sql .= "default {$cd->default} ";
|
||||||
|
} else {
|
||||||
|
$sql .= ($cd->nullable) ? "null " : "not null ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($cd->auto_increment)) {
|
||||||
|
$sql .= " auto_increment ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($cd->extra)) {
|
||||||
|
$sql .= "{$cd->extra} ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sql;
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,7 @@ class PublicQueueHandler extends QueueHandler
|
|||||||
try {
|
try {
|
||||||
return jabber_public_notice($notice);
|
return jabber_public_notice($notice);
|
||||||
} catch (XMPPHP_Exception $e) {
|
} catch (XMPPHP_Exception $e) {
|
||||||
$this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
|
common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,8 @@ class Router
|
|||||||
|
|
||||||
if (Event::handle('StartInitializeRouter', array(&$m))) {
|
if (Event::handle('StartInitializeRouter', array(&$m))) {
|
||||||
|
|
||||||
|
$m->connect('robots.txt', array('action' => 'robotstxt'));
|
||||||
|
|
||||||
$m->connect('opensearch/people', array('action' => 'opensearch',
|
$m->connect('opensearch/people', array('action' => 'opensearch',
|
||||||
'type' => 'people'));
|
'type' => 'people'));
|
||||||
$m->connect('opensearch/notice', array('action' => 'opensearch',
|
$m->connect('opensearch/notice', array('action' => 'opensearch',
|
||||||
@ -649,7 +651,16 @@ class Router
|
|||||||
|
|
||||||
if (common_config('singleuser', 'enabled')) {
|
if (common_config('singleuser', 'enabled')) {
|
||||||
|
|
||||||
$nickname = common_config('singleuser', 'nickname');
|
$user = User::siteOwner();
|
||||||
|
|
||||||
|
if (!empty($user)) {
|
||||||
|
$nickname = $user->nickname;
|
||||||
|
} else {
|
||||||
|
$nickname = common_config('singleuser', 'nickname');
|
||||||
|
if (empty($nickname)) {
|
||||||
|
throw new ServerException(_("No single user defined for single-user mode."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (array('subscriptions', 'subscribers',
|
foreach (array('subscriptions', 'subscribers',
|
||||||
'all', 'foaf', 'xrds',
|
'all', 'foaf', 'xrds',
|
||||||
@ -697,6 +708,10 @@ class Router
|
|||||||
'nickname' => $nickname),
|
'nickname' => $nickname),
|
||||||
array('tag' => '[a-zA-Z0-9]+'));
|
array('tag' => '[a-zA-Z0-9]+'));
|
||||||
|
|
||||||
|
$m->connect('rsd.xml',
|
||||||
|
array('action' => 'rsd',
|
||||||
|
'nickname' => $nickname));
|
||||||
|
|
||||||
$m->connect('',
|
$m->connect('',
|
||||||
array('action' => 'showstream',
|
array('action' => 'showstream',
|
||||||
'nickname' => $nickname));
|
'nickname' => $nickname));
|
||||||
@ -711,6 +726,7 @@ class Router
|
|||||||
$m->connect('featured', array('action' => 'featured'));
|
$m->connect('featured', array('action' => 'featured'));
|
||||||
$m->connect('favorited/', array('action' => 'favorited'));
|
$m->connect('favorited/', array('action' => 'favorited'));
|
||||||
$m->connect('favorited', array('action' => 'favorited'));
|
$m->connect('favorited', array('action' => 'favorited'));
|
||||||
|
$m->connect('rsd.xml', array('action' => 'rsd'));
|
||||||
|
|
||||||
foreach (array('subscriptions', 'subscribers',
|
foreach (array('subscriptions', 'subscribers',
|
||||||
'nudge', 'all', 'foaf', 'xrds',
|
'nudge', 'all', 'foaf', 'xrds',
|
||||||
@ -758,6 +774,10 @@ class Router
|
|||||||
array('nickname' => '[a-zA-Z0-9]{1,64}'),
|
array('nickname' => '[a-zA-Z0-9]{1,64}'),
|
||||||
array('tag' => '[a-zA-Z0-9]+'));
|
array('tag' => '[a-zA-Z0-9]+'));
|
||||||
|
|
||||||
|
$m->connect(':nickname/rsd.xml',
|
||||||
|
array('action' => 'rsd'),
|
||||||
|
array('nickname' => '[a-zA-Z0-9]{1,64}'));
|
||||||
|
|
||||||
$m->connect(':nickname',
|
$m->connect(':nickname',
|
||||||
array('action' => 'showstream'),
|
array('action' => 'showstream'),
|
||||||
array('nickname' => '[a-zA-Z0-9]{1,64}'));
|
array('nickname' => '[a-zA-Z0-9]{1,64}'));
|
||||||
|
@ -75,64 +75,14 @@ class Schema
|
|||||||
|
|
||||||
static function get()
|
static function get()
|
||||||
{
|
{
|
||||||
|
$type = common_config('db', 'type');
|
||||||
if (empty(self::$_single)) {
|
if (empty(self::$_single)) {
|
||||||
self::$_single = new Schema();
|
$schemaClass = ucfirst($type).'Schema';
|
||||||
|
self::$_single = new $schemaClass();
|
||||||
}
|
}
|
||||||
return self::$_single;
|
return self::$_single;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a TableDef object for the table
|
|
||||||
* in the schema with the given name.
|
|
||||||
*
|
|
||||||
* Throws an exception if the table is not found.
|
|
||||||
*
|
|
||||||
* @param string $name Name of the table to get
|
|
||||||
*
|
|
||||||
* @return TableDef tabledef for that table.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function getTableDef($name)
|
|
||||||
{
|
|
||||||
$res = $this->conn->query('DESCRIBE ' . $name);
|
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
|
||||||
throw new Exception($res->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
$td = new TableDef();
|
|
||||||
|
|
||||||
$td->name = $name;
|
|
||||||
$td->columns = array();
|
|
||||||
|
|
||||||
$row = array();
|
|
||||||
|
|
||||||
while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
|
|
||||||
|
|
||||||
$cd = new ColumnDef();
|
|
||||||
|
|
||||||
$cd->name = $row['Field'];
|
|
||||||
|
|
||||||
$packed = $row['Type'];
|
|
||||||
|
|
||||||
if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) {
|
|
||||||
$cd->type = $match[1];
|
|
||||||
$cd->size = $match[2];
|
|
||||||
} else {
|
|
||||||
$cd->type = $packed;
|
|
||||||
}
|
|
||||||
|
|
||||||
$cd->nullable = ($row['Null'] == 'YES') ? true : false;
|
|
||||||
$cd->key = $row['Key'];
|
|
||||||
$cd->default = $row['Default'];
|
|
||||||
$cd->extra = $row['Extra'];
|
|
||||||
|
|
||||||
$td->columns[] = $cd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $td;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a ColumnDef object for a single column.
|
* Gets a ColumnDef object for a single column.
|
||||||
*
|
*
|
||||||
@ -523,7 +473,7 @@ class Schema
|
|||||||
} else {
|
} else {
|
||||||
$sql .= ($cd->nullable) ? "null " : "not null ";
|
$sql .= ($cd->nullable) ? "null " : "not null ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($cd->auto_increment)) {
|
if (!empty($cd->auto_increment)) {
|
||||||
$sql .= " auto_increment ";
|
$sql .= " auto_increment ";
|
||||||
}
|
}
|
||||||
|
@ -29,28 +29,36 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
require_once 'Stomp.php';
|
require_once 'Stomp.php';
|
||||||
|
require_once 'Stomp/Exception.php';
|
||||||
|
|
||||||
class StompQueueManager extends QueueManager
|
class StompQueueManager extends QueueManager
|
||||||
{
|
{
|
||||||
var $server = null;
|
protected $servers;
|
||||||
var $username = null;
|
protected $username;
|
||||||
var $password = null;
|
protected $password;
|
||||||
var $base = null;
|
protected $base;
|
||||||
var $con = null;
|
|
||||||
protected $control;
|
protected $control;
|
||||||
|
|
||||||
|
protected $useTransactions = true;
|
||||||
|
|
||||||
protected $sites = array();
|
protected $sites = array();
|
||||||
protected $subscriptions = array();
|
protected $subscriptions = array();
|
||||||
|
|
||||||
protected $useTransactions = true;
|
protected $cons = array(); // all open connections
|
||||||
protected $transaction = null;
|
protected $disconnect = array();
|
||||||
protected $transactionCount = 0;
|
protected $transaction = array();
|
||||||
|
protected $transactionCount = array();
|
||||||
|
protected $defaultIdx = 0;
|
||||||
|
|
||||||
function __construct()
|
function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->server = common_config('queue', 'stomp_server');
|
$server = common_config('queue', 'stomp_server');
|
||||||
|
if (is_array($server)) {
|
||||||
|
$this->servers = $server;
|
||||||
|
} else {
|
||||||
|
$this->servers = array($server);
|
||||||
|
}
|
||||||
$this->username = common_config('queue', 'stomp_username');
|
$this->username = common_config('queue', 'stomp_username');
|
||||||
$this->password = common_config('queue', 'stomp_password');
|
$this->password = common_config('queue', 'stomp_password');
|
||||||
$this->base = common_config('queue', 'queue_basename');
|
$this->base = common_config('queue', 'queue_basename');
|
||||||
@ -99,9 +107,9 @@ class StompQueueManager extends QueueManager
|
|||||||
$message .= ':' . $param;
|
$message .= ':' . $param;
|
||||||
}
|
}
|
||||||
$this->_connect();
|
$this->_connect();
|
||||||
$result = $this->con->send($this->control,
|
$result = $this->_send($this->control,
|
||||||
$message,
|
$message,
|
||||||
array ('created' => common_sql_now()));
|
array ('created' => common_sql_now()));
|
||||||
if ($result) {
|
if ($result) {
|
||||||
$this->_log(LOG_INFO, "Sent control ping to queue daemons: $message");
|
$this->_log(LOG_INFO, "Sent control ping to queue daemons: $message");
|
||||||
return true;
|
return true;
|
||||||
@ -166,28 +174,59 @@ class StompQueueManager extends QueueManager
|
|||||||
/**
|
/**
|
||||||
* Saves a notice object reference into the queue item table.
|
* Saves a notice object reference into the queue item table.
|
||||||
* @return boolean true on success
|
* @return boolean true on success
|
||||||
|
* @throws StompException on connection or send error
|
||||||
*/
|
*/
|
||||||
public function enqueue($object, $queue)
|
public function enqueue($object, $queue)
|
||||||
|
{
|
||||||
|
$this->_connect();
|
||||||
|
return $this->_doEnqueue($object, $queue, $this->defaultIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a notice object reference into the queue item table
|
||||||
|
* on the given connection.
|
||||||
|
*
|
||||||
|
* @return boolean true on success
|
||||||
|
* @throws StompException on connection or send error
|
||||||
|
*/
|
||||||
|
protected function _doEnqueue($object, $queue, $idx)
|
||||||
{
|
{
|
||||||
$msg = $this->encode($object);
|
$msg = $this->encode($object);
|
||||||
$rep = $this->logrep($object);
|
$rep = $this->logrep($object);
|
||||||
|
|
||||||
$this->_connect();
|
$props = array('created' => common_sql_now());
|
||||||
|
if ($this->isPersistent($queue)) {
|
||||||
|
$props['persistent'] = 'true';
|
||||||
|
}
|
||||||
|
|
||||||
// XXX: serialize and send entire notice
|
$con = $this->cons[$idx];
|
||||||
|
$host = $con->getServer();
|
||||||
$result = $this->con->send($this->queueName($queue),
|
$result = $con->send($this->queueName($queue), $msg, $props);
|
||||||
$msg, // BODY of the message
|
|
||||||
array ('created' => common_sql_now(),
|
|
||||||
'persistent' => 'true'));
|
|
||||||
|
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
common_log(LOG_ERR, "Error sending $rep to $queue queue");
|
common_log(LOG_ERR, "Error sending $rep to $queue queue on $host");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
common_log(LOG_DEBUG, "complete remote queueing $rep for $queue");
|
common_log(LOG_DEBUG, "complete remote queueing $rep for $queue on $host");
|
||||||
$this->stats('enqueued', $queue);
|
$this->stats('enqueued', $queue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether messages to this queue should be marked as persistent.
|
||||||
|
* Actual persistent storage depends on the queue server's configuration.
|
||||||
|
* @param string $queue
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isPersistent($queue)
|
||||||
|
{
|
||||||
|
$mode = common_config('queue', 'stomp_persistent');
|
||||||
|
if (is_array($mode)) {
|
||||||
|
return in_array($queue, $mode);
|
||||||
|
} else {
|
||||||
|
return (bool)$mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -198,7 +237,29 @@ class StompQueueManager extends QueueManager
|
|||||||
*/
|
*/
|
||||||
public function getSockets()
|
public function getSockets()
|
||||||
{
|
{
|
||||||
return array($this->con->getSocket());
|
$sockets = array();
|
||||||
|
foreach ($this->cons as $con) {
|
||||||
|
if ($con) {
|
||||||
|
$sockets[] = $con->getSocket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $sockets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Stomp connection object associated with the given socket.
|
||||||
|
* @param resource $socket
|
||||||
|
* @return int index into connections list
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function connectionFromSocket($socket)
|
||||||
|
{
|
||||||
|
foreach ($this->cons as $i => $con) {
|
||||||
|
if ($con && $con->getSocket() === $socket) {
|
||||||
|
return $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception(__CLASS__ . " asked to read from unrecognized socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -210,27 +271,56 @@ class StompQueueManager extends QueueManager
|
|||||||
*/
|
*/
|
||||||
public function handleInput($socket)
|
public function handleInput($socket)
|
||||||
{
|
{
|
||||||
assert($socket === $this->con->getSocket());
|
$idx = $this->connectionFromSocket($socket);
|
||||||
|
$con = $this->cons[$idx];
|
||||||
|
$host = $con->getServer();
|
||||||
|
|
||||||
$ok = true;
|
$ok = true;
|
||||||
$frames = $this->con->readFrames();
|
try {
|
||||||
|
$frames = $con->readFrames();
|
||||||
|
} catch (StompException $e) {
|
||||||
|
common_log(LOG_ERR, "Lost connection to $host: " . $e->getMessage());
|
||||||
|
$this->cons[$idx] = null;
|
||||||
|
$this->transaction[$idx] = null;
|
||||||
|
$this->disconnect[$idx] = time();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
foreach ($frames as $frame) {
|
foreach ($frames as $frame) {
|
||||||
$dest = $frame->headers['destination'];
|
$dest = $frame->headers['destination'];
|
||||||
if ($dest == $this->control) {
|
if ($dest == $this->control) {
|
||||||
if (!$this->handleControlSignal($frame)) {
|
if (!$this->handleControlSignal($idx, $frame)) {
|
||||||
// We got a control event that requests a shutdown;
|
// We got a control event that requests a shutdown;
|
||||||
// close out and stop handling anything else!
|
// close out and stop handling anything else!
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$ok = $ok && $this->handleItem($frame);
|
$ok = $ok && $this->handleItem($idx, $frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $ok;
|
return $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to reconnect in background if we lost a connection.
|
||||||
|
*/
|
||||||
|
function idle()
|
||||||
|
{
|
||||||
|
$now = time();
|
||||||
|
foreach ($this->cons as $idx => $con) {
|
||||||
|
if (empty($con)) {
|
||||||
|
$age = $now - $this->disconnect[$idx];
|
||||||
|
if ($age >= 60) {
|
||||||
|
$this->_reconnect($idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize our connection and subscribe to all the queues
|
* Initialize our connection and subscribe to all the queues
|
||||||
* we're going to need to handle...
|
* we're going to need to handle... If multiple queue servers
|
||||||
|
* are configured for failover, we'll listen to all of them.
|
||||||
*
|
*
|
||||||
* Side effects: in multi-site mode, may reset site configuration.
|
* Side effects: in multi-site mode, may reset site configuration.
|
||||||
*
|
*
|
||||||
@ -240,9 +330,14 @@ class StompQueueManager extends QueueManager
|
|||||||
public function start($master)
|
public function start($master)
|
||||||
{
|
{
|
||||||
parent::start($master);
|
parent::start($master);
|
||||||
$this->_connect();
|
$this->_connectAll();
|
||||||
|
|
||||||
$this->con->subscribe($this->control);
|
common_log(LOG_INFO, "Subscribing to $this->control");
|
||||||
|
foreach ($this->cons as $con) {
|
||||||
|
if ($con) {
|
||||||
|
$con->subscribe($this->control);
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($this->sites) {
|
if ($this->sites) {
|
||||||
foreach ($this->sites as $server) {
|
foreach ($this->sites as $server) {
|
||||||
StatusNet::init($server);
|
StatusNet::init($server);
|
||||||
@ -251,10 +346,14 @@ class StompQueueManager extends QueueManager
|
|||||||
} else {
|
} else {
|
||||||
$this->doSubscribe();
|
$this->doSubscribe();
|
||||||
}
|
}
|
||||||
$this->begin();
|
foreach ($this->cons as $i => $con) {
|
||||||
|
if ($con) {
|
||||||
|
$this->begin($i);
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to all the queues we're going to need to handle...
|
* Subscribe to all the queues we're going to need to handle...
|
||||||
*
|
*
|
||||||
@ -266,8 +365,12 @@ class StompQueueManager extends QueueManager
|
|||||||
{
|
{
|
||||||
// If there are any outstanding delivered messages we haven't processed,
|
// If there are any outstanding delivered messages we haven't processed,
|
||||||
// free them for another thread to take.
|
// free them for another thread to take.
|
||||||
$this->rollback();
|
foreach ($this->cons as $i => $con) {
|
||||||
$this->con->unsubscribe($this->control);
|
if ($con) {
|
||||||
|
$this->rollback($i);
|
||||||
|
$con->unsubscribe($this->control);
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($this->sites) {
|
if ($this->sites) {
|
||||||
foreach ($this->sites as $server) {
|
foreach ($this->sites as $server) {
|
||||||
StatusNet::init($server);
|
StatusNet::init($server);
|
||||||
@ -289,23 +392,106 @@ class StompQueueManager extends QueueManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazy open connection to Stomp queue server.
|
* Lazy open a single connection to Stomp queue server.
|
||||||
|
* If multiple servers are configured, we let the Stomp client library
|
||||||
|
* worry about finding a working connection among them.
|
||||||
*/
|
*/
|
||||||
protected function _connect()
|
protected function _connect()
|
||||||
{
|
{
|
||||||
if (empty($this->con)) {
|
if (empty($this->cons)) {
|
||||||
$this->_log(LOG_INFO, "Connecting to '$this->server' as '$this->username'...");
|
$list = $this->servers;
|
||||||
$this->con = new LiberalStomp($this->server);
|
if (count($list) > 1) {
|
||||||
|
shuffle($list); // Randomize to spread load
|
||||||
if ($this->con->connect($this->username, $this->password)) {
|
$url = 'failover://(' . implode(',', $list) . ')';
|
||||||
$this->_log(LOG_INFO, "Connected.");
|
|
||||||
} else {
|
} else {
|
||||||
$this->_log(LOG_ERR, 'Failed to connect to queue server');
|
$url = $list[0];
|
||||||
throw new ServerException('Failed to connect to queue server');
|
}
|
||||||
|
$con = $this->_doConnect($url);
|
||||||
|
$this->cons = array($con);
|
||||||
|
$this->transactionCount = array(0);
|
||||||
|
$this->transaction = array(null);
|
||||||
|
$this->disconnect = array(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazy open connections to all Stomp servers, if in manual failover
|
||||||
|
* mode. This means the queue servers don't speak to each other, so
|
||||||
|
* we have to listen to all of them to make sure we get all events.
|
||||||
|
*/
|
||||||
|
protected function _connectAll()
|
||||||
|
{
|
||||||
|
if (!common_config('queue', 'stomp_manual_failover')) {
|
||||||
|
return $this->_connect();
|
||||||
|
}
|
||||||
|
if (empty($this->cons)) {
|
||||||
|
$this->cons = array();
|
||||||
|
$this->transactionCount = array();
|
||||||
|
$this->transaction = array();
|
||||||
|
foreach ($this->servers as $idx => $server) {
|
||||||
|
try {
|
||||||
|
$this->cons[] = $this->_doConnect($server);
|
||||||
|
$this->disconnect[] = null;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// s'okay, we'll live
|
||||||
|
$this->cons[] = null;
|
||||||
|
$this->disconnect[] = time();
|
||||||
|
}
|
||||||
|
$this->transactionCount[] = 0;
|
||||||
|
$this->transaction[] = null;
|
||||||
|
}
|
||||||
|
if (empty($this->cons)) {
|
||||||
|
throw new ServerException("No queue servers reachable...");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function _reconnect($idx)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$con = $this->_doConnect($this->servers[$idx]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->_log(LOG_ERR, $e->getMessage());
|
||||||
|
$con = null;
|
||||||
|
}
|
||||||
|
if ($con) {
|
||||||
|
$this->cons[$idx] = $con;
|
||||||
|
$this->disconnect[$idx] = null;
|
||||||
|
|
||||||
|
// now we have to listen to everything...
|
||||||
|
// @fixme refactor this nicer. :P
|
||||||
|
$host = $con->getServer();
|
||||||
|
$this->_log(LOG_INFO, "Resubscribing to $this->control on $host");
|
||||||
|
$con->subscribe($this->control);
|
||||||
|
foreach ($this->subscriptions as $site => $queues) {
|
||||||
|
foreach ($queues as $queue) {
|
||||||
|
$this->_log(LOG_INFO, "Resubscribing to $queue on $host");
|
||||||
|
$con->subscribe($queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->begin($idx);
|
||||||
|
} else {
|
||||||
|
// Try again later...
|
||||||
|
$this->disconnect[$idx] = time();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _doConnect($server)
|
||||||
|
{
|
||||||
|
$this->_log(LOG_INFO, "Connecting to '$server' as '$this->username'...");
|
||||||
|
$con = new LiberalStomp($server);
|
||||||
|
|
||||||
|
if ($con->connect($this->username, $this->password)) {
|
||||||
|
$this->_log(LOG_INFO, "Connected.");
|
||||||
|
} else {
|
||||||
|
$this->_log(LOG_ERR, 'Failed to connect to queue server');
|
||||||
|
throw new ServerException('Failed to connect to queue server');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $con;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to all enabled notice queues for the current site.
|
* Subscribe to all enabled notice queues for the current site.
|
||||||
*/
|
*/
|
||||||
@ -317,7 +503,11 @@ class StompQueueManager extends QueueManager
|
|||||||
$rawqueue = $this->queueName($queue);
|
$rawqueue = $this->queueName($queue);
|
||||||
$this->subscriptions[$site][$queue] = $rawqueue;
|
$this->subscriptions[$site][$queue] = $rawqueue;
|
||||||
$this->_log(LOG_INFO, "Subscribing to $rawqueue");
|
$this->_log(LOG_INFO, "Subscribing to $rawqueue");
|
||||||
$this->con->subscribe($rawqueue);
|
foreach ($this->cons as $con) {
|
||||||
|
if ($con) {
|
||||||
|
$con->subscribe($rawqueue);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +521,11 @@ class StompQueueManager extends QueueManager
|
|||||||
if (!empty($this->subscriptions[$site])) {
|
if (!empty($this->subscriptions[$site])) {
|
||||||
foreach ($this->subscriptions[$site] as $queue => $rawqueue) {
|
foreach ($this->subscriptions[$site] as $queue => $rawqueue) {
|
||||||
$this->_log(LOG_INFO, "Unsubscribing from $rawqueue");
|
$this->_log(LOG_INFO, "Unsubscribing from $rawqueue");
|
||||||
$this->con->unsubscribe($rawqueue);
|
foreach ($this->cons as $con) {
|
||||||
|
if ($con) {
|
||||||
|
$con->unsubscribe($rawqueue);
|
||||||
|
}
|
||||||
|
}
|
||||||
unset($this->subscriptions[$site][$queue]);
|
unset($this->subscriptions[$site][$queue]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,27 +540,31 @@ class StompQueueManager extends QueueManager
|
|||||||
* Side effects: in multi-site mode, may reset site configuration to
|
* Side effects: in multi-site mode, may reset site configuration to
|
||||||
* match the site that queued the event.
|
* match the site that queued the event.
|
||||||
*
|
*
|
||||||
|
* @param int $idx connection index
|
||||||
* @param StompFrame $frame
|
* @param StompFrame $frame
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function handleItem($frame)
|
protected function handleItem($idx, $frame)
|
||||||
{
|
{
|
||||||
|
$this->defaultIdx = $idx;
|
||||||
|
|
||||||
list($site, $queue) = $this->parseDestination($frame->headers['destination']);
|
list($site, $queue) = $this->parseDestination($frame->headers['destination']);
|
||||||
if ($site != $this->currentSite()) {
|
if ($site != $this->currentSite()) {
|
||||||
$this->stats('switch');
|
$this->stats('switch');
|
||||||
StatusNet::init($site);
|
StatusNet::init($site);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$host = $this->cons[$idx]->getServer();
|
||||||
if (is_numeric($frame->body)) {
|
if (is_numeric($frame->body)) {
|
||||||
$id = intval($frame->body);
|
$id = intval($frame->body);
|
||||||
$info = "notice $id posted at {$frame->headers['created']} in queue $queue";
|
$info = "notice $id posted at {$frame->headers['created']} in queue $queue from $host";
|
||||||
|
|
||||||
$notice = Notice::staticGet('id', $id);
|
$notice = Notice::staticGet('id', $id);
|
||||||
if (empty($notice)) {
|
if (empty($notice)) {
|
||||||
$this->_log(LOG_WARNING, "Skipping missing $info");
|
$this->_log(LOG_WARNING, "Skipping missing $info");
|
||||||
$this->ack($frame);
|
$this->ack($idx, $frame);
|
||||||
$this->commit();
|
$this->commit($idx);
|
||||||
$this->begin();
|
$this->begin($idx);
|
||||||
$this->stats('badnotice', $queue);
|
$this->stats('badnotice', $queue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -374,39 +572,47 @@ class StompQueueManager extends QueueManager
|
|||||||
$item = $notice;
|
$item = $notice;
|
||||||
} else {
|
} else {
|
||||||
// @fixme should we serialize, or json, or what here?
|
// @fixme should we serialize, or json, or what here?
|
||||||
$info = "string posted at {$frame->headers['created']} in queue $queue";
|
$info = "string posted at {$frame->headers['created']} in queue $queue from $host";
|
||||||
$item = $frame->body;
|
$item = $frame->body;
|
||||||
}
|
}
|
||||||
|
|
||||||
$handler = $this->getHandler($queue);
|
$handler = $this->getHandler($queue);
|
||||||
if (!$handler) {
|
if (!$handler) {
|
||||||
$this->_log(LOG_ERR, "Missing handler class; skipping $info");
|
$this->_log(LOG_ERR, "Missing handler class; skipping $info");
|
||||||
$this->ack($frame);
|
$this->ack($idx, $frame);
|
||||||
$this->commit();
|
$this->commit($idx);
|
||||||
$this->begin();
|
$this->begin($idx);
|
||||||
$this->stats('badhandler', $queue);
|
$this->stats('badhandler', $queue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ok = $handler->handle($item);
|
// If there's an exception when handling,
|
||||||
|
// log the error and let it get requeued.
|
||||||
|
|
||||||
|
try {
|
||||||
|
$ok = $handler->handle($item);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->_log(LOG_ERR, "Exception on queue $queue: " . $e->getMessage());
|
||||||
|
$ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$ok) {
|
if (!$ok) {
|
||||||
$this->_log(LOG_WARNING, "Failed handling $info");
|
$this->_log(LOG_WARNING, "Failed handling $info");
|
||||||
// FIXME we probably shouldn't have to do
|
// FIXME we probably shouldn't have to do
|
||||||
// this kind of queue management ourselves;
|
// this kind of queue management ourselves;
|
||||||
// if we don't ack, it should resend...
|
// if we don't ack, it should resend...
|
||||||
$this->ack($frame);
|
$this->ack($idx, $frame);
|
||||||
$this->enqueue($item, $queue);
|
$this->enqueue($item, $queue);
|
||||||
$this->commit();
|
$this->commit($idx);
|
||||||
$this->begin();
|
$this->begin($idx);
|
||||||
$this->stats('requeued', $queue);
|
$this->stats('requeued', $queue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_log(LOG_INFO, "Successfully handled $info");
|
$this->_log(LOG_INFO, "Successfully handled $info");
|
||||||
$this->ack($frame);
|
$this->ack($idx, $frame);
|
||||||
$this->commit();
|
$this->commit($idx);
|
||||||
$this->begin();
|
$this->begin($idx);
|
||||||
$this->stats('handled', $queue);
|
$this->stats('handled', $queue);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -414,10 +620,11 @@ class StompQueueManager extends QueueManager
|
|||||||
/**
|
/**
|
||||||
* Process a control signal broadcast.
|
* Process a control signal broadcast.
|
||||||
*
|
*
|
||||||
|
* @param int $idx connection index
|
||||||
* @param array $frame Stomp frame
|
* @param array $frame Stomp frame
|
||||||
* @return bool true to continue; false to stop further processing.
|
* @return bool true to continue; false to stop further processing.
|
||||||
*/
|
*/
|
||||||
protected function handleControlSignal($frame)
|
protected function handleControlSignal($idx, $frame)
|
||||||
{
|
{
|
||||||
$message = trim($frame->body);
|
$message = trim($frame->body);
|
||||||
if (strpos($message, ':') !== false) {
|
if (strpos($message, ':') !== false) {
|
||||||
@ -441,12 +648,12 @@ class StompQueueManager extends QueueManager
|
|||||||
$this->_log(LOG_ERR, "Ignoring unrecognized control message: $message");
|
$this->_log(LOG_ERR, "Ignoring unrecognized control message: $message");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->ack($frame);
|
$this->ack($idx, $frame);
|
||||||
$this->commit();
|
$this->commit($idx);
|
||||||
$this->begin();
|
$this->begin($idx);
|
||||||
return $shutdown;
|
return $shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set us up with queue subscriptions for a new site added at runtime,
|
* Set us up with queue subscriptions for a new site added at runtime,
|
||||||
* triggered by a broadcast to the 'statusnet-control' topic.
|
* triggered by a broadcast to the 'statusnet-control' topic.
|
||||||
@ -520,47 +727,49 @@ class StompQueueManager extends QueueManager
|
|||||||
common_log($level, 'StompQueueManager: '.$msg);
|
common_log($level, 'StompQueueManager: '.$msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function begin()
|
protected function begin($idx)
|
||||||
{
|
{
|
||||||
if ($this->useTransactions) {
|
if ($this->useTransactions) {
|
||||||
if ($this->transaction) {
|
if (!empty($this->transaction[$idx])) {
|
||||||
throw new Exception("Tried to start transaction in the middle of a transaction");
|
throw new Exception("Tried to start transaction in the middle of a transaction");
|
||||||
}
|
}
|
||||||
$this->transactionCount++;
|
$this->transactionCount[$idx]++;
|
||||||
$this->transaction = $this->master->id . '-' . $this->transactionCount . '-' . time();
|
$this->transaction[$idx] = $this->master->id . '-' . $this->transactionCount[$idx] . '-' . time();
|
||||||
$this->con->begin($this->transaction);
|
$this->cons[$idx]->begin($this->transaction[$idx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function ack($frame)
|
protected function ack($idx, $frame)
|
||||||
{
|
{
|
||||||
if ($this->useTransactions) {
|
if ($this->useTransactions) {
|
||||||
if (!$this->transaction) {
|
if (empty($this->transaction[$idx])) {
|
||||||
throw new Exception("Tried to ack but not in a transaction");
|
throw new Exception("Tried to ack but not in a transaction");
|
||||||
}
|
}
|
||||||
|
$this->cons[$idx]->ack($frame, $this->transaction[$idx]);
|
||||||
|
} else {
|
||||||
|
$this->cons[$idx]->ack($frame);
|
||||||
}
|
}
|
||||||
$this->con->ack($frame, $this->transaction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function commit()
|
protected function commit($idx)
|
||||||
{
|
{
|
||||||
if ($this->useTransactions) {
|
if ($this->useTransactions) {
|
||||||
if (!$this->transaction) {
|
if (empty($this->transaction[$idx])) {
|
||||||
throw new Exception("Tried to commit but not in a transaction");
|
throw new Exception("Tried to commit but not in a transaction");
|
||||||
}
|
}
|
||||||
$this->con->commit($this->transaction);
|
$this->cons[$idx]->commit($this->transaction[$idx]);
|
||||||
$this->transaction = null;
|
$this->transaction[$idx] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function rollback()
|
protected function rollback($idx)
|
||||||
{
|
{
|
||||||
if ($this->useTransactions) {
|
if ($this->useTransactions) {
|
||||||
if (!$this->transaction) {
|
if (empty($this->transaction[$idx])) {
|
||||||
throw new Exception("Tried to rollback but not in a transaction");
|
throw new Exception("Tried to rollback but not in a transaction");
|
||||||
}
|
}
|
||||||
$this->con->commit($this->transaction);
|
$this->cons[$idx]->commit($this->transaction[$idx]);
|
||||||
$this->transaction = null;
|
$this->transaction[$idx] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,6 @@ function common_ensure_session()
|
|||||||
}
|
}
|
||||||
if (isset($id)) {
|
if (isset($id)) {
|
||||||
session_id($id);
|
session_id($id);
|
||||||
setcookie(session_name(), $id);
|
|
||||||
}
|
}
|
||||||
@session_start();
|
@session_start();
|
||||||
if (!isset($_SESSION['started'])) {
|
if (!isset($_SESSION['started'])) {
|
||||||
|
160
plugins/Adsense/AdsensePlugin.php
Normal file
160
plugins/Adsense/AdsensePlugin.php
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Plugin for Google Adsense
|
||||||
|
*
|
||||||
|
* 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 Ads
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin to add Google Adsense to StatusNet sites
|
||||||
|
*
|
||||||
|
* This plugin lets you add Adsense ad units to your StatusNet site.
|
||||||
|
*
|
||||||
|
* We support the 4 ad sizes for the Universal Ad Platform (UAP):
|
||||||
|
*
|
||||||
|
* Medium Rectangle
|
||||||
|
* (Small) Rectangle
|
||||||
|
* Leaderboard
|
||||||
|
* Wide Skyscraper
|
||||||
|
*
|
||||||
|
* They fit in different places on the default theme. Some themes
|
||||||
|
* might interact quite poorly with this plugin.
|
||||||
|
*
|
||||||
|
* To enable advertising, you must sign up with Google Adsense and
|
||||||
|
* get a client ID.
|
||||||
|
*
|
||||||
|
* https://www.google.com/adsense/
|
||||||
|
*
|
||||||
|
* You'll also need to create an Adsense for Content unit in one
|
||||||
|
* of the four sizes described above. At the end of the process,
|
||||||
|
* note the "google_ad_client" and "google_ad_slot" values in the
|
||||||
|
* resultant Javascript.
|
||||||
|
*
|
||||||
|
* Add the plugin to config.php like so:
|
||||||
|
*
|
||||||
|
* addPlugin('Adsense', array('client' => 'Your client ID',
|
||||||
|
* 'rectangle' => 'slot'));
|
||||||
|
*
|
||||||
|
* Here, your client ID is the value of google_ad_client and the
|
||||||
|
* slot is the value of google_ad_slot. Note that if you create
|
||||||
|
* a different size, you'll need to provide different arguments:
|
||||||
|
* 'mediumRectangle', 'leaderboard', or 'wideSkyscraper'.
|
||||||
|
*
|
||||||
|
* If for some reason your ad server is different from the default,
|
||||||
|
* use the 'adScript' parameter to set the full path to the ad script.
|
||||||
|
*
|
||||||
|
* @category Plugin
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*
|
||||||
|
* @seeAlso UAPPlugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AdsensePlugin extends UAPPlugin
|
||||||
|
{
|
||||||
|
public $adScript = 'http://pagead2.googlesyndication.com/pagead/show_ads.js';
|
||||||
|
public $client = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a medium rectangle 'ad'
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showMediumRectangle($action)
|
||||||
|
{
|
||||||
|
$this->showAdsenseCode($action, 300, 250, $this->mediumRectangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a rectangle 'ad'
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showRectangle($action)
|
||||||
|
{
|
||||||
|
$this->showAdsenseCode($action, 180, 150, $this->rectangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a wide skyscraper ad
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showWideSkyscraper($action)
|
||||||
|
{
|
||||||
|
$this->showAdsenseCode($action, 160, 600, $this->wideSkyscraper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a leaderboard ad
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showLeaderboard($action)
|
||||||
|
{
|
||||||
|
$this->showAdsenseCode($action, 728, 90, $this->leaderboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output the bits of JavaScript code to show Adsense
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
* @param integer $width Width of the block
|
||||||
|
* @param integer $height Height of the block
|
||||||
|
* @param string $slot Slot identifier
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showAdsenseCode($action, $width, $height, $slot)
|
||||||
|
{
|
||||||
|
$code = 'google_ad_client = "'.$this->client.'"; ';
|
||||||
|
$code .= 'google_ad_slot = "'.$slot.'"; ';
|
||||||
|
$code .= 'google_ad_width = '.$width.'; ';
|
||||||
|
$code .= 'google_ad_height = '.$height.'; ';
|
||||||
|
|
||||||
|
$action->inlineScript($code);
|
||||||
|
|
||||||
|
$action->script($this->adScript);
|
||||||
|
}
|
||||||
|
}
|
@ -71,7 +71,7 @@ class GeonamesPlugin extends Plugin
|
|||||||
$loc = $this->getCache(array('name' => $name,
|
$loc = $this->getCache(array('name' => $name,
|
||||||
'language' => $language));
|
'language' => $language));
|
||||||
|
|
||||||
if (!empty($loc)) {
|
if ($loc !== false) {
|
||||||
$location = $loc;
|
$location = $loc;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -87,12 +87,20 @@ class GeonamesPlugin extends Plugin
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (count($geonames) == 0) {
|
||||||
|
// no results
|
||||||
|
$this->setCache(array('name' => $name,
|
||||||
|
'language' => $language),
|
||||||
|
null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$n = $geonames[0];
|
$n = $geonames[0];
|
||||||
|
|
||||||
$location = new Location();
|
$location = new Location();
|
||||||
|
|
||||||
$location->lat = (string)$n->lat;
|
$location->lat = $this->canonical($n->lat);
|
||||||
$location->lon = (string)$n->lng;
|
$location->lon = $this->canonical($n->lng);
|
||||||
$location->names[$language] = (string)$n->name;
|
$location->names[$language] = (string)$n->name;
|
||||||
$location->location_id = (string)$n->geonameId;
|
$location->location_id = (string)$n->geonameId;
|
||||||
$location->location_ns = self::LOCATION_NS;
|
$location->location_ns = self::LOCATION_NS;
|
||||||
@ -125,7 +133,7 @@ class GeonamesPlugin extends Plugin
|
|||||||
|
|
||||||
$loc = $this->getCache(array('id' => $id));
|
$loc = $this->getCache(array('id' => $id));
|
||||||
|
|
||||||
if (!empty($loc)) {
|
if ($loc !== false) {
|
||||||
$location = $loc;
|
$location = $loc;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -157,8 +165,9 @@ class GeonamesPlugin extends Plugin
|
|||||||
|
|
||||||
$location->location_id = (string)$last->geonameId;
|
$location->location_id = (string)$last->geonameId;
|
||||||
$location->location_ns = self::LOCATION_NS;
|
$location->location_ns = self::LOCATION_NS;
|
||||||
$location->lat = (string)$last->lat;
|
$location->lat = $this->canonical($last->lat);
|
||||||
$location->lon = (string)$last->lng;
|
$location->lon = $this->canonical($last->lng);
|
||||||
|
|
||||||
$location->names[$language] = implode(', ', array_reverse($parts));
|
$location->names[$language] = implode(', ', array_reverse($parts));
|
||||||
|
|
||||||
$this->setCache(array('id' => (string)$last->geonameId),
|
$this->setCache(array('id' => (string)$last->geonameId),
|
||||||
@ -186,13 +195,15 @@ class GeonamesPlugin extends Plugin
|
|||||||
|
|
||||||
function onLocationFromLatLon($lat, $lon, $language, &$location)
|
function onLocationFromLatLon($lat, $lon, $language, &$location)
|
||||||
{
|
{
|
||||||
$lat = rtrim($lat, "0");
|
// Make sure they're canonical
|
||||||
$lon = rtrim($lon, "0");
|
|
||||||
|
$lat = $this->canonical($lat);
|
||||||
|
$lon = $this->canonical($lon);
|
||||||
|
|
||||||
$loc = $this->getCache(array('lat' => $lat,
|
$loc = $this->getCache(array('lat' => $lat,
|
||||||
'lon' => $lon));
|
'lon' => $lon));
|
||||||
|
|
||||||
if (!empty($loc)) {
|
if ($loc !== false) {
|
||||||
$location = $loc;
|
$location = $loc;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -207,6 +218,14 @@ class GeonamesPlugin extends Plugin
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (count($geonames) == 0) {
|
||||||
|
// no results
|
||||||
|
$this->setCache(array('lat' => $lat,
|
||||||
|
'lon' => $lon),
|
||||||
|
null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$n = $geonames[0];
|
$n = $geonames[0];
|
||||||
|
|
||||||
$parts = array();
|
$parts = array();
|
||||||
@ -225,8 +244,8 @@ class GeonamesPlugin extends Plugin
|
|||||||
|
|
||||||
$location->location_id = (string)$n->geonameId;
|
$location->location_id = (string)$n->geonameId;
|
||||||
$location->location_ns = self::LOCATION_NS;
|
$location->location_ns = self::LOCATION_NS;
|
||||||
$location->lat = (string)$lat;
|
$location->lat = $this->canonical($n->lat);
|
||||||
$location->lon = (string)$lon;
|
$location->lon = $this->canonical($n->lng);
|
||||||
|
|
||||||
$location->names[$language] = implode(', ', $parts);
|
$location->names[$language] = implode(', ', $parts);
|
||||||
|
|
||||||
@ -264,7 +283,7 @@ class GeonamesPlugin extends Plugin
|
|||||||
$n = $this->getCache(array('id' => $id,
|
$n = $this->getCache(array('id' => $id,
|
||||||
'language' => $language));
|
'language' => $language));
|
||||||
|
|
||||||
if (!empty($n)) {
|
if ($n !== false) {
|
||||||
$name = $n;
|
$name = $n;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -278,6 +297,13 @@ class GeonamesPlugin extends Plugin
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (count($geonames) == 0) {
|
||||||
|
$this->setCache(array('id' => $id,
|
||||||
|
'language' => $language),
|
||||||
|
null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$parts = array();
|
$parts = array();
|
||||||
|
|
||||||
foreach ($geonames as $level) {
|
foreach ($geonames as $level) {
|
||||||
@ -412,17 +438,29 @@ class GeonamesPlugin extends Plugin
|
|||||||
throw new Exception("HTTP error code " . $result->code);
|
throw new Exception("HTTP error code " . $result->code);
|
||||||
}
|
}
|
||||||
|
|
||||||
$document = new SimpleXMLElement($result->getBody());
|
$body = $result->getBody();
|
||||||
|
|
||||||
if (empty($document)) {
|
if (empty($body)) {
|
||||||
throw new Exception("No results in response");
|
throw new Exception("Empty HTTP body in response");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will throw an exception if the XML is mal-formed
|
||||||
|
|
||||||
|
$document = new SimpleXMLElement($body);
|
||||||
|
|
||||||
|
// No children, usually no results
|
||||||
|
|
||||||
|
$children = $document->children();
|
||||||
|
|
||||||
|
if (count($children) == 0) {
|
||||||
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($document->status)) {
|
if (isset($document->status)) {
|
||||||
throw new Exception("Error #".$document->status['value']." ('".$document->status['message']."')");
|
throw new Exception("Error #".$document->status['value']." ('".$document->status['message']."')");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array of elements
|
// Array of elements, >0 elements
|
||||||
|
|
||||||
return $document->geoname;
|
return $document->geoname;
|
||||||
}
|
}
|
||||||
@ -438,4 +476,12 @@ class GeonamesPlugin extends Plugin
|
|||||||
'names for locations based on user-provided lat/long pairs.'));
|
'names for locations based on user-provided lat/long pairs.'));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canonical($coord)
|
||||||
|
{
|
||||||
|
$coord = rtrim($coord, "0");
|
||||||
|
$coord = rtrim($coord, ".");
|
||||||
|
|
||||||
|
return $coord;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,8 @@ class MemcachePlugin extends Plugin
|
|||||||
|
|
||||||
public $persistent = null;
|
public $persistent = null;
|
||||||
|
|
||||||
|
public $defaultExpiry = 86400; // 24h
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the plugin
|
* Initialize the plugin
|
||||||
*
|
*
|
||||||
@ -110,6 +112,9 @@ class MemcachePlugin extends Plugin
|
|||||||
function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success)
|
function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success)
|
||||||
{
|
{
|
||||||
$this->_ensureConn();
|
$this->_ensureConn();
|
||||||
|
if ($expiry === null) {
|
||||||
|
$expiry = $this->defaultExpiry;
|
||||||
|
}
|
||||||
$success = $this->_conn->set($key, $value, $flag, $expiry);
|
$success = $this->_conn->set($key, $value, $flag, $expiry);
|
||||||
Event::handle('EndCacheSet', array($key, $value, $flag,
|
Event::handle('EndCacheSet', array($key, $value, $flag,
|
||||||
$expiry));
|
$expiry));
|
||||||
|
165
plugins/OpenX/OpenXPlugin.php
Normal file
165
plugins/OpenX/OpenXPlugin.php
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Plugin for OpenX ad server
|
||||||
|
*
|
||||||
|
* 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 Ads
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin for OpenX Ad Server
|
||||||
|
*
|
||||||
|
* This plugin supports the OpenX ad server, http://www.openx.org/
|
||||||
|
*
|
||||||
|
* We support the 4 ad sizes for the Universal Ad Platform (UAP):
|
||||||
|
*
|
||||||
|
* Medium Rectangle
|
||||||
|
* (Small) Rectangle
|
||||||
|
* Leaderboard
|
||||||
|
* Wide Skyscraper
|
||||||
|
*
|
||||||
|
* They fit in different places on the default theme. Some themes
|
||||||
|
* might interact quite poorly with this plugin.
|
||||||
|
*
|
||||||
|
* To enable advertising, you will need an OpenX server. You'll need
|
||||||
|
* to set up a "zone" for your StatusNet site that identifies a
|
||||||
|
* kind of ad you want to place (of the above 4 sizes).
|
||||||
|
*
|
||||||
|
* Add the plugin to config.php like so:
|
||||||
|
*
|
||||||
|
* addPlugin('OpenX', array('adScript' => 'full path to script',
|
||||||
|
* 'rectangle' => 1));
|
||||||
|
*
|
||||||
|
* Here, the 'adScript' parameter is the full path to the OpenX
|
||||||
|
* ad script, like 'http://example.com/www/delivery/ajs.php'. Note
|
||||||
|
* that we don't do any magic to swap between HTTP and HTTPS, so
|
||||||
|
* if you want HTTPS, say so.
|
||||||
|
*
|
||||||
|
* The 'rectangle' parameter is the zone ID for that ad space on
|
||||||
|
* your site. If you've configured another size, try 'mediumRectangle',
|
||||||
|
* 'leaderboard', or 'wideSkyscraper'.
|
||||||
|
*
|
||||||
|
* If for some reason your ad server is different from the default,
|
||||||
|
* use the 'adScript' parameter to set the full path to the ad script.
|
||||||
|
*
|
||||||
|
* @category Ads
|
||||||
|
* @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/
|
||||||
|
*
|
||||||
|
* @seeAlso UAPPlugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
class OpenXPlugin extends UAPPlugin
|
||||||
|
{
|
||||||
|
public $adScript = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a medium rectangle 'ad'
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showMediumRectangle($action)
|
||||||
|
{
|
||||||
|
$this->showAd($action, $this->mediumRectangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a rectangle 'ad'
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showRectangle($action)
|
||||||
|
{
|
||||||
|
$this->showAd($action, $this->rectangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a wide skyscraper ad
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showWideSkyscraper($action)
|
||||||
|
{
|
||||||
|
$this->showAd($action, $this->wideSkyscraper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a leaderboard ad
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showLeaderboard($action)
|
||||||
|
{
|
||||||
|
$this->showAd($action, $this->leaderboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show an ad using OpenX
|
||||||
|
*
|
||||||
|
* @param Action $action Action being shown
|
||||||
|
* @param integer $zone Zone to show
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function showAd($action, $zone)
|
||||||
|
{
|
||||||
|
$scr = <<<ENDOFSCRIPT
|
||||||
|
var m3_u = '%s';
|
||||||
|
var m3_r = Math.floor(Math.random()*99999999999);
|
||||||
|
if (!document.MAX_used) document.MAX_used = ',';
|
||||||
|
document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
|
||||||
|
document.write ("?zoneid=%d");
|
||||||
|
document.write ('&cb=' + m3_r);
|
||||||
|
if (document.MAX_used != ',') document.write ("&exclude=" + document.MAX_used);
|
||||||
|
document.write (document.charset ? '&charset='+document.charset : (document.characterSet ? '&charset='+document.characterSet : ''));
|
||||||
|
document.write ("&loc=" + escape(window.location));
|
||||||
|
if (document.referrer) document.write ("&referer=" + escape(document.referrer));
|
||||||
|
if (document.context) document.write ("&context=" + escape(document.context));
|
||||||
|
if (document.mmm_fo) document.write ("&mmm_fo=1");
|
||||||
|
document.write ("'><\/scr"+"ipt>");
|
||||||
|
ENDOFSCRIPT;
|
||||||
|
|
||||||
|
$action->inlineScript(sprintf($scr, $this->adScript, $zone));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -87,7 +87,7 @@ class RealtimePlugin extends Plugin
|
|||||||
$scripts = $this->_getScripts();
|
$scripts = $this->_getScripts();
|
||||||
|
|
||||||
foreach ($scripts as $script) {
|
foreach ($scripts as $script) {
|
||||||
$action->script(common_path($script));
|
$action->script($script);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = common_current_user();
|
$user = common_current_user();
|
||||||
@ -307,7 +307,7 @@ class RealtimePlugin extends Plugin
|
|||||||
|
|
||||||
function _getScripts()
|
function _getScripts()
|
||||||
{
|
{
|
||||||
return array('plugins/Realtime/realtimeupdate.js');
|
return array(common_path('plugins/Realtime/realtimeupdate.js'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function _updateInitialize($timeline, $user_id)
|
function _updateInitialize($timeline, $user_id)
|
||||||
|
@ -132,11 +132,11 @@ RealtimeUpdate = {
|
|||||||
user = data['user'];
|
user = data['user'];
|
||||||
html = data['html'].replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/&/g,'&');
|
html = data['html'].replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/&/g,'&');
|
||||||
source = data['source'].replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/&/g,'&');
|
source = data['source'].replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/&/g,'&');
|
||||||
|
console.log(data);
|
||||||
ni = "<li class=\"hentry notice\" id=\"notice-"+unique+"\">"+
|
ni = "<li class=\"hentry notice\" id=\"notice-"+unique+"\">"+
|
||||||
"<div class=\"entry-title\">"+
|
"<div class=\"entry-title\">"+
|
||||||
"<span class=\"vcard author\">"+
|
"<span class=\"vcard author\">"+
|
||||||
"<a href=\""+user['profile_url']+"\" class=\"url\">"+
|
"<a href=\""+user['profile_url']+"\" class=\"url\" title=\""+user['name']+"\">"+
|
||||||
"<img src=\""+user['profile_image_url']+"\" class=\"avatar photo\" width=\"48\" height=\"48\" alt=\""+user['screen_name']+"\"/>"+
|
"<img src=\""+user['profile_image_url']+"\" class=\"avatar photo\" width=\"48\" height=\"48\" alt=\""+user['screen_name']+"\"/>"+
|
||||||
"<span class=\"nickname fn\">"+user['screen_name']+"</span>"+
|
"<span class=\"nickname fn\">"+user['screen_name']+"</span>"+
|
||||||
"</a>"+
|
"</a>"+
|
||||||
|
92
plugins/UserLimitPlugin.php
Normal file
92
plugins/UserLimitPlugin.php
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Plugin to limit number of users that can register (best for cloud providers)
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* LICENCE: This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @category Action
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2009 StatusNet Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin to limit number of users that can register (best for cloud providers)
|
||||||
|
*
|
||||||
|
* For cloud providers whose freemium model is based on how many
|
||||||
|
* users can register. We use it on the StatusNet Cloud.
|
||||||
|
*
|
||||||
|
* @category Plugin
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*
|
||||||
|
* @seeAlso Location
|
||||||
|
*/
|
||||||
|
|
||||||
|
class UserLimitPlugin extends Plugin
|
||||||
|
{
|
||||||
|
public $maxUsers = null;
|
||||||
|
|
||||||
|
function onStartUserRegister(&$user, &$profile)
|
||||||
|
{
|
||||||
|
$this->_checkMaxUsers();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStartRegistrationTry($action)
|
||||||
|
{
|
||||||
|
$this->_checkMaxUsers();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _checkMaxUsers()
|
||||||
|
{
|
||||||
|
if (!is_null($this->maxUsers)) {
|
||||||
|
|
||||||
|
$cls = new User();
|
||||||
|
|
||||||
|
$cnt = $cls->count();
|
||||||
|
|
||||||
|
if ($cnt >= $this->maxUsers) {
|
||||||
|
$msg = sprintf(_('Cannot register; maximum number of users (%d) reached.'),
|
||||||
|
$this->maxUsers);
|
||||||
|
|
||||||
|
throw new ClientException($msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPluginVersion(&$versions)
|
||||||
|
{
|
||||||
|
$versions[] = array('name' => 'UserLimit',
|
||||||
|
'version' => STATUSNET_VERSION,
|
||||||
|
'author' => 'Evan Prodromou',
|
||||||
|
'homepage' => 'http://status.net/wiki/Plugin:UserLimit',
|
||||||
|
'description' =>
|
||||||
|
_m('Limit the number of users who can register.'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# live fast! die young!
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
source /etc/statusnet/setup.cfg
|
source /etc/statusnet/setup.cfg
|
||||||
|
|
||||||
export nickname=$1
|
export nickname=$1
|
||||||
|
@ -109,7 +109,13 @@ class QueueDaemon extends SpawningDaemon
|
|||||||
|
|
||||||
$master = new QueueMaster($this->get_id());
|
$master = new QueueMaster($this->get_id());
|
||||||
$master->init($this->all);
|
$master->init($this->all);
|
||||||
$master->service();
|
try {
|
||||||
|
$master->service();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
common_log(LOG_ERR, "Unhandled exception: " . $e->getMessage() . ' ' .
|
||||||
|
str_replace("\n", " ", $e->getTraceAsString()));
|
||||||
|
return self::EXIT_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
$this->log(LOG_INFO, 'finished servicing the queue');
|
$this->log(LOG_INFO, 'finished servicing the queue');
|
||||||
|
|
||||||
|
82
scripts/sendemail.php
Executable file
82
scripts/sendemail.php
Executable file
@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* StatusNet - a distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2008, 2009, StatusNet, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
|
||||||
|
|
||||||
|
$shortoptions = 'i:n:';
|
||||||
|
$longoptions = array('id=', 'nickname=', 'subject=');
|
||||||
|
|
||||||
|
$helptext = <<<END_OF_USEREMAIL_HELP
|
||||||
|
sendemail.php [options] < <message body>
|
||||||
|
Sends given email text to user.
|
||||||
|
|
||||||
|
-i --id id of the user to query
|
||||||
|
-n --nickname nickname of the user to query
|
||||||
|
--subject mail subject line (required)
|
||||||
|
|
||||||
|
END_OF_USEREMAIL_HELP;
|
||||||
|
|
||||||
|
require_once INSTALLDIR.'/scripts/commandline.inc';
|
||||||
|
|
||||||
|
if (have_option('i', 'id')) {
|
||||||
|
$id = get_option_value('i', 'id');
|
||||||
|
$user = User::staticGet('id', $id);
|
||||||
|
if (empty($user)) {
|
||||||
|
print "Can't find user with ID $id\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (have_option('n', 'nickname')) {
|
||||||
|
$nickname = get_option_value('n', 'nickname');
|
||||||
|
$user = User::staticGet('nickname', $nickname);
|
||||||
|
if (empty($user)) {
|
||||||
|
print "Can't find user with nickname '$nickname'\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print "You must provide a user by --id or --nickname\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($user->email)) {
|
||||||
|
// @fixme unconfirmed address?
|
||||||
|
print "No email registered for user '$user->nickname'\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_option('subject')) {
|
||||||
|
echo "You must provide a subject line for the mail in --subject='...' param.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
$subject = get_option_value('subject');
|
||||||
|
|
||||||
|
if (posix_isatty(STDIN)) {
|
||||||
|
print "You must provide message input on stdin!\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
$body = file_get_contents('php://stdin');
|
||||||
|
|
||||||
|
print "Sending to $user->email...";
|
||||||
|
if (mail_to_user($user, $subject, $body)) {
|
||||||
|
print " done\n";
|
||||||
|
} else {
|
||||||
|
print " failed.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
84
scripts/settag.php
Normal file
84
scripts/settag.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* StatusNet - a distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2008, 2009, StatusNet, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
|
||||||
|
|
||||||
|
$shortoptions = 'd';
|
||||||
|
$longoptions = array('delete');
|
||||||
|
|
||||||
|
$helptext = <<<END_OF_SETTAG_HELP
|
||||||
|
settag.php [options] <site> <tag>
|
||||||
|
Set the tag <tag> for site <site>.
|
||||||
|
|
||||||
|
With -d, delete the tag.
|
||||||
|
|
||||||
|
END_OF_SETTAG_HELP;
|
||||||
|
|
||||||
|
require_once INSTALLDIR.'/scripts/commandline.inc';
|
||||||
|
|
||||||
|
if (count($args) != 2) {
|
||||||
|
show_help();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$nickname = $args[0];
|
||||||
|
$tag = strtolower($args[1]);
|
||||||
|
|
||||||
|
$sn = Status_network::memGet('nickname', $nickname);
|
||||||
|
|
||||||
|
if (empty($sn)) {
|
||||||
|
print "No such site.\n";
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tags = $sn->getTags();
|
||||||
|
|
||||||
|
$i = array_search($tag, $tags);
|
||||||
|
|
||||||
|
if ($i !== false) {
|
||||||
|
if (have_option('d', 'delete')) { // Delete
|
||||||
|
unset($tags[$i]);
|
||||||
|
|
||||||
|
$orig = clone($sn);
|
||||||
|
$sn->tags = implode('|', $tags);
|
||||||
|
$result = $sn->update($orig);
|
||||||
|
if (!$result) {
|
||||||
|
print "Couldn't update.\n";
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print "Already set.\n";
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (have_option('d', 'delete')) { // Delete
|
||||||
|
print "No such tag.\n";
|
||||||
|
exit(-1);
|
||||||
|
} else {
|
||||||
|
$tags[] = $tag;
|
||||||
|
$orig = clone($sn);
|
||||||
|
$sn->tags = implode('|', $tags);
|
||||||
|
$result = $sn->update($orig);
|
||||||
|
if (!$result) {
|
||||||
|
print "Couldn't update.\n";
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,4 +11,8 @@ export AVATARBASE=/var/www/avatar.example.net
|
|||||||
export BACKGROUNDBASE=/var/www/background.example.net
|
export BACKGROUNDBASE=/var/www/background.example.net
|
||||||
export FILEBASE=/var/www/file.example.net
|
export FILEBASE=/var/www/file.example.net
|
||||||
export PWDGEN="pwgen 20"
|
export PWDGEN="pwgen 20"
|
||||||
|
export PHPBASE=/var/www/statusnet
|
||||||
|
export WILDCARD=example.net
|
||||||
|
export MAILTEMPLATE=/etc/statusnet/newsite-mail.txt
|
||||||
|
export MAILSUBJECT="Your new StatusNet site"
|
||||||
|
export POSTINSTALL=/etc/statusnet/morestuff.sh
|
||||||
|
@ -1,10 +1,28 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# live fast! die young!
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
source /etc/statusnet/setup.cfg
|
source /etc/statusnet/setup.cfg
|
||||||
|
|
||||||
export nickname=$1
|
# setup_status_net.sh mysite 'My Site' '1user' 'owner@example.com' 'Firsty McLastname'
|
||||||
export sitename=$2
|
|
||||||
|
|
||||||
|
export nickname="$1"
|
||||||
|
export sitename="$2"
|
||||||
|
export tags="$3"
|
||||||
|
export email="$4"
|
||||||
|
export fullname="$5"
|
||||||
|
|
||||||
|
# Fixme: if this is changed later we need to update profile URLs
|
||||||
|
# for the created user.
|
||||||
|
export server="$nickname.$WILDCARD"
|
||||||
|
|
||||||
|
# End-user info
|
||||||
|
export userpass=`$PWDGEN`
|
||||||
|
export roles="administrator moderator owner"
|
||||||
|
|
||||||
|
# DB info
|
||||||
export password=`$PWDGEN`
|
export password=`$PWDGEN`
|
||||||
export database=$nickname$DBBASE
|
export database=$nickname$DBBASE
|
||||||
export username=$nickname$USERBASE
|
export username=$nickname$USERBASE
|
||||||
@ -21,8 +39,8 @@ mysql -h $DBHOST -u $ADMIN --password=$ADMINPASS $SITEDB << ENDOFCOMMANDS
|
|||||||
|
|
||||||
GRANT ALL ON $database.* TO '$username'@'localhost' IDENTIFIED BY '$password';
|
GRANT ALL ON $database.* TO '$username'@'localhost' IDENTIFIED BY '$password';
|
||||||
GRANT ALL ON $database.* TO '$username'@'%' IDENTIFIED BY '$password';
|
GRANT ALL ON $database.* TO '$username'@'%' IDENTIFIED BY '$password';
|
||||||
INSERT INTO status_network (nickname, dbhost, dbuser, dbpass, dbname, sitename, created)
|
INSERT INTO status_network (nickname, dbhost, dbuser, dbpass, dbname, sitename, created, tags)
|
||||||
VALUES ('$nickname', '$DBHOSTNAME', '$username', '$password', '$database', '$sitename', now());
|
VALUES ('$nickname', '$DBHOSTNAME', '$username', '$password', '$database', '$sitename', now(), '$tags');
|
||||||
|
|
||||||
ENDOFCOMMANDS
|
ENDOFCOMMANDS
|
||||||
|
|
||||||
@ -30,3 +48,39 @@ for top in $AVATARBASE $FILEBASE $BACKGROUNDBASE; do
|
|||||||
mkdir $top/$nickname
|
mkdir $top/$nickname
|
||||||
chmod a+w $top/$nickname
|
chmod a+w $top/$nickname
|
||||||
done
|
done
|
||||||
|
|
||||||
|
php $PHPBASE/scripts/registeruser.php \
|
||||||
|
-s"$server" \
|
||||||
|
-n"$nickname" \
|
||||||
|
-f"$fullname" \
|
||||||
|
-w"$userpass" \
|
||||||
|
-e"$email"
|
||||||
|
|
||||||
|
for role in $roles
|
||||||
|
do
|
||||||
|
php $PHPBASE/scripts/userrole.php \
|
||||||
|
-s"$server" \
|
||||||
|
-n"$nickname" \
|
||||||
|
-r"$role"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -f "$MAILTEMPLATE" ]
|
||||||
|
then
|
||||||
|
# fixme how safe is this? are sitenames sanitized?
|
||||||
|
cat $MAILTEMPLATE | \
|
||||||
|
sed "s/\$nickname/$nickname/" | \
|
||||||
|
sed "s/\$sitename/$sitename/" | \
|
||||||
|
sed "s/\$userpass/$userpass/" | \
|
||||||
|
php $PHPBASE/scripts/sendemail.php \
|
||||||
|
-s"$server" \
|
||||||
|
-n"$nickname" \
|
||||||
|
--subject="$MAILSUBJECT"
|
||||||
|
else
|
||||||
|
echo "No mail template, not sending email."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$POSTINSTALL" ]
|
||||||
|
then
|
||||||
|
echo "Running $POSTINSTALL ..."
|
||||||
|
source "$POSTINSTALL"
|
||||||
|
fi
|
||||||
|
@ -1130,8 +1130,17 @@ top:3px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dialogbox .submit_dialogbox {
|
.dialogbox .submit_dialogbox {
|
||||||
text-indent:0;
|
|
||||||
font-weight:bold;
|
font-weight:bold;
|
||||||
|
text-indent:0;
|
||||||
|
min-width:46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrap form.processing input.submit,
|
||||||
|
.entity_actions a.processing,
|
||||||
|
.dialogbox.processing .submit_dialogbox {
|
||||||
|
cursor:wait;
|
||||||
|
outline:none;
|
||||||
|
text-indent:-9999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-options {
|
.notice-options {
|
||||||
|
@ -196,11 +196,12 @@ background-color:transparent;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#wrap form.processing input.submit,
|
#wrap form.processing input.submit,
|
||||||
.entity_actions a.processing {
|
.entity_actions a.processing,
|
||||||
|
.dialogbox.processing .submit_dialogbox {
|
||||||
background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
|
background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
|
||||||
cursor:wait;
|
}
|
||||||
text-indent:-9999px;
|
.notice-options .form_repeat.processing {
|
||||||
outline:none;
|
background-image:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
|
@ -196,11 +196,12 @@ background-color:transparent;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#wrap form.processing input.submit,
|
#wrap form.processing input.submit,
|
||||||
.entity_actions a.processing {
|
.entity_actions a.processing,
|
||||||
|
.dialogbox.processing .submit_dialogbox {
|
||||||
background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
|
background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
|
||||||
cursor:wait;
|
}
|
||||||
text-indent:-9999px;
|
.notice-options .form_repeat.processing {
|
||||||
outline:none;
|
background-image:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
|
Loading…
Reference in New Issue
Block a user