implemented the mention system
This commit is contained in:
parent
a9a22984d2
commit
2c6d0b4404
@ -33,6 +33,36 @@ class ActionsPost
|
||||
];
|
||||
}
|
||||
|
||||
preg_match_all ("/@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?/", $request->get ("content"), $mention_matches);
|
||||
$mentions = $mention_matches [0];
|
||||
$processed_mentions = [];
|
||||
|
||||
foreach ($mentions as $mention)
|
||||
{
|
||||
$ats = explode ("@", $mention);
|
||||
$actor = null;
|
||||
|
||||
if (count ($ats) == 2)
|
||||
{
|
||||
// it's a local user
|
||||
$actor = Actor::where ("preferredUsername", $ats [1])->first ();
|
||||
if (!$actor)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$actor = Actor::where ("local_actor_id", $mention)->first ();
|
||||
if (!$actor)
|
||||
continue;
|
||||
}
|
||||
|
||||
$processed_mentions[] = [
|
||||
"type" => "Mention",
|
||||
"href" => $actor->actor_id,
|
||||
"name" => $mention
|
||||
];
|
||||
}
|
||||
|
||||
$processed_content = Str::markdown ($request->get ("content"));
|
||||
$attachments = [];
|
||||
|
||||
@ -57,7 +87,8 @@ class ActionsPost
|
||||
"content" => $processed_content,
|
||||
"attachments" => $attachments,
|
||||
"inReplyTo" => $request->inReplyTo ?? null,
|
||||
"tags" => $processed_tags
|
||||
"tags" => $processed_tags,
|
||||
"mentions" => $processed_mentions
|
||||
];
|
||||
}
|
||||
|
||||
@ -96,6 +127,7 @@ class ActionsPost
|
||||
"attachments" => $processed ["attachments"],
|
||||
"inReplyTo" => $processed ["inReplyTo"] ?? null,
|
||||
"tags" => $processed ["tags"] ?? null,
|
||||
"mentions" => $processed ["mentions"] ?? null
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\AP;
|
||||
|
||||
use App\Models\Note;
|
||||
use App\Models\NoteAttachment;
|
||||
use App\Models\NoteMention;
|
||||
use App\Models\Announcement;
|
||||
use App\Models\User;
|
||||
use App\Models\Actor;
|
||||
@ -208,6 +209,7 @@ class APOutboxController extends Controller
|
||||
/* if (!$response || $response->getStatusCode () < 200 || $response->getStatusCode () >= 300)
|
||||
return response ()->json ([ "error" => "failed to post activity" ], 500); */
|
||||
|
||||
Log::info ($follow_activity);
|
||||
$follow_activity->delete ();
|
||||
return [
|
||||
"success" => "unfollowed"
|
||||
@ -368,8 +370,27 @@ class APOutboxController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$create_activity = TypeActivity::craft_create ($actor, $note);
|
||||
if (isset ($request ["mentions"]))
|
||||
{
|
||||
foreach ($request ["mentions"] as $mention)
|
||||
{
|
||||
$mention_exists = NoteMention::where ("note_id", $note->id)->where ("object", $mention ["href"])->first ();
|
||||
if ($mention_exists)
|
||||
continue;
|
||||
|
||||
$object = TypeActor::actor_exists ($mention ["href"]);
|
||||
if (!$object)
|
||||
// we don't obtain actors when we are just mentioning them
|
||||
continue;
|
||||
|
||||
$mention = NoteMention::create ([
|
||||
"note_id" => $note->id,
|
||||
"actor_id" => $object->id
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$create_activity = TypeActivity::craft_create ($actor, $note);
|
||||
$note->activity_id = $create_activity->id;
|
||||
$note->save ();
|
||||
|
||||
|
@ -44,10 +44,6 @@ class PostActivityJob implements ShouldQueue
|
||||
|
||||
if ($this->should_sign)
|
||||
{
|
||||
$crafted_activity ["to"] = [
|
||||
"https://www.w3.org/ns/activitystreams#Public",
|
||||
];
|
||||
|
||||
$crafted_activity ["cc"] = [
|
||||
$this->actor->following
|
||||
];
|
||||
|
@ -12,12 +12,16 @@ class Activity extends Model
|
||||
"type",
|
||||
"object",
|
||||
"target",
|
||||
"summary"
|
||||
"summary",
|
||||
"to",
|
||||
"cc"
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
"object" => "array",
|
||||
"target" => "array"
|
||||
"target" => "array",
|
||||
"to" => "array",
|
||||
"cc" => "array"
|
||||
];
|
||||
|
||||
public function setObjectAttribute ($value)
|
||||
@ -25,6 +29,16 @@ class Activity extends Model
|
||||
$this->attributes["object"] = json_encode ($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
|
||||
}
|
||||
|
||||
public function setToAttribute ($value)
|
||||
{
|
||||
$this->attributes["to"] = json_encode ($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
|
||||
}
|
||||
|
||||
public function setCcAttribute ($value)
|
||||
{
|
||||
$this->attributes["cc"] = json_encode ($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
|
||||
}
|
||||
|
||||
public function actor ()
|
||||
{
|
||||
return $this->belongsTo (Actor::class);
|
||||
|
@ -19,8 +19,25 @@ class Note extends Model
|
||||
"attributedTo",
|
||||
"content",
|
||||
"tag",
|
||||
"to",
|
||||
"cc"
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
"to" => "array",
|
||||
"cc" => "array"
|
||||
];
|
||||
|
||||
public function setToAttribute ($value)
|
||||
{
|
||||
$this->attributes["to"] = json_encode ($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
|
||||
}
|
||||
|
||||
public function setCcAttribute ($value)
|
||||
{
|
||||
$this->attributes["cc"] = json_encode ($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
|
||||
}
|
||||
|
||||
public function get_activity ()
|
||||
{
|
||||
return $this->hasOne (Activity::class, "id", "activity_id");
|
||||
@ -57,6 +74,11 @@ class Note extends Model
|
||||
return $this->belongsToMany (Hashtag::class, "note_hashtag");
|
||||
}
|
||||
|
||||
public function get_mentions ()
|
||||
{
|
||||
return $this->hasMany (NoteMention::class);
|
||||
}
|
||||
|
||||
public function attachments ()
|
||||
{
|
||||
return $this->hasMany (NoteAttachment::class);
|
||||
|
23
app/Models/NoteMention.php
Normal file
23
app/Models/NoteMention.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class NoteMention extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
"note_id",
|
||||
"actor_id"
|
||||
];
|
||||
|
||||
public function note ()
|
||||
{
|
||||
return $this->belongsTo (Note::class);
|
||||
}
|
||||
|
||||
public function actor ()
|
||||
{
|
||||
return $this->belongsTo (Actor::class);
|
||||
}
|
||||
}
|
@ -21,6 +21,8 @@ class TypeActivity {
|
||||
"type" => $activity->type,
|
||||
"actor" => $activity->actor,
|
||||
"object" => $activity->object,
|
||||
"to" => $activity->to,
|
||||
"cc" => $activity->cc,
|
||||
"published" => $activity->created_at,
|
||||
];
|
||||
|
||||
@ -96,6 +98,7 @@ class TypeActivity {
|
||||
{
|
||||
case "Note":
|
||||
$create_activity->object = TypeNote::build_response ($fields);
|
||||
$create_activity->cc = $fields["cc"];
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -366,6 +366,7 @@ class TypeActor {
|
||||
return TypeActor::actor_process_ordered_collection ($actor->featured);
|
||||
}
|
||||
|
||||
// TODO: Move this to TypeOrderedCollection
|
||||
public static function actor_process_ordered_collection ($collection_link)
|
||||
{
|
||||
$items = [];
|
||||
|
@ -7,7 +7,7 @@ use App\Models\Hashtag;
|
||||
use App\Models\Actor;
|
||||
use App\Models\Activity;
|
||||
use App\Models\NoteAttachment;
|
||||
|
||||
use App\Models\NoteMention;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@ -27,12 +27,8 @@ class TypeNote
|
||||
"updated" => $note->updated_at,
|
||||
"url" => $note->url,
|
||||
"attributedTo" => $note->attributedTo,
|
||||
"to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc" => [
|
||||
$author->following
|
||||
],
|
||||
"to" => $note->to,
|
||||
"cc" => $note->cc,
|
||||
"content" => $note->content
|
||||
];
|
||||
|
||||
@ -56,6 +52,18 @@ class TypeNote
|
||||
];
|
||||
}
|
||||
|
||||
$mentions = $note->get_mentions ()->get ();
|
||||
foreach ($mentions as $mention)
|
||||
{
|
||||
$response ["tag"] [] = [
|
||||
"type" => "Mention",
|
||||
"href" => $mention->actor->actor_id,
|
||||
"name" => $mention->actor->local_actor_id ?? "@" . $mention->actor->preferredUsername
|
||||
];
|
||||
}
|
||||
|
||||
Log::info (json_encode ($response));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
@ -74,7 +82,12 @@ class TypeNote
|
||||
"summary" => $request ["summary"] ?? null,
|
||||
"attributedTo" => $actor->actor_id,
|
||||
"content" => $request ["content"] ?? null,
|
||||
"tag" => $request ["tag"] ?? null
|
||||
"tag" => $request ["tag"] ?? null,
|
||||
|
||||
// TODO: This should change when I implement visibilities and private notes
|
||||
"cc" => [
|
||||
$actor->followers
|
||||
]
|
||||
]);
|
||||
|
||||
$note->url = route ('posts.show', $note->id);
|
||||
@ -135,37 +148,61 @@ class TypeNote
|
||||
}
|
||||
}
|
||||
|
||||
if (isset ($request ["tag"]) && $request ["tag"])
|
||||
{
|
||||
foreach ($request ["tag"] as $tag)
|
||||
{
|
||||
if ($tag ["type"] != "Hashtag")
|
||||
continue;
|
||||
|
||||
$tag_name = $tag ["name"];
|
||||
|
||||
$hashtag_exists = Hashtag::where ("name", $tag_name)->first ();
|
||||
if ($hashtag_exists)
|
||||
{
|
||||
$note->get_hashtags ()->attach ($hashtag_exists->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
$hashtag = Hashtag::create ([
|
||||
"name" => $tag_name
|
||||
]);
|
||||
$note->get_hashtags ()->attach ($hashtag->id);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request ["inReplyTo"])
|
||||
{
|
||||
$parent_exists = Note::where ("note_id", $request ["inReplyTo"])->first ();
|
||||
if (!$parent_exists)
|
||||
$parent_exists = TypeNote::obtain_external ($request ["inReplyTo"]);
|
||||
|
||||
$note->in_reply_to = $parent_exists ? $parent_exists->note_id : null;
|
||||
}
|
||||
|
||||
if (isset ($request ["tag"]) && $request ["tag"])
|
||||
{
|
||||
foreach ($request ["tag"] as $tag)
|
||||
{
|
||||
$parent = TypeNote::obtain_external ($request ["inReplyTo"]);
|
||||
if ($parent)
|
||||
$note->in_reply_to = $parent->note_id;
|
||||
// TODO: refactor this, this code is shit but I want to get first working first
|
||||
switch ($tag ["type"])
|
||||
{
|
||||
case "Hashtag":
|
||||
$tag_name = $tag ["name"];
|
||||
|
||||
$hashtag_exists = Hashtag::where ("name", $tag_name)->first ();
|
||||
if ($hashtag_exists)
|
||||
{
|
||||
$note->get_hashtags ()->attach ($hashtag_exists->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
$hashtag = Hashtag::create ([
|
||||
"name" => $tag_name
|
||||
]);
|
||||
$note->get_hashtags ()->attach ($hashtag->id);
|
||||
break;
|
||||
|
||||
case "Mention":
|
||||
$mention_name = $tag["name"];
|
||||
$mention_actor = null;
|
||||
|
||||
$actor_exists = Actor::where ("local_actor_id", $mention_name)->first ();
|
||||
if (!$actor_exists)
|
||||
{
|
||||
// let's check if maybe it's local
|
||||
$processed_name = explode ("@", $mention_name);
|
||||
if (count ($processed_name) < 2)
|
||||
continue;
|
||||
|
||||
$actor_exists = Actor::where ("preferredUsername", $processed_name [1])->first ();
|
||||
if (!$actor_exists)
|
||||
continue;
|
||||
}
|
||||
|
||||
$mention = NoteMention::create ([
|
||||
"note_id" => $note->id,
|
||||
"actor_id" => $actor_exists->id
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('activities', function (Blueprint $table) {
|
||||
$table->json ("to")->default (json_encode (["https://www.w3.org/ns/activitystreams#Public"], JSON_UNESCAPED_SLASHES))->nullable ();
|
||||
$table->json ("cc")->default (json_encode ([], JSON_UNESCAPED_SLASHES))->nullable ();
|
||||
});
|
||||
|
||||
Schema::table ("notes", function (Blueprint $table) {
|
||||
$table->json ("to")->default (json_encode (["https://www.w3.org/ns/activitystreams#Public"], JSON_UNESCAPED_SLASHES))->nullable ();
|
||||
$table->json ("cc")->default (json_encode ([], JSON_UNESCAPED_SLASHES))->nullable ();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('activities', function (Blueprint $table) {
|
||||
$table->dropColumn ("to");
|
||||
$table->dropColumn ("cc");
|
||||
});
|
||||
|
||||
Schema::table ("notes", function (Blueprint $table) {
|
||||
$table->dropColumn ("to");
|
||||
$table->dropColumn ("cc");
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('note_mentions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->foreignId ("note_id")->constrained()->onDelete("cascade");
|
||||
$table->foreignId ("actor_id")->constrained()->onDelete("cascade");
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('note_mentions');
|
||||
}
|
||||
};
|
@ -317,7 +317,7 @@
|
||||
</div>
|
||||
<div class="inner">
|
||||
<p>
|
||||
<b>{{ $actor->name }} has <span class="count">{{ count ($actor->get_posts ()) }}</span> posts.</b>
|
||||
<b>{{ $actor->name }} has <span class="count">{{ $actor->get_posts ()->total () }}</span> posts.</b>
|
||||
</p>
|
||||
|
||||
@if (auth ()->user () && auth ()->user ()->is ($user))
|
||||
|
Loading…
x
Reference in New Issue
Block a user