From 7bdea95ccbf31cf9c9191a93c44dedb22f1fd3df Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 23 Apr 2010 06:55:46 -0700 Subject: Fix to make blowing of replies stream cache more consistent when receiving replies. (Was being done at mail notify time instead of at save time for local replies; now moved to reply save time internally so it can't get forgotten) --- classes/Notice.php | 4 +--- classes/Reply.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 4cf12fc6f..c4a316888 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -985,8 +985,6 @@ class Notice extends Memcached_DataObject $reply->profile_id = $user->id; $id = $reply->insert(); - - self::blow('reply:stream:%d', $user->id); } } @@ -1052,6 +1050,7 @@ class Notice extends Memcached_DataObject throw new ServerException("Couldn't save reply for {$this->id}, {$mentioned->id}"); } else { $replied[$mentioned->id] = 1; + self::blow('reply:stream:%d', $mentioned->id); } } } @@ -1107,7 +1106,6 @@ class Notice extends Memcached_DataObject foreach ($recipientIds as $recipientId) { $user = User::staticGet('id', $recipientId); if (!empty($user)) { - self::blow('reply:stream:%d', $recipientId); mail_notify_attn($user, $this); } } diff --git a/classes/Reply.php b/classes/Reply.php index 659e04c92..dc6296bda 100644 --- a/classes/Reply.php +++ b/classes/Reply.php @@ -22,6 +22,20 @@ class Reply extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + /** + * Wrapper for record insertion to update related caches + */ + function insert() + { + $result = parent::insert(); + + if ($result) { + self::blow('reply:stream:%d', $this->profile_id); + } + + return $result; + } + function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0) { $ids = Notice::stream(array('Reply', '_streamDirect'), -- cgit v1.2.3-54-g00ecf From 67b8b1334fc53fb06f1a751e534533e30b7cfd01 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 23 Apr 2010 07:10:36 -0700 Subject: Fix keys / keyTypes for Blacklist plugin - was spewing notices for undefined array indexes when saving blacklist entries from admin panel --- plugins/Blacklist/Homepage_blacklist.php | 4 ++-- plugins/Blacklist/Nickname_blacklist.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/Blacklist/Homepage_blacklist.php b/plugins/Blacklist/Homepage_blacklist.php index 32080667e..ec89ee4bd 100644 --- a/plugins/Blacklist/Homepage_blacklist.php +++ b/plugins/Blacklist/Homepage_blacklist.php @@ -94,7 +94,7 @@ class Homepage_blacklist extends Memcached_DataObject function keys() { - return array('pattern' => 'K'); + return array_keys($this->keyTypes()); } /** @@ -108,7 +108,7 @@ class Homepage_blacklist extends Memcached_DataObject function keyTypes() { - return $this->keys(); + return array('pattern' => 'K'); } /** diff --git a/plugins/Blacklist/Nickname_blacklist.php b/plugins/Blacklist/Nickname_blacklist.php index 981063144..e8545292d 100644 --- a/plugins/Blacklist/Nickname_blacklist.php +++ b/plugins/Blacklist/Nickname_blacklist.php @@ -88,7 +88,7 @@ class Nickname_blacklist extends Memcached_DataObject function keys() { - return array('pattern' => 'K'); + return array_keys($this->keyTypes()); } /** @@ -99,7 +99,7 @@ class Nickname_blacklist extends Memcached_DataObject function keyTypes() { - return $this->keys(); + return array('pattern' => 'K'); } /** -- cgit v1.2.3-54-g00ecf From 390a2a8624b71be7d598b945452fc6e0000f3df5 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 23 Apr 2010 07:17:52 -0700 Subject: Fix for Blacklist plugin: was saving an empty entry if blacklist was empty, which would match *all* possible nickname registrations, preventing all registration on mozilla.status.net. Now saving only non-empty lines, and only matching non-empty lines so we don't fail if we still have a bogus entry. --- plugins/Blacklist/BlacklistPlugin.php | 4 ++-- plugins/Blacklist/blacklistadminpanel.php | 29 ++++++++++++++--------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/plugins/Blacklist/BlacklistPlugin.php b/plugins/Blacklist/BlacklistPlugin.php index adc4d9d7e..63bffe2c6 100644 --- a/plugins/Blacklist/BlacklistPlugin.php +++ b/plugins/Blacklist/BlacklistPlugin.php @@ -262,7 +262,7 @@ class BlacklistPlugin extends Plugin $patterns = $this->_getUrlPatterns(); foreach ($patterns as $pattern) { - if (preg_match("/$pattern/", $url)) { + if ($pattern != '' && preg_match("/$pattern/", $url)) { return false; } } @@ -285,7 +285,7 @@ class BlacklistPlugin extends Plugin $patterns = $this->_getNicknamePatterns(); foreach ($patterns as $pattern) { - if (preg_match("/$pattern/", $nickname)) { + if ($pattern != '' && preg_match("/$pattern/", $nickname)) { return false; } } diff --git a/plugins/Blacklist/blacklistadminpanel.php b/plugins/Blacklist/blacklistadminpanel.php index b996aba8d..23c503cd8 100644 --- a/plugins/Blacklist/blacklistadminpanel.php +++ b/plugins/Blacklist/blacklistadminpanel.php @@ -88,28 +88,27 @@ class BlacklistadminpanelAction extends AdminPanelAction function saveSettings() { - $nickPatterns = array(); - - $rawNickPatterns = explode("\n", $this->trimmed('blacklist-nicknames')); - - foreach ($rawNickPatterns as $raw) { - $nickPatterns[] = trim($raw); - } - + $nickPatterns = $this->splitPatterns($this->trimmed('blacklist-nicknames')); Nickname_blacklist::saveNew($nickPatterns); - $rawUrlPatterns = explode("\n", $this->trimmed('blacklist-urls')); - $urlPatterns = array(); - - foreach ($rawUrlPatterns as $raw) { - $urlPatterns[] = trim($raw); - } - + $urlPatterns = $this->splitPatterns($this->trimmed('url-nicknames')); Homepage_blacklist::saveNew($urlPatterns); return; } + protected function splitPatterns($text) + { + $patterns = array(); + foreach (explode("\n", $text) as $raw) { + $trimmed = trim($raw); + if ($trimmed != '') { + $patterns[] = $trimmed; + } + } + return $patterns; + } + /** * Validate the values * -- cgit v1.2.3-54-g00ecf From 0f975f42159ce714b59b5d7ca958f3f5ee1b226b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 23 Apr 2010 08:24:53 -0700 Subject: Fix to regression in last commit; wrong field name for homepage blacklist --- plugins/Blacklist/blacklistadminpanel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Blacklist/blacklistadminpanel.php b/plugins/Blacklist/blacklistadminpanel.php index 23c503cd8..4289dec1b 100644 --- a/plugins/Blacklist/blacklistadminpanel.php +++ b/plugins/Blacklist/blacklistadminpanel.php @@ -91,7 +91,7 @@ class BlacklistadminpanelAction extends AdminPanelAction $nickPatterns = $this->splitPatterns($this->trimmed('blacklist-nicknames')); Nickname_blacklist::saveNew($nickPatterns); - $urlPatterns = $this->splitPatterns($this->trimmed('url-nicknames')); + $urlPatterns = $this->splitPatterns($this->trimmed('blacklist-urls')); Homepage_blacklist::saveNew($urlPatterns); return; -- cgit v1.2.3-54-g00ecf From 4beaba9fb013ab32be0e07fe4d25a622c95b8a06 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 23 Apr 2010 11:28:50 -0700 Subject: Ticket #93: pretty up the auto-submit for OpenID logins a bit. * throwing in our spinner * cleanup of texts * "If this doesn't go through click the button" instead of just a mystery button * slightly faster submission: immediate at end of page rather than waiting for jQuery to confirm document setup completion --- plugins/OpenID/openid.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/plugins/OpenID/openid.php b/plugins/OpenID/openid.php index 152438917..4ec336e1c 100644 --- a/plugins/OpenID/openid.php +++ b/plugins/OpenID/openid.php @@ -299,11 +299,21 @@ class AutosubmitAction extends Action function title() { - return _m('OpenID Auto-Submit'); + return _m('OpenID Login Submission'); } function showContent() { + $this->raw('

'); + // @fixme this would be better using standard CSS class, but the present theme's a bit scary. + $this->element('img', array('src' => Theme::path('images/icons/icon_processing.gif', 'base'), + // for some reason the base CSS sets s as block display?! + 'style' => 'display: inline')); + $this->text(_m('Requesting authorization from your login provider...')); + $this->raw('

'); + $this->raw('

'); + $this->text(_m('If you are not redirected to your login provider in a few seconds, try pushing the button below.')); + $this->raw('

'); $this->raw($this->form_html); } @@ -311,8 +321,6 @@ class AutosubmitAction extends Action { parent::showScripts(); $this->element('script', null, - '$(document).ready(function() { ' . - ' $(\'#'. $this->form_id .'\').submit(); '. - '});'); + 'document.getElementById(\'' . $this->form_id . '\').submit();'); } } -- cgit v1.2.3-54-g00ecf From 9c8052e755e5ad4c8120ace9acdd75ee910e2ab7 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 23 Apr 2010 12:54:31 -0700 Subject: Rerun feed discovery and update the feed, salmon, and hub for the given OStatus remote profile. Restarts subscription fresh as well. update-profile.php -sexample.com http://example.com/path/to/profile/url --- plugins/OStatus/scripts/update-profile.php | 147 +++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 plugins/OStatus/scripts/update-profile.php diff --git a/plugins/OStatus/scripts/update-profile.php b/plugins/OStatus/scripts/update-profile.php new file mode 100644 index 000000000..d06de4f90 --- /dev/null +++ b/plugins/OStatus/scripts/update-profile.php @@ -0,0 +1,147 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); + +$helptext = <<uri\n"; +showProfile($oprofile); + +print "\n"; +print "Re-running feed discovery for profile URL $oprofile->uri\n"; +// @fixme will bork where the URI isn't the profile URL for now +$discover = new FeedDiscovery(); +$feedurl = $discover->discoverFromURL($oprofile->uri); +$huburi = $discover->getAtomLink('hub'); +$salmonuri = $discover->getAtomLink(Salmon::NS_REPLIES); + +print " Feed URL: $feedurl\n"; +print " Hub URL: $huburi\n"; +print " Salmon URL: $salmonuri\n"; + +if ($feedurl != $oprofile->feeduri || $salmonuri != $oprofile->salmonuri) { + print "\n"; + print "Updating...\n"; + // @fixme update keys :P + #$orig = clone($oprofile); + #$oprofile->feeduri = $feedurl; + #$oprofile->salmonuri = $salmonuri; + #$ok = $oprofile->update($orig); + $ok = $oprofile->query('UPDATE ostatus_profile SET ' . + 'feeduri=\'' . $oprofile->escape($feedurl) . '\',' . + 'salmonuri=\'' . $oprofile->escape($salmonuri) . '\' ' . + 'WHERE uri=\'' . $oprofile->escape($uri) . '\''); + + if (!$ok) { + print "Failed to update profile record...\n"; + exit(1); + } + + $oprofile->decache(); +} else { + print "\n"; + print "Ok, ostatus_profile record unchanged.\n\n"; +} + +$sub = FeedSub::ensureFeed($feedurl); + +if ($huburi != $sub->huburi) { + print "\n"; + print "Updating hub record for feed; was $sub->huburi\n"; + $orig = clone($sub); + $sub->huburi = $huburi; + $ok = $sub->update($orig); + + if (!$ok) { + print "Failed to update sub record...\n"; + exit(1); + } +} else { + print "\n"; + print "Feed record ok, not changing.\n\n"; +} + +print "\n"; +print "Pinging hub $sub->huburi with new subscription for $sub->uri\n"; +$ok = $sub->subscribe(); + +if ($ok) { + print "ok\n"; +} else { + print "Could not confirm.\n"; +} + +$o2 = Ostatus_profile::staticGet('uri', $uri); + +print "\n"; +print "New profile state:\n"; +showProfile($o2); + +print "\n"; +print "New feed state:\n"; +$sub2 = FeedSub::ensureFeed($feedurl); +showSub($sub2); + +function showProfile($oprofile) +{ + print " Feed URL: $oprofile->feeduri\n"; + print " Salmon URL: $oprofile->salmonuri\n"; + print " Avatar URL: $oprofile->avatar\n"; + print " Profile ID: $oprofile->profile_id\n"; + print " Group ID: $oprofile->group_id\n"; + print " Record created: $oprofile->created\n"; + print " Record modified: $oprofile->modified\n"; +} + +function showSub($sub) +{ + print " Subscription state: $sub->sub_state\n"; + print " Verify token: $sub->verify_token\n"; + print " Signature secret: $sub->secret\n"; + print " Sub start date: $sub->sub_start\n"; + print " Record created: $sub->created\n"; + print " Record modified: $sub->modified\n"; +} -- cgit v1.2.3-54-g00ecf From 8fd0059bf69ed16ed4efad7b8e16dc2afda32e18 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 23 Apr 2010 15:40:48 -0700 Subject: Test cases and fixes for Atom and RSS content decoding. Fix extraction of Atom and ; we were failing to escape plaintext source data to HTML, and doing an extraneous double-deescape on HTML source resulting in breakage of notices containing text that looks like HTML. Only was working correctly previously. Fixes for RSS2 content processing: we were failing to load at all due to using wrong element name, and were applying an extraneous de-escape for rather than the escaping that is required to turn plaintext into HTML. (Per spec, must be plaintext.) --- lib/activity.php | 14 ++++++-- lib/activityutils.php | 12 +++++-- tests/ActivityParseTests.php | 77 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 5 deletions(-) diff --git a/lib/activity.php b/lib/activity.php index 5d6230c6d..27f09ab4d 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -83,6 +83,7 @@ class Activity const CREATOR = 'creator'; const CONTENTNS = 'http://purl.org/rss/1.0/modules/content/'; + const ENCODED = 'encoded'; public $actor; // an ActivityObject public $verb; // a string (the URL) @@ -268,14 +269,21 @@ class Activity $this->title = ActivityUtils::childContent($item, ActivityObject::TITLE, self::RSS); - $contentEl = ActivityUtils::child($item, ActivityUtils::CONTENT, self::CONTENTNS); + $contentEl = ActivityUtils::child($item, self::ENCODED, self::CONTENTNS); if (!empty($contentEl)) { - $this->content = htmlspecialchars_decode($contentEl->textContent, ENT_QUOTES); + // XML node's text content is HTML; no further processing needed. + $this->content = $contentEl->textContent; } else { $descriptionEl = ActivityUtils::child($item, self::DESCRIPTION, self::RSS); if (!empty($descriptionEl)) { - $this->content = htmlspecialchars_decode($descriptionEl->textContent, ENT_QUOTES); + // Per spec, must be plaintext. + // In practice, often there's HTML... but these days good + // feeds are using which is explicitly + // real HTML. + // We'll treat this following spec, and do HTML escaping + // to convert from plaintext to HTML. + $this->content = htmlspecialchars($descriptionEl->textContent); } } diff --git a/lib/activityutils.php b/lib/activityutils.php index a7e99fb11..401fd7fc2 100644 --- a/lib/activityutils.php +++ b/lib/activityutils.php @@ -213,11 +213,19 @@ class ActivityUtils // slavishly following http://atompub.org/rfc4287.html#rfc.section.4.1.3.3 if (empty($type) || $type == 'text') { - return $el->textContent; + // We have plaintext saved as the XML text content. + // Since we want HTML, we need to escape any special chars. + return htmlspecialchars($el->textContent); } else if ($type == 'html') { + // We have HTML saved as the XML text content. + // No additional processing required once we've got it. $text = $el->textContent; - return htmlspecialchars_decode($text, ENT_QUOTES); + return $text; } else if ($type == 'xhtml') { + // Per spec, the contains a single + // HTML
with XHTML namespace on it as a child node. + // We need to pull all of that
's child nodes and + // serialize them back to an (X)HTML source fragment. $divEl = ActivityUtils::child($el, 'div', 'http://www.w3.org/1999/xhtml'); if (empty($divEl)) { return null; diff --git a/tests/ActivityParseTests.php b/tests/ActivityParseTests.php index 4563da914..378478d74 100644 --- a/tests/ActivityParseTests.php +++ b/tests/ActivityParseTests.php @@ -32,6 +32,18 @@ class ActivityParseTests extends PHPUnit_Framework_TestCase $this->assertEquals('tag:versioncentral.example.org,2009:/change/1643245', $act->objects[0]->id); } + public function testExample2() + { + global $_example2; + $dom = DOMDocument::loadXML($_example2); + $act = new Activity($dom->documentElement); + + $this->assertFalse(empty($act)); + // Did we handle correctly with a typical payload? + $this->assertEquals("

Geraldine posted a Photo on PhotoPanic

\n " . + "", trim($act->content)); + } + public function testExample3() { global $_example3; @@ -305,6 +317,71 @@ class ActivityParseTests extends PHPUnit_Framework_TestCase } + public function testAtomContent() + { + $tests = array(array("Some regular plain text.", + "Some regular plain text."), + array("<b>this is not HTML</b>", + "<b>this is not HTML</b>"), + array("Some regular plain HTML.", + "Some regular plain HTML."), + array("<b>this is too HTML</b>", + "this is too HTML"), + array("&lt;b&gt;but this is not HTML!&lt;/b&gt;", + "<b>but this is not HTML!</b>"), + array("
Some regular plain XHTML.
", + "Some regular plain XHTML."), + array("
This is some XHTML!
", + "This is some XHTML!"), + array("
<b>This is not some XHTML!</b>
", + "<b>This is not some XHTML!</b>"), + array("
&lt;b&gt;This is not some XHTML either!&lt;/b&gt;
", + "&lt;b&gt;This is not some XHTML either!&lt;/b&gt;")); + foreach ($tests as $data) { + list($source, $output) = $data; + $xml = "" . + "http://example.com/fakeid" . + "Test" . + "Atom content tests" . + $source . + ""; + $dom = DOMDocument::loadXML($xml); + $act = new Activity($dom->documentElement); + + $this->assertFalse(empty($act)); + $this->assertEquals($output, trim($act->content)); + } + } + + public function testRssContent() + { + $tests = array(array("Some regular plain HTML.", + "Some regular plain HTML."), + array("Some <b>exciting bold HTML</b>", + "Some exciting bold HTML"), + array("Some &lt;b&gt;escaped non-HTML.&lt;/b&gt;", + "Some <b>escaped non-HTML.</b>"), + array("Some plain text.", + "Some plain text."), + array("Some <b>non-HTML text</b>", + "Some <b>non-HTML text</b>"), + array("Some &lt;b&gt;double-escaped text&lt;/b&gt;", + "Some &lt;b&gt;double-escaped text&lt;/b&gt;")); + foreach ($tests as $data) { + list($source, $output) = $data; + $xml = "" . + "http://example.com/fakeid" . + "RSS content tests" . + $source . + ""; + $dom = DOMDocument::loadXML($xml); + $act = new Activity($dom->documentElement); + + $this->assertFalse(empty($act)); + $this->assertEquals($output, trim($act->content)); + } + } + } $_example1 = <<