diff options
Diffstat (limited to 'plugins')
63 files changed, 3271 insertions, 437 deletions
diff --git a/plugins/Authentication/AuthenticationPlugin.php b/plugins/Authentication/AuthenticationPlugin.php new file mode 100644 index 000000000..1b9084187 --- /dev/null +++ b/plugins/Authentication/AuthenticationPlugin.php @@ -0,0 +1,217 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Superclass for plugins that do authentication and/or authorization + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @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') && !defined('LACONICA')) { + exit(1); +} + +/** + * Superclass for plugins that do authentication + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +abstract class AuthenticationPlugin extends Plugin +{ + //is this plugin authoritative for authentication? + public $authoritative = false; + + //should accounts be automatically created after a successful login attempt? + public $autoregistration = false; + + //can the user change their email address + public $password_changeable=true; + + //unique name for this authentication provider + public $provider_name; + + //------------Auth plugin should implement some (or all) of these methods------------\\ + /** + * Check if a nickname/password combination is valid + * @param username + * @param password + * @return boolean true if the credentials are valid, false if they are invalid. + */ + function checkPassword($username, $password) + { + return false; + } + + /** + * Automatically register a user when they attempt to login with valid credentials. + * User::register($data) is a very useful method for this implementation + * @param username + * @return mixed instance of User, or false (if user couldn't be created) + */ + function autoRegister($username) + { + $registration_data = array(); + $registration_data['nickname'] = $username ; + return User::register($registration_data); + } + + /** + * Change a user's password + * The old password has been verified to be valid by this plugin before this call is made + * @param username + * @param oldpassword + * @param newpassword + * @return boolean true if the password was changed, false if password changing failed for some reason + */ + function changePassword($username,$oldpassword,$newpassword) + { + return false; + } + + //------------Below are the methods that connect StatusNet to the implementing Auth plugin------------\\ + function onInitializePlugin(){ + if(!isset($this->provider_name)){ + throw new Exception("must specify a provider_name for this authentication provider"); + } + } + + function onStartCheckPassword($nickname, $password, &$authenticatedUser){ + //map the nickname to a username + $user_username = new User_username(); + $user_username->username=$nickname; + $user_username->provider_name=$this->provider_name; + if($user_username->find() && $user_username->fetch()){ + $username = $user_username->username; + $authenticated = $this->checkPassword($username, $password); + if($authenticated){ + $authenticatedUser = User::staticGet('id', $user_username->user_id); + return false; + } + }else{ + $user = User::staticGet('nickname', $nickname); + if($user){ + //make sure a different provider isn't handling this nickname + $user_username = new User_username(); + $user_username->username=$nickname; + if(!$user_username->find()){ + //no other provider claims this username, so it's safe for us to handle it + $authenticated = $this->checkPassword($nickname, $password); + if($authenticated){ + $authenticatedUser = User::staticGet('nickname', $nickname); + User_username::register($authenticatedUser,$nickname,$this->provider_name); + return false; + } + } + }else{ + if($this->autoregistration){ + $authenticated = $this->checkPassword($nickname, $password); + if($authenticated){ + $user = $this->autoregister($nickname); + if($user){ + $authenticatedUser = $user; + User_username::register($authenticatedUser,$nickname,$this->provider_name); + return false; + } + } + } + } + } + if($this->authoritative){ + return false; + }else{ + //we're not authoritative, so let other handlers try + return; + } + } + + function onStartChangePassword($user,$oldpassword,$newpassword) + { + if($this->password_changeable){ + $user_username = new User_username(); + $user_username->user_id=$user->id; + $user_username->provider_name=$this->provider_name; + if($user_username->find() && $user_username->fetch()){ + $authenticated = $this->checkPassword($user_username->username, $oldpassword); + if($authenticated){ + $result = $this->changePassword($user_username->username,$oldpassword,$newpassword); + if($result){ + //stop handling of other handlers, because what was requested was done + return false; + }else{ + throw new Exception(_('Password changing failed')); + } + }else{ + if($this->authoritative){ + //since we're authoritative, no other plugin could do this + throw new Exception(_('Password changing failed')); + }else{ + //let another handler try + return null; + } + } + } + }else{ + if($this->authoritative){ + //since we're authoritative, no other plugin could do this + throw new Exception(_('Password changing is not allowed')); + } + } + } + + function onStartAccountSettingsPasswordMenuItem($widget) + { + if($this->authoritative && !$this->password_changeable){ + //since we're authoritative, no other plugin could change passwords, so do not render the menu item + return false; + } + } + + function onAutoload($cls) + { + switch ($cls) + { + case 'User_username': + require_once(INSTALLDIR.'/plugins/Authentication/User_username.php'); + return false; + default: + return true; + } + } + + function onCheckSchema() { + $schema = Schema::get(); + $schema->ensureDataObject('User_username'); + return true; + } + + function onUserDeleteRelated($user, &$tables) + { + $tables[] = 'User_username'; + return true; + } +} + diff --git a/plugins/Authentication/User_username.php b/plugins/Authentication/User_username.php new file mode 100644 index 000000000..6826f2681 --- /dev/null +++ b/plugins/Authentication/User_username.php @@ -0,0 +1,64 @@ +<?php +/** + * Table Definition for user_username + */ +require_once INSTALLDIR.'/classes/Plugin_DataObject.php'; + +class User_username extends Plugin_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'user_username'; // table name + public $user_id; // int(4) not_null + public $provider_name; // varchar(255) primary_key not_null + public $username; // varchar(255) primary_key not_null + public $created; // datetime() not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('User_username',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + /** + * Register a user with a username on a given provider + * @param User User object + * @param string username on the given provider + * @param provider_name string name of the provider + * @return mixed User_username instance if the registration succeeded, false if it did not + */ + static function register($user, $username, $provider_name) + { + $user_username = new User_username(); + $user_username->user_id = $user->id; + $user_username->provider_name = $provider_name; + $user_username->username = $username; + $user_username->created = DB_DataObject_Cast::dateTime(); + if($user_username->insert()){ + return $user_username; + }else{ + return false; + } + } + + /** + * Get the TableDef object that represents the table backing this class + * @return TableDef TableDef instance + */ + function tableDef() + { + return new TableDef($this->__table, + array(new ColumnDef('provider_name', 'varchar', + '255', false, 'PRI'), + new ColumnDef('username', 'varchar', + '255', false, 'PRI'), + new ColumnDef('user_id', 'integer', + null, false), + new ColumnDef('created', 'datetime', + null, false), + new ColumnDef('modified', 'timestamp'))); + } +} diff --git a/plugins/Autocomplete/autocomplete.php b/plugins/Autocomplete/autocomplete.php index aa57b3915..379390ffd 100644 --- a/plugins/Autocomplete/autocomplete.php +++ b/plugins/Autocomplete/autocomplete.php @@ -98,11 +98,10 @@ class AutocompleteAction extends Action $user = new User(); $user->limit($limit); $user->whereAdd('nickname like \'' . trim($user->escape($q), '\'') . '%\''); - $user->find(); - while($user->fetch()) { - $profile = Profile::staticGet($user->id); - $user->profile=$profile; - $this->users[]=$user; + if($user->find()){ + while($user->fetch()) { + $this->users[]=clone($user); + } } } if(substr($q,0,1)=='!'){ @@ -111,9 +110,10 @@ class AutocompleteAction extends Action $group = new User_group(); $group->limit($limit); $group->whereAdd('nickname like \'' . trim($group->escape($q), '\'') . '%\''); - $group->find(); - while($group->fetch()) { - $this->groups[]=$group; + if($group->find()){ + while($group->fetch()) { + $this->groups[]=clone($group); + } } } return true; @@ -124,7 +124,8 @@ class AutocompleteAction extends Action parent::handle($args); $results = array(); foreach($this->users as $user){ - $results[]=array('nickname' => $user->nickname, 'fullname'=> $user->profile->fullname, 'type'=>'user'); + $profile = $user->getProfile(); + $results[]=array('nickname' => $user->nickname, 'fullname'=> $profile->fullname, 'type'=>'user'); } foreach($this->groups as $group){ $results[]=array('nickname' => $group->nickname, 'fullname'=> $group->fullname, 'type'=>'group'); diff --git a/plugins/BitlyUrl/BitlyUrlPlugin.php b/plugins/BitlyUrl/BitlyUrlPlugin.php new file mode 100644 index 000000000..65d0f70e6 --- /dev/null +++ b/plugins/BitlyUrl/BitlyUrlPlugin.php @@ -0,0 +1,54 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Plugin to push RSS/Atom updates to a PubSubHubBub hub + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR.'/plugins/UrlShortener/UrlShortenerPlugin.php'; + +class BitlyUrlPlugin extends UrlShortenerPlugin +{ + public $serviceUrl; + + function onInitializePlugin(){ + parent::onInitializePlugin(); + if(!isset($this->serviceUrl)){ + throw new Exception("must specify a serviceUrl"); + } + } + + protected function shorten($url) { + $response = $this->http_get($url); + if(!$response) return; + return current(json_decode($response)->results)->hashUrl; + } +} + + diff --git a/plugins/BlogspamNetPlugin.php b/plugins/BlogspamNetPlugin.php index c14569746..51236001a 100644 --- a/plugins/BlogspamNetPlugin.php +++ b/plugins/BlogspamNetPlugin.php @@ -22,6 +22,7 @@ * @category Plugin * @package StatusNet * @author Evan Prodromou <evan@status.net> + * @author Brion Vibber <brion@status.net> * @copyright 2009 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ @@ -69,14 +70,12 @@ class BlogspamNetPlugin extends Plugin { $args = $this->testArgs($notice); common_debug("Blogspamnet args = " . print_r($args, TRUE)); - $request = xmlrpc_encode_request('testComment', array($args)); - $context = stream_context_create(array('http' => array('method' => "POST", - 'header' => - "Content-Type: text/xml\r\n". - "User-Agent: " . $this->userAgent(), - 'content' => $request))); - $file = file_get_contents($this->baseUrl, false, $context); - $response = xmlrpc_decode($file); + $requestBody = xmlrpc_encode_request('testComment', array($args)); + + $request = HTTPClient::start(); + $httpResponse = $request->post($this->baseUrl, array('Content-Type: text/xml'), $requestBody); + + $response = xmlrpc_decode($httpResponse->getBody()); if (xmlrpc_is_fault($response)) { throw new ServerException("$response[faultString] ($response[faultCode])", 500); } else { diff --git a/plugins/EmailAuthentication/EmailAuthenticationPlugin.php b/plugins/EmailAuthentication/EmailAuthenticationPlugin.php new file mode 100644 index 000000000..25e537735 --- /dev/null +++ b/plugins/EmailAuthentication/EmailAuthenticationPlugin.php @@ -0,0 +1,54 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Plugin that uses the email address as a username, and checks the password as normal + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @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') && !defined('LACONICA')) { + exit(1); +} + +class EmailAuthenticationPlugin extends Plugin +{ + //---interface implementation---// + + function onStartCheckPassword($nickname, $password, &$authenticatedUser) + { + if(strpos($nickname, '@')) + { + $user = User::staticGet('email',$nickname); + if($user && isset($user->email)) + { + if(common_check_user($user->nickname,$password)) + { + $authenticatedUser = $user; + return false; + } + } + } + } +} + diff --git a/plugins/EmailAuthentication/README b/plugins/EmailAuthentication/README new file mode 100644 index 000000000..320815689 --- /dev/null +++ b/plugins/EmailAuthentication/README @@ -0,0 +1,7 @@ +The Email Authentication plugin allows users to login using their email address. + +The provided email address is used to lookup the user's nickname, then that nickname and the provided password is checked. + +Installation +============ +add "addPlugin('emailAuthentication');" to the bottom of your config.php diff --git a/plugins/Facebook/FacebookPlugin.php b/plugins/Facebook/FacebookPlugin.php index fc3adcfad..b68534b24 100644 --- a/plugins/Facebook/FacebookPlugin.php +++ b/plugins/Facebook/FacebookPlugin.php @@ -58,7 +58,7 @@ class FacebookPlugin extends Plugin * @return boolean hook return */ - function onRouterInitialized(&$m) + function onRouterInitialized($m) { // Facebook App stuff diff --git a/plugins/Facebook/facebookaction.php b/plugins/Facebook/facebookaction.php index f5ad3d06b..c852bbf5e 100644 --- a/plugins/Facebook/facebookaction.php +++ b/plugins/Facebook/facebookaction.php @@ -83,8 +83,8 @@ class FacebookAction extends Action function showStylesheets() { $this->cssLink('css/display.css', 'base'); - $this->cssLink('css/display.css',null,'screen, projection, tv'); - $this->cssLink('css/facebookapp.css', 'base'); + $this->cssLink('css/display.css', null, 'screen, projection, tv'); + $this->cssLink('plugins/Facebook/facebookapp.css'); } function showScripts() @@ -382,8 +382,7 @@ class FacebookAction extends Action { // Does a little before-after block for next/prev page if ($have_before || $have_after) { - $this->elementStart('div', array('class' => 'pagination')); - $this->elementStart('dl', null); + $this->elementStart('dl', 'pagination'); $this->element('dt', null, _('Pagination')); $this->elementStart('dd', null); $this->elementStart('ul', array('class' => 'nav')); @@ -408,7 +407,6 @@ class FacebookAction extends Action $this->elementEnd('ul'); $this->elementEnd('dd'); $this->elementEnd('dl'); - $this->elementEnd('div'); } } diff --git a/plugins/Facebook/facebookapp.css b/plugins/Facebook/facebookapp.css new file mode 100644 index 000000000..8cd06f78a --- /dev/null +++ b/plugins/Facebook/facebookapp.css @@ -0,0 +1,115 @@ +* { +font-size:14px; +font-family:"Lucida Sans Unicode", "Lucida Grande", sans-serif; +} + +#wrap { +background-color:#F0F2F5; +padding-left:1.795%; +padding-right:1.795%; +width:auto; +} + +p,label, +h1,h2,h3,h4,h5,h6 { +color:#000; +} + +#header { +width:131%; +} + +#content { +width:92.7%; +} + +#aside_primary { +display:none; +} + +#site_nav_local_views a { +background-color:#D0DFE7; +} +#site_nav_local_views a:hover { +background-color:#FAFBFC; +} + +#form_notice .form_note + label, +#form_notice #notice_data-attach { +display:none; +} + +#form_notice #notice_action-submit { +height:47px !important; +} + + +span.facebook-button { +border: 2px solid #aaa; +padding: 3px; +display: block; +float: left; +margin-right: 20px; +-moz-border-radius: 4px; +border-radius:4px; +-webkit-border-radius:4px; +font-weight: bold; +background-color:#A9BF4F; +color:#fff; +font-size:1.2em +} + +span.facebook-button a { color:#fff } + +.facebook_guide { +margin-bottom:18px; +} +.facebook_guide p { +font-weight:bold; +} + + +input { +height:auto !important; +} + +#facebook-friends { +float:left; +width:100%; +} + +#facebook-friends li { +float:left; +margin-right:2%; +margin-bottom:11px; +width:18%; +height:115px; +} +#facebook-friends li a { +float:left; +} + +#add_to_profile { +position:absolute; +right:18px; +top:10px; +z-index:2; +} + +.notice div.entry-content dl, +.notice div.entry-content dt, +.notice div.entry-content dd { +margin-right:5px; +} + +#content_inner p { +margin-bottom:18px; +} + +#content_inner ul { +list-style-type:none; +} + +.form_settings label { +margin-right:18px; +} diff --git a/plugins/Facebook/facebookhome.php b/plugins/Facebook/facebookhome.php index 91c0cc6b8..ea141c2c2 100644 --- a/plugins/Facebook/facebookhome.php +++ b/plugins/Facebook/facebookhome.php @@ -244,8 +244,7 @@ class FacebookhomeAction extends FacebookAction // XXX: Fix so this uses common_local_url() if possible. if ($have_before || $have_after) { - $this->elementStart('div', array('class' => 'pagination')); - $this->elementStart('dl', null); + $this->elementStart('dl', 'pagination'); $this->element('dt', null, _('Pagination')); $this->elementStart('dd', null); $this->elementStart('ul', array('class' => 'nav')); @@ -270,7 +269,6 @@ class FacebookhomeAction extends FacebookAction $this->elementEnd('ul'); $this->elementEnd('dd'); $this->elementEnd('dl'); - $this->elementEnd('div'); } } diff --git a/plugins/Facebook/facebookinvite.php b/plugins/Facebook/facebookinvite.php index ecda1717c..3380b4c85 100644 --- a/plugins/Facebook/facebookinvite.php +++ b/plugins/Facebook/facebookinvite.php @@ -105,6 +105,7 @@ class FacebookinviteAction extends FacebookAction $multi_params = array('showborder' => 'false'); $multi_params['actiontext'] = $actiontext; $multi_params['bypass'] = 'cancel'; + $multi_params['cols'] = 4; // Get a list of users who are already using the app for exclusion $exclude_ids = $this->facebook->api_client->friends_getAppUsers(); diff --git a/plugins/GeoURLPlugin.php b/plugins/GeoURLPlugin.php new file mode 100644 index 000000000..30ff2c278 --- /dev/null +++ b/plugins/GeoURLPlugin.php @@ -0,0 +1,119 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Plugin to add ICBM metadata to HTML pages and report data to GeoURL.org + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2009 StatusNet Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to 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 <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso Location + */ + +class GeoURLPlugin extends Plugin +{ + public $ping = 'http://geourl.org/ping/'; + + /** + * Add extra <meta> 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; + } +} diff --git a/plugins/GeonamesPlugin.php b/plugins/GeonamesPlugin.php new file mode 100644 index 000000000..e18957c36 --- /dev/null +++ b/plugins/GeonamesPlugin.php @@ -0,0 +1,305 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Plugin to convert string locations to Geonames IDs and vice versa + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2009 StatusNet Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to 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 <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso Location + */ + +class GeonamesPlugin extends Plugin +{ + const NAMESPACE = 1; + + /** + * 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) + { + $client = HTTPClient::start(); + + // XXX: break down a name by commas, narrow by each + + $str = http_build_query(array('maxRows' => 1, + 'q' => $name, + 'lang' => $language, + 'type' => 'json')); + + $result = $client->get('http://ws.geonames.org/search?'.$str); + + if ($result->isOk()) { + $rj = json_decode($result->getBody()); + if (count($rj->geonames) > 0) { + $n = $rj->geonames[0]; + + $location = new Location(); + + $location->lat = $n->lat; + $location->lon = $n->lng; + $location->names[$language] = $n->name; + $location->location_id = $n->geonameId; + $location->location_ns = self::NAMESPACE; + + // handled, don't continue processing! + return false; + } + } + + // Continue processing; we don't have the answer + return true; + } + + /** + * 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::NAMESPACE) { + // It's not one of our IDs... keep processing + return true; + } + + $client = HTTPClient::start(); + + $str = http_build_query(array('geonameId' => $id, + 'lang' => $language)); + + $result = $client->get('http://ws.geonames.org/hierarchyJSON?'.$str); + + if ($result->isOk()) { + + $rj = json_decode($result->getBody()); + + if (count($rj->geonames) > 0) { + + $parts = array(); + + foreach ($rj->geonames as $level) { + if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $level->name; + } + } + + $last = $rj->geonames[count($rj->geonames)-1]; + + if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $last->name; + } + + $location = new Location(); + + $location->location_id = $last->geonameId; + $location->location_ns = self::NAMESPACE; + $location->lat = $last->lat; + $location->lon = $last->lng; + $location->names[$language] = implode(', ', array_reverse($parts)); + } + } + + // 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) + { + $client = HTTPClient::start(); + + $str = http_build_query(array('lat' => $lat, + 'lng' => $lon, + 'lang' => $language)); + + $result = + $client->get('http://ws.geonames.org/findNearbyPlaceNameJSON?'.$str); + + if ($result->isOk()) { + + $rj = json_decode($result->getBody()); + + if (count($rj->geonames) > 0) { + + $n = $rj->geonames[0]; + + $parts = array(); + + $location = new Location(); + + $parts[] = $n->name; + + if (!empty($n->adminName1)) { + $parts[] = $n->adminName1; + } + + if (!empty($n->countryName)) { + $parts[] = $n->countryName; + } + + $location->location_id = $n->geonameId; + $location->location_ns = self::NAMESPACE; + $location->lat = $lat; + $location->lon = $lon; + + $location->names[$language] = implode(', ', $parts); + + // Success! We handled it, so no further processing + + return false; + } + } + + // For some reason we don't know, so pass. + + return true; + } + + /** + * 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::NAMESPACE) { + // It's not one of our IDs... keep processing + return true; + } + + $client = HTTPClient::start(); + + $str = http_build_query(array('geonameId' => $id, + 'lang' => $language)); + + $result = $client->get('http://ws.geonames.org/hierarchyJSON?'.$str); + + if ($result->isOk()) { + + $rj = json_decode($result->getBody()); + + if (count($rj->geonames) > 0) { + + $parts = array(); + + foreach ($rj->geonames as $level) { + if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $level->name; + } + } + + $last = $rj->geonames[count($rj->geonames)-1]; + + if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $last->name; + } + + if (count($parts)) { + $name = implode(', ', array_reverse($parts)); + return false; + } + } + } + + return true; + } + + /** + * Human-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 onLocationUrl($location, &$url) + { + if ($location->location_ns != self::NAMESPACE) { + // 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; + } +} diff --git a/plugins/InfiniteScroll/infinitescroll.js b/plugins/InfiniteScroll/infinitescroll.js index ae4d53d09..0dafef6d5 100644 --- a/plugins/InfiniteScroll/infinitescroll.js +++ b/plugins/InfiniteScroll/infinitescroll.js @@ -2,14 +2,20 @@ jQuery(document).ready(function($){ $('notices_primary').infinitescroll({ debug: true, infiniteScroll : false, - nextSelector : "li.nav_next a", + nextSelector : 'body#public li.nav_next a,'+ + 'body#all li.nav_next a,'+ + 'body#showstream li.nav_next a,'+ + 'body#replies li.nav_next a,'+ + 'body#showfavorites li.nav_next a,'+ + 'body#showgroup li.nav_next a,'+ + 'body#favorited li.nav_next a', loadingImg : $('address .url')[0].href+'plugins/InfiniteScroll/ajax-loader.gif', text : "<em>Loading the next set of posts...</em>", donetext : "<em>Congratulations, you\'ve reached the end of the Internet.</em>", - navSelector : "div.pagination", + navSelector : ".pagination", contentSelector : "#notices_primary ol.notices", itemSelector : "#notices_primary ol.notices li" },function(){ - NoticeAttachments(); + SN.Init.Notices(); }); }); diff --git a/plugins/LdapAuthentication/LdapAuthenticationPlugin.php b/plugins/LdapAuthentication/LdapAuthenticationPlugin.php new file mode 100644 index 000000000..865154730 --- /dev/null +++ b/plugins/LdapAuthentication/LdapAuthenticationPlugin.php @@ -0,0 +1,180 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Plugin to enable LDAP Authentication and Authorization + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @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') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/plugins/Authentication/AuthenticationPlugin.php'; +require_once 'Net/LDAP2.php'; + +class LdapAuthenticationPlugin extends AuthenticationPlugin +{ + public $host=null; + public $port=null; + public $version=null; + public $starttls=null; + public $binddn=null; + public $bindpw=null; + public $basedn=null; + public $options=null; + public $filter=null; + public $scope=null; + public $attributes=array(); + + function onInitializePlugin(){ + parent::onInitializePlugin(); + if(!isset($this->host)){ + throw new Exception("must specify a host"); + } + if(!isset($this->basedn)){ + throw new Exception("must specify a basedn"); + } + if(!isset($this->attributes['nickname'])){ + throw new Exception("must specify a nickname attribute"); + } + if(!isset($this->attributes['username'])){ + throw new Exception("must specify a username attribute"); + } + } + + //---interface implementation---// + + function checkPassword($username, $password) + { + $ldap = $this->ldap_get_connection(); + if(!$ldap){ + return false; + } + $entry = $this->ldap_get_user($username); + if(!$entry){ + return false; + }else{ + $config = $this->ldap_get_config(); + $config['binddn']=$entry->dn(); + $config['bindpw']=$password; + if($this->ldap_get_connection($config)){ + return true; + }else{ + return false; + } + } + } + + function autoRegister($username) + { + $entry = $this->ldap_get_user($username,$this->attributes); + if($entry){ + $registration_data = array(); + foreach($this->attributes as $sn_attribute=>$ldap_attribute){ + $registration_data[$sn_attribute]=$entry->getValue($ldap_attribute,'single'); + } + if(isset($registration_data['email']) && !empty($registration_data['email'])){ + $registration_data['email_confirmed']=true; + } + //set the database saved password to a random string. + $registration_data['password']=common_good_rand(16); + return User::register($registration_data); + }else{ + //user isn't in ldap, so we cannot register him + return false; + } + } + + function changePassword($username,$oldpassword,$newpassword) + { + //TODO implement this + throw new Exception(_('Sorry, changing LDAP passwords is not supported at this time')); + + return false; + } + + //---utility functions---// + function ldap_get_config(){ + $config = array(); + $keys = array('host','port','version','starttls','binddn','bindpw','basedn','options','filter','scope'); + foreach($keys as $key){ + $value = $this->$key; + if($value!==null){ + $config[$key]=$value; + } + } + return $config; + } + + function ldap_get_connection($config = null){ + if($config == null){ + $config = $this->ldap_get_config(); + } + + //cannot use Net_LDAP2::connect() as StatusNet uses + //PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); + //PEAR handling can be overridden on instance objects, so we do that. + $ldap = new Net_LDAP2($config); + $ldap->setErrorHandling(PEAR_ERROR_RETURN); + $err=$ldap->bind(); + if (Net_LDAP2::isError($err)) { + common_log(LOG_WARNING, 'Could not connect to LDAP server: '.$err->getMessage()); + return false; + } + return $ldap; + } + + /** + * get an LDAP entry for a user with a given username + * + * @param string $username + * $param array $attributes LDAP attributes to retrieve + * @return string DN + */ + function ldap_get_user($username,$attributes=array()){ + $ldap = $this->ldap_get_connection(); + $filter = Net_LDAP2_Filter::create($this->attributes['username'], 'equals', $username); + $options = array( + 'scope' => 'sub', + 'attributes' => $attributes + ); + $search = $ldap->search(null,$filter,$options); + + if (PEAR::isError($search)) { + common_log(LOG_WARNING, 'Error while getting DN for user: '.$search->getMessage()); + return false; + } + + if($search->count()==0){ + return false; + }else if($search->count()==1){ + $entry = $search->shiftEntry(); + return $entry; + }else{ + common_log(LOG_WARNING, 'Found ' . $search->count() . ' ldap user with the username: ' . $username); + return false; + } + } +} diff --git a/plugins/LdapAuthentication/README b/plugins/LdapAuthentication/README new file mode 100644 index 000000000..dc3f4ba88 --- /dev/null +++ b/plugins/LdapAuthentication/README @@ -0,0 +1,76 @@ +The LDAP Authentication plugin allows for StatusNet to handle authentication +through LDAP. + +Installation +============ +add "addPlugin('ldapAuthentication', + array('setting'=>'value', 'setting2'=>'value2', ...);" +to the bottom of your config.php + +Settings +======== +provider_name*: a unique name for this authentication provider. +authoritative (false): Set to true if LDAP's responses are authoritative + (if authorative and LDAP fails, no other password checking will be done). +autoregistration (false): Set to true if users should be automatically created + when they attempt to login. +email_changeable (true): Are users allowed to change their email address? + (true or false) +password_changeable (true): Are users allowed to change their passwords? + (true or false) + +host*: LDAP server name to connect to. You can provide several hosts in an + array in which case the hosts are tried from left to right. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +port: Port on the server. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +version: LDAP version. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +starttls: TLS is started after connecting. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +binddn: The distinguished name to bind as (username). + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +bindpw: Password for the binddn. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +basedn*: LDAP base name (root directory). + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +options: See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +filter: Default search filter. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php +scope: Default search scope. + See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php + +attributes: an array that relates StatusNet user attributes to LDAP ones + username*: LDAP attribute value entered when authenticating to StatusNet + nickname*: LDAP attribute value shown as the user's nickname + email + fullname + homepage + location + +* required +default values are in (parenthesis) + +For most LDAP installations, the "nickname" and "username" attributes should + be the same. + +Example +======= +Here's an example of an LDAP plugin configuration that connects to + Microsoft Active Directory. + +addPlugin('ldapAuthentication', array( + 'provider_name'=>'Example', + 'authoritative'=>true, + 'autoregistration'=>true, + 'binddn'=>'username', + 'bindpw'=>'password', + 'basedn'=>'OU=Users,OU=StatusNet,OU=US,DC=americas,DC=global,DC=loc', + 'host'=>array('server1', 'server2'), + 'attributes'=>array( + 'username'=>'sAMAccountName', + 'nickname'=>'sAMAccountName', + 'email'=>'mail', + 'fullname'=>'displayName') +)); + diff --git a/plugins/LilUrl/LilUrlPlugin.php b/plugins/LilUrl/LilUrlPlugin.php index 7665b6c1e..e906751e8 100644 --- a/plugins/LilUrl/LilUrlPlugin.php +++ b/plugins/LilUrl/LilUrlPlugin.php @@ -31,34 +31,31 @@ if (!defined('STATUSNET')) { exit(1); } -require_once(INSTALLDIR.'/lib/Shorturl_api.php'); +require_once INSTALLDIR.'/plugins/UrlShortener/UrlShortenerPlugin.php'; -class LilUrlPlugin extends Plugin +class LilUrlPlugin extends UrlShortenerPlugin { - function __construct() - { - parent::__construct(); - } + public $serviceUrl; function onInitializePlugin(){ - $this->registerUrlShortener( - 'ur1.ca', - array('freeService'=>true), - array('LilUrl',array('http://ur1.ca/')) - ); + parent::onInitializePlugin(); + if(!isset($this->serviceUrl)){ + throw new Exception("must specify a serviceUrl"); + } } -} -class LilUrl extends ShortUrlApi -{ - protected function shorten_imp($url) { - $data['longurl'] = $url; - $response = $this->http_post($data); - if (!$response) return $url; - $y = @simplexml_load_string($response); - if (!isset($y->body)) return $url; + protected function shorten($url) { + $data = array('longurl' => $url); + + $responseBody = $this->http_post($this->serviceUrl,$data); + + if (!$responseBody) return; + $y = @simplexml_load_string($responseBody); + if (!isset($y->body)) return; $x = $y->body->p[0]->a->attributes(); - if (isset($x['href'])) return $x['href']; - return $url; + if (isset($x['href'])) { + return $x['href']; + } } } + diff --git a/plugins/LinkbackPlugin.php b/plugins/LinkbackPlugin.php index 60f7a60c7..915d15c07 100644 --- a/plugins/LinkbackPlugin.php +++ b/plugins/LinkbackPlugin.php @@ -129,18 +129,12 @@ class LinkbackPlugin extends Plugin } } - $request = xmlrpc_encode_request('pingback.ping', $args); - $context = stream_context_create(array('http' => array('method' => "POST", - 'header' => - "Content-Type: text/xml\r\n". - "User-Agent: " . $this->userAgent(), - 'content' => $request))); - $file = file_get_contents($endpoint, false, $context); - if (!$file) { - common_log(LOG_WARNING, - "Pingback request failed for '$url' ($endpoint)"); - } else { - $response = xmlrpc_decode($file); + $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): ". @@ -150,6 +144,9 @@ class LinkbackPlugin extends Plugin "Pingback success for '$url' ($endpoint): ". "'$response'"); } + } catch (HTTP_Request2_Exception $e) { + common_log(LOG_WARNING, + "Pingback request failed for '$url' ($endpoint)"); } } diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php index a933a1155..6dd8a3f5a 100644 --- a/plugins/OpenID/OpenIDPlugin.php +++ b/plugins/OpenID/OpenIDPlugin.php @@ -62,17 +62,59 @@ class OpenIDPlugin extends Plugin * @return boolean hook return */ - function onRouterInitialized(&$m) + function onStartInitializeRouter($m) { $m->connect('main/openid', array('action' => 'openidlogin')); + $m->connect('main/openidtrust', array('action' => 'openidtrust')); $m->connect('settings/openid', array('action' => 'openidsettings')); - $m->connect('xrds', array('action' => 'publicxrds')); $m->connect('index.php?action=finishopenidlogin', array('action' => 'finishopenidlogin')); $m->connect('index.php?action=finishaddopenid', array('action' => 'finishaddopenid')); - + $m->connect('main/openidserver', array('action' => 'openidserver')); + return true; } + function onEndPublicXRDS($action, &$xrdsOutputter) + { + $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + $xrdsOutputter->element('Type', null, 'xri://$xrds*simple'); + //consumer + foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) { + $xrdsOutputter->showXrdsService(Auth_OpenID_RP_RETURN_TO_URL_TYPE, + common_local_url($finish)); + } + //provider + $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/server', + common_local_url('openidserver'), + null, + null, + 'http://specs.openid.net/auth/2.0/identifier_select'); + $xrdsOutputter->elementEnd('XRD'); + } + + function onEndUserXRDS($action, &$xrdsOutputter) + { + $xrdsOutputter->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xml:id' => 'openid', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + $xrdsOutputter->element('Type', null, 'xri://$xrds*simple'); + + //consumer + $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/return_to', + common_local_url('finishopenidlogin')); + + //provider + $xrdsOutputter->showXrdsService('http://specs.openid.net/auth/2.0/signon', + common_local_url('openidserver'), + null, + null, + common_profile_url($action->user->nickname)); + $xrdsOutputter->elementEnd('XRD'); + } + function onEndLoginGroupNav(&$action) { $action_name = $action->trimmed('action'); @@ -107,11 +149,16 @@ class OpenIDPlugin extends Plugin case 'XrdsAction': case 'PublicxrdsAction': case 'OpenidsettingsAction': + case 'OpenidserverAction': + case 'OpenidtrustAction': require_once(INSTALLDIR.'/plugins/OpenID/' . strtolower(mb_substr($cls, 0, -6)) . '.php'); return false; case 'User_openid': require_once(INSTALLDIR.'/plugins/OpenID/User_openid.php'); return false; + case 'User_openid_trustroot': + require_once(INSTALLDIR.'/plugins/OpenID/User_openid_trustroot.php'); + return false; default: return true; } @@ -136,6 +183,7 @@ class OpenIDPlugin extends Plugin { case 'openidlogin': case 'finishopenidlogin': + case 'openidserver': $login = true; return false; default: @@ -150,11 +198,19 @@ class OpenIDPlugin extends Plugin * @return void */ - function onEndHeadChildren($action) + function onEndShowHeadElements($action) { - // for client side of OpenID authentication - $action->element('meta', array('http-equiv' => 'X-XRDS-Location', - 'content' => common_local_url('publicxrds'))); + if($action instanceof ShowstreamAction){ + $action->element('link', array('rel' => 'openid2.provider', + 'href' => common_local_url('openidserver'))); + $action->element('link', array('rel' => 'openid2.local_id', + 'href' => $action->profile->profileurl)); + $action->element('link', array('rel' => 'openid.server', + 'href' => common_local_url('openidserver'))); + $action->element('link', array('rel' => 'openid.delegate', + 'href' => $action->profile->profileurl)); + } + return true; } /** @@ -225,16 +281,15 @@ class OpenIDPlugin extends Plugin function onCheckSchema() { $schema = Schema::get(); - $schema->ensureTable('user_openid', - array(new ColumnDef('canonical', 'varchar', - '255', false, 'PRI'), - new ColumnDef('display', 'varchar', - '255', false), - new ColumnDef('user_id', 'integer', - null, false, 'MUL'), - new ColumnDef('created', 'datetime', - null, false), - new ColumnDef('modified', 'timestamp'))); + $schema->ensureDataObject('User_openid'); + $schema->ensureDataObject('User_openid_trustroot'); + return true; + } + + function onUserDeleteRelated($user, &$tables) + { + $tables[] = 'User_openid'; + $tables[] = 'User_openid_trustroot'; return true; } } diff --git a/plugins/OpenID/User_openid.php b/plugins/OpenID/User_openid.php index 338e0f6e9..c3624118e 100644 --- a/plugins/OpenID/User_openid.php +++ b/plugins/OpenID/User_openid.php @@ -2,9 +2,9 @@ /** * Table Definition for user_openid */ -require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; +require_once INSTALLDIR.'/classes/Plugin_DataObject.php'; -class User_openid extends Memcached_DataObject +class User_openid extends Plugin_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -33,4 +33,22 @@ class User_openid extends Memcached_DataObject return ($cnt > 0); } + + /** + * Get the TableDef object that represents the table backing this class + * @return TableDef TableDef instance + */ + function tableDef() + { + return new TableDef($this->__table, + array(new ColumnDef('canonical', 'varchar', + '255', false, 'PRI'), + new ColumnDef('display', 'varchar', + '255', false), + new ColumnDef('user_id', 'integer', + null, false, 'MUL'), + new ColumnDef('created', 'datetime', + null, false), + new ColumnDef('modified', 'timestamp'))); + } } diff --git a/plugins/OpenID/User_openid_trustroot.php b/plugins/OpenID/User_openid_trustroot.php new file mode 100644 index 000000000..b208dddfd --- /dev/null +++ b/plugins/OpenID/User_openid_trustroot.php @@ -0,0 +1,45 @@ +<?php +/** + * Table Definition for user_openid_trustroot + */ +require_once INSTALLDIR.'/classes/Plugin_DataObject.php'; + +class User_openid_trustroot extends Plugin_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'user_openid_trustroot'; // table name + public $trustroot; // varchar(255) primary_key not_null + public $user_id; // int(4) primary_key not_null + public $created; // datetime() not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('User_openid_trustroot',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('User_openid_trustroot', $kv); + } + + /** + * Get the TableDef object that represents the table backing this class + * @return TableDef TableDef instance + */ + function tableDef() + { + return new TableDef($this->__table, + array(new ColumnDef('trustroot', 'varchar', + '255', false, 'PRI'), + new ColumnDef('user_id', 'integer', + null, false, 'PRI'), + new ColumnDef('created', 'datetime', + null, false), + new ColumnDef('modified', 'timestamp'))); + } +} diff --git a/plugins/OpenID/finishopenidlogin.php b/plugins/OpenID/finishopenidlogin.php index 50a9c15c8..ff0b451d3 100644 --- a/plugins/OpenID/finishopenidlogin.php +++ b/plugins/OpenID/finishopenidlogin.php @@ -265,7 +265,7 @@ class FinishopenidloginAction extends Action $fullname = ''; } - if (!empty($sreg['email']) && Validate::email($sreg['email'], true)) { + if (!empty($sreg['email']) && Validate::email($sreg['email'], common_config('email', 'check_domain'))) { $email = $sreg['email']; } else { $email = ''; diff --git a/plugins/OpenID/openid.php b/plugins/OpenID/openid.php index 0944117c0..dd628e773 100644 --- a/plugins/OpenID/openid.php +++ b/plugins/OpenID/openid.php @@ -23,6 +23,7 @@ require_once(INSTALLDIR.'/plugins/OpenID/User_openid.php'); require_once('Auth/OpenID.php'); require_once('Auth/OpenID/Consumer.php'); +require_once('Auth/OpenID/Server.php'); require_once('Auth/OpenID/SReg.php'); require_once('Auth/OpenID/MySQLStore.php'); @@ -50,6 +51,13 @@ function oid_consumer() return $consumer; } +function oid_server() +{ + $store = oid_store(); + $server = new Auth_OpenID_Server($store, common_local_url('openidserver')); + return $server; +} + function oid_clear_last() { oid_set_last(''); @@ -241,7 +249,7 @@ function oid_update_user(&$user, &$sreg) $orig_user = clone($user); - if ($sreg['email'] && Validate::email($sreg['email'], true)) { + if ($sreg['email'] && Validate::email($sreg['email'], common_config('email', 'check_domain'))) { $user->email = $sreg['email']; } @@ -272,6 +280,11 @@ class AutosubmitAction extends Action function showContent() { $this->raw($this->form_html); + } + + function showScripts() + { + parent::showScripts(); $this->element('script', null, '$(document).ready(function() { ' . ' $(\'#'. $this->form_id .'\').submit(); '. diff --git a/plugins/OpenID/openidserver.php b/plugins/OpenID/openidserver.php new file mode 100644 index 000000000..dab97c93e --- /dev/null +++ b/plugins/OpenID/openidserver.php @@ -0,0 +1,151 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Settings for OpenID + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Settings + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2008-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') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/action.php'; +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; +require_once(INSTALLDIR.'/plugins/OpenID/User_openid_trustroot.php'); + +/** + * Settings for OpenID + * + * Lets users add, edit and delete OpenIDs from their account + * + * @category Settings + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class OpenidserverAction extends Action +{ + var $oserver; + + function prepare($args) + { + parent::prepare($args); + $this->oserver = oid_server(); + return true; + } + + function handle($args) + { + parent::handle($args); + $request = $this->oserver->decodeRequest(); + if (in_array($request->mode, array('checkid_immediate', + 'checkid_setup'))) { + $user = common_current_user(); + if(!$user){ + if($request->immediate){ + //cannot prompt the user to login in immediate mode, so answer false + $response = $this->generateDenyResponse($request); + }else{ + /* Go log in, and then come back. */ + common_set_returnto($_SERVER['REQUEST_URI']); + common_redirect(common_local_url('login')); + return; + } + }else if(common_profile_url($user->nickname) == $request->identity || $request->idSelect()){ + $user_openid_trustroot = User_openid_trustroot::pkeyGet( + array('user_id'=>$user->id, 'trustroot'=>$request->trust_root)); + if(empty($user_openid_trustroot)){ + if($request->immediate){ + //cannot prompt the user to trust this trust root in immediate mode, so answer false + $response = $this->generateDenyResponse($request); + }else{ + common_ensure_session(); + $_SESSION['openid_trust_root'] = $request->trust_root; + $allowResponse = $this->generateAllowResponse($request, $user); + $this->oserver->encodeResponse($allowResponse); //sign the response + $denyResponse = $this->generateDenyResponse($request); + $this->oserver->encodeResponse($denyResponse); //sign the response + $_SESSION['openid_allow_url'] = $allowResponse->encodeToUrl(); + $_SESSION['openid_deny_url'] = $denyResponse->encodeToUrl(); + //ask the user to trust this trust root + common_redirect(common_local_url('openidtrust')); + return; + } + }else{ + //user has previously authorized this trust root + $response = $this->generateAllowResponse($request, $user); + //$response = $request->answer(true, null, common_profile_url($user->nickname)); + } + } else if ($request->immediate) { + $response = $this->generateDenyResponse($request); + } else { + //invalid + $this->clientError(sprintf(_('You are not authorized to use the identity %s'),$request->identity),$code=403); + } + } else { + $response = $this->oserver->handleRequest($request); + } + + if($response){ + $response = $this->oserver->encodeResponse($response); + if ($response->code != AUTH_OPENID_HTTP_OK) { + header(sprintf("HTTP/1.1 %d ", $response->code), + true, $response->code); + } + + if($response->headers){ + foreach ($response->headers as $k => $v) { + header("$k: $v"); + } + } + $this->raw($response->body); + }else{ + $this->clientError(_('Just an OpenID provider. Nothing to see here, move along...'),$code=500); + } + } + + function generateAllowResponse($request, $user){ + $response = $request->answer(true, null, common_profile_url($user->nickname)); + + $profile = $user->getProfile(); + $sreg_data = array( + 'fullname' => $profile->fullname, + 'nickname' => $user->nickname, + 'email' => $user->email, + 'language' => $user->language, + 'timezone' => $user->timezone); + $sreg_request = Auth_OpenID_SRegRequest::fromOpenIDRequest($request); + $sreg_response = Auth_OpenID_SRegResponse::extractResponse( + $sreg_request, $sreg_data); + $sreg_response->toMessage($response->fields); + return $response; + } + + function generateDenyResponse($request){ + $response = $request->answer(false); + return $response; + } +} diff --git a/plugins/OpenID/openidtrust.php b/plugins/OpenID/openidtrust.php new file mode 100644 index 000000000..29c7bdc23 --- /dev/null +++ b/plugins/OpenID/openidtrust.php @@ -0,0 +1,142 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/plugins/OpenID/openid.php'; +require_once(INSTALLDIR.'/plugins/OpenID/User_openid_trustroot.php'); + +class OpenidtrustAction extends Action +{ + var $trust_root; + var $allowUrl; + var $denyUrl; + var $user; + + /** + * Is this a read-only action? + * + * @return boolean false + */ + + function isReadOnly($args) + { + return false; + } + + /** + * Title of the page + * + * @return string title of the page + */ + + function title() + { + return _('OpenID Identity Verification'); + } + + function prepare($args) + { + parent::prepare($args); + common_ensure_session(); + $this->user = common_current_user(); + if(empty($this->user)){ + /* Go log in, and then come back. */ + common_set_returnto($_SERVER['REQUEST_URI']); + common_redirect(common_local_url('login')); + return; + } + $this->trust_root = $_SESSION['openid_trust_root']; + $this->allowUrl = $_SESSION['openid_allow_url']; + $this->denyUrl = $_SESSION['openid_deny_url']; + if(empty($this->trust_root) || empty($this->allowUrl) || empty($this->denyUrl)){ + $this->clientError(_('This page should only be reached during OpenID processing, not directly.')); + return; + } + return true; + } + + function handle($args) + { + parent::handle($args); + if($_SERVER['REQUEST_METHOD'] == 'POST'){ + $this->handleSubmit(); + }else{ + $this->showPage(); + } + } + + function handleSubmit() + { + unset($_SESSION['openid_trust_root']); + unset($_SESSION['openid_allow_url']); + unset($_SESSION['openid_deny_url']); + if($this->arg('allow')) + { + //save to database + $user_openid_trustroot = new User_openid_trustroot(); + $user_openid_trustroot->user_id = $this->user->id; + $user_openid_trustroot->trustroot = $this->trust_root; + $user_openid_trustroot->created = DB_DataObject_Cast::dateTime(); + if (!$user_openid_trustroot->insert()) { + $err = PEAR::getStaticProperty('DB_DataObject','lastError'); + common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__); + } + common_redirect($this->allowUrl, $code=302); + }else{ + common_redirect($this->denyUrl, $code=302); + } + } + + /** + * Show page notice + * + * Display a notice for how to use the page, or the + * error if it exists. + * + * @return void + */ + + function showPageNotice() + { + $this->element('p',null,sprintf(_('%s has asked to verify your identity. Click Continue to verify your identity and login without creating a new password.'),$this->trust_root)); + } + + /** + * Core of the display code + * + * Shows the login form. + * + * @return void + */ + + function showContent() + { + $this->elementStart('form', array('method' => 'post', + 'id' => 'form_openidtrust', + 'class' => 'form_settings', + 'action' => common_local_url('openidtrust'))); + $this->elementStart('fieldset'); + $this->submit('allow', _('Continue')); + $this->submit('deny', _('Cancel')); + + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + } +} diff --git a/plugins/OpenID/publicxrds.php b/plugins/OpenID/publicxrds.php deleted file mode 100644 index 1b2b359ca..000000000 --- a/plugins/OpenID/publicxrds.php +++ /dev/null @@ -1,122 +0,0 @@ -<?php - -/** - * Public XRDS for OpenID - * - * PHP version 5 - * - * @category Action - * @package StatusNet - * @author Evan Prodromou <evan@status.net> - * @author Robin Millette <millette@status.net> - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ - * - * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2008, 2009, StatusNet, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR.'/plugins/OpenID/openid.php'; - -/** - * Public XRDS for OpenID - * - * @category Action - * @package StatusNet - * @author Evan Prodromou <evan@status.net> - * @author Robin Millette <millette@status.net> - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ - * - * @todo factor out similarities with XrdsAction - */ -class PublicxrdsAction extends Action -{ - /** - * Is read only? - * - * @return boolean true - */ - function isReadOnly($args) - { - return true; - } - - /** - * Class handler. - * - * @param array $args array of arguments - * - * @return nothing - */ - function handle($args) - { - parent::handle($args); - header('Content-Type: application/xrds+xml'); - $this->startXML(); - $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds')); - $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', - 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', - 'version' => '2.0')); - $this->element('Type', null, 'xri://$xrds*simple'); - foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) { - $this->showService(Auth_OpenID_RP_RETURN_TO_URL_TYPE, - common_local_url($finish)); - } - $this->elementEnd('XRD'); - $this->elementEnd('XRDS'); - $this->endXML(); - } - - /** - * Show service. - * - * @param string $type XRDS type - * @param string $uri URI - * @param array $params type parameters, null by default - * @param array $sigs type signatures, null by default - * @param string $localId local ID, null by default - * - * @return void - */ - function showService($type, $uri, $params=null, $sigs=null, $localId=null) - { - $this->elementStart('Service'); - if ($uri) { - $this->element('URI', null, $uri); - } - $this->element('Type', null, $type); - if ($params) { - foreach ($params as $param) { - $this->element('Type', null, $param); - } - } - if ($sigs) { - foreach ($sigs as $sig) { - $this->element('Type', null, $sig); - } - } - if ($localId) { - $this->element('LocalID', null, $localId); - } - $this->elementEnd('Service'); - } -} - diff --git a/plugins/PtitUrl/PtitUrlPlugin.php b/plugins/PtitUrl/PtitUrlPlugin.php index f00d3e2f2..ef453e96d 100644 --- a/plugins/PtitUrl/PtitUrlPlugin.php +++ b/plugins/PtitUrl/PtitUrlPlugin.php @@ -30,33 +30,28 @@ if (!defined('STATUSNET')) { exit(1); } +require_once INSTALLDIR.'/plugins/UrlShortener/UrlShortenerPlugin.php'; -class PtitUrlPlugin extends Plugin +class PtitUrlPlugin extends UrlShortenerPlugin { - function __construct() - { - parent::__construct(); - } + public $serviceUrl; function onInitializePlugin(){ - $this->registerUrlShortener( - 'ptiturl.com', - array(), - array('PtitUrl',array('http://ptiturl.com/?creer=oui&action=Reduire&url=')) - ); + parent::onInitializePlugin(); + if(!isset($this->serviceUrl)){ + throw new Exception("must specify a serviceUrl"); + } } -} -class PtitUrl extends ShortUrlApi -{ - protected function shorten_imp($url) { - $response = $this->http_get($url); - if (!$response) return $url; + protected function shorten($url) + { + $response = $this->http_get(sprintf($this->serviceUrl,urlencode($url))); + if (!$response) return; $response = $this->tidy($response); $y = @simplexml_load_string($response); - if (!isset($y->body)) return $url; + if (!isset($y->body)) return; $xml = $y->body->center->table->tr->td->pre->a->attributes(); if (isset($xml['href'])) return $xml['href']; - return $url; } } + diff --git a/plugins/PubSubHubBub/PubSubHubBubPlugin.php b/plugins/PubSubHubBub/PubSubHubBubPlugin.php index e1e82e352..d15a869cb 100644 --- a/plugins/PubSubHubBub/PubSubHubBubPlugin.php +++ b/plugins/PubSubHubBub/PubSubHubBubPlugin.php @@ -65,21 +65,21 @@ class PubSubHubBubPlugin extends Plugin $feeds = array(); //public timeline feeds - $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'public_timeline.rss')); - $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'public_timeline.atom')); + $feeds[]=common_local_url('ApiTimelinePublic',array('format' => 'rss')); + $feeds[]=common_local_url('ApiTimelinePublic',array('format' => 'atom')); //author's own feeds $user = User::staticGet('id',$notice->profile_id); - $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss')); - $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom')); + $feeds[]=common_local_url('ApiTimelineUser',array('id' => $user->nickname, 'format'=>'rss')); + $feeds[]=common_local_url('ApiTimelineUser',array('id' => $user->nickname, 'format'=>'atom')); //tag feeds $tag = new Notice_tag(); $tag->notice_id = $notice->id; if ($tag->find()) { while ($tag->fetch()) { - $feeds[]=common_local_url('api',array('apiaction' => 'tags','method' => 'timeline', 'argument'=>$tag->tag.'.atom')); - $feeds[]=common_local_url('api',array('apiaction' => 'tags','method' => 'timeline', 'argument'=>$tag->tag.'.rss')); + $feeds[]=common_local_url('ApiTimelineTag',array('tag'=>$tag->tag, 'format'=>'rss')); + $feeds[]=common_local_url('ApiTimelineTag',array('tag'=>$tag->tag, 'format'=>'atom')); } } @@ -89,8 +89,8 @@ class PubSubHubBubPlugin extends Plugin if ($group_inbox->find()) { while ($group_inbox->fetch()) { $group = User_group::staticGet('id',$group_inbox->group_id); - $feeds[]=common_local_url('api',array('apiaction' => 'groups','method' => 'timeline','argument' => $group->nickname.'.rss')); - $feeds[]=common_local_url('api',array('apiaction' => 'groups','method' => 'timeline','argument' => $group->nickname.'.atom')); + $feeds[]=common_local_url('ApiTimelineGroup',array('id' => $group->nickname,'format'=>'rss')); + $feeds[]=common_local_url('ApiTimelineGroup',array('id' => $group->nickname,'format'=>'atom')); } } @@ -100,18 +100,17 @@ class PubSubHubBubPlugin extends Plugin if ($notice_inbox->find()) { while ($notice_inbox->fetch()) { $user = User::staticGet('id',$notice_inbox->user_id); - $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss')); - $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom')); + $feeds[]=common_local_url('ApiTimelineUser',array('id' => $user->nickname, 'format'=>'rss')); + $feeds[]=common_local_url('ApiTimelineUser',array('id' => $user->nickname, 'format'=>'atom')); } } - /* TODO: when the reply page gets RSS and ATOM feeds, implement this //feed of user replied to if($notice->reply_to){ $user = User::staticGet('id',$notice->reply_to); - $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.rss')); - $feeds[]=common_local_url('api',array('apiaction' => 'statuses','method' => 'user_timeline','argument' => $user->nickname.'.atom')); - }*/ + $feeds[]=common_local_url('ApiTimelineMentions',array('id' => $user->nickname,'format'=>'rss')); + $feeds[]=common_local_url('ApiTimelineMentions',array('id' => $user->nickname,'format'=>'atom')); + } foreach(array_unique($feeds) as $feed){ if(! $publisher->publish_update($feed)){ diff --git a/plugins/Realtime/realtimeupdate.js b/plugins/Realtime/realtimeupdate.js index 9371326fe..9b9991b9e 100644 --- a/plugins/Realtime/realtimeupdate.js +++ b/plugins/Realtime/realtimeupdate.js @@ -1,5 +1,31 @@ -// add a notice encoded as JSON into the current timeline -// +/* + * StatusNet - a distributed open-source microblogging tool + * Copyright (C) 2008, StatusNet, Inc. + * + * Add a notice encoded as JSON into the current timeline + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @author Sarven Capadisli <csarven@status.net> + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + // TODO: i18n RealtimeUpdate = { @@ -19,15 +45,9 @@ RealtimeUpdate = { DT = document.title; $(window).blur(function() { - $('#notices_primary .notice').css({ - 'border-top-color':$('#notices_primary .notice:last').css('border-top-color'), - 'border-top-style':'dotted' - }); + $('#notices_primary .notice').removeClass('mark-top'); - $('#notices_primary .notice:first').css({ - 'border-top-color':'#AAAAAA', - 'border-top-style':'solid' - }); + $('#notices_primary .notice:first').addClass('mark-top'); RealtimeUpdate._updatecounter = 0; document.title = DT; @@ -50,7 +70,7 @@ RealtimeUpdate = { $("#notices_primary .notices").prepend(noticeItem); $("#notices_primary .notice:first").css({display:"none"}); $("#notices_primary .notice:first").fadeIn(1000); - NoticeReply(); + SN.U.NoticeReply(); RealtimeUpdate._updatecounter += 1; document.title = '('+RealtimeUpdate._updatecounter+') ' + DT; @@ -137,12 +157,14 @@ RealtimeUpdate = { addPopup: function(url, timeline, iconurl) { - $('#notices_primary').css({'position':'relative'}); - $('#notices_primary').prepend('<button id="realtime_timeline" title="Pop up in a window">Pop up</button>'); + var NP = $('#notices_primary'); + NP.css({'position':'relative'}); + NP.prepend('<button id="realtime_timeline" title="Pop up in a window">Pop up</button>'); - $('#realtime_timeline').css({ + var RT = $('#realtime_timeline'); + RT.css({ 'margin':'0 0 11px 0', - 'background':'transparent url('+ iconurl + ') no-repeat 0% 30%', + 'background':'transparent url('+ iconurl + ') no-repeat 0 30%', 'padding':'0 0 0 20px', 'display':'block', 'position':'absolute', @@ -150,15 +172,16 @@ RealtimeUpdate = { 'right':'0', 'border':'none', 'cursor':'pointer', - 'color':$("a").css("color"), + 'color':$('a').css('color'), 'font-weight':'bold', 'font-size':'1em' }); + $('#showstream #notices_primary').css({'margin-top':'18px'}); - $('#realtime_timeline').click(function() { + RT.click(function() { window.open(url, timeline, - 'toolbar=no,resizable=yes,scrollbars=yes,status=yes'); + 'toolbar=no,resizable=yes,scrollbars=yes,status=yes,width=500,height=550'); return false; }); @@ -166,7 +189,6 @@ RealtimeUpdate = { initPopupWindow: function() { - window.resizeTo(500, 550); $('address').hide(); $('#content').css({'width':'93.5%'}); diff --git a/plugins/recaptcha/LICENSE b/plugins/Recaptcha/LICENSE index b612f71f0..b612f71f0 100644 --- a/plugins/recaptcha/LICENSE +++ b/plugins/Recaptcha/LICENSE diff --git a/plugins/Recaptcha/README b/plugins/Recaptcha/README new file mode 100644 index 000000000..49c4b9c27 --- /dev/null +++ b/plugins/Recaptcha/README @@ -0,0 +1,28 @@ +StatusNet reCAPTCHA plugin 0.3 11/16/09 +======================================= +Adds a captcha to your registration page to reduce automated spam bots registering. + +Use: +1. Get an API key from http://recaptcha.net + +2. In config.php add: +addPlugin('recaptcha', array('private_key' => 'YourKeyHere', + 'public_key' => 'ReplaceWithYourKey')); +or +addPlugin('recaptcha', array('private_key' => 'YourKeyHere', + 'public_key' => 'ReplaceWithYourKey', + 'display_errors' => true)); +Changelog +========= +0.1 initial release +0.2 Work around for webkit browsers +0.3 Moved to new plugin arch for SN + **YOU WILL NEED TO CHANGE YOUR CONFIG.PHP!** + +reCAPTCHA Lib README +==================== + +The reCAPTCHA PHP Lirary helps you use the reCAPTCHA API. Documentation +for this library can be found at + + http://recaptcha.net/plugins/php diff --git a/plugins/recaptcha/recaptcha.php b/plugins/Recaptcha/RecaptchaPlugin.php index 94cf0ccd1..1a51b16be 100644 --- a/plugins/recaptcha/recaptcha.php +++ b/plugins/Recaptcha/RecaptchaPlugin.php @@ -33,7 +33,9 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { define('RECAPTCHA', '0.2'); -class recaptcha extends Plugin +require_once(INSTALLDIR.'/plugins/Recaptcha/recaptchalib.php'); + +class RecaptchaPlugin extends Plugin { var $private_key; var $public_key; @@ -41,13 +43,13 @@ class recaptcha extends Plugin var $failed; var $ssl; - function __construct($public_key, $private_key, $display_errors=false) - { - parent::__construct(); - require_once(INSTALLDIR.'/plugins/recaptcha/recaptchalib.php'); - $this->public_key = $public_key; - $this->private_key = $private_key; - $this->display_errors = $display_errors; + function onInitializePlugin(){ + if(!isset($this->private_key)){ + common_log(LOG_ERR, "Recaptcha: Must specify private_key in config.php"); + } + if(!isset($this->public_key)){ + common_log(LOG_ERR, "Recaptcha: Must specify public_key in config.php"); + } } function checkssl(){ diff --git a/plugins/recaptcha/recaptchalib.php b/plugins/Recaptcha/recaptchalib.php index 897c50981..897c50981 100644 --- a/plugins/recaptcha/recaptchalib.php +++ b/plugins/Recaptcha/recaptchalib.php diff --git a/plugins/ReverseUsernameAuthentication/README b/plugins/ReverseUsernameAuthentication/README new file mode 100644 index 000000000..e9160ed9b --- /dev/null +++ b/plugins/ReverseUsernameAuthentication/README @@ -0,0 +1,26 @@ +The Reverse Username Authentication plugin allows for StatusNet to handle authentication by checking if the provided password is the same as the reverse of the username. + +THIS PLUGIN IS FOR TESTING PURPOSES ONLY + +Installation +============ +add "addPlugin('reverseUsernameAuthentication', array('setting'=>'value', 'setting2'=>'value2', ...);" to the bottom of your config.php + +Settings +======== +provider_name*: a unique name for this authentication provider. +password_changeable*: must be set to false. This plugin does not support changing passwords. +authoritative (false): Set to true if this plugin's responses are authoritative (meaning if this fails, do check any other plugins or the internal password database). +autoregistration (false): Set to true if users should be automatically created when they attempt to login. + +* required +default values are in (parenthesis) + +Example +======= +addPlugin('reverseUsernameAuthentication', array( + 'provider_name'=>'Example', + 'password_changeable'=>false, + 'authoritative'=>true, + 'autoregistration'=>true +)); diff --git a/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php b/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php new file mode 100644 index 000000000..d48283b2e --- /dev/null +++ b/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php @@ -0,0 +1,58 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Plugin that checks if the password is the reverse of username + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @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') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/plugins/Authentication/AuthenticationPlugin.php'; + +class ReverseUsernameAuthenticationPlugin extends AuthenticationPlugin +{ + //---interface implementation---// + + function onInitializePlugin(){ + parent::onInitializePlugin(); + if(!isset($this->password_changeable) && $this->password_changeable){ + throw new Exception("password_changeable cannot be set to true. This plugin does not support changing passwords."); + } + } + + function checkPassword($username, $password) + { + return $username == strrev($password); + } + + function autoRegister($username) + { + $registration_data = array(); + $registration_data['nickname'] = $username ; + return User::register($registration_data); + } +} diff --git a/plugins/SimpleUrl/SimpleUrlPlugin.php b/plugins/SimpleUrl/SimpleUrlPlugin.php index 82d772048..45b745b07 100644 --- a/plugins/SimpleUrl/SimpleUrlPlugin.php +++ b/plugins/SimpleUrl/SimpleUrlPlugin.php @@ -31,49 +31,21 @@ if (!defined('STATUSNET')) { exit(1); } -class SimpleUrlPlugin extends Plugin +require_once INSTALLDIR.'/plugins/UrlShortener/UrlShortenerPlugin.php'; + +class SimpleUrlPlugin extends UrlShortenerPlugin { - function __construct() - { - parent::__construct(); - } + public $serviceUrl; function onInitializePlugin(){ - $this->registerUrlShortener( - 'is.gd', - array(), - array('SimpleUrl',array('http://is.gd/api.php?longurl=')) - ); - $this->registerUrlShortener( - 'snipr.com', - array(), - array('SimpleUrl',array('http://snipr.com/site/snip?r=simple&link=')) - ); - $this->registerUrlShortener( - 'metamark.net', - array(), - array('SimpleUrl',array('http://metamark.net/api/rest/simple?long_url=')) - ); - $this->registerUrlShortener( - 'tinyurl.com', - array(), - array('SimpleUrl',array('http://tinyurl.com/api-create.php?url=')) - ); + parent::onInitializePlugin(); + if(!isset($this->serviceUrl)){ + throw new Exception("must specify a serviceUrl"); + } } -} -class SimpleUrl extends ShortUrlApi -{ - protected function shorten_imp($url) { - $curlh = curl_init(); - curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait - curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet'); - curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true); - - curl_setopt($curlh, CURLOPT_URL, $this->service_url.urlencode($url)); - $short_url = curl_exec($curlh); - - curl_close($curlh); - return $short_url; + protected function shorten($url) { + return $this->http_get(sprintf($this->serviceUrl,urlencode($url))); } } + diff --git a/plugins/SphinxSearch/README b/plugins/SphinxSearch/README new file mode 100644 index 000000000..5a2c063bd --- /dev/null +++ b/plugins/SphinxSearch/README @@ -0,0 +1,45 @@ +You can get a significant boost in performance using Sphinx Search +instead of your database server to search for users and notices. +<http://sphinxsearch.com/>. + +Configuration +------------- + +In StatusNet's configuration, you can adjust the following settings +under 'sphinx': + +enabled: Set to true to enable. Default false. +server: a string with the hostname of the sphinx server. +port: an integer with the port number of the sphinx server. + + +Requirements +------------ + +To use a Sphinx server to search users and notices, you also need +to install, compile and enable the sphinx pecl extension for php on the +client side, which itself depends on the sphinx development files. +"pecl install sphinx" should take care of that. Add "extension=sphinx.so" +to your php.ini and reload apache to enable it. + +You can update your MySQL or Postgresql databases to drop their fulltext +search indexes, since they're now provided by sphinx. + + +You will also need a Sphinx server to serve the search queries. + +On the sphinx server side, a script reads the main database and build +the keyword index. A cron job reads the database and keeps the sphinx +indexes up to date. scripts/sphinx-cron.sh should be called by cron +every 5 minutes, for example. scripts/sphinx.sh is an init.d script +to start and stop the sphinx search daemon. + + +Server configuration +-------------------- +scripts/gen_config.php can generate a sphinx.conf file listing MySQL +data sources for your databases. You may need to tweak paths afterwards. + + $ plugins/SphinxSearch/scripts/gen_config.php > sphinx.conf + +If you wish, you can build a full config yourself based on sphinx.conf.sample diff --git a/plugins/SphinxSearch/SphinxSearchPlugin.php b/plugins/SphinxSearch/SphinxSearchPlugin.php new file mode 100644 index 000000000..7a27a4c04 --- /dev/null +++ b/plugins/SphinxSearch/SphinxSearchPlugin.php @@ -0,0 +1,100 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Brion Vibber <brion@status.net> + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +// Set defaults if not already set in the config array... +global $config; +$sphinxDefaults = + array('enabled' => true, + 'server' => 'localhost', + 'port' => 3312); +foreach($sphinxDefaults as $key => $val) { + if (!isset($config['sphinx'][$key])) { + $config['sphinx'][$key] = $val; + } +} + + + +/** + * Plugin for Sphinx search backend. + * + * @category Plugin + * @package StatusNet + * @author Brion Vibber <brion@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * @link http://twitter.com/ + */ + +class SphinxSearchPlugin extends Plugin +{ + /** + * Automatically load any classes used + * + * @param string $cls the class + * @return boolean hook return + */ + function onAutoload($cls) + { + switch ($cls) { + case 'SphinxSearch': + include_once INSTALLDIR . '/plugins/SphinxSearch/' . + strtolower($cls) . '.php'; + return false; + default: + return true; + } + } + + /** + * Create sphinx search engine object for the given table type. + * + * @param Memcached_DataObject $target + * @param string $table + * @param out &$search_engine SearchEngine object on output if successful + * @ return boolean hook return + */ + function onGetSearchEngine(Memcached_DataObject $target, $table, &$search_engine) + { + if (common_config('sphinx', 'enabled')) { + if (!class_exists('SphinxClient')) { + throw new ServerException('Sphinx PHP extension must be installed.'); + } + $engine = new SphinxSearch($target, $table); + if ($engine->is_connected()) { + $search_engine = $engine; + return false; + } + } + // Sphinx disabled or disconnected + return true; + } +} diff --git a/plugins/SphinxSearch/scripts/gen_config.php b/plugins/SphinxSearch/scripts/gen_config.php new file mode 100755 index 000000000..d5a00b6b6 --- /dev/null +++ b/plugins/SphinxSearch/scripts/gen_config.php @@ -0,0 +1,126 @@ +#!/usr/bin/env php +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); + +$longoptions = array('base=', 'network'); + +$helptext = <<<END_OF_TRIM_HELP +Generates sphinx.conf file based on StatusNet configuration. + --base Base dir to Sphinx install + (default /usr/local) + --network Use status_network global config table + (non-functional at present) + + +END_OF_TRIM_HELP; + +require_once INSTALLDIR . '/scripts/commandline.inc'; +require dirname(__FILE__) . '/sphinx-utils.php'; + + +$timestamp = date('r'); +print <<<END +# +# Sphinx configuration for StatusNet +# Generated {$timestamp} +# + +END; + +sphinx_iterate_sites('sphinx_site_template'); + +print <<<END + +indexer +{ + mem_limit = 300M +} + +searchd +{ + port = 3312 + log = {$base}/log/searchd.log + query_log = {$base}/log/query.log + read_timeout = 5 + max_children = 30 + pid_file = {$base}/log/searchd.pid + max_matches = 1000 + seamless_rotate = 1 + preopen_indexes = 0 + unlink_old = 1 +} + +END; + + + +/** + * Build config entries for a single site + * @fixme we only seem to have master DB currently available... + */ +function sphinx_site_template($sn) +{ + return + sphinx_template($sn, + 'profile', + 'SELECT id, UNIX_TIMESTAMP(created) as created_ts, nickname, fullname, location, bio, homepage FROM profile', + 'SELECT * FROM profile where id = $id') . + sphinx_template($sn, + 'notice', + 'SELECT id, UNIX_TIMESTAMP(created) as created_ts, content FROM notice', + 'SELECT * FROM notice where notice.id = $id AND notice.is_local != -2'); +} + +function sphinx_template($sn, $table, $query, $query_info) +{ + $base = sphinx_base(); + $dbtype = common_config('db', 'type'); + + print <<<END + +# +# {$sn->sitename} +# +source {$sn->dbname}_src_{$table} +{ + type = {$dbtype} + sql_host = {$sn->dbhost} + sql_user = {$sn->dbuser} + sql_pass = {$sn->dbpass} + sql_db = {$sn->dbname} + sql_query_pre = SET NAMES utf8; + sql_query = {$query} + sql_query_info = {$query_info} + sql_attr_timestamp = created_ts +} + +index {$sn->dbname}_{$table} +{ + source = {$sn->dbname}_src_{$table} + path = {$base}/data/{$sn->dbname}_{$table} + docinfo = extern + charset_type = utf-8 + min_word_len = 3 +} + + +END; +} diff --git a/plugins/SphinxSearch/scripts/index_update.php b/plugins/SphinxSearch/scripts/index_update.php new file mode 100755 index 000000000..23c60ced7 --- /dev/null +++ b/plugins/SphinxSearch/scripts/index_update.php @@ -0,0 +1,61 @@ +#!/usr/bin/env php +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); + +$longoptions = array('base=', 'network'); + +$helptext = <<<END_OF_TRIM_HELP +Runs Sphinx search indexer. + --rotate Have Sphinx run index update in background and + rotate updated indexes into place as they finish. + --base Base dir to Sphinx install + (default /usr/local) + --network Use status_network global config table for site list + (non-functional at present) + + +END_OF_TRIM_HELP; + +require_once INSTALLDIR . '/scripts/commandline.inc'; +require dirname(__FILE__) . '/sphinx-utils.php'; + +sphinx_iterate_sites('sphinx_index_update'); + +function sphinx_index_update($sn) +{ + $base = sphinx_base(); + + $baseIndexes = array('notice', 'profile'); + $params = array(); + + if (have_option('rotate')) { + $params[] = '--rotate'; + } + foreach ($baseIndexes as $index) { + $params[] = "{$sn->dbname}_{$index}"; + } + + $params = implode(' ', $params); + $cmd = "$base/bin/indexer --config $base/etc/sphinx.conf $params"; + + print "$cmd\n"; + system($cmd); +} diff --git a/plugins/SphinxSearch/scripts/sphinx-utils.php b/plugins/SphinxSearch/scripts/sphinx-utils.php new file mode 100644 index 000000000..7bbc25270 --- /dev/null +++ b/plugins/SphinxSearch/scripts/sphinx-utils.php @@ -0,0 +1,63 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +function sphinx_use_network() +{ + return have_option('network'); +} + +function sphinx_base() +{ + if (have_option('base')) { + return get_option_value('base'); + } else { + return "/usr/local/sphinx"; + } +} + +function sphinx_iterate_sites($callback) +{ + if (sphinx_use_network()) { + // @fixme this should use, like, some kind of config + Status_network::setupDB('localhost', 'statusnet', 'statuspass', 'statusnet'); + $sn = new Status_network(); + if (!$sn->find()) { + die("Confused... no sites in status_network table or lookup failed.\n"); + } + while ($sn->fetch()) { + $callback($sn); + } + } else { + if (preg_match('!^(mysqli?|pgsql)://(.*?):(.*?)@(.*?)/(.*?)$!', + common_config('db', 'database'), $matches)) { + list(/*all*/, $dbtype, $dbuser, $dbpass, $dbhost, $dbname) = $matches; + $sn = (object)array( + 'sitename' => common_config('site', 'name'), + 'dbhost' => $dbhost, + 'dbuser' => $dbuser, + 'dbpass' => $dbpass, + 'dbname' => $dbname); + $callback($sn); + } else { + print "Unrecognized database configuration string in config.php\n"; + exit(1); + } + } +} + diff --git a/plugins/SphinxSearch/scripts/sphinx.sh b/plugins/SphinxSearch/scripts/sphinx.sh new file mode 100755 index 000000000..b8edeb302 --- /dev/null +++ b/plugins/SphinxSearch/scripts/sphinx.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [[ $1 = "start" ]] +then + echo "Stopping any running daemons..." + /usr/local/bin/searchd --config /usr/local/etc/sphinx.conf --stop 2> /dev/null + echo "Starting sphinx search daemon..." + /usr/local/bin/searchd --config /usr/local/etc/sphinx.conf 2> /dev/null +fi + +if [[ $1 = "stop" ]] +then + echo "Stopping sphinx search daemon..." + /usr/local/bin/searchd --config /usr/local/etc/sphinx.conf --stop 2> /dev/null +fi diff --git a/plugins/SphinxSearch/sphinx.conf.sample b/plugins/SphinxSearch/sphinx.conf.sample new file mode 100644 index 000000000..3de62f637 --- /dev/null +++ b/plugins/SphinxSearch/sphinx.conf.sample @@ -0,0 +1,71 @@ +# +# Minimal Sphinx configuration sample for statusnet +# + +source src1 +{ + type = mysql + sql_host = localhost + sql_user = USERNAME + sql_pass = PASSWORD + sql_db = identi_ca + sql_port = 3306 + sql_query = SELECT id, UNIX_TIMESTAMP(created) as created_ts, nickname, fullname, location, bio, homepage FROM profile + sql_query_info = SELECT * FROM profile where id = $id + sql_attr_timestamp = created_ts +} + + +source src2 +{ + type = mysql + sql_host = localhost + sql_user = USERNAME + sql_pass = PASSWORD + sql_db = identi_ca + sql_port = 3306 + sql_query = SELECT id, UNIX_TIMESTAMP(created) as created_ts, content FROM notice + sql_query_info = SELECT * FROM notice where notice.id = $id AND notice.is_local != -2 + sql_attr_timestamp = created_ts +} + +index identica_notices +{ + source = src2 + path = DIRECTORY/data/identica_notices + docinfo = extern + charset_type = utf-8 + min_word_len = 3 + stopwords = DIRECTORY/data/stopwords-en.txt +} + + +index identica_people +{ + source = src1 + path = DIRECTORY/data/identica_people + docinfo = extern + charset_type = utf-8 + min_word_len = 3 + stopwords = DIRECTORY/data/stopwords-en.txt +} + +indexer +{ + mem_limit = 32M +} + +searchd +{ + port = 3312 + log = DIRECTORY/log/searchd.log + query_log = DIRECTORY/log/query.log + read_timeout = 5 + max_children = 30 + pid_file = DIRECTORY/log/searchd.pid + max_matches = 1000 + seamless_rotate = 1 + preopen_indexes = 0 + unlink_old = 1 +} + diff --git a/plugins/SphinxSearch/sphinxsearch.php b/plugins/SphinxSearch/sphinxsearch.php new file mode 100644 index 000000000..71f330828 --- /dev/null +++ b/plugins/SphinxSearch/sphinxsearch.php @@ -0,0 +1,96 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +class SphinxSearch extends SearchEngine +{ + private $sphinx; + private $connected; + + function __construct($target, $table) + { + $fp = @fsockopen(common_config('sphinx', 'server'), common_config('sphinx', 'port')); + if (!$fp) { + $this->connected = false; + return; + } + fclose($fp); + parent::__construct($target, $table); + $this->sphinx = new SphinxClient; + $this->sphinx->setServer(common_config('sphinx', 'server'), common_config('sphinx', 'port')); + $this->connected = true; + } + + function is_connected() + { + return $this->connected; + } + + function limit($offset, $count, $rss = false) + { + //FIXME without LARGEST_POSSIBLE, the most recent results aren't returned + // this probably has a large impact on performance + $LARGEST_POSSIBLE = 1e6; + + if ($rss) { + $this->sphinx->setLimits($offset, $count, $count, $LARGEST_POSSIBLE); + } + else { + // return at most 50 pages of results + $this->sphinx->setLimits($offset, $count, 50 * ($count - 1), $LARGEST_POSSIBLE); + } + + return $this->target->limit(0, $count); + } + + function query($q) + { + $result = $this->sphinx->query($q, $this->remote_table()); + if (!isset($result['matches'])) return false; + $id_set = join(', ', array_keys($result['matches'])); + $this->target->whereAdd("id in ($id_set)"); + return true; + } + + function set_sort_mode($mode) + { + if ('chron' === $mode) { + $this->sphinx->SetSortMode(SPH_SORT_ATTR_DESC, 'created_ts'); + return $this->target->orderBy('created desc'); + } + } + + function remote_table() + { + return $this->dbname() . '_' . $this->table; + } + + function dbname() + { + // @fixme there should be a less dreadful way to do this. + // DB objects won't give database back until they connect, it's confusing + if (preg_match('!^.*?://.*?:.*?@.*?/(.*?)$!', common_config('db', 'database'), $matches)) { + return $matches[1]; + } + throw new ServerException("Sphinx search could not identify database name"); + } +} diff --git a/plugins/TemplatePlugin.php b/plugins/TemplatePlugin.php index cfa051162..5f3ad81f5 100644 --- a/plugins/TemplatePlugin.php +++ b/plugins/TemplatePlugin.php @@ -32,7 +32,7 @@ class TemplatePlugin extends Plugin { // capture the RouterInitialized event // and connect a new API method // for updating the template - function onRouterInitialized( &$m ) { + function onRouterInitialized( $m ) { $m->connect( 'template/update', array( 'action' => 'template', )); diff --git a/plugins/TightUrl/TightUrlPlugin.php b/plugins/TightUrl/TightUrlPlugin.php index 48efb355f..56414c8c8 100644 --- a/plugins/TightUrl/TightUrlPlugin.php +++ b/plugins/TightUrl/TightUrlPlugin.php @@ -31,32 +31,27 @@ if (!defined('STATUSNET')) { exit(1); } -class TightUrlPlugin extends Plugin +require_once INSTALLDIR.'/plugins/UrlShortener/UrlShortenerPlugin.php'; + +class TightUrlPlugin extends UrlShortenerPlugin { - function __construct() - { - parent::__construct(); - } + public $serviceUrl; function onInitializePlugin(){ - $this->registerUrlShortener( - '2tu.us', - array('freeService'=>true), - array('TightUrl',array('http://2tu.us/?save=y&url=')) - ); + parent::onInitializePlugin(); + if(!isset($this->serviceUrl)){ + throw new Exception("must specify a serviceUrl"); + } } -} -class TightUrl extends ShortUrlApi -{ - protected function shorten_imp($url) { - $response = $this->http_get($url); - if (!$response) return $url; + protected function shorten($url) + { + $response = $this->http_get(sprintf($this->serviceUrl,urlencode($url))); + if (!$response) return; $response = $this->tidy($response); $y = @simplexml_load_string($response); - if (!isset($y->body)) return $url; + if (!isset($y->body)) return; $xml = $y->body->p[0]->code[0]->a->attributes(); if (isset($xml['href'])) return $xml['href']; - return $url; } } diff --git a/plugins/TwitterBridge/README b/plugins/TwitterBridge/README index 09352188e..d3bcda598 100644 --- a/plugins/TwitterBridge/README +++ b/plugins/TwitterBridge/README @@ -11,8 +11,7 @@ Installation To enable the plugin, add the following to your config.php: - require_once(INSTALLDIR . '/plugins/TwitterBridge/TwitterBridgePlugin.php'); - $tb = new TwitterBridgePlugin(); + addPlugin("TwitterBridge"); OAuth is used to to access protected resources on Twitter (as opposed to HTTP Basic Auth)*. To use Twitter bridging you will need to register diff --git a/plugins/TwitterBridge/TwitterBridgePlugin.php b/plugins/TwitterBridge/TwitterBridgePlugin.php index e69567fc7..ad3c2e551 100644 --- a/plugins/TwitterBridge/TwitterBridgePlugin.php +++ b/plugins/TwitterBridge/TwitterBridgePlugin.php @@ -60,12 +60,12 @@ class TwitterBridgePlugin extends Plugin * * Hook for RouterInitialized event. * - * @param Net_URL_Mapper &$m path-to-action mapper + * @param Net_URL_Mapper $m path-to-action mapper * * @return boolean hook return */ - function onRouterInitialized(&$m) + function onRouterInitialized($m) { $m->connect('twitter/authorization', array('action' => 'twitterauthorization')); diff --git a/plugins/TwitterBridge/daemons/synctwitterfriends.php b/plugins/TwitterBridge/daemons/synctwitterfriends.php index ed2bf48a2..671e3c7af 100755 --- a/plugins/TwitterBridge/daemons/synctwitterfriends.php +++ b/plugins/TwitterBridge/daemons/synctwitterfriends.php @@ -152,8 +152,8 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon $friends_ids = $client->friendsIds(); } catch (Exception $e) { common_log(LOG_WARNING, $this->name() . - ' - cURL error getting friend ids ' . - $e->getCode() . ' - ' . $e->getMessage()); + ' - error getting friend ids: ' . + $e->getMessage()); return $friends; } diff --git a/plugins/TwitterBridge/daemons/twitterstatusfetcher.php b/plugins/TwitterBridge/daemons/twitterstatusfetcher.php index 81bbbc7c5..b5428316b 100755 --- a/plugins/TwitterBridge/daemons/twitterstatusfetcher.php +++ b/plugins/TwitterBridge/daemons/twitterstatusfetcher.php @@ -109,12 +109,16 @@ class TwitterStatusFetcher extends ParallelizingDaemon $flink->find(); $flinks = array(); + common_log(LOG_INFO, "hello"); while ($flink->fetch()) { if (($flink->noticesync & FOREIGN_NOTICE_RECV) == FOREIGN_NOTICE_RECV) { $flinks[] = clone($flink); + common_log(LOG_INFO, "sync: foreign id $flink->foreign_id"); + } else { + common_log(LOG_INFO, "nothing to sync"); } } @@ -515,31 +519,32 @@ class TwitterStatusFetcher extends ParallelizingDaemon return $id; } + /** + * Fetch a remote avatar image and save to local storage. + * + * @param string $url avatar source URL + * @param string $filename bare local filename for download + * @return bool true on success, false on failure + */ function fetchAvatar($url, $filename) { - $avatarfile = Avatar::path($filename); + common_debug($this->name() . " - Fetching Twitter avatar: $url"); - $out = fopen($avatarfile, 'wb'); - if (!$out) { - common_log(LOG_WARNING, $this->name() . - " - Couldn't open file $filename"); + $request = HTTPClient::start(); + $response = $request->get($url); + if ($response->isOk()) { + $avatarfile = Avatar::path($filename); + $ok = file_put_contents($avatarfile, $response->getBody()); + if (!$ok) { + common_log(LOG_WARNING, $this->name() . + " - Couldn't open file $filename"); + return false; + } + } else { return false; } - common_debug($this->name() . " - Fetching Twitter avatar: $url"); - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_FILE, $out); - curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); - $result = curl_exec($ch); - curl_close($ch); - - fclose($out); - - return $result; + return true; } } diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php index 1a5248a9b..3c6803e49 100644 --- a/plugins/TwitterBridge/twitter.php +++ b/plugins/TwitterBridge/twitter.php @@ -215,7 +215,7 @@ function broadcast_basicauth($notice, $flink) try { $status = $client->statusesUpdate($statustxt); - } catch (BasicAuthCurlException $e) { + } catch (HTTP_Request2_Exception $e) { return process_error($e, $flink); } diff --git a/plugins/TwitterBridge/twitterauthorization.php b/plugins/TwitterBridge/twitterauthorization.php index 2a93ff13e..f1daefab1 100644 --- a/plugins/TwitterBridge/twitterauthorization.php +++ b/plugins/TwitterBridge/twitterauthorization.php @@ -125,7 +125,7 @@ class TwitterauthorizationAction extends Action $auth_link = $client->getAuthorizeLink($req_tok); - } catch (TwitterOAuthClientException $e) { + } catch (OAuthClientException $e) { $msg = sprintf('OAuth client cURL error - code: %1s, msg: %2s', $e->getCode(), $e->getMessage()); $this->serverError(_('Couldn\'t link your Twitter account.')); diff --git a/plugins/TwitterBridge/twitterbasicauthclient.php b/plugins/TwitterBridge/twitterbasicauthclient.php index 1040d72fb..d1cf45aec 100644 --- a/plugins/TwitterBridge/twitterbasicauthclient.php +++ b/plugins/TwitterBridge/twitterbasicauthclient.php @@ -32,26 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { } /** - * Exception wrapper for cURL errors - * - * @category Integration - * @package StatusNet - * @author Adrian Lang <mail@adrianlang.de> - * @author Brenda Wallace <shiny@cpan.org> - * @author Craig Andrews <candrews@integralblue.com> - * @author Dan Moore <dan@moore.cx> - * @author Evan Prodromou <evan@status.net> - * @author mEDI <medi@milaro.net> - * @author Sarven Capadisli <csarven@status.net> - * @author Zach Copley <zach@status.net> * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - */ -class BasicAuthCurlException extends Exception -{ -} - -/** * Class for talking to the Twitter API with HTTP Basic Auth. * * @category Integration @@ -198,45 +178,27 @@ class TwitterBasicAuthClient */ function httpRequest($url, $params = null, $auth = true) { - $options = array( - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FAILONERROR => true, - CURLOPT_HEADER => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_USERAGENT => 'StatusNet', - CURLOPT_CONNECTTIMEOUT => 120, - CURLOPT_TIMEOUT => 120, - CURLOPT_HTTPAUTH => CURLAUTH_ANY, - CURLOPT_SSL_VERIFYPEER => false, - - // Twitter is strict about accepting invalid "Expect" headers - - CURLOPT_HTTPHEADER => array('Expect:') - ); - - if (isset($params)) { - $options[CURLOPT_POST] = true; - $options[CURLOPT_POSTFIELDS] = $params; - } + $request = HTTPClient::start(); + $request->setConfig(array( + 'follow_redirects' => true, + 'connect_timeout' => 120, + 'timeout' => 120, + 'ssl_verifypeer' => false, + )); if ($auth) { - $options[CURLOPT_USERPWD] = $this->screen_name . - ':' . $this->password; + $request->setAuth($this->screen_name, $this->password); } - $ch = curl_init($url); - curl_setopt_array($ch, $options); - $response = curl_exec($ch); - - if ($response === false) { - $msg = curl_error($ch); - $code = curl_errno($ch); - throw new BasicAuthCurlException($msg, $code); + if (isset($params)) { + // Twitter is strict about accepting invalid "Expect" headers + $headers = array('Expect:'); + $response = $request->post($url, $headers, $params); + } else { + $response = $request->get($url); } - curl_close($ch); - - return $response; + return $response->getBody(); } } diff --git a/plugins/UrlShortener/UrlShortenerPlugin.php b/plugins/UrlShortener/UrlShortenerPlugin.php new file mode 100644 index 000000000..37206aa89 --- /dev/null +++ b/plugins/UrlShortener/UrlShortenerPlugin.php @@ -0,0 +1,103 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Superclass for plugins that do URL shortening + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @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') && !defined('LACONICA')) { + exit(1); +} + +/** + * Superclass for plugins that do URL shortening + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +abstract class UrlShortenerPlugin extends Plugin +{ + public $shortenerName; + public $freeService=false; + //------------Url Shortener plugin should implement some (or all) of these methods------------\\ + + /** + * Short a URL + * @param url + * @return string shortened version of the url, or null if URL shortening failed + */ + protected abstract function shorten($url); + + //------------These methods may help you implement your plugin------------\\ + protected function http_get($url) + { + $request = HTTPClient::start(); + $response = $request->get($url); + return $response->getBody(); + } + + protected function http_post($url,$data) + { + $request = HTTPClient::start(); + $response = $request->post($url, null, $data); + return $response->getBody(); + } + + protected function tidy($response) { + $response = str_replace(' ', ' ', $response); + $config = array('output-xhtml' => true); + $tidy = new tidy; + $tidy->parseString($response, $config, 'utf8'); + $tidy->cleanRepair(); + return (string)$tidy; + } + //------------Below are the methods that connect StatusNet to the implementing Url Shortener plugin------------\\ + + function onInitializePlugin(){ + if(!isset($this->shortenerName)){ + throw new Exception("must specify a shortenerName"); + } + } + + function onGetUrlShorteners(&$shorteners) + { + $shorteners[$this->shortenerName]=array('freeService'=>$this->freeService); + } + + function onStartShortenUrl($url,$shortenerName,&$shortenedUrl) + { + if($shortenerName == $this->shortenerName && strlen($url) >= common_config('site', 'shorturllength')){ + $result = $this->shorten($url); + if(isset($result) && $result != null && $result !== false){ + $shortenedUrl=$result; + common_log(LOG_INFO, __CLASS__ . ": $this->shortenerName shortened $url to $shortenedUrl"); + return false; + } + } + } +} diff --git a/plugins/UserFlag/UserFlagPlugin.php b/plugins/UserFlag/UserFlagPlugin.php new file mode 100644 index 000000000..c276c4b9f --- /dev/null +++ b/plugins/UserFlag/UserFlagPlugin.php @@ -0,0 +1,148 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Allows users to flag content and accounts as offensive/spam/whatever + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Allows users to flag content and accounts as offensive/spam/whatever + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class UserFlagPlugin extends Plugin +{ + function onCheckSchema() + { + $schema = Schema::get(); + + // For storing user-submitted flags on profiles + $schema->ensureDataObject('User_flag_profile'); + + return true; + } + + function onInitializePlugin() + { + // XXX: do something here? + return true; + } + + function onRouterInitialized(&$m) { + $m->connect('main/flag/profile', array('action' => 'flagprofile')); + $m->connect('admin/profile/flag', array('action' => 'adminprofileflag')); + return true; + } + + function onAutoload($cls) + { + switch ($cls) + { + case 'FlagprofileAction': + case 'AdminprofileflagAction': + require_once(INSTALLDIR.'/plugins/UserFlag/' . strtolower(mb_substr($cls, 0, -6)) . '.php'); + return false; + case 'FlagProfileForm': + require_once(INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php')); + return false; + case 'User_flag_profile': + require_once(INSTALLDIR.'/plugins/UserFlag/'.$cls.'.php'); + return false; + default: + return true; + } + } + + function onEndProfilePageActionsElements(&$action, $profile) + { + $user = common_current_user(); + + if (!empty($user)) { + + $action->elementStart('li', 'entity_flag'); + + if (User_flag_profile::exists($profile->id, $user->id)) { + $action->element('p', 'flagged', _('Flagged')); + } else { + $form = new FlagProfileForm($action, $profile, + array('action' => 'showstream', + 'nickname' => $profile->nickname)); + $form->show(); + } + + $action->elementEnd('li'); + } + + return true; + } + + function onEndProfileListItemActionElements($item) + { + $user = common_current_user(); + + if (!empty($user)) { + + list($action, $args) = $item->action->returnToArgs(); + + $args['action'] = $action; + + $form = new FlagProfileForm($item->action, $item->profile, $args); + + $item->action->elementStart('li', 'entity_flag'); + $form->show(); + $item->action->elementEnd('li'); + } + + return true; + } + + function onEndShowStatusNetStyles($action) + { + $action->elementStart('style', array('type' => 'text/css')); + $action->raw('.entity_flag input, .entity_flag p {'. + ' background:url('.common_path('plugins/UserFlag/flag.gif').') 5px 5px no-repeat;'. + ' }'); + $action->elementEnd('style'); + + return true; + } + + function onEndShowScripts($action) + { + $action->elementStart('script', array('type' => 'text/javascript')); + $action->raw('/*<![CDATA[*/ SN.U.FormXHR($(".form_entity_flag")); /*]]>*/'); + $action->elementEnd('script'); + return true; + } +} diff --git a/plugins/UserFlag/User_flag_profile.php b/plugins/UserFlag/User_flag_profile.php new file mode 100644 index 000000000..2fb27912d --- /dev/null +++ b/plugins/UserFlag/User_flag_profile.php @@ -0,0 +1,85 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR.'/classes/Plugin_DataObject.php'; + +class User_flag_profile extends Plugin_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'user_flag_profile'; // table name + public $profile_id; // int(4) primary_key not_null + public $user_id; // int(4) primary_key not_null + public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00 + public $cleared; // datetime not_null default_0000-00-00%2000%3A00%3A00 + + /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User_flag_profile',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + function table() { + return array( + 'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, + 'cleared' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + ); + } + + function keys() { + return array('profile_id' => 'N', 'user_id' => 'N'); + } + + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('User_flag_profile', $kv); + } + + static function exists($profile_id, $user_id) + { + $ufp = User_flag_profile::pkeyGet(array('profile_id' => $profile_id, + 'user_id' => $user_id)); + + return !empty($ufp); + } + + /** + * Get the TableDef object that represents the table backing this class + * @return TableDef TableDef instance + */ + function tableDef() + { + return new TableDef($this->__table, + array(new ColumnDef('profile_id', 'integer', null, + false, 'PRI'), + new ColumnDef('user_id', 'integer', null, + false, 'PRI'), + new ColumnDef('created', 'datetime', null, + false, 'MUL'), + new ColumnDef('cleared', 'datetime', null, + true, 'MUL'))); + } +} diff --git a/plugins/UserFlag/adminprofileflag.php b/plugins/UserFlag/adminprofileflag.php new file mode 100644 index 000000000..1ac76b506 --- /dev/null +++ b/plugins/UserFlag/adminprofileflag.php @@ -0,0 +1,201 @@ +<?php +/** + * Show latest and greatest profile flags + * + * PHP version 5 + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Show the latest and greatest profile flags + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class AdminprofileflagAction extends Action +{ + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + */ + + function prepare($args) + { + parent::prepare($args); + + return true; + } + + /** + * Handle request + * + * @param array $args $_REQUEST args; handled in prepare() + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $this->showPage(); + } + + function title() { + return _('Flagged profiles'); + } + + /** + * save the profile flag + * + * @return void + */ + + function showContent() + { + $profile = $this->getProfiles(); + + $pl = new FlaggedProfileList($profile, $this); + + $pl->show(); + } + + function getProfiles() + { + $ufp = new User_flag_profile(); + + $ufp->selectAdd(); + $ufp->selectAdd('profile_id'); + $ufp->selectAdd('count(*) as flag_count'); + + $ufp->whereAdd('cleared is NULL'); + + $ufp->groupBy('profile_id'); + $ufp->orderBy('flag_count DESC'); + + $profiles = array(); + + if ($ufp->find()) { + while ($ufp->fetch()) { + $profile = Profile::staticGet('id', $ufp->profile_id); + if (!empty($profile)) { + $profiles[] = $profile; + } + } + } + + $ufp->free(); + + return new ArrayWrapper($profiles); + } +} + +class FlaggedProfileList extends ProfileList { + + function newListItem($profile) + { + return new FlaggedProfileListItem($this->profile, $this->action); + } +} + +class FlaggedProfileListItem extends ProfileListItem +{ + var $user = null; + var $r2args = null; + + function showActions() + { + $this->user = common_current_user(); + + list($action, $this->r2args) = $this->out->returnToArgs(); + + $this->r2args['action'] = $action; + + $this->startActions(); + if (Event::handle('StartProfileListItemActionElements', array($this))) { + $this->showSandboxButton(); + $this->showSilenceButton(); + $this->showDeleteButton(); + $this->showClearButton(); + Event::handle('EndProfileListItemActionElements', array($this)); + } + $this->endActions(); + } + + function showSandboxButton() + { + if ($this->user->hasRight(Right::SANDBOXUSER)) { + $this->out->elementStart('li', 'entity_sandbox'); + if ($this->profile->isSandboxed()) { + $usf = new UnSandboxForm($this->out, $this->profile, $this->r2args); + $usf->show(); + } else { + $sf = new SandboxForm($this->out, $this->profile, $this->r2args); + $sf->show(); + } + $this->out->elementEnd('li'); + } + } + + function showSilenceButton() + { + if ($this->user->hasRight(Right::SILENCEUSER)) { + $this->out->elementStart('li', 'entity_silence'); + if ($this->profile->isSilenced()) { + $usf = new UnSilenceForm($this->out, $this->profile, $this->r2args); + $usf->show(); + } else { + $sf = new SilenceForm($this->out, $this->profile, $this->r2args); + $sf->show(); + } + $this->out->elementEnd('li'); + } + } + + function showDeleteButton() + { + + if ($this->user->hasRight(Right::DELETEUSER)) { + $this->out->elementStart('li', 'entity_delete'); + $df = new DeleteUserForm($this->out, $this->profile, $this->r2args); + $df->show(); + $this->out->elementEnd('li'); + } + } + + function showClearButton() + { + } +} diff --git a/plugins/UserFlag/flag.gif b/plugins/UserFlag/flag.gif Binary files differnew file mode 100644 index 000000000..68c8aee25 --- /dev/null +++ b/plugins/UserFlag/flag.gif diff --git a/plugins/UserFlag/flagprofile.php b/plugins/UserFlag/flagprofile.php new file mode 100644 index 000000000..8ff2f1f72 --- /dev/null +++ b/plugins/UserFlag/flagprofile.php @@ -0,0 +1,115 @@ +<?php +/** + * Add a flag to a profile + * + * PHP version 5 + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Action to flag a profile. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class FlagprofileAction extends ProfileFormAction +{ + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + */ + + function prepare($args) + { + if (!parent::prepare($args)) { + return false; + } + + $user = common_current_user(); + + assert(!empty($user)); // checked above + assert(!empty($this->profile)); // checked above + + if (User_flag_profile::exists($this->profile->id, + $user->id)) + { + $this->clientError(_('Flag already exists.')); + return false; + } + + return true; + } + + /** + * Handle POST + * + * @return void + */ + + function handlePost() + { + $user = common_current_user(); + + assert(!empty($user)); + assert(!empty($this->profile)); + + $ufp = new User_flag_profile(); + + $ufp->profile_id = $this->profile->id; + $ufp->user_id = $user->id; + $ufp->created = common_sql_now(); + + if (!$ufp->insert()) { + throw new ServerException(sprintf(_("Couldn't flag profile '%s' for review."), + $this->profile->nickname)); + } + + $ufp->free(); + } + + function ajaxResults() { + header('Content-Type: text/xml;charset=utf-8'); + $this->xw->startDocument('1.0', 'UTF-8'); + $this->elementStart('html'); + $this->elementStart('head'); + $this->element('title', null, _('Flagged for review')); + $this->elementEnd('head'); + $this->elementStart('body'); + $this->element('p', 'flagged', _('Flagged')); + $this->elementEnd('body'); + $this->elementEnd('html'); + } +} + diff --git a/plugins/UserFlag/flagprofileform.php b/plugins/UserFlag/flagprofileform.php new file mode 100644 index 000000000..262dad4a7 --- /dev/null +++ b/plugins/UserFlag/flagprofileform.php @@ -0,0 +1,82 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Form for flagging a profile + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Form + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/form.php'; + +/** + * Form for flagging a profile + * + * A form for flagging a profile + * + * @category Form + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class FlagProfileForm extends ProfileActionForm +{ + /** + * Action this form provides + * + * @return string Name of the action, lowercased. + */ + + function target() + { + return 'flagprofile'; + } + + /** + * Title of the form + * + * @return string Title of the form, internationalized + */ + + function title() + { + return _('Flag'); + } + + /** + * Description of the form + * + * @return string description of the form, internationalized + */ + + function description() + { + return _('Flag profile for review'); + } +} diff --git a/plugins/WikiHashtagsPlugin.php b/plugins/WikiHashtagsPlugin.php index 0c5649aa4..334fc13ba 100644 --- a/plugins/WikiHashtagsPlugin.php +++ b/plugins/WikiHashtagsPlugin.php @@ -68,14 +68,13 @@ class WikiHashtagsPlugin extends Plugin $editurl = sprintf('http://hashtags.wikia.com/index.php?title=%s&action=edit', urlencode($tag)); - $context = stream_context_create(array('http' => array('method' => "GET", - 'header' => - "User-Agent: " . $this->userAgent()))); - $html = @file_get_contents($url, false, $context); + $request = HTTPClient::start(); + $response = $request->get($url); + $html = $response->getBody(); $action->elementStart('div', array('id' => 'wikihashtags', 'class' => 'section')); - if (!empty($html)) { + if ($response->isOk() && !empty($html)) { $action->element('style', null, "span.editsection { display: none }\n". "table.toc { display: none }"); @@ -100,10 +99,4 @@ class WikiHashtagsPlugin extends Plugin return true; } - - function userAgent() - { - return 'WikiHashtagsPlugin/'.WIKIHASHTAGSPLUGIN_VERSION . - ' StatusNet/' . STATUSNET_VERSION; - } } diff --git a/plugins/recaptcha/README b/plugins/recaptcha/README deleted file mode 100644 index b996f96cc..000000000 --- a/plugins/recaptcha/README +++ /dev/null @@ -1,23 +0,0 @@ -StatusNet reCAPTCHA plugin 0.2 8/3/09 -==================================== -Adds a captcha to your registration page to reduce automated spam bots registering. - -Use: -1. Get an API key from http://recaptcha.net - -2. In config.php add: -include_once('plugins/recaptcha/recaptcha.php'); -$captcha = new recaptcha(publickey, privatekey, showErrors); - -Changelog -========= -0.1 initial release -0.2 Work around for webkit browsers - -reCAPTCHA README -================ - -The reCAPTCHA PHP Lirary helps you use the reCAPTCHA API. Documentation -for this library can be found at - - http://recaptcha.net/plugins/php |