-
-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into laravel-10
- Loading branch information
Showing
82 changed files
with
2,356 additions
and
901 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Community\Actions; | ||
|
||
use App\Community\Events\MessageCreated; | ||
use App\Community\Models\Message; | ||
use App\Community\Models\MessageThread; | ||
use App\Site\Models\User; | ||
use Illuminate\Support\Carbon; | ||
|
||
class AddToMessageThreadAction | ||
{ | ||
public function execute(MessageThread $thread, User $userFrom, string $body): void | ||
{ | ||
$message = new Message([ | ||
'thread_id' => $thread->id, | ||
'author_id' => $userFrom->ID, | ||
'body' => $body, | ||
'created_at' => Carbon::now(), | ||
]); | ||
$message->save(); | ||
|
||
$thread->num_messages++; | ||
$thread->last_message_id = $message->id; | ||
$thread->save(); | ||
|
||
MessageCreated::dispatch($message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Community\Actions; | ||
|
||
use App\Community\Models\MessageThread; | ||
use App\Community\Models\MessageThreadParticipant; | ||
use App\Site\Models\User; | ||
use Illuminate\Support\Carbon; | ||
|
||
class CreateMessageThreadAction | ||
{ | ||
public function execute(User $userFrom, User $userTo, string $title, string $body, bool $isProxied = false): MessageThread | ||
{ | ||
$thread = new MessageThread([ | ||
'title' => $title, | ||
]); | ||
$thread->save(); | ||
|
||
$participantFrom = new MessageThreadParticipant([ | ||
'user_id' => $userFrom->ID, | ||
'thread_id' => $thread->id, | ||
]); | ||
|
||
if ($isProxied) { | ||
$participantFrom->deleted_at = Carbon::now(); | ||
} | ||
|
||
$participantFrom->save(); | ||
|
||
if ($userTo->ID != $userFrom->ID) { | ||
$participantTo = new MessageThreadParticipant([ | ||
'user_id' => $userTo->ID, | ||
'thread_id' => $thread->id, | ||
]); | ||
|
||
// if the recipient has blocked the sender, immediately mark the thread as deleted for the recipient | ||
if ($userTo->isBlocking($userFrom->User)) { | ||
$participantTo->deleted_at = Carbon::now(); | ||
} | ||
|
||
$participantTo->save(); | ||
} | ||
|
||
(new AddToMessageThreadAction())->execute($thread, $userFrom, $body); | ||
|
||
return $thread; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Community\Actions; | ||
|
||
use App\Community\Models\MessageThread; | ||
use App\Community\Models\MessageThreadParticipant; | ||
use App\Site\Models\User; | ||
|
||
class DeleteMessageThreadAction | ||
{ | ||
public function execute(MessageThread $thread, User $user): void | ||
{ | ||
$participant = MessageThreadParticipant::where('thread_id', $thread->id) | ||
->where('user_id', $user->id) | ||
->first(); | ||
|
||
if ($participant) { | ||
// make sure num_unread is 0 before we soft-delete the record. | ||
if ($participant->num_unread) { | ||
$participant->num_unread = 0; | ||
$participant->save(); | ||
} | ||
|
||
$participant->delete(); | ||
|
||
(new UpdateUnreadMessageCountAction())->execute($user); | ||
|
||
$hasOtherActiveParticipants = MessageThreadParticipant::where('thread_id', $thread->id) | ||
->where('user_id', '!=', $user->id) | ||
->whereNull('deleted_at') | ||
->exists(); | ||
if (!$hasOtherActiveParticipants) { | ||
// this will also cascade delete the message_participants and messages | ||
$thread->delete(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Community\Actions; | ||
|
||
use App\Community\Models\MessageThread; | ||
use App\Community\Models\MessageThreadParticipant; | ||
use App\Site\Models\User; | ||
|
||
class ReadMessageThreadAction | ||
{ | ||
public function execute(MessageThread $thread, User $user): void | ||
{ | ||
$participant = MessageThreadParticipant::where('user_id', $user->id) | ||
->where('thread_id', $thread->id) | ||
->whereNull('deleted_at') | ||
->first(); | ||
|
||
if ($participant) { | ||
ReadMessageThreadAction::markParticipantRead($participant, $user); | ||
} | ||
} | ||
|
||
public static function markParticipantRead(MessageThreadParticipant $participant, User $user): void | ||
{ | ||
if ($participant->num_unread) { | ||
$participant->num_unread = 0; | ||
$participant->save(); | ||
|
||
(new UpdateUnreadMessageCountAction())->execute($user); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Community\Actions; | ||
|
||
use App\Community\Models\MessageThreadParticipant; | ||
use App\Site\Models\User; | ||
|
||
class UpdateUnreadMessageCountAction | ||
{ | ||
public function execute(User $user): void | ||
{ | ||
$totalUnread = MessageThreadParticipant::where('user_id', $user->id) | ||
->whereNull('deleted_at') | ||
->sum('num_unread'); | ||
|
||
$user->UnreadMessageCount = $totalUnread; | ||
$user->save(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Community\Commands; | ||
|
||
use App\Community\Models\Message; | ||
use App\Community\Models\MessageThread; | ||
use App\Community\Models\MessageThreadParticipant; | ||
use App\Site\Models\User; | ||
use Illuminate\Console\Command; | ||
use Illuminate\Database\Schema\Blueprint; | ||
use Illuminate\Support\Facades\DB; | ||
use Illuminate\Support\Facades\Schema; | ||
|
||
class MigrateMessages extends Command | ||
{ | ||
protected $signature = 'ra:platform:messages:migrate-to-threads'; | ||
protected $description = 'Sync messages'; | ||
|
||
public function __construct() | ||
{ | ||
parent::__construct(); | ||
} | ||
|
||
public function handle(): void | ||
{ | ||
$count = Message::where('thread_id', 0)->count(); | ||
|
||
$progressBar = $this->output->createProgressBar($count); | ||
$progressBar->start(); | ||
|
||
// populate author_id for all records | ||
DB::statement("UPDATE messages m SET m.author_id = (SELECT u.ID FROM UserAccounts u WHERE u.User = m.UserFrom)"); | ||
|
||
// delete records associated to non-existant users | ||
DB::statement("DELETE FROM messages WHERE author_id=0"); | ||
|
||
// process remaining unprocessed records (thread_id=0) | ||
// have to do this in batches to prevent exhausting memory | ||
// due to requesting payloads (message content) | ||
Message::where('thread_id', 0)->chunkById(100, function ($messages) use ($progressBar) { | ||
foreach ($messages as $message) { | ||
$this->migrateMessage($message); | ||
$progressBar->advance(); | ||
} | ||
}); | ||
|
||
$count = Message::where('thread_id', 0)->count(); | ||
if ($count == 0) { | ||
// all messages sync'd. add the foreign keys so deletes will cascade | ||
$sm = Schema::getConnection()->getDoctrineSchemaManager(); | ||
$foreignKeysFound = $sm->listTableForeignKeys('messages'); | ||
|
||
$foundThreadForeignKey = false; | ||
$foundAuthorForeignKey = false; | ||
foreach ($foreignKeysFound as $foreignKey) { | ||
if ($foreignKey->getName() == 'messages_thread_id_foreign') { | ||
$foundThreadForeignKey = true; | ||
} elseif ($foreignKey->getName() == 'messages_author_id_foreign') { | ||
$foundAuthorForeignKey = true; | ||
} | ||
} | ||
|
||
if (!$foundThreadForeignKey) { | ||
Schema::table('messages', function (Blueprint $table) { | ||
$table->foreign('thread_id')->references('ID')->on('message_threads')->onDelete('cascade'); | ||
}); | ||
} | ||
|
||
if (!$foundAuthorForeignKey) { | ||
Schema::table('messages', function (Blueprint $table) { | ||
$table->foreign('author_id')->references('ID')->on('UserAccounts')->onDelete('cascade'); | ||
}); | ||
} | ||
} | ||
|
||
// automatically mark bug report notifications as deleted by the sender if | ||
// the recipient hasn't replied to them. | ||
DB::statement("UPDATE message_thread_participants mtp | ||
INNER JOIN message_threads mt ON mt.id=mtp.thread_id | ||
INNER JOIN messages m ON m.thread_id=mtp.thread_id AND m.author_id=mtp.user_id | ||
SET mtp.deleted_at=mtp.updated_at | ||
WHERE mt.num_messages=1 AND mt.title LIKE 'Bug Report (%'"); | ||
|
||
$progressBar->finish(); | ||
$this->line(PHP_EOL); | ||
} | ||
|
||
private function migrateMessage(Message $message): void | ||
{ | ||
// recipient can be entered by user. trim whitespace before trying to match | ||
$message->UserTo = trim($message->UserTo); | ||
$userTo = User::withTrashed()->where('User', $message->UserTo)->first(); | ||
if (!$userTo) { | ||
$message->delete(); | ||
|
||
return; | ||
} | ||
|
||
$thread = null; | ||
if (strtolower(substr($message->Title, 0, 4)) == 're: ') { | ||
$threadId = Message::where('title', '=', substr($message->Title, 4)) | ||
->where('UserFrom', '=', $message->UserTo) | ||
->where('UserTo', '=', $message->UserFrom) | ||
->where('id', '<', $message->id) | ||
->value('thread_id'); | ||
if ($threadId > 0) { | ||
$thread = MessageThread::firstWhere('id', $threadId); | ||
} | ||
} | ||
|
||
if ($thread === null) { | ||
$thread = new MessageThread([ | ||
'title' => $message->Title, | ||
'created_at' => $message->created_at, | ||
'updated_at' => $message->created_at, | ||
]); | ||
$thread->save(); | ||
|
||
$participantTo = new MessageThreadParticipant([ | ||
'user_id' => $userTo->ID, | ||
'thread_id' => $thread->id, | ||
'created_at' => $message->created_at, | ||
'updated_at' => $message->created_at, | ||
]); | ||
$participantTo->save(); | ||
|
||
if ($message->author_id != $userTo->ID) { | ||
$participantFrom = new MessageThreadParticipant([ | ||
'user_id' => $message->author_id, | ||
'thread_id' => $thread->id, | ||
'created_at' => $message->created_at, | ||
'updated_at' => $message->created_at, | ||
]); | ||
$participantFrom->save(); | ||
} | ||
} else { | ||
$threadParticipants = MessageThreadParticipant::withTrashed()->where('thread_id', $thread->id); | ||
$participantTo = $threadParticipants->where('user_id', $userTo->ID)->first(); | ||
} | ||
|
||
if ($message->Unread) { | ||
$participantTo->num_unread++; | ||
$participantTo->save(); | ||
} | ||
|
||
$thread->num_messages++; | ||
$thread->last_message_id = $message->id; | ||
$thread->updated_at = $message->created_at; | ||
$thread->timestamps = false; | ||
$thread->save(); | ||
|
||
$message->thread_id = $thread->id; | ||
$message->save(); | ||
} | ||
} |
Oops, something went wrong.