diff --git a/htaccess.sample b/htaccess.sample index 18a868698c..fa09b30f6d 100644 --- a/htaccess.sample +++ b/htaccess.sample @@ -1,12 +1,17 @@ RewriteEngine On - # NOTE: change this to your actual StatusNet path; may be "/". - + # NOTE: change this to your actual StatusNet base URL path, + # minus the domain part: + # + # http://example.com/ => / + # http://example.com/mublog/ => /mublog/ + # RewriteBase /mublog/ ## Uncomment these if having trouble with API authentication ## when PHP is running in CGI or FastCGI mode. + # #RewriteCond %{HTTP:Authorization} ^(.*) #RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1] diff --git a/lib/router.php b/lib/router.php index 9fe2f60ae7..a040abb832 100644 --- a/lib/router.php +++ b/lib/router.php @@ -33,6 +33,33 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { require_once 'Net/URL/Mapper.php'; +class StatusNet_URL_Mapper extends Net_URL_Mapper { + + private static $_singleton = null; + + private function __construct() + { + } + + public static function getInstance($id = '__default__') + { + if (empty(self::$_singleton)) { + self::$_singleton = new StatusNet_URL_Mapper(); + } + return self::$_singleton; + } + + public function connect($path, $defaults = array(), $rules = array()) + { + $result = null; + if (Event::handle('StartConnectPath', array(&$path, &$defaults, &$rules, &$result))) { + $result = parent::connect($path, $defaults, $rules); + Event::handle('EndConnectPath', array($path, $defaults, $rules, $result)); + } + return $result; + } +} + /** * URL Router * @@ -69,7 +96,7 @@ class Router function initialize() { - $m = Net_URL_Mapper::getInstance(); + $m = StatusNet_URL_Mapper::getInstance(); if (Event::handle('StartInitializeRouter', array(&$m))) { diff --git a/plugins/Blacklist/BlacklistPlugin.php b/plugins/Blacklist/BlacklistPlugin.php index fb8f7306f5..a7d0942da5 100644 --- a/plugins/Blacklist/BlacklistPlugin.php +++ b/plugins/Blacklist/BlacklistPlugin.php @@ -62,13 +62,56 @@ class BlacklistPlugin extends Plugin { $confNicknames = $this->_configArray('blacklist', 'nicknames'); + $dbNicknames = Nickname_blacklist::getPatterns(); + $this->_nicknamePatterns = array_merge($this->nicknames, - $confNicknames); + $confNicknames, + $dbNicknames); $confURLs = $this->_configArray('blacklist', 'urls'); + $dbURLs = Homepage_blacklist::getPatterns(); + $this->_urlPatterns = array_merge($this->urls, - $confURLs); + $confURLs, + $dbURLs); + } + + /** + * Database schema setup + * + * @return boolean hook value + */ + + function onCheckSchema() + { + $schema = Schema::get(); + + // For storing blacklist patterns for nicknames + + $schema->ensureTable('nickname_blacklist', + array(new ColumnDef('pattern', + 'varchar', + 255, + false, + 'PRI'), + new ColumnDef('created', + 'datetime', + null, + false))); + + $schema->ensureTable('homepage_blacklist', + array(new ColumnDef('pattern', + 'varchar', + 255, + false, + 'PRI'), + new ColumnDef('created', + 'datetime', + null, + false))); + + return true; } /** @@ -280,6 +323,10 @@ class BlacklistPlugin extends Plugin { switch (strtolower($cls)) { + case 'nickname_blacklist': + case 'homepage_blacklist': + include_once INSTALLDIR.'/plugins/Blacklist/'.ucfirst($cls).'.php'; + return false; case 'blacklistadminpanelaction': $base = strtolower(mb_substr($cls, 0, -6)); include_once INSTALLDIR.'/plugins/Blacklist/'.$base.'.php'; @@ -391,20 +438,14 @@ class BlacklistPlugin extends Plugin function onEndDeleteUser($action, $user) { - common_debug("Action args: " . print_r($action->args, true)); - if ($action->boolean('blacklisthomepage')) { $pattern = $action->trimmed('blacklisthomepagepattern'); - $confURLs = $this->_configArray('blacklist', 'urls'); - $confURLs[] = $pattern; - Config::save('blacklist', 'urls', implode("\r\n", $confURLs)); + Homepage_blacklist::ensurePattern($pattern); } if ($action->boolean('blacklistnickname')) { $pattern = $action->trimmed('blacklistnicknamepattern'); - $confNicknames = $this->_configArray('blacklist', 'nicknames'); - $confNicknames[] = $pattern; - Config::save('blacklist', 'nicknames', implode("\r\n", $confNicknames)); + Nickname_blacklist::ensurePattern($pattern); } return true; diff --git a/plugins/Blacklist/Homepage_blacklist.php b/plugins/Blacklist/Homepage_blacklist.php new file mode 100644 index 0000000000..32080667e1 --- /dev/null +++ b/plugins/Blacklist/Homepage_blacklist.php @@ -0,0 +1,189 @@ + + * @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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; + +/** + * Data class for Homepage blacklist + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @see DB_DataObject + */ + +class Homepage_blacklist extends Memcached_DataObject +{ + public $__table = 'homepage_blacklist'; // table name + public $pattern; // string pattern + public $created; // datetime + + /** + * Get an instance by key + * + * This is a utility method to get a single instance with a given key value. + * + * @param string $k Key to use to lookup (usually 'user_id' for this class) + * @param mixed $v Value to lookup + * + * @return Homepage_blacklist object found, or null for no hits + * + */ + + function staticGet($k, $v=null) + { + return Memcached_DataObject::staticGet('Homepage_blacklist', $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * DB_DataObject needs to know something about the table to manipulate + * instances. This method provides all the DB_DataObject needs to know. + * + * @return array array of column definitions + */ + + function table() + { + return array('pattern' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); + } + + /** + * return key definitions for DB_DataObject + * + * DB_DataObject needs to know about keys that the table has; this function + * defines them. + * + * @return array key definitions + */ + + function keys() + { + return array('pattern' => 'K'); + } + + /** + * return key definitions for Memcached_DataObject + * + * Our caching system uses the same key definitions, but uses a different + * method to get them. + * + * @return array key definitions + */ + + function keyTypes() + { + return $this->keys(); + } + + /** + * Return a list of patterns to check + * + * @return array string patterns to check + */ + + static function getPatterns() + { + $patterns = self::cacheGet('homepage_blacklist:patterns'); + + if ($patterns === false) { + + $patterns = array(); + + $nb = new Homepage_blacklist(); + + $nb->find(); + + while ($nb->fetch()) { + $patterns[] = $nb->pattern; + } + + self::cacheSet('homepage_blacklist:patterns', $patterns); + } + + return $patterns; + } + + /** + * Save new list of patterns + * + * @return array of patterns to check + */ + + static function saveNew($newPatterns) + { + $oldPatterns = self::getPatterns(); + + // Delete stuff that's old that not in new + + $toDelete = array_diff($oldPatterns, $newPatterns); + + // Insert stuff that's in new and not in old + + $toInsert = array_diff($newPatterns, $oldPatterns); + + foreach ($toDelete as $pattern) { + $nb = Homepage_blacklist::staticGet('pattern', $pattern); + if (!empty($nb)) { + $nb->delete(); + } + } + + foreach ($toInsert as $pattern) { + $nb = new Homepage_blacklist(); + $nb->pattern = $pattern; + $nb->created = common_sql_now(); + $nb->insert(); + } + + self::blow('homepage_blacklist:patterns'); + } + + static function ensurePattern($pattern) + { + $hb = Homepage_blacklist::staticGet('pattern', $pattern); + + if (empty($nb)) { + $hb = new Homepage_blacklist(); + $hb->pattern = $pattern; + $hb->created = common_sql_now(); + $hb->insert(); + self::blow('homepage_blacklist:patterns'); + } + } +} diff --git a/plugins/Blacklist/Nickname_blacklist.php b/plugins/Blacklist/Nickname_blacklist.php new file mode 100644 index 0000000000..9810631444 --- /dev/null +++ b/plugins/Blacklist/Nickname_blacklist.php @@ -0,0 +1,180 @@ + + * @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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; + +/** + * Data class for Nickname blacklist + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @see DB_DataObject + */ + +class Nickname_blacklist extends Memcached_DataObject +{ + public $__table = 'nickname_blacklist'; // table name + public $pattern; // string pattern + public $created; // datetime + + /** + * Get an instance by key + * + * This is a utility method to get a single instance with a given key value. + * + * @param string $k Key to use to lookup + * @param mixed $v Value to lookup + * + * @return Nickname_blacklist object found, or null for no hits + * + */ + + function staticGet($k, $v=null) + { + return Memcached_DataObject::staticGet('Nickname_blacklist', $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * @return array array of column definitions + */ + + function table() + { + return array('pattern' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); + } + + /** + * return key definitions for DB_DataObject + * + * @return array key definitions + */ + + function keys() + { + return array('pattern' => 'K'); + } + + /** + * return key definitions for Memcached_DataObject + * + * @return array key definitions + */ + + function keyTypes() + { + return $this->keys(); + } + + /** + * Return a list of patterns to check + * + * @return array string patterns to check + */ + + static function getPatterns() + { + $patterns = self::cacheGet('nickname_blacklist:patterns'); + + if ($patterns === false) { + + $patterns = array(); + + $nb = new Nickname_blacklist(); + + $nb->find(); + + while ($nb->fetch()) { + $patterns[] = $nb->pattern; + } + + self::cacheSet('nickname_blacklist:patterns', $patterns); + } + + return $patterns; + } + + /** + * Save new list of patterns + * + * @return array of patterns to check + */ + + static function saveNew($newPatterns) + { + $oldPatterns = self::getPatterns(); + + // Delete stuff that's old that not in new + + $toDelete = array_diff($oldPatterns, $newPatterns); + + // Insert stuff that's in new and not in old + + $toInsert = array_diff($newPatterns, $oldPatterns); + + foreach ($toDelete as $pattern) { + $nb = Nickname_blacklist::staticGet('pattern', $pattern); + if (!empty($nb)) { + $nb->delete(); + } + } + + foreach ($toInsert as $pattern) { + $nb = new Nickname_blacklist(); + $nb->pattern = $pattern; + $nb->created = common_sql_now(); + $nb->insert(); + } + + self::blow('nickname_blacklist:patterns'); + } + + static function ensurePattern($pattern) + { + $nb = Nickname_blacklist::staticGet('pattern', $pattern); + + if (empty($nb)) { + $nb = new Nickname_blacklist(); + $nb->pattern = $pattern; + $nb->created = common_sql_now(); + $nb->insert(); + self::blow('nickname_blacklist:patterns'); + } + } +} diff --git a/plugins/Blacklist/blacklistadminpanel.php b/plugins/Blacklist/blacklistadminpanel.php index 98d07080db..b996aba8dc 100644 --- a/plugins/Blacklist/blacklistadminpanel.php +++ b/plugins/Blacklist/blacklistadminpanel.php @@ -88,35 +88,24 @@ class BlacklistadminpanelAction extends AdminPanelAction function saveSettings() { - static $settings = array( - 'blacklist' => array('nicknames', 'urls'), - ); + $nickPatterns = array(); - $values = array(); + $rawNickPatterns = explode("\n", $this->trimmed('blacklist-nicknames')); - foreach ($settings as $section => $parts) { - foreach ($parts as $setting) { - $values[$section][$setting] = $this->trimmed("$section-$setting"); - } + foreach ($rawNickPatterns as $raw) { + $nickPatterns[] = trim($raw); } - // This throws an exception on validation errors + Nickname_blacklist::saveNew($nickPatterns); - $this->validate($values); + $rawUrlPatterns = explode("\n", $this->trimmed('blacklist-urls')); + $urlPatterns = array(); - // assert(all values are valid); - - $config = new Config(); - - $config->query('BEGIN'); - - foreach ($settings as $section => $parts) { - foreach ($parts as $setting) { - Config::save($section, $setting, $values[$section][$setting]); - } + foreach ($rawUrlPatterns as $raw) { + $urlPatterns[] = trim($raw); } - $config->query('COMMIT'); + Homepage_blacklist::saveNew($urlPatterns); return; } @@ -191,14 +180,19 @@ class BlacklistAdminPanelForm extends Form $this->out->elementStart('ul', 'form_data'); $this->out->elementStart('li'); + + $nickPatterns = Nickname_blacklist::getPatterns(); + $this->out->textarea('blacklist-nicknames', _m('Nicknames'), - common_config('blacklist', 'nicknames'), + implode("\r\n", $nickPatterns), _('Patterns of nicknames to block, one per line')); $this->out->elementEnd('li'); + $urlPatterns = Homepage_blacklist::getPatterns(); + $this->out->elementStart('li'); $this->out->textarea('blacklist-urls', _m('URLs'), - common_config('blacklist', 'urls'), + implode("\r\n", $urlPatterns), _('Patterns of URLs to block, one per line')); $this->out->elementEnd('li'); diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php index 1724b5f7be..270e2c624b 100644 --- a/plugins/OpenID/OpenIDPlugin.php +++ b/plugins/OpenID/OpenIDPlugin.php @@ -45,14 +45,7 @@ if (!defined('STATUSNET')) { class OpenIDPlugin extends Plugin { - /** - * Initializer for the plugin. - */ - - function __construct() - { - parent::__construct(); - } + public $openidOnly = false; /** * Add OpenID-related paths to the router table @@ -78,6 +71,60 @@ class OpenIDPlugin extends Plugin return true; } + /** + * In OpenID-only mode, disable paths for password stuff + * + * @param string $path path to connect + * @param array $defaults path defaults + * @param array $rules path rules + * @param array $result unused + * + * @return boolean hook return + */ + + function onStartConnectPath(&$path, &$defaults, &$rules, &$result) + { + if ($this->openidOnly) { + static $block = array('main/login', + 'main/register', + 'main/recoverpassword', + 'settings/password'); + + if (in_array($path, $block)) { + return false; + } + } + + return true; + } + + /** + * If we've been hit with password-login args, redirect + * + * @param array $args args (URL, Get, post) + * + * @return boolean hook return + */ + + function onArgsInitialize($args) + { + if ($this->openidOnly) { + if (array_key_exists('action', $args)) { + $action = trim($args['action']); + if (in_array($action, array('login', 'register'))) { + common_redirect(common_local_url('openidlogin')); + exit(0); + } else if ($action == 'passwordsettings') { + common_redirect(common_local_url('openidsettings')); + exit(0); + } else if ($action == 'recoverpassword') { + throw new ClientException('Unavailable action'); + } + } + } + return true; + } + /** * Public XRDS output hook * @@ -142,6 +189,69 @@ class OpenIDPlugin extends Plugin $xrdsOutputter->elementEnd('XRD'); } + /** + * If we're in OpenID-only mode, hide all the main menu except OpenID login. + * + * @param Action $action Action being run + * + * @return boolean hook return + */ + + function onStartPrimaryNav($action) + { + if ($this->openidOnly && !common_logged_in()) { + // TRANS: Tooltip for main menu option "Login" + $tooltip = _m('TOOLTIP', 'Login to the site'); + // TRANS: Main menu option when not logged in to log in + $action->menuItem(common_local_url('openidlogin'), + _m('MENU', 'Login'), + $tooltip, + false, + 'nav_login'); + // TRANS: Tooltip for main menu option "Help" + $tooltip = _m('TOOLTIP', 'Help me!'); + // TRANS: Main menu option for help on the StatusNet site + $action->menuItem(common_local_url('doc', array('title' => 'help')), + _m('MENU', 'Help'), + $tooltip, + false, + 'nav_help'); + if (!common_config('site', 'private')) { + // TRANS: Tooltip for main menu option "Search" + $tooltip = _m('TOOLTIP', 'Search for people or text'); + // TRANS: Main menu option when logged in or when the StatusNet instance is not private + $action->menuItem(common_local_url('peoplesearch'), + _m('MENU', 'Search'), $tooltip, false, 'nav_search'); + } + Event::handle('EndPrimaryNav', array($action)); + return false; + } + return true; + } + + /** + * Menu for login + * + * If we're in openidOnly mode, we disable the menu for all other login. + * + * @param Action &$action Action being executed + * + * @return boolean hook return + */ + + function onStartLoginGroupNav(&$action) + { + if ($this->openidOnly) { + $this->showOpenIDLoginTab($action); + // Even though we replace this code, we + // DON'T run the End* hook, to keep others from + // adding tabs. Not nice, but. + return false; + } + + return true; + } + /** * Menu item for login * @@ -151,6 +261,21 @@ class OpenIDPlugin extends Plugin */ function onEndLoginGroupNav(&$action) + { + $this->showOpenIDLoginTab($action); + + return true; + } + + /** + * Show menu item for login + * + * @param Action $action Action being executed + * + * @return void + */ + + function showOpenIDLoginTab($action) { $action_name = $action->trimmed('action'); @@ -158,12 +283,28 @@ class OpenIDPlugin extends Plugin _m('OpenID'), _m('Login or register with OpenID'), $action_name === 'openidlogin'); + } + /** + * Show menu item for password + * + * We hide it in openID-only mode + * + * @param Action $menu Widget for menu + * @param void &$unused Unused value + * + * @return void + */ + + function onStartAccountSettingsPasswordMenuItem($menu, &$unused) { + if ($this->openidOnly) { + return false; + } return true; } /** - * Menu item for OpenID admin + * Menu item for OpenID settings * * @param Action &$action Action being executed * @@ -301,7 +442,7 @@ class OpenIDPlugin extends Plugin function onRedirectToLogin($action, $user) { - if (!empty($user) && User_openid::hasOpenID($user->id)) { + if ($this->openidOnly || (!empty($user) && User_openid::hasOpenID($user->id))) { common_redirect(common_local_url('openidlogin'), 303); return false; } diff --git a/plugins/OpenID/finishopenidlogin.php b/plugins/OpenID/finishopenidlogin.php index 438a728d83..f3a4833006 100644 --- a/plugins/OpenID/finishopenidlogin.php +++ b/plugins/OpenID/finishopenidlogin.php @@ -48,7 +48,6 @@ class FinishopenidloginAction extends Action } else if ($this->arg('connect')) { $this->connectUser(); } else { - common_debug(print_r($this->args, true), __FILE__); $this->showForm(_m('Something weird happened.'), $this->trimmed('newname')); } @@ -159,6 +158,9 @@ class FinishopenidloginAction extends Action $canonical = ($response->endpoint->canonicalID) ? $response->endpoint->canonicalID : $response->getDisplayIdentifier(); + oid_assert_allowed($display); + oid_assert_allowed($canonical); + $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response); if ($sreg_resp) { diff --git a/plugins/OpenID/openid.php b/plugins/OpenID/openid.php index 9e02c7a883..1524389177 100644 --- a/plugins/OpenID/openid.php +++ b/plugins/OpenID/openid.php @@ -94,7 +94,6 @@ function oid_link_user($id, $canonical, $display) if (!$oid->insert()) { $err = PEAR::getStaticProperty('DB_DataObject','lastError'); - common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__); return false; } @@ -119,13 +118,10 @@ function oid_check_immediate($openid_url, $backto=null) unset($args['action']); $backto = common_local_url($action, $args); } - common_debug('going back to "' . $backto . '"', __FILE__); common_ensure_session(); $_SESSION['openid_immediate_backto'] = $backto; - common_debug('passed-in variable is "' . $backto . '"', __FILE__); - common_debug('session variable is "' . $_SESSION['openid_immediate_backto'] . '"', __FILE__); oid_authenticate($openid_url, 'finishimmediate', @@ -261,6 +257,35 @@ function oid_update_user(&$user, &$sreg) return true; } +function oid_assert_allowed($url) +{ + $blacklist = common_config('openid', 'blacklist'); + $whitelist = common_config('openid', 'whitelist'); + + if (empty($blacklist)) { + $blacklist = array(); + } + + if (empty($whitelist)) { + $whitelist = array(); + } + + foreach ($blacklist as $pattern) { + if (preg_match("/$pattern/", $url)) { + common_log(LOG_INFO, "Matched OpenID blacklist pattern {$pattern} with {$url}"); + foreach ($whitelist as $exception) { + if (preg_match("/$exception/", $url)) { + common_log(LOG_INFO, "Matched OpenID whitelist pattern {$exception} with {$url}"); + return; + } + } + throw new ClientException(_m("Unauthorized URL used for OpenID login."), 403); + } + } + + return; +} + class AutosubmitAction extends Action { var $form_html = null; @@ -281,7 +306,7 @@ class AutosubmitAction extends Action { $this->raw($this->form_html); } - + function showScripts() { parent::showScripts(); diff --git a/plugins/OpenID/openidlogin.php b/plugins/OpenID/openidlogin.php index 9ba55911c0..2a743672cf 100644 --- a/plugins/OpenID/openidlogin.php +++ b/plugins/OpenID/openidlogin.php @@ -31,6 +31,8 @@ class OpenidloginAction extends Action } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $openid_url = $this->trimmed('openid_url'); + oid_assert_allowed($openid_url); + # CSRF protection $token = $this->trimmed('token'); if (!$token || $token != common_session_token()) { diff --git a/plugins/OpenID/openidtrust.php b/plugins/OpenID/openidtrust.php index fa7ea36e26..ed6ca73a47 100644 --- a/plugins/OpenID/openidtrust.php +++ b/plugins/OpenID/openidtrust.php @@ -71,7 +71,7 @@ class OpenidtrustAction extends Action } return true; } - + function handle($args) { parent::handle($args); @@ -96,7 +96,6 @@ class OpenidtrustAction extends Action $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{ @@ -135,7 +134,7 @@ class OpenidtrustAction extends Action $this->elementStart('fieldset'); $this->submit('allow', _m('Continue')); $this->submit('deny', _m('Cancel')); - + $this->elementEnd('fieldset'); $this->elementEnd('form'); } diff --git a/theme/base/css/display.css b/theme/base/css/display.css index b0ab02bcec..f48bdf55e7 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -858,6 +858,9 @@ display:inline; display:inline; margin-right:7px; } +.entity_tags li:before { +content:'\0009'; +} .aside .section { margin-bottom:29px; diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 43b8e4656c..f5efdb49c4 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -1,7 +1,7 @@ /** theme: biz base * * @package StatusNet - * @author Sarven Capadisli + * @author Sarven Capadisli * @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/ @@ -849,7 +849,8 @@ margin-right:11px; /* NOTICE */ .notice, -.profile { +.profile, +.application { position:relative; padding-top:11px; padding-bottom:11px; @@ -862,10 +863,15 @@ border-top-style:dotted; .notices li { list-style-type:none; } -.notices li.hover { -border-radius:4px; --moz-border-radius:4px; --webkit-border-radius:4px; +.notices .notices { +margin-top:7px; +margin-left:2%; +width:98%; +float:left; +} +.mark-top { +border-top-width:1px; +border-top-style:solid; } /* NOTICES */ @@ -996,25 +1002,22 @@ text-transform:lowercase; .dialogbox { position:absolute; -top:-4px; -right:29px; +top:-1px; +right:-1px; z-index:9; -min-width:199px; float:none; -background-color:#FFF; padding:11px; border-radius:7px; -moz-border-radius:7px; -webkit-border-radius:7px; border-style:solid; border-width:1px; -border-color:#DDDDDD; --moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); } .dialogbox legend { display:block !important; margin-right:18px; +margin-bottom:18px; } .dialogbox button.close { @@ -1023,11 +1026,22 @@ right:3px; top:3px; } +.dialogbox .form_guide { +font-weight:normal; +padding:0; +} + .dialogbox .submit_dialogbox { font-weight:bold; text-indent:0; min-width:46px; } +.dialogbox input { +padding-left:4px; +} +.dialogbox fieldset { +margin-bottom:0; +} #wrap form.processing input.submit, .entity_actions a.processing, @@ -1103,6 +1117,104 @@ width:16px; height:16px; } +.notice .attachment { +position:relative; +padding-left:16px; +} +.notice .attachment.more { +text-indent:-9999px; +width:16px; +height:16px; +display:inline-block; +overflow:hidden; +vertical-align:middle; +margin-left:4px; +} + +#attachments .attachment, +.notice .attachment.more { +padding-left:0; +} +.notice .attachment img { +position:absolute; +top:18px; +left:0; +z-index:99; +} +#shownotice .notice .attachment img { +position:static; +} + +#attachments { +clear:both; +float:left; +width:100%; +margin-top:18px; +} +#attachments dt { +font-weight:bold; +font-size:1.3em; +margin-bottom:4px; +} + +#attachments ol li { +margin-bottom:18px; +list-style-type:decimal; +float:left; +clear:both; +} + +#jOverlayContent, +#jOverlayContent #content, +#jOverlayContent #content_inner { +width: auto !important; +margin-bottom:0; +} +#jOverlayContent #content { +padding:11px; +min-height:auto; +} +#jOverlayContent .entry-title { +display:block; +margin-bottom:11px; +} +#jOverlayContent button { +position:absolute; +top:0; +right:0; +} +#jOverlayContent h1 { +max-width:425px; +} +#jOverlayContent #content { +border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +} +#jOverlayLoading { +top:5%; +left:40%; +} +#attachment_view img { +max-width:480px; +max-height:480px; +} +#attachment_view #oembed_info { +margin-top:11px; +} +#attachment_view #oembed_info dt, +#attachment_view #oembed_info dd { +float:left; +} +#attachment_view #oembed_info dt { +clear:left; +margin-right:11px; +font-weight:bold; +} +#attachment_view #oembed_info dt:after { +content: ":"; +} + #usergroups #new_group { float: left; margin-right: 2em; diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index cafb152dcc..ea09ef4c0f 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -206,15 +206,26 @@ button.close, .form_user_unsubscribe input.submit, .form_group_join input.submit, .form_user_subscribe input.submit, +.form_remote_authorize input.submit, .entity_subscribe a, .entity_moderation p, .entity_sandbox input.submit, .entity_silence input.submit, .entity_delete input.submit, +.entity_role p, +.entity_role_administrator input.submit, +.entity_role_moderator input.submit, .notice-options .repeated, .form_notice label[for=notice_data-geo], button.minimize, -.form_reset_key input.submit { +.form_reset_key input.submit, +.entity_clear input.submit, +.entity_flag input.submit, +.entity_flag p, +.entity_subscribe input.submit, +#realtime_play, +#realtime_pause, +#realtime_popup { background-image:url(../../base/images/icons/icons-01.gif); background-repeat:no-repeat; background-color:transparent; @@ -242,7 +253,9 @@ border-color:#FFFFFF; #content, #site_nav_local_views .current a, .entity_send-a-message .form_notice, -.entity_moderation:hover ul { +.entity_moderation:hover ul, +.entity_role:hover ul, +.dialogbox { background-color:#FFFFFF; } @@ -353,12 +366,37 @@ background-position: 5px -1511px; .form_reset_key input.submit { background-position: 5px -1973px; } +.entity_clear input.submit { +background-position: 5px -2039px; +} +.entity_flag input.submit, +.entity_flag p { +background-position: 5px -2105px; +} +.entity_subscribe input.accept { +background-position: 5px -2171px; +} +.entity_subscribe input.reject { +background-position: 5px -2237px; +} +#realtime_play { +background-position: 0 -2308px; +} +#realtime_pause { +background-position: 0 -2374px; +} +#realtime_popup { +background-position: 0 -1714px; +} /* NOTICES */ .notice .attachment { background-position:0 -394px; } +.notice .attachment.more { +background-position:0 -2770px; +} #attachments .attachment { background:none; } @@ -381,14 +419,19 @@ background-position:0 -1582px; background-position:0 -1648px; } +.notices .attachment.more, .notices div.entry-content, .notices div.notice-options { opacity:0.4; } +.notices li:hover .attachment.more, .notices li:hover div.entry-content, .notices li:hover div.notice-options { opacity:1; } +.opaque { +opacity:1 !important; +} div.notice-options a, div.notice-options input { font-family:sans-serif; diff --git a/theme/cloudy/css/display.css b/theme/cloudy/css/display.css index d9e9f3ce20..caea5cf443 100644 --- a/theme/cloudy/css/display.css +++ b/theme/cloudy/css/display.css @@ -873,7 +873,7 @@ display:inline; } .profile .entity_profile .fn, -.profile .entity_profile .location { +.profile .entity_profile .label { margin-left:11px; margin-bottom:4px; width:auto; @@ -901,7 +901,8 @@ margin-right:11px; /* NOTICE */ .notice, -.profile { +.profile, +.application { position:relative; padding-top:11px; padding-bottom:11px; @@ -1032,25 +1033,22 @@ left:0; .dialogbox { position:absolute; -top:-4px; -right:29px; +top:-1px; +right:-1px; z-index:9; -min-width:199px; float:none; -background-color:#FFF; padding:11px; border-radius:7px; -moz-border-radius:7px; -webkit-border-radius:7px; border-style:solid; border-width:1px; -border-color:#DDDDDD; --moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); } .dialogbox legend { display:block !important; margin-right:18px; +margin-bottom:18px; } .dialogbox button.close { @@ -1059,9 +1057,21 @@ right:3px; top:3px; } +.dialogbox .form_guide { +font-weight:normal; +padding:0; +} + .dialogbox .submit_dialogbox { -text-indent:0; font-weight:bold; +text-indent:0; +min-width:46px; +} +.dialogbox input { +padding-left:4px; +} +.dialogbox fieldset { +margin-bottom:0; } .notice-options { @@ -1653,7 +1663,8 @@ background-color:transparent; } #wrap form.processing input.submit, -.entity_actions a.processing { +#core a.processing, +.dialogbox.processing .submit_dialogbox { background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; cursor:wait; text-indent:-9999px; @@ -1808,7 +1819,9 @@ border-color:#FFFFFF; #content, #site_nav_local_views .current a, .entity_send-a-message .form_notice, -.entity_moderation:hover ul { +.entity_moderation:hover ul, +.entity_role:hover ul, +.dialogbox { background-color:#FFFFFF; } @@ -1940,6 +1953,9 @@ background-position: 0 -1714px; .notice .attachment { background-position:0 -394px; } +.notice .attachment.more { +background-position:0 -2770px; +} #attachments .attachment { background:none; } @@ -1962,10 +1978,12 @@ background-position:0 -1582px; background-position:0 -1648px; } +.notices .attachment.more, .notices div.entry-content, .notices div.notice-options { opacity:0.4; } +.notices li:hover .attachment.more, .notices li:hover div.entry-content, .notices li:hover div.notice-options { opacity:1; diff --git a/theme/default/css/display.css b/theme/default/css/display.css index 7ccd234cd0..5e3748cb7a 100644 --- a/theme/default/css/display.css +++ b/theme/default/css/display.css @@ -214,7 +214,7 @@ background-color:transparent; } #wrap form.processing input.submit, -#content a.processing, +#core a.processing, .dialogbox.processing .submit_dialogbox { background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; } diff --git a/theme/h4ck3r/css/base.css b/theme/h4ck3r/css/base.css index 0302653fde..4c0e74218e 100644 --- a/theme/h4ck3r/css/base.css +++ b/theme/h4ck3r/css/base.css @@ -701,7 +701,8 @@ margin-right:11px; /* NOTICE */ .notice, -.profile { +.profile, +.application { position:relative; padding-top:11px; padding-bottom:11px; @@ -709,11 +710,21 @@ clear:both; float:left; width:100%; border-top-width:1px; -border-top-style:dashed; +border-top-style:dotted; } .notices li { list-style-type:none; } +.notices .notices { +margin-top:7px; +margin-left:2%; +width:98%; +float:left; +} +.mark-top { +border-top-width:1px; +border-top-style:solid; +} /* NOTICES */ @@ -813,75 +824,249 @@ text-transform:lowercase; } -.notice-options { -padding-left:2%; +.notice .notice-options a, +.notice .notice-options input { float:left; -width:50%; -position:relative; -font-size:0.95em; -width:12.5%; -float:right; +font-size:1.025em; } -.notice-options a { -float:left; +.notice div.entry-content .timestamp { +display:inline-block; } -.notice-options .notice_delete, -.notice-options .notice_reply, -.notice-options .form_favor, -.notice-options .form_disfavor { -position:absolute; -top:0; + +.entry-content .repeat { +display:block; } -.notice-options .form_favor, -.notice-options .form_disfavor { +.entry-content .repeat .photo { +float:none; +margin-right:1px; +position:relative; +top:4px; left:0; } -.notice-options .notice_reply { -left:29px; -} -.notice-options .notice_delete { -right:0; -} -.notice-options .notice_reply dt { -display:none; + +.dialogbox { +position:absolute; +top:-1px; +right:-1px; +z-index:9; +float:none; +padding:11px; +border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +border-style:solid; +border-width:1px; +} + +.dialogbox legend { +display:block !important; +margin-right:18px; +margin-bottom:18px; +} + +.dialogbox button.close { +position:absolute; +right:3px; +top:3px; +} + +.dialogbox .form_guide { +font-weight:normal; +padding:0; +} + +.dialogbox .submit_dialogbox { +font-weight:bold; +text-indent:0; +min-width:46px; +} +.dialogbox input { +padding-left:4px; +} +.dialogbox fieldset { +margin-bottom:0; +} + +#wrap form.processing input.submit, +.entity_actions a.processing, +.dialogbox.processing .submit_dialogbox { +cursor:wait; +outline:none; +text-indent:-9999px; +} + +.form_repeat.dialogbox { +top:-4px; +right:29px; +min-width:199px; +} + +.notice-options { +position:relative; +font-size:0.95em; +width:113px; +float:right; +margin-top:3px; +margin-right:4px; } -.notice-options input, .notice-options a { +float:left; +} +.notice-options .notice_reply, +.notice-options .form_repeat, +.notice-options .form_favor, +.notice-options .form_disfavor, +.notice-options .repeated { +float:left; +margin-left:14.2%; +} +.notice-options .form_favor, +.notice-options .form_disfavor { +margin-left:0; +} +.notice-options input, +.notice-options a, +.notice-options .repeated { text-indent:-9999px; outline:none; } - -.notice-options .notice_reply a, .notice-options input.submit { display:block; border:0; } -.notice-options .notice_reply a, -.notice-options .notice_delete a { +.notice-options .notice_reply, +.notice-options .notice_delete { text-decoration:none; -padding-left:16px; } - +.notice .notice-options .notice_delete { +float:right; +} .notice-options form input.submit { width:16px; -padding:2px 0; +height:16px; +padding:0; +border-radius:0; +-moz-border-radius:0; +-webkit-border-radius:0; } - -.notice-options .notice_delete dt, +.notice-options .form_repeat legend, .notice-options .form_favor legend, .notice-options .form_disfavor legend { display:none; } -.notice-options .notice_delete fieldset, +.notice-options .form_repeat fieldset, .notice-options .form_favor fieldset, .notice-options .form_disfavor fieldset { border:0; padding:0; } +.notice-options a, +.notice-options .repeated { +width:16px; +height:16px; +} +.notice .attachment { +position:relative; +padding-left:16px; +} +.notice .attachment.more { +text-indent:-9999px; +width:16px; +height:16px; +display:inline-block; +overflow:hidden; +vertical-align:middle; +margin-left:4px; +} + +#attachments .attachment, +.notice .attachment.more { +padding-left:0; +} +.notice .attachment img { +position:absolute; +top:18px; +left:0; +z-index:99; +} +#shownotice .notice .attachment img { +position:static; +} + +#attachments { +clear:both; +float:left; +width:100%; +margin-top:18px; +} +#attachments dt { +font-weight:bold; +font-size:1.3em; +margin-bottom:4px; +} + +#attachments ol li { +margin-bottom:18px; +list-style-type:decimal; +float:left; +clear:both; +} + +#jOverlayContent, +#jOverlayContent #content, +#jOverlayContent #content_inner { +width: auto !important; +margin-bottom:0; +} +#jOverlayContent #content { +padding:11px; +min-height:auto; +} +#jOverlayContent .entry-title { +display:block; +margin-bottom:11px; +} +#jOverlayContent button { +position:absolute; +top:0; +right:0; +} +#jOverlayContent h1 { +max-width:425px; +} +#jOverlayContent #content { +border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +} +#jOverlayLoading { +top:5%; +left:40%; +} +#attachment_view img { +max-width:480px; +max-height:480px; +} +#attachment_view #oembed_info { +margin-top:11px; +} +#attachment_view #oembed_info dt, +#attachment_view #oembed_info dd { +float:left; +} +#attachment_view #oembed_info dt { +clear:left; +margin-right:11px; +font-weight:bold; +} +#attachment_view #oembed_info dt:after { +content: ":"; +} + #usergroups #new_group { float: left; margin-right: 2em; diff --git a/theme/h4ck3r/css/display.css b/theme/h4ck3r/css/display.css index 7112765abd..276659dced 100644 --- a/theme/h4ck3r/css/display.css +++ b/theme/h4ck3r/css/display.css @@ -200,14 +200,19 @@ background:transparent url(../../base/images/icons/twotone/green/disfavourite.gi background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%; } +.notices .attachment.more, .notices div.entry-content, .notices div.notice-options { opacity:0.4; } -.notices li.hover div.entry-content, -.notices li.hover div.notice-options { +.notices li:hover .attachment.more, +.notices li:hover div.entry-content, +.notices li:hover div.notice-options { opacity:1; } +.opaque { +opacity:1 !important; +} div.entry-content { color:#ccc; } diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css index 3972657a79..440dd8be22 100644 --- a/theme/identica/css/display.css +++ b/theme/identica/css/display.css @@ -215,7 +215,7 @@ background-color:transparent; } #wrap form.processing input.submit, -#content a.processing, +#core a.processing, .dialogbox.processing .submit_dialogbox { background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; } diff --git a/theme/pigeonthoughts/css/base.css b/theme/pigeonthoughts/css/base.css index bd12e6eaac..bc2e24dc50 100644 --- a/theme/pigeonthoughts/css/base.css +++ b/theme/pigeonthoughts/css/base.css @@ -792,25 +792,30 @@ margin-right:11px; /* NOTICE */ .notice, -.profile { +.profile, +.application { position:relative; -padding:11px 2%; +padding-top:11px; +padding-bottom:11px; clear:both; float:left; -width:95.7%; -border-width:1px; -border-style:solid; -margin-bottom:11px; +width:100%; +border-top-width:1px; +border-top-style:dotted; } .notices li { list-style-type:none; } .notices .notices { margin-top:7px; -margin-left:5%; -width:95%; +margin-left:2%; +width:98%; float:left; } +.mark-top { +border-top-width:1px; +border-top-style:solid; +} #aside_primary .notice, #aside_primary .profile { @@ -970,36 +975,38 @@ outline:none; text-indent:-9999px; } +.form_repeat.dialogbox { +top:-4px; +right:29px; +min-width:199px; +} + .notice-options { position:relative; font-size:0.95em; -width:90px; +width:113px; float:right; -margin-right:11px; +margin-top:3px; +margin-right:4px; } - .notice-options a { float:left; } -.notice-options .notice_delete, .notice-options .notice_reply, +.notice-options .form_repeat, .notice-options .form_favor, -.notice-options .form_disfavor { -position:absolute; -top:0; +.notice-options .form_disfavor, +.notice-options .repeated { +float:left; +margin-left:14.2%; } .notice-options .form_favor, .notice-options .form_disfavor { -left:0; -} -.notice-options .notice_reply { -left:29px; -} -.notice-options .notice_delete { -right:0; +margin-left:0; } .notice-options input, -.notice-options a { +.notice-options a, +.notice-options .repeated { text-indent:-9999px; outline:none; } @@ -1010,27 +1017,51 @@ border:0; .notice-options .notice_reply, .notice-options .notice_delete { text-decoration:none; -padding-left:16px; +} +.notice .notice-options .notice_delete { +float:right; } .notice-options form input.submit { width:16px; -padding:2px 0; +height:16px; +padding:0; +border-radius:0; +-moz-border-radius:0; +-webkit-border-radius:0; } +.notice-options .form_repeat legend, .notice-options .form_favor legend, .notice-options .form_disfavor legend { display:none; } +.notice-options .form_repeat fieldset, .notice-options .form_favor fieldset, .notice-options .form_disfavor fieldset { border:0; padding:0; } +.notice-options a, +.notice-options .repeated { +width:16px; +height:16px; +} .notice .attachment { position:relative; padding-left:16px; } -#attachments .attachment { +.notice .attachment.more { +text-indent:-9999px; +width:16px; +height:16px; +display:inline-block; +overflow:hidden; +vertical-align:middle; +margin-left:4px; +} + +#attachments .attachment, +.notice .attachment.more { padding-left:0; } .notice .attachment img { diff --git a/theme/pigeonthoughts/css/display.css b/theme/pigeonthoughts/css/display.css index de5164ea82..e584683fcd 100644 --- a/theme/pigeonthoughts/css/display.css +++ b/theme/pigeonthoughts/css/display.css @@ -141,7 +141,6 @@ background-color:transparent; color:#000000; } - .aside .section { border-color:#FFFFFF; background-color:#FFFFFF; @@ -173,6 +172,11 @@ color:#7F1114; color:#FFFFFF; } +.aside .section .dialogbox { +color:#000000; +} + + .section .profile { border-top-color:#87B4C8; @@ -246,7 +250,7 @@ background-color:transparent; #wrap form.processing input.submit, -.entity_actions a.processing, +#core a.processing, .dialogbox.processing .submit_dialogbox { background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; } @@ -254,6 +258,13 @@ background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47 background-image:none; } +.entity_send-a-message .form_notice, +.entity_moderation:hover ul, +.entity_role:hover ul, +.dialogbox { +background-color:#FFFFFF; +} + #content, #site_nav_local_views a { border-color:#FFFFFF; @@ -414,6 +425,9 @@ background-position: 0 -1714px; .notice .attachment { background-position:0 -394px; } +.notice .attachment.more { +background-position:0 -2770px; +} #attachments .attachment { background:none; } @@ -436,15 +450,19 @@ background-position:0 -1582px; background-position:0 -1648px; } - +.notices .attachment.more, .notices div.entry-content, .notices div.notice-options { opacity:0.4; } +.notices li:hover .attachment.more, .notices li:hover div.entry-content, .notices li:hover div.notice-options { opacity:1; } +.opaque { +opacity:1 !important; +} div.entry-content { color:#333333; }