Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
1,281 additions
and
9 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,59 @@ | ||
<?php | ||
|
||
namespace App\Http\Controllers; | ||
|
||
use Illuminate\Http\Request; | ||
use App\Http\Requests\Status\StoreStatusEditRequest; | ||
use App\Status; | ||
use App\Models\StatusEdit; | ||
use Purify; | ||
use App\Services\Status\UpdateStatusService; | ||
use App\Services\StatusService; | ||
use App\Util\Lexer\Autolink; | ||
use App\Jobs\StatusPipeline\StatusLocalUpdateActivityPubDeliverPipeline; | ||
|
||
class StatusEditController extends Controller | ||
{ | ||
public function __construct() | ||
{ | ||
$this->middleware('auth'); | ||
abort_if(!config('exp.pue'), 404, 'Post editing is not enabled on this server.'); | ||
} | ||
|
||
public function store(StoreStatusEditRequest $request, $id) | ||
{ | ||
$validated = $request->validated(); | ||
|
||
$status = Status::findOrFail($id); | ||
abort_if(StatusEdit::whereStatusId($status->id)->count() >= 10, 400, 'You cannot edit your post more than 10 times.'); | ||
$res = UpdateStatusService::call($status, $validated); | ||
|
||
$status = Status::findOrFail($id); | ||
StatusLocalUpdateActivityPubDeliverPipeline::dispatch($status)->delay(now()->addMinutes(1)); | ||
return $res; | ||
} | ||
|
||
public function history(Request $request, $id) | ||
{ | ||
abort_if(!$request->user(), 403); | ||
$status = Status::whereNull('reblog_of_id')->findOrFail($id); | ||
abort_if(!in_array($status->scope, ['public', 'unlisted']), 403); | ||
if(!$status->edits()->count()) { | ||
return []; | ||
} | ||
$cached = StatusService::get($status->id, false); | ||
|
||
$res = $status->edits->map(function($edit) use($cached) { | ||
return [ | ||
'content' => Autolink::create()->autolink($edit->caption), | ||
'spoiler_text' => $edit->spoiler_text, | ||
'sensitive' => (bool) $edit->is_nsfw, | ||
'created_at' => str_replace('+00:00', 'Z', $edit->created_at->format(DATE_RFC3339_EXTENDED)), | ||
'account' => $cached['account'], | ||
'media_attachments' => $cached['media_attachments'], | ||
'emojis' => $cached['emojis'], | ||
]; | ||
})->reverse()->values()->toArray(); | ||
return $res; | ||
} | ||
} |
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,69 @@ | ||
<?php | ||
|
||
namespace App\Http\Requests\Status; | ||
|
||
use Illuminate\Foundation\Http\FormRequest; | ||
use App\Media; | ||
use App\Status; | ||
use Closure; | ||
|
||
class StoreStatusEditRequest extends FormRequest | ||
{ | ||
/** | ||
* Determine if the user is authorized to make this request. | ||
*/ | ||
public function authorize(): bool | ||
{ | ||
$profile = $this->user()->profile; | ||
if($profile->status != null) { | ||
return false; | ||
} | ||
if($profile->unlisted == true && $profile->cw == true) { | ||
return false; | ||
} | ||
$types = [ | ||
"photo", | ||
"photo:album", | ||
"photo:video:album", | ||
"reply", | ||
"text", | ||
"video", | ||
"video:album" | ||
]; | ||
$scopes = ['public', 'unlisted', 'private']; | ||
$status = Status::whereNull('reblog_of_id')->whereIn('type', $types)->whereIn('scope', $scopes)->find($this->route('id')); | ||
return $status && $this->user()->profile_id === $status->profile_id; | ||
} | ||
|
||
/** | ||
* Get the validation rules that apply to the request. | ||
* | ||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string> | ||
*/ | ||
public function rules(): array | ||
{ | ||
return [ | ||
'status' => 'sometimes|max:'.config('pixelfed.max_caption_length', 500), | ||
'spoiler_text' => 'nullable|string|max:140', | ||
'sensitive' => 'sometimes|boolean', | ||
'media_ids' => [ | ||
'nullable', | ||
'required_without:status', | ||
'array', | ||
'max:' . config('pixelfed.max_album_length'), | ||
function (string $attribute, mixed $value, Closure $fail) { | ||
Media::whereProfileId($this->user()->profile_id) | ||
->where(function($query) { | ||
return $query->whereNull('status_id') | ||
->orWhere('status_id', '=', $this->route('id')); | ||
}) | ||
->findOrFail($value); | ||
}, | ||
], | ||
'location' => 'sometimes|nullable', | ||
'location.id' => 'sometimes|integer|min:1|max:128769', | ||
'location.country' => 'required_with:location.id', | ||
'location.name' => 'required_with:location.id', | ||
]; | ||
} | ||
} |
129 changes: 129 additions & 0 deletions
129
app/Jobs/StatusPipeline/StatusLocalUpdateActivityPubDeliverPipeline.php
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,129 @@ | ||
<?php | ||
|
||
namespace App\Jobs\StatusPipeline; | ||
|
||
use Cache, Log; | ||
use App\Status; | ||
use Illuminate\Bus\Queueable; | ||
use Illuminate\Contracts\Queue\ShouldQueue; | ||
use Illuminate\Foundation\Bus\Dispatchable; | ||
use Illuminate\Queue\InteractsWithQueue; | ||
use Illuminate\Queue\SerializesModels; | ||
use League\Fractal; | ||
use League\Fractal\Serializer\ArraySerializer; | ||
use App\Transformer\ActivityPub\Verb\UpdateNote; | ||
use App\Util\ActivityPub\Helpers; | ||
use GuzzleHttp\Pool; | ||
use GuzzleHttp\Client; | ||
use GuzzleHttp\Promise; | ||
use App\Util\ActivityPub\HttpSignature; | ||
|
||
class StatusLocalUpdateActivityPubDeliverPipeline implements ShouldQueue | ||
{ | ||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||
|
||
protected $status; | ||
|
||
/** | ||
* Delete the job if its models no longer exist. | ||
* | ||
* @var bool | ||
*/ | ||
public $deleteWhenMissingModels = true; | ||
|
||
/** | ||
* Create a new job instance. | ||
* | ||
* @return void | ||
*/ | ||
public function __construct(Status $status) | ||
{ | ||
$this->status = $status; | ||
} | ||
|
||
/** | ||
* Execute the job. | ||
* | ||
* @return void | ||
*/ | ||
public function handle() | ||
{ | ||
$status = $this->status; | ||
$profile = $status->profile; | ||
|
||
// ignore group posts | ||
// if($status->group_id != null) { | ||
// return; | ||
// } | ||
|
||
if($status->local == false || $status->url || $status->uri) { | ||
return; | ||
} | ||
|
||
$audience = $status->profile->getAudienceInbox(); | ||
|
||
if(empty($audience) || !in_array($status->scope, ['public', 'unlisted', 'private'])) { | ||
// Return on profiles with no remote followers | ||
return; | ||
} | ||
|
||
switch($status->type) { | ||
case 'poll': | ||
// Polls not yet supported | ||
return; | ||
break; | ||
|
||
default: | ||
$activitypubObject = new UpdateNote(); | ||
break; | ||
} | ||
|
||
|
||
$fractal = new Fractal\Manager(); | ||
$fractal->setSerializer(new ArraySerializer()); | ||
$resource = new Fractal\Resource\Item($status, $activitypubObject); | ||
$activity = $fractal->createData($resource)->toArray(); | ||
|
||
$payload = json_encode($activity); | ||
|
||
$client = new Client([ | ||
'timeout' => config('federation.activitypub.delivery.timeout') | ||
]); | ||
|
||
$version = config('pixelfed.version'); | ||
$appUrl = config('app.url'); | ||
$userAgent = "(Pixelfed/{$version}; +{$appUrl})"; | ||
|
||
$requests = function($audience) use ($client, $activity, $profile, $payload, $userAgent) { | ||
foreach($audience as $url) { | ||
$headers = HttpSignature::sign($profile, $url, $activity, [ | ||
'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', | ||
'User-Agent' => $userAgent, | ||
]); | ||
yield function() use ($client, $url, $headers, $payload) { | ||
return $client->postAsync($url, [ | ||
'curl' => [ | ||
CURLOPT_HTTPHEADER => $headers, | ||
CURLOPT_POSTFIELDS => $payload, | ||
CURLOPT_HEADER => true, | ||
CURLOPT_SSL_VERIFYPEER => false, | ||
CURLOPT_SSL_VERIFYHOST => false | ||
] | ||
]); | ||
}; | ||
} | ||
}; | ||
|
||
$pool = new Pool($client, $requests($audience), [ | ||
'concurrency' => config('federation.activitypub.delivery.concurrency'), | ||
'fulfilled' => function ($response, $index) { | ||
}, | ||
'rejected' => function ($reason, $index) { | ||
} | ||
]); | ||
|
||
$promise = $pool->promise(); | ||
|
||
$promise->wait(); | ||
} | ||
} |
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,19 @@ | ||
<?php | ||
|
||
namespace App\Models; | ||
|
||
use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class StatusEdit extends Model | ||
{ | ||
use HasFactory; | ||
|
||
protected $casts = [ | ||
'ordered_media_attachment_ids' => 'array', | ||
'media_descriptions' => 'array', | ||
'poll_options' => 'array' | ||
]; | ||
|
||
protected $guarded = []; | ||
} |
Oops, something went wrong.