diff --git a/README b/README index 3507fa420b..74ef138a2a 100644 --- a/README +++ b/README @@ -2,8 +2,8 @@ README ------ -StatusNet 0.9.6 "Man on the Moon" -29 October 2010 +StatusNet 0.9.7 "World Leader Pretend" +17 March 2011 This is the README file for StatusNet, the Open Source microblogging platform. It includes installation instructions, descriptions of @@ -96,43 +96,47 @@ for additional terms. New this version ================ -This is a security, bug and feature release since version 0.9.5 released on -10 September 2010. +This is a security, bug and feature release since version 0.9.6 released on +23 October 2010. -For best compatibility with client software and site federation, and a lot of -bug fixes, it is highly recommended that all public sites upgrade to the new -version. +For best compatibility with client software and site federation, and a +lot of bug fixes, it is highly recommended that all public sites +upgrade to the new version. Upgrades require new database indexes for +best performance; see Upgrade below. Notable changes this version: -- Site moderators can now delete groups. -- New themes: clean, shiny, mnml, victorian -- New YammerImport plugin allows site admins to import non-private profiles and - message from an authenticated Yammer site. -- New experimental plugins: AnonFavorites, SlicedFavorites, GroupFavorited, - ForceGroup, ShareNotice -- OAuth upgraded to 1.0a -- Localization updates now include plugins, thanks to translatewiki.net! -- SSL link generation should be more consistent; alternate SSL URLs can be - set in the admin UI for more parts of the system. -- Experimental backupuser.php, restoreuser.php command-line scripts to - dump/restore a user's complete activity stream. Can be used to transfer - accounts manually between sites, or to save a backup before deleting. -- Unicode fixes for OStatus notices -- Header metadata on notice pages to aid in manual reposting on Facebook -- Lots of little fixes... +- GroupPrivateMessage plugin lets users send private messages + to a group. (Similar to "private groups" on Yammer.) +- Support for Twitter streaming API in Twitter bridge plugin +- Support for a new Activity Streams-based API using AtomPub, allowing + richer API data. See http://status.net/wiki/AtomPub for details. +- Unified Facebook plugin, replacing previous Facebook application + and Facebook Connect plugin. +- A plugin to send out a daily summary email to network users. +- In-line thumbnails of some attachments (video, images) and oEmbed objects. +- Local copies of remote profiles to let moderators manage OStatus users. +- Upgrade upstream JS, minify everything. +- Allow pushing plugin JS, CSS, and static files to a CDN. +- Configurable nickname rules. +- Better support for bit.ly URL shortener. +- InProcessCache plugin for additional caching on top of memcached. +- Support for Activity Streams JSON feeds on many streams. +- User-initiated backup and restore of account data in Activity Streams + format. +- Bookmark plugin for making del.icio.us-like social bookmarking sites, + including del.icio.us backup file import. Supports OStatus. +- SQLProfile plugin to tune SQL queries. +- Better sorting on timelines to support restored or imported data. +- Hundreds of translations from http://translatewiki.net/ +- Hundreds of performance tunings, bug fixes, and UI improvements. +- Remove deprecated data from Activity Streams Atom output, to the + extent possible. +- NewMenu plugin for new layout of menu items. +- Experimental support for moving an account from one server to + another, using new AtomPub API. -Changes from 0.9.6 release candidate 1: -- fix for broken group pages when logged out -- fix for stuck ping queue entries when bad profile -- fix for bogus single-user nickname config entry error -- i18n updates -- nofollow updates -- SSL-only mode secure cookie fix -- experimental ApiLogger plugin for usage data gathering -- experimental follow-everyone plugin - -A full changelog is available at http://status.net/wiki/StatusNet_0.9.6. +A full changelog is available at http://status.net/wiki/StatusNet_0.9.7. Prerequisites ============= @@ -243,9 +247,9 @@ especially if you've previously installed PHP/MySQL packages. 1. Unpack the tarball you downloaded on your Web server. Usually a command like this will work: - tar zxf statusnet-0.9.6.tar.gz + tar zxf statusnet-0.9.7.tar.gz - ...which will make a statusnet-0.9.6 subdirectory in your current + ...which will make a statusnet-0.9.7 subdirectory in your current directory. (If you don't have shell access on your Web server, you may have to unpack the tarball on your local computer and FTP the files to the server.) @@ -253,7 +257,7 @@ especially if you've previously installed PHP/MySQL packages. 2. Move the tarball to a directory of your choosing in your Web root directory. Usually something like this will work: - mv statusnet-0.9.6 /var/www/statusnet + mv statusnet-0.9.7 /var/www/statusnet This will make your StatusNet instance available in the statusnet path of your server, like "http://example.net/statusnet". "microblog" or @@ -668,7 +672,7 @@ with this situation. If you've been using StatusNet 0.7, 0.6, 0.5 or lower, or if you've been tracking the "git" version of the software, you will probably want to upgrade and keep your existing data. There is no automated -upgrade procedure in StatusNet 0.9.6. Try these step-by-step +upgrade procedure in StatusNet 0.9.7. Try these step-by-step instructions; read to the end first before trying them. 0. Download StatusNet and set up all the prerequisites as if you were @@ -689,25 +693,30 @@ instructions; read to the end first before trying them. 5. Once all writing processes to your site are turned off, make a final backup of the Web directory and database. 6. Move your StatusNet directory to a backup spot, like "statusnet.bak". -7. Unpack your StatusNet 0.9.6 tarball and move it to "statusnet" or +7. Unpack your StatusNet 0.9.7 tarball and move it to "statusnet" or wherever your code used to be. 8. Copy the config.php file and the contents of the avatar/, background/, file/, and local/ subdirectories from your old directory to your new directory. 9. Copy htaccess.sample to .htaccess in the new directory. Change the RewriteBase to use the correct path. -10. Rebuild the database. (You can safely skip this step and go to #12 - if you're upgrading from another 0.9.x version). +10. Rebuild the database. NOTE: this step is destructive and cannot be reversed. YOU CAN EASILY DESTROY YOUR SITE WITH THIS STEP. Don't do it without a known-good backup! - If your database is at version 0.8.0 or above, you can run a + If your database is at version 0.8.0 or higher in the 0.8.x line, you can run a special upgrade script: mysql -u -p db/08to09.sql + If you are upgrading from any 0.9.x version like 0.9.6, run this script: + + mysql -u -p db/096to097.sql + + Despite the name, it should work for any 0.9.x branch. + Otherwise, go to your StatusNet directory and AFTER YOU MAKE A BACKUP run the rebuilddb.sh script like this: @@ -1143,6 +1152,9 @@ ssl: Whether to use SSL for JavaScript files. Default is null, which means sslserver: SSL server to use when page is HTTPS-encrypted. If unspecified, site ssl server and so on will be used. sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted. +bustframes: If true, all web pages will break out of framesets. If false, + can comfortably live in a frame or iframe... probably. Default + to true. xmpp ---- diff --git a/actions/newapplication.php b/actions/newapplication.php index ca5d902a26..93ef417f63 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -295,9 +295,8 @@ class NewApplicationAction extends OwnerDesignAction $app->uploadLogo(); } catch (Exception $e) { $app->query('ROLLBACK'); - // TRANS: Form validation error on New application page when providing an invalid image upload. $this->showForm(_('Invalid image.')); - return; + return; } $app->query('COMMIT'); diff --git a/classes/Notice.php b/classes/Notice.php index ba66dd8054..87363158dd 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -561,10 +561,10 @@ class Notice extends Memcached_DataObject function blowOnInsert($conversation = false) { - self::blow('profile:notice_ids:%d', $this->profile_id); + $this->blowStream('profile:notice_ids:%d', $this->profile_id); if ($this->isPublic()) { - self::blow('public'); + $this->blowStream('public'); } // XXX: Before we were blowing the casche only if the notice id @@ -574,7 +574,7 @@ class Notice extends Memcached_DataObject self::blow('conversation::notice_count:%d', $this->conversation); if (!empty($this->repeat_of)) { - self::blow('notice:repeats:%d', $this->repeat_of); + $this->blowStream('notice:repeats:%d', $this->repeat_of); } $original = Notice::staticGet('id', $this->repeat_of); @@ -582,11 +582,12 @@ class Notice extends Memcached_DataObject if (!empty($original)) { $originalUser = User::staticGet('id', $original->profile_id); if (!empty($originalUser)) { - self::blow('user:repeats_of_me:%d', $originalUser->id); + $this->blowStream('user:repeats_of_me:%d', $originalUser->id); } } $profile = Profile::staticGet($this->profile_id); + if (!empty($profile)) { $profile->blowNoticeCount(); } @@ -624,6 +625,42 @@ class Notice extends Memcached_DataObject } } + function blowStream() + { + $c = self::memcache(); + + if (empty($c)) { + return false; + } + + $args = func_get_args(); + + $format = array_shift($args); + + $keyPart = vsprintf($format, $args); + + $cacheKey = Cache::key($keyPart); + + $c->delete($cacheKey); + + // delete the "last" stream, too, if this notice is + // older than the top of that stream + + $lastKey = $cacheKey.';last'; + + $lastStr = $c->get($lastKey); + + if ($lastStr !== false) { + $window = explode(',', $lastStr); + $lastID = $window[0]; + $lastNotice = Notice::staticGet('id', $lastID); + if (empty($lastNotice) // just weird + || strtotime($lastNotice->created) >= strtotime($this->created)) { + $c->delete($lastKey); + } + } + } + /** save all urls in the notice to the db * * follow redirects and save all available file information diff --git a/lib/action.php b/lib/action.php index a16a027573..3492873c59 100644 --- a/lib/action.php +++ b/lib/action.php @@ -335,7 +335,9 @@ class Action extends HTMLOutputter // lawsuit common_local_url('peopletagautocomplete') . '";'); $this->showScriptMessages(); // Frame-busting code to avoid clickjacking attacks. - $this->inlineScript('if (window.top !== window.self) { window.top.location.href = window.self.location.href; }'); + if (common_config('javascript', 'bustframes')) { + $this->inlineScript('if (window.top !== window.self) { window.top.location.href = window.self.location.href; }'); + } Event::handle('EndShowStatusNetScripts', array($this)); Event::handle('EndShowLaconicaScripts', array($this)); } diff --git a/lib/activityimporter.php b/lib/activityimporter.php index bd812609de..da6fc5e321 100644 --- a/lib/activityimporter.php +++ b/lib/activityimporter.php @@ -83,15 +83,9 @@ class ActivityImporter extends QueueHandler Event::handle('EndImportActivity', array($user, $author, $activity, $trusted)); $done = true; - } catch (ClientException $ce) { - common_log(LOG_WARNING, $ce->getMessage()); - $done = true; - } catch (ServerException $se) { - common_log(LOG_ERR, $se->getMessage()); - $done = false; } catch (Exception $e) { common_log(LOG_ERR, $e->getMessage()); - $done = false; + $done = true; } } return $done; diff --git a/lib/default.php b/lib/default.php index eaafee93a0..cdd8e5460d 100644 --- a/lib/default.php +++ b/lib/default.php @@ -156,7 +156,8 @@ $default = 'javascript' => array('server' => null, 'path'=> null, - 'ssl' => null), + 'ssl' => null, + 'bustframes' => true), 'local' => // To override path/server for themes in 'local' dir (not currently applied to local plugins) array('server' => null, 'dir' => null, diff --git a/lib/router.php b/lib/router.php index a155c52372..9ba8051522 100644 --- a/lib/router.php +++ b/lib/router.php @@ -126,6 +126,15 @@ class Router return Router::$inst; } + /** + * Clear the global singleton instance for this class. + * Needed to ensure reset when switching site configurations. + */ + static function clear() + { + Router::$inst = null; + } + function __construct() { if (empty($this->m)) { diff --git a/lib/statusnet.php b/lib/statusnet.php index 648369ec44..292f074199 100644 --- a/lib/statusnet.php +++ b/lib/statusnet.php @@ -108,6 +108,8 @@ class StatusNet */ public static function init($server=null, $path=null, $conffile=null) { + Router::clear(); + StatusNet::initDefaults($server, $path); StatusNet::loadConfigFile($conffile); diff --git a/plugins/Bookmark/Bookmark.php b/plugins/Bookmark/Bookmark.php index 66c451c5c8..6836744799 100644 --- a/plugins/Bookmark/Bookmark.php +++ b/plugins/Bookmark/Bookmark.php @@ -230,8 +230,23 @@ class Bookmark extends Memcached_DataObject if (array_key_exists('uri', $options)) { $nb->uri = $options['uri']; } else { - $nb->uri = common_local_url('showbookmark', - array('id' => $nb->id)); + // FIXME: hacks to work around router bugs in + // queue daemons + + $r = Router::get(); + + $path = $r->build('showbookmark', + array('id' => $nb->id)); + + if (empty($path)) { + $nb->uri = common_path('bookmark/'.$nb->id, false, false); + } else { + $nb->uri = common_local_url('showbookmark', + array('id' => $nb->id), + null, + null, + false); + } } $nb->insert(); @@ -313,11 +328,20 @@ class Bookmark extends Memcached_DataObject $options['uri'] = $nb->uri; } - $saved = Notice::saveNew($profile->id, - $content, - array_key_exists('source', $options) ? - $options['source'] : 'web', - $options); + try { + $saved = Notice::saveNew($profile->id, + $content, + array_key_exists('source', $options) ? + $options['source'] : 'web', + $options); + } catch (Exception $e) { + $nb->delete(); + throw $e; + } + + if (empty($saved)) { + $nb->delete(); + } return $saved; } diff --git a/plugins/Bookmark/deliciousbackupimporter.php b/plugins/Bookmark/deliciousbackupimporter.php index 1eb015f380..54a618b73a 100644 --- a/plugins/Bookmark/deliciousbackupimporter.php +++ b/plugins/Bookmark/deliciousbackupimporter.php @@ -76,6 +76,12 @@ class DeliciousBackupImporter extends QueueHandler $doc = $this->importHTML($body); + // If we can't parse it, it's no good + + if (empty($doc)) { + return true; + } + $dls = $doc->getElementsByTagName('dl'); if ($dls->length != 1) { @@ -110,9 +116,11 @@ class DeliciousBackupImporter extends QueueHandler case 'dd': $dd = $child; - // This
contains a description for the bookmark in - // the preceding
node. - $saved = $this->importBookmark($user, $dt, $dd); + if (!empty($dt)) { + // This
contains a description for the bookmark in + // the preceding
node. + $saved = $this->importBookmark($user, $dt, $dd); + } $dt = null; $dd = null; diff --git a/plugins/GroupPrivateMessage/Group_message_profile.php b/plugins/GroupPrivateMessage/Group_message_profile.php index 20ce0120ac..69cc3e40d4 100644 --- a/plugins/GroupPrivateMessage/Group_message_profile.php +++ b/plugins/GroupPrivateMessage/Group_message_profile.php @@ -125,7 +125,10 @@ class Group_message_profile extends Memcached_DataObject $gmp->insert(); - $gmp->notify(); + // If it's not for the author, send email notification + if ($gm->from_profile != $profile->id) { + $gmp->notify(); + } return $gmp; } diff --git a/plugins/SiteNoticeInSidebar/SiteNoticeInSidebarPlugin.php b/plugins/SiteNoticeInSidebar/SiteNoticeInSidebarPlugin.php new file mode 100644 index 0000000000..4e6aade2a5 --- /dev/null +++ b/plugins/SiteNoticeInSidebar/SiteNoticeInSidebarPlugin.php @@ -0,0 +1,92 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * Put the site notice in the sidebar + * + * @category General + * @package StatusNet + * @author Evan Prodromou + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class SiteNoticeInSidebarPlugin extends Plugin +{ + /** + * Function comment + * + * @param + * + * @return + */ + + function onStartShowSiteNotice($action) + { + return false; + } + + function onStartShowSections($action) + { + $text = common_config('site', 'notice'); + if (!empty($text)) { + $sns = new SiteNoticeSection($action, $text); + $sns->show(); + } + return true; + } + + function onEndShowStyles($action) + { + $action->element('style', null, '#site_notice { width: 100% }'); + return true; + } + + function onAutoload($cls) + { + $dir = dirname(__FILE__); + + switch ($cls) + { + case 'SiteNoticeSection': + include_once $dir . '/'.strtolower($cls).'.php'; + return false; + default: + return true; + } + } +} diff --git a/plugins/SiteNoticeInSidebar/sitenoticesection.php b/plugins/SiteNoticeInSidebar/sitenoticesection.php new file mode 100644 index 0000000000..37c37edaf2 --- /dev/null +++ b/plugins/SiteNoticeInSidebar/sitenoticesection.php @@ -0,0 +1,67 @@ +. + * + * @category Site + * @package StatusNet + * @author Evan Prodromou + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * Site notice section + * + * @category Site + * @package StatusNet + * @author Evan Prodromou + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class SiteNoticeSection extends Section +{ + var $text; + + function __construct($action, $text) + { + parent::__construct($action); + $this->text = $text; + } + + function title() + { + return _('Site notice'); + } + + function showContent() + { + $this->out->raw($this->text); + } +}