From 95b534c5cee56602dffcc11121908c0f518cab6d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 15 Sep 2010 14:09:23 -0700 Subject: Move standalone plugin files into subdirectories so they can have locale data, etc added to them at leisure. --- plugins/APC/APCPlugin.php | 119 +++++ plugins/APCPlugin.php | 119 ----- plugins/BlogspamNet/BlogspamNetPlugin.php | 145 ++++++ plugins/BlogspamNetPlugin.php | 145 ------ plugins/CacheLog/CacheLogPlugin.php | 121 +++++ plugins/CacheLogPlugin.php | 121 ----- plugins/DiskCache/DiskCachePlugin.php | 168 +++++++ plugins/DiskCachePlugin.php | 168 ------- plugins/Disqus/DisqusPlugin.php | 171 +++++++ plugins/DisqusPlugin.php | 171 ------- plugins/Echo/EchoPlugin.php | 116 +++++ plugins/EchoPlugin.php | 116 ----- plugins/GeoURL/GeoURLPlugin.php | 131 ++++++ plugins/GeoURLPlugin.php | 131 ------ plugins/Geonames/GeonamesPlugin.php | 510 +++++++++++++++++++++ plugins/GeonamesPlugin.php | 510 --------------------- plugins/GoogleAnalytics/GoogleAnalyticsPlugin.php | 85 ++++ plugins/GoogleAnalyticsPlugin.php | 85 ---- plugins/Linkback/LinkbackPlugin.php | 249 ++++++++++ plugins/LinkbackPlugin.php | 249 ---------- plugins/Memcache/MemcachePlugin.php | 256 +++++++++++ plugins/MemcachePlugin.php | 256 ----------- plugins/Memcached/MemcachedPlugin.php | 227 +++++++++ plugins/MemcachedPlugin.php | 227 --------- plugins/PiwikAnalytics/PiwikAnalyticsPlugin.php | 112 +++++ plugins/PiwikAnalyticsPlugin.php | 112 ----- plugins/Spotify/SpotifyPlugin.php | 113 +++++ plugins/SpotifyPlugin.php | 113 ----- .../SubscriptionThrottlePlugin.php | 175 +++++++ plugins/SubscriptionThrottlePlugin.php | 175 ------- plugins/Template/TemplatePlugin.php | 357 +++++++++++++++ plugins/TemplatePlugin.php | 357 --------------- plugins/UserLimit/UserLimitPlugin.php | 92 ++++ plugins/UserLimitPlugin.php | 92 ---- plugins/WikiHashtags/WikiHashtagsPlugin.php | 113 +++++ plugins/WikiHashtagsPlugin.php | 113 ----- plugins/XCache/XCachePlugin.php | 124 +++++ plugins/XCachePlugin.php | 124 ----- 38 files changed, 3384 insertions(+), 3384 deletions(-) create mode 100644 plugins/APC/APCPlugin.php delete mode 100644 plugins/APCPlugin.php create mode 100644 plugins/BlogspamNet/BlogspamNetPlugin.php delete mode 100644 plugins/BlogspamNetPlugin.php create mode 100644 plugins/CacheLog/CacheLogPlugin.php delete mode 100644 plugins/CacheLogPlugin.php create mode 100644 plugins/DiskCache/DiskCachePlugin.php delete mode 100644 plugins/DiskCachePlugin.php create mode 100644 plugins/Disqus/DisqusPlugin.php delete mode 100644 plugins/DisqusPlugin.php create mode 100644 plugins/Echo/EchoPlugin.php delete mode 100644 plugins/EchoPlugin.php create mode 100644 plugins/GeoURL/GeoURLPlugin.php delete mode 100644 plugins/GeoURLPlugin.php create mode 100644 plugins/Geonames/GeonamesPlugin.php delete mode 100644 plugins/GeonamesPlugin.php create mode 100644 plugins/GoogleAnalytics/GoogleAnalyticsPlugin.php delete mode 100644 plugins/GoogleAnalyticsPlugin.php create mode 100644 plugins/Linkback/LinkbackPlugin.php delete mode 100644 plugins/LinkbackPlugin.php create mode 100644 plugins/Memcache/MemcachePlugin.php delete mode 100644 plugins/MemcachePlugin.php create mode 100644 plugins/Memcached/MemcachedPlugin.php delete mode 100644 plugins/MemcachedPlugin.php create mode 100644 plugins/PiwikAnalytics/PiwikAnalyticsPlugin.php delete mode 100644 plugins/PiwikAnalyticsPlugin.php create mode 100644 plugins/Spotify/SpotifyPlugin.php delete mode 100644 plugins/SpotifyPlugin.php create mode 100644 plugins/SubscriptionThrottle/SubscriptionThrottlePlugin.php delete mode 100644 plugins/SubscriptionThrottlePlugin.php create mode 100644 plugins/Template/TemplatePlugin.php delete mode 100644 plugins/TemplatePlugin.php create mode 100644 plugins/UserLimit/UserLimitPlugin.php delete mode 100644 plugins/UserLimitPlugin.php create mode 100644 plugins/WikiHashtags/WikiHashtagsPlugin.php delete mode 100644 plugins/WikiHashtagsPlugin.php create mode 100644 plugins/XCache/XCachePlugin.php delete mode 100644 plugins/XCachePlugin.php (limited to 'plugins') diff --git a/plugins/APC/APCPlugin.php b/plugins/APC/APCPlugin.php new file mode 100644 index 000000000..666f64b14 --- /dev/null +++ b/plugins/APC/APCPlugin.php @@ -0,0 +1,119 @@ +. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * A plugin to use APC's variable cache for the cache interface + * + * New plugin interface lets us use alternative cache systems + * for caching. This one uses APC's variable cache. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class APCPlugin extends Plugin +{ + /** + * Get a value associated with a key + * + * The value should have been set previously. + * + * @param string &$key in; Lookup key + * @param mixed &$value out; value associated with key + * + * @return boolean hook success + */ + + function onStartCacheGet(&$key, &$value) + { + $value = apc_fetch($key); + Event::handle('EndCacheGet', array($key, &$value)); + return false; + } + + /** + * Associate a value with a key + * + * @param string &$key in; Key to use for lookups + * @param mixed &$value in; Value to associate + * @param integer &$flag in; Flag (passed through to Memcache) + * @param integer &$expiry in; Expiry (passed through to Memcache) + * @param boolean &$success out; Whether the set was successful + * + * @return boolean hook success + */ + + function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) + { + $success = apc_store($key, $value, ((is_null($expiry)) ? 0 : $expiry)); + + Event::handle('EndCacheSet', array($key, $value, $flag, + $expiry)); + return false; + } + + /** + * Delete a value associated with a key + * + * @param string &$key in; Key to lookup + * @param boolean &$success out; whether it worked + * + * @return boolean hook success + */ + + function onStartCacheDelete(&$key, &$success) + { + $success = apc_delete($key); + Event::handle('EndCacheDelete', array($key)); + return false; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'APC', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:APC', + 'rawdescription' => + _m('Use the APC variable cache to cache query results.')); + return true; + } +} + diff --git a/plugins/APCPlugin.php b/plugins/APCPlugin.php deleted file mode 100644 index 666f64b14..000000000 --- a/plugins/APCPlugin.php +++ /dev/null @@ -1,119 +0,0 @@ -. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - // This check helps protect against security problems; - // your code file can't be executed directly from the web. - exit(1); -} - -/** - * A plugin to use APC's variable cache for the cache interface - * - * New plugin interface lets us use alternative cache systems - * for caching. This one uses APC's variable cache. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -class APCPlugin extends Plugin -{ - /** - * Get a value associated with a key - * - * The value should have been set previously. - * - * @param string &$key in; Lookup key - * @param mixed &$value out; value associated with key - * - * @return boolean hook success - */ - - function onStartCacheGet(&$key, &$value) - { - $value = apc_fetch($key); - Event::handle('EndCacheGet', array($key, &$value)); - return false; - } - - /** - * Associate a value with a key - * - * @param string &$key in; Key to use for lookups - * @param mixed &$value in; Value to associate - * @param integer &$flag in; Flag (passed through to Memcache) - * @param integer &$expiry in; Expiry (passed through to Memcache) - * @param boolean &$success out; Whether the set was successful - * - * @return boolean hook success - */ - - function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) - { - $success = apc_store($key, $value, ((is_null($expiry)) ? 0 : $expiry)); - - Event::handle('EndCacheSet', array($key, $value, $flag, - $expiry)); - return false; - } - - /** - * Delete a value associated with a key - * - * @param string &$key in; Key to lookup - * @param boolean &$success out; whether it worked - * - * @return boolean hook success - */ - - function onStartCacheDelete(&$key, &$success) - { - $success = apc_delete($key); - Event::handle('EndCacheDelete', array($key)); - return false; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'APC', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:APC', - 'rawdescription' => - _m('Use the APC variable cache to cache query results.')); - return true; - } -} - diff --git a/plugins/BlogspamNet/BlogspamNetPlugin.php b/plugins/BlogspamNet/BlogspamNetPlugin.php new file mode 100644 index 000000000..d52e6006a --- /dev/null +++ b/plugins/BlogspamNet/BlogspamNetPlugin.php @@ -0,0 +1,145 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @author Brion Vibber + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +define('BLOGSPAMNETPLUGIN_VERSION', '0.1'); + +/** + * Plugin to check submitted notices with blogspam.net + * + * When new notices are saved, we check their text with blogspam.net (or + * a compatible service). + * + * Blogspam.net is supposed to catch blog comment spam, and I found that + * some of its tests (min/max size, bayesian match) gave a lot of false positives. + * So, I've turned those tests off by default. This may not get as many + * hits, but it's better than nothing. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Event + */ + +class BlogspamNetPlugin extends Plugin +{ + var $baseUrl = 'http://test.blogspam.net:8888/'; + + function __construct($url=null) + { + parent::__construct(); + if ($url) { + $this->baseUrl = $url; + } + } + + function onStartNoticeSave($notice) + { + $args = $this->testArgs($notice); + common_debug("Blogspamnet args = " . print_r($args, TRUE)); + $requestBody = xmlrpc_encode_request('testComment', array($args)); + + $request = new HTTPClient($this->baseUrl, HTTPClient::METHOD_POST); + $request->setHeader('Content-Type', 'text/xml'); + $request->setBody($requestBody); + $httpResponse = $request->send(); + + $response = xmlrpc_decode($httpResponse->getBody()); + if (xmlrpc_is_fault($response)) { + throw new ServerException("$response[faultString] ($response[faultCode])", 500); + } else { + common_debug("Blogspamnet results = " . $response); + if (preg_match('/^ERROR(:(.*))?$/', $response, $match)) { + throw new ServerException(sprintf(_("Error from %s: %s"), $this->baseUrl, $match[2]), 500); + } else if (preg_match('/^SPAM(:(.*))?$/', $response, $match)) { + throw new ClientException(sprintf(_("Spam checker results: %s"), $match[2]), 400); + } else if (preg_match('/^OK$/', $response)) { + // don't do anything + } else { + throw new ServerException(sprintf(_("Unexpected response from %s: %s"), $this->baseUrl, $response), 500); + } + } + return true; + } + + function testArgs($notice) + { + $args = array(); + $args['comment'] = $notice->content; + $args['ip'] = $this->getClientIP(); + + if (isset($_SERVER) && array_key_exists('HTTP_USER_AGENT', $_SERVER)) { + $args['agent'] = $_SERVER['HTTP_USER_AGENT']; + } + + $profile = $notice->getProfile(); + + if ($profile && $profile->homepage) { + $args['link'] = $profile->homepage; + } + + if ($profile && $profile->fullname) { + $args['name'] = $profile->fullname; + } else { + $args['name'] = $profile->nickname; + } + + $args['site'] = common_root_url(); + $args['version'] = $this->userAgent(); + + $args['options'] = "max-size=" . common_config('site','textlimit') . ",min-size=0,min-words=0,exclude=bayasian"; + + return $args; + } + + function getClientIP() + { + if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { + // Note: order matters here; use proxy-forwarded stuff first + foreach (array('HTTP_X_FORWARDED_FOR', 'CLIENT-IP', 'REMOTE_ADDR') as $k) { + if (isset($_SERVER[$k])) { + return $_SERVER[$k]; + } + } + } + return '127.0.0.1'; + } + + function userAgent() + { + return 'BlogspamNetPlugin/'.BLOGSPAMNETPLUGIN_VERSION . ' StatusNet/' . STATUSNET_VERSION; + } +} diff --git a/plugins/BlogspamNetPlugin.php b/plugins/BlogspamNetPlugin.php deleted file mode 100644 index d52e6006a..000000000 --- a/plugins/BlogspamNetPlugin.php +++ /dev/null @@ -1,145 +0,0 @@ -. - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @author Brion Vibber - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -define('BLOGSPAMNETPLUGIN_VERSION', '0.1'); - -/** - * Plugin to check submitted notices with blogspam.net - * - * When new notices are saved, we check their text with blogspam.net (or - * a compatible service). - * - * Blogspam.net is supposed to catch blog comment spam, and I found that - * some of its tests (min/max size, bayesian match) gave a lot of false positives. - * So, I've turned those tests off by default. This may not get as many - * hits, but it's better than nothing. - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @see Event - */ - -class BlogspamNetPlugin extends Plugin -{ - var $baseUrl = 'http://test.blogspam.net:8888/'; - - function __construct($url=null) - { - parent::__construct(); - if ($url) { - $this->baseUrl = $url; - } - } - - function onStartNoticeSave($notice) - { - $args = $this->testArgs($notice); - common_debug("Blogspamnet args = " . print_r($args, TRUE)); - $requestBody = xmlrpc_encode_request('testComment', array($args)); - - $request = new HTTPClient($this->baseUrl, HTTPClient::METHOD_POST); - $request->setHeader('Content-Type', 'text/xml'); - $request->setBody($requestBody); - $httpResponse = $request->send(); - - $response = xmlrpc_decode($httpResponse->getBody()); - if (xmlrpc_is_fault($response)) { - throw new ServerException("$response[faultString] ($response[faultCode])", 500); - } else { - common_debug("Blogspamnet results = " . $response); - if (preg_match('/^ERROR(:(.*))?$/', $response, $match)) { - throw new ServerException(sprintf(_("Error from %s: %s"), $this->baseUrl, $match[2]), 500); - } else if (preg_match('/^SPAM(:(.*))?$/', $response, $match)) { - throw new ClientException(sprintf(_("Spam checker results: %s"), $match[2]), 400); - } else if (preg_match('/^OK$/', $response)) { - // don't do anything - } else { - throw new ServerException(sprintf(_("Unexpected response from %s: %s"), $this->baseUrl, $response), 500); - } - } - return true; - } - - function testArgs($notice) - { - $args = array(); - $args['comment'] = $notice->content; - $args['ip'] = $this->getClientIP(); - - if (isset($_SERVER) && array_key_exists('HTTP_USER_AGENT', $_SERVER)) { - $args['agent'] = $_SERVER['HTTP_USER_AGENT']; - } - - $profile = $notice->getProfile(); - - if ($profile && $profile->homepage) { - $args['link'] = $profile->homepage; - } - - if ($profile && $profile->fullname) { - $args['name'] = $profile->fullname; - } else { - $args['name'] = $profile->nickname; - } - - $args['site'] = common_root_url(); - $args['version'] = $this->userAgent(); - - $args['options'] = "max-size=" . common_config('site','textlimit') . ",min-size=0,min-words=0,exclude=bayasian"; - - return $args; - } - - function getClientIP() - { - if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - // Note: order matters here; use proxy-forwarded stuff first - foreach (array('HTTP_X_FORWARDED_FOR', 'CLIENT-IP', 'REMOTE_ADDR') as $k) { - if (isset($_SERVER[$k])) { - return $_SERVER[$k]; - } - } - } - return '127.0.0.1'; - } - - function userAgent() - { - return 'BlogspamNetPlugin/'.BLOGSPAMNETPLUGIN_VERSION . ' StatusNet/' . STATUSNET_VERSION; - } -} diff --git a/plugins/CacheLog/CacheLogPlugin.php b/plugins/CacheLog/CacheLogPlugin.php new file mode 100644 index 000000000..4c47de80e --- /dev/null +++ b/plugins/CacheLog/CacheLogPlugin.php @@ -0,0 +1,121 @@ +. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * Log cache access + * + * Note that since most caching plugins return false for StartCache* + * methods, you should add this plugin before them, i.e. + * + * addPlugin('CacheLog'); + * addPlugin('XCache'); + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class CacheLogPlugin extends Plugin +{ + function onStartCacheGet(&$key, &$value) + { + $this->log(LOG_INFO, "Fetching key '$key'"); + return true; + } + + function onEndCacheGet($key, &$value) + { + if ($value === false) { + $this->log(LOG_INFO, "Cache MISS for key '$key'"); + } else { + $this->log(LOG_INFO, "Cache HIT for key '$key'"); + } + return true; + } + + function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) + { + if (empty($value)) { + if (is_array($value)) { + $this->log(LOG_INFO, "Setting empty array for key '$key'"); + } else if (is_null($value)) { + $this->log(LOG_INFO, "Setting null value for key '$key'"); + } else if (is_string($value)) { + $this->log(LOG_INFO, "Setting empty string for key '$key'"); + } else if (is_integer($value)) { + $this->log(LOG_INFO, "Setting integer 0 for key '$key'"); + } else { + $this->log(LOG_INFO, "Setting empty value '$value' for key '$key'"); + } + } else { + $this->log(LOG_INFO, "Setting non-empty value for key '$key'"); + } + return true; + } + + function onEndCacheSet($key, $value, $flag, $expiry) + { + $this->log(LOG_INFO, "Done setting cache value for key '$key'"); + return true; + } + + function onStartCacheDelete(&$key, &$success) + { + $this->log(LOG_INFO, "Deleting cache value for key '$key'"); + return true; + } + + function onEndCacheDelete($key) + { + $this->log(LOG_INFO, "Done deleting cache value for key '$key'"); + return true; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'CacheLog', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:CacheLog', + 'description' => + _m('Log reads and writes to the cache')); + return true; + } +} + diff --git a/plugins/CacheLogPlugin.php b/plugins/CacheLogPlugin.php deleted file mode 100644 index 4c47de80e..000000000 --- a/plugins/CacheLogPlugin.php +++ /dev/null @@ -1,121 +0,0 @@ -. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - // This check helps protect against security problems; - // your code file can't be executed directly from the web. - exit(1); -} - -/** - * Log cache access - * - * Note that since most caching plugins return false for StartCache* - * methods, you should add this plugin before them, i.e. - * - * addPlugin('CacheLog'); - * addPlugin('XCache'); - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 - * @link http://status.net/ - */ - -class CacheLogPlugin extends Plugin -{ - function onStartCacheGet(&$key, &$value) - { - $this->log(LOG_INFO, "Fetching key '$key'"); - return true; - } - - function onEndCacheGet($key, &$value) - { - if ($value === false) { - $this->log(LOG_INFO, "Cache MISS for key '$key'"); - } else { - $this->log(LOG_INFO, "Cache HIT for key '$key'"); - } - return true; - } - - function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) - { - if (empty($value)) { - if (is_array($value)) { - $this->log(LOG_INFO, "Setting empty array for key '$key'"); - } else if (is_null($value)) { - $this->log(LOG_INFO, "Setting null value for key '$key'"); - } else if (is_string($value)) { - $this->log(LOG_INFO, "Setting empty string for key '$key'"); - } else if (is_integer($value)) { - $this->log(LOG_INFO, "Setting integer 0 for key '$key'"); - } else { - $this->log(LOG_INFO, "Setting empty value '$value' for key '$key'"); - } - } else { - $this->log(LOG_INFO, "Setting non-empty value for key '$key'"); - } - return true; - } - - function onEndCacheSet($key, $value, $flag, $expiry) - { - $this->log(LOG_INFO, "Done setting cache value for key '$key'"); - return true; - } - - function onStartCacheDelete(&$key, &$success) - { - $this->log(LOG_INFO, "Deleting cache value for key '$key'"); - return true; - } - - function onEndCacheDelete($key) - { - $this->log(LOG_INFO, "Done deleting cache value for key '$key'"); - return true; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'CacheLog', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:CacheLog', - 'description' => - _m('Log reads and writes to the cache')); - return true; - } -} - diff --git a/plugins/DiskCache/DiskCachePlugin.php b/plugins/DiskCache/DiskCachePlugin.php new file mode 100644 index 000000000..b709ea3b3 --- /dev/null +++ b/plugins/DiskCache/DiskCachePlugin.php @@ -0,0 +1,168 @@ +. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * A plugin to cache data on local disk + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class DiskCachePlugin extends Plugin +{ + var $root = '/tmp'; + + function keyToFilename($key) + { + return $this->root . '/' . str_replace(':', '/', $key); + } + + /** + * Get a value associated with a key + * + * The value should have been set previously. + * + * @param string &$key in; Lookup key + * @param mixed &$value out; value associated with key + * + * @return boolean hook success + */ + + function onStartCacheGet(&$key, &$value) + { + $filename = $this->keyToFilename($key); + + if (file_exists($filename)) { + $data = file_get_contents($filename); + if ($data !== false) { + $value = unserialize($data); + } + } + + Event::handle('EndCacheGet', array($key, &$value)); + return false; + } + + /** + * Associate a value with a key + * + * @param string &$key in; Key to use for lookups + * @param mixed &$value in; Value to associate + * @param integer &$flag in; Flag (passed through to Memcache) + * @param integer &$expiry in; Expiry (passed through to Memcache) + * @param boolean &$success out; Whether the set was successful + * + * @return boolean hook success + */ + + function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) + { + $filename = $this->keyToFilename($key); + $parent = dirname($filename); + + $sofar = ''; + + foreach (explode('/', $parent) as $part) { + if (empty($part)) { + continue; + } + $sofar .= '/' . $part; + if (!is_dir($sofar)) { + $this->debug("Creating new directory '$sofar'"); + $success = mkdir($sofar, 0750); + if (!$success) { + $this->log(LOG_ERR, "Can't create directory '$sofar'"); + return false; + } + } + } + + if (is_dir($filename)) { + $success = false; + return false; + } + + // Write to a temp file and move to destination + + $tempname = tempnam(null, 'statusnetdiskcache'); + + $result = file_put_contents($tempname, serialize($value)); + + if ($result === false) { + $this->log(LOG_ERR, "Couldn't write '$key' to temp file '$tempname'"); + return false; + } + + $result = rename($tempname, $filename); + + if (!$result) { + $this->log(LOG_ERR, "Couldn't move temp file '$tempname' to path '$filename' for key '$key'"); + @unlink($tempname); + return false; + } + + Event::handle('EndCacheSet', array($key, $value, $flag, + $expiry)); + + return false; + } + + /** + * Delete a value associated with a key + * + * @param string &$key in; Key to lookup + * @param boolean &$success out; whether it worked + * + * @return boolean hook success + */ + + function onStartCacheDelete(&$key, &$success) + { + $filename = $this->keyToFilename($key); + + if (file_exists($filename) && !is_dir($filename)) { + unlink($filename); + } + + Event::handle('EndCacheDelete', array($key)); + return false; + } +} + diff --git a/plugins/DiskCachePlugin.php b/plugins/DiskCachePlugin.php deleted file mode 100644 index b709ea3b3..000000000 --- a/plugins/DiskCachePlugin.php +++ /dev/null @@ -1,168 +0,0 @@ -. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - // This check helps protect against security problems; - // your code file can't be executed directly from the web. - exit(1); -} - -/** - * A plugin to cache data on local disk - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -class DiskCachePlugin extends Plugin -{ - var $root = '/tmp'; - - function keyToFilename($key) - { - return $this->root . '/' . str_replace(':', '/', $key); - } - - /** - * Get a value associated with a key - * - * The value should have been set previously. - * - * @param string &$key in; Lookup key - * @param mixed &$value out; value associated with key - * - * @return boolean hook success - */ - - function onStartCacheGet(&$key, &$value) - { - $filename = $this->keyToFilename($key); - - if (file_exists($filename)) { - $data = file_get_contents($filename); - if ($data !== false) { - $value = unserialize($data); - } - } - - Event::handle('EndCacheGet', array($key, &$value)); - return false; - } - - /** - * Associate a value with a key - * - * @param string &$key in; Key to use for lookups - * @param mixed &$value in; Value to associate - * @param integer &$flag in; Flag (passed through to Memcache) - * @param integer &$expiry in; Expiry (passed through to Memcache) - * @param boolean &$success out; Whether the set was successful - * - * @return boolean hook success - */ - - function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) - { - $filename = $this->keyToFilename($key); - $parent = dirname($filename); - - $sofar = ''; - - foreach (explode('/', $parent) as $part) { - if (empty($part)) { - continue; - } - $sofar .= '/' . $part; - if (!is_dir($sofar)) { - $this->debug("Creating new directory '$sofar'"); - $success = mkdir($sofar, 0750); - if (!$success) { - $this->log(LOG_ERR, "Can't create directory '$sofar'"); - return false; - } - } - } - - if (is_dir($filename)) { - $success = false; - return false; - } - - // Write to a temp file and move to destination - - $tempname = tempnam(null, 'statusnetdiskcache'); - - $result = file_put_contents($tempname, serialize($value)); - - if ($result === false) { - $this->log(LOG_ERR, "Couldn't write '$key' to temp file '$tempname'"); - return false; - } - - $result = rename($tempname, $filename); - - if (!$result) { - $this->log(LOG_ERR, "Couldn't move temp file '$tempname' to path '$filename' for key '$key'"); - @unlink($tempname); - return false; - } - - Event::handle('EndCacheSet', array($key, $value, $flag, - $expiry)); - - return false; - } - - /** - * Delete a value associated with a key - * - * @param string &$key in; Key to lookup - * @param boolean &$success out; whether it worked - * - * @return boolean hook success - */ - - function onStartCacheDelete(&$key, &$success) - { - $filename = $this->keyToFilename($key); - - if (file_exists($filename) && !is_dir($filename)) { - unlink($filename); - } - - Event::handle('EndCacheDelete', array($key)); - return false; - } -} - diff --git a/plugins/Disqus/DisqusPlugin.php b/plugins/Disqus/DisqusPlugin.php new file mode 100644 index 000000000..c07eaaabd --- /dev/null +++ b/plugins/Disqus/DisqusPlugin.php @@ -0,0 +1,171 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @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); +} + +/** + * + * This plugin adds Disqus commenting to your notices. Enabling this + * plugin will make each notice page display the Disqus widget, and + * notice lists will display the number of commments each notice has. + * + * To use this plugin, you need to first register your site with Disqus + * and get a Discus 'shortname' for it. + * + * http://disqus.com + * + * To enable the plugin, put the following in you config.php: + * + * addPlugin( + * 'Disqus', array( + * 'shortname' => 'YOURSHORTNAME', + * 'div_style' => 'width:675px; padding-top:10px; position:relative; float:left;' + * ) + * ); + * + * NOTE: the 'div_style' in an optional parameter that passes in some + * inline CSS when creating the Disqus widget. It's a shortcut to make + * the widget look OK with the default StatusNet theme. If you leave + * it out you'll have to edit your theme CSS files to make the widget + * look good. You can also control the way the widget looks by + * adding style rules to your theme. + * + * See: http://help.disqus.com/entries/100878-css-customization + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Event + */ + +class DisqusPlugin extends Plugin +{ + function onEndShowContentBlock($action) + { + if (get_class($action) == 'ShownoticeAction') { + + $attrs = array(); + $attrs['id'] = 'disqus_thread'; + + if ($this->div_style) { + $attrs['style'] = $this->div_style; + } + + $action->element('div', $attrs, null); + + $script = <<inlineScript(sprintf($script, $action->notice->id, $this->shortname)); + + $attrs = array(); + + $attrs['id'] = 'disqus_thread_footer'; + + if ($this->div_style) { + $attrs['style'] = $this->div_style; + } + + $action->elementStart('div', $attrs); + $action->elementStart('noscript'); + + $action->raw('Please enable JavaScript to view the '); + $noscriptUrl = 'http://disqus.com/?ref_noscript=' . $this->shortname; + $action->element('a', array('href' => $noscriptUrl), 'comments powered by Disqus.'); + $action->elementEnd('noscript'); + + $action->elementStart('a', array('href' => 'http://disqus.com', 'class' => 'dsq-brlink')); + $action->raw('blog comments powered by '); + $action->element('span', array('class' => 'logo-disqus'), 'Disqus'); + $action->elementEnd('a'); + $action->elementEnd('div'); + } + } + + function onEndShowScripts($action) + { + // fugly + $script = <<inlineScript(sprintf($script, $this->shortname, $this->shortname)); + + return true; + } + + function onStartShowNoticeItem($noticeListItem) + { + if (empty($noticeListItem->notice->is_local)) { + return true; + } + + $noticeListItem->showNotice(); + $noticeListItem->showNoticeInfo(); + + $noticeUrl = $noticeListItem->notice->bestUrl(); + $noticeUrl .= '#disqus_thread'; + + $noticeListItem->out->element( + 'a', array('href' => $noticeUrl, 'class' => 'disqus_count'), 'Comments' + ); + + $noticeListItem->showNoticeOptions(); + Event::handle('EndShowNoticeItem', array($noticeListItem)); + + return false; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Disqus', + 'version' => STATUSNET_VERSION, + 'author' => 'Zach Copley', + 'homepage' => 'http://status.net/wiki/Plugin:Disqus', + 'rawdescription' => + _m('Use Disqus'. + ' to add commenting to notice pages.')); + return true; + } +} diff --git a/plugins/DisqusPlugin.php b/plugins/DisqusPlugin.php deleted file mode 100644 index c07eaaabd..000000000 --- a/plugins/DisqusPlugin.php +++ /dev/null @@ -1,171 +0,0 @@ -. - * - * @category Plugin - * @package StatusNet - * @author Zach Copley - * @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); -} - -/** - * - * This plugin adds Disqus commenting to your notices. Enabling this - * plugin will make each notice page display the Disqus widget, and - * notice lists will display the number of commments each notice has. - * - * To use this plugin, you need to first register your site with Disqus - * and get a Discus 'shortname' for it. - * - * http://disqus.com - * - * To enable the plugin, put the following in you config.php: - * - * addPlugin( - * 'Disqus', array( - * 'shortname' => 'YOURSHORTNAME', - * 'div_style' => 'width:675px; padding-top:10px; position:relative; float:left;' - * ) - * ); - * - * NOTE: the 'div_style' in an optional parameter that passes in some - * inline CSS when creating the Disqus widget. It's a shortcut to make - * the widget look OK with the default StatusNet theme. If you leave - * it out you'll have to edit your theme CSS files to make the widget - * look good. You can also control the way the widget looks by - * adding style rules to your theme. - * - * See: http://help.disqus.com/entries/100878-css-customization - * - * @category Plugin - * @package StatusNet - * @author Zach Copley - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @see Event - */ - -class DisqusPlugin extends Plugin -{ - function onEndShowContentBlock($action) - { - if (get_class($action) == 'ShownoticeAction') { - - $attrs = array(); - $attrs['id'] = 'disqus_thread'; - - if ($this->div_style) { - $attrs['style'] = $this->div_style; - } - - $action->element('div', $attrs, null); - - $script = <<inlineScript(sprintf($script, $action->notice->id, $this->shortname)); - - $attrs = array(); - - $attrs['id'] = 'disqus_thread_footer'; - - if ($this->div_style) { - $attrs['style'] = $this->div_style; - } - - $action->elementStart('div', $attrs); - $action->elementStart('noscript'); - - $action->raw('Please enable JavaScript to view the '); - $noscriptUrl = 'http://disqus.com/?ref_noscript=' . $this->shortname; - $action->element('a', array('href' => $noscriptUrl), 'comments powered by Disqus.'); - $action->elementEnd('noscript'); - - $action->elementStart('a', array('href' => 'http://disqus.com', 'class' => 'dsq-brlink')); - $action->raw('blog comments powered by '); - $action->element('span', array('class' => 'logo-disqus'), 'Disqus'); - $action->elementEnd('a'); - $action->elementEnd('div'); - } - } - - function onEndShowScripts($action) - { - // fugly - $script = <<inlineScript(sprintf($script, $this->shortname, $this->shortname)); - - return true; - } - - function onStartShowNoticeItem($noticeListItem) - { - if (empty($noticeListItem->notice->is_local)) { - return true; - } - - $noticeListItem->showNotice(); - $noticeListItem->showNoticeInfo(); - - $noticeUrl = $noticeListItem->notice->bestUrl(); - $noticeUrl .= '#disqus_thread'; - - $noticeListItem->out->element( - 'a', array('href' => $noticeUrl, 'class' => 'disqus_count'), 'Comments' - ); - - $noticeListItem->showNoticeOptions(); - Event::handle('EndShowNoticeItem', array($noticeListItem)); - - return false; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'Disqus', - 'version' => STATUSNET_VERSION, - 'author' => 'Zach Copley', - 'homepage' => 'http://status.net/wiki/Plugin:Disqus', - 'rawdescription' => - _m('Use Disqus'. - ' to add commenting to notice pages.')); - return true; - } -} diff --git a/plugins/Echo/EchoPlugin.php b/plugins/Echo/EchoPlugin.php new file mode 100644 index 000000000..7b51866eb --- /dev/null +++ b/plugins/Echo/EchoPlugin.php @@ -0,0 +1,116 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to use Echo (formerly JS-Kit) + * + * This plugin adds an Echo commenting widget to each notice page on + * your site. To get it to work, first you'll have to sign up for Echo + * (a for-pay service) and register your site's URL. + * + * http://aboutecho.com/ + * + * Once you've done that it's pretty straight forward to turn the + * plugin on; just add this to your config.php: + * + * addPlugin( + * 'Echo', + * array('div_style' => 'width:675px; padding-top:10px; position:relative; float:left;') + * ); + * + * NOTE: the 'div_style' in an optional parameter that passes in some + * inline CSS when creating the Echo widget. It's a shortcut to make + * the widget look OK with the default StatusNet theme. If you leave + * it out you'll have to edit your theme CSS files to make the widget + * look good. You can also control the way the widget looks by + * adding style rules to your theme. + * + * See: http://wiki.js-kit.com/Skinning-Guide#UsingCSSnbsptocustomizefontsandcolors + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Event + */ + +class EchoPlugin extends Plugin +{ + // NOTE: The Echo documentation says that this script will change on + // a per site basis, but I think that's incorrect. It always seems to + // be the same. + public $script = 'http://cdn.js-kit.com/scripts/comments.js'; + + function onEndShowScripts($action) + { + if (get_class($action) == 'ShownoticeAction') { + $action->script($this->script); + } + + return true; + } + + function onEndShowContentBlock($action) + { + if (get_class($action) == 'ShownoticeAction') { + + $attrs = array(); + $attrs['class'] = 'js-kit-comments'; + $attrs['permalink'] = $action->notice->uri; + $attrs['uniq'] = $action->notice->id; + + // NOTE: there are some other attributes that could be useful + // http://wiki.js-kit.com/Echo-Behavior + + if (!empty($this->div_style)) { + $attrs['style'] = $this->div_style; + } + + $action->element('div', $attrs, null); + } + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Echo', + 'version' => STATUSNET_VERSION, + 'author' => 'Zach Copley', + 'homepage' => 'http://status.net/wiki/Plugin:Echo', + 'rawdescription' => + _m('Use Echo'. + ' to add commenting to notice pages.')); + return true; + } +} diff --git a/plugins/EchoPlugin.php b/plugins/EchoPlugin.php deleted file mode 100644 index 7b51866eb..000000000 --- a/plugins/EchoPlugin.php +++ /dev/null @@ -1,116 +0,0 @@ -. - * - * @category Plugin - * @package StatusNet - * @author Zach Copley - * @copyright 2010 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -/** - * Plugin to use Echo (formerly JS-Kit) - * - * This plugin adds an Echo commenting widget to each notice page on - * your site. To get it to work, first you'll have to sign up for Echo - * (a for-pay service) and register your site's URL. - * - * http://aboutecho.com/ - * - * Once you've done that it's pretty straight forward to turn the - * plugin on; just add this to your config.php: - * - * addPlugin( - * 'Echo', - * array('div_style' => 'width:675px; padding-top:10px; position:relative; float:left;') - * ); - * - * NOTE: the 'div_style' in an optional parameter that passes in some - * inline CSS when creating the Echo widget. It's a shortcut to make - * the widget look OK with the default StatusNet theme. If you leave - * it out you'll have to edit your theme CSS files to make the widget - * look good. You can also control the way the widget looks by - * adding style rules to your theme. - * - * See: http://wiki.js-kit.com/Skinning-Guide#UsingCSSnbsptocustomizefontsandcolors - * - * @category Plugin - * @package StatusNet - * @author Zach Copley - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @see Event - */ - -class EchoPlugin extends Plugin -{ - // NOTE: The Echo documentation says that this script will change on - // a per site basis, but I think that's incorrect. It always seems to - // be the same. - public $script = 'http://cdn.js-kit.com/scripts/comments.js'; - - function onEndShowScripts($action) - { - if (get_class($action) == 'ShownoticeAction') { - $action->script($this->script); - } - - return true; - } - - function onEndShowContentBlock($action) - { - if (get_class($action) == 'ShownoticeAction') { - - $attrs = array(); - $attrs['class'] = 'js-kit-comments'; - $attrs['permalink'] = $action->notice->uri; - $attrs['uniq'] = $action->notice->id; - - // NOTE: there are some other attributes that could be useful - // http://wiki.js-kit.com/Echo-Behavior - - if (!empty($this->div_style)) { - $attrs['style'] = $this->div_style; - } - - $action->element('div', $attrs, null); - } - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'Echo', - 'version' => STATUSNET_VERSION, - 'author' => 'Zach Copley', - 'homepage' => 'http://status.net/wiki/Plugin:Echo', - 'rawdescription' => - _m('Use Echo'. - ' to add commenting to notice pages.')); - return true; - } -} diff --git a/plugins/GeoURL/GeoURLPlugin.php b/plugins/GeoURL/GeoURLPlugin.php new file mode 100644 index 000000000..01178f39c --- /dev/null +++ b/plugins/GeoURL/GeoURLPlugin.php @@ -0,0 +1,131 @@ +. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to add ICBM metadata to HTML pages and report data to GeoURL.org + * + * Adds metadata to notice and profile pages that geourl.org and others + * understand. Also, pings geourl.org when a new notice is saved or + * a profile is changed. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso Location + */ + +class GeoURLPlugin extends Plugin +{ + public $ping = 'http://geourl.org/ping/'; + + /** + * Add extra headers for certain pages that geourl.org understands + * + * @param Action $action page being shown + * + * @return boolean event handler flag + */ + + function onEndShowHeadElements($action) + { + $name = $action->trimmed('action'); + + $location = null; + + if ($name == 'showstream') { + $profile = $action->profile; + if (!empty($profile) && !empty($profile->lat) && !empty($profile->lon)) { + $location = $profile->lat . ', ' . $profile->lon; + } + } else if ($name == 'shownotice') { + $notice = $action->profile; + if (!empty($notice) && !empty($notice->lat) && !empty($notice->lon)) { + $location = $notice->lat . ', ' . $notice->lon; + } + } + + if (!empty($location)) { + $action->element('meta', array('name' => 'ICBM', + 'content' => $location)); + $action->element('meta', array('name' => 'DC.title', + 'content' => $action->title())); + } + + return true; + } + + /** + * Report local notices to GeoURL.org when they're created + * + * @param Notice &$notice queued notice + * + * @return boolean event handler flag + */ + + function onHandleQueuedNotice(&$notice) + { + if ($notice->is_local == 1) { + + $request = HTTPClient::start(); + + $url = common_local_url('shownotice', + array('notice' => $notice->id)); + + try { + $request->post($this->ping, + null, + array('p' => $url)); + } catch (HTTP_Request2_Exception $e) { + common_log(LOG_WARNING, + "GeoURL.org ping failed for '$url' ($this->ping)"); + } + } + + return true; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'GeoURL', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:GeoURL', + 'rawdescription' => + _m('Ping GeoURL when '. + 'new geolocation-enhanced notices are posted.')); + return true; + } +} diff --git a/plugins/GeoURLPlugin.php b/plugins/GeoURLPlugin.php deleted file mode 100644 index 01178f39c..000000000 --- a/plugins/GeoURLPlugin.php +++ /dev/null @@ -1,131 +0,0 @@ -. - * - * @category Action - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -/** - * Plugin to add ICBM metadata to HTML pages and report data to GeoURL.org - * - * Adds metadata to notice and profile pages that geourl.org and others - * understand. Also, pings geourl.org when a new notice is saved or - * a profile is changed. - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @seeAlso Location - */ - -class GeoURLPlugin extends Plugin -{ - public $ping = 'http://geourl.org/ping/'; - - /** - * Add extra headers for certain pages that geourl.org understands - * - * @param Action $action page being shown - * - * @return boolean event handler flag - */ - - function onEndShowHeadElements($action) - { - $name = $action->trimmed('action'); - - $location = null; - - if ($name == 'showstream') { - $profile = $action->profile; - if (!empty($profile) && !empty($profile->lat) && !empty($profile->lon)) { - $location = $profile->lat . ', ' . $profile->lon; - } - } else if ($name == 'shownotice') { - $notice = $action->profile; - if (!empty($notice) && !empty($notice->lat) && !empty($notice->lon)) { - $location = $notice->lat . ', ' . $notice->lon; - } - } - - if (!empty($location)) { - $action->element('meta', array('name' => 'ICBM', - 'content' => $location)); - $action->element('meta', array('name' => 'DC.title', - 'content' => $action->title())); - } - - return true; - } - - /** - * Report local notices to GeoURL.org when they're created - * - * @param Notice &$notice queued notice - * - * @return boolean event handler flag - */ - - function onHandleQueuedNotice(&$notice) - { - if ($notice->is_local == 1) { - - $request = HTTPClient::start(); - - $url = common_local_url('shownotice', - array('notice' => $notice->id)); - - try { - $request->post($this->ping, - null, - array('p' => $url)); - } catch (HTTP_Request2_Exception $e) { - common_log(LOG_WARNING, - "GeoURL.org ping failed for '$url' ($this->ping)"); - } - } - - return true; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'GeoURL', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:GeoURL', - 'rawdescription' => - _m('Ping GeoURL when '. - 'new geolocation-enhanced notices are posted.')); - return true; - } -} diff --git a/plugins/Geonames/GeonamesPlugin.php b/plugins/Geonames/GeonamesPlugin.php new file mode 100644 index 000000000..3815a31fa --- /dev/null +++ b/plugins/Geonames/GeonamesPlugin.php @@ -0,0 +1,510 @@ +. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to convert string locations to Geonames IDs and vice versa + * + * This handles most of the events that Location class emits. It uses + * the geonames.org Web service to convert names like 'Montreal, Quebec, Canada' + * into IDs and lat/lon pairs. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso Location + */ + +class GeonamesPlugin extends Plugin +{ + const LOCATION_NS = 1; + + public $host = 'ws.geonames.org'; + public $username = null; + public $token = null; + public $expiry = 7776000; // 90-day expiry + public $timeout = 2; // Web service timeout in seconds. + public $timeoutWindow = 60; // Further lookups in this process will be disabled for N seconds after a timeout. + public $cachePrefix = null; // Optional shared memcache prefix override + // to share lookups between local instances. + + protected $lastTimeout = null; // timestamp of last web service timeout + + /** + * convert a name into a Location object + * + * @param string $name Name to convert + * @param string $language ISO code for anguage the name is in + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + + function onLocationFromName($name, $language, &$location) + { + $loc = $this->getCache(array('name' => $name, + 'language' => $language)); + + if ($loc !== false) { + $location = $loc; + return false; + } + + try { + $geonames = $this->getGeonames('search', + array('maxRows' => 1, + 'q' => $name, + 'lang' => $language, + 'type' => 'xml')); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for $name: " . $e->getMessage()); + return true; + } + + if (count($geonames) == 0) { + // no results + $this->setCache(array('name' => $name, + 'language' => $language), + null); + return true; + } + + $n = $geonames[0]; + + $location = new Location(); + + $location->lat = $this->canonical($n->lat); + $location->lon = $this->canonical($n->lng); + $location->names[$language] = (string)$n->name; + $location->location_id = (string)$n->geonameId; + $location->location_ns = self::LOCATION_NS; + + $this->setCache(array('name' => $name, + 'language' => $language), + $location); + + // handled, don't continue processing! + return false; + } + + /** + * convert an id into a Location object + * + * @param string $id Name to convert + * @param string $ns Name to convert + * @param string $language ISO code for language for results + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + + function onLocationFromId($id, $ns, $language, &$location) + { + if ($ns != self::LOCATION_NS) { + // It's not one of our IDs... keep processing + return true; + } + + $loc = $this->getCache(array('id' => $id)); + + if ($loc !== false) { + $location = $loc; + return false; + } + + try { + $geonames = $this->getGeonames('hierarchy', + array('geonameId' => $id, + 'lang' => $language)); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); + return false; + } + + $parts = array(); + + foreach ($geonames as $level) { + if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$level->name; + } + } + + $last = $geonames[count($geonames)-1]; + + if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$last->name; + } + + $location = new Location(); + + $location->location_id = (string)$last->geonameId; + $location->location_ns = self::LOCATION_NS; + $location->lat = $this->canonical($last->lat); + $location->lon = $this->canonical($last->lng); + + $location->names[$language] = implode(', ', array_reverse($parts)); + + $this->setCache(array('id' => (string)$last->geonameId), + $location); + + // We're responsible for this namespace; nobody else + // can resolve it + + return false; + } + + /** + * convert a lat/lon pair into a Location object + * + * Given a lat/lon, we try to find a Location that's around + * it or nearby. We prefer populated places (cities, towns, villages). + * + * @param string $lat Latitude + * @param string $lon Longitude + * @param string $language ISO code for language for results + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + + function onLocationFromLatLon($lat, $lon, $language, &$location) + { + // Make sure they're canonical + + $lat = $this->canonical($lat); + $lon = $this->canonical($lon); + + $loc = $this->getCache(array('lat' => $lat, + 'lon' => $lon)); + + if ($loc !== false) { + $location = $loc; + return false; + } + + try { + $geonames = $this->getGeonames('findNearbyPlaceName', + array('lat' => $lat, + 'lng' => $lon, + 'lang' => $language)); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for coords $lat, $lon: " . $e->getMessage()); + return true; + } + + if (count($geonames) == 0) { + // no results + $this->setCache(array('lat' => $lat, + 'lon' => $lon), + null); + return true; + } + + $n = $geonames[0]; + + $parts = array(); + + $location = new Location(); + + $parts[] = (string)$n->name; + + if (!empty($n->adminName1)) { + $parts[] = (string)$n->adminName1; + } + + if (!empty($n->countryName)) { + $parts[] = (string)$n->countryName; + } + + $location->location_id = (string)$n->geonameId; + $location->location_ns = self::LOCATION_NS; + $location->lat = $this->canonical($n->lat); + $location->lon = $this->canonical($n->lng); + + $location->names[$language] = implode(', ', $parts); + + $this->setCache(array('lat' => $lat, + 'lon' => $lon), + $location); + + // Success! We handled it, so no further processing + + return false; + } + + /** + * Human-readable name for a location + * + * Given a location, we try to retrieve a human-readable name + * in the target language. + * + * @param Location $location Location to get the name for + * @param string $language ISO code for language to find name in + * @param string &$name Place to put the name + * + * @return boolean whether to continue + */ + + function onLocationNameLanguage($location, $language, &$name) + { + if ($location->location_ns != self::LOCATION_NS) { + // It's not one of our IDs... keep processing + return true; + } + + $id = $location->location_id; + + $n = $this->getCache(array('id' => $id, + 'language' => $language)); + + if ($n !== false) { + $name = $n; + return false; + } + + try { + $geonames = $this->getGeonames('hierarchy', + array('geonameId' => $id, + 'lang' => $language)); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); + return false; + } + + if (count($geonames) == 0) { + $this->setCache(array('id' => $id, + 'language' => $language), + null); + return false; + } + + $parts = array(); + + foreach ($geonames as $level) { + if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$level->name; + } + } + + $last = $geonames[count($geonames)-1]; + + if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$last->name; + } + + if (count($parts)) { + $name = implode(', ', array_reverse($parts)); + $this->setCache(array('id' => $id, + 'language' => $language), + $name); + } + + return false; + } + + /** + * Human-readable URL for a location + * + * Given a location, we try to retrieve a geonames.org URL. + * + * @param Location $location Location to get the url for + * @param string &$url Place to put the url + * + * @return boolean whether to continue + */ + + function onLocationUrl($location, &$url) + { + if ($location->location_ns != self::LOCATION_NS) { + // It's not one of our IDs... keep processing + return true; + } + + $url = 'http://www.geonames.org/' . $location->location_id; + + // it's been filled, so don't process further. + return false; + } + + /** + * Machine-readable name for a location + * + * Given a location, we try to retrieve a geonames.org URL. + * + * @param Location $location Location to get the url for + * @param string &$url Place to put the url + * + * @return boolean whether to continue + */ + + function onLocationRdfUrl($location, &$url) + { + if ($location->location_ns != self::LOCATION_NS) { + // It's not one of our IDs... keep processing + return true; + } + + $url = 'http://sws.geonames.org/' . $location->location_id . '/'; + + // it's been filled, so don't process further. + return false; + } + + function getCache($attrs) + { + $c = common_memcache(); + + if (empty($c)) { + return null; + } + + $key = $this->cacheKey($attrs); + + $value = $c->get($key); + + return $value; + } + + function setCache($attrs, $loc) + { + $c = common_memcache(); + + if (empty($c)) { + return null; + } + + $key = $this->cacheKey($attrs); + + $result = $c->set($key, $loc, 0, time() + $this->expiry); + + return $result; + } + + function cacheKey($attrs) + { + $key = 'geonames:' . + implode(',', array_keys($attrs)) . ':'. + common_keyize(implode(',', array_values($attrs))); + if ($this->cachePrefix) { + return $this->cachePrefix . ':' . $key; + } else { + return common_cache_key($key); + } + } + + function wsUrl($method, $params) + { + if (!empty($this->username)) { + $params['username'] = $this->username; + } + + if (!empty($this->token)) { + $params['token'] = $this->token; + } + + $str = http_build_query($params, null, '&'); + + return 'http://'.$this->host.'/'.$method.'?'.$str; + } + + function getGeonames($method, $params) + { + if ($this->lastTimeout && (time() - $this->lastTimeout < $this->timeoutWindow)) { + throw new Exception("skipping due to recent web service timeout"); + } + + $client = HTTPClient::start(); + $client->setConfig('connect_timeout', $this->timeout); + $client->setConfig('timeout', $this->timeout); + + try { + $result = $client->get($this->wsUrl($method, $params)); + } catch (Exception $e) { + common_log(LOG_ERR, __METHOD__ . ": " . $e->getMessage()); + $this->lastTimeout = time(); + throw $e; + } + + if (!$result->isOk()) { + throw new Exception("HTTP error code " . $result->getStatus()); + } + + $body = $result->getBody(); + + if (empty($body)) { + throw new Exception("Empty HTTP body in response"); + } + + // This will throw an exception if the XML is mal-formed + + $document = new SimpleXMLElement($body); + + // No children, usually no results + + $children = $document->children(); + + if (count($children) == 0) { + return array(); + } + + if (isset($document->status)) { + throw new Exception("Error #".$document->status['value']." ('".$document->status['message']."')"); + } + + // Array of elements, >0 elements + + return $document->geoname; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Geonames', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:Geonames', + 'rawdescription' => + _m('Uses Geonames service to get human-readable '. + 'names for locations based on user-provided lat/long pairs.')); + return true; + } + + function canonical($coord) + { + $coord = rtrim($coord, "0"); + $coord = rtrim($coord, "."); + + return $coord; + } +} diff --git a/plugins/GeonamesPlugin.php b/plugins/GeonamesPlugin.php deleted file mode 100644 index 3815a31fa..000000000 --- a/plugins/GeonamesPlugin.php +++ /dev/null @@ -1,510 +0,0 @@ -. - * - * @category Action - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -/** - * Plugin to convert string locations to Geonames IDs and vice versa - * - * This handles most of the events that Location class emits. It uses - * the geonames.org Web service to convert names like 'Montreal, Quebec, Canada' - * into IDs and lat/lon pairs. - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @seeAlso Location - */ - -class GeonamesPlugin extends Plugin -{ - const LOCATION_NS = 1; - - public $host = 'ws.geonames.org'; - public $username = null; - public $token = null; - public $expiry = 7776000; // 90-day expiry - public $timeout = 2; // Web service timeout in seconds. - public $timeoutWindow = 60; // Further lookups in this process will be disabled for N seconds after a timeout. - public $cachePrefix = null; // Optional shared memcache prefix override - // to share lookups between local instances. - - protected $lastTimeout = null; // timestamp of last web service timeout - - /** - * convert a name into a Location object - * - * @param string $name Name to convert - * @param string $language ISO code for anguage the name is in - * @param Location &$location Location object (may be null) - * - * @return boolean whether to continue (results in $location) - */ - - function onLocationFromName($name, $language, &$location) - { - $loc = $this->getCache(array('name' => $name, - 'language' => $language)); - - if ($loc !== false) { - $location = $loc; - return false; - } - - try { - $geonames = $this->getGeonames('search', - array('maxRows' => 1, - 'q' => $name, - 'lang' => $language, - 'type' => 'xml')); - } catch (Exception $e) { - $this->log(LOG_WARNING, "Error for $name: " . $e->getMessage()); - return true; - } - - if (count($geonames) == 0) { - // no results - $this->setCache(array('name' => $name, - 'language' => $language), - null); - return true; - } - - $n = $geonames[0]; - - $location = new Location(); - - $location->lat = $this->canonical($n->lat); - $location->lon = $this->canonical($n->lng); - $location->names[$language] = (string)$n->name; - $location->location_id = (string)$n->geonameId; - $location->location_ns = self::LOCATION_NS; - - $this->setCache(array('name' => $name, - 'language' => $language), - $location); - - // handled, don't continue processing! - return false; - } - - /** - * convert an id into a Location object - * - * @param string $id Name to convert - * @param string $ns Name to convert - * @param string $language ISO code for language for results - * @param Location &$location Location object (may be null) - * - * @return boolean whether to continue (results in $location) - */ - - function onLocationFromId($id, $ns, $language, &$location) - { - if ($ns != self::LOCATION_NS) { - // It's not one of our IDs... keep processing - return true; - } - - $loc = $this->getCache(array('id' => $id)); - - if ($loc !== false) { - $location = $loc; - return false; - } - - try { - $geonames = $this->getGeonames('hierarchy', - array('geonameId' => $id, - 'lang' => $language)); - } catch (Exception $e) { - $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); - return false; - } - - $parts = array(); - - foreach ($geonames as $level) { - if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = (string)$level->name; - } - } - - $last = $geonames[count($geonames)-1]; - - if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = (string)$last->name; - } - - $location = new Location(); - - $location->location_id = (string)$last->geonameId; - $location->location_ns = self::LOCATION_NS; - $location->lat = $this->canonical($last->lat); - $location->lon = $this->canonical($last->lng); - - $location->names[$language] = implode(', ', array_reverse($parts)); - - $this->setCache(array('id' => (string)$last->geonameId), - $location); - - // We're responsible for this namespace; nobody else - // can resolve it - - return false; - } - - /** - * convert a lat/lon pair into a Location object - * - * Given a lat/lon, we try to find a Location that's around - * it or nearby. We prefer populated places (cities, towns, villages). - * - * @param string $lat Latitude - * @param string $lon Longitude - * @param string $language ISO code for language for results - * @param Location &$location Location object (may be null) - * - * @return boolean whether to continue (results in $location) - */ - - function onLocationFromLatLon($lat, $lon, $language, &$location) - { - // Make sure they're canonical - - $lat = $this->canonical($lat); - $lon = $this->canonical($lon); - - $loc = $this->getCache(array('lat' => $lat, - 'lon' => $lon)); - - if ($loc !== false) { - $location = $loc; - return false; - } - - try { - $geonames = $this->getGeonames('findNearbyPlaceName', - array('lat' => $lat, - 'lng' => $lon, - 'lang' => $language)); - } catch (Exception $e) { - $this->log(LOG_WARNING, "Error for coords $lat, $lon: " . $e->getMessage()); - return true; - } - - if (count($geonames) == 0) { - // no results - $this->setCache(array('lat' => $lat, - 'lon' => $lon), - null); - return true; - } - - $n = $geonames[0]; - - $parts = array(); - - $location = new Location(); - - $parts[] = (string)$n->name; - - if (!empty($n->adminName1)) { - $parts[] = (string)$n->adminName1; - } - - if (!empty($n->countryName)) { - $parts[] = (string)$n->countryName; - } - - $location->location_id = (string)$n->geonameId; - $location->location_ns = self::LOCATION_NS; - $location->lat = $this->canonical($n->lat); - $location->lon = $this->canonical($n->lng); - - $location->names[$language] = implode(', ', $parts); - - $this->setCache(array('lat' => $lat, - 'lon' => $lon), - $location); - - // Success! We handled it, so no further processing - - return false; - } - - /** - * Human-readable name for a location - * - * Given a location, we try to retrieve a human-readable name - * in the target language. - * - * @param Location $location Location to get the name for - * @param string $language ISO code for language to find name in - * @param string &$name Place to put the name - * - * @return boolean whether to continue - */ - - function onLocationNameLanguage($location, $language, &$name) - { - if ($location->location_ns != self::LOCATION_NS) { - // It's not one of our IDs... keep processing - return true; - } - - $id = $location->location_id; - - $n = $this->getCache(array('id' => $id, - 'language' => $language)); - - if ($n !== false) { - $name = $n; - return false; - } - - try { - $geonames = $this->getGeonames('hierarchy', - array('geonameId' => $id, - 'lang' => $language)); - } catch (Exception $e) { - $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); - return false; - } - - if (count($geonames) == 0) { - $this->setCache(array('id' => $id, - 'language' => $language), - null); - return false; - } - - $parts = array(); - - foreach ($geonames as $level) { - if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = (string)$level->name; - } - } - - $last = $geonames[count($geonames)-1]; - - if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = (string)$last->name; - } - - if (count($parts)) { - $name = implode(', ', array_reverse($parts)); - $this->setCache(array('id' => $id, - 'language' => $language), - $name); - } - - return false; - } - - /** - * Human-readable URL for a location - * - * Given a location, we try to retrieve a geonames.org URL. - * - * @param Location $location Location to get the url for - * @param string &$url Place to put the url - * - * @return boolean whether to continue - */ - - function onLocationUrl($location, &$url) - { - if ($location->location_ns != self::LOCATION_NS) { - // It's not one of our IDs... keep processing - return true; - } - - $url = 'http://www.geonames.org/' . $location->location_id; - - // it's been filled, so don't process further. - return false; - } - - /** - * Machine-readable name for a location - * - * Given a location, we try to retrieve a geonames.org URL. - * - * @param Location $location Location to get the url for - * @param string &$url Place to put the url - * - * @return boolean whether to continue - */ - - function onLocationRdfUrl($location, &$url) - { - if ($location->location_ns != self::LOCATION_NS) { - // It's not one of our IDs... keep processing - return true; - } - - $url = 'http://sws.geonames.org/' . $location->location_id . '/'; - - // it's been filled, so don't process further. - return false; - } - - function getCache($attrs) - { - $c = common_memcache(); - - if (empty($c)) { - return null; - } - - $key = $this->cacheKey($attrs); - - $value = $c->get($key); - - return $value; - } - - function setCache($attrs, $loc) - { - $c = common_memcache(); - - if (empty($c)) { - return null; - } - - $key = $this->cacheKey($attrs); - - $result = $c->set($key, $loc, 0, time() + $this->expiry); - - return $result; - } - - function cacheKey($attrs) - { - $key = 'geonames:' . - implode(',', array_keys($attrs)) . ':'. - common_keyize(implode(',', array_values($attrs))); - if ($this->cachePrefix) { - return $this->cachePrefix . ':' . $key; - } else { - return common_cache_key($key); - } - } - - function wsUrl($method, $params) - { - if (!empty($this->username)) { - $params['username'] = $this->username; - } - - if (!empty($this->token)) { - $params['token'] = $this->token; - } - - $str = http_build_query($params, null, '&'); - - return 'http://'.$this->host.'/'.$method.'?'.$str; - } - - function getGeonames($method, $params) - { - if ($this->lastTimeout && (time() - $this->lastTimeout < $this->timeoutWindow)) { - throw new Exception("skipping due to recent web service timeout"); - } - - $client = HTTPClient::start(); - $client->setConfig('connect_timeout', $this->timeout); - $client->setConfig('timeout', $this->timeout); - - try { - $result = $client->get($this->wsUrl($method, $params)); - } catch (Exception $e) { - common_log(LOG_ERR, __METHOD__ . ": " . $e->getMessage()); - $this->lastTimeout = time(); - throw $e; - } - - if (!$result->isOk()) { - throw new Exception("HTTP error code " . $result->getStatus()); - } - - $body = $result->getBody(); - - if (empty($body)) { - throw new Exception("Empty HTTP body in response"); - } - - // This will throw an exception if the XML is mal-formed - - $document = new SimpleXMLElement($body); - - // No children, usually no results - - $children = $document->children(); - - if (count($children) == 0) { - return array(); - } - - if (isset($document->status)) { - throw new Exception("Error #".$document->status['value']." ('".$document->status['message']."')"); - } - - // Array of elements, >0 elements - - return $document->geoname; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'Geonames', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:Geonames', - 'rawdescription' => - _m('Uses Geonames service to get human-readable '. - 'names for locations based on user-provided lat/long pairs.')); - return true; - } - - function canonical($coord) - { - $coord = rtrim($coord, "0"); - $coord = rtrim($coord, "."); - - return $coord; - } -} diff --git a/plugins/GoogleAnalytics/GoogleAnalyticsPlugin.php b/plugins/GoogleAnalytics/GoogleAnalyticsPlugin.php new file mode 100644 index 000000000..c646bf113 --- /dev/null +++ b/plugins/GoogleAnalytics/GoogleAnalyticsPlugin.php @@ -0,0 +1,85 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @copyright 2008 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to use Google Analytics + * + * This plugin will spoot out the correct JavaScript spell to invoke Google Analytics on a page. + * + * Note that Google Analytics is not compatible with the Franklin Street Statement; consider using + * Piwik (http://www.piwik.org/) instead! + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Event + */ + +class GoogleAnalyticsPlugin extends Plugin +{ + var $code = null; + + function __construct($code=null) + { + $this->code = $code; + parent::__construct(); + } + + function onEndShowScripts($action) + { + $js1 = 'var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");'. + 'document.write(unescape("%3Cscript src=\'" + gaJsHost + "google-analytics.com/ga.js\' type=\'text/javascript\'%3E%3C/script%3E"));'; + $js2 = sprintf('try{'. + 'var pageTracker = _gat._getTracker("%s");'. + 'pageTracker._trackPageview();'. + '} catch(err) {}', + $this->code); + $action->inlineScript($js1); + $action->inlineScript($js2); + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'GoogleAnalytics', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:GoogleAnalytics', + 'rawdescription' => + _m('Use Google Analytics'. + ' to track Web access.')); + return true; + } +} diff --git a/plugins/GoogleAnalyticsPlugin.php b/plugins/GoogleAnalyticsPlugin.php deleted file mode 100644 index c646bf113..000000000 --- a/plugins/GoogleAnalyticsPlugin.php +++ /dev/null @@ -1,85 +0,0 @@ -. - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @copyright 2008 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -/** - * Plugin to use Google Analytics - * - * This plugin will spoot out the correct JavaScript spell to invoke Google Analytics on a page. - * - * Note that Google Analytics is not compatible with the Franklin Street Statement; consider using - * Piwik (http://www.piwik.org/) instead! - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @see Event - */ - -class GoogleAnalyticsPlugin extends Plugin -{ - var $code = null; - - function __construct($code=null) - { - $this->code = $code; - parent::__construct(); - } - - function onEndShowScripts($action) - { - $js1 = 'var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");'. - 'document.write(unescape("%3Cscript src=\'" + gaJsHost + "google-analytics.com/ga.js\' type=\'text/javascript\'%3E%3C/script%3E"));'; - $js2 = sprintf('try{'. - 'var pageTracker = _gat._getTracker("%s");'. - 'pageTracker._trackPageview();'. - '} catch(err) {}', - $this->code); - $action->inlineScript($js1); - $action->inlineScript($js2); - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'GoogleAnalytics', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:GoogleAnalytics', - 'rawdescription' => - _m('Use Google Analytics'. - ' to track Web access.')); - return true; - } -} diff --git a/plugins/Linkback/LinkbackPlugin.php b/plugins/Linkback/LinkbackPlugin.php new file mode 100644 index 000000000..8e44beae1 --- /dev/null +++ b/plugins/Linkback/LinkbackPlugin.php @@ -0,0 +1,249 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once('Auth/Yadis/Yadis.php'); + +define('LINKBACKPLUGIN_VERSION', '0.1'); + +/** + * Plugin to do linkbacks for notices containing URLs + * + * After new notices are saved, we check their text for URLs. If there + * are URLs, we test each URL to see if it supports any + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Event + */ + +class LinkbackPlugin extends Plugin +{ + var $notice = null; + + function __construct() + { + parent::__construct(); + } + + function onHandleQueuedNotice($notice) + { + if ($notice->is_local == 1) { + // Try to avoid actually mucking with the + // notice content + $c = $notice->content; + $this->notice = $notice; + // Ignoring results + common_replace_urls_callback($c, + array($this, 'linkbackUrl')); + } + return true; + } + + function linkbackUrl($url) + { + common_log(LOG_DEBUG,"Attempting linkback for " . $url); + + $orig = $url; + $url = htmlspecialchars_decode($orig); + $scheme = parse_url($url, PHP_URL_SCHEME); + if (!in_array($scheme, array('http', 'https'))) { + return $orig; + } + + // XXX: Do a HEAD first to save some time/bandwidth + + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + + $result = $fetcher->get($url, + array('User-Agent: ' . $this->userAgent(), + 'Accept: application/html+xml,text/html')); + + if (!in_array($result->status, array('200', '206'))) { + return $orig; + } + + $pb = null; + $tb = null; + + if (array_key_exists('X-Pingback', $result->headers)) { + $pb = $result->headers['X-Pingback']; + } else if (preg_match('//', + $result->body, + $match)) { + $pb = $match[1]; + } + + if (!empty($pb)) { + $this->pingback($result->final_url, $pb); + } else { + $tb = $this->getTrackback($result->body, $result->final_url); + if (!empty($tb)) { + $this->trackback($result->final_url, $tb); + } + } + + return $orig; + } + + function pingback($url, $endpoint) + { + $args = array($this->notice->uri, $url); + + if (!extension_loaded('xmlrpc')) { + if (!dl('xmlrpc.so')) { + common_log(LOG_ERR, "Can't pingback; xmlrpc extension not available."); + return; + } + } + + $request = HTTPClient::start(); + try { + $response = $request->post($endpoint, + array('Content-Type: text/xml'), + xmlrpc_encode_request('pingback.ping', $args)); + $response = xmlrpc_decode($response->getBody()); + if (xmlrpc_is_fault($response)) { + common_log(LOG_WARNING, + "Pingback error for '$url' ($endpoint): ". + "$response[faultString] ($response[faultCode])"); + } else { + common_log(LOG_INFO, + "Pingback success for '$url' ($endpoint): ". + "'$response'"); + } + } catch (HTTP_Request2_Exception $e) { + common_log(LOG_WARNING, + "Pingback request failed for '$url' ($endpoint)"); + } + } + + // Largely cadged from trackback_cls.php by + // Ran Aroussi , GPL2 or any later version + // http://phptrackback.sourceforge.net/ + + function getTrackback($text, $url) + { + if (preg_match_all('/()/sm', $text, $match, PREG_SET_ORDER)) { + for ($i = 0; $i < count($match); $i++) { + if (preg_match('|dc:identifier="' . preg_quote($url) . '"|ms', $match[$i][1])) { + $rdf_array[] = trim($match[$i][1]); + } + } + + // Loop through the RDFs array and extract trackback URIs + + $tb_array = array(); // <- holds list of trackback URIs + + if (!empty($rdf_array)) { + + for ($i = 0; $i < count($rdf_array); $i++) { + if (preg_match('/trackback:ping="([^"]+)"/', $rdf_array[$i], $array)) { + $tb_array[] = trim($array[1]); + break; + } + } + } + + // Return Trackbacks + + if (empty($tb_array)) { + return null; + } else { + return $tb_array[0]; + } + } + + if (preg_match_all('/(]*?rel=[\'"]trackback[\'"][^>]*?>)/', $text, $match)) { + foreach ($match[1] as $atag) { + if (preg_match('/href=[\'"]([^\'"]*?)[\'"]/', $atag, $url)) { + return $url[1]; + } + } + } + + return null; + + } + + function trackback($url, $endpoint) + { + $profile = $this->notice->getProfile(); + + $args = array('title' => sprintf(_('%1$s\'s status on %2$s'), + $profile->nickname, + common_exact_date($this->notice->created)), + 'excerpt' => $this->notice->content, + 'url' => $this->notice->uri, + 'blog_name' => $profile->nickname); + + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + + $result = $fetcher->post($endpoint, + http_build_query($args), + array('User-Agent: ' . $this->userAgent())); + + if ($result->status != '200') { + common_log(LOG_WARNING, + "Trackback error for '$url' ($endpoint): ". + "$result->body"); + } else { + common_log(LOG_INFO, + "Trackback success for '$url' ($endpoint): ". + "'$result->body'"); + } + } + + function userAgent() + { + return 'LinkbackPlugin/'.LINKBACKPLUGIN_VERSION . + ' StatusNet/' . STATUSNET_VERSION; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Linkback', + 'version' => LINKBACKPLUGIN_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:Linkback', + 'rawdescription' => + _m('Notify blog authors when their posts have been linked in '. + 'microblog notices using '. + 'Pingback '. + 'or Trackback protocols.')); + return true; + } +} diff --git a/plugins/LinkbackPlugin.php b/plugins/LinkbackPlugin.php deleted file mode 100644 index 8e44beae1..000000000 --- a/plugins/LinkbackPlugin.php +++ /dev/null @@ -1,249 +0,0 @@ -. - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -require_once('Auth/Yadis/Yadis.php'); - -define('LINKBACKPLUGIN_VERSION', '0.1'); - -/** - * Plugin to do linkbacks for notices containing URLs - * - * After new notices are saved, we check their text for URLs. If there - * are URLs, we test each URL to see if it supports any - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @see Event - */ - -class LinkbackPlugin extends Plugin -{ - var $notice = null; - - function __construct() - { - parent::__construct(); - } - - function onHandleQueuedNotice($notice) - { - if ($notice->is_local == 1) { - // Try to avoid actually mucking with the - // notice content - $c = $notice->content; - $this->notice = $notice; - // Ignoring results - common_replace_urls_callback($c, - array($this, 'linkbackUrl')); - } - return true; - } - - function linkbackUrl($url) - { - common_log(LOG_DEBUG,"Attempting linkback for " . $url); - - $orig = $url; - $url = htmlspecialchars_decode($orig); - $scheme = parse_url($url, PHP_URL_SCHEME); - if (!in_array($scheme, array('http', 'https'))) { - return $orig; - } - - // XXX: Do a HEAD first to save some time/bandwidth - - $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); - - $result = $fetcher->get($url, - array('User-Agent: ' . $this->userAgent(), - 'Accept: application/html+xml,text/html')); - - if (!in_array($result->status, array('200', '206'))) { - return $orig; - } - - $pb = null; - $tb = null; - - if (array_key_exists('X-Pingback', $result->headers)) { - $pb = $result->headers['X-Pingback']; - } else if (preg_match('//', - $result->body, - $match)) { - $pb = $match[1]; - } - - if (!empty($pb)) { - $this->pingback($result->final_url, $pb); - } else { - $tb = $this->getTrackback($result->body, $result->final_url); - if (!empty($tb)) { - $this->trackback($result->final_url, $tb); - } - } - - return $orig; - } - - function pingback($url, $endpoint) - { - $args = array($this->notice->uri, $url); - - if (!extension_loaded('xmlrpc')) { - if (!dl('xmlrpc.so')) { - common_log(LOG_ERR, "Can't pingback; xmlrpc extension not available."); - return; - } - } - - $request = HTTPClient::start(); - try { - $response = $request->post($endpoint, - array('Content-Type: text/xml'), - xmlrpc_encode_request('pingback.ping', $args)); - $response = xmlrpc_decode($response->getBody()); - if (xmlrpc_is_fault($response)) { - common_log(LOG_WARNING, - "Pingback error for '$url' ($endpoint): ". - "$response[faultString] ($response[faultCode])"); - } else { - common_log(LOG_INFO, - "Pingback success for '$url' ($endpoint): ". - "'$response'"); - } - } catch (HTTP_Request2_Exception $e) { - common_log(LOG_WARNING, - "Pingback request failed for '$url' ($endpoint)"); - } - } - - // Largely cadged from trackback_cls.php by - // Ran Aroussi , GPL2 or any later version - // http://phptrackback.sourceforge.net/ - - function getTrackback($text, $url) - { - if (preg_match_all('/()/sm', $text, $match, PREG_SET_ORDER)) { - for ($i = 0; $i < count($match); $i++) { - if (preg_match('|dc:identifier="' . preg_quote($url) . '"|ms', $match[$i][1])) { - $rdf_array[] = trim($match[$i][1]); - } - } - - // Loop through the RDFs array and extract trackback URIs - - $tb_array = array(); // <- holds list of trackback URIs - - if (!empty($rdf_array)) { - - for ($i = 0; $i < count($rdf_array); $i++) { - if (preg_match('/trackback:ping="([^"]+)"/', $rdf_array[$i], $array)) { - $tb_array[] = trim($array[1]); - break; - } - } - } - - // Return Trackbacks - - if (empty($tb_array)) { - return null; - } else { - return $tb_array[0]; - } - } - - if (preg_match_all('/(]*?rel=[\'"]trackback[\'"][^>]*?>)/', $text, $match)) { - foreach ($match[1] as $atag) { - if (preg_match('/href=[\'"]([^\'"]*?)[\'"]/', $atag, $url)) { - return $url[1]; - } - } - } - - return null; - - } - - function trackback($url, $endpoint) - { - $profile = $this->notice->getProfile(); - - $args = array('title' => sprintf(_('%1$s\'s status on %2$s'), - $profile->nickname, - common_exact_date($this->notice->created)), - 'excerpt' => $this->notice->content, - 'url' => $this->notice->uri, - 'blog_name' => $profile->nickname); - - $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); - - $result = $fetcher->post($endpoint, - http_build_query($args), - array('User-Agent: ' . $this->userAgent())); - - if ($result->status != '200') { - common_log(LOG_WARNING, - "Trackback error for '$url' ($endpoint): ". - "$result->body"); - } else { - common_log(LOG_INFO, - "Trackback success for '$url' ($endpoint): ". - "'$result->body'"); - } - } - - function userAgent() - { - return 'LinkbackPlugin/'.LINKBACKPLUGIN_VERSION . - ' StatusNet/' . STATUSNET_VERSION; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'Linkback', - 'version' => LINKBACKPLUGIN_VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:Linkback', - 'rawdescription' => - _m('Notify blog authors when their posts have been linked in '. - 'microblog notices using '. - 'Pingback '. - 'or Trackback protocols.')); - return true; - } -} diff --git a/plugins/Memcache/MemcachePlugin.php b/plugins/Memcache/MemcachePlugin.php new file mode 100644 index 000000000..c3ca5c135 --- /dev/null +++ b/plugins/Memcache/MemcachePlugin.php @@ -0,0 +1,256 @@ +. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * A plugin to use memcache for the cache interface + * + * This used to be encoded as config-variable options in the core code; + * it's now broken out to a separate plugin. The same interface can be + * implemented by other plugins. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class MemcachePlugin extends Plugin +{ + static $cacheInitialized = false; + + private $_conn = null; + public $servers = array('127.0.0.1;11211'); + + public $compressThreshold = 20480; + public $compressMinSaving = 0.2; + + public $persistent = null; + + public $defaultExpiry = 86400; // 24h + + /** + * Initialize the plugin + * + * Note that onStartCacheGet() may have been called before this! + * + * @return boolean flag value + */ + + function onInitializePlugin() + { + 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->_ensureConn(); + self::$cacheInitialized = true; + return true; + } + + /** + * Get a value associated with a key + * + * The value should have been set previously. + * + * @param string &$key in; Lookup key + * @param mixed &$value out; value associated with key + * + * @return boolean hook success + */ + + function onStartCacheGet(&$key, &$value) + { + $this->_ensureConn(); + $value = $this->_conn->get($key); + Event::handle('EndCacheGet', array($key, &$value)); + return false; + } + + /** + * Associate a value with a key + * + * @param string &$key in; Key to use for lookups + * @param mixed &$value in; Value to associate + * @param integer &$flag in; Flag empty or Cache::COMPRESSED + * @param integer &$expiry in; Expiry (passed through to Memcache) + * @param boolean &$success out; Whether the set was successful + * + * @return boolean hook success + */ + + function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) + { + $this->_ensureConn(); + if ($expiry === null) { + $expiry = $this->defaultExpiry; + } + $success = $this->_conn->set($key, $value, $this->flag(intval($flag)), $expiry); + Event::handle('EndCacheSet', array($key, $value, $flag, + $expiry)); + 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 + * + * @param string &$key in; Key to lookup + * @param boolean &$success out; whether it worked + * + * @return boolean hook success + */ + + function onStartCacheDelete(&$key, &$success) + { + $this->_ensureConn(); + $success = $this->_conn->delete($key); + Event::handle('EndCacheDelete', array($key)); + return false; + } + + function onStartCacheReconnect(&$success) + { + if (empty($this->_conn)) { + // nothing to do + return true; + } + if ($this->persistent) { + common_log(LOG_ERR, "Cannot close persistent memcached connection"); + $success = false; + } else { + common_log(LOG_INFO, "Closing memcached connection"); + $success = $this->_conn->close(); + $this->_conn = null; + } + return false; + } + + /** + * Ensure that a connection exists + * + * Checks the instance $_conn variable and connects + * if it is empty. + * + * @return void + */ + + private function _ensureConn() + { + if (empty($this->_conn)) { + $this->_conn = new Memcache(); + + if (is_array($this->servers)) { + $servers = $this->servers; + } else { + $servers = array($this->servers); + } + foreach ($servers as $server) { + if (strpos($server, ';') !== false) { + list($host, $port) = explode(';', $server); + } else { + $host = $server; + $port = 11211; + } + + $this->_conn->addServer($host, $port, $this->persistent); + } + + // Compress items stored in the cache if they're over threshold in size + // (default 2KiB) and the compression would save more than min savings + // ratio (default 0.2). + + // Allows the cache to store objects larger than 1MB (if they + // compress to less than 1MB), and improves cache memory efficiency. + + $this->_conn->setCompressThreshold($this->compressThreshold, + $this->compressMinSaving); + } + } + + /** + * Translate general flags to Memcached-specific flags + * @param int $flag + * @return int + */ + protected function flag($flag) + { + $out = 0; + if ($flag & Cache::COMPRESSED == Cache::COMPRESSED) { + $out |= MEMCACHE_COMPRESSED; + } + return $out; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Memcache', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou, Craig Andrews', + 'homepage' => 'http://status.net/wiki/Plugin:Memcache', + 'rawdescription' => + _m('Use Memcached to cache query results.')); + return true; + } +} + diff --git a/plugins/MemcachePlugin.php b/plugins/MemcachePlugin.php deleted file mode 100644 index c3ca5c135..000000000 --- a/plugins/MemcachePlugin.php +++ /dev/null @@ -1,256 +0,0 @@ -. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - // This check helps protect against security problems; - // your code file can't be executed directly from the web. - exit(1); -} - -/** - * A plugin to use memcache for the cache interface - * - * This used to be encoded as config-variable options in the core code; - * it's now broken out to a separate plugin. The same interface can be - * implemented by other plugins. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -class MemcachePlugin extends Plugin -{ - static $cacheInitialized = false; - - private $_conn = null; - public $servers = array('127.0.0.1;11211'); - - public $compressThreshold = 20480; - public $compressMinSaving = 0.2; - - public $persistent = null; - - public $defaultExpiry = 86400; // 24h - - /** - * Initialize the plugin - * - * Note that onStartCacheGet() may have been called before this! - * - * @return boolean flag value - */ - - function onInitializePlugin() - { - 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->_ensureConn(); - self::$cacheInitialized = true; - return true; - } - - /** - * Get a value associated with a key - * - * The value should have been set previously. - * - * @param string &$key in; Lookup key - * @param mixed &$value out; value associated with key - * - * @return boolean hook success - */ - - function onStartCacheGet(&$key, &$value) - { - $this->_ensureConn(); - $value = $this->_conn->get($key); - Event::handle('EndCacheGet', array($key, &$value)); - return false; - } - - /** - * Associate a value with a key - * - * @param string &$key in; Key to use for lookups - * @param mixed &$value in; Value to associate - * @param integer &$flag in; Flag empty or Cache::COMPRESSED - * @param integer &$expiry in; Expiry (passed through to Memcache) - * @param boolean &$success out; Whether the set was successful - * - * @return boolean hook success - */ - - function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) - { - $this->_ensureConn(); - if ($expiry === null) { - $expiry = $this->defaultExpiry; - } - $success = $this->_conn->set($key, $value, $this->flag(intval($flag)), $expiry); - Event::handle('EndCacheSet', array($key, $value, $flag, - $expiry)); - 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 - * - * @param string &$key in; Key to lookup - * @param boolean &$success out; whether it worked - * - * @return boolean hook success - */ - - function onStartCacheDelete(&$key, &$success) - { - $this->_ensureConn(); - $success = $this->_conn->delete($key); - Event::handle('EndCacheDelete', array($key)); - return false; - } - - function onStartCacheReconnect(&$success) - { - if (empty($this->_conn)) { - // nothing to do - return true; - } - if ($this->persistent) { - common_log(LOG_ERR, "Cannot close persistent memcached connection"); - $success = false; - } else { - common_log(LOG_INFO, "Closing memcached connection"); - $success = $this->_conn->close(); - $this->_conn = null; - } - return false; - } - - /** - * Ensure that a connection exists - * - * Checks the instance $_conn variable and connects - * if it is empty. - * - * @return void - */ - - private function _ensureConn() - { - if (empty($this->_conn)) { - $this->_conn = new Memcache(); - - if (is_array($this->servers)) { - $servers = $this->servers; - } else { - $servers = array($this->servers); - } - foreach ($servers as $server) { - if (strpos($server, ';') !== false) { - list($host, $port) = explode(';', $server); - } else { - $host = $server; - $port = 11211; - } - - $this->_conn->addServer($host, $port, $this->persistent); - } - - // Compress items stored in the cache if they're over threshold in size - // (default 2KiB) and the compression would save more than min savings - // ratio (default 0.2). - - // Allows the cache to store objects larger than 1MB (if they - // compress to less than 1MB), and improves cache memory efficiency. - - $this->_conn->setCompressThreshold($this->compressThreshold, - $this->compressMinSaving); - } - } - - /** - * Translate general flags to Memcached-specific flags - * @param int $flag - * @return int - */ - protected function flag($flag) - { - $out = 0; - if ($flag & Cache::COMPRESSED == Cache::COMPRESSED) { - $out |= MEMCACHE_COMPRESSED; - } - return $out; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'Memcache', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou, Craig Andrews', - 'homepage' => 'http://status.net/wiki/Plugin:Memcache', - 'rawdescription' => - _m('Use Memcached to cache query results.')); - return true; - } -} - diff --git a/plugins/Memcached/MemcachedPlugin.php b/plugins/Memcached/MemcachedPlugin.php new file mode 100644 index 000000000..77b989b95 --- /dev/null +++ b/plugins/Memcached/MemcachedPlugin.php @@ -0,0 +1,227 @@ +. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @author Craig Andrews + * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * A plugin to use memcached for the cache interface + * + * This used to be encoded as config-variable options in the core code; + * it's now broken out to a separate plugin. The same interface can be + * implemented by other plugins. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @author Craig Andrews + * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class MemcachedPlugin extends Plugin +{ + static $cacheInitialized = false; + + private $_conn = null; + public $servers = array('127.0.0.1;11211'); + + public $defaultExpiry = 86400; // 24h + + /** + * Initialize the plugin + * + * Note that onStartCacheGet() may have been called before this! + * + * @return boolean flag value + */ + + function onInitializePlugin() + { + $this->_ensureConn(); + self::$cacheInitialized = true; + return true; + } + + /** + * Get a value associated with a key + * + * The value should have been set previously. + * + * @param string &$key in; Lookup key + * @param mixed &$value out; value associated with key + * + * @return boolean hook success + */ + + function onStartCacheGet(&$key, &$value) + { + $this->_ensureConn(); + $value = $this->_conn->get($key); + Event::handle('EndCacheGet', array($key, &$value)); + return false; + } + + /** + * Associate a value with a key + * + * @param string &$key in; Key to use for lookups + * @param mixed &$value in; Value to associate + * @param integer &$flag in; Flag empty or Cache::COMPRESSED + * @param integer &$expiry in; Expiry (passed through to Memcache) + * @param boolean &$success out; Whether the set was successful + * + * @return boolean hook success + */ + + function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) + { + $this->_ensureConn(); + if ($expiry === null) { + $expiry = $this->defaultExpiry; + } + $success = $this->_conn->set($key, $value, $expiry); + Event::handle('EndCacheSet', array($key, $value, $flag, + $expiry)); + 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 + * + * @param string &$key in; Key to lookup + * @param boolean &$success out; whether it worked + * + * @return boolean hook success + */ + + function onStartCacheDelete(&$key, &$success) + { + $this->_ensureConn(); + $success = $this->_conn->delete($key); + Event::handle('EndCacheDelete', array($key)); + return false; + } + + function onStartCacheReconnect(&$success) + { + // nothing to do + return true; + } + + /** + * Ensure that a connection exists + * + * Checks the instance $_conn variable and connects + * if it is empty. + * + * @return void + */ + + private function _ensureConn() + { + if (empty($this->_conn)) { + $this->_conn = new Memcached(common_config('site', 'nickname')); + + if (!count($this->_conn->getServerList())) { + if (is_array($this->servers)) { + $servers = $this->servers; + } else { + $servers = array($this->servers); + } + foreach ($servers as $server) { + if (strpos($server, ';') !== false) { + list($host, $port) = explode(';', $server); + } else { + $host = $server; + $port = 11211; + } + + $this->_conn->addServer($host, $port); + } + + // Compress items stored in the cache. + + // Allows the cache to store objects larger than 1MB (if they + // compress to less than 1MB), and improves cache memory efficiency. + + $this->_conn->setOption(Memcached::OPT_COMPRESSION, true); + } + } + } + + /** + * Translate general flags to Memcached-specific flags + * @param int $flag + * @return int + */ + protected function flag($flag) + { + //no flags are presently supported + return $flag; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Memcached', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou, Craig Andrews', + 'homepage' => 'http://status.net/wiki/Plugin:Memcached', + 'rawdescription' => + _m('Use Memcached to cache query results.')); + return true; + } +} + diff --git a/plugins/MemcachedPlugin.php b/plugins/MemcachedPlugin.php deleted file mode 100644 index 77b989b95..000000000 --- a/plugins/MemcachedPlugin.php +++ /dev/null @@ -1,227 +0,0 @@ -. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @author Craig Andrews - * @copyright 2009 StatusNet, Inc. - * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - // This check helps protect against security problems; - // your code file can't be executed directly from the web. - exit(1); -} - -/** - * A plugin to use memcached for the cache interface - * - * This used to be encoded as config-variable options in the core code; - * it's now broken out to a separate plugin. The same interface can be - * implemented by other plugins. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @author Craig Andrews - * @copyright 2009 StatusNet, Inc. - * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -class MemcachedPlugin extends Plugin -{ - static $cacheInitialized = false; - - private $_conn = null; - public $servers = array('127.0.0.1;11211'); - - public $defaultExpiry = 86400; // 24h - - /** - * Initialize the plugin - * - * Note that onStartCacheGet() may have been called before this! - * - * @return boolean flag value - */ - - function onInitializePlugin() - { - $this->_ensureConn(); - self::$cacheInitialized = true; - return true; - } - - /** - * Get a value associated with a key - * - * The value should have been set previously. - * - * @param string &$key in; Lookup key - * @param mixed &$value out; value associated with key - * - * @return boolean hook success - */ - - function onStartCacheGet(&$key, &$value) - { - $this->_ensureConn(); - $value = $this->_conn->get($key); - Event::handle('EndCacheGet', array($key, &$value)); - return false; - } - - /** - * Associate a value with a key - * - * @param string &$key in; Key to use for lookups - * @param mixed &$value in; Value to associate - * @param integer &$flag in; Flag empty or Cache::COMPRESSED - * @param integer &$expiry in; Expiry (passed through to Memcache) - * @param boolean &$success out; Whether the set was successful - * - * @return boolean hook success - */ - - function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) - { - $this->_ensureConn(); - if ($expiry === null) { - $expiry = $this->defaultExpiry; - } - $success = $this->_conn->set($key, $value, $expiry); - Event::handle('EndCacheSet', array($key, $value, $flag, - $expiry)); - 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 - * - * @param string &$key in; Key to lookup - * @param boolean &$success out; whether it worked - * - * @return boolean hook success - */ - - function onStartCacheDelete(&$key, &$success) - { - $this->_ensureConn(); - $success = $this->_conn->delete($key); - Event::handle('EndCacheDelete', array($key)); - return false; - } - - function onStartCacheReconnect(&$success) - { - // nothing to do - return true; - } - - /** - * Ensure that a connection exists - * - * Checks the instance $_conn variable and connects - * if it is empty. - * - * @return void - */ - - private function _ensureConn() - { - if (empty($this->_conn)) { - $this->_conn = new Memcached(common_config('site', 'nickname')); - - if (!count($this->_conn->getServerList())) { - if (is_array($this->servers)) { - $servers = $this->servers; - } else { - $servers = array($this->servers); - } - foreach ($servers as $server) { - if (strpos($server, ';') !== false) { - list($host, $port) = explode(';', $server); - } else { - $host = $server; - $port = 11211; - } - - $this->_conn->addServer($host, $port); - } - - // Compress items stored in the cache. - - // Allows the cache to store objects larger than 1MB (if they - // compress to less than 1MB), and improves cache memory efficiency. - - $this->_conn->setOption(Memcached::OPT_COMPRESSION, true); - } - } - } - - /** - * Translate general flags to Memcached-specific flags - * @param int $flag - * @return int - */ - protected function flag($flag) - { - //no flags are presently supported - return $flag; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'Memcached', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou, Craig Andrews', - 'homepage' => 'http://status.net/wiki/Plugin:Memcached', - 'rawdescription' => - _m('Use Memcached to cache query results.')); - return true; - } -} - diff --git a/plugins/PiwikAnalytics/PiwikAnalyticsPlugin.php b/plugins/PiwikAnalytics/PiwikAnalyticsPlugin.php new file mode 100644 index 000000000..b353d7255 --- /dev/null +++ b/plugins/PiwikAnalytics/PiwikAnalyticsPlugin.php @@ -0,0 +1,112 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @author Tobias Diekershoff + * @copyright 2008 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to use Piwik Analytics (based on the Google Analytics plugin by Evan) + * + * This plugin will spoot out the correct JavaScript spell to invoke + * Piwik Analytics on a page. + * + * To use this plugin add the following to your config.php + * + * addPlugin('PiwikAnalytics', array('piwikroot' => 'example.com/piwik/', + * 'piwikId' => 'id')); + * + * Replace 'example.com/piwik/' with the URL to your Piwik installation and + * make sure you don't forget the final /. + * Replace 'id' with the ID your statusnet installation has in your Piwik + * analytics setup - for example '8'. + * + */ + +class PiwikAnalyticsPlugin extends Plugin +{ + /** the base of your Piwik installation */ + public $piwikroot = null; + /** the Piwik Id of your statusnet installation */ + public $piwikId = null; + + /** + * constructor + * + * @param string $root Piwik root URL + * @param string $id Piwik ID of this app + */ + + function __construct($root=null, $id=null) + { + $this->piwikroot = $root; + $this->piwikId = $id; + parent::__construct(); + } + + /** + * Called when all scripts have been shown + * + * @param Action $action Current action + * + * @return boolean ignored + */ + + function onEndShowScripts($action) + { + $piwikCode1 = <<piwikroot}" : "http://{$this->piwikroot}"); +document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E")); +ENDOFPIWIK; + $piwikCode2 = <<piwikId}); + piwikTracker.trackPageView(); + piwikTracker.enableLinkTracking(); +} catch( err ) {} +ENDOFPIWIK; + + $action->inlineScript($piwikCode1); + $action->inlineScript($piwikCode2); + return true; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'PiwikAnalytics', + 'version' => STATUSNET_VERSION, + 'author' => 'Tobias Diekershoff, Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:Piwik', + 'rawdescription' => + _m('Use Piwik Open Source Web analytics software.')); + return true; + } + +} diff --git a/plugins/PiwikAnalyticsPlugin.php b/plugins/PiwikAnalyticsPlugin.php deleted file mode 100644 index b353d7255..000000000 --- a/plugins/PiwikAnalyticsPlugin.php +++ /dev/null @@ -1,112 +0,0 @@ -. - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @author Tobias Diekershoff - * @copyright 2008 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -/** - * Plugin to use Piwik Analytics (based on the Google Analytics plugin by Evan) - * - * This plugin will spoot out the correct JavaScript spell to invoke - * Piwik Analytics on a page. - * - * To use this plugin add the following to your config.php - * - * addPlugin('PiwikAnalytics', array('piwikroot' => 'example.com/piwik/', - * 'piwikId' => 'id')); - * - * Replace 'example.com/piwik/' with the URL to your Piwik installation and - * make sure you don't forget the final /. - * Replace 'id' with the ID your statusnet installation has in your Piwik - * analytics setup - for example '8'. - * - */ - -class PiwikAnalyticsPlugin extends Plugin -{ - /** the base of your Piwik installation */ - public $piwikroot = null; - /** the Piwik Id of your statusnet installation */ - public $piwikId = null; - - /** - * constructor - * - * @param string $root Piwik root URL - * @param string $id Piwik ID of this app - */ - - function __construct($root=null, $id=null) - { - $this->piwikroot = $root; - $this->piwikId = $id; - parent::__construct(); - } - - /** - * Called when all scripts have been shown - * - * @param Action $action Current action - * - * @return boolean ignored - */ - - function onEndShowScripts($action) - { - $piwikCode1 = <<piwikroot}" : "http://{$this->piwikroot}"); -document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E")); -ENDOFPIWIK; - $piwikCode2 = <<piwikId}); - piwikTracker.trackPageView(); - piwikTracker.enableLinkTracking(); -} catch( err ) {} -ENDOFPIWIK; - - $action->inlineScript($piwikCode1); - $action->inlineScript($piwikCode2); - return true; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'PiwikAnalytics', - 'version' => STATUSNET_VERSION, - 'author' => 'Tobias Diekershoff, Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:Piwik', - 'rawdescription' => - _m('Use Piwik Open Source Web analytics software.')); - return true; - } - -} diff --git a/plugins/Spotify/SpotifyPlugin.php b/plugins/Spotify/SpotifyPlugin.php new file mode 100644 index 000000000..e7a5a5382 --- /dev/null +++ b/plugins/Spotify/SpotifyPlugin.php @@ -0,0 +1,113 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Nick Holliday + * @copyright Nick Holliday + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Event + */ +if (!defined('STATUSNET')) { + exit(1); +} +define('SPOTIFYPLUGIN_VERSION', '0.1'); + +/** + * Plugin to create pretty Spotify URLs + * + * The Spotify API is called before the notice is saved to gather artist and track information. + * + * @category Plugin + * @package StatusNet + * @author Nick Holliday + * @copyright Nick Holliday + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Event + */ + +class SpotifyPlugin extends Plugin +{ + + function __construct() + { + parent::__construct(); + } + + function onStartNoticeSave($notice) + { + $notice->rendered = preg_replace_callback('/spotify:[a-z]{5,6}:[a-z0-9]{22}/i', + "renderSpotifyURILink", + $notice->rendered); + + $notice->rendered = preg_replace_callback('/http:\/\/open.spotify.com\/[a-z]{5,6}\/[a-z0-9]{22}<\/a>/i', + "renderSpotifyHTTPLink", + $notice->rendered); + + return true; + } + + function userAgent() + { + return 'SpotifyPlugin/'.SPOTIFYPLUGIN_VERSION . + ' StatusNet/' . STATUSNET_VERSION; + } +} + +function doSpotifyLookup($uri, $isArtist) +{ + $request = HTTPClient::start(); + $response = $request->get('http://ws.spotify.com/lookup/1/?uri=' . $uri); + if ($response->isOk()) { + $xml = simplexml_load_string($response->getBody()); + + if($isArtist) + return $xml->name; + else + return $xml->artist->name . ' - ' . $xml->name; + } +} + +function renderSpotifyURILink($match) +{ + $isArtist = false; + if(preg_match('/artist/', $match[0]) > 0) $isArtist = true; + + $name = doSpotifyLookup($match[0], $isArtist); + return "" . $name . ""; +} + +function renderSpotifyHTTPLink($match) +{ + $match[0] = preg_replace('/http:\/\/open.spotify.com\//i', 'spotify:', $match[0]); + $match[0] = preg_replace('/<\/a>/', '', $match[0]); + $match[0] = preg_replace('/\//', ':', $match[0]); + + $isArtist = false; + if(preg_match('/artist/', $match[0]) > 0) $isArtist = true; + + $name = doSpotifyLookup($match[0], $isArtist); + return "" . $name . ""; +} diff --git a/plugins/SpotifyPlugin.php b/plugins/SpotifyPlugin.php deleted file mode 100644 index e7a5a5382..000000000 --- a/plugins/SpotifyPlugin.php +++ /dev/null @@ -1,113 +0,0 @@ -. - * - * @category Plugin - * @package StatusNet - * @author Nick Holliday - * @copyright Nick Holliday - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @see Event - */ -if (!defined('STATUSNET')) { - exit(1); -} -define('SPOTIFYPLUGIN_VERSION', '0.1'); - -/** - * Plugin to create pretty Spotify URLs - * - * The Spotify API is called before the notice is saved to gather artist and track information. - * - * @category Plugin - * @package StatusNet - * @author Nick Holliday - * @copyright Nick Holliday - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @see Event - */ - -class SpotifyPlugin extends Plugin -{ - - function __construct() - { - parent::__construct(); - } - - function onStartNoticeSave($notice) - { - $notice->rendered = preg_replace_callback('/spotify:[a-z]{5,6}:[a-z0-9]{22}/i', - "renderSpotifyURILink", - $notice->rendered); - - $notice->rendered = preg_replace_callback('/http:\/\/open.spotify.com\/[a-z]{5,6}\/[a-z0-9]{22}<\/a>/i', - "renderSpotifyHTTPLink", - $notice->rendered); - - return true; - } - - function userAgent() - { - return 'SpotifyPlugin/'.SPOTIFYPLUGIN_VERSION . - ' StatusNet/' . STATUSNET_VERSION; - } -} - -function doSpotifyLookup($uri, $isArtist) -{ - $request = HTTPClient::start(); - $response = $request->get('http://ws.spotify.com/lookup/1/?uri=' . $uri); - if ($response->isOk()) { - $xml = simplexml_load_string($response->getBody()); - - if($isArtist) - return $xml->name; - else - return $xml->artist->name . ' - ' . $xml->name; - } -} - -function renderSpotifyURILink($match) -{ - $isArtist = false; - if(preg_match('/artist/', $match[0]) > 0) $isArtist = true; - - $name = doSpotifyLookup($match[0], $isArtist); - return "" . $name . ""; -} - -function renderSpotifyHTTPLink($match) -{ - $match[0] = preg_replace('/http:\/\/open.spotify.com\//i', 'spotify:', $match[0]); - $match[0] = preg_replace('/<\/a>/', '', $match[0]); - $match[0] = preg_replace('/\//', ':', $match[0]); - - $isArtist = false; - if(preg_match('/artist/', $match[0]) > 0) $isArtist = true; - - $name = doSpotifyLookup($match[0], $isArtist); - return "" . $name . ""; -} diff --git a/plugins/SubscriptionThrottle/SubscriptionThrottlePlugin.php b/plugins/SubscriptionThrottle/SubscriptionThrottlePlugin.php new file mode 100644 index 000000000..114113360 --- /dev/null +++ b/plugins/SubscriptionThrottle/SubscriptionThrottlePlugin.php @@ -0,0 +1,175 @@ +. + * + * @category Throttle + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * Subscription throttle + * + * @category Throttle + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class SubscriptionThrottlePlugin extends Plugin +{ + public $subLimits = array(86400 => 100, + 3600 => 50); + + public $groupLimits = array(86400 => 50, + 3600 => 25); + + /** + * Filter subscriptions to see if they're coming too fast. + * + * @param User $user The user subscribing + * @param User $other The user being subscribed to + * + * @return boolean hook value + */ + + function onStartSubscribe($user, $other) + { + foreach ($this->subLimits as $seconds => $limit) { + $sub = $this->_getNthSub($user, $limit); + + if (!empty($sub)) { + $subtime = strtotime($sub->created); + $now = time(); + if ($now - $subtime < $seconds) { + throw new Exception(_("Too many subscriptions. Take a break and try again later.")); + } + } + } + + return true; + } + + /** + * Filter group joins to see if they're coming too fast. + * + * @param Group $group The group being joined + * @param User $user The user joining + * + * @return boolean hook value + */ + + function onStartJoinGroup($group, $user) + { + foreach ($this->groupLimits as $seconds => $limit) { + $mem = $this->_getNthMem($user, $limit); + if (!empty($mem)) { + + $jointime = strtotime($mem->created); + $now = time(); + if ($now - $jointime < $seconds) { + throw new Exception(_("Too many memberships. Take a break and try again later.")); + } + } + } + + return true; + } + + /** + * Get the Nth most recent subscription for this user + * + * @param User $user The user to get subscriptions for + * @param integer $n How far to count back + * + * @return Subscription a subscription or null + */ + + private function _getNthSub($user, $n) + { + $sub = new Subscription(); + + $sub->subscriber = $user->id; + $sub->orderBy('created DESC'); + $sub->limit($n - 1, 1); + + if ($sub->find(true)) { + return $sub; + } else { + return null; + } + } + + /** + * Get the Nth most recent group membership for this user + * + * @param User $user The user to get memberships for + * @param integer $n How far to count back + * + * @return Group_member a membership or null + */ + + private function _getNthMem($user, $n) + { + $mem = new Group_member(); + + $mem->profile_id = $user->id; + $mem->orderBy('created DESC'); + $mem->limit($n - 1, 1); + + if ($mem->find(true)) { + return $mem; + } else { + return null; + } + } + + /** + * Return plugin version data for display + * + * @param array &$versions Array of version arrays + * + * @return boolean hook value + */ + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'SubscriptionThrottle', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:SubscriptionThrottle', + 'rawdescription' => + _m('Configurable limits for subscriptions and group memberships.')); + return true; + } +} + diff --git a/plugins/SubscriptionThrottlePlugin.php b/plugins/SubscriptionThrottlePlugin.php deleted file mode 100644 index 114113360..000000000 --- a/plugins/SubscriptionThrottlePlugin.php +++ /dev/null @@ -1,175 +0,0 @@ -. - * - * @category Throttle - * @package StatusNet - * @author Evan Prodromou - * @copyright 2010 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - // This check helps protect against security problems; - // your code file can't be executed directly from the web. - exit(1); -} - -/** - * Subscription throttle - * - * @category Throttle - * @package StatusNet - * @author Evan Prodromou - * @copyright 2010 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 - * @link http://status.net/ - */ - -class SubscriptionThrottlePlugin extends Plugin -{ - public $subLimits = array(86400 => 100, - 3600 => 50); - - public $groupLimits = array(86400 => 50, - 3600 => 25); - - /** - * Filter subscriptions to see if they're coming too fast. - * - * @param User $user The user subscribing - * @param User $other The user being subscribed to - * - * @return boolean hook value - */ - - function onStartSubscribe($user, $other) - { - foreach ($this->subLimits as $seconds => $limit) { - $sub = $this->_getNthSub($user, $limit); - - if (!empty($sub)) { - $subtime = strtotime($sub->created); - $now = time(); - if ($now - $subtime < $seconds) { - throw new Exception(_("Too many subscriptions. Take a break and try again later.")); - } - } - } - - return true; - } - - /** - * Filter group joins to see if they're coming too fast. - * - * @param Group $group The group being joined - * @param User $user The user joining - * - * @return boolean hook value - */ - - function onStartJoinGroup($group, $user) - { - foreach ($this->groupLimits as $seconds => $limit) { - $mem = $this->_getNthMem($user, $limit); - if (!empty($mem)) { - - $jointime = strtotime($mem->created); - $now = time(); - if ($now - $jointime < $seconds) { - throw new Exception(_("Too many memberships. Take a break and try again later.")); - } - } - } - - return true; - } - - /** - * Get the Nth most recent subscription for this user - * - * @param User $user The user to get subscriptions for - * @param integer $n How far to count back - * - * @return Subscription a subscription or null - */ - - private function _getNthSub($user, $n) - { - $sub = new Subscription(); - - $sub->subscriber = $user->id; - $sub->orderBy('created DESC'); - $sub->limit($n - 1, 1); - - if ($sub->find(true)) { - return $sub; - } else { - return null; - } - } - - /** - * Get the Nth most recent group membership for this user - * - * @param User $user The user to get memberships for - * @param integer $n How far to count back - * - * @return Group_member a membership or null - */ - - private function _getNthMem($user, $n) - { - $mem = new Group_member(); - - $mem->profile_id = $user->id; - $mem->orderBy('created DESC'); - $mem->limit($n - 1, 1); - - if ($mem->find(true)) { - return $mem; - } else { - return null; - } - } - - /** - * Return plugin version data for display - * - * @param array &$versions Array of version arrays - * - * @return boolean hook value - */ - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'SubscriptionThrottle', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:SubscriptionThrottle', - 'rawdescription' => - _m('Configurable limits for subscriptions and group memberships.')); - return true; - } -} - diff --git a/plugins/Template/TemplatePlugin.php b/plugins/Template/TemplatePlugin.php new file mode 100644 index 000000000..80625c5b7 --- /dev/null +++ b/plugins/Template/TemplatePlugin.php @@ -0,0 +1,357 @@ + + * @copyright 2009 Megapump, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://megapump.com/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +define('TEMPLATEPLUGIN_VERSION', '0.1'); + +class TemplatePlugin extends Plugin { + + var $blocks = array(); + + function __construct() { + parent::__construct(); + } + + // capture the RouterInitialized event + // and connect a new API method + // for updating the template + function onRouterInitialized( $m ) { + $m->connect( 'template/update', array( + 'action' => 'template', + )); + } + + // <%styles%> + // <%scripts%> + // <%search%> + // <%feeds%> + // <%description%> + // <%head%> + function onStartShowHead( &$act ) { + $this->clear_xmlWriter($act); + $act->extraHead(); + $this->blocks['head'] = $act->xw->flush(); + $act->showStylesheets(); + $this->blocks['styles'] = $act->xw->flush(); + $act->showScripts(); + $this->blocks['scripts'] = $act->xw->flush(); + $act->showFeeds(); + $this->blocks['feeds'] = $act->xw->flush(); + $act->showOpenSearch(); + $this->blocks['search'] = $act->xw->flush(); + $act->showDescription(); + $this->blocks['description'] = $act->xw->flush(); + return false; + } + + // <%bodytext%> + function onStartShowContentBlock( &$act ) { + $this->clear_xmlWriter($act); + return true; + } + function onEndShowContentBlock( &$act ) { + $this->blocks['bodytext'] = $act->xw->flush(); + } + + // <%localnav%> + function onStartShowLocalNavBlock( &$act ) { + $this->clear_xmlWriter($act); + return true; + } + function onEndShowLocalNavBlock( &$act ) { + $this->blocks['localnav'] = $act->xw->flush(); + } + + // <%export%> + function onStartShowExportData( &$act ) { + $this->clear_xmlWriter($act); + return true; + } + function onEndShowExportData( &$act ) { + $this->blocks['export'] = $act->xw->flush(); + } + + // <%subscriptions%> + // <%subscribers%> + // <%groups%> + // <%statistics%> + // <%cloud%> + // <%groupmembers%> + // <%groupstatistics%> + // <%groupcloud%> + // <%popular%> + // <%groupsbyposts%> + // <%featuredusers%> + // <%groupsbymembers%> + function onStartShowSections( &$act ) { + global $action; + $this->clear_xmlWriter($act); + switch ($action) { + case "showstream": + $act->showSubscriptions(); + $this->blocks['subscriptions'] = $act->xw->flush(); + $act->showSubscribers(); + $this->blocks['subscribers'] = $act->xw->flush(); + $act->showGroups(); + $this->blocks['groups'] = $act->xw->flush(); + $act->showStatistics(); + $this->blocks['statistics'] = $act->xw->flush(); + $cloud = new PersonalTagCloudSection($act, $act->user); + $cloud->show(); + $this->blocks['cloud'] = $act->xw->flush(); + break; + case "showgroup": + $act->showMembers(); + $this->blocks['groupmembers'] = $act->xw->flush(); + $act->showStatistics(); + $this->blocks['groupstatistics'] = $act->xw->flush(); + $cloud = new GroupTagCloudSection($act, $act->group); + $cloud->show(); + $this->blocks['groupcloud'] = $act->xw->flush(); + break; + case "public": + $pop = new PopularNoticeSection($act); + $pop->show(); + $this->blocks['popular'] = $act->xw->flush(); + $gbp = new GroupsByPostsSection($act); + $gbp->show(); + $this->blocks['groupsbyposts'] = $act->xw->flush(); + $feat = new FeaturedUsersSection($act); + $feat->show(); + $this->blocks['featuredusers'] = $act->xw->flush(); + break; + case "groups": + $gbp = new GroupsByPostsSection($act); + $gbp->show(); + $this->blocks['groupsbyposts'] = $act->xw->flush(); + $gbm = new GroupsByMembersSection($act); + $gbm->show(); + $this->blocks['groupsbymembers'] = $act->xw->flush(); + break; + } + return false; + } + + // <%logo%> + // <%nav%> + // <%notice%> + // <%noticeform%> + function onStartShowHeader( &$act ) { + $this->clear_xmlWriter($act); + $act->showLogo(); + $this->blocks['logo'] = $act->xw->flush(); + $act->showPrimaryNav(); + $this->blocks['nav'] = $act->xw->flush(); + $act->showSiteNotice(); + $this->blocks['notice'] = $act->xw->flush(); + if (common_logged_in()) { + $act->showNoticeForm(); + } else { + $act->showAnonymousMessage(); + } + $this->blocks['noticeform'] = $act->xw->flush(); + return false; + } + + // <%secondarynav%> + // <%licenses%> + function onStartShowFooter( &$act ) { + $this->clear_xmlWriter($act); + $act->showSecondaryNav(); + $this->blocks['secondarynav'] = $act->xw->flush(); + $act->showLicenses(); + $this->blocks['licenses'] = $act->xw->flush(); + return false; + } + + // capture the EndHTML event + // and include the template + function onEndEndHTML($act) { + + global $action, $tags; + + // set the action and title values + $vars = array( + 'action'=>$action, + 'title'=>$act->title(). " - ". common_config('site', 'name') + ); + + // use the PHP template + // unless statusnet config: + // $config['template']['mode'] = 'html'; + if (!(common_config('template', 'mode') == 'html')) { + $tpl_file = $this->templateFolder() . '/index.php'; + $tags = array_merge($vars,$this->blocks); + include $tpl_file; + return; + } + + $tpl_file = $this->templateFolder() . '/index.html'; + + // read the static template + $output = file_get_contents( $tpl_file ); + + $tags = array(); + + // get a list of the <%tags%> in the template + $pattern='/<%([a-z]+)%>/'; + + if ( 1 <= preg_match_all( $pattern, $output, $found )) + $tags[] = $found; + + // for each found tag, set its value from the rendered blocks + foreach( $tags[0][1] as $pos=>$tag ) { + if (isset($this->blocks[$tag])) + $vars[$tag] = $this->blocks[$tag]; + + // didn't find a block for the tag + elseif (!isset($vars[$tag])) + $vars[$tag] = ''; + } + + // replace the tags in the template + foreach( $vars as $key=>$val ) + $output = str_replace( '<%'.$key.'%>', $val, $output ); + + echo $output; + + return true; + + } + function templateFolder() { + return 'tpl'; + } + + // catching the StartShowHTML event to halt the rendering + function onStartShowHTML( &$act ) { + $this->clear_xmlWriter($act); + return true; + } + + // clear the xmlWriter + function clear_xmlWriter( &$act ) { + $act->xw->openMemory(); + $act->xw->setIndent(true); + } + +} + +/** + * Action for updating the template remotely + * + * "template/update" -- a POST method that requires a single + * parameter "template", containing the new template code + * + * @category Plugin + * @package StatusNet + * @author Brian Hendrickson + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://megapump.com/ + * + */ + +class TemplateAction extends Action +{ + + function prepare($args) { + parent::prepare($args); + return true; + } + + function handle($args) { + + parent::handle($args); + + if (!isset($_SERVER['PHP_AUTH_USER'])) { + + // not authenticated, show login form + header('WWW-Authenticate: Basic realm="StatusNet API"'); + + // cancelled the browser login form + $this->clientError(_('Authentication error!'), $code = 401); + + } else { + + $nick = $_SERVER['PHP_AUTH_USER']; + $pass = $_SERVER['PHP_AUTH_PW']; + + // check username and password + $user = common_check_user($nick,$pass); + + if ($user) { + + // verify that user is admin + if (!($user->id == 1)) + $this->clientError(_('Only User #1 can update the template.'), $code = 401); + + // open the old template + $tpl_file = $this->templateFolder() . '/index.html'; + $fp = fopen( $tpl_file, 'w+' ); + + // overwrite with the new template + fwrite($fp, $this->arg('template')); + fclose($fp); + + header('HTTP/1.1 200 OK'); + header('Content-type: text/plain'); + print "Template Updated!"; + + } else { + + // bad username and password + $this->clientError(_('Authentication error!'), $code = 401); + + } + + } + } + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Template', + 'version' => TEMPLATEPLUGIN_VERSION, + 'author' => 'Brian Hendrickson', + 'homepage' => 'http://status.net/wiki/Plugin:Template', + 'rawdescription' => + _m('Use an HTML template for Web output.')); + return true; + } + +} + +/** + * Function for retrieving a statusnet display section + * + * requires one parameter, the name of the section + * section names are listed in the comments of the TemplatePlugin class + * + * @category Plugin + * @package StatusNet + * @author Brian Hendrickson + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://megapump.com/ + * + */ + +function section($tagname) { + global $tags; + if (isset($tags[$tagname])) + return $tags[$tagname]; +} + diff --git a/plugins/TemplatePlugin.php b/plugins/TemplatePlugin.php deleted file mode 100644 index 80625c5b7..000000000 --- a/plugins/TemplatePlugin.php +++ /dev/null @@ -1,357 +0,0 @@ - - * @copyright 2009 Megapump, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://megapump.com/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -define('TEMPLATEPLUGIN_VERSION', '0.1'); - -class TemplatePlugin extends Plugin { - - var $blocks = array(); - - function __construct() { - parent::__construct(); - } - - // capture the RouterInitialized event - // and connect a new API method - // for updating the template - function onRouterInitialized( $m ) { - $m->connect( 'template/update', array( - 'action' => 'template', - )); - } - - // <%styles%> - // <%scripts%> - // <%search%> - // <%feeds%> - // <%description%> - // <%head%> - function onStartShowHead( &$act ) { - $this->clear_xmlWriter($act); - $act->extraHead(); - $this->blocks['head'] = $act->xw->flush(); - $act->showStylesheets(); - $this->blocks['styles'] = $act->xw->flush(); - $act->showScripts(); - $this->blocks['scripts'] = $act->xw->flush(); - $act->showFeeds(); - $this->blocks['feeds'] = $act->xw->flush(); - $act->showOpenSearch(); - $this->blocks['search'] = $act->xw->flush(); - $act->showDescription(); - $this->blocks['description'] = $act->xw->flush(); - return false; - } - - // <%bodytext%> - function onStartShowContentBlock( &$act ) { - $this->clear_xmlWriter($act); - return true; - } - function onEndShowContentBlock( &$act ) { - $this->blocks['bodytext'] = $act->xw->flush(); - } - - // <%localnav%> - function onStartShowLocalNavBlock( &$act ) { - $this->clear_xmlWriter($act); - return true; - } - function onEndShowLocalNavBlock( &$act ) { - $this->blocks['localnav'] = $act->xw->flush(); - } - - // <%export%> - function onStartShowExportData( &$act ) { - $this->clear_xmlWriter($act); - return true; - } - function onEndShowExportData( &$act ) { - $this->blocks['export'] = $act->xw->flush(); - } - - // <%subscriptions%> - // <%subscribers%> - // <%groups%> - // <%statistics%> - // <%cloud%> - // <%groupmembers%> - // <%groupstatistics%> - // <%groupcloud%> - // <%popular%> - // <%groupsbyposts%> - // <%featuredusers%> - // <%groupsbymembers%> - function onStartShowSections( &$act ) { - global $action; - $this->clear_xmlWriter($act); - switch ($action) { - case "showstream": - $act->showSubscriptions(); - $this->blocks['subscriptions'] = $act->xw->flush(); - $act->showSubscribers(); - $this->blocks['subscribers'] = $act->xw->flush(); - $act->showGroups(); - $this->blocks['groups'] = $act->xw->flush(); - $act->showStatistics(); - $this->blocks['statistics'] = $act->xw->flush(); - $cloud = new PersonalTagCloudSection($act, $act->user); - $cloud->show(); - $this->blocks['cloud'] = $act->xw->flush(); - break; - case "showgroup": - $act->showMembers(); - $this->blocks['groupmembers'] = $act->xw->flush(); - $act->showStatistics(); - $this->blocks['groupstatistics'] = $act->xw->flush(); - $cloud = new GroupTagCloudSection($act, $act->group); - $cloud->show(); - $this->blocks['groupcloud'] = $act->xw->flush(); - break; - case "public": - $pop = new PopularNoticeSection($act); - $pop->show(); - $this->blocks['popular'] = $act->xw->flush(); - $gbp = new GroupsByPostsSection($act); - $gbp->show(); - $this->blocks['groupsbyposts'] = $act->xw->flush(); - $feat = new FeaturedUsersSection($act); - $feat->show(); - $this->blocks['featuredusers'] = $act->xw->flush(); - break; - case "groups": - $gbp = new GroupsByPostsSection($act); - $gbp->show(); - $this->blocks['groupsbyposts'] = $act->xw->flush(); - $gbm = new GroupsByMembersSection($act); - $gbm->show(); - $this->blocks['groupsbymembers'] = $act->xw->flush(); - break; - } - return false; - } - - // <%logo%> - // <%nav%> - // <%notice%> - // <%noticeform%> - function onStartShowHeader( &$act ) { - $this->clear_xmlWriter($act); - $act->showLogo(); - $this->blocks['logo'] = $act->xw->flush(); - $act->showPrimaryNav(); - $this->blocks['nav'] = $act->xw->flush(); - $act->showSiteNotice(); - $this->blocks['notice'] = $act->xw->flush(); - if (common_logged_in()) { - $act->showNoticeForm(); - } else { - $act->showAnonymousMessage(); - } - $this->blocks['noticeform'] = $act->xw->flush(); - return false; - } - - // <%secondarynav%> - // <%licenses%> - function onStartShowFooter( &$act ) { - $this->clear_xmlWriter($act); - $act->showSecondaryNav(); - $this->blocks['secondarynav'] = $act->xw->flush(); - $act->showLicenses(); - $this->blocks['licenses'] = $act->xw->flush(); - return false; - } - - // capture the EndHTML event - // and include the template - function onEndEndHTML($act) { - - global $action, $tags; - - // set the action and title values - $vars = array( - 'action'=>$action, - 'title'=>$act->title(). " - ". common_config('site', 'name') - ); - - // use the PHP template - // unless statusnet config: - // $config['template']['mode'] = 'html'; - if (!(common_config('template', 'mode') == 'html')) { - $tpl_file = $this->templateFolder() . '/index.php'; - $tags = array_merge($vars,$this->blocks); - include $tpl_file; - return; - } - - $tpl_file = $this->templateFolder() . '/index.html'; - - // read the static template - $output = file_get_contents( $tpl_file ); - - $tags = array(); - - // get a list of the <%tags%> in the template - $pattern='/<%([a-z]+)%>/'; - - if ( 1 <= preg_match_all( $pattern, $output, $found )) - $tags[] = $found; - - // for each found tag, set its value from the rendered blocks - foreach( $tags[0][1] as $pos=>$tag ) { - if (isset($this->blocks[$tag])) - $vars[$tag] = $this->blocks[$tag]; - - // didn't find a block for the tag - elseif (!isset($vars[$tag])) - $vars[$tag] = ''; - } - - // replace the tags in the template - foreach( $vars as $key=>$val ) - $output = str_replace( '<%'.$key.'%>', $val, $output ); - - echo $output; - - return true; - - } - function templateFolder() { - return 'tpl'; - } - - // catching the StartShowHTML event to halt the rendering - function onStartShowHTML( &$act ) { - $this->clear_xmlWriter($act); - return true; - } - - // clear the xmlWriter - function clear_xmlWriter( &$act ) { - $act->xw->openMemory(); - $act->xw->setIndent(true); - } - -} - -/** - * Action for updating the template remotely - * - * "template/update" -- a POST method that requires a single - * parameter "template", containing the new template code - * - * @category Plugin - * @package StatusNet - * @author Brian Hendrickson - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://megapump.com/ - * - */ - -class TemplateAction extends Action -{ - - function prepare($args) { - parent::prepare($args); - return true; - } - - function handle($args) { - - parent::handle($args); - - if (!isset($_SERVER['PHP_AUTH_USER'])) { - - // not authenticated, show login form - header('WWW-Authenticate: Basic realm="StatusNet API"'); - - // cancelled the browser login form - $this->clientError(_('Authentication error!'), $code = 401); - - } else { - - $nick = $_SERVER['PHP_AUTH_USER']; - $pass = $_SERVER['PHP_AUTH_PW']; - - // check username and password - $user = common_check_user($nick,$pass); - - if ($user) { - - // verify that user is admin - if (!($user->id == 1)) - $this->clientError(_('Only User #1 can update the template.'), $code = 401); - - // open the old template - $tpl_file = $this->templateFolder() . '/index.html'; - $fp = fopen( $tpl_file, 'w+' ); - - // overwrite with the new template - fwrite($fp, $this->arg('template')); - fclose($fp); - - header('HTTP/1.1 200 OK'); - header('Content-type: text/plain'); - print "Template Updated!"; - - } else { - - // bad username and password - $this->clientError(_('Authentication error!'), $code = 401); - - } - - } - } - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'Template', - 'version' => TEMPLATEPLUGIN_VERSION, - 'author' => 'Brian Hendrickson', - 'homepage' => 'http://status.net/wiki/Plugin:Template', - 'rawdescription' => - _m('Use an HTML template for Web output.')); - return true; - } - -} - -/** - * Function for retrieving a statusnet display section - * - * requires one parameter, the name of the section - * section names are listed in the comments of the TemplatePlugin class - * - * @category Plugin - * @package StatusNet - * @author Brian Hendrickson - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://megapump.com/ - * - */ - -function section($tagname) { - global $tags; - if (isset($tags[$tagname])) - return $tags[$tagname]; -} - diff --git a/plugins/UserLimit/UserLimitPlugin.php b/plugins/UserLimit/UserLimitPlugin.php new file mode 100644 index 000000000..ab3187299 --- /dev/null +++ b/plugins/UserLimit/UserLimitPlugin.php @@ -0,0 +1,92 @@ +. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to limit number of users that can register (best for cloud providers) + * + * For cloud providers whose freemium model is based on how many + * users can register. We use it on the StatusNet Cloud. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso Location + */ + +class UserLimitPlugin extends Plugin +{ + public $maxUsers = null; + + function onStartUserRegister(&$user, &$profile) + { + $this->_checkMaxUsers(); + return true; + } + + function onStartRegistrationTry($action) + { + $this->_checkMaxUsers(); + return true; + } + + function _checkMaxUsers() + { + if (!is_null($this->maxUsers)) { + + $cls = new User(); + + $cnt = $cls->count(); + + if ($cnt >= $this->maxUsers) { + $msg = sprintf(_('Cannot register; maximum number of users (%d) reached.'), + $this->maxUsers); + + throw new ClientException($msg); + } + } + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'UserLimit', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:UserLimit', + 'description' => + _m('Limit the number of users who can register.')); + return true; + } +} diff --git a/plugins/UserLimitPlugin.php b/plugins/UserLimitPlugin.php deleted file mode 100644 index ab3187299..000000000 --- a/plugins/UserLimitPlugin.php +++ /dev/null @@ -1,92 +0,0 @@ -. - * - * @category Action - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -/** - * Plugin to limit number of users that can register (best for cloud providers) - * - * For cloud providers whose freemium model is based on how many - * users can register. We use it on the StatusNet Cloud. - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @seeAlso Location - */ - -class UserLimitPlugin extends Plugin -{ - public $maxUsers = null; - - function onStartUserRegister(&$user, &$profile) - { - $this->_checkMaxUsers(); - return true; - } - - function onStartRegistrationTry($action) - { - $this->_checkMaxUsers(); - return true; - } - - function _checkMaxUsers() - { - if (!is_null($this->maxUsers)) { - - $cls = new User(); - - $cnt = $cls->count(); - - if ($cnt >= $this->maxUsers) { - $msg = sprintf(_('Cannot register; maximum number of users (%d) reached.'), - $this->maxUsers); - - throw new ClientException($msg); - } - } - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'UserLimit', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:UserLimit', - 'description' => - _m('Limit the number of users who can register.')); - return true; - } -} diff --git a/plugins/WikiHashtags/WikiHashtagsPlugin.php b/plugins/WikiHashtags/WikiHashtagsPlugin.php new file mode 100644 index 000000000..c6c976b8f --- /dev/null +++ b/plugins/WikiHashtags/WikiHashtagsPlugin.php @@ -0,0 +1,113 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @copyright 2008 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to use WikiHashtags + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see Event + */ + +class WikiHashtagsPlugin extends Plugin +{ + const VERSION = '0.1'; + + function __construct($code=null) + { + parent::__construct(); + } + + function onStartShowSections($action) + { + $name = $action->trimmed('action'); + + if ($name == 'tag') { + + $taginput = $action->trimmed('tag'); + $tag = common_canonical_tag($taginput); + + if (!empty($tag)) { + + $url = sprintf('http://hashtags.wikia.com/index.php?title=%s&action=render', + urlencode($tag)); + $editurl = sprintf('http://hashtags.wikia.com/index.php?title=%s&action=edit', + urlencode($tag)); + + $request = HTTPClient::start(); + $response = $request->get($url); + $html = $response->getBody(); + + $action->elementStart('div', array('id' => 'wikihashtags', 'class' => 'section')); + + if ($response->isOk() && !empty($html)) { + $action->element('style', null, + "span.editsection { display: none }\n". + "table.toc { display: none }"); + $action->raw($html); + $action->elementStart('p'); + $action->element('a', array('href' => $editurl, + 'title' => sprintf(_('Edit the article for #%s on WikiHashtags'), $tag)), + _('Edit')); + $action->element('a', array('href' => 'http://www.gnu.org/copyleft/fdl.html', + 'title' => _('Shared under the terms of the GNU Free Documentation License'), + 'rel' => 'license'), + 'GNU FDL'); + $action->elementEnd('p'); + } else { + $action->element('a', array('href' => $editurl), + sprintf(_('Start the article for #%s on WikiHashtags'), $tag)); + } + + $action->elementEnd('div'); + } + } + + return true; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'WikiHashtags', + 'version' => self::VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:WikiHashtags', + 'rawdescription' => + _m('Gets hashtag descriptions from WikiHashtags.')); + return true; + } +} diff --git a/plugins/WikiHashtagsPlugin.php b/plugins/WikiHashtagsPlugin.php deleted file mode 100644 index c6c976b8f..000000000 --- a/plugins/WikiHashtagsPlugin.php +++ /dev/null @@ -1,113 +0,0 @@ -. - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @copyright 2008 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - exit(1); -} - -/** - * Plugin to use WikiHashtags - * - * @category Plugin - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @see Event - */ - -class WikiHashtagsPlugin extends Plugin -{ - const VERSION = '0.1'; - - function __construct($code=null) - { - parent::__construct(); - } - - function onStartShowSections($action) - { - $name = $action->trimmed('action'); - - if ($name == 'tag') { - - $taginput = $action->trimmed('tag'); - $tag = common_canonical_tag($taginput); - - if (!empty($tag)) { - - $url = sprintf('http://hashtags.wikia.com/index.php?title=%s&action=render', - urlencode($tag)); - $editurl = sprintf('http://hashtags.wikia.com/index.php?title=%s&action=edit', - urlencode($tag)); - - $request = HTTPClient::start(); - $response = $request->get($url); - $html = $response->getBody(); - - $action->elementStart('div', array('id' => 'wikihashtags', 'class' => 'section')); - - if ($response->isOk() && !empty($html)) { - $action->element('style', null, - "span.editsection { display: none }\n". - "table.toc { display: none }"); - $action->raw($html); - $action->elementStart('p'); - $action->element('a', array('href' => $editurl, - 'title' => sprintf(_('Edit the article for #%s on WikiHashtags'), $tag)), - _('Edit')); - $action->element('a', array('href' => 'http://www.gnu.org/copyleft/fdl.html', - 'title' => _('Shared under the terms of the GNU Free Documentation License'), - 'rel' => 'license'), - 'GNU FDL'); - $action->elementEnd('p'); - } else { - $action->element('a', array('href' => $editurl), - sprintf(_('Start the article for #%s on WikiHashtags'), $tag)); - } - - $action->elementEnd('div'); - } - } - - return true; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'WikiHashtags', - 'version' => self::VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:WikiHashtags', - 'rawdescription' => - _m('Gets hashtag descriptions from WikiHashtags.')); - return true; - } -} diff --git a/plugins/XCache/XCachePlugin.php b/plugins/XCache/XCachePlugin.php new file mode 100644 index 000000000..2baa290ed --- /dev/null +++ b/plugins/XCache/XCachePlugin.php @@ -0,0 +1,124 @@ +. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * A plugin to use XCache's variable cache for the cache interface + * + * New plugin interface lets us use alternative cache systems + * for caching. This one uses XCache's variable cache. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class XCachePlugin extends Plugin +{ + /** + * Get a value associated with a key + * + * The value should have been set previously. + * + * @param string &$key in; Lookup key + * @param mixed &$value out; value associated with key + * + * @return boolean hook success + */ + + function onStartCacheGet(&$key, &$value) + { + if (!xcache_isset($key)) { + $value = false; + } else { + $value = xcache_get($key); + $value = unserialize($value); + } + Event::handle('EndCacheGet', array($key, &$value)); + return false; + } + + /** + * Associate a value with a key + * + * @param string &$key in; Key to use for lookups + * @param mixed &$value in; Value to associate + * @param integer &$flag in; Flag (passed through to Memcache) + * @param integer &$expiry in; Expiry (passed through to Memcache) + * @param boolean &$success out; Whether the set was successful + * + * @return boolean hook success + */ + + function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) + { + $success = xcache_set($key, serialize($value)); + + Event::handle('EndCacheSet', array($key, $value, $flag, + $expiry)); + return false; + } + + /** + * Delete a value associated with a key + * + * @param string &$key in; Key to lookup + * @param boolean &$success out; whether it worked + * + * @return boolean hook success + */ + + function onStartCacheDelete(&$key, &$success) + { + $success = xcache_unset($key); + Event::handle('EndCacheDelete', array($key)); + return false; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'XCache', + 'version' => STATUSNET_VERSION, + 'author' => 'Craig Andrews', + 'homepage' => 'http://status.net/wiki/Plugin:XCache', + 'rawdescription' => + _m('Use the XCache variable cache to cache query results.')); + return true; + } +} + diff --git a/plugins/XCachePlugin.php b/plugins/XCachePlugin.php deleted file mode 100644 index 2baa290ed..000000000 --- a/plugins/XCachePlugin.php +++ /dev/null @@ -1,124 +0,0 @@ -. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET')) { - // This check helps protect against security problems; - // your code file can't be executed directly from the web. - exit(1); -} - -/** - * A plugin to use XCache's variable cache for the cache interface - * - * New plugin interface lets us use alternative cache systems - * for caching. This one uses XCache's variable cache. - * - * @category Cache - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -class XCachePlugin extends Plugin -{ - /** - * Get a value associated with a key - * - * The value should have been set previously. - * - * @param string &$key in; Lookup key - * @param mixed &$value out; value associated with key - * - * @return boolean hook success - */ - - function onStartCacheGet(&$key, &$value) - { - if (!xcache_isset($key)) { - $value = false; - } else { - $value = xcache_get($key); - $value = unserialize($value); - } - Event::handle('EndCacheGet', array($key, &$value)); - return false; - } - - /** - * Associate a value with a key - * - * @param string &$key in; Key to use for lookups - * @param mixed &$value in; Value to associate - * @param integer &$flag in; Flag (passed through to Memcache) - * @param integer &$expiry in; Expiry (passed through to Memcache) - * @param boolean &$success out; Whether the set was successful - * - * @return boolean hook success - */ - - function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) - { - $success = xcache_set($key, serialize($value)); - - Event::handle('EndCacheSet', array($key, $value, $flag, - $expiry)); - return false; - } - - /** - * Delete a value associated with a key - * - * @param string &$key in; Key to lookup - * @param boolean &$success out; whether it worked - * - * @return boolean hook success - */ - - function onStartCacheDelete(&$key, &$success) - { - $success = xcache_unset($key); - Event::handle('EndCacheDelete', array($key)); - return false; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'XCache', - 'version' => STATUSNET_VERSION, - 'author' => 'Craig Andrews', - 'homepage' => 'http://status.net/wiki/Plugin:XCache', - 'rawdescription' => - _m('Use the XCache variable cache to cache query results.')); - return true; - } -} - -- cgit v1.2.3-54-g00ecf