mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-23 13:33:18 +00:00
421 lines
14 KiB
PHP
421 lines
14 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace App\Http\Controllers\Groups;
|
||
|
|
||
|
use Illuminate\Support\Facades\Bus;
|
||
|
use Illuminate\Support\Facades\Cache;
|
||
|
use Illuminate\Support\Facades\Storage;
|
||
|
use Illuminate\Support\Facades\RateLimiter;
|
||
|
use App\Http\Controllers\Controller;
|
||
|
use Illuminate\Http\Request;
|
||
|
use App\Services\AccountService;
|
||
|
use App\Services\GroupService;
|
||
|
use App\Services\Groups\GroupFeedService;
|
||
|
use App\Services\Groups\GroupPostService;
|
||
|
use App\Services\Groups\GroupMediaService;
|
||
|
use App\Services\Groups\GroupsLikeService;
|
||
|
use App\Follower;
|
||
|
use App\Profile;
|
||
|
use App\Models\Group;
|
||
|
use App\Models\GroupHashtag;
|
||
|
use App\Models\GroupPost;
|
||
|
use App\Models\GroupLike;
|
||
|
use App\Models\GroupMember;
|
||
|
use App\Models\GroupInvitation;
|
||
|
use App\Models\GroupMedia;
|
||
|
use App\Jobs\GroupsPipeline\ImageResizePipeline;
|
||
|
use App\Jobs\GroupsPipeline\ImageS3UploadPipeline;
|
||
|
use App\Jobs\GroupsPipeline\NewPostPipeline;
|
||
|
|
||
|
class GroupsPostController extends Controller
|
||
|
{
|
||
|
public function __construct()
|
||
|
{
|
||
|
$this->middleware('auth');
|
||
|
}
|
||
|
|
||
|
public function storePost(Request $request)
|
||
|
{
|
||
|
$this->validate($request, [
|
||
|
'group_id' => 'required|exists:groups,id',
|
||
|
'caption' => 'sometimes|string|max:'.config_cache('pixelfed.max_caption_length', 500),
|
||
|
'pollOptions' => 'sometimes|array|min:1|max:4'
|
||
|
]);
|
||
|
|
||
|
$group = Group::findOrFail($request->input('group_id'));
|
||
|
$pid = $request->user()->profile_id;
|
||
|
$caption = $request->input('caption');
|
||
|
$type = $request->input('type', 'text');
|
||
|
|
||
|
abort_if(!GroupService::canPost($group->id, $pid), 422, 'You cannot create new posts at this time');
|
||
|
|
||
|
if($type == 'text') {
|
||
|
abort_if(strlen(e($caption)) == 0, 403);
|
||
|
}
|
||
|
|
||
|
$gp = new GroupPost;
|
||
|
$gp->group_id = $group->id;
|
||
|
$gp->profile_id = $pid;
|
||
|
$gp->caption = e($caption);
|
||
|
$gp->type = $type;
|
||
|
$gp->visibility = 'draft';
|
||
|
$gp->save();
|
||
|
|
||
|
$status = $gp;
|
||
|
|
||
|
NewPostPipeline::dispatchSync($gp);
|
||
|
|
||
|
// NewStatusPipeline::dispatch($status, $gp);
|
||
|
|
||
|
if($type == 'poll') {
|
||
|
// Polls not supported yet
|
||
|
// $poll = new Poll;
|
||
|
// $poll->status_id = $status->id;
|
||
|
// $poll->profile_id = $status->profile_id;
|
||
|
// $poll->poll_options = $request->input('pollOptions');
|
||
|
// $poll->expires_at = now()->addMinutes($request->input('expiry'));
|
||
|
// $poll->cached_tallies = collect($poll->poll_options)->map(function($o) {
|
||
|
// return 0;
|
||
|
// })->toArray();
|
||
|
// $poll->save();
|
||
|
// sleep(5);
|
||
|
}
|
||
|
if($type == 'photo') {
|
||
|
$photo = $request->file('photo');
|
||
|
$storagePath = GroupMediaService::path($group->id, $pid, $status->id);
|
||
|
// $storagePath = 'public/g/' . $group->id . '/p/' . $status->id;
|
||
|
$path = $photo->storePublicly($storagePath);
|
||
|
// $hash = \hash_file('sha256', $photo);
|
||
|
|
||
|
$media = new GroupMedia();
|
||
|
$media->group_id = $group->id;
|
||
|
$media->status_id = $status->id;
|
||
|
$media->profile_id = $request->user()->profile_id;
|
||
|
$media->media_path = $path;
|
||
|
$media->size = $photo->getSize();
|
||
|
$media->mime = $photo->getMimeType();
|
||
|
$media->save();
|
||
|
|
||
|
// Bus::chain([
|
||
|
// new ImageResizePipeline($media),
|
||
|
// new ImageS3UploadPipeline($media),
|
||
|
// ])->dispatch($media);
|
||
|
|
||
|
ImageResizePipeline::dispatchSync($media);
|
||
|
ImageS3UploadPipeline::dispatchSync($media);
|
||
|
// ImageOptimize::dispatch($media);
|
||
|
// delay response while background job optimizes media
|
||
|
// sleep(5);
|
||
|
}
|
||
|
if($type == 'video') {
|
||
|
$video = $request->file('video');
|
||
|
$storagePath = 'public/g/' . $group->id . '/p/' . $status->id;
|
||
|
$path = $video->storePublicly($storagePath);
|
||
|
$hash = \hash_file('sha256', $video);
|
||
|
|
||
|
$media = new Media();
|
||
|
$media->status_id = $status->id;
|
||
|
$media->profile_id = $request->user()->profile_id;
|
||
|
$media->user_id = $request->user()->id;
|
||
|
$media->media_path = $path;
|
||
|
$media->original_sha256 = $hash;
|
||
|
$media->size = $video->getSize();
|
||
|
$media->mime = $video->getMimeType();
|
||
|
$media->save();
|
||
|
|
||
|
VideoThumbnail::dispatch($media);
|
||
|
sleep(15);
|
||
|
}
|
||
|
|
||
|
GroupService::log(
|
||
|
$group->id,
|
||
|
$pid,
|
||
|
'group:status:created',
|
||
|
[
|
||
|
'type' => $gp->type,
|
||
|
'status_id' => $status->id
|
||
|
],
|
||
|
GroupPost::class,
|
||
|
$gp->id
|
||
|
);
|
||
|
|
||
|
$s = GroupPostService::get($status->group_id, $status->id);
|
||
|
GroupFeedService::add($group->id, $gp->id);
|
||
|
Cache::forget('groups:self:feed:' . $pid);
|
||
|
|
||
|
$s['pf_type'] = $type;
|
||
|
$s['visibility'] = 'public';
|
||
|
$s['url'] = $gp->url();
|
||
|
|
||
|
if($type == 'poll') {
|
||
|
$s['poll'] = PollService::get($status->id);
|
||
|
}
|
||
|
|
||
|
$group->last_active_at = now();
|
||
|
$group->save();
|
||
|
|
||
|
return $s;
|
||
|
}
|
||
|
|
||
|
public function deletePost(Request $request)
|
||
|
{
|
||
|
abort_if(!$request->user(), 403);
|
||
|
|
||
|
$this->validate($request, [
|
||
|
'id' => 'required|integer|min:1',
|
||
|
'gid' => 'required|integer|min:1'
|
||
|
]);
|
||
|
|
||
|
$pid = $request->user()->profile_id;
|
||
|
$gid = $request->input('gid');
|
||
|
$group = Group::findOrFail($gid);
|
||
|
abort_if(!$group->isMember($pid), 403, 'Not a member of group.');
|
||
|
|
||
|
$gp = GroupPost::whereGroupId($status->group_id)->findOrFail($request->input('id'));
|
||
|
abort_if($gp->profile_id != $pid && $group->profile_id != $pid, 403);
|
||
|
$cached = GroupPostService::get($status->group_id, $status->id);
|
||
|
|
||
|
if($cached) {
|
||
|
$cached = collect($cached)->filter(function($r, $k) {
|
||
|
return in_array($k, [
|
||
|
'id',
|
||
|
'sensitive',
|
||
|
'pf_type',
|
||
|
'media_attachments',
|
||
|
'content_text',
|
||
|
'created_at'
|
||
|
]);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
GroupService::log(
|
||
|
$status->group_id,
|
||
|
$request->user()->profile_id,
|
||
|
'group:status:deleted',
|
||
|
[
|
||
|
'type' => $gp->type,
|
||
|
'status_id' => $status->id,
|
||
|
'original' => $cached
|
||
|
],
|
||
|
GroupPost::class,
|
||
|
$gp->id
|
||
|
);
|
||
|
|
||
|
$user = $request->user();
|
||
|
|
||
|
// if($status->profile_id != $user->profile->id &&
|
||
|
// $user->is_admin == true &&
|
||
|
// $status->uri == null
|
||
|
// ) {
|
||
|
// $media = $status->media;
|
||
|
|
||
|
// $ai = new AccountInterstitial;
|
||
|
// $ai->user_id = $status->profile->user_id;
|
||
|
// $ai->type = 'post.removed';
|
||
|
// $ai->view = 'account.moderation.post.removed';
|
||
|
// $ai->item_type = 'App\Status';
|
||
|
// $ai->item_id = $status->id;
|
||
|
// $ai->has_media = (bool) $media->count();
|
||
|
// $ai->blurhash = $media->count() ? $media->first()->blurhash : null;
|
||
|
// $ai->meta = json_encode([
|
||
|
// 'caption' => $status->caption,
|
||
|
// 'created_at' => $status->created_at,
|
||
|
// 'type' => $status->type,
|
||
|
// 'url' => $status->url(),
|
||
|
// 'is_nsfw' => $status->is_nsfw,
|
||
|
// 'scope' => $status->scope,
|
||
|
// 'reblog' => $status->reblog_of_id,
|
||
|
// 'likes_count' => $status->likes_count,
|
||
|
// 'reblogs_count' => $status->reblogs_count,
|
||
|
// ]);
|
||
|
// $ai->save();
|
||
|
|
||
|
// $u = $status->profile->user;
|
||
|
// $u->has_interstitial = true;
|
||
|
// $u->save();
|
||
|
// }
|
||
|
|
||
|
if($status->in_reply_to_id) {
|
||
|
$parent = GroupPost::find($status->in_reply_to_id);
|
||
|
if($parent) {
|
||
|
$parent->reply_count = GroupPost::whereInReplyToId($parent->id)->count();
|
||
|
$parent->save();
|
||
|
GroupPostService::del($group->id, GroupService::sidToGid($group->id, $parent->id));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GroupPostService::del($group->id, $gp->id);
|
||
|
GroupFeedService::del($group->id, $gp->id);
|
||
|
if ($status->profile_id == $user->profile->id || $user->is_admin == true) {
|
||
|
// Cache::forget('profile:status_count:'.$status->profile_id);
|
||
|
StatusDelete::dispatch($status);
|
||
|
}
|
||
|
|
||
|
if($request->wantsJson()) {
|
||
|
return response()->json(['Status successfully deleted.']);
|
||
|
} else {
|
||
|
return redirect($user->url());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function likePost(Request $request)
|
||
|
{
|
||
|
$this->validate($request, [
|
||
|
'gid' => 'required',
|
||
|
'sid' => 'required'
|
||
|
]);
|
||
|
|
||
|
$pid = $request->user()->profile_id;
|
||
|
$gid = $request->input('gid');
|
||
|
$sid = $request->input('sid');
|
||
|
|
||
|
$group = GroupService::get($gid);
|
||
|
abort_if(!$group, 422, 'Invalid group');
|
||
|
abort_if(!GroupService::canLike($gid, $pid), 422, 'You cannot interact with this content at this time');
|
||
|
abort_if(!GroupService::isMember($gid, $pid), 403, 'Not a member of group');
|
||
|
$gp = GroupPostService::get($gid, $sid);
|
||
|
abort_if(!$gp, 422, 'Invalid status');
|
||
|
$count = $gp['favourites_count'] ?? 0;
|
||
|
|
||
|
$like = GroupLike::firstOrCreate([
|
||
|
'group_id' => $gid,
|
||
|
'profile_id' => $pid,
|
||
|
'status_id' => $sid,
|
||
|
]);
|
||
|
|
||
|
if($like->wasRecentlyCreated) {
|
||
|
// update parent post like count
|
||
|
$parent = GroupPost::whereGroupId($gid)->find($sid);
|
||
|
abort_if(!$parent, 422, 'Invalid status');
|
||
|
$parent->likes_count = $parent->likes_count + 1;
|
||
|
$parent->save();
|
||
|
GroupsLikeService::add($pid, $sid);
|
||
|
// invalidate cache
|
||
|
GroupPostService::del($gid, $sid);
|
||
|
$count++;
|
||
|
GroupService::log(
|
||
|
$gid,
|
||
|
$pid,
|
||
|
'group:like',
|
||
|
null,
|
||
|
GroupLike::class,
|
||
|
$like->id
|
||
|
);
|
||
|
}
|
||
|
// if (GroupLike::whereGroupId($gid)->whereStatusId($sid)->whereProfileId($pid)->exists()) {
|
||
|
// $like = GroupLike::whereProfileId($pid)->whereStatusId($sid)->firstOrFail();
|
||
|
// // UnlikePipeline::dispatch($like);
|
||
|
// $count = $gp->likes_count - 1;
|
||
|
// $action = 'group:unlike';
|
||
|
// } else {
|
||
|
// $count = $gp->likes_count;
|
||
|
// $like = GroupLike::firstOrCreate([
|
||
|
// 'group_id' => $gid,
|
||
|
// 'profile_id' => $pid,
|
||
|
// 'status_id' => $sid
|
||
|
// ]);
|
||
|
// if($like->wasRecentlyCreated == true) {
|
||
|
// $count++;
|
||
|
// $gp->likes_count = $count;
|
||
|
// $like->save();
|
||
|
// $gp->save();
|
||
|
// // LikePipeline::dispatch($like);
|
||
|
// $action = 'group:like';
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
|
||
|
// Cache::forget('status:'.$status->id.':likedby:userid:'.$request->user()->id);
|
||
|
// StatusService::del($status->id);
|
||
|
|
||
|
$response = ['code' => 200, 'msg' => 'Like saved', 'count' => $count];
|
||
|
|
||
|
return $response;
|
||
|
}
|
||
|
|
||
|
public function unlikePost(Request $request)
|
||
|
{
|
||
|
$this->validate($request, [
|
||
|
'gid' => 'required',
|
||
|
'sid' => 'required'
|
||
|
]);
|
||
|
|
||
|
$pid = $request->user()->profile_id;
|
||
|
$gid = $request->input('gid');
|
||
|
$sid = $request->input('sid');
|
||
|
|
||
|
$group = GroupService::get($gid);
|
||
|
abort_if(!$group, 422, 'Invalid group');
|
||
|
abort_if(!GroupService::canLike($gid, $pid), 422, 'You cannot interact with this content at this time');
|
||
|
abort_if(!GroupService::isMember($gid, $pid), 403, 'Not a member of group');
|
||
|
$gp = GroupPostService::get($gid, $sid);
|
||
|
abort_if(!$gp, 422, 'Invalid status');
|
||
|
$count = $gp['favourites_count'] ?? 0;
|
||
|
|
||
|
$like = GroupLike::where([
|
||
|
'group_id' => $gid,
|
||
|
'profile_id' => $pid,
|
||
|
'status_id' => $sid,
|
||
|
])->first();
|
||
|
|
||
|
if($like) {
|
||
|
$like->delete();
|
||
|
$parent = GroupPost::whereGroupId($gid)->find($sid);
|
||
|
abort_if(!$parent, 422, 'Invalid status');
|
||
|
$parent->likes_count = $parent->likes_count - 1;
|
||
|
$parent->save();
|
||
|
GroupsLikeService::remove($pid, $sid);
|
||
|
// invalidate cache
|
||
|
GroupPostService::del($gid, $sid);
|
||
|
$count--;
|
||
|
}
|
||
|
|
||
|
$response = ['code' => 200, 'msg' => 'Unliked post', 'count' => $count];
|
||
|
|
||
|
return $response;
|
||
|
}
|
||
|
|
||
|
public function getGroupMedia(Request $request)
|
||
|
{
|
||
|
$this->validate($request, [
|
||
|
'gid' => 'required',
|
||
|
'type' => 'required|in:photo,video'
|
||
|
]);
|
||
|
|
||
|
abort_if(!$request->user(), 404);
|
||
|
|
||
|
$pid = $request->user()->profile_id;
|
||
|
$gid = $request->input('gid');
|
||
|
$type = $request->input('type');
|
||
|
$group = Group::findOrFail($gid);
|
||
|
|
||
|
abort_if(!$group->isMember($pid), 403, 'Not a member of group.');
|
||
|
|
||
|
$media = GroupPost::whereGroupId($gid)
|
||
|
->whereType($type)
|
||
|
->latest()
|
||
|
->simplePaginate(20)
|
||
|
->map(function($gp) use($pid) {
|
||
|
$status = GroupPostService::get($gp['group_id'], $gp['id']);
|
||
|
if(!$status) {
|
||
|
return false;
|
||
|
}
|
||
|
$status['favourited'] = (bool) GroupsLikeService::liked($pid, $gp['id']);
|
||
|
$status['favourites_count'] = GroupsLikeService::count($gp['id']);
|
||
|
$status['pf_type'] = $gp['type'];
|
||
|
$status['visibility'] = 'public';
|
||
|
$status['url'] = $gp->url();
|
||
|
|
||
|
// if($gp['type'] == 'poll') {
|
||
|
// $status['poll'] = PollService::get($status['id']);
|
||
|
// }
|
||
|
|
||
|
return $status;
|
||
|
})->filter(function($status) {
|
||
|
return $status;
|
||
|
});
|
||
|
|
||
|
return response()->json($media->toArray(), 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||
|
}
|
||
|
}
|