diff --git a/CHANGELOG.md b/CHANGELOG.md
index c726d7f3..933bd71d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
- Updated StatusController, restrict edits to 24 hours ([ae24433b](https://github.com/pixelfed/pixelfed/commit/ae24433b))
- Updated RateLimit, add max post edits per hour and day ([51fbfcdc](https://github.com/pixelfed/pixelfed/commit/51fbfcdc))
- Updated Timeline.vue, move announcements from sidebar to top of timeline ([228f5044](https://github.com/pixelfed/pixelfed/commit/228f5044))
+- Updated lexer autolinker and extractor, add support for mentioned usernames containing dashes, periods and underscore characters ([f911c96d](https://github.com/pixelfed/pixelfed/commit/f911c96d))
## [v0.10.8 (2020-01-29)](https://github.com/pixelfed/pixelfed/compare/v0.10.7...v0.10.8)
### Added
diff --git a/app/Services/ActivityPubDeliveryService.php b/app/Services/ActivityPubDeliveryService.php
new file mode 100644
index 00000000..1e1b5851
--- /dev/null
+++ b/app/Services/ActivityPubDeliveryService.php
@@ -0,0 +1,61 @@
+sender = $profile;
+ return $this;
+ }
+
+ public function to(string $url)
+ {
+ $this->to = $url;
+ return $this;
+ }
+
+ public function payload($payload)
+ {
+ $this->payload = $payload;
+ return $this;
+ }
+
+ public function send()
+ {
+ return $this->queueDelivery();
+ }
+
+ protected function queueDelivery()
+ {
+ abort_if(!$this->sender || !$this->to || !$this->payload, 400);
+ abort_if(!Helpers::validateUrl($this->to), 400);
+ abort_if($this->sender->domain != null || $this->sender->status != null, 400);
+
+ $body = $this->payload;
+ $payload = json_encode($body);
+ $headers = HttpSignature::sign($this->sender, $this->to, $body);
+
+ $ch = curl_init($this->to);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
+ curl_setopt($ch, CURLOPT_HEADER, true);
+ curl_exec($ch);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Util/ActivityPub/Helpers.php b/app/Util/ActivityPub/Helpers.php
index e6d85bae..da74a1cb 100644
--- a/app/Util/ActivityPub/Helpers.php
+++ b/app/Util/ActivityPub/Helpers.php
@@ -23,6 +23,7 @@ use App\Jobs\ImageOptimizePipeline\{ImageOptimize,ImageThumbnail};
use App\Jobs\StatusPipeline\NewStatusPipeline;
use App\Util\ActivityPub\HttpSignature;
use Illuminate\Support\Str;
+use App\Services\ActivityPubDeliveryService;
class Helpers {
@@ -435,35 +436,12 @@ class Helpers {
return self::profileFirstOrNew($url);
}
- public static function sendSignedObject($senderProfile, $url, $body)
+ public static function sendSignedObject($profile, $url, $body)
{
- abort_if(!self::validateUrl($url), 400);
-
- $payload = json_encode($body);
- $headers = HttpSignature::sign($senderProfile, $url, $body);
-
- $ch = curl_init($url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
- curl_setopt($ch, CURLOPT_HEADER, true);
- $response = curl_exec($ch);
- return;
- }
-
- public static function apSignedPostRequest($senderProfile, $url, $body)
- {
- abort_if(!self::validateUrl($url), 400);
-
- $payload = json_encode($body);
- $headers = HttpSignature::sign($senderProfile, $url, $body);
-
- $ch = curl_init($url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
- curl_setopt($ch, CURLOPT_HEADER, true);
- $response = curl_exec($ch);
- return;
+ ActivityPubDeliveryService::queue()
+ ->from($profile)
+ ->to($url)
+ ->payload($body)
+ ->send();
}
}
diff --git a/app/Util/Lexer/Regex.php b/app/Util/Lexer/Regex.php
index c24e0d4b..ecc468d0 100755
--- a/app/Util/Lexer/Regex.php
+++ b/app/Util/Lexer/Regex.php
@@ -162,9 +162,9 @@ abstract class Regex
// look-ahead capture here and don't append $after when we return.
$tmp['valid_mention_preceding_chars'] = '([^a-zA-Z0-9_!#\$%&*@@\/]|^|(?:^|[^a-z0-9_+~.-])RT:?)';
- $re['valid_mentions_or_lists'] = '/'.$tmp['valid_mention_preceding_chars'].'(['.$tmp['at_signs'].'])([a-z0-9_]{1,20})((\/[a-z][a-z0-9_\-]{0,24})?(?=(.*|$))(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i';
+ $re['valid_mentions_or_lists'] = '/'.$tmp['valid_mention_preceding_chars'].'(['.$tmp['at_signs'].'])([a-z0-9_\-.]{1,20})((\/[a-z][a-z0-9_\-]{0,24})?(?=(.*|$))(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i';
- $re['valid_reply'] = '/^(?:['.$tmp['spaces'].'])*['.$tmp['at_signs'].']([a-z0-9_]{1,20})(?=(.*|$))/iu';
+ $re['valid_reply'] = '/^(?:['.$tmp['spaces'].'])*['.$tmp['at_signs'].']([a-z0-9_\-.]{1,20})(?=(.*|$))/iu';
$re['end_mention_match'] = '/\A(?:['.$tmp['at_signs'].']|['.$tmp['latin_accents'].']|:\/\/)/iu';
// URL related hash regex collection
diff --git a/tests/Unit/Lexer/UsernameTest.php b/tests/Unit/Lexer/UsernameTest.php
new file mode 100644
index 00000000..e5c310db
--- /dev/null
+++ b/tests/Unit/Lexer/UsernameTest.php
@@ -0,0 +1,179 @@
+extract($username);
+ $autolink = Autolink::create()->autolink($username);
+ $expectedAutolink = '@dansup';
+ $expectedEntity = [
+ "hashtags" => [],
+ "urls" => [],
+ "mentions" => [
+ "dansup",
+ ],
+ "replyto" => "dansup",
+ "hashtags_with_indices" => [],
+ "urls_with_indices" => [],
+ "mentions_with_indices" => [
+ [
+ "screen_name" => "dansup",
+ "indices" => [
+ 0,
+ 7,
+ ],
+ ],
+ ],
+ ];
+ $this->assertEquals($expectedAutolink, $autolink);
+ $this->assertEquals($expectedEntity, $entities);
+ }
+
+ /** @test **/
+ public function usernameWithPeriod()
+ {
+ $username = '@dansup.two';
+ $autolink = Autolink::create()->autolink($username);
+ $entities = Extractor::create()->extract($username);
+ $expectedAutolink = '@dansup.two';
+ $expectedEntity = [
+ "hashtags" => [],
+ "urls" => [],
+ "mentions" => [
+ "dansup.two",
+ ],
+ "replyto" => "dansup.two",
+ "hashtags_with_indices" => [],
+ "urls_with_indices" => [],
+ "mentions_with_indices" => [
+ [
+ "screen_name" => "dansup.two",
+ "indices" => [
+ 0,
+ 11,
+ ],
+ ],
+ ],
+ ];
+ $this->assertEquals($expectedAutolink, $autolink);
+ $this->assertEquals($expectedEntity, $entities);
+ }
+
+ /** @test **/
+ public function usernameWithDash()
+ {
+ $username = '@dansup-too';
+ $autolink = Autolink::create()->autolink($username);
+ $entities = Extractor::create()->extract($username);
+ $expectedAutolink = '@dansup-too';
+ $expectedEntity = [
+ "hashtags" => [],
+ "urls" => [],
+ "mentions" => [
+ "dansup-too",
+ ],
+ "replyto" => "dansup-too",
+ "hashtags_with_indices" => [],
+ "urls_with_indices" => [],
+ "mentions_with_indices" => [
+ [
+ "screen_name" => "dansup-too",
+ "indices" => [
+ 0,
+ 11,
+ ],
+ ],
+ ],
+ ];
+ $this->assertEquals($expectedAutolink, $autolink);
+ $this->assertEquals($expectedEntity, $entities);
+ }
+
+ /** @test **/
+ public function usernameWithUnderscore()
+ {
+ $username = '@dansup_too';
+ $autolink = Autolink::create()->autolink($username);
+ $entities = Extractor::create()->extract($username);
+ $expectedAutolink = '@dansup_too';
+ $expectedEntity = [
+ "hashtags" => [],
+ "urls" => [],
+ "mentions" => [
+ "dansup_too",
+ ],
+ "replyto" => "dansup_too",
+ "hashtags_with_indices" => [],
+ "urls_with_indices" => [],
+ "mentions_with_indices" => [
+ [
+ "screen_name" => "dansup_too",
+ "indices" => [
+ 0,
+ 11,
+ ],
+ ],
+ ],
+ ];
+ $this->assertEquals($expectedAutolink, $autolink);
+ $this->assertEquals($expectedEntity, $entities);
+ }
+
+ /** @test **/
+ public function multipleMentions()
+ {
+ $text = 'hello @dansup and @pixelfed.team from @username_underscore';
+ $autolink = Autolink::create()->autolink($text);
+ $entities = Extractor::create()->extract($text);
+ $expectedAutolink = 'hello @dansup and @pixelfed.team from @username_underscore';
+ $expectedEntity = [
+ "hashtags" => [],
+ "urls" => [],
+ "mentions" => [
+ "dansup",
+ "pixelfed.team",
+ "username_underscore",
+ ],
+ "replyto" => null,
+ "hashtags_with_indices" => [],
+ "urls_with_indices" => [],
+ "mentions_with_indices" => [
+ [
+ "screen_name" => "dansup",
+ "indices" => [
+ 6,
+ 13,
+ ],
+ ],
+ [
+ "screen_name" => "pixelfed.team",
+ "indices" => [
+ 18,
+ 32,
+ ],
+ ],
+ [
+ "screen_name" => "username_underscore",
+ "indices" => [
+ 38,
+ 58,
+ ],
+ ],
+ ],
+ ];
+
+ $this->assertEquals($expectedAutolink, $autolink);
+ $this->assertEquals($expectedEntity, $entities);
+ }
+
+}
\ No newline at end of file