Merge branch '0.9.x' into 1.0.x
Conflicts: lib/queuemanager.php lib/xmppmanager.php plugins/Xmpp/Fake_XMPP.php scripts/imdaemon.php
This commit is contained in:
commit
20d6a7caed
10
EVENTS.txt
10
EVENTS.txt
@ -1,4 +1,4 @@
|
|||||||
InitializePlugin: a chance to initialize a plugin in a complete environment
|
\InitializePlugin: a chance to initialize a plugin in a complete environment
|
||||||
|
|
||||||
CleanupPlugin: a chance to cleanup a plugin at the end of a program
|
CleanupPlugin: a chance to cleanup a plugin at the end of a program
|
||||||
|
|
||||||
@ -355,6 +355,14 @@ EndShowHeadElements: Right before the </head> tag; put <script>s here if you nee
|
|||||||
|
|
||||||
CheckSchema: chance to check the schema
|
CheckSchema: chance to check the schema
|
||||||
|
|
||||||
|
StartProfileRemoteSubscribe: Before showing the link to remote subscription
|
||||||
|
- $userprofile: UserProfile widget
|
||||||
|
- &$profile: the profile being shown
|
||||||
|
|
||||||
|
EndProfileRemoteSubscribe: After showing the link to remote subscription
|
||||||
|
- $userprofile: UserProfile widget
|
||||||
|
- &$profile: the profile being shown
|
||||||
|
|
||||||
StartProfilePageProfileSection: Starting to show the section of the
|
StartProfilePageProfileSection: Starting to show the section of the
|
||||||
profile page with the actual profile data;
|
profile page with the actual profile data;
|
||||||
hook to prevent showing the profile (e.g.)
|
hook to prevent showing the profile (e.g.)
|
||||||
|
19
README
19
README
@ -1192,6 +1192,8 @@ server: If set, defines another server where avatars are stored in the
|
|||||||
typically only make 2 connections to a single server at a
|
typically only make 2 connections to a single server at a
|
||||||
time <http://ur1.ca/6ih>, so this can parallelize the job.
|
time <http://ur1.ca/6ih>, so this can parallelize the job.
|
||||||
Defaults to null.
|
Defaults to null.
|
||||||
|
ssl: Whether to access avatars using HTTPS. Defaults to null, meaning
|
||||||
|
to guess based on site-wide SSL settings.
|
||||||
|
|
||||||
public
|
public
|
||||||
------
|
------
|
||||||
@ -1221,6 +1223,19 @@ path: Path part of theme URLs, before the theme name. Relative to the
|
|||||||
(using version numbers as the path) to make sure that all files are
|
(using version numbers as the path) to make sure that all files are
|
||||||
reloaded by caching clients or proxies. Defaults to null,
|
reloaded by caching clients or proxies. Defaults to null,
|
||||||
which means to use the site path + '/theme'.
|
which means to use the site path + '/theme'.
|
||||||
|
ssl: Whether to use SSL for theme elements. Default is null, which means
|
||||||
|
guess based on site SSL settings.
|
||||||
|
|
||||||
|
javascript
|
||||||
|
----------
|
||||||
|
|
||||||
|
server: You can speed up page loading by pointing the
|
||||||
|
theme file lookup to another server (virtual or real).
|
||||||
|
Defaults to NULL, meaning to use the site server.
|
||||||
|
path: Path part of Javascript URLs. Defaults to null,
|
||||||
|
which means to use the site path + '/js/'.
|
||||||
|
ssl: Whether to use SSL for JavaScript files. Default is null, which means
|
||||||
|
guess based on site SSL settings.
|
||||||
|
|
||||||
xmpp
|
xmpp
|
||||||
----
|
----
|
||||||
@ -1447,6 +1462,8 @@ server: server name to use when creating URLs for uploaded files.
|
|||||||
a virtual server here can speed up Web performance.
|
a virtual server here can speed up Web performance.
|
||||||
path: URL path, relative to the server, to find files. Defaults to
|
path: URL path, relative to the server, to find files. Defaults to
|
||||||
main path + '/file/'.
|
main path + '/file/'.
|
||||||
|
ssl: whether to use HTTPS for file URLs. Defaults to null, meaning to
|
||||||
|
guess based on other SSL settings.
|
||||||
filecommand: command to use for determining the type of a file. May be
|
filecommand: command to use for determining the type of a file. May be
|
||||||
skipped if fileinfo extension is installed. Defaults to
|
skipped if fileinfo extension is installed. Defaults to
|
||||||
'/usr/bin/file'.
|
'/usr/bin/file'.
|
||||||
@ -1506,6 +1523,8 @@ dir: directory to write backgrounds too. Default is '/background/'
|
|||||||
subdir of install dir.
|
subdir of install dir.
|
||||||
path: path to backgrounds. Default is sub-path of install path; note
|
path: path to backgrounds. Default is sub-path of install path; note
|
||||||
that you may need to change this if you change site-path too.
|
that you may need to change this if you change site-path too.
|
||||||
|
ssl: Whether or not to use HTTPS for background files. Defaults to
|
||||||
|
null, meaning to guess from site-wide SSL settings.
|
||||||
|
|
||||||
ping
|
ping
|
||||||
----
|
----
|
||||||
|
@ -138,7 +138,19 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
$atom = new AtomNoticeFeed();
|
// If this was called using an integer ID, i.e.: using the canonical
|
||||||
|
// URL for this group's feed, then pass the Group object into the feed,
|
||||||
|
// so the OStatus plugin, and possibly other plugins, can access it.
|
||||||
|
// Feels sorta hacky. -- Z
|
||||||
|
|
||||||
|
$atom = null;
|
||||||
|
$id = $this->arg('id');
|
||||||
|
|
||||||
|
if (strval(intval($id)) === strval($id)) {
|
||||||
|
$atom = new AtomGroupNoticeFeed($this->group);
|
||||||
|
} else {
|
||||||
|
$atom = new AtomGroupNoticeFeed();
|
||||||
|
}
|
||||||
|
|
||||||
$atom->setId($id);
|
$atom->setId($id);
|
||||||
$atom->setTitle($title);
|
$atom->setTitle($title);
|
||||||
|
@ -148,7 +148,19 @@ class ApiTimelineUserAction extends ApiBareAuthAction
|
|||||||
|
|
||||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||||
|
|
||||||
$atom = new AtomNoticeFeed();
|
// If this was called using an integer ID, i.e.: using the canonical
|
||||||
|
// URL for this user's feed, then pass the User object into the feed,
|
||||||
|
// so the OStatus plugin, and possibly other plugins, can access it.
|
||||||
|
// Feels sorta hacky. -- Z
|
||||||
|
|
||||||
|
$atom = null;
|
||||||
|
$id = $this->arg('id');
|
||||||
|
|
||||||
|
if (strval(intval($id)) === strval($id)) {
|
||||||
|
$atom = new AtomUserNoticeFeed($this->user);
|
||||||
|
} else {
|
||||||
|
$atom = new AtomUserNoticeFeed();
|
||||||
|
}
|
||||||
|
|
||||||
$atom->setId($id);
|
$atom->setId($id);
|
||||||
$atom->setTitle($title);
|
$atom->setTitle($title);
|
||||||
|
@ -82,9 +82,20 @@ class Avatar extends Memcached_DataObject
|
|||||||
$server = common_config('site', 'server');
|
$server = common_config('site', 'server');
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: protocol
|
$ssl = common_config('avatar', 'ssl');
|
||||||
|
|
||||||
return 'http://'.$server.$path.$filename;
|
if (is_null($ssl)) { // null -> guess
|
||||||
|
if (common_config('site', 'ssl') == 'always' &&
|
||||||
|
!common_config('avatar', 'server')) {
|
||||||
|
$ssl = true;
|
||||||
|
} else {
|
||||||
|
$ssl = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = ($ssl) ? 'https' : 'http';
|
||||||
|
|
||||||
|
return $protocol.'://'.$server.$path.$filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayUrl()
|
function displayUrl()
|
||||||
|
@ -155,9 +155,20 @@ class Design extends Memcached_DataObject
|
|||||||
$server = common_config('site', 'server');
|
$server = common_config('site', 'server');
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: protocol
|
$ssl = common_config('background', 'ssl');
|
||||||
|
|
||||||
return 'http://'.$server.$path.$filename;
|
if (is_null($ssl)) { // null -> guess
|
||||||
|
if (common_config('site', 'ssl') == 'always' &&
|
||||||
|
!common_config('background', 'server')) {
|
||||||
|
$ssl = true;
|
||||||
|
} else {
|
||||||
|
$ssl = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = ($ssl) ? 'https' : 'http';
|
||||||
|
|
||||||
|
return $protocol.'://'.$server.$path.$filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDisposition($on, $off, $tile)
|
function setDisposition($on, $off, $tile)
|
||||||
|
@ -228,9 +228,20 @@ class File extends Memcached_DataObject
|
|||||||
$server = common_config('site', 'server');
|
$server = common_config('site', 'server');
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: protocol
|
$ssl = common_config('attachments', 'ssl');
|
||||||
|
|
||||||
return 'http://'.$server.$path.$filename;
|
if (is_null($ssl)) { // null -> guess
|
||||||
|
if (common_config('site', 'ssl') == 'always' &&
|
||||||
|
!common_config('attachments', 'server')) {
|
||||||
|
$ssl = true;
|
||||||
|
} else {
|
||||||
|
$ssl = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = ($ssl) ? 'https' : 'http';
|
||||||
|
|
||||||
|
return $protocol.'://'.$server.$path.$filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,57 +19,8 @@
|
|||||||
|
|
||||||
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
||||||
|
|
||||||
class Memcached_DataObject extends DB_DataObject
|
class Memcached_DataObject extends Safe_DataObject
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Destructor to free global memory resources associated with
|
|
||||||
* this data object when it's unset or goes out of scope.
|
|
||||||
* DB_DataObject doesn't do this yet by itself.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __destruct()
|
|
||||||
{
|
|
||||||
$this->free();
|
|
||||||
if (method_exists('DB_DataObject', '__destruct')) {
|
|
||||||
parent::__destruct();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Magic function called at serialize() time.
|
|
||||||
*
|
|
||||||
* We use this to drop a couple process-specific references
|
|
||||||
* from DB_DataObject which can cause trouble in future
|
|
||||||
* processes.
|
|
||||||
*
|
|
||||||
* @return array of variable names to include in serialization.
|
|
||||||
*/
|
|
||||||
function __sleep()
|
|
||||||
{
|
|
||||||
$vars = array_keys(get_object_vars($this));
|
|
||||||
$skip = array('_DB_resultid', '_link_loaded');
|
|
||||||
return array_diff($vars, $skip);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Magic function called at unserialize() time.
|
|
||||||
*
|
|
||||||
* Clean out some process-specific variables which might
|
|
||||||
* be floating around from a previous process's cached
|
|
||||||
* objects.
|
|
||||||
*
|
|
||||||
* Old cached objects may still have them.
|
|
||||||
*/
|
|
||||||
function __wakeup()
|
|
||||||
{
|
|
||||||
// Refers to global state info from a previous process.
|
|
||||||
// Clear this out so we don't accidentally break global
|
|
||||||
// state in *this* process.
|
|
||||||
$this->_DB_resultid = null;
|
|
||||||
// We don't have any local DBO refs, so clear these out.
|
|
||||||
$this->_link_loaded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for DB_DataObject's static lookup using memcached
|
* Wrapper for DB_DataObject's static lookup using memcached
|
||||||
* as backing instead of an in-process cache array.
|
* as backing instead of an in-process cache array.
|
||||||
@ -579,3 +530,4 @@ class Memcached_DataObject extends DB_DataObject
|
|||||||
return $c->set($cacheKey, $value);
|
return $c->set($cacheKey, $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
250
classes/Safe_DataObject.php
Normal file
250
classes/Safe_DataObject.php
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2010, StatusNet, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended DB_DataObject to improve a few things:
|
||||||
|
* - free global resources from destructor
|
||||||
|
* - remove bogus global references from serialized objects
|
||||||
|
* - don't leak memory when loading already-used .ini files
|
||||||
|
* (eg when using the same schema on thousands of databases)
|
||||||
|
*/
|
||||||
|
class Safe_DataObject extends DB_DataObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Destructor to free global memory resources associated with
|
||||||
|
* this data object when it's unset or goes out of scope.
|
||||||
|
* DB_DataObject doesn't do this yet by itself.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function __destruct()
|
||||||
|
{
|
||||||
|
$this->free();
|
||||||
|
if (method_exists('DB_DataObject', '__destruct')) {
|
||||||
|
parent::__destruct();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic function called at serialize() time.
|
||||||
|
*
|
||||||
|
* We use this to drop a couple process-specific references
|
||||||
|
* from DB_DataObject which can cause trouble in future
|
||||||
|
* processes.
|
||||||
|
*
|
||||||
|
* @return array of variable names to include in serialization.
|
||||||
|
*/
|
||||||
|
function __sleep()
|
||||||
|
{
|
||||||
|
$vars = array_keys(get_object_vars($this));
|
||||||
|
$skip = array('_DB_resultid', '_link_loaded');
|
||||||
|
return array_diff($vars, $skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic function called at unserialize() time.
|
||||||
|
*
|
||||||
|
* Clean out some process-specific variables which might
|
||||||
|
* be floating around from a previous process's cached
|
||||||
|
* objects.
|
||||||
|
*
|
||||||
|
* Old cached objects may still have them.
|
||||||
|
*/
|
||||||
|
function __wakeup()
|
||||||
|
{
|
||||||
|
// Refers to global state info from a previous process.
|
||||||
|
// Clear this out so we don't accidentally break global
|
||||||
|
// state in *this* process.
|
||||||
|
$this->_DB_resultid = null;
|
||||||
|
// We don't have any local DBO refs, so clear these out.
|
||||||
|
$this->_link_loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Work around memory-leak bugs...
|
||||||
|
* Had to copy-paste the whole function in order to patch a couple lines of it.
|
||||||
|
* Would be nice if this code was better factored.
|
||||||
|
*
|
||||||
|
* @param optional string name of database to assign / read
|
||||||
|
* @param optional array structure of database, and keys
|
||||||
|
* @param optional array table links
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return true or PEAR:error on wrong paramenters.. or false if no file exists..
|
||||||
|
* or the array(tablename => array(column_name=>type)) if called with 1 argument.. (databasename)
|
||||||
|
*/
|
||||||
|
function databaseStructure()
|
||||||
|
{
|
||||||
|
|
||||||
|
global $_DB_DATAOBJECT;
|
||||||
|
|
||||||
|
// Assignment code
|
||||||
|
|
||||||
|
if ($args = func_get_args()) {
|
||||||
|
|
||||||
|
if (count($args) == 1) {
|
||||||
|
|
||||||
|
// this returns all the tables and their structure..
|
||||||
|
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||||
|
$this->debug("Loading Generator as databaseStructure called with args",1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$x = new DB_DataObject;
|
||||||
|
$x->_database = $args[0];
|
||||||
|
$this->_connect();
|
||||||
|
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
|
||||||
|
|
||||||
|
$tables = $DB->getListOf('tables');
|
||||||
|
class_exists('DB_DataObject_Generator') ? '' :
|
||||||
|
require_once 'DB/DataObject/Generator.php';
|
||||||
|
|
||||||
|
foreach($tables as $table) {
|
||||||
|
$y = new DB_DataObject_Generator;
|
||||||
|
$y->fillTableSchema($x->_database,$table);
|
||||||
|
}
|
||||||
|
return $_DB_DATAOBJECT['INI'][$x->_database];
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$_DB_DATAOBJECT['INI'][$args[0]] = isset($_DB_DATAOBJECT['INI'][$args[0]]) ?
|
||||||
|
$_DB_DATAOBJECT['INI'][$args[0]] + $args[1] : $args[1];
|
||||||
|
|
||||||
|
if (isset($args[1])) {
|
||||||
|
$_DB_DATAOBJECT['LINKS'][$args[0]] = isset($_DB_DATAOBJECT['LINKS'][$args[0]]) ?
|
||||||
|
$_DB_DATAOBJECT['LINKS'][$args[0]] + $args[2] : $args[2];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!$this->_database) {
|
||||||
|
$this->_connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// loaded already?
|
||||||
|
if (!empty($_DB_DATAOBJECT['INI'][$this->_database])) {
|
||||||
|
|
||||||
|
// database loaded - but this is table is not available..
|
||||||
|
if (
|
||||||
|
empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])
|
||||||
|
&& !empty($_DB_DATAOBJECT['CONFIG']['proxy'])
|
||||||
|
) {
|
||||||
|
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||||
|
$this->debug("Loading Generator to fetch Schema",1);
|
||||||
|
}
|
||||||
|
class_exists('DB_DataObject_Generator') ? '' :
|
||||||
|
require_once 'DB/DataObject/Generator.php';
|
||||||
|
|
||||||
|
|
||||||
|
$x = new DB_DataObject_Generator;
|
||||||
|
$x->fillTableSchema($this->_database,$this->__table);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (empty($_DB_DATAOBJECT['CONFIG'])) {
|
||||||
|
DB_DataObject::_loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if you supply this with arguments, then it will take those
|
||||||
|
// as the database and links array...
|
||||||
|
|
||||||
|
$schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ?
|
||||||
|
array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") :
|
||||||
|
array() ;
|
||||||
|
|
||||||
|
if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
|
||||||
|
$schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ?
|
||||||
|
$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] :
|
||||||
|
explode(PATH_SEPARATOR,$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* BEGIN CHANGED FROM UPSTREAM */
|
||||||
|
$_DB_DATAOBJECT['INI'][$this->_database] = $this->parseIniFiles($schemas);
|
||||||
|
/* END CHANGED FROM UPSTREAM */
|
||||||
|
|
||||||
|
// now have we loaded the structure..
|
||||||
|
|
||||||
|
if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// - if not try building it..
|
||||||
|
if (!empty($_DB_DATAOBJECT['CONFIG']['proxy'])) {
|
||||||
|
class_exists('DB_DataObject_Generator') ? '' :
|
||||||
|
require_once 'DB/DataObject/Generator.php';
|
||||||
|
|
||||||
|
$x = new DB_DataObject_Generator;
|
||||||
|
$x->fillTableSchema($this->_database,$this->__table);
|
||||||
|
// should this fail!!!???
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$this->debug("Cant find database schema: {$this->_database}/{$this->__table} \n".
|
||||||
|
"in links file data: " . print_r($_DB_DATAOBJECT['INI'],true),"databaseStructure",5);
|
||||||
|
// we have to die here!! - it causes chaos if we dont (including looping forever!)
|
||||||
|
$this->raiseError( "Unable to load schema for database and table (turn debugging up to 5 for full error message)", DB_DATAOBJECT_ERROR_INVALIDARGS, PEAR_ERROR_DIE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For parseIniFiles */
|
||||||
|
protected static $iniCache = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When switching site configurations, DB_DataObject was loading its
|
||||||
|
* .ini files over and over, leaking gobs of memory.
|
||||||
|
* This refactored helper function uses a local cache of .ini files
|
||||||
|
* to minimize the leaks.
|
||||||
|
*
|
||||||
|
* @param array of .ini file names $schemas
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function parseIniFiles($schemas)
|
||||||
|
{
|
||||||
|
$key = implode("|", $schemas);
|
||||||
|
if (!isset(Safe_DataObject::$iniCache[$key])) {
|
||||||
|
$data = array();
|
||||||
|
foreach ($schemas as $ini) {
|
||||||
|
if (file_exists($ini) && is_file($ini)) {
|
||||||
|
$data = array_merge($data, parse_ini_file($ini, true));
|
||||||
|
|
||||||
|
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||||
|
if (!is_readable ($ini)) {
|
||||||
|
$this->debug("ini file is not readable: $ini","databaseStructure",1);
|
||||||
|
} else {
|
||||||
|
$this->debug("Loaded ini file: $ini","databaseStructure",1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||||
|
$this->debug("Missing ini file: $ini","databaseStructure",1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Safe_DataObject::$iniCache[$key] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Safe_DataObject::$iniCache[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
||||||
|
|
||||||
class Status_network extends DB_DataObject
|
class Status_network extends Safe_DataObject
|
||||||
{
|
{
|
||||||
###START_AUTOCODE
|
###START_AUTOCODE
|
||||||
/* the code below is auto generated do not remove the above tag */
|
/* the code below is auto generated do not remove the above tag */
|
||||||
@ -57,6 +57,7 @@ class Status_network extends DB_DataObject
|
|||||||
###END_AUTOCODE
|
###END_AUTOCODE
|
||||||
|
|
||||||
static $cache = null;
|
static $cache = null;
|
||||||
|
static $cacheInitialized = false;
|
||||||
static $base = null;
|
static $base = null;
|
||||||
static $wildcard = null;
|
static $wildcard = null;
|
||||||
|
|
||||||
@ -78,11 +79,15 @@ class Status_network extends DB_DataObject
|
|||||||
if (class_exists('Memcache')) {
|
if (class_exists('Memcache')) {
|
||||||
self::$cache = new Memcache();
|
self::$cache = new Memcache();
|
||||||
|
|
||||||
// Can't close persistent connections, making forking painful.
|
// If we're a parent command-line process we need
|
||||||
|
// to be able to close out the connection after
|
||||||
|
// forking, so disable persistence.
|
||||||
//
|
//
|
||||||
// @fixme only do this in *parent* CLI processes.
|
// We'll turn it back on again the second time
|
||||||
// single-process and child-processes *should* use persistent.
|
// through which will either be in a child process,
|
||||||
$persist = php_sapi_name() != 'cli';
|
// or a single-process script which is switching
|
||||||
|
// configurations.
|
||||||
|
$persist = php_sapi_name() != 'cli' || self::$cacheInitialized;
|
||||||
if (is_array($servers)) {
|
if (is_array($servers)) {
|
||||||
foreach($servers as $server) {
|
foreach($servers as $server) {
|
||||||
self::$cache->addServer($server, 11211, $persist);
|
self::$cache->addServer($server, 11211, $persist);
|
||||||
@ -90,6 +95,7 @@ class Status_network extends DB_DataObject
|
|||||||
} else {
|
} else {
|
||||||
self::$cache->addServer($servers, 11211, $persist);
|
self::$cache->addServer($servers, 11211, $persist);
|
||||||
}
|
}
|
||||||
|
self::$cacheInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$base = $dbname;
|
self::$base = $dbname;
|
||||||
|
@ -285,6 +285,10 @@ class OMB_Service_Provider {
|
|||||||
list($consumer, $token) = $this->getOAuthServer()->verify_request($req);
|
list($consumer, $token) = $this->getOAuthServer()->verify_request($req);
|
||||||
} catch (OAuthException $e) {
|
} catch (OAuthException $e) {
|
||||||
header('HTTP/1.1 403 Forbidden');
|
header('HTTP/1.1 403 Forbidden');
|
||||||
|
// @debug hack
|
||||||
|
throw OMB_RemoteServiceException::forRequest($uri,
|
||||||
|
'Revoked accesstoken for ' . $listenee . ': ' . $e->getMessage());
|
||||||
|
// @end debug
|
||||||
throw OMB_RemoteServiceException::forRequest($uri,
|
throw OMB_RemoteServiceException::forRequest($uri,
|
||||||
'Revoked accesstoken for ' . $listenee);
|
'Revoked accesstoken for ' . $listenee);
|
||||||
}
|
}
|
||||||
|
52
js/util.js
52
js/util.js
@ -404,12 +404,10 @@ var SN = { // StatusNet
|
|||||||
},
|
},
|
||||||
|
|
||||||
NoticeWithAttachment: function(notice) {
|
NoticeWithAttachment: function(notice) {
|
||||||
if ($('.attachment', notice).length === 0) {
|
if (notice.find('.attachment').length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var notice_id = notice.attr('id');
|
|
||||||
|
|
||||||
$.fn.jOverlay.options = {
|
$.fn.jOverlay.options = {
|
||||||
method : 'GET',
|
method : 'GET',
|
||||||
data : '',
|
data : '',
|
||||||
@ -429,35 +427,37 @@ var SN = { // StatusNet
|
|||||||
css : {'max-width':'542px', 'top':'5%', 'left':'32.5%'}
|
css : {'max-width':'542px', 'top':'5%', 'left':'32.5%'}
|
||||||
};
|
};
|
||||||
|
|
||||||
$('#'+notice_id+' a.attachment').click(function() {
|
notice.find('a.attachment').click(function() {
|
||||||
$().jOverlay({url: $('address .url')[0].href+'attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'});
|
$().jOverlay({url: $('address .url')[0].href+'attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
var t;
|
if ($('#shownotice').length == 0) {
|
||||||
$("body:not(#shownotice) #"+notice_id+" a.thumbnail").hover(
|
var t;
|
||||||
function() {
|
notice.find('a.thumbnail').hover(
|
||||||
var anchor = $(this);
|
function() {
|
||||||
$("a.thumbnail").children('img').hide();
|
var anchor = $(this);
|
||||||
anchor.closest(".entry-title").addClass('ov');
|
$('a.thumbnail').children('img').hide();
|
||||||
|
anchor.closest(".entry-title").addClass('ov');
|
||||||
|
|
||||||
if (anchor.children('img').length === 0) {
|
if (anchor.children('img').length === 0) {
|
||||||
t = setTimeout(function() {
|
t = setTimeout(function() {
|
||||||
$.get($('address .url')[0].href+'attachment/' + (anchor.attr('id').substring('attachment'.length + 1)) + '/thumbnail', null, function(data) {
|
$.get($('address .url')[0].href+'attachment/' + (anchor.attr('id').substring('attachment'.length + 1)) + '/thumbnail', null, function(data) {
|
||||||
anchor.append(data);
|
anchor.append(data);
|
||||||
});
|
});
|
||||||
}, 500);
|
}, 500);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
anchor.children('img').show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
clearTimeout(t);
|
||||||
|
$('a.thumbnail').children('img').hide();
|
||||||
|
$(this).closest('.entry-title').removeClass('ov');
|
||||||
}
|
}
|
||||||
else {
|
);
|
||||||
anchor.children('img').show();
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
clearTimeout(t);
|
|
||||||
$("a.thumbnail").children('img').hide();
|
|
||||||
$(this).closest(".entry-title").removeClass('ov');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
NoticeDataAttach: function() {
|
NoticeDataAttach: function() {
|
||||||
|
@ -405,6 +405,7 @@ class Action extends HTMLOutputter // lawsuit
|
|||||||
'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png'),
|
'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png'),
|
||||||
'alt' => common_config('site', 'name')));
|
'alt' => common_config('site', 'name')));
|
||||||
}
|
}
|
||||||
|
$this->text(' ');
|
||||||
$this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
|
$this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
|
||||||
$this->elementEnd('a');
|
$this->elementEnd('a');
|
||||||
Event::handle('EndAddressData', array($this));
|
Event::handle('EndAddressData', array($this));
|
||||||
@ -822,12 +823,14 @@ class Action extends HTMLOutputter // lawsuit
|
|||||||
'alt' => common_config('license', 'title'),
|
'alt' => common_config('license', 'title'),
|
||||||
'width' => '80',
|
'width' => '80',
|
||||||
'height' => '15'));
|
'height' => '15'));
|
||||||
|
$this->text(' ');
|
||||||
//TODO: This is dirty: i18n
|
//TODO: This is dirty: i18n
|
||||||
$this->text(_('All '.common_config('site', 'name').' content and data are available under the '));
|
$this->text(_('All '.common_config('site', 'name').' content and data are available under the '));
|
||||||
$this->element('a', array('class' => 'license',
|
$this->element('a', array('class' => 'license',
|
||||||
'rel' => 'external license',
|
'rel' => 'external license',
|
||||||
'href' => common_config('license', 'url')),
|
'href' => common_config('license', 'url')),
|
||||||
common_config('license', 'title'));
|
common_config('license', 'title'));
|
||||||
|
$this->text(' ');
|
||||||
$this->text(_('license.'));
|
$this->text(_('license.'));
|
||||||
$this->elementEnd('p');
|
$this->elementEnd('p');
|
||||||
break;
|
break;
|
||||||
|
@ -1155,7 +1155,6 @@ class ApiAction extends Action
|
|||||||
$this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
|
$this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
|
||||||
'xml:lang' => 'en-US',
|
'xml:lang' => 'en-US',
|
||||||
'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
|
'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
|
||||||
Event::handle('StartApiAtom', array($this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function endTwitterAtom()
|
function endTwitterAtom()
|
||||||
|
@ -175,6 +175,8 @@ class Atom10Feed extends XMLStringer
|
|||||||
|
|
||||||
$this->element('updated', null, $this->updated);
|
$this->element('updated', null, $this->updated);
|
||||||
|
|
||||||
|
$this->renderAuthors();
|
||||||
|
|
||||||
$this->renderLinks();
|
$this->renderLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,18 +223,21 @@ class Atom10Feed extends XMLStringer
|
|||||||
|
|
||||||
function getString()
|
function getString()
|
||||||
{
|
{
|
||||||
$this->validate();
|
if (Event::handle('StartApiAtom', array($this))) {
|
||||||
|
|
||||||
$this->initFeed();
|
$this->validate();
|
||||||
$this->renderAuthors();
|
$this->initFeed();
|
||||||
|
|
||||||
if (!empty($this->subject)) {
|
if (!empty($this->subject)) {
|
||||||
$this->raw($this->subject);
|
$this->raw($this->subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->renderEntries();
|
||||||
|
$this->endFeed();
|
||||||
|
|
||||||
|
Event::handle('EndApiAtom', array($this));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->renderEntries();
|
|
||||||
$this->endFeed();
|
|
||||||
|
|
||||||
return $this->xw->outputMemory();
|
return $this->xw->outputMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
67
lib/atomgroupnoticefeed.php
Normal file
67
lib/atomgroupnoticefeed.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Class for building an in-memory Atom feed for a particular group's
|
||||||
|
* timeline.
|
||||||
|
*
|
||||||
|
* 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 Feed
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Zach Copley <zach@status.net>
|
||||||
|
* @copyright 2010 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET'))
|
||||||
|
{
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for group notice feeds. May contains a reference to the group.
|
||||||
|
*
|
||||||
|
* @category Feed
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Zach Copley <zach@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
class AtomGroupNoticeFeed extends AtomNoticeFeed
|
||||||
|
{
|
||||||
|
private $group;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param Group $group the group for the feed (optional)
|
||||||
|
* @param boolean $indent flag to turn indenting on or off
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function __construct($group = null, $indent = true) {
|
||||||
|
parent::__construct($indent);
|
||||||
|
$this->group = $group;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGroup()
|
||||||
|
{
|
||||||
|
return $this->group;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* StatusNet, the distributed open-source microblogging tool
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
*
|
*
|
||||||
* Class for building and Atom feed from a collection of notices
|
* Class for building an Atom feed from a collection of notices
|
||||||
*
|
*
|
||||||
* PHP version 5
|
* PHP version 5
|
||||||
*
|
*
|
||||||
@ -101,3 +101,5 @@ class AtomNoticeFeed extends Atom10Feed
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
66
lib/atomusernoticefeed.php
Normal file
66
lib/atomusernoticefeed.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Class for building an in-memory Atom feed for a particular user's
|
||||||
|
* timeline.
|
||||||
|
*
|
||||||
|
* 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 Feed
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Zach Copley <zach@status.net>
|
||||||
|
* @copyright 2010 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET'))
|
||||||
|
{
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for user notice feeds. May contain a reference to the user.
|
||||||
|
*
|
||||||
|
* @category Feed
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Zach Copley <zach@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
class AtomUserNoticeFeed extends AtomNoticeFeed
|
||||||
|
{
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param User $user the user for the feed (optional)
|
||||||
|
* @param boolean $indent flag to turn indenting on or off
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function __construct($user = null, $indent = true) {
|
||||||
|
parent::__construct($indent);
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUser()
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
}
|
@ -159,6 +159,32 @@ class Cache
|
|||||||
return $success;
|
return $success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically increment an existing numeric value.
|
||||||
|
* Existing expiration time should remain unchanged, if any.
|
||||||
|
*
|
||||||
|
* @param string $key The key to use for lookups
|
||||||
|
* @param int $step Amount to increment (default 1)
|
||||||
|
*
|
||||||
|
* @return mixed incremented value, or false if not set.
|
||||||
|
*/
|
||||||
|
function increment($key, $step=1)
|
||||||
|
{
|
||||||
|
$value = false;
|
||||||
|
if (Event::handle('StartCacheIncrement', array(&$key, &$step, &$value))) {
|
||||||
|
// Fallback is not guaranteed to be atomic,
|
||||||
|
// and may original expiry value.
|
||||||
|
$value = $this->get($key);
|
||||||
|
if ($value !== false) {
|
||||||
|
$value += $step;
|
||||||
|
$ok = $this->set($key, $value);
|
||||||
|
$got = $this->get($key);
|
||||||
|
}
|
||||||
|
Event::handle('EndCacheIncrement', array($key, $step, $value));
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the value associated with a key
|
* Delete the value associated with a key
|
||||||
*
|
*
|
||||||
|
@ -72,7 +72,7 @@ class DBQueueManager extends QueueManager
|
|||||||
public function poll()
|
public function poll()
|
||||||
{
|
{
|
||||||
$this->_log(LOG_DEBUG, 'Checking for notices...');
|
$this->_log(LOG_DEBUG, 'Checking for notices...');
|
||||||
$qi = Queue_item::top($this->getQueues());
|
$qi = Queue_item::top($this->activeQueues());
|
||||||
if (empty($qi)) {
|
if (empty($qi)) {
|
||||||
$this->_log(LOG_DEBUG, 'No notices waiting; idling.');
|
$this->_log(LOG_DEBUG, 'No notices waiting; idling.');
|
||||||
return false;
|
return false;
|
||||||
@ -142,9 +142,4 @@ class DBQueueManager extends QueueManager
|
|||||||
|
|
||||||
$this->stats('error', $queue);
|
$this->stats('error', $queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _log($level, $msg)
|
|
||||||
{
|
|
||||||
common_log($level, 'DBQueueManager: '.$msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ $default =
|
|||||||
'subsystem' => 'db', # default to database, or 'stomp'
|
'subsystem' => 'db', # default to database, or 'stomp'
|
||||||
'stomp_server' => null,
|
'stomp_server' => null,
|
||||||
'queue_basename' => '/queue/statusnet/',
|
'queue_basename' => '/queue/statusnet/',
|
||||||
'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_persistent' => true, // keep items across queue server restart, if persistence is enabled
|
||||||
@ -91,6 +91,12 @@ $default =
|
|||||||
'spawndelay' => 1, // Wait at least N seconds between (re)spawns of child processes to avoid slamming the queue server with subscription startup
|
'spawndelay' => 1, // Wait at least N seconds between (re)spawns of child processes to avoid slamming the queue server with subscription startup
|
||||||
'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
|
'inboxes' => true, // true to do inbox distribution & output queueing from in background via 'distrib' queue
|
||||||
|
'breakout' => array('*' => 'shared'), // set global or per-handler queue breakout
|
||||||
|
// 'shared': use a shared queue for all sites
|
||||||
|
// 'handler': share each/this handler over multiple sites
|
||||||
|
// 'site': break out for each/this handler on this site
|
||||||
|
'max_retries' => 10, // drop messages after N failed attempts to process (Stomp)
|
||||||
|
'dead_letter_dir' => false, // set to directory to save dropped messages into (Stomp)
|
||||||
),
|
),
|
||||||
'license' =>
|
'license' =>
|
||||||
array('type' => 'cc', # can be 'cc', 'allrightsreserved', 'private'
|
array('type' => 'cc', # can be 'cc', 'allrightsreserved', 'private'
|
||||||
@ -111,11 +117,13 @@ $default =
|
|||||||
'avatar' =>
|
'avatar' =>
|
||||||
array('server' => null,
|
array('server' => null,
|
||||||
'dir' => INSTALLDIR . '/avatar/',
|
'dir' => INSTALLDIR . '/avatar/',
|
||||||
'path' => $_path . '/avatar/'),
|
'path' => $_path . '/avatar/',
|
||||||
|
'ssl' => null),
|
||||||
'background' =>
|
'background' =>
|
||||||
array('server' => null,
|
array('server' => null,
|
||||||
'dir' => INSTALLDIR . '/background/',
|
'dir' => INSTALLDIR . '/background/',
|
||||||
'path' => $_path . '/background/'),
|
'path' => $_path . '/background/',
|
||||||
|
'ssl' => null),
|
||||||
'public' =>
|
'public' =>
|
||||||
array('localonly' => true,
|
array('localonly' => true,
|
||||||
'blacklist' => array(),
|
'blacklist' => array(),
|
||||||
@ -123,10 +131,12 @@ $default =
|
|||||||
'theme' =>
|
'theme' =>
|
||||||
array('server' => null,
|
array('server' => null,
|
||||||
'dir' => null,
|
'dir' => null,
|
||||||
'path'=> null),
|
'path'=> null,
|
||||||
|
'ssl' => null),
|
||||||
'javascript' =>
|
'javascript' =>
|
||||||
array('server' => null,
|
array('server' => null,
|
||||||
'path'=> null),
|
'path'=> null,
|
||||||
|
'ssl' => null),
|
||||||
'throttle' =>
|
'throttle' =>
|
||||||
array('enabled' => false, // whether to throttle edits; false by default
|
array('enabled' => false, // whether to throttle edits; false by default
|
||||||
'count' => 20, // number of allowed messages in timespan
|
'count' => 20, // number of allowed messages in timespan
|
||||||
@ -184,6 +194,7 @@ $default =
|
|||||||
array('server' => null,
|
array('server' => null,
|
||||||
'dir' => INSTALLDIR . '/file/',
|
'dir' => INSTALLDIR . '/file/',
|
||||||
'path' => $_path . '/file/',
|
'path' => $_path . '/file/',
|
||||||
|
'ssl' => null,
|
||||||
'supported' => array('image/png',
|
'supported' => array('image/png',
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/gif',
|
'image/gif',
|
||||||
|
@ -105,6 +105,7 @@ class GroupList extends Widget
|
|||||||
'alt' =>
|
'alt' =>
|
||||||
($this->group->fullname) ? $this->group->fullname :
|
($this->group->fullname) ? $this->group->fullname :
|
||||||
$this->group->nickname));
|
$this->group->nickname));
|
||||||
|
$this->out->text(' ');
|
||||||
$hasFN = ($this->group->fullname) ? 'nickname' : 'fn org nickname';
|
$hasFN = ($this->group->fullname) ? 'nickname' : 'fn org nickname';
|
||||||
$this->out->elementStart('span', $hasFN);
|
$this->out->elementStart('span', $hasFN);
|
||||||
$this->out->raw($this->highlight($this->group->nickname));
|
$this->out->raw($this->highlight($this->group->nickname));
|
||||||
@ -112,16 +113,19 @@ class GroupList extends Widget
|
|||||||
$this->out->elementEnd('a');
|
$this->out->elementEnd('a');
|
||||||
|
|
||||||
if ($this->group->fullname) {
|
if ($this->group->fullname) {
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->elementStart('span', 'fn org');
|
$this->out->elementStart('span', 'fn org');
|
||||||
$this->out->raw($this->highlight($this->group->fullname));
|
$this->out->raw($this->highlight($this->group->fullname));
|
||||||
$this->out->elementEnd('span');
|
$this->out->elementEnd('span');
|
||||||
}
|
}
|
||||||
if ($this->group->location) {
|
if ($this->group->location) {
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->elementStart('span', 'label');
|
$this->out->elementStart('span', 'label');
|
||||||
$this->out->raw($this->highlight($this->group->location));
|
$this->out->raw($this->highlight($this->group->location));
|
||||||
$this->out->elementEnd('span');
|
$this->out->elementEnd('span');
|
||||||
}
|
}
|
||||||
if ($this->group->homepage) {
|
if ($this->group->homepage) {
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->elementStart('a', array('href' => $this->group->homepage,
|
$this->out->elementStart('a', array('href' => $this->group->homepage,
|
||||||
'class' => 'url'));
|
'class' => 'url'));
|
||||||
$this->out->raw($this->highlight($this->group->homepage));
|
$this->out->raw($this->highlight($this->group->homepage));
|
||||||
|
@ -85,9 +85,9 @@ class GroupSection extends Section
|
|||||||
'href' => $group->homeUrl(),
|
'href' => $group->homeUrl(),
|
||||||
'rel' => 'contact group',
|
'rel' => 'contact group',
|
||||||
'class' => 'url'));
|
'class' => 'url'));
|
||||||
|
$this->out->text(' ');
|
||||||
$logo = ($group->stream_logo) ?
|
$logo = ($group->stream_logo) ?
|
||||||
$group->stream_logo : User_group::defaultLogo(AVATAR_STREAM_SIZE);
|
$group->stream_logo : User_group::defaultLogo(AVATAR_STREAM_SIZE);
|
||||||
|
|
||||||
$this->out->element('img', array('src' => $logo,
|
$this->out->element('img', array('src' => $logo,
|
||||||
'width' => AVATAR_MINI_SIZE,
|
'width' => AVATAR_MINI_SIZE,
|
||||||
'height' => AVATAR_MINI_SIZE,
|
'height' => AVATAR_MINI_SIZE,
|
||||||
@ -95,6 +95,7 @@ class GroupSection extends Section
|
|||||||
'alt' => ($group->fullname) ?
|
'alt' => ($group->fullname) ?
|
||||||
$group->fullname :
|
$group->fullname :
|
||||||
$group->nickname));
|
$group->nickname));
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->element('span', 'fn org nickname', $group->nickname);
|
$this->out->element('span', 'fn org nickname', $group->nickname);
|
||||||
$this->out->elementEnd('a');
|
$this->out->elementEnd('a');
|
||||||
$this->out->elementEnd('span');
|
$this->out->elementEnd('span');
|
||||||
|
@ -376,9 +376,20 @@ class HTMLOutputter extends XMLOutputter
|
|||||||
$server = common_config('site', 'server');
|
$server = common_config('site', 'server');
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: protocol
|
$ssl = common_config('javascript', 'ssl');
|
||||||
|
|
||||||
$src = 'http://'.$server.$path.$src . '?version=' . STATUSNET_VERSION;
|
if (is_null($ssl)) { // null -> guess
|
||||||
|
if (common_config('site', 'ssl') == 'always' &&
|
||||||
|
!common_config('javascript', 'server')) {
|
||||||
|
$ssl = true;
|
||||||
|
} else {
|
||||||
|
$ssl = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = ($ssl) ? 'https' : 'http';
|
||||||
|
|
||||||
|
$src = $protocol.'://'.$server.$path.$src . '?version=' . STATUSNET_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->element('script', array('type' => $type,
|
$this->element('script', array('type' => $type,
|
||||||
|
@ -42,8 +42,7 @@ abstract class ImManager extends IoManager
|
|||||||
function __construct($imPlugin)
|
function __construct($imPlugin)
|
||||||
{
|
{
|
||||||
$this->plugin = $imPlugin;
|
$this->plugin = $imPlugin;
|
||||||
//TODO We only really want to register this event if this is the thread that runs the ImManager
|
$this->plugin->imManager = $this;
|
||||||
Event::addHandler('EndInitializeQueueManager', array($this, 'onEndInitializeQueueManager'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,17 +53,4 @@ abstract class ImManager extends IoManager
|
|||||||
{
|
{
|
||||||
throw new Exception('ImManager should be created using it\'s constructor, not the static get method');
|
throw new Exception('ImManager should be created using it\'s constructor, not the static get method');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Register notice queue handler
|
|
||||||
*
|
|
||||||
* @param QueueManager $manager
|
|
||||||
*
|
|
||||||
* @return boolean hook return
|
|
||||||
*/
|
|
||||||
function onEndInitializeQueueManager($manager)
|
|
||||||
{
|
|
||||||
$manager->connect($this->plugin->transport . '-out', new ImSenderQueueHandler($this->plugin, $this), 'imdaemon');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -507,8 +507,9 @@ abstract class ImPlugin extends Plugin
|
|||||||
*/
|
*/
|
||||||
function onEndInitializeQueueManager($manager)
|
function onEndInitializeQueueManager($manager)
|
||||||
{
|
{
|
||||||
$manager->connect($this->transport . '-in', new ImReceiverQueueHandler($this));
|
$manager->connect($this->transport . '-in', new ImReceiverQueueHandler($this), 'im');
|
||||||
$manager->connect($this->transport, new ImQueueHandler($this));
|
$manager->connect($this->transport, new ImQueueHandler($this));
|
||||||
|
$manager->connect($this->transport . '-out', new ImSenderQueueHandler($this), 'im');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,10 +25,9 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
|||||||
|
|
||||||
class ImSenderQueueHandler extends QueueHandler
|
class ImSenderQueueHandler extends QueueHandler
|
||||||
{
|
{
|
||||||
function __construct($plugin, $immanager)
|
function __construct($plugin)
|
||||||
{
|
{
|
||||||
$this->plugin = $plugin;
|
$this->plugin = $plugin;
|
||||||
$this->immanager = $immanager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +37,7 @@ class ImSenderQueueHandler extends QueueHandler
|
|||||||
*/
|
*/
|
||||||
function handle($data)
|
function handle($data)
|
||||||
{
|
{
|
||||||
return $this->immanager->send_raw_message($data);
|
return $this->plugin->imManager->send_raw_message($data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +59,10 @@ abstract class IoManager
|
|||||||
* your manager about each site you'll have to handle so you
|
* your manager about each site you'll have to handle so you
|
||||||
* can do any necessary per-site setup.
|
* can do any necessary per-site setup.
|
||||||
*
|
*
|
||||||
* @param string $site target site server name
|
* The new site will be the currently live configuration during
|
||||||
|
* this call.
|
||||||
*/
|
*/
|
||||||
public function addSite($site)
|
public function addSite()
|
||||||
{
|
{
|
||||||
/* no-op */
|
/* no-op */
|
||||||
}
|
}
|
||||||
|
@ -56,9 +56,9 @@ abstract class IoMaster
|
|||||||
$this->multiSite = $multiSite;
|
$this->multiSite = $multiSite;
|
||||||
}
|
}
|
||||||
if ($this->multiSite) {
|
if ($this->multiSite) {
|
||||||
$this->sites = $this->findAllSites();
|
$this->sites = StatusNet::findAllSites();
|
||||||
} else {
|
} else {
|
||||||
$this->sites = array(common_config('site', 'server'));
|
$this->sites = array(StatusNet::currentSite());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($this->sites)) {
|
if (empty($this->sites)) {
|
||||||
@ -66,9 +66,7 @@ abstract class IoMaster
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->sites as $site) {
|
foreach ($this->sites as $site) {
|
||||||
if ($site != common_config('site', 'server')) {
|
StatusNet::switchSite($site);
|
||||||
StatusNet::init($site);
|
|
||||||
}
|
|
||||||
$this->initManagers();
|
$this->initManagers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,61 +79,31 @@ abstract class IoMaster
|
|||||||
*/
|
*/
|
||||||
abstract function initManagers();
|
abstract function initManagers();
|
||||||
|
|
||||||
/**
|
|
||||||
* Pull all local sites from status_network table.
|
|
||||||
* @return array of hostnames
|
|
||||||
*/
|
|
||||||
protected function findAllSites()
|
|
||||||
{
|
|
||||||
$hosts = array();
|
|
||||||
$sn = new Status_network();
|
|
||||||
$sn->find();
|
|
||||||
while ($sn->fetch()) {
|
|
||||||
$hosts[] = $sn->getServerName();
|
|
||||||
}
|
|
||||||
return $hosts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate an i/o manager class for the current site.
|
* Instantiate an i/o manager class for the current site.
|
||||||
* If a multi-site capable handler is already present,
|
* If a multi-site capable handler is already present,
|
||||||
* we don't need to build a new one.
|
* we don't need to build a new one.
|
||||||
*
|
*
|
||||||
* @param string $class
|
* @param mixed $manager class name (to run $class::get()) or object
|
||||||
*/
|
*/
|
||||||
protected function instantiate($class)
|
protected function instantiate($manager)
|
||||||
{
|
{
|
||||||
if (is_string($class) && isset($this->singletons[$class])) {
|
if (is_string($manager)) {
|
||||||
// Already instantiated a multi-site-capable handler.
|
$manager = call_user_func(array($class, 'get'));
|
||||||
// Just let it know it should listen to this site too!
|
|
||||||
$this->singletons[$class]->addSite(common_config('site', 'server'));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$manager = $this->getManager($class);
|
$caps = $manager->multiSite();
|
||||||
|
if ($caps == IoManager::SINGLE_ONLY) {
|
||||||
if ($this->multiSite) {
|
if ($this->multiSite) {
|
||||||
$caps = $manager->multiSite();
|
|
||||||
if ($caps == IoManager::SINGLE_ONLY) {
|
|
||||||
throw new Exception("$class can't run with --all; aborting.");
|
throw new Exception("$class can't run with --all; aborting.");
|
||||||
}
|
}
|
||||||
if ($caps == IoManager::INSTANCE_PER_PROCESS) {
|
} else if ($caps == IoManager::INSTANCE_PER_PROCESS) {
|
||||||
// Save this guy for later!
|
$manager->addSite();
|
||||||
// We'll only need the one to cover multiple sites.
|
|
||||||
$this->singletons[$class] = $manager;
|
|
||||||
$manager->addSite(common_config('site', 'server'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->managers[] = $manager;
|
if (!in_array($manager, $this->managers, true)) {
|
||||||
}
|
// Only need to save singletons once
|
||||||
|
$this->managers[] = $manager;
|
||||||
protected function getManager($class)
|
|
||||||
{
|
|
||||||
if(is_object($class)){
|
|
||||||
return $class;
|
|
||||||
} else {
|
|
||||||
return call_user_func(array($class, 'get'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +118,7 @@ abstract class IoMaster
|
|||||||
{
|
{
|
||||||
$this->logState('init');
|
$this->logState('init');
|
||||||
$this->start();
|
$this->start();
|
||||||
|
$this->checkMemory(false);
|
||||||
|
|
||||||
while (!$this->shutdown) {
|
while (!$this->shutdown) {
|
||||||
$timeouts = array_values($this->pollTimeouts);
|
$timeouts = array_values($this->pollTimeouts);
|
||||||
@ -213,17 +182,24 @@ abstract class IoMaster
|
|||||||
/**
|
/**
|
||||||
* Check runtime memory usage, possibly triggering a graceful shutdown
|
* Check runtime memory usage, possibly triggering a graceful shutdown
|
||||||
* and thread respawn if we've crossed the soft limit.
|
* and thread respawn if we've crossed the soft limit.
|
||||||
|
*
|
||||||
|
* @param boolean $respawn if false we'll shut down instead of respawning
|
||||||
*/
|
*/
|
||||||
protected function checkMemory()
|
protected function checkMemory($respawn=true)
|
||||||
{
|
{
|
||||||
$memoryLimit = $this->softMemoryLimit();
|
$memoryLimit = $this->softMemoryLimit();
|
||||||
if ($memoryLimit > 0) {
|
if ($memoryLimit > 0) {
|
||||||
$usage = memory_get_usage();
|
$usage = memory_get_usage();
|
||||||
if ($usage > $memoryLimit) {
|
if ($usage > $memoryLimit) {
|
||||||
common_log(LOG_INFO, "Queue thread hit soft memory limit ($usage > $memoryLimit); gracefully restarting.");
|
common_log(LOG_INFO, "Queue thread hit soft memory limit ($usage > $memoryLimit); gracefully restarting.");
|
||||||
$this->requestRestart();
|
if ($respawn) {
|
||||||
|
$this->requestRestart();
|
||||||
|
} else {
|
||||||
|
$this->requestShutdown();
|
||||||
|
}
|
||||||
} else if (common_config('queue', 'debug_memory')) {
|
} else if (common_config('queue', 'debug_memory')) {
|
||||||
common_log(LOG_DEBUG, "Memory usage $usage");
|
$fmt = number_format($usage);
|
||||||
|
common_log(LOG_DEBUG, "Memory usage $fmt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -294,6 +294,7 @@ class NoticeListItem extends Widget
|
|||||||
}
|
}
|
||||||
$this->out->elementStart('a', $attrs);
|
$this->out->elementStart('a', $attrs);
|
||||||
$this->showAvatar();
|
$this->showAvatar();
|
||||||
|
$this->out->text(' ');
|
||||||
$this->showNickname();
|
$this->showNickname();
|
||||||
$this->out->elementEnd('a');
|
$this->out->elementEnd('a');
|
||||||
$this->out->elementEnd('span');
|
$this->out->elementEnd('span');
|
||||||
@ -432,8 +433,10 @@ class NoticeListItem extends Widget
|
|||||||
|
|
||||||
$url = $location->getUrl();
|
$url = $location->getUrl();
|
||||||
|
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->elementStart('span', array('class' => 'location'));
|
$this->out->elementStart('span', array('class' => 'location'));
|
||||||
$this->out->text(_('at'));
|
$this->out->text(_('at'));
|
||||||
|
$this->out->text(' ');
|
||||||
if (empty($url)) {
|
if (empty($url)) {
|
||||||
$this->out->element('span', array('class' => 'geo',
|
$this->out->element('span', array('class' => 'geo',
|
||||||
'title' => $latlon),
|
'title' => $latlon),
|
||||||
@ -473,9 +476,11 @@ class NoticeListItem extends Widget
|
|||||||
function showNoticeSource()
|
function showNoticeSource()
|
||||||
{
|
{
|
||||||
if ($this->notice->source) {
|
if ($this->notice->source) {
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->elementStart('span', 'source');
|
$this->out->elementStart('span', 'source');
|
||||||
$this->out->text(_('from'));
|
$this->out->text(_('from'));
|
||||||
$source_name = _($this->notice->source);
|
$source_name = _($this->notice->source);
|
||||||
|
$this->out->text(' ');
|
||||||
switch ($this->notice->source) {
|
switch ($this->notice->source) {
|
||||||
case 'web':
|
case 'web':
|
||||||
case 'xmpp':
|
case 'xmpp':
|
||||||
@ -540,6 +545,7 @@ class NoticeListItem extends Widget
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($hasConversation){
|
if ($hasConversation){
|
||||||
|
$this->out->text(' ');
|
||||||
$convurl = common_local_url('conversation',
|
$convurl = common_local_url('conversation',
|
||||||
array('id' => $this->notice->conversation));
|
array('id' => $this->notice->conversation));
|
||||||
$this->out->element('a', array('href' => $convurl.'#notice-'.$this->notice->id,
|
$this->out->element('a', array('href' => $convurl.'#notice-'.$this->notice->id,
|
||||||
@ -591,12 +597,14 @@ class NoticeListItem extends Widget
|
|||||||
function showReplyLink()
|
function showReplyLink()
|
||||||
{
|
{
|
||||||
if (common_logged_in()) {
|
if (common_logged_in()) {
|
||||||
|
$this->out->text(' ');
|
||||||
$reply_url = common_local_url('newnotice',
|
$reply_url = common_local_url('newnotice',
|
||||||
array('replyto' => $this->profile->nickname, 'inreplyto' => $this->notice->id));
|
array('replyto' => $this->profile->nickname, 'inreplyto' => $this->notice->id));
|
||||||
$this->out->elementStart('a', array('href' => $reply_url,
|
$this->out->elementStart('a', array('href' => $reply_url,
|
||||||
'class' => 'notice_reply',
|
'class' => 'notice_reply',
|
||||||
'title' => _('Reply to this notice')));
|
'title' => _('Reply to this notice')));
|
||||||
$this->out->text(_('Reply'));
|
$this->out->text(_('Reply'));
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->element('span', 'notice_id', $this->notice->id);
|
$this->out->element('span', 'notice_id', $this->notice->id);
|
||||||
$this->out->elementEnd('a');
|
$this->out->elementEnd('a');
|
||||||
}
|
}
|
||||||
@ -616,7 +624,7 @@ class NoticeListItem extends Widget
|
|||||||
|
|
||||||
if (!empty($user) &&
|
if (!empty($user) &&
|
||||||
($todel->profile_id == $user->id || $user->hasRight(Right::DELETEOTHERSNOTICE))) {
|
($todel->profile_id == $user->id || $user->hasRight(Right::DELETEOTHERSNOTICE))) {
|
||||||
|
$this->out->text(' ');
|
||||||
$deleteurl = common_local_url('deletenotice',
|
$deleteurl = common_local_url('deletenotice',
|
||||||
array('notice' => $todel->id));
|
array('notice' => $todel->id));
|
||||||
$this->out->element('a', array('href' => $deleteurl,
|
$this->out->element('a', array('href' => $deleteurl,
|
||||||
@ -635,6 +643,7 @@ class NoticeListItem extends Widget
|
|||||||
{
|
{
|
||||||
$user = common_current_user();
|
$user = common_current_user();
|
||||||
if ($user && $user->id != $this->notice->profile_id) {
|
if ($user && $user->id != $this->notice->profile_id) {
|
||||||
|
$this->out->text(' ');
|
||||||
$profile = $user->getProfile();
|
$profile = $user->getProfile();
|
||||||
if ($profile->hasRepeated($this->notice->id)) {
|
if ($profile->hasRepeated($this->notice->id)) {
|
||||||
$this->out->element('span', array('class' => 'repeated',
|
$this->out->element('span', array('class' => 'repeated',
|
||||||
|
@ -90,6 +90,7 @@ class NoticeSection extends Section
|
|||||||
'alt' => ($profile->fullname) ?
|
'alt' => ($profile->fullname) ?
|
||||||
$profile->fullname :
|
$profile->fullname :
|
||||||
$profile->nickname));
|
$profile->nickname));
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->element('span', 'fn nickname', $profile->nickname);
|
$this->out->element('span', 'fn nickname', $profile->nickname);
|
||||||
$this->out->elementEnd('a');
|
$this->out->elementEnd('a');
|
||||||
$this->out->elementEnd('span');
|
$this->out->elementEnd('span');
|
||||||
|
@ -191,6 +191,7 @@ class ProfileListItem extends Widget
|
|||||||
'alt' =>
|
'alt' =>
|
||||||
($this->profile->fullname) ? $this->profile->fullname :
|
($this->profile->fullname) ? $this->profile->fullname :
|
||||||
$this->profile->nickname));
|
$this->profile->nickname));
|
||||||
|
$this->out->text(' ');
|
||||||
$hasFN = (!empty($this->profile->fullname)) ? 'nickname' : 'fn nickname';
|
$hasFN = (!empty($this->profile->fullname)) ? 'nickname' : 'fn nickname';
|
||||||
$this->out->elementStart('span', $hasFN);
|
$this->out->elementStart('span', $hasFN);
|
||||||
$this->out->raw($this->highlight($this->profile->nickname));
|
$this->out->raw($this->highlight($this->profile->nickname));
|
||||||
@ -201,6 +202,7 @@ class ProfileListItem extends Widget
|
|||||||
function showFullName()
|
function showFullName()
|
||||||
{
|
{
|
||||||
if (!empty($this->profile->fullname)) {
|
if (!empty($this->profile->fullname)) {
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->elementStart('span', 'fn');
|
$this->out->elementStart('span', 'fn');
|
||||||
$this->out->raw($this->highlight($this->profile->fullname));
|
$this->out->raw($this->highlight($this->profile->fullname));
|
||||||
$this->out->elementEnd('span');
|
$this->out->elementEnd('span');
|
||||||
@ -210,6 +212,7 @@ class ProfileListItem extends Widget
|
|||||||
function showLocation()
|
function showLocation()
|
||||||
{
|
{
|
||||||
if (!empty($this->profile->location)) {
|
if (!empty($this->profile->location)) {
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->elementStart('span', 'location');
|
$this->out->elementStart('span', 'location');
|
||||||
$this->out->raw($this->highlight($this->profile->location));
|
$this->out->raw($this->highlight($this->profile->location));
|
||||||
$this->out->elementEnd('span');
|
$this->out->elementEnd('span');
|
||||||
@ -219,6 +222,7 @@ class ProfileListItem extends Widget
|
|||||||
function showHomepage()
|
function showHomepage()
|
||||||
{
|
{
|
||||||
if (!empty($this->profile->homepage)) {
|
if (!empty($this->profile->homepage)) {
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->elementStart('a', array('href' => $this->profile->homepage,
|
$this->out->elementStart('a', array('href' => $this->profile->homepage,
|
||||||
'class' => 'url'));
|
'class' => 'url'));
|
||||||
$this->out->raw($this->highlight($this->profile->homepage));
|
$this->out->raw($this->highlight($this->profile->homepage));
|
||||||
|
@ -85,6 +85,7 @@ class ProfileSection extends Section
|
|||||||
'href' => $profile->profileurl,
|
'href' => $profile->profileurl,
|
||||||
'rel' => 'contact member',
|
'rel' => 'contact member',
|
||||||
'class' => 'url'));
|
'class' => 'url'));
|
||||||
|
$this->out->text(' ');
|
||||||
$avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
|
$avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
|
||||||
$this->out->element('img', array('src' => (($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_MINI_SIZE)),
|
$this->out->element('img', array('src' => (($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_MINI_SIZE)),
|
||||||
'width' => AVATAR_MINI_SIZE,
|
'width' => AVATAR_MINI_SIZE,
|
||||||
@ -93,6 +94,7 @@ class ProfileSection extends Section
|
|||||||
'alt' => ($profile->fullname) ?
|
'alt' => ($profile->fullname) ?
|
||||||
$profile->fullname :
|
$profile->fullname :
|
||||||
$profile->nickname));
|
$profile->nickname));
|
||||||
|
$this->out->text(' ');
|
||||||
$this->out->element('span', 'fn nickname', $profile->nickname);
|
$this->out->element('span', 'fn nickname', $profile->nickname);
|
||||||
$this->out->elementEnd('a');
|
$this->out->elementEnd('a');
|
||||||
$this->out->elementEnd('span');
|
$this->out->elementEnd('span');
|
||||||
|
@ -39,9 +39,10 @@ abstract class QueueManager extends IoManager
|
|||||||
{
|
{
|
||||||
static $qm = null;
|
static $qm = null;
|
||||||
|
|
||||||
public $master = null;
|
protected $master = null;
|
||||||
public $handlers = array();
|
protected $handlers = array();
|
||||||
public $groups = array();
|
protected $groups = array();
|
||||||
|
protected $activeGroups = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory function to pull the appropriate QueueManager object
|
* Factory function to pull the appropriate QueueManager object
|
||||||
@ -162,7 +163,7 @@ abstract class QueueManager extends IoManager
|
|||||||
*/
|
*/
|
||||||
protected function encode($item)
|
protected function encode($item)
|
||||||
{
|
{
|
||||||
return serialize($object);
|
return serialize($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -192,40 +193,48 @@ abstract class QueueManager extends IoManager
|
|||||||
} else if (class_exists($class)) {
|
} else if (class_exists($class)) {
|
||||||
return new $class();
|
return new $class();
|
||||||
} else {
|
} else {
|
||||||
common_log(LOG_ERR, "Nonexistent handler class '$class' for queue '$queue'");
|
$this->_log(LOG_ERR, "Nonexistent handler class '$class' for queue '$queue'");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
common_log(LOG_ERR, "Requested handler for unkown queue '$queue'");
|
$this->_log(LOG_ERR, "Requested handler for unkown queue '$queue'");
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of registered queue transport names to be used
|
* Get a list of registered queue transport names to be used
|
||||||
* for this daemon.
|
* for listening in this daemon.
|
||||||
*
|
*
|
||||||
* @return array of strings
|
* @return array of strings
|
||||||
*/
|
*/
|
||||||
function getQueues()
|
function activeQueues()
|
||||||
{
|
{
|
||||||
$group = $this->activeGroup();
|
$queues = array();
|
||||||
return array_keys($this->groups[$group]);
|
foreach ($this->activeGroups as $group) {
|
||||||
|
if (isset($this->groups[$group])) {
|
||||||
|
$queues = array_merge($queues, $this->groups[$group]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys($queues);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the list of queue handlers
|
* Initialize the list of queue handlers for the current site.
|
||||||
*
|
*
|
||||||
* @event StartInitializeQueueManager
|
* @event StartInitializeQueueManager
|
||||||
* @event EndInitializeQueueManager
|
* @event EndInitializeQueueManager
|
||||||
*/
|
*/
|
||||||
function initialize()
|
function initialize()
|
||||||
{
|
{
|
||||||
// @fixme we'll want to be able to listen to particular queues...
|
$this->handlers = array();
|
||||||
|
$this->groups = array();
|
||||||
|
$this->groupsByTransport = array();
|
||||||
|
|
||||||
if (Event::handle('StartInitializeQueueManager', array($this))) {
|
if (Event::handle('StartInitializeQueueManager', array($this))) {
|
||||||
$this->connect('plugin', 'PluginQueueHandler');
|
$this->connect('distrib', 'DistribQueueHandler');
|
||||||
$this->connect('omb', 'OmbQueueHandler');
|
$this->connect('omb', 'OmbQueueHandler');
|
||||||
$this->connect('ping', 'PingQueueHandler');
|
$this->connect('ping', 'PingQueueHandler');
|
||||||
$this->connect('distrib', 'DistribQueueHandler');
|
|
||||||
if (common_config('sms', 'enabled')) {
|
if (common_config('sms', 'enabled')) {
|
||||||
$this->connect('sms', 'SmsQueueHandler');
|
$this->connect('sms', 'SmsQueueHandler');
|
||||||
}
|
}
|
||||||
@ -244,25 +253,41 @@ abstract class QueueManager extends IoManager
|
|||||||
* @param string $class class name or object instance
|
* @param string $class class name or object instance
|
||||||
* @param string $group
|
* @param string $group
|
||||||
*/
|
*/
|
||||||
public function connect($transport, $class, $group='queuedaemon')
|
public function connect($transport, $class, $group='main')
|
||||||
{
|
{
|
||||||
$this->handlers[$transport] = $class;
|
$this->handlers[$transport] = $class;
|
||||||
$this->groups[$group][$transport] = $class;
|
$this->groups[$group][$transport] = $class;
|
||||||
|
$this->groupsByTransport[$transport] = $group;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string queue group to use for this request
|
* Set the active group which will be used for listening.
|
||||||
|
* @param string $group
|
||||||
*/
|
*/
|
||||||
function activeGroup()
|
function setActiveGroup($group)
|
||||||
{
|
{
|
||||||
$group = 'queuedaemon';
|
$this->activeGroups = array($group);
|
||||||
if ($this->master) {
|
}
|
||||||
// hack hack
|
|
||||||
if ($this->master instanceof ImMaster) {
|
/**
|
||||||
return 'imdaemon';
|
* Set the active group(s) which will be used for listening.
|
||||||
}
|
* @param array $groups
|
||||||
|
*/
|
||||||
|
function setActiveGroups($groups)
|
||||||
|
{
|
||||||
|
$this->activeGroups = $groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string queue group for this queue
|
||||||
|
*/
|
||||||
|
function queueGroup($queue)
|
||||||
|
{
|
||||||
|
if (isset($this->groupsByTransport[$queue])) {
|
||||||
|
return $this->groupsByTransport[$queue];
|
||||||
|
} else {
|
||||||
|
throw new Exception("Requested group for unregistered transport $queue");
|
||||||
}
|
}
|
||||||
return $group;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,4 +311,15 @@ abstract class QueueManager extends IoManager
|
|||||||
$monitor->stats($key, $owners);
|
$monitor->stats($key, $owners);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function _log($level, $msg)
|
||||||
|
{
|
||||||
|
$class = get_class($this);
|
||||||
|
if ($this->activeGroups) {
|
||||||
|
$groups = ' (' . implode(',', $this->activeGroups) . ')';
|
||||||
|
} else {
|
||||||
|
$groups = '';
|
||||||
|
}
|
||||||
|
common_log($level, "$class$groups: $msg");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,18 +90,24 @@ abstract class SpawningDaemon extends Daemon
|
|||||||
while (count($children) > 0) {
|
while (count($children) > 0) {
|
||||||
$status = null;
|
$status = null;
|
||||||
$pid = pcntl_wait($status);
|
$pid = pcntl_wait($status);
|
||||||
if ($pid > 0 && pcntl_wifexited($status)) {
|
if ($pid > 0) {
|
||||||
$exitCode = pcntl_wexitstatus($status);
|
|
||||||
|
|
||||||
$i = array_search($pid, $children);
|
$i = array_search($pid, $children);
|
||||||
if ($i === false) {
|
if ($i === false) {
|
||||||
$this->log(LOG_ERR, "Unrecognized child pid $pid exited with status $exitCode");
|
$this->log(LOG_ERR, "Ignoring exit of unrecognized child pid $pid");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (pcntl_wifexited($status)) {
|
||||||
|
$exitCode = pcntl_wexitstatus($status);
|
||||||
|
$info = "status $exitCode";
|
||||||
|
} else if (pcntl_wifsignaled($status)) {
|
||||||
|
$exitCode = self::EXIT_ERR;
|
||||||
|
$signal = pcntl_wtermsig($status);
|
||||||
|
$info = "signal $signal";
|
||||||
|
}
|
||||||
unset($children[$i]);
|
unset($children[$i]);
|
||||||
|
|
||||||
if ($this->shouldRespawn($exitCode)) {
|
if ($this->shouldRespawn($exitCode)) {
|
||||||
$this->log(LOG_INFO, "Thread $i pid $pid exited with status $exitCode; respawing.");
|
$this->log(LOG_INFO, "Thread $i pid $pid exited with $info; respawing.");
|
||||||
|
|
||||||
$pid = pcntl_fork();
|
$pid = pcntl_fork();
|
||||||
if ($pid < 0) {
|
if ($pid < 0) {
|
||||||
|
@ -102,6 +102,60 @@ class StatusNet
|
|||||||
self::initPlugins();
|
self::initPlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get identifier of the currently active site configuration
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function currentSite()
|
||||||
|
{
|
||||||
|
return common_config('site', 'nickname');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change site configuration to site specified by nickname,
|
||||||
|
* if set up via Status_network. If not, sites other than
|
||||||
|
* the current will fail horribly.
|
||||||
|
*
|
||||||
|
* May throw exception or trigger a fatal error if the given
|
||||||
|
* site is missing or configured incorrectly.
|
||||||
|
*
|
||||||
|
* @param string $nickname
|
||||||
|
*/
|
||||||
|
public static function switchSite($nickname)
|
||||||
|
{
|
||||||
|
if ($nickname == StatusNet::currentSite()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sn = Status_network::staticGet($nickname);
|
||||||
|
if (empty($sn)) {
|
||||||
|
return false;
|
||||||
|
throw new Exception("No such site nickname '$nickname'");
|
||||||
|
}
|
||||||
|
|
||||||
|
$server = $sn->getServerName();
|
||||||
|
StatusNet::init($server);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull all local sites from status_network table.
|
||||||
|
*
|
||||||
|
* Behavior undefined if site is not configured via Status_network.
|
||||||
|
*
|
||||||
|
* @return array of nicknames
|
||||||
|
*/
|
||||||
|
public static function findAllSites()
|
||||||
|
{
|
||||||
|
$sites = array();
|
||||||
|
$sn = new Status_network();
|
||||||
|
$sn->find();
|
||||||
|
while ($sn->fetch()) {
|
||||||
|
$sites[] = $sn->nickname;
|
||||||
|
}
|
||||||
|
return $sites;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fire initialization events for all instantiated plugins.
|
* Fire initialization events for all instantiated plugins.
|
||||||
*/
|
*/
|
||||||
|
@ -63,6 +63,7 @@ class StompQueueManager extends QueueManager
|
|||||||
$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');
|
||||||
$this->control = common_config('queue', 'control_channel');
|
$this->control = common_config('queue', 'control_channel');
|
||||||
|
$this->subscriptions = array($this->control => $this->control);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,17 +76,25 @@ class StompQueueManager extends QueueManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record each site we'll be handling input for in this process,
|
* Record queue subscriptions we'll need to handle the current site.
|
||||||
* so we can listen to the necessary queues for it.
|
|
||||||
*
|
|
||||||
* @fixme possibly actually do subscription here to save another
|
|
||||||
* loop over all sites later?
|
|
||||||
* @fixme possibly don't assume it's the current site
|
|
||||||
*/
|
*/
|
||||||
public function addSite($server)
|
public function addSite()
|
||||||
{
|
{
|
||||||
$this->sites[] = $server;
|
$this->sites[] = StatusNet::currentSite();
|
||||||
|
|
||||||
|
// Set up handlers active for this site...
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
|
|
||||||
|
foreach ($this->activeGroups as $group) {
|
||||||
|
if (isset($this->groups[$group])) {
|
||||||
|
// Actual queues may be broken out or consolidated...
|
||||||
|
// Subscribe to all the target queues we'll need.
|
||||||
|
foreach ($this->groups[$group] as $transport => $class) {
|
||||||
|
$target = $this->queueName($transport);
|
||||||
|
$this->subscriptions[$target] = $target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,59 +130,11 @@ class StompQueueManager extends QueueManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate the appropriate QueueHandler class for the given queue.
|
* Saves an object into the queue item table.
|
||||||
*
|
*
|
||||||
|
* @param mixed $object
|
||||||
* @param string $queue
|
* @param string $queue
|
||||||
* @return mixed QueueHandler or null
|
|
||||||
*/
|
|
||||||
function getHandler($queue)
|
|
||||||
{
|
|
||||||
$handlers = $this->handlers[$this->currentSite()];
|
|
||||||
if (isset($handlers[$queue])) {
|
|
||||||
$class = $handlers[$queue];
|
|
||||||
if (class_exists($class)) {
|
|
||||||
return new $class();
|
|
||||||
} else {
|
|
||||||
common_log(LOG_ERR, "Nonexistent handler class '$class' for queue '$queue'");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
common_log(LOG_ERR, "Requested handler for unkown queue '$queue'");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of all registered queue transport names.
|
|
||||||
*
|
*
|
||||||
* @return array of strings
|
|
||||||
*/
|
|
||||||
function getQueues()
|
|
||||||
{
|
|
||||||
$group = $this->activeGroup();
|
|
||||||
$site = $this->currentSite();
|
|
||||||
if (empty($this->groups[$site][$group])) {
|
|
||||||
return array();
|
|
||||||
} else {
|
|
||||||
return array_keys($this->groups[$site][$group]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a queue transport name and handler class for your plugin.
|
|
||||||
* Only registered transports will be reliably picked up!
|
|
||||||
*
|
|
||||||
* @param string $transport
|
|
||||||
* @param string $class
|
|
||||||
* @param string $group
|
|
||||||
*/
|
|
||||||
public function connect($transport, $class, $group='queuedaemon')
|
|
||||||
{
|
|
||||||
$this->handlers[$this->currentSite()][$transport] = $class;
|
|
||||||
$this->groups[$this->currentSite()][$group][$transport] = $class;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* @throws StompException on connection or send error
|
||||||
*/
|
*/
|
||||||
@ -192,8 +153,11 @@ class StompQueueManager extends QueueManager
|
|||||||
*/
|
*/
|
||||||
protected function _doEnqueue($object, $queue, $idx)
|
protected function _doEnqueue($object, $queue, $idx)
|
||||||
{
|
{
|
||||||
$msg = $this->encode($object);
|
|
||||||
$rep = $this->logrep($object);
|
$rep = $this->logrep($object);
|
||||||
|
$envelope = array('site' => common_config('site', 'nickname'),
|
||||||
|
'handler' => $queue,
|
||||||
|
'payload' => $this->encode($object));
|
||||||
|
$msg = serialize($envelope);
|
||||||
|
|
||||||
$props = array('created' => common_sql_now());
|
$props = array('created' => common_sql_now());
|
||||||
if ($this->isPersistent($queue)) {
|
if ($this->isPersistent($queue)) {
|
||||||
@ -205,11 +169,11 @@ class StompQueueManager extends QueueManager
|
|||||||
$result = $con->send($this->queueName($queue), $msg, $props);
|
$result = $con->send($this->queueName($queue), $msg, $props);
|
||||||
|
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
common_log(LOG_ERR, "Error sending $rep to $queue queue on $host");
|
$this->_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 on $host");
|
$this->_log(LOG_DEBUG, "complete remote queueing $rep for $queue on $host");
|
||||||
$this->stats('enqueued', $queue);
|
$this->stats('enqueued', $queue);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -275,12 +239,14 @@ class StompQueueManager extends QueueManager
|
|||||||
$idx = $this->connectionFromSocket($socket);
|
$idx = $this->connectionFromSocket($socket);
|
||||||
$con = $this->cons[$idx];
|
$con = $this->cons[$idx];
|
||||||
$host = $con->getServer();
|
$host = $con->getServer();
|
||||||
|
$this->defaultIdx = $idx;
|
||||||
|
|
||||||
$ok = true;
|
$ok = true;
|
||||||
try {
|
try {
|
||||||
$frames = $con->readFrames();
|
$frames = $con->readFrames();
|
||||||
} catch (StompException $e) {
|
} catch (StompException $e) {
|
||||||
common_log(LOG_ERR, "Lost connection to $host: " . $e->getMessage());
|
$this->_log(LOG_ERR, "Lost connection to $host: " . $e->getMessage());
|
||||||
|
fclose($socket); // ???
|
||||||
$this->cons[$idx] = null;
|
$this->cons[$idx] = null;
|
||||||
$this->transaction[$idx] = null;
|
$this->transaction[$idx] = null;
|
||||||
$this->disconnect[$idx] = time();
|
$this->disconnect[$idx] = time();
|
||||||
@ -289,14 +255,17 @@ class StompQueueManager extends QueueManager
|
|||||||
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($idx, $frame)) {
|
if (!$this->handleControlSignal($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($idx, $frame);
|
$ok = $this->handleItem($frame) && $ok;
|
||||||
}
|
}
|
||||||
|
$this->ack($idx, $frame);
|
||||||
|
$this->commit($idx);
|
||||||
|
$this->begin($idx);
|
||||||
}
|
}
|
||||||
return $ok;
|
return $ok;
|
||||||
}
|
}
|
||||||
@ -333,22 +302,9 @@ class StompQueueManager extends QueueManager
|
|||||||
parent::start($master);
|
parent::start($master);
|
||||||
$this->_connectAll();
|
$this->_connectAll();
|
||||||
|
|
||||||
common_log(LOG_INFO, "Subscribing to $this->control");
|
|
||||||
foreach ($this->cons as $con) {
|
|
||||||
if ($con) {
|
|
||||||
$con->subscribe($this->control);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->sites) {
|
|
||||||
foreach ($this->sites as $server) {
|
|
||||||
StatusNet::init($server);
|
|
||||||
$this->doSubscribe();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->doSubscribe();
|
|
||||||
}
|
|
||||||
foreach ($this->cons as $i => $con) {
|
foreach ($this->cons as $i => $con) {
|
||||||
if ($con) {
|
if ($con) {
|
||||||
|
$this->doSubscribe($con);
|
||||||
$this->begin($i);
|
$this->begin($i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,9 +312,7 @@ class StompQueueManager extends QueueManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to all the queues we're going to need to handle...
|
* Close out any active connections.
|
||||||
*
|
|
||||||
* Side effects: in multi-site mode, may reset site configuration.
|
|
||||||
*
|
*
|
||||||
* @return bool return false on failure
|
* @return bool return false on failure
|
||||||
*/
|
*/
|
||||||
@ -376,15 +330,6 @@ class StompQueueManager extends QueueManager
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get identifier of the currently active site configuration
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function currentSite()
|
|
||||||
{
|
|
||||||
return common_config('site', 'server'); // @fixme switch to nickname
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazy open a single 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
|
* If multiple servers are configured, we let the Stomp client library
|
||||||
@ -441,6 +386,10 @@ class StompQueueManager extends QueueManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to manually reconnect to the Stomp server for the given
|
||||||
|
* slot. If successful, set up our subscriptions on it.
|
||||||
|
*/
|
||||||
protected function _reconnect($idx)
|
protected function _reconnect($idx)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@ -453,17 +402,7 @@ class StompQueueManager extends QueueManager
|
|||||||
$this->cons[$idx] = $con;
|
$this->cons[$idx] = $con;
|
||||||
$this->disconnect[$idx] = null;
|
$this->disconnect[$idx] = null;
|
||||||
|
|
||||||
// now we have to listen to everything...
|
$this->doSubscribe($con);
|
||||||
// @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);
|
$this->begin($idx);
|
||||||
} else {
|
} else {
|
||||||
// Try again later...
|
// Try again later...
|
||||||
@ -487,41 +426,15 @@ class StompQueueManager extends QueueManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to all enabled notice queues for the current site.
|
* Set up all our raw queue subscriptions on the given connection
|
||||||
|
* @param LiberalStomp $con
|
||||||
*/
|
*/
|
||||||
protected function doSubscribe()
|
protected function doSubscribe(LiberalStomp $con)
|
||||||
{
|
{
|
||||||
$site = $this->currentSite();
|
$host = $con->getServer();
|
||||||
$this->_connect();
|
foreach ($this->subscriptions as $queue) {
|
||||||
foreach ($this->getQueues() as $queue) {
|
$this->_log(LOG_INFO, "Subscribing to $queue on $host");
|
||||||
$rawqueue = $this->queueName($queue);
|
$con->subscribe($queue);
|
||||||
$this->subscriptions[$site][$queue] = $rawqueue;
|
|
||||||
$this->_log(LOG_INFO, "Subscribing to $rawqueue");
|
|
||||||
foreach ($this->cons as $con) {
|
|
||||||
if ($con) {
|
|
||||||
$con->subscribe($rawqueue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribe from all enabled notice queues for the current site.
|
|
||||||
*/
|
|
||||||
protected function doUnsubscribe()
|
|
||||||
{
|
|
||||||
$site = $this->currentSite();
|
|
||||||
$this->_connect();
|
|
||||||
if (!empty($this->subscriptions[$site])) {
|
|
||||||
foreach ($this->subscriptions[$site] as $queue => $rawqueue) {
|
|
||||||
$this->_log(LOG_INFO, "Unsubscribing from $rawqueue");
|
|
||||||
foreach ($this->cons as $con) {
|
|
||||||
if ($con) {
|
|
||||||
$con->unsubscribe($rawqueue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unset($this->subscriptions[$site][$queue]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,25 +447,29 @@ 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 success
|
||||||
*/
|
*/
|
||||||
protected function handleItem($idx, $frame)
|
protected function handleItem($frame)
|
||||||
{
|
{
|
||||||
$this->defaultIdx = $idx;
|
$host = $this->cons[$this->defaultIdx]->getServer();
|
||||||
|
$message = unserialize($frame->body);
|
||||||
|
$site = $message['site'];
|
||||||
|
$queue = $message['handler'];
|
||||||
|
|
||||||
list($site, $queue) = $this->parseDestination($frame->headers['destination']);
|
if ($this->isDeadletter($frame, $message)) {
|
||||||
if ($site != $this->currentSite()) {
|
$this->stats('deadletter', $queue);
|
||||||
$this->stats('switch');
|
return false;
|
||||||
StatusNet::init($site);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$host = $this->cons[$idx]->getServer();
|
// @fixme detect failing site switches
|
||||||
$item = $this->decode($frame->body);
|
$this->switchSite($site);
|
||||||
|
|
||||||
|
$item = $this->decode($message['payload']);
|
||||||
if (empty($item)) {
|
if (empty($item)) {
|
||||||
$this->_log(LOG_ERR, "Skipping empty or deleted item in queue $queue from $host");
|
$this->_log(LOG_ERR, "Skipping empty or deleted item in queue $queue from $host");
|
||||||
return true;
|
$this->stats('baditem', $queue);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
$info = $this->logrep($item) . " posted at " .
|
$info = $this->logrep($item) . " posted at " .
|
||||||
$frame->headers['created'] . " in queue $queue from $host";
|
$frame->headers['created'] . " in queue $queue from $host";
|
||||||
@ -561,16 +478,10 @@ class StompQueueManager extends QueueManager
|
|||||||
$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($idx, $frame);
|
|
||||||
$this->commit($idx);
|
|
||||||
$this->begin($idx);
|
|
||||||
$this->stats('badhandler', $queue);
|
$this->stats('badhandler', $queue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's an exception when handling,
|
|
||||||
// log the error and let it get requeued.
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$ok = $handler->handle($item);
|
$ok = $handler->handle($item);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@ -578,25 +489,80 @@ class StompQueueManager extends QueueManager
|
|||||||
$ok = false;
|
$ok = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$ok) {
|
if ($ok) {
|
||||||
|
$this->_log(LOG_INFO, "Successfully handled $info");
|
||||||
|
$this->stats('handled', $queue);
|
||||||
|
} else {
|
||||||
$this->_log(LOG_WARNING, "Failed handling $info");
|
$this->_log(LOG_WARNING, "Failed handling $info");
|
||||||
// FIXME we probably shouldn't have to do
|
// Requeing moves the item to the end of the line for its next try.
|
||||||
// this kind of queue management ourselves;
|
// @fixme add a manual retry count
|
||||||
// if we don't ack, it should resend...
|
|
||||||
$this->ack($idx, $frame);
|
|
||||||
$this->enqueue($item, $queue);
|
$this->enqueue($item, $queue);
|
||||||
$this->commit($idx);
|
|
||||||
$this->begin($idx);
|
|
||||||
$this->stats('requeued', $queue);
|
$this->stats('requeued', $queue);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_log(LOG_INFO, "Successfully handled $info");
|
return $ok;
|
||||||
$this->ack($idx, $frame);
|
}
|
||||||
$this->commit($idx);
|
|
||||||
$this->begin($idx);
|
/**
|
||||||
$this->stats('handled', $queue);
|
* Check if a redelivered message has been run through enough
|
||||||
return true;
|
* that we're going to give up on it.
|
||||||
|
*
|
||||||
|
* @param StompFrame $frame
|
||||||
|
* @param array $message unserialized message body
|
||||||
|
* @return boolean true if we should discard
|
||||||
|
*/
|
||||||
|
protected function isDeadLetter($frame, $message)
|
||||||
|
{
|
||||||
|
if (isset($frame->headers['redelivered']) && $frame->headers['redelivered'] == 'true') {
|
||||||
|
// Message was redelivered, possibly indicating a previous failure.
|
||||||
|
$msgId = $frame->headers['message-id'];
|
||||||
|
$site = $message['site'];
|
||||||
|
$queue = $message['handler'];
|
||||||
|
$msgInfo = "message $msgId for $site in queue $queue";
|
||||||
|
|
||||||
|
$deliveries = $this->incDeliveryCount($msgId);
|
||||||
|
if ($deliveries > common_config('queue', 'max_retries')) {
|
||||||
|
$info = "DEAD-LETTER FILE: Gave up after retry $deliveries on $msgInfo";
|
||||||
|
|
||||||
|
$outdir = common_config('queue', 'dead_letter_dir');
|
||||||
|
if ($outdir) {
|
||||||
|
$filename = $outdir . "/$site-$queue-" . rawurlencode($msgId);
|
||||||
|
$info .= ": dumping to $filename";
|
||||||
|
file_put_contents($filename, $message['payload']);
|
||||||
|
}
|
||||||
|
|
||||||
|
common_log(LOG_ERR, $info);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
common_log(LOG_INFO, "retry $deliveries on $msgInfo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update count of times we've re-encountered this message recently,
|
||||||
|
* triggered when we get a message marked as 'redelivered'.
|
||||||
|
*
|
||||||
|
* Requires a CLI-friendly cache configuration.
|
||||||
|
*
|
||||||
|
* @param string $msgId message-id header from message
|
||||||
|
* @return int number of retries recorded
|
||||||
|
*/
|
||||||
|
function incDeliveryCount($msgId)
|
||||||
|
{
|
||||||
|
$count = 0;
|
||||||
|
$cache = common_memcache();
|
||||||
|
if ($cache) {
|
||||||
|
$key = 'statusnet:stomp:message-retries:' . $msgId;
|
||||||
|
$count = $cache->increment($key);
|
||||||
|
if (!$count) {
|
||||||
|
$count = 1;
|
||||||
|
$cache->set($key, $count, null, 3600);
|
||||||
|
$got = $cache->get($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -629,13 +595,22 @@ class StompQueueManager extends QueueManager
|
|||||||
} else {
|
} else {
|
||||||
$this->_log(LOG_ERR, "Ignoring unrecognized control message: $message");
|
$this->_log(LOG_ERR, "Ignoring unrecognized control message: $message");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->ack($idx, $frame);
|
|
||||||
$this->commit($idx);
|
|
||||||
$this->begin($idx);
|
|
||||||
return $shutdown;
|
return $shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch site, if necessary, and reset current handler assignments
|
||||||
|
* @param string $site
|
||||||
|
*/
|
||||||
|
function switchSite($site)
|
||||||
|
{
|
||||||
|
if ($site != StatusNet::currentSite()) {
|
||||||
|
$this->stats('switch');
|
||||||
|
StatusNet::switchSite($site);
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@ -648,22 +623,17 @@ class StompQueueManager extends QueueManager
|
|||||||
if (empty($this->sites)) {
|
if (empty($this->sites)) {
|
||||||
if ($nickname == common_config('site', 'nickname')) {
|
if ($nickname == common_config('site', 'nickname')) {
|
||||||
StatusNet::init(common_config('site', 'server'));
|
StatusNet::init(common_config('site', 'server'));
|
||||||
$this->doUnsubscribe();
|
|
||||||
$this->doSubscribe();
|
|
||||||
} else {
|
} else {
|
||||||
$this->_log(LOG_INFO, "Ignoring update ping for other site $nickname");
|
$this->_log(LOG_INFO, "Ignoring update ping for other site $nickname");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$sn = Status_network::staticGet($nickname);
|
$sn = Status_network::staticGet($nickname);
|
||||||
if ($sn) {
|
if ($sn) {
|
||||||
$server = $sn->getServerName(); // @fixme do config-by-nick
|
$this->switchSite($nickname);
|
||||||
StatusNet::init($server);
|
if (!in_array($nickname, $this->sites)) {
|
||||||
if (empty($this->sites[$server])) {
|
$this->addSite();
|
||||||
$this->addSite($server);
|
|
||||||
}
|
}
|
||||||
$this->_log(LOG_INFO, "(Re)subscribing to queues for site $nickname / $server");
|
// @fixme update subscriptions, if applicable
|
||||||
$this->doUnsubscribe();
|
|
||||||
$this->doSubscribe();
|
|
||||||
$this->stats('siteupdate');
|
$this->stats('siteupdate');
|
||||||
} else {
|
} else {
|
||||||
$this->_log(LOG_ERR, "Ignoring ping for unrecognized new site $nickname");
|
$this->_log(LOG_ERR, "Ignoring ping for unrecognized new site $nickname");
|
||||||
@ -673,42 +643,47 @@ class StompQueueManager extends QueueManager
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Combines the queue_basename from configuration with the
|
* Combines the queue_basename from configuration with the
|
||||||
* site server name and queue name to give eg:
|
* group name for this queue to give eg:
|
||||||
*
|
*
|
||||||
* /queue/statusnet/identi.ca/sms
|
* /queue/statusnet/main
|
||||||
*
|
*
|
||||||
* @param string $queue
|
* @param string $queue
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function queueName($queue)
|
protected function queueName($queue)
|
||||||
{
|
{
|
||||||
return common_config('queue', 'queue_basename') .
|
$base = common_config('queue', 'queue_basename');
|
||||||
$this->currentSite() . '/' . $queue;
|
$group = $this->queueGroup($queue);
|
||||||
|
$breakout = $this->breakoutMode($queue);
|
||||||
|
if ($breakout == 'shared') {
|
||||||
|
return $base . "$group";
|
||||||
|
} else if ($breakout == 'handler') {
|
||||||
|
return $base . "$group/$queue";
|
||||||
|
} else if ($breakout == 'site') {
|
||||||
|
$site = StatusNet::currentSite();
|
||||||
|
return $base . "$group/$queue/$site";
|
||||||
|
}
|
||||||
|
throw Exception("Unrecognized queue breakout mode '$breakout' for '$queue'");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the site and queue name from the server-side queue.
|
* Get the breakout mode for the given queue on the current site.
|
||||||
*
|
*
|
||||||
* @param string queue destination (eg '/queue/statusnet/identi.ca/sms')
|
* @param string $queue
|
||||||
* @return array of site and queue: ('identi.ca','sms') or false if unrecognized
|
* @return string one of 'shared', 'handler', 'site'
|
||||||
*/
|
*/
|
||||||
protected function parseDestination($dest)
|
protected function breakoutMode($queue)
|
||||||
{
|
{
|
||||||
$prefix = common_config('queue', 'queue_basename');
|
$breakout = common_config('queue', 'breakout');
|
||||||
if (substr($dest, 0, strlen($prefix)) == $prefix) {
|
if (isset($breakout[$queue])) {
|
||||||
$rest = substr($dest, strlen($prefix));
|
return $breakout[$queue];
|
||||||
return explode("/", $rest, 2);
|
} else if (isset($breakout['*'])) {
|
||||||
|
return $breakout['*'];
|
||||||
} else {
|
} else {
|
||||||
common_log(LOG_ERR, "Got a message from unrecognized stomp queue: $dest");
|
return 'shared';
|
||||||
return array(false, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _log($level, $msg)
|
|
||||||
{
|
|
||||||
common_log($level, 'StompQueueManager: '.$msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function begin($idx)
|
protected function begin($idx)
|
||||||
{
|
{
|
||||||
if ($this->useTransactions) {
|
if ($this->useTransactions) {
|
||||||
|
@ -110,9 +110,20 @@ class Theme
|
|||||||
$server = common_config('site', 'server');
|
$server = common_config('site', 'server');
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: protocol
|
$ssl = common_config('theme', 'ssl');
|
||||||
|
|
||||||
$this->path = 'http://'.$server.$path.$name;
|
if (is_null($ssl)) { // null -> guess
|
||||||
|
if (common_config('site', 'ssl') == 'always' &&
|
||||||
|
!common_config('theme', 'server')) {
|
||||||
|
$ssl = true;
|
||||||
|
} else {
|
||||||
|
$ssl = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$protocol = ($ssl) ? 'https' : 'http';
|
||||||
|
|
||||||
|
$this->path = $protocol . '://'.$server.$path.$name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,9 +238,12 @@ class UserProfile extends Widget
|
|||||||
|
|
||||||
if (Event::handle('StartProfilePageActionsElements', array(&$this->out, $this->profile))) {
|
if (Event::handle('StartProfilePageActionsElements', array(&$this->out, $this->profile))) {
|
||||||
if (empty($cur)) { // not logged in
|
if (empty($cur)) { // not logged in
|
||||||
$this->out->elementStart('li', 'entity_subscribe');
|
if (Event::handle('StartProfileRemoteSubscribe', array(&$this->out, $this->profile))) {
|
||||||
$this->showRemoteSubscribeLink();
|
$this->out->elementStart('li', 'entity_subscribe');
|
||||||
$this->out->elementEnd('li');
|
$this->showRemoteSubscribeLink();
|
||||||
|
$this->out->elementEnd('li');
|
||||||
|
Event::handle('EndProfileRemoteSubscribe', array(&$this->out, $this->profile));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($cur->id == $this->profile->id) { // your own page
|
if ($cur->id == $this->profile->id) { // your own page
|
||||||
$this->out->elementStart('li', 'entity_edit');
|
$this->out->elementStart('li', 'entity_edit');
|
||||||
|
54
lib/util.php
54
lib/util.php
@ -1000,7 +1000,6 @@ function common_enqueue_notice($notice)
|
|||||||
if (Event::hasHandler('HandleQueuedNotice')) {
|
if (Event::hasHandler('HandleQueuedNotice')) {
|
||||||
$transports[] = 'plugin';
|
$transports[] = 'plugin';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// @fixme move these checks into QueueManager and/or individual handlers
|
// @fixme move these checks into QueueManager and/or individual handlers
|
||||||
if ($notice->is_local == Notice::LOCAL_PUBLIC ||
|
if ($notice->is_local == Notice::LOCAL_PUBLIC ||
|
||||||
@ -1558,3 +1557,56 @@ function common_client_ip()
|
|||||||
|
|
||||||
return array($proxy, $ip);
|
return array($proxy, $ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function common_url_to_nickname($url)
|
||||||
|
{
|
||||||
|
static $bad = array('query', 'user', 'password', 'port', 'fragment');
|
||||||
|
|
||||||
|
$parts = parse_url($url);
|
||||||
|
|
||||||
|
# If any of these parts exist, this won't work
|
||||||
|
|
||||||
|
foreach ($bad as $badpart) {
|
||||||
|
if (array_key_exists($badpart, $parts)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# We just have host and/or path
|
||||||
|
|
||||||
|
# If it's just a host...
|
||||||
|
if (array_key_exists('host', $parts) &&
|
||||||
|
(!array_key_exists('path', $parts) || strcmp($parts['path'], '/') == 0))
|
||||||
|
{
|
||||||
|
$hostparts = explode('.', $parts['host']);
|
||||||
|
|
||||||
|
# Try to catch common idiom of nickname.service.tld
|
||||||
|
|
||||||
|
if ((count($hostparts) > 2) &&
|
||||||
|
(strlen($hostparts[count($hostparts) - 2]) > 3) && # try to skip .co.uk, .com.au
|
||||||
|
(strcmp($hostparts[0], 'www') != 0))
|
||||||
|
{
|
||||||
|
return common_nicknamize($hostparts[0]);
|
||||||
|
} else {
|
||||||
|
# Do the whole hostname
|
||||||
|
return common_nicknamize($parts['host']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (array_key_exists('path', $parts)) {
|
||||||
|
# Strip starting, ending slashes
|
||||||
|
$path = preg_replace('@/$@', '', $parts['path']);
|
||||||
|
$path = preg_replace('@^/@', '', $path);
|
||||||
|
if (strpos($path, '/') === false) {
|
||||||
|
return common_nicknamize($path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function common_nicknamize($str)
|
||||||
|
{
|
||||||
|
$str = preg_replace('/\W/', '', $str);
|
||||||
|
return strtolower($str);
|
||||||
|
}
|
||||||
|
@ -51,6 +51,8 @@ if (!defined('STATUSNET')) {
|
|||||||
|
|
||||||
class MemcachePlugin extends Plugin
|
class MemcachePlugin extends Plugin
|
||||||
{
|
{
|
||||||
|
static $cacheInitialized = false;
|
||||||
|
|
||||||
private $_conn = null;
|
private $_conn = null;
|
||||||
public $servers = array('127.0.0.1;11211');
|
public $servers = array('127.0.0.1;11211');
|
||||||
|
|
||||||
@ -71,10 +73,21 @@ class MemcachePlugin extends Plugin
|
|||||||
|
|
||||||
function onInitializePlugin()
|
function onInitializePlugin()
|
||||||
{
|
{
|
||||||
if (is_null($this->persistent)) {
|
if (self::$cacheInitialized) {
|
||||||
|
$this->persistent = true;
|
||||||
|
} else {
|
||||||
|
// If we're a parent command-line process we need
|
||||||
|
// to be able to close out the connection after
|
||||||
|
// forking, so disable persistence.
|
||||||
|
//
|
||||||
|
// We'll turn it back on again the second time
|
||||||
|
// through which will either be in a child process,
|
||||||
|
// or a single-process script which is switching
|
||||||
|
// configurations.
|
||||||
$this->persistent = (php_sapi_name() == 'cli') ? false : true;
|
$this->persistent = (php_sapi_name() == 'cli') ? false : true;
|
||||||
}
|
}
|
||||||
$this->_ensureConn();
|
$this->_ensureConn();
|
||||||
|
self::$cacheInitialized = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +134,24 @@ class MemcachePlugin extends Plugin
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically increment an existing numeric key value.
|
||||||
|
* Existing expiration time will not be changed.
|
||||||
|
*
|
||||||
|
* @param string &$key in; Key to use for lookups
|
||||||
|
* @param int &$step in; Amount to increment (default 1)
|
||||||
|
* @param mixed &$value out; Incremented value, or false if key not set.
|
||||||
|
*
|
||||||
|
* @return boolean hook success
|
||||||
|
*/
|
||||||
|
function onStartCacheIncrement(&$key, &$step, &$value)
|
||||||
|
{
|
||||||
|
$this->_ensureConn();
|
||||||
|
$value = $this->_conn->increment($key, $step);
|
||||||
|
Event::handle('EndCacheIncrement', array($key, $step, $value));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a value associated with a key
|
* Delete a value associated with a key
|
||||||
*
|
*
|
||||||
|
@ -63,9 +63,9 @@ class OStatusPlugin extends Plugin
|
|||||||
$m->connect('main/ostatus?nickname=:nickname',
|
$m->connect('main/ostatus?nickname=:nickname',
|
||||||
array('action' => 'ostatusinit'), array('nickname' => '[A-Za-z0-9_-]+'));
|
array('action' => 'ostatusinit'), array('nickname' => '[A-Za-z0-9_-]+'));
|
||||||
$m->connect('main/ostatussub',
|
$m->connect('main/ostatussub',
|
||||||
array('action' => 'ostatussub'));
|
array('action' => 'ostatussub'));
|
||||||
$m->connect('main/ostatussub',
|
$m->connect('main/ostatussub',
|
||||||
array('action' => 'ostatussub'), array('feed' => '[A-Za-z0-9\.\/\:]+'));
|
array('action' => 'ostatussub'), array('feed' => '[A-Za-z0-9\.\/\:]+'));
|
||||||
|
|
||||||
// PuSH actions
|
// PuSH actions
|
||||||
$m->connect('main/push/hub', array('action' => 'pushhub'));
|
$m->connect('main/push/hub', array('action' => 'pushhub'));
|
||||||
@ -112,35 +112,34 @@ class OStatusPlugin extends Plugin
|
|||||||
* Set up a PuSH hub link to our internal link for canonical timeline
|
* Set up a PuSH hub link to our internal link for canonical timeline
|
||||||
* Atom feeds for users and groups.
|
* Atom feeds for users and groups.
|
||||||
*/
|
*/
|
||||||
function onStartApiAtom(Action $action)
|
function onStartApiAtom(AtomNoticeFeed $feed)
|
||||||
{
|
{
|
||||||
if ($action instanceof ApiTimelineUserAction) {
|
$id = null;
|
||||||
|
|
||||||
|
if ($feed instanceof AtomUserNoticeFeed) {
|
||||||
$salmonAction = 'salmon';
|
$salmonAction = 'salmon';
|
||||||
} else if ($action instanceof ApiTimelineGroupAction) {
|
$id = $feed->getUser()->id;
|
||||||
|
} else if ($feed instanceof AtomGroupNoticeFeed) {
|
||||||
$salmonAction = 'salmongroup';
|
$salmonAction = 'salmongroup';
|
||||||
|
$id = $feed->getGroup()->id;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $action->arg('id');
|
if (!empty($id)) {
|
||||||
if (strval(intval($id)) === strval($id)) {
|
|
||||||
// Canonical form of id in URL? These are used for OStatus syndication.
|
|
||||||
|
|
||||||
$hub = common_config('ostatus', 'hub');
|
$hub = common_config('ostatus', 'hub');
|
||||||
if (empty($hub)) {
|
if (empty($hub)) {
|
||||||
// Updates will be handled through our internal PuSH hub.
|
// Updates will be handled through our internal PuSH hub.
|
||||||
$hub = common_local_url('pushhub');
|
$hub = common_local_url('pushhub');
|
||||||
}
|
}
|
||||||
$action->element('link', array('rel' => 'hub',
|
$feed->addLink($hub, array('rel' => 'hub'));
|
||||||
'href' => $hub));
|
|
||||||
|
|
||||||
// Also, we'll add in the salmon link
|
// Also, we'll add in the salmon link
|
||||||
$salmon = common_local_url($salmonAction, array('id' => $id));
|
$salmon = common_local_url($salmonAction, array('id' => $id));
|
||||||
$action->element('link', array('rel' => 'salmon',
|
$feed->addLink($salmon, array('rel' => 'salmon'));
|
||||||
'href' => $salmon));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the feed settings page to the Connect Settings menu
|
* Add the feed settings page to the Connect Settings menu
|
||||||
*
|
*
|
||||||
@ -189,7 +188,7 @@ class OStatusPlugin extends Plugin
|
|||||||
/**
|
/**
|
||||||
* Add in an OStatus subscribe button
|
* Add in an OStatus subscribe button
|
||||||
*/
|
*/
|
||||||
function onStartProfilePageActionsElements($output, $profile)
|
function onStartProfileRemoteSubscribe($output, $profile)
|
||||||
{
|
{
|
||||||
$cur = common_current_user();
|
$cur = common_current_user();
|
||||||
|
|
||||||
@ -200,10 +199,12 @@ class OStatusPlugin extends Plugin
|
|||||||
array('nickname' => $profile->nickname));
|
array('nickname' => $profile->nickname));
|
||||||
$output->element('a', array('href' => $url,
|
$output->element('a', array('href' => $url,
|
||||||
'class' => 'entity_remote_subscribe'),
|
'class' => 'entity_remote_subscribe'),
|
||||||
_m('OStatus'));
|
_m('Subscribe'));
|
||||||
|
|
||||||
$output->elementEnd('li');
|
$output->elementEnd('li');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,29 +218,34 @@ class OStatusPlugin extends Plugin
|
|||||||
$count = preg_match_all('/(\w+\.)*\w+@(\w+\.)*\w+(\w+\-\w+)*\.\w+/', $notice->content, $matches);
|
$count = preg_match_all('/(\w+\.)*\w+@(\w+\.)*\w+(\w+\-\w+)*\.\w+/', $notice->content, $matches);
|
||||||
if ($count) {
|
if ($count) {
|
||||||
foreach ($matches[0] as $webfinger) {
|
foreach ($matches[0] as $webfinger) {
|
||||||
|
|
||||||
|
// FIXME: look up locally first
|
||||||
|
|
||||||
// Check to see if we've got an actual webfinger
|
// Check to see if we've got an actual webfinger
|
||||||
$w = new Webfinger;
|
$w = new Webfinger;
|
||||||
|
|
||||||
$endpoint_uri = '';
|
$endpoint_uri = '';
|
||||||
|
|
||||||
$result = $w->lookup($webfinger);
|
$result = $w->lookup($webfinger);
|
||||||
if (empty($result)) {
|
if (empty($result)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($result->links as $link) {
|
foreach ($result->links as $link) {
|
||||||
if ($link['rel'] == 'salmon') {
|
if ($link['rel'] == 'salmon') {
|
||||||
$endpoint_uri = $link['href'];
|
$endpoint_uri = $link['href'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($endpoint_uri)) {
|
if (empty($endpoint_uri)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: this needs to go out in a queue handler
|
||||||
|
|
||||||
$xml = '<?xml version="1.0" encoding="UTF-8" ?>';
|
$xml = '<?xml version="1.0" encoding="UTF-8" ?>';
|
||||||
$xml .= $notice->asAtomEntry();
|
$xml .= $notice->asAtomEntry();
|
||||||
|
|
||||||
$salmon = new Salmon();
|
$salmon = new Salmon();
|
||||||
$salmon->post($endpoint_uri, $xml);
|
$salmon->post($endpoint_uri, $xml);
|
||||||
}
|
}
|
||||||
@ -273,4 +279,14 @@ class OStatusPlugin extends Plugin
|
|||||||
$schema->ensureTable('hubsub', HubSub::schemaDef());
|
$schema->ensureTable('hubsub', HubSub::schemaDef());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onEndShowStatusNetStyles($action) {
|
||||||
|
$action->cssLink(common_path('plugins/OStatus/theme/base/css/ostatus.css'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEndShowStatusNetScripts($action) {
|
||||||
|
$action->script(common_path('plugins/OStatus/js/ostatus.js'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,9 +67,21 @@ class OStatusInitAction extends Action
|
|||||||
|
|
||||||
function showForm($err = null)
|
function showForm($err = null)
|
||||||
{
|
{
|
||||||
$this->err = $err;
|
$this->err = $err;
|
||||||
$this->showPage();
|
if ($this->boolean('ajax')) {
|
||||||
|
header('Content-Type: text/xml;charset=utf-8');
|
||||||
|
$this->xw->startDocument('1.0', 'UTF-8');
|
||||||
|
$this->elementStart('html');
|
||||||
|
$this->elementStart('head');
|
||||||
|
$this->element('title', null, _('Subscribe to user'));
|
||||||
|
$this->elementEnd('head');
|
||||||
|
$this->elementStart('body');
|
||||||
|
$this->showContent();
|
||||||
|
$this->elementEnd('body');
|
||||||
|
$this->elementEnd('html');
|
||||||
|
} else {
|
||||||
|
$this->showPage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showContent()
|
function showContent()
|
||||||
@ -79,15 +91,15 @@ class OStatusInitAction extends Action
|
|||||||
'class' => 'form_settings',
|
'class' => 'form_settings',
|
||||||
'action' => common_local_url('ostatusinit')));
|
'action' => common_local_url('ostatusinit')));
|
||||||
$this->elementStart('fieldset');
|
$this->elementStart('fieldset');
|
||||||
$this->element('legend', _('Subscribe to a remote user'));
|
$this->element('legend', null, sprintf(_('Subscribe to %s'), $this->nickname));
|
||||||
$this->hidden('token', common_session_token());
|
$this->hidden('token', common_session_token());
|
||||||
|
|
||||||
$this->elementStart('ul', 'form_data');
|
$this->elementStart('ul', 'form_data');
|
||||||
$this->elementStart('li');
|
$this->elementStart('li', array('id' => 'ostatus_nickname'));
|
||||||
$this->input('nickname', _('User nickname'), $this->nickname,
|
$this->input('nickname', _('User nickname'), $this->nickname,
|
||||||
_('Nickname of the user you want to follow'));
|
_('Nickname of the user you want to follow'));
|
||||||
$this->elementEnd('li');
|
$this->elementEnd('li');
|
||||||
$this->elementStart('li');
|
$this->elementStart('li', array('id' => 'ostatus_profile'));
|
||||||
$this->input('acct', _('Profile Account'), $this->acct,
|
$this->input('acct', _('Profile Account'), $this->acct,
|
||||||
_('Your account id (i.e. user@identi.ca)'));
|
_('Your account id (i.e. user@identi.ca)'));
|
||||||
$this->elementEnd('li');
|
$this->elementEnd('li');
|
||||||
@ -95,7 +107,7 @@ class OStatusInitAction extends Action
|
|||||||
$this->submit('submit', _('Subscribe'));
|
$this->submit('submit', _('Subscribe'));
|
||||||
$this->elementEnd('fieldset');
|
$this->elementEnd('fieldset');
|
||||||
$this->elementEnd('form');
|
$this->elementEnd('form');
|
||||||
}
|
}
|
||||||
|
|
||||||
function ostatusConnect()
|
function ostatusConnect()
|
||||||
{
|
{
|
||||||
@ -125,4 +137,4 @@ class OStatusInitAction extends Action
|
|||||||
return _('OStatus Connect');
|
return _('OStatus Connect');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ class OStatusSubAction extends Action
|
|||||||
$this->elementStart('fieldset', array('id' => 'settings_feeds'));
|
$this->elementStart('fieldset', array('id' => 'settings_feeds'));
|
||||||
|
|
||||||
$this->elementStart('ul', 'form_data');
|
$this->elementStart('ul', 'form_data');
|
||||||
$this->elementStart('li', array('id' => 'settings_twitter_login_button'));
|
$this->elementStart('li');
|
||||||
$this->input('feedurl', _('Feed URL'), $this->feedurl, _('Enter the URL of a PubSubHubbub-enabled feed'));
|
$this->input('feedurl', _('Feed URL'), $this->feedurl, _('Enter the URL of a PubSubHubbub-enabled feed'));
|
||||||
$this->elementEnd('li');
|
$this->elementEnd('li');
|
||||||
$this->elementEnd('ul');
|
$this->elementEnd('ul');
|
||||||
@ -223,4 +223,4 @@ class OStatusSubAction extends Action
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ class SalmonAction extends Action
|
|||||||
// XXX: check that document element is Atom entry
|
// XXX: check that document element is Atom entry
|
||||||
// XXX: check the signature
|
// XXX: check the signature
|
||||||
|
|
||||||
$this->act = Activity::fromAtomEntry($dom->documentElement);
|
$this->act = new Activity($dom->documentElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle($args)
|
function handle($args)
|
||||||
|
@ -29,15 +29,15 @@ PuSH subscription flow:
|
|||||||
generate random verification token
|
generate random verification token
|
||||||
save to verify_token
|
save to verify_token
|
||||||
sends a sub request to the hub...
|
sends a sub request to the hub...
|
||||||
|
|
||||||
main/push/callback
|
main/push/callback
|
||||||
hub sends confirmation back to us via GET
|
hub sends confirmation back to us via GET
|
||||||
We verify the request, then echo back the challenge.
|
We verify the request, then echo back the challenge.
|
||||||
On our end, we save the time we subscribed and the lease expiration
|
On our end, we save the time we subscribed and the lease expiration
|
||||||
|
|
||||||
main/push/callback
|
main/push/callback
|
||||||
hub sends us updates via POST
|
hub sends us updates via POST
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class FeedDBException extends FeedSubException
|
class FeedDBException extends FeedSubException
|
||||||
@ -75,7 +75,6 @@ class Ostatus_profile extends Memcached_DataObject
|
|||||||
public $created;
|
public $created;
|
||||||
public $lastupdate;
|
public $lastupdate;
|
||||||
|
|
||||||
|
|
||||||
public /*static*/ function staticGet($k, $v=null)
|
public /*static*/ function staticGet($k, $v=null)
|
||||||
{
|
{
|
||||||
return parent::staticGet(__CLASS__, $k, $v);
|
return parent::staticGet(__CLASS__, $k, $v);
|
||||||
@ -107,7 +106,7 @@ class Ostatus_profile extends Memcached_DataObject
|
|||||||
'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
|
'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
|
||||||
'lastupdate' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
|
'lastupdate' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function schemaDef()
|
static function schemaDef()
|
||||||
{
|
{
|
||||||
return array(new ColumnDef('id', 'integer',
|
return array(new ColumnDef('id', 'integer',
|
||||||
@ -486,7 +485,7 @@ class Ostatus_profile extends Memcached_DataObject
|
|||||||
}
|
}
|
||||||
if ($this->salmonuri) {
|
if ($this->salmonuri) {
|
||||||
$text = 'update'; // @fixme
|
$text = 'update'; // @fixme
|
||||||
$id = 'tag:' . common_config('site', 'server') .
|
$id = 'tag:' . common_config('site', 'server') .
|
||||||
':' . $verb .
|
':' . $verb .
|
||||||
':' . $actor->id .
|
':' . $actor->id .
|
||||||
':' . time(); // @fixme
|
':' . time(); // @fixme
|
||||||
@ -589,7 +588,7 @@ class Ostatus_profile extends Memcached_DataObject
|
|||||||
require_once "XML/Feed/Parser.php";
|
require_once "XML/Feed/Parser.php";
|
||||||
$feed = new XML_Feed_Parser($xml, false, false, true);
|
$feed = new XML_Feed_Parser($xml, false, false, true);
|
||||||
$munger = new FeedMunger($feed);
|
$munger = new FeedMunger($feed);
|
||||||
|
|
||||||
$hits = 0;
|
$hits = 0;
|
||||||
foreach ($feed as $index => $entry) {
|
foreach ($feed as $index => $entry) {
|
||||||
// @fixme this might sort in wrong order if we get multiple updates
|
// @fixme this might sort in wrong order if we get multiple updates
|
||||||
@ -598,9 +597,10 @@ class Ostatus_profile extends Memcached_DataObject
|
|||||||
|
|
||||||
// Double-check for oldies
|
// Double-check for oldies
|
||||||
// @fixme this could explode horribly for multiple feeds on a blog. sigh
|
// @fixme this could explode horribly for multiple feeds on a blog. sigh
|
||||||
$dupe = new Notice();
|
|
||||||
$dupe->uri = $notice->uri;
|
$dupe = Notice::staticGet('uri', $notice->uri);
|
||||||
if ($dupe->find(true)) {
|
|
||||||
|
if (!empty($dupe)) {
|
||||||
common_log(LOG_WARNING, __METHOD__ . ": tried to save dupe notice for entry {$notice->uri} of feed {$this->feeduri}");
|
common_log(LOG_WARNING, __METHOD__ . ": tried to save dupe notice for entry {$notice->uri} of feed {$this->feeduri}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
60
plugins/OStatus/js/ostatus.js
Normal file
60
plugins/OStatus/js/ostatus.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
SN.U.DialogBox = {
|
||||||
|
Subscribe: function(a) {
|
||||||
|
var f = a.parent().find('#form_ostatus_connect');
|
||||||
|
if (f.length > 0) {
|
||||||
|
f.show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
dataType: 'xml',
|
||||||
|
url: a[0].href+'&ajax=1',
|
||||||
|
beforeSend: function(formData) {
|
||||||
|
a.addClass('processing');
|
||||||
|
},
|
||||||
|
error: function (xhr, textStatus, errorThrown) {
|
||||||
|
alert(errorThrown || textStatus);
|
||||||
|
},
|
||||||
|
success: function(data, textStatus, xhr) {
|
||||||
|
if (typeof($('form', data)[0]) != 'undefined') {
|
||||||
|
a.after(document._importNode($('form', data)[0], true));
|
||||||
|
|
||||||
|
var form = a.parent().find('#form_ostatus_connect');
|
||||||
|
|
||||||
|
form
|
||||||
|
.addClass('dialogbox')
|
||||||
|
.append('<button class="close">×</button>');
|
||||||
|
|
||||||
|
form
|
||||||
|
.find('.submit')
|
||||||
|
.addClass('submit_dialogbox')
|
||||||
|
.removeClass('submit')
|
||||||
|
.bind('click', function() {
|
||||||
|
form.addClass('processing');
|
||||||
|
});
|
||||||
|
|
||||||
|
form.find('button.close').click(function(){
|
||||||
|
form.hide();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
form.find('#acct').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
a.removeClass('processing');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SN.Init.Subscribe = function() {
|
||||||
|
$('.entity_subscribe a').live('click', function() { SN.U.DialogBox.Subscribe($(this)); return false; });
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
if ($('.entity_subscribe .entity_remote_subscribe').length > 0) {
|
||||||
|
SN.Init.Subscribe();
|
||||||
|
}
|
||||||
|
});
|
@ -31,7 +31,75 @@ if (!defined('STATUSNET')) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActivityNoun
|
/**
|
||||||
|
* Utilities for turning DOMish things into Activityish things
|
||||||
|
*
|
||||||
|
* Some common functions that I didn't have the bandwidth to try to factor
|
||||||
|
* into some kind of reasonable superclass, so just dumped here. Might
|
||||||
|
* be useful to have an ActivityObject parent class or something.
|
||||||
|
*
|
||||||
|
* @category OStatus
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2010 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ActivityUtils
|
||||||
|
{
|
||||||
|
const ATOM = 'http://www.w3.org/2005/Atom';
|
||||||
|
|
||||||
|
const LINK = 'link';
|
||||||
|
const REL = 'rel';
|
||||||
|
const TYPE = 'type';
|
||||||
|
const HREF = 'href';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the permalink for an Activity object
|
||||||
|
*
|
||||||
|
* @param DOMElement $element A DOM element
|
||||||
|
*
|
||||||
|
* @return string related link, if any
|
||||||
|
*/
|
||||||
|
|
||||||
|
static function getLink($element)
|
||||||
|
{
|
||||||
|
$links = $element->getElementsByTagnameNS(self::ATOM, self::LINK);
|
||||||
|
|
||||||
|
foreach ($links as $link) {
|
||||||
|
|
||||||
|
$rel = $link->getAttribute(self::REL);
|
||||||
|
$type = $link->getAttribute(self::TYPE);
|
||||||
|
|
||||||
|
if ($rel == 'alternate' && $type == 'text/html') {
|
||||||
|
return $link->getAttribute(self::HREF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A noun-ish thing in the activity universe
|
||||||
|
*
|
||||||
|
* The activity streams spec talks about activity objects, while also having
|
||||||
|
* a tag activity:object, which is in fact an activity object. Aaaaaah!
|
||||||
|
*
|
||||||
|
* This is just a thing in the activity universe. Can be the subject, object,
|
||||||
|
* or indirect object (target!) of an activity verb. Rotten name, and I'm
|
||||||
|
* propagating it. *sigh*
|
||||||
|
*
|
||||||
|
* @category OStatus
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2010 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ActivityObject
|
||||||
{
|
{
|
||||||
const ARTICLE = 'http://activitystrea.ms/schema/1.0/article';
|
const ARTICLE = 'http://activitystrea.ms/schema/1.0/article';
|
||||||
const BLOGENTRY = 'http://activitystrea.ms/schema/1.0/blog-entry';
|
const BLOGENTRY = 'http://activitystrea.ms/schema/1.0/blog-entry';
|
||||||
@ -47,19 +115,114 @@ class ActivityNoun
|
|||||||
const PERSON = 'http://activitystrea.ms/schema/1.0/person';
|
const PERSON = 'http://activitystrea.ms/schema/1.0/person';
|
||||||
const GROUP = 'http://activitystrea.ms/schema/1.0/group';
|
const GROUP = 'http://activitystrea.ms/schema/1.0/group';
|
||||||
const PLACE = 'http://activitystrea.ms/schema/1.0/place';
|
const PLACE = 'http://activitystrea.ms/schema/1.0/place';
|
||||||
const COMMENT = 'http://activitystrea.ms/schema/1.0/comment'; // tea
|
const COMMENT = 'http://activitystrea.ms/schema/1.0/comment';
|
||||||
|
// ^^^^^^^^^^ tea!
|
||||||
|
|
||||||
|
// Atom elements we snarf
|
||||||
|
|
||||||
|
const TITLE = 'title';
|
||||||
|
const SUMMARY = 'summary';
|
||||||
|
const CONTENT = 'content';
|
||||||
|
const ID = 'id';
|
||||||
|
const SOURCE = 'source';
|
||||||
|
|
||||||
|
const NAME = 'name';
|
||||||
|
const URI = 'uri';
|
||||||
|
const EMAIL = 'email';
|
||||||
|
|
||||||
public $type;
|
public $type;
|
||||||
public $id;
|
public $id;
|
||||||
public $title;
|
public $title;
|
||||||
public $summary;
|
public $summary;
|
||||||
public $content;
|
public $content;
|
||||||
|
public $link;
|
||||||
|
public $source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* This probably needs to be refactored
|
||||||
|
* to generate a local class (ActivityPerson, ActivityFile, ...)
|
||||||
|
* based on the object type.
|
||||||
|
*
|
||||||
|
* @param DOMElement $element DOM thing to turn into an Activity thing
|
||||||
|
*/
|
||||||
|
|
||||||
|
function __construct($element)
|
||||||
|
{
|
||||||
|
$this->source = $element;
|
||||||
|
|
||||||
|
if ($element->tagName == 'author') {
|
||||||
|
|
||||||
|
$this->type = self::PERSON; // XXX: is this fair?
|
||||||
|
$this->title = $this->_childContent($element, self::NAME);
|
||||||
|
$this->id = $this->_childContent($element, self::URI);
|
||||||
|
|
||||||
|
if (empty($this->id)) {
|
||||||
|
$email = $this->_childContent($element, self::EMAIL);
|
||||||
|
if (!empty($email)) {
|
||||||
|
// XXX: acct: ?
|
||||||
|
$this->id = 'mailto:'.$email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$this->type = $this->_childContent($element, Activity::OBJECTTYPE,
|
||||||
|
Activity::SPEC);
|
||||||
|
|
||||||
|
if (empty($this->type)) {
|
||||||
|
$this->type = ActivityObject::NOTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->id = $this->_childContent($element, self::ID);
|
||||||
|
$this->title = $this->_childContent($element, self::TITLE);
|
||||||
|
$this->summary = $this->_childContent($element, self::SUMMARY);
|
||||||
|
$this->content = $this->_childContent($element, self::CONTENT);
|
||||||
|
$this->source = $this->_childContent($element, self::SOURCE);
|
||||||
|
|
||||||
|
$this->link = ActivityUtils::getLink($element);
|
||||||
|
|
||||||
|
// XXX: grab PoCo stuff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grab the text content of a DOM element child of the current element
|
||||||
|
*
|
||||||
|
* @param DOMElement $element Element whose children we examine
|
||||||
|
* @param string $tag Tag to look up
|
||||||
|
* @param string $namespace Namespace to use, defaults to Atom
|
||||||
|
*
|
||||||
|
* @return string content of the child
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function _childContent($element, $tag, $namespace=Activity::ATOM)
|
||||||
|
{
|
||||||
|
$els = $element->getElementsByTagnameNS($namespace, $tag);
|
||||||
|
|
||||||
|
if (empty($els) || $els->length == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
$el = $els->item(0);
|
||||||
|
return $el->textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Activity
|
/**
|
||||||
{
|
* Utility class to hold a bunch of constant defining default verb types
|
||||||
const NAMESPACE = 'http://activitystrea.ms/schema/1.0/';
|
*
|
||||||
|
* @category OStatus
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2010 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ActivityVerb
|
||||||
|
{
|
||||||
const POST = 'http://activitystrea.ms/schema/1.0/post';
|
const POST = 'http://activitystrea.ms/schema/1.0/post';
|
||||||
const SHARE = 'http://activitystrea.ms/schema/1.0/share';
|
const SHARE = 'http://activitystrea.ms/schema/1.0/share';
|
||||||
const SAVE = 'http://activitystrea.ms/schema/1.0/save';
|
const SAVE = 'http://activitystrea.ms/schema/1.0/save';
|
||||||
@ -69,17 +232,162 @@ class Activity
|
|||||||
const FRIEND = 'http://activitystrea.ms/schema/1.0/make-friend';
|
const FRIEND = 'http://activitystrea.ms/schema/1.0/make-friend';
|
||||||
const JOIN = 'http://activitystrea.ms/schema/1.0/join';
|
const JOIN = 'http://activitystrea.ms/schema/1.0/join';
|
||||||
const TAG = 'http://activitystrea.ms/schema/1.0/tag';
|
const TAG = 'http://activitystrea.ms/schema/1.0/tag';
|
||||||
|
}
|
||||||
|
|
||||||
public $actor; // an ActivityNoun
|
/**
|
||||||
public $verb; // a string (the URL)
|
* An activity in the ActivityStrea.ms world
|
||||||
public $object; // an ActivityNoun
|
*
|
||||||
public $target; // an ActivityNoun
|
* An activity is kind of like a sentence: someone did something
|
||||||
|
* to something else.
|
||||||
|
*
|
||||||
|
* 'someone' is the 'actor'; 'did something' is the verb;
|
||||||
|
* 'something else' is the object.
|
||||||
|
*
|
||||||
|
* @category OStatus
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2010 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
static function fromAtomEntry($domEntry)
|
class Activity
|
||||||
|
{
|
||||||
|
const SPEC = 'http://activitystrea.ms/spec/1.0/';
|
||||||
|
const SCHEMA = 'http://activitystrea.ms/schema/1.0/';
|
||||||
|
|
||||||
|
const VERB = 'verb';
|
||||||
|
const OBJECT = 'object';
|
||||||
|
const ACTOR = 'actor';
|
||||||
|
const SUBJECT = 'subject';
|
||||||
|
const OBJECTTYPE = 'object-type';
|
||||||
|
const CONTEXT = 'context';
|
||||||
|
const TARGET = 'target';
|
||||||
|
|
||||||
|
const ATOM = 'http://www.w3.org/2005/Atom';
|
||||||
|
|
||||||
|
const AUTHOR = 'author';
|
||||||
|
const PUBLISHED = 'published';
|
||||||
|
const UPDATED = 'updated';
|
||||||
|
|
||||||
|
public $actor; // an ActivityObject
|
||||||
|
public $verb; // a string (the URL)
|
||||||
|
public $object; // an ActivityObject
|
||||||
|
public $target; // an ActivityObject
|
||||||
|
public $context; // an ActivityObject
|
||||||
|
public $time; // Time of the activity
|
||||||
|
public $link; // an ActivityObject
|
||||||
|
public $entry; // the source entry
|
||||||
|
public $feed; // the source feed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns a regular old Atom <entry> into a magical activity
|
||||||
|
*
|
||||||
|
* @param DOMElement $entry Atom entry to poke at
|
||||||
|
* @param DOMElement $feed Atom feed, for context
|
||||||
|
*/
|
||||||
|
|
||||||
|
function __construct($entry, $feed = null)
|
||||||
{
|
{
|
||||||
|
$this->entry = $entry;
|
||||||
|
$this->feed = $feed;
|
||||||
|
|
||||||
|
$pubEl = $this->_child($entry, self::PUBLISHED, self::ATOM);
|
||||||
|
|
||||||
|
if (!empty($pubEl)) {
|
||||||
|
$this->time = strtotime($pubEl->textContent);
|
||||||
|
} else {
|
||||||
|
// XXX technically an error; being liberal. Good idea...?
|
||||||
|
$updateEl = $this->_child($entry, self::UPDATED, self::ATOM);
|
||||||
|
if (!empty($updateEl)) {
|
||||||
|
$this->time = strtotime($updateEl->textContent);
|
||||||
|
} else {
|
||||||
|
$this->time = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->link = ActivityUtils::getLink($entry);
|
||||||
|
|
||||||
|
$verbEl = $this->_child($entry, self::VERB);
|
||||||
|
|
||||||
|
if (!empty($verbEl)) {
|
||||||
|
$this->verb = trim($verbEl->textContent);
|
||||||
|
} else {
|
||||||
|
$this->verb = ActivityVerb::POST;
|
||||||
|
// XXX: do other implied stuff here
|
||||||
|
}
|
||||||
|
|
||||||
|
$objectEl = $this->_child($entry, self::OBJECT);
|
||||||
|
|
||||||
|
if (!empty($objectEl)) {
|
||||||
|
$this->object = new ActivityObject($objectEl);
|
||||||
|
} else {
|
||||||
|
$this->object = new ActivityObject($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
$actorEl = $this->_child($entry, self::ACTOR);
|
||||||
|
|
||||||
|
if (!empty($actorEl)) {
|
||||||
|
|
||||||
|
$this->actor = new ActivityObject($actorEl);
|
||||||
|
|
||||||
|
} else if (!empty($feed) &&
|
||||||
|
$subjectEl = $this->_child($feed, self::SUBJECT)) {
|
||||||
|
|
||||||
|
$this->actor = new ActivityObject($subjectEl);
|
||||||
|
|
||||||
|
} else if ($authorEl = $this->_child($entry, self::AUTHOR, self::ATOM)) {
|
||||||
|
|
||||||
|
$this->actor = new ActivityObject($authorEl);
|
||||||
|
|
||||||
|
} else if (!empty($feed) && $authorEl = $this->_child($feed, self::AUTHOR,
|
||||||
|
self::ATOM)) {
|
||||||
|
|
||||||
|
$this->actor = new ActivityObject($authorEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
$contextEl = $this->_child($entry, self::CONTEXT);
|
||||||
|
|
||||||
|
if (!empty($contextEl)) {
|
||||||
|
$this->context = new ActivityObject($contextEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetEl = $this->_child($entry, self::TARGET);
|
||||||
|
|
||||||
|
if (!empty($targetEl)) {
|
||||||
|
$this->target = new ActivityObject($targetEl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Atom <entry> based on this activity
|
||||||
|
*
|
||||||
|
* @return DOMElement Atom entry
|
||||||
|
*/
|
||||||
|
|
||||||
function toAtomEntry()
|
function toAtomEntry()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Gets the first child element with the given tag
|
||||||
|
*
|
||||||
|
* @param DOMElement $element element to pick at
|
||||||
|
* @param string $tag tag to look for
|
||||||
|
* @param string $namespace Namespace to look under
|
||||||
|
*
|
||||||
|
* @return DOMElement found element or null
|
||||||
|
*/
|
||||||
|
|
||||||
|
private function _child($element, $tag, $namespace=self::SPEC)
|
||||||
|
{
|
||||||
|
$els = $element->getElementsByTagnameNS($namespace, $tag);
|
||||||
|
|
||||||
|
if (empty($els) || $els->length == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return $els->item(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
147
plugins/OStatus/tests/ActivityParseTests.php
Normal file
147
plugins/OStatus/tests/ActivityParseTests.php
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
|
||||||
|
print "This script must be run from the command line\n";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: we should probably have some common source for this stuff
|
||||||
|
|
||||||
|
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
|
||||||
|
define('STATUSNET', true);
|
||||||
|
|
||||||
|
require_once INSTALLDIR . '/lib/common.php';
|
||||||
|
require_once INSTALLDIR . '/plugins/OStatus/lib/activity.php';
|
||||||
|
|
||||||
|
class ActivityParseTests extends PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testExample1()
|
||||||
|
{
|
||||||
|
global $_example1;
|
||||||
|
$dom = DOMDocument::loadXML($_example1);
|
||||||
|
$act = new Activity($dom->documentElement);
|
||||||
|
|
||||||
|
$this->assertFalse(empty($act));
|
||||||
|
$this->assertEquals($act->time, 1243860840);
|
||||||
|
$this->assertEquals($act->verb, ActivityVerb::POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExample3()
|
||||||
|
{
|
||||||
|
global $_example3;
|
||||||
|
$dom = DOMDocument::loadXML($_example3);
|
||||||
|
|
||||||
|
$feed = $dom->documentElement;
|
||||||
|
|
||||||
|
$entries = $feed->getElementsByTagName('entry');
|
||||||
|
|
||||||
|
$entry = $entries->item(0);
|
||||||
|
|
||||||
|
$act = new Activity($entry, $feed);
|
||||||
|
|
||||||
|
$this->assertFalse(empty($act));
|
||||||
|
$this->assertEquals($act->time, 1071340202);
|
||||||
|
$this->assertEquals($act->link, 'http://example.org/2003/12/13/atom03.html');
|
||||||
|
|
||||||
|
$this->assertEquals($act->verb, ActivityVerb::POST);
|
||||||
|
|
||||||
|
$this->assertFalse(empty($act->actor));
|
||||||
|
$this->assertEquals($act->actor->type, ActivityObject::PERSON);
|
||||||
|
$this->assertEquals($act->actor->title, 'John Doe');
|
||||||
|
$this->assertEquals($act->actor->id, 'mailto:johndoe@example.com');
|
||||||
|
|
||||||
|
$this->assertFalse(empty($act->object));
|
||||||
|
$this->assertEquals($act->object->type, ActivityObject::NOTE);
|
||||||
|
$this->assertEquals($act->object->id, 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a');
|
||||||
|
$this->assertEquals($act->object->title, 'Atom-Powered Robots Run Amok');
|
||||||
|
$this->assertEquals($act->object->summary, 'Some text.');
|
||||||
|
$this->assertEquals($act->object->link, 'http://example.org/2003/12/13/atom03.html');
|
||||||
|
|
||||||
|
$this->assertTrue(empty($act->context));
|
||||||
|
$this->assertTrue(empty($act->target));
|
||||||
|
|
||||||
|
$this->assertEquals($act->entry, $entry);
|
||||||
|
$this->assertEquals($act->feed, $feed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$_example1 = <<<EXAMPLE1
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:activity='http://activitystrea.ms/spec/1.0/'>
|
||||||
|
<id>tag:versioncentral.example.org,2009:/commit/1643245</id>
|
||||||
|
<published>2009-06-01T12:54:00Z</published>
|
||||||
|
<title>Geraldine committed a change to yate</title>
|
||||||
|
<content type="xhtml">Geraldine just committed a change to yate on VersionCentral</content>
|
||||||
|
<link rel="alternate" type="text/html"
|
||||||
|
href="http://versioncentral.example.org/geraldine/yate/commit/1643245" />
|
||||||
|
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
||||||
|
<activity:verb>http://versioncentral.example.org/activity/commit</activity:verb>
|
||||||
|
<activity:object>
|
||||||
|
<activity:object-type>http://versioncentral.example.org/activity/changeset</activity:object-type>
|
||||||
|
<id>tag:versioncentral.example.org,2009:/change/1643245</id>
|
||||||
|
<title>Punctuation Changeset</title>
|
||||||
|
<summary>Fixing punctuation because it makes it more readable.</summary>
|
||||||
|
<link rel="alternate" type="text/html" href="..." />
|
||||||
|
</activity:object>
|
||||||
|
</entry>
|
||||||
|
EXAMPLE1;
|
||||||
|
|
||||||
|
$_example2 = <<<EXAMPLE2
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:activity='http://activitystrea.ms/spec/1.0/'>
|
||||||
|
<id>tag:photopanic.example.com,2008:activity01</id>
|
||||||
|
<title>Geraldine posted a Photo on PhotoPanic</title>
|
||||||
|
<published>2008-11-02T15:29:00Z</published>
|
||||||
|
<link rel="alternate" type="text/html" href="/geraldine/activities/1" />
|
||||||
|
<activity:verb>
|
||||||
|
http://activitystrea.ms/schema/1.0/post
|
||||||
|
</activity:verb>
|
||||||
|
<activity:object>
|
||||||
|
<id>tag:photopanic.example.com,2008:photo01</id>
|
||||||
|
<title>My Cat</title>
|
||||||
|
<published>2008-11-02T15:29:00Z</published>
|
||||||
|
<link rel="alternate" type="text/html" href="/geraldine/photos/1" />
|
||||||
|
<activity:object-type>
|
||||||
|
tag:atomactivity.example.com,2008:photo
|
||||||
|
</activity:object-type>
|
||||||
|
<source>
|
||||||
|
<title>Geraldine's Photos</title>
|
||||||
|
<link rel="self" type="application/atom+xml" href="/geraldine/photofeed.xml" />
|
||||||
|
<link rel="alternate" type="text/html" href="/geraldine/" />
|
||||||
|
</source>
|
||||||
|
</activity:object>
|
||||||
|
<content type="html">
|
||||||
|
<p>Geraldine posted a Photo on PhotoPanic</p>
|
||||||
|
<img src="/geraldine/photo1.jpg">
|
||||||
|
</content>
|
||||||
|
</entry>
|
||||||
|
EXAMPLE2;
|
||||||
|
|
||||||
|
$_example3 = <<<EXAMPLE3
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
|
||||||
|
<title>Example Feed</title>
|
||||||
|
<subtitle>A subtitle.</subtitle>
|
||||||
|
<link href="http://example.org/feed/" rel="self" />
|
||||||
|
<link href="http://example.org/" />
|
||||||
|
<id>urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6</id>
|
||||||
|
<updated>2003-12-13T18:30:02Z</updated>
|
||||||
|
<author>
|
||||||
|
<name>John Doe</name>
|
||||||
|
<email>johndoe@example.com</email>
|
||||||
|
</author>
|
||||||
|
|
||||||
|
<entry>
|
||||||
|
<title>Atom-Powered Robots Run Amok</title>
|
||||||
|
<link href="http://example.org/2003/12/13/atom03" />
|
||||||
|
<link rel="alternate" type="text/html" href="http://example.org/2003/12/13/atom03.html"/>
|
||||||
|
<link rel="edit" href="http://example.org/2003/12/13/atom03/edit"/>
|
||||||
|
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
|
||||||
|
<updated>2003-12-13T18:30:02Z</updated>
|
||||||
|
<summary>Some text.</summary>
|
||||||
|
</entry>
|
||||||
|
|
||||||
|
</feed>
|
||||||
|
EXAMPLE3;
|
30
plugins/OStatus/theme/base/css/ostatus.css
Normal file
30
plugins/OStatus/theme/base/css/ostatus.css
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/** theme: base for OStatus
|
||||||
|
*
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Sarven Capadisli <csarven@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/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#form_ostatus_connect.dialogbox {
|
||||||
|
width:70%;
|
||||||
|
background-image:none;
|
||||||
|
}
|
||||||
|
#form_ostatus_connect.dialogbox .form_data label {
|
||||||
|
width:34%;
|
||||||
|
}
|
||||||
|
#form_ostatus_connect.dialogbox .form_data input {
|
||||||
|
width:57%;
|
||||||
|
}
|
||||||
|
#form_ostatus_connect.dialogbox .form_data .form_guide {
|
||||||
|
margin-left:36%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form_ostatus_connect.dialogbox #ostatus_nickname {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form_ostatus_connect.dialogbox .submit_dialogbox {
|
||||||
|
min-width:96px;
|
||||||
|
}
|
@ -438,49 +438,7 @@ class FinishopenidloginAction extends Action
|
|||||||
|
|
||||||
function urlToNickname($openid)
|
function urlToNickname($openid)
|
||||||
{
|
{
|
||||||
static $bad = array('query', 'user', 'password', 'port', 'fragment');
|
return common_url_to_nickname($openid);
|
||||||
|
|
||||||
$parts = parse_url($openid);
|
|
||||||
|
|
||||||
# If any of these parts exist, this won't work
|
|
||||||
|
|
||||||
foreach ($bad as $badpart) {
|
|
||||||
if (array_key_exists($badpart, $parts)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# We just have host and/or path
|
|
||||||
|
|
||||||
# If it's just a host...
|
|
||||||
if (array_key_exists('host', $parts) &&
|
|
||||||
(!array_key_exists('path', $parts) || strcmp($parts['path'], '/') == 0))
|
|
||||||
{
|
|
||||||
$hostparts = explode('.', $parts['host']);
|
|
||||||
|
|
||||||
# Try to catch common idiom of nickname.service.tld
|
|
||||||
|
|
||||||
if ((count($hostparts) > 2) &&
|
|
||||||
(strlen($hostparts[count($hostparts) - 2]) > 3) && # try to skip .co.uk, .com.au
|
|
||||||
(strcmp($hostparts[0], 'www') != 0))
|
|
||||||
{
|
|
||||||
return $this->nicknamize($hostparts[0]);
|
|
||||||
} else {
|
|
||||||
# Do the whole hostname
|
|
||||||
return $this->nicknamize($parts['host']);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (array_key_exists('path', $parts)) {
|
|
||||||
# Strip starting, ending slashes
|
|
||||||
$path = preg_replace('@/$@', '', $parts['path']);
|
|
||||||
$path = preg_replace('@^/@', '', $path);
|
|
||||||
if (strpos($path, '/') === false) {
|
|
||||||
return $this->nicknamize($path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function xriToNickname($xri)
|
function xriToNickname($xri)
|
||||||
@ -510,7 +468,6 @@ class FinishopenidloginAction extends Action
|
|||||||
|
|
||||||
function nicknamize($str)
|
function nicknamize($str)
|
||||||
{
|
{
|
||||||
$str = preg_replace('/\W/', '', $str);
|
return common_nicknamize($str);
|
||||||
return strtolower($str);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ class PoweredByStatusNetPlugin extends Plugin
|
|||||||
{
|
{
|
||||||
function onEndAddressData($action)
|
function onEndAddressData($action)
|
||||||
{
|
{
|
||||||
|
$action->text(' ');
|
||||||
$action->elementStart('span', 'poweredby');
|
$action->elementStart('span', 'poweredby');
|
||||||
$action->raw(sprintf(_m('powered by %s'),
|
$action->raw(sprintf(_m('powered by %s'),
|
||||||
sprintf('<a href="http://status.net/">%s</a>',
|
sprintf('<a href="http://status.net/">%s</a>',
|
||||||
|
@ -20,13 +20,15 @@
|
|||||||
|
|
||||||
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
|
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
|
||||||
|
|
||||||
$shortoptions = 'fi::';
|
$shortoptions = 'fi::a';
|
||||||
$longoptions = array('id::', 'foreground');
|
$longoptions = array('id::', 'foreground', 'all');
|
||||||
|
|
||||||
$helptext = <<<END_OF_IM_HELP
|
$helptext = <<<END_OF_IM_HELP
|
||||||
Daemon script for receiving new notices from IM users.
|
Daemon script for receiving new notices from IM users.
|
||||||
|
|
||||||
-i --id Identity (default none)
|
-i --id Identity (default none)
|
||||||
|
-a --all Handle XMPP for all local sites
|
||||||
|
(requires Stomp queue handler, status_network setup)
|
||||||
-f --foreground Stay in the foreground (default background)
|
-f --foreground Stay in the foreground (default background)
|
||||||
|
|
||||||
END_OF_IM_HELP;
|
END_OF_IM_HELP;
|
||||||
@ -35,13 +37,16 @@ require_once INSTALLDIR.'/scripts/commandline.inc';
|
|||||||
|
|
||||||
class ImDaemon extends SpawningDaemon
|
class ImDaemon extends SpawningDaemon
|
||||||
{
|
{
|
||||||
function __construct($id=null, $daemonize=true, $threads=1)
|
protected $allsites = false;
|
||||||
|
|
||||||
|
function __construct($id=null, $daemonize=true, $threads=1, $allsites=false)
|
||||||
{
|
{
|
||||||
if ($threads != 1) {
|
if ($threads != 1) {
|
||||||
// This should never happen. :)
|
// This should never happen. :)
|
||||||
throw new Exception("IMDaemon can must run single-threaded");
|
throw new Exception("IMDaemon can must run single-threaded");
|
||||||
}
|
}
|
||||||
parent::__construct($id, $daemonize, $threads);
|
parent::__construct($id, $daemonize, $threads);
|
||||||
|
$this->allsites = $allsites;
|
||||||
}
|
}
|
||||||
|
|
||||||
function runThread()
|
function runThread()
|
||||||
@ -49,7 +54,7 @@ class ImDaemon extends SpawningDaemon
|
|||||||
common_log(LOG_INFO, 'Waiting to listen to IM connections and queues');
|
common_log(LOG_INFO, 'Waiting to listen to IM connections and queues');
|
||||||
|
|
||||||
$master = new ImMaster($this->get_id());
|
$master = new ImMaster($this->get_id());
|
||||||
$master->init();
|
$master->init($this->allsites);
|
||||||
$master->service();
|
$master->service();
|
||||||
|
|
||||||
common_log(LOG_INFO, 'terminating normally');
|
common_log(LOG_INFO, 'terminating normally');
|
||||||
@ -69,7 +74,9 @@ class ImMaster extends IoMaster
|
|||||||
{
|
{
|
||||||
$classes = array();
|
$classes = array();
|
||||||
if (Event::handle('StartImDaemonIoManagers', array(&$classes))) {
|
if (Event::handle('StartImDaemonIoManagers', array(&$classes))) {
|
||||||
$classes[] = 'QueueManager';
|
$qm = QueueManager::get();
|
||||||
|
$qm->setActiveGroup('im');
|
||||||
|
$classes[] = $qm;
|
||||||
}
|
}
|
||||||
Event::handle('EndImDaemonIoManagers', array(&$classes));
|
Event::handle('EndImDaemonIoManagers', array(&$classes));
|
||||||
foreach ($classes as $class) {
|
foreach ($classes as $class) {
|
||||||
@ -87,7 +94,8 @@ if (have_option('i', 'id')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$foreground = have_option('f', 'foreground');
|
$foreground = have_option('f', 'foreground');
|
||||||
|
$all = have_option('a') || have_option('--all');
|
||||||
|
|
||||||
$daemon = new ImDaemon($id, !$foreground);
|
$daemon = new ImDaemon($id, !$foreground, 1, $all);
|
||||||
|
|
||||||
$daemon->runOnce();
|
$daemon->runOnce();
|
||||||
|
@ -74,8 +74,6 @@ require_once(INSTALLDIR.'/lib/daemon.php');
|
|||||||
require_once(INSTALLDIR.'/classes/Queue_item.php');
|
require_once(INSTALLDIR.'/classes/Queue_item.php');
|
||||||
require_once(INSTALLDIR.'/classes/Notice.php');
|
require_once(INSTALLDIR.'/classes/Notice.php');
|
||||||
|
|
||||||
define('CLAIM_TIMEOUT', 1200);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue handling daemon...
|
* Queue handling daemon...
|
||||||
*
|
*
|
||||||
@ -92,7 +90,7 @@ class QueueDaemon extends SpawningDaemon
|
|||||||
function __construct($id=null, $daemonize=true, $threads=1, $allsites=false)
|
function __construct($id=null, $daemonize=true, $threads=1, $allsites=false)
|
||||||
{
|
{
|
||||||
parent::__construct($id, $daemonize, $threads);
|
parent::__construct($id, $daemonize, $threads);
|
||||||
$this->all = $allsites;
|
$this->allsites = $allsites;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,7 +106,7 @@ class QueueDaemon extends SpawningDaemon
|
|||||||
$this->log(LOG_INFO, 'checking for queued notices');
|
$this->log(LOG_INFO, 'checking for queued notices');
|
||||||
|
|
||||||
$master = new QueueMaster($this->get_id());
|
$master = new QueueMaster($this->get_id());
|
||||||
$master->init($this->all);
|
$master->init($this->allsites);
|
||||||
try {
|
try {
|
||||||
$master->service();
|
$master->service();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@ -133,14 +131,16 @@ class QueueMaster extends IoMaster
|
|||||||
*/
|
*/
|
||||||
function initManagers()
|
function initManagers()
|
||||||
{
|
{
|
||||||
$classes = array();
|
$managers = array();
|
||||||
if (Event::handle('StartQueueDaemonIoManagers', array(&$classes))) {
|
if (Event::handle('StartQueueDaemonIoManagers', array(&$managers))) {
|
||||||
$classes[] = 'QueueManager';
|
$qm = QueueManager::get();
|
||||||
|
$qm->setActiveGroup('main');
|
||||||
|
$managers[] = $qm;
|
||||||
}
|
}
|
||||||
Event::handle('EndQueueDaemonIoManagers', array(&$classes));
|
Event::handle('EndQueueDaemonIoManagers', array(&$managers));
|
||||||
|
|
||||||
foreach ($classes as $class) {
|
foreach ($managers as $manager) {
|
||||||
$this->instantiate($class);
|
$this->instantiate($manager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,7 +288,7 @@ margin-left:18px;
|
|||||||
}
|
}
|
||||||
#site_nav_global_primary li {
|
#site_nav_global_primary li {
|
||||||
display:inline;
|
display:inline;
|
||||||
margin-left:11px;
|
margin-left:18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.system_notice dt {
|
.system_notice dt {
|
||||||
@ -370,7 +370,7 @@ margin-bottom:11px;
|
|||||||
|
|
||||||
#site_nav_global_secondary ul li {
|
#site_nav_global_secondary ul li {
|
||||||
display:inline;
|
display:inline;
|
||||||
margin-right:11px;
|
margin-right:18px;
|
||||||
}
|
}
|
||||||
#export_data li a {
|
#export_data li a {
|
||||||
padding-left:20px;
|
padding-left:20px;
|
||||||
@ -383,15 +383,13 @@ padding-left:28px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#export_data ul {
|
#export_data ul {
|
||||||
display:inline;
|
width:100%;
|
||||||
|
float:left;
|
||||||
}
|
}
|
||||||
#export_data li {
|
#export_data li {
|
||||||
list-style-type:none;
|
list-style-type:none;
|
||||||
display:inline;
|
float:left;
|
||||||
margin-left:11px;
|
margin-right:11px;
|
||||||
}
|
|
||||||
#export_data li:first-child {
|
|
||||||
margin-left:0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#licenses {
|
#licenses {
|
||||||
@ -801,8 +799,8 @@ list-style-type:none;
|
|||||||
display:inline;
|
display:inline;
|
||||||
}
|
}
|
||||||
.entity_tags li {
|
.entity_tags li {
|
||||||
display:inline;
|
float:left;
|
||||||
margin-right:4px;
|
margin-right:11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aside .section {
|
.aside .section {
|
||||||
@ -820,6 +818,7 @@ font-size:1em;
|
|||||||
#entity_statistics dt,
|
#entity_statistics dt,
|
||||||
#entity_statistics dd {
|
#entity_statistics dd {
|
||||||
display:inline;
|
display:inline;
|
||||||
|
margin-right:11px;
|
||||||
}
|
}
|
||||||
#entity_statistics dt:after {
|
#entity_statistics dt:after {
|
||||||
content: ":";
|
content: ":";
|
||||||
@ -1496,6 +1495,11 @@ display:inline;
|
|||||||
margin-right:7px;
|
margin-right:7px;
|
||||||
line-height:1.25;
|
line-height:1.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tag-cloud li:before {
|
||||||
|
content:'\0009';
|
||||||
|
}
|
||||||
|
|
||||||
.aside .tag-cloud li {
|
.aside .tag-cloud li {
|
||||||
line-height:1.5;
|
line-height:1.5;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user