Use CommonMark plugin for @-mentions
This commit is contained in:
parent
08b3691aeb
commit
3ff4149304
6 changed files with 89 additions and 17 deletions
17
app/CommonMark/Generators/ContactMentionGenerator.php
Normal file
17
app/CommonMark/Generators/ContactMentionGenerator.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\CommonMark\Generators;
|
||||||
|
|
||||||
|
use League\CommonMark\Extension\Mention\Generator\MentionGeneratorInterface;
|
||||||
|
use League\CommonMark\Extension\Mention\Mention;
|
||||||
|
use League\CommonMark\Node\Inline\AbstractInline;
|
||||||
|
|
||||||
|
class ContactMentionGenerator implements MentionGeneratorInterface
|
||||||
|
{
|
||||||
|
public function generateMention(Mention $mention): ?AbstractInline
|
||||||
|
{
|
||||||
|
return $mention;
|
||||||
|
}
|
||||||
|
}
|
24
app/CommonMark/Renderers/ContactMentionRenderer.php
Normal file
24
app/CommonMark/Renderers/ContactMentionRenderer.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\CommonMark\Renderers;
|
||||||
|
|
||||||
|
use App\Models\Contact;
|
||||||
|
use League\CommonMark\Node\Node;
|
||||||
|
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||||
|
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||||
|
|
||||||
|
class ContactMentionRenderer implements NodeRendererInterface
|
||||||
|
{
|
||||||
|
public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
|
||||||
|
{
|
||||||
|
$contact = Contact::where('nick', $node->getIdentifier())->first();
|
||||||
|
|
||||||
|
if ($contact === null) {
|
||||||
|
return '<a href="https://twitter.com/' . $node->getIdentifier() . '">@' . $node->getIdentifier() . '</a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim(view('templates.mini-hcard', ['contact' => $contact])->render());
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,10 +4,9 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
|
|
||||||
class Contact extends Model
|
class Contact extends Model
|
||||||
{
|
{
|
||||||
|
@ -26,4 +25,20 @@ class Contact extends Model
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $fillable = ['nick', 'name', 'homepage', 'twitter', 'facebook'];
|
protected $fillable = ['nick', 'name', 'homepage', 'twitter', 'facebook'];
|
||||||
|
|
||||||
|
protected function photo(): Attribute
|
||||||
|
{
|
||||||
|
$photo = '/assets/profile-images/default-image';
|
||||||
|
|
||||||
|
if (array_key_exists('homepage', $this->attributes) && !empty($this->attributes['homepage'])) {
|
||||||
|
$host = parse_url($this->attributes['homepage'], PHP_URL_HOST);
|
||||||
|
if (file_exists(public_path() . '/assets/profile-images/' . $host . '/image')) {
|
||||||
|
$photo = '/assets/profile-images/' . $host . '/image';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Attribute::make(
|
||||||
|
get: fn () => $photo,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,14 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\CommonMark\Generators\ContactMentionGenerator;
|
||||||
|
use App\CommonMark\Renderers\ContactMentionRenderer;
|
||||||
use App\Exceptions\TwitterContentException;
|
use App\Exceptions\TwitterContentException;
|
||||||
use Barryvdh\LaravelIdeHelper\Eloquent;
|
|
||||||
use Codebird\Codebird;
|
use Codebird\Codebird;
|
||||||
use Exception;
|
use Exception;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Illuminate\Database\Eloquent\Relations\{BelongsTo, BelongsToMany, HasMany, MorphMany};
|
|
||||||
use Illuminate\Database\Eloquent\{Builder, Factories\HasFactory, Model, SoftDeletes};
|
use Illuminate\Database\Eloquent\{Builder, Factories\HasFactory, Model, SoftDeletes};
|
||||||
|
use Illuminate\Database\Eloquent\Relations\{BelongsTo, BelongsToMany, HasMany, MorphMany};
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use JetBrains\PhpStorm\ArrayShape;
|
use JetBrains\PhpStorm\ArrayShape;
|
||||||
use Jonnybarnes\IndieWeb\Numbers;
|
use Jonnybarnes\IndieWeb\Numbers;
|
||||||
|
@ -19,6 +20,8 @@ use League\CommonMark\Extension\Autolink\AutolinkExtension;
|
||||||
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
||||||
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
|
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
|
||||||
use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode;
|
use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode;
|
||||||
|
use League\CommonMark\Extension\Mention\Mention;
|
||||||
|
use League\CommonMark\Extension\Mention\MentionExtension;
|
||||||
use League\CommonMark\MarkdownConverter;
|
use League\CommonMark\MarkdownConverter;
|
||||||
use Normalizer;
|
use Normalizer;
|
||||||
use Spatie\CommonMarkHighlighter\{FencedCodeRenderer, IndentedCodeRenderer};
|
use Spatie\CommonMarkHighlighter\{FencedCodeRenderer, IndentedCodeRenderer};
|
||||||
|
@ -172,8 +175,7 @@ class Note extends Model
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$hcards = $this->makeHCards($value);
|
$hashtags = $this->autoLinkHashtag($value);
|
||||||
$hashtags = $this->autoLinkHashtag($hcards);
|
|
||||||
|
|
||||||
return $this->convertMarkdown($hashtags);
|
return $this->convertMarkdown($hashtags);
|
||||||
}
|
}
|
||||||
|
@ -377,9 +379,11 @@ class Note extends Model
|
||||||
*/
|
*/
|
||||||
public function getTwitterContentAttribute(): string
|
public function getTwitterContentAttribute(): string
|
||||||
{
|
{
|
||||||
|
$this->getContacts();
|
||||||
|
|
||||||
// check for contacts
|
// check for contacts
|
||||||
if ($this->contacts === null || count($this->contacts) === 0) {
|
if ($this->contacts === null || count($this->contacts) === 0) {
|
||||||
throw new TwitterContentException('There are no contacts for this note');
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// here we check the matched contact from the note corresponds to a contact
|
// here we check the matched contact from the note corresponds to a contact
|
||||||
|
@ -388,10 +392,10 @@ class Note extends Model
|
||||||
count(array_unique(array_values($this->contacts))) === 1
|
count(array_unique(array_values($this->contacts))) === 1
|
||||||
&& array_unique(array_values($this->contacts))[0] === null
|
&& array_unique(array_values($this->contacts))[0] === null
|
||||||
) {
|
) {
|
||||||
throw new TwitterContentException('The matched contact is not in the database');
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// swap in twitter usernames
|
// swap in Twitter usernames
|
||||||
$swapped = preg_replace_callback(
|
$swapped = preg_replace_callback(
|
||||||
self::USERNAMES_REGEX,
|
self::USERNAMES_REGEX,
|
||||||
function ($matches) {
|
function ($matches) {
|
||||||
|
@ -406,7 +410,7 @@ class Note extends Model
|
||||||
|
|
||||||
return $contact->name;
|
return $contact->name;
|
||||||
},
|
},
|
||||||
$this->getOriginal('note')
|
$this->getRawOriginal('note')
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->convertMarkdown($swapped);
|
return $this->convertMarkdown($swapped);
|
||||||
|
@ -527,9 +531,21 @@ class Note extends Model
|
||||||
*/
|
*/
|
||||||
private function convertMarkdown(string $note): string
|
private function convertMarkdown(string $note): string
|
||||||
{
|
{
|
||||||
$environment = new Environment();
|
$config = [
|
||||||
|
'mentions' => [
|
||||||
|
'contacts_handle' => [
|
||||||
|
'prefix' => '@',
|
||||||
|
'pattern' => '[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}(?!\w)',
|
||||||
|
'generator' => new ContactMentionGenerator(),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$environment = new Environment($config);
|
||||||
$environment->addExtension(new CommonMarkCoreExtension());
|
$environment->addExtension(new CommonMarkCoreExtension());
|
||||||
$environment->addExtension(new AutolinkExtension());
|
$environment->addExtension(new AutolinkExtension());
|
||||||
|
$environment->addExtension(new MentionExtension());
|
||||||
|
$environment->addRenderer(Mention::class, new ContactMentionRenderer());
|
||||||
$environment->addRenderer(FencedCode::class, new FencedCodeRenderer());
|
$environment->addRenderer(FencedCode::class, new FencedCodeRenderer());
|
||||||
$environment->addRenderer(IndentedCode::class, new IndentedCodeRenderer());
|
$environment->addRenderer(IndentedCode::class, new IndentedCodeRenderer());
|
||||||
$markdownConverter = new MarkdownConverter($environment);
|
$markdownConverter = new MarkdownConverter($environment);
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
@if($media->type == 'download') <p><a class="u-attachment" href="{{ $media->url }}">Download the attached media</a></p>@endif
|
@if($media->type == 'download') <p><a class="u-attachment" href="{{ $media->url }}">Download the attached media</a></p>@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
@php
|
@if ($note->twitter_content)
|
||||||
try {
|
<div class="p-bridgy-twitter-content">
|
||||||
echo '<div class="p-bridgy-twitter-content">' . $note->twitter_content . '</div>';
|
{!! $note->twitter_content !!}
|
||||||
} catch (App\Exceptions\TwitterContentException $exception) {}
|
</div>
|
||||||
@endphp
|
@endif
|
||||||
<div class="note-metadata">
|
<div class="note-metadata">
|
||||||
<div>
|
<div>
|
||||||
<a class="u-url" href="/notes/{{ $note->nb60id }}"><time class="dt-published" datetime="{{ $note->iso8601 }}" title="{{ $note->iso8601 }}">{{ $note->humandiff }}</time></a>@if($note->client) via <a class="client" href="{{ $note->client->client_url }}">{{ $note->client->client_name }}</a>@endif
|
<a class="u-url" href="/notes/{{ $note->nb60id }}"><time class="dt-published" datetime="{{ $note->iso8601 }}" title="{{ $note->iso8601 }}">{{ $note->humandiff }}</time></a>@if($note->client) via <a class="client" href="{{ $note->client->client_url }}">{{ $note->client->client_name }}</a>@endif
|
||||||
|
|
|
@ -25,6 +25,6 @@ class BridgyPosseTest extends TestCase
|
||||||
$response = $this->get($note->longurl);
|
$response = $this->get($note->longurl);
|
||||||
|
|
||||||
$html = $response->content();
|
$html = $response->content();
|
||||||
$this->assertStringContainsString('p-bridgy-twitter-content', $html);
|
$this->assertStringContainsString('Hi @joe__', $html);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue