|
@@ -29,306 +29,315 @@ use App\Jobs\StoryPipeline\StoryFanout;
|
|
|
use App\Jobs\StoryPipeline\StoryDelete;
|
|
|
use ImageOptimizer;
|
|
|
use App\Models\Conversation;
|
|
|
+use App\Services\UserRoleService;
|
|
|
|
|
|
class StoryComposeController extends Controller
|
|
|
{
|
|
|
public function apiV1Add(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $this->validate($request, [
|
|
|
- 'file' => function() {
|
|
|
- return [
|
|
|
- 'required',
|
|
|
- 'mimetypes:image/jpeg,image/png,video/mp4',
|
|
|
- 'max:' . config_cache('pixelfed.max_photo_size'),
|
|
|
- ];
|
|
|
- },
|
|
|
- ]);
|
|
|
-
|
|
|
- $user = $request->user();
|
|
|
-
|
|
|
- $count = Story::whereProfileId($user->profile_id)
|
|
|
- ->whereActive(true)
|
|
|
- ->where('expires_at', '>', now())
|
|
|
- ->count();
|
|
|
-
|
|
|
- if($count >= Story::MAX_PER_DAY) {
|
|
|
- abort(418, 'You have reached your limit for new Stories today.');
|
|
|
- }
|
|
|
-
|
|
|
- $photo = $request->file('file');
|
|
|
- $path = $this->storePhoto($photo, $user);
|
|
|
-
|
|
|
- $story = new Story();
|
|
|
- $story->duration = 3;
|
|
|
- $story->profile_id = $user->profile_id;
|
|
|
- $story->type = Str::endsWith($photo->getMimeType(), 'mp4') ? 'video' :'photo';
|
|
|
- $story->mime = $photo->getMimeType();
|
|
|
- $story->path = $path;
|
|
|
- $story->local = true;
|
|
|
- $story->size = $photo->getSize();
|
|
|
- $story->bearcap_token = str_random(64);
|
|
|
- $story->expires_at = now()->addMinutes(1440);
|
|
|
- $story->save();
|
|
|
-
|
|
|
- $url = $story->path;
|
|
|
-
|
|
|
- $res = [
|
|
|
- 'code' => 200,
|
|
|
- 'msg' => 'Successfully added',
|
|
|
- 'media_id' => (string) $story->id,
|
|
|
- 'media_url' => url(Storage::url($url)) . '?v=' . time(),
|
|
|
- 'media_type' => $story->type
|
|
|
- ];
|
|
|
-
|
|
|
- if($story->type === 'video') {
|
|
|
- $video = FFMpeg::open($path);
|
|
|
- $duration = $video->getDurationInSeconds();
|
|
|
- $res['media_duration'] = $duration;
|
|
|
- if($duration > 500) {
|
|
|
- Storage::delete($story->path);
|
|
|
- $story->delete();
|
|
|
- return response()->json([
|
|
|
- 'message' => 'Video duration cannot exceed 60 seconds'
|
|
|
- ], 422);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return $res;
|
|
|
- }
|
|
|
-
|
|
|
- protected function storePhoto($photo, $user)
|
|
|
- {
|
|
|
- $mimes = explode(',', config_cache('pixelfed.media_types'));
|
|
|
- if(in_array($photo->getMimeType(), [
|
|
|
- 'image/jpeg',
|
|
|
- 'image/png',
|
|
|
- 'video/mp4'
|
|
|
- ]) == false) {
|
|
|
- abort(400, 'Invalid media type');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- $storagePath = MediaPathService::story($user->profile);
|
|
|
- $path = $photo->storePubliclyAs($storagePath, Str::random(random_int(2, 12)) . '_' . Str::random(random_int(32, 35)) . '_' . Str::random(random_int(1, 14)) . '.' . $photo->extension());
|
|
|
- if(in_array($photo->getMimeType(), ['image/jpeg','image/png'])) {
|
|
|
- $fpath = storage_path('app/' . $path);
|
|
|
- $img = Intervention::make($fpath);
|
|
|
- $img->orientate();
|
|
|
- $img->save($fpath, config_cache('pixelfed.image_quality'));
|
|
|
- $img->destroy();
|
|
|
- }
|
|
|
- return $path;
|
|
|
- }
|
|
|
-
|
|
|
- public function cropPhoto(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $this->validate($request, [
|
|
|
- 'media_id' => 'required|integer|min:1',
|
|
|
- 'width' => 'required',
|
|
|
- 'height' => 'required',
|
|
|
- 'x' => 'required',
|
|
|
- 'y' => 'required'
|
|
|
- ]);
|
|
|
-
|
|
|
- $user = $request->user();
|
|
|
- $id = $request->input('media_id');
|
|
|
- $width = round($request->input('width'));
|
|
|
- $height = round($request->input('height'));
|
|
|
- $x = round($request->input('x'));
|
|
|
- $y = round($request->input('y'));
|
|
|
-
|
|
|
- $story = Story::whereProfileId($user->profile_id)->findOrFail($id);
|
|
|
-
|
|
|
- $path = storage_path('app/' . $story->path);
|
|
|
-
|
|
|
- if(!is_file($path)) {
|
|
|
- abort(400, 'Invalid or missing media.');
|
|
|
- }
|
|
|
-
|
|
|
- if($story->type === 'photo') {
|
|
|
- $img = Intervention::make($path);
|
|
|
- $img->crop($width, $height, $x, $y);
|
|
|
- $img->resize(1080, 1920, function ($constraint) {
|
|
|
- $constraint->aspectRatio();
|
|
|
- });
|
|
|
- $img->save($path, config_cache('pixelfed.image_quality'));
|
|
|
- }
|
|
|
-
|
|
|
- return [
|
|
|
- 'code' => 200,
|
|
|
- 'msg' => 'Successfully cropped',
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
- public function publishStory(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $this->validate($request, [
|
|
|
- 'media_id' => 'required',
|
|
|
- 'duration' => 'required|integer|min:3|max:120',
|
|
|
- 'can_reply' => 'required|boolean',
|
|
|
- 'can_react' => 'required|boolean'
|
|
|
- ]);
|
|
|
-
|
|
|
- $id = $request->input('media_id');
|
|
|
- $user = $request->user();
|
|
|
- $story = Story::whereProfileId($user->profile_id)
|
|
|
- ->findOrFail($id);
|
|
|
-
|
|
|
- $story->active = true;
|
|
|
- $story->duration = $request->input('duration', 10);
|
|
|
- $story->can_reply = $request->input('can_reply');
|
|
|
- $story->can_react = $request->input('can_react');
|
|
|
- $story->save();
|
|
|
-
|
|
|
- StoryService::delLatest($story->profile_id);
|
|
|
- StoryFanout::dispatch($story)->onQueue('story');
|
|
|
- StoryService::addRotateQueue($story->id);
|
|
|
-
|
|
|
- return [
|
|
|
- 'code' => 200,
|
|
|
- 'msg' => 'Successfully published',
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
- public function apiV1Delete(Request $request, $id)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $user = $request->user();
|
|
|
-
|
|
|
- $story = Story::whereProfileId($user->profile_id)
|
|
|
- ->findOrFail($id);
|
|
|
- $story->active = false;
|
|
|
- $story->save();
|
|
|
-
|
|
|
- StoryDelete::dispatch($story)->onQueue('story');
|
|
|
-
|
|
|
- return [
|
|
|
- 'code' => 200,
|
|
|
- 'msg' => 'Successfully deleted'
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
- public function compose(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- return view('stories.compose');
|
|
|
- }
|
|
|
-
|
|
|
- public function createPoll(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
- abort_if(!config_cache('instance.polls.enabled'), 404);
|
|
|
-
|
|
|
- return $request->all();
|
|
|
- }
|
|
|
-
|
|
|
- public function publishStoryPoll(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $this->validate($request, [
|
|
|
- 'question' => 'required|string|min:6|max:140',
|
|
|
- 'options' => 'required|array|min:2|max:4',
|
|
|
- 'can_reply' => 'required|boolean',
|
|
|
- 'can_react' => 'required|boolean'
|
|
|
- ]);
|
|
|
-
|
|
|
- $pid = $request->user()->profile_id;
|
|
|
-
|
|
|
- $count = Story::whereProfileId($pid)
|
|
|
- ->whereActive(true)
|
|
|
- ->where('expires_at', '>', now())
|
|
|
- ->count();
|
|
|
-
|
|
|
- if($count >= Story::MAX_PER_DAY) {
|
|
|
- abort(418, 'You have reached your limit for new Stories today.');
|
|
|
- }
|
|
|
-
|
|
|
- $story = new Story;
|
|
|
- $story->type = 'poll';
|
|
|
- $story->story = json_encode([
|
|
|
- 'question' => $request->input('question'),
|
|
|
- 'options' => $request->input('options')
|
|
|
- ]);
|
|
|
- $story->public = false;
|
|
|
- $story->local = true;
|
|
|
- $story->profile_id = $pid;
|
|
|
- $story->expires_at = now()->addMinutes(1440);
|
|
|
- $story->duration = 30;
|
|
|
- $story->can_reply = false;
|
|
|
- $story->can_react = false;
|
|
|
- $story->save();
|
|
|
-
|
|
|
- $poll = new Poll;
|
|
|
- $poll->story_id = $story->id;
|
|
|
- $poll->profile_id = $pid;
|
|
|
- $poll->poll_options = $request->input('options');
|
|
|
- $poll->expires_at = $story->expires_at;
|
|
|
- $poll->cached_tallies = collect($poll->poll_options)->map(function($o) {
|
|
|
- return 0;
|
|
|
- })->toArray();
|
|
|
- $poll->save();
|
|
|
-
|
|
|
- $story->active = true;
|
|
|
- $story->save();
|
|
|
-
|
|
|
- StoryService::delLatest($story->profile_id);
|
|
|
-
|
|
|
- return [
|
|
|
- 'code' => 200,
|
|
|
- 'msg' => 'Successfully published',
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
- public function storyPollVote(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $this->validate($request, [
|
|
|
- 'sid' => 'required',
|
|
|
- 'ci' => 'required|integer|min:0|max:3'
|
|
|
- ]);
|
|
|
-
|
|
|
- $pid = $request->user()->profile_id;
|
|
|
- $ci = $request->input('ci');
|
|
|
- $story = Story::findOrFail($request->input('sid'));
|
|
|
- abort_if(!FollowerService::follows($pid, $story->profile_id), 403);
|
|
|
- $poll = Poll::whereStoryId($story->id)->firstOrFail();
|
|
|
-
|
|
|
- $vote = new PollVote;
|
|
|
- $vote->profile_id = $pid;
|
|
|
- $vote->poll_id = $poll->id;
|
|
|
- $vote->story_id = $story->id;
|
|
|
- $vote->status_id = null;
|
|
|
- $vote->choice = $ci;
|
|
|
- $vote->save();
|
|
|
-
|
|
|
- $poll->votes_count = $poll->votes_count + 1;
|
|
|
- $poll->cached_tallies = collect($poll->getTallies())->map(function($tally, $key) use($ci) {
|
|
|
- return $ci == $key ? $tally + 1 : $tally;
|
|
|
- })->toArray();
|
|
|
- $poll->save();
|
|
|
-
|
|
|
- return 200;
|
|
|
- }
|
|
|
-
|
|
|
- public function storeReport(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $this->validate($request, [
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+
|
|
|
+ $this->validate($request, [
|
|
|
+ 'file' => function() {
|
|
|
+ return [
|
|
|
+ 'required',
|
|
|
+ 'mimetypes:image/jpeg,image/png,video/mp4',
|
|
|
+ 'max:' . config_cache('pixelfed.max_photo_size'),
|
|
|
+ ];
|
|
|
+ },
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $user = $request->user();
|
|
|
+ abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action');
|
|
|
+ $count = Story::whereProfileId($user->profile_id)
|
|
|
+ ->whereActive(true)
|
|
|
+ ->where('expires_at', '>', now())
|
|
|
+ ->count();
|
|
|
+
|
|
|
+ if($count >= Story::MAX_PER_DAY) {
|
|
|
+ abort(418, 'You have reached your limit for new Stories today.');
|
|
|
+ }
|
|
|
+
|
|
|
+ $photo = $request->file('file');
|
|
|
+ $path = $this->storePhoto($photo, $user);
|
|
|
+
|
|
|
+ $story = new Story();
|
|
|
+ $story->duration = 3;
|
|
|
+ $story->profile_id = $user->profile_id;
|
|
|
+ $story->type = Str::endsWith($photo->getMimeType(), 'mp4') ? 'video' :'photo';
|
|
|
+ $story->mime = $photo->getMimeType();
|
|
|
+ $story->path = $path;
|
|
|
+ $story->local = true;
|
|
|
+ $story->size = $photo->getSize();
|
|
|
+ $story->bearcap_token = str_random(64);
|
|
|
+ $story->expires_at = now()->addMinutes(1440);
|
|
|
+ $story->save();
|
|
|
+
|
|
|
+ $url = $story->path;
|
|
|
+
|
|
|
+ $res = [
|
|
|
+ 'code' => 200,
|
|
|
+ 'msg' => 'Successfully added',
|
|
|
+ 'media_id' => (string) $story->id,
|
|
|
+ 'media_url' => url(Storage::url($url)) . '?v=' . time(),
|
|
|
+ 'media_type' => $story->type
|
|
|
+ ];
|
|
|
+
|
|
|
+ if($story->type === 'video') {
|
|
|
+ $video = FFMpeg::open($path);
|
|
|
+ $duration = $video->getDurationInSeconds();
|
|
|
+ $res['media_duration'] = $duration;
|
|
|
+ if($duration > 500) {
|
|
|
+ Storage::delete($story->path);
|
|
|
+ $story->delete();
|
|
|
+ return response()->json([
|
|
|
+ 'message' => 'Video duration cannot exceed 60 seconds'
|
|
|
+ ], 422);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $res;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function storePhoto($photo, $user)
|
|
|
+ {
|
|
|
+ $mimes = explode(',', config_cache('pixelfed.media_types'));
|
|
|
+ if(in_array($photo->getMimeType(), [
|
|
|
+ 'image/jpeg',
|
|
|
+ 'image/png',
|
|
|
+ 'video/mp4'
|
|
|
+ ]) == false) {
|
|
|
+ abort(400, 'Invalid media type');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $storagePath = MediaPathService::story($user->profile);
|
|
|
+ $path = $photo->storePubliclyAs($storagePath, Str::random(random_int(2, 12)) . '_' . Str::random(random_int(32, 35)) . '_' . Str::random(random_int(1, 14)) . '.' . $photo->extension());
|
|
|
+ if(in_array($photo->getMimeType(), ['image/jpeg','image/png'])) {
|
|
|
+ $fpath = storage_path('app/' . $path);
|
|
|
+ $img = Intervention::make($fpath);
|
|
|
+ $img->orientate();
|
|
|
+ $img->save($fpath, config_cache('pixelfed.image_quality'));
|
|
|
+ $img->destroy();
|
|
|
+ }
|
|
|
+ return $path;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function cropPhoto(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+
|
|
|
+ $this->validate($request, [
|
|
|
+ 'media_id' => 'required|integer|min:1',
|
|
|
+ 'width' => 'required',
|
|
|
+ 'height' => 'required',
|
|
|
+ 'x' => 'required',
|
|
|
+ 'y' => 'required'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $user = $request->user();
|
|
|
+ $id = $request->input('media_id');
|
|
|
+ $width = round($request->input('width'));
|
|
|
+ $height = round($request->input('height'));
|
|
|
+ $x = round($request->input('x'));
|
|
|
+ $y = round($request->input('y'));
|
|
|
+
|
|
|
+ $story = Story::whereProfileId($user->profile_id)->findOrFail($id);
|
|
|
+
|
|
|
+ $path = storage_path('app/' . $story->path);
|
|
|
+
|
|
|
+ if(!is_file($path)) {
|
|
|
+ abort(400, 'Invalid or missing media.');
|
|
|
+ }
|
|
|
+
|
|
|
+ if($story->type === 'photo') {
|
|
|
+ $img = Intervention::make($path);
|
|
|
+ $img->crop($width, $height, $x, $y);
|
|
|
+ $img->resize(1080, 1920, function ($constraint) {
|
|
|
+ $constraint->aspectRatio();
|
|
|
+ });
|
|
|
+ $img->save($path, config_cache('pixelfed.image_quality'));
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'code' => 200,
|
|
|
+ 'msg' => 'Successfully cropped',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function publishStory(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+
|
|
|
+ $this->validate($request, [
|
|
|
+ 'media_id' => 'required',
|
|
|
+ 'duration' => 'required|integer|min:3|max:120',
|
|
|
+ 'can_reply' => 'required|boolean',
|
|
|
+ 'can_react' => 'required|boolean'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $id = $request->input('media_id');
|
|
|
+ $user = $request->user();
|
|
|
+ abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action');
|
|
|
+ $story = Story::whereProfileId($user->profile_id)
|
|
|
+ ->findOrFail($id);
|
|
|
+
|
|
|
+ $story->active = true;
|
|
|
+ $story->duration = $request->input('duration', 10);
|
|
|
+ $story->can_reply = $request->input('can_reply');
|
|
|
+ $story->can_react = $request->input('can_react');
|
|
|
+ $story->save();
|
|
|
+
|
|
|
+ StoryService::delLatest($story->profile_id);
|
|
|
+ StoryFanout::dispatch($story)->onQueue('story');
|
|
|
+ StoryService::addRotateQueue($story->id);
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'code' => 200,
|
|
|
+ 'msg' => 'Successfully published',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function apiV1Delete(Request $request, $id)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+
|
|
|
+ $user = $request->user();
|
|
|
+
|
|
|
+ $story = Story::whereProfileId($user->profile_id)
|
|
|
+ ->findOrFail($id);
|
|
|
+ $story->active = false;
|
|
|
+ $story->save();
|
|
|
+
|
|
|
+ StoryDelete::dispatch($story)->onQueue('story');
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'code' => 200,
|
|
|
+ 'msg' => 'Successfully deleted'
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function compose(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+ $user = $request->user();
|
|
|
+ abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action');
|
|
|
+
|
|
|
+ return view('stories.compose');
|
|
|
+ }
|
|
|
+
|
|
|
+ public function createPoll(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+ abort_if(!config_cache('instance.polls.enabled'), 404);
|
|
|
+
|
|
|
+ return $request->all();
|
|
|
+ }
|
|
|
+
|
|
|
+ public function publishStoryPoll(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+
|
|
|
+ $this->validate($request, [
|
|
|
+ 'question' => 'required|string|min:6|max:140',
|
|
|
+ 'options' => 'required|array|min:2|max:4',
|
|
|
+ 'can_reply' => 'required|boolean',
|
|
|
+ 'can_react' => 'required|boolean'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $user = $request->user();
|
|
|
+ abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action');
|
|
|
+ $pid = $request->user()->profile_id;
|
|
|
+
|
|
|
+ $count = Story::whereProfileId($pid)
|
|
|
+ ->whereActive(true)
|
|
|
+ ->where('expires_at', '>', now())
|
|
|
+ ->count();
|
|
|
+
|
|
|
+ if($count >= Story::MAX_PER_DAY) {
|
|
|
+ abort(418, 'You have reached your limit for new Stories today.');
|
|
|
+ }
|
|
|
+
|
|
|
+ $story = new Story;
|
|
|
+ $story->type = 'poll';
|
|
|
+ $story->story = json_encode([
|
|
|
+ 'question' => $request->input('question'),
|
|
|
+ 'options' => $request->input('options')
|
|
|
+ ]);
|
|
|
+ $story->public = false;
|
|
|
+ $story->local = true;
|
|
|
+ $story->profile_id = $pid;
|
|
|
+ $story->expires_at = now()->addMinutes(1440);
|
|
|
+ $story->duration = 30;
|
|
|
+ $story->can_reply = false;
|
|
|
+ $story->can_react = false;
|
|
|
+ $story->save();
|
|
|
+
|
|
|
+ $poll = new Poll;
|
|
|
+ $poll->story_id = $story->id;
|
|
|
+ $poll->profile_id = $pid;
|
|
|
+ $poll->poll_options = $request->input('options');
|
|
|
+ $poll->expires_at = $story->expires_at;
|
|
|
+ $poll->cached_tallies = collect($poll->poll_options)->map(function($o) {
|
|
|
+ return 0;
|
|
|
+ })->toArray();
|
|
|
+ $poll->save();
|
|
|
+
|
|
|
+ $story->active = true;
|
|
|
+ $story->save();
|
|
|
+
|
|
|
+ StoryService::delLatest($story->profile_id);
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'code' => 200,
|
|
|
+ 'msg' => 'Successfully published',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function storyPollVote(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+
|
|
|
+ $this->validate($request, [
|
|
|
+ 'sid' => 'required',
|
|
|
+ 'ci' => 'required|integer|min:0|max:3'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $pid = $request->user()->profile_id;
|
|
|
+ $ci = $request->input('ci');
|
|
|
+ $story = Story::findOrFail($request->input('sid'));
|
|
|
+ abort_if(!FollowerService::follows($pid, $story->profile_id), 403);
|
|
|
+ $poll = Poll::whereStoryId($story->id)->firstOrFail();
|
|
|
+
|
|
|
+ $vote = new PollVote;
|
|
|
+ $vote->profile_id = $pid;
|
|
|
+ $vote->poll_id = $poll->id;
|
|
|
+ $vote->story_id = $story->id;
|
|
|
+ $vote->status_id = null;
|
|
|
+ $vote->choice = $ci;
|
|
|
+ $vote->save();
|
|
|
+
|
|
|
+ $poll->votes_count = $poll->votes_count + 1;
|
|
|
+ $poll->cached_tallies = collect($poll->getTallies())->map(function($tally, $key) use($ci) {
|
|
|
+ return $ci == $key ? $tally + 1 : $tally;
|
|
|
+ })->toArray();
|
|
|
+ $poll->save();
|
|
|
+
|
|
|
+ return 200;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function storeReport(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+
|
|
|
+ $this->validate($request, [
|
|
|
'type' => 'required|alpha_dash',
|
|
|
'id' => 'required|integer|min:1',
|
|
|
]);
|
|
|
|
|
|
+ $user = $request->user();
|
|
|
+ abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action');
|
|
|
+
|
|
|
$pid = $request->user()->profile_id;
|
|
|
$sid = $request->input('id');
|
|
|
$type = $request->input('type');
|
|
@@ -355,17 +364,17 @@ class StoryComposeController extends Controller
|
|
|
abort_if(!FollowerService::follows($pid, $story->profile_id), 422, 'Cannot report a story from an account you do not follow');
|
|
|
|
|
|
if( Report::whereProfileId($pid)
|
|
|
- ->whereObjectType('App\Story')
|
|
|
- ->whereObjectId($story->id)
|
|
|
- ->exists()
|
|
|
+ ->whereObjectType('App\Story')
|
|
|
+ ->whereObjectId($story->id)
|
|
|
+ ->exists()
|
|
|
) {
|
|
|
- return response()->json(['error' => [
|
|
|
- 'code' => 409,
|
|
|
- 'message' => 'Cannot report the same story again'
|
|
|
- ]], 409);
|
|
|
+ return response()->json(['error' => [
|
|
|
+ 'code' => 409,
|
|
|
+ 'message' => 'Cannot report the same story again'
|
|
|
+ ]], 409);
|
|
|
}
|
|
|
|
|
|
- $report = new Report;
|
|
|
+ $report = new Report;
|
|
|
$report->profile_id = $pid;
|
|
|
$report->user_id = $request->user()->id;
|
|
|
$report->object_id = $story->id;
|
|
@@ -376,149 +385,151 @@ class StoryComposeController extends Controller
|
|
|
$report->save();
|
|
|
|
|
|
return [200];
|
|
|
- }
|
|
|
-
|
|
|
- public function react(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
- $this->validate($request, [
|
|
|
- 'sid' => 'required',
|
|
|
- 'reaction' => 'required|string'
|
|
|
- ]);
|
|
|
- $pid = $request->user()->profile_id;
|
|
|
- $text = $request->input('reaction');
|
|
|
-
|
|
|
- $story = Story::findOrFail($request->input('sid'));
|
|
|
-
|
|
|
- abort_if(!$story->can_react, 422);
|
|
|
- abort_if(StoryService::reactCounter($story->id, $pid) >= 5, 422, 'You have already reacted to this story');
|
|
|
-
|
|
|
- $status = new Status;
|
|
|
- $status->profile_id = $pid;
|
|
|
- $status->type = 'story:reaction';
|
|
|
- $status->caption = $text;
|
|
|
- $status->rendered = $text;
|
|
|
- $status->scope = 'direct';
|
|
|
- $status->visibility = 'direct';
|
|
|
- $status->in_reply_to_profile_id = $story->profile_id;
|
|
|
- $status->entities = json_encode([
|
|
|
- 'story_id' => $story->id,
|
|
|
- 'reaction' => $text
|
|
|
- ]);
|
|
|
- $status->save();
|
|
|
-
|
|
|
- $dm = new DirectMessage;
|
|
|
- $dm->to_id = $story->profile_id;
|
|
|
- $dm->from_id = $pid;
|
|
|
- $dm->type = 'story:react';
|
|
|
- $dm->status_id = $status->id;
|
|
|
- $dm->meta = json_encode([
|
|
|
- 'story_username' => $story->profile->username,
|
|
|
- 'story_actor_username' => $request->user()->username,
|
|
|
- 'story_id' => $story->id,
|
|
|
- 'story_media_url' => url(Storage::url($story->path)),
|
|
|
- 'reaction' => $text
|
|
|
- ]);
|
|
|
- $dm->save();
|
|
|
-
|
|
|
- Conversation::updateOrInsert(
|
|
|
- [
|
|
|
- 'to_id' => $story->profile_id,
|
|
|
- 'from_id' => $pid
|
|
|
- ],
|
|
|
- [
|
|
|
- 'type' => 'story:react',
|
|
|
- 'status_id' => $status->id,
|
|
|
- 'dm_id' => $dm->id,
|
|
|
- 'is_hidden' => false
|
|
|
- ]
|
|
|
- );
|
|
|
-
|
|
|
- if($story->local) {
|
|
|
- // generate notification
|
|
|
- $n = new Notification;
|
|
|
- $n->profile_id = $dm->to_id;
|
|
|
- $n->actor_id = $dm->from_id;
|
|
|
- $n->item_id = $dm->id;
|
|
|
- $n->item_type = 'App\DirectMessage';
|
|
|
- $n->action = 'story:react';
|
|
|
- $n->save();
|
|
|
- } else {
|
|
|
- StoryReactionDeliver::dispatch($story, $status)->onQueue('story');
|
|
|
- }
|
|
|
-
|
|
|
- StoryService::reactIncrement($story->id, $pid);
|
|
|
-
|
|
|
- return 200;
|
|
|
- }
|
|
|
-
|
|
|
- public function comment(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
- $this->validate($request, [
|
|
|
- 'sid' => 'required',
|
|
|
- 'caption' => 'required|string'
|
|
|
- ]);
|
|
|
- $pid = $request->user()->profile_id;
|
|
|
- $text = $request->input('caption');
|
|
|
-
|
|
|
- $story = Story::findOrFail($request->input('sid'));
|
|
|
-
|
|
|
- abort_if(!$story->can_reply, 422);
|
|
|
-
|
|
|
- $status = new Status;
|
|
|
- $status->type = 'story:reply';
|
|
|
- $status->profile_id = $pid;
|
|
|
- $status->caption = $text;
|
|
|
- $status->rendered = $text;
|
|
|
- $status->scope = 'direct';
|
|
|
- $status->visibility = 'direct';
|
|
|
- $status->in_reply_to_profile_id = $story->profile_id;
|
|
|
- $status->entities = json_encode([
|
|
|
- 'story_id' => $story->id
|
|
|
- ]);
|
|
|
- $status->save();
|
|
|
-
|
|
|
- $dm = new DirectMessage;
|
|
|
- $dm->to_id = $story->profile_id;
|
|
|
- $dm->from_id = $pid;
|
|
|
- $dm->type = 'story:comment';
|
|
|
- $dm->status_id = $status->id;
|
|
|
- $dm->meta = json_encode([
|
|
|
- 'story_username' => $story->profile->username,
|
|
|
- 'story_actor_username' => $request->user()->username,
|
|
|
- 'story_id' => $story->id,
|
|
|
- 'story_media_url' => url(Storage::url($story->path)),
|
|
|
- 'caption' => $text
|
|
|
- ]);
|
|
|
- $dm->save();
|
|
|
-
|
|
|
- Conversation::updateOrInsert(
|
|
|
- [
|
|
|
- 'to_id' => $story->profile_id,
|
|
|
- 'from_id' => $pid
|
|
|
- ],
|
|
|
- [
|
|
|
- 'type' => 'story:comment',
|
|
|
- 'status_id' => $status->id,
|
|
|
- 'dm_id' => $dm->id,
|
|
|
- 'is_hidden' => false
|
|
|
- ]
|
|
|
- );
|
|
|
-
|
|
|
- if($story->local) {
|
|
|
- // generate notification
|
|
|
- $n = new Notification;
|
|
|
- $n->profile_id = $dm->to_id;
|
|
|
- $n->actor_id = $dm->from_id;
|
|
|
- $n->item_id = $dm->id;
|
|
|
- $n->item_type = 'App\DirectMessage';
|
|
|
- $n->action = 'story:comment';
|
|
|
- $n->save();
|
|
|
- } else {
|
|
|
- StoryReplyDeliver::dispatch($story, $status)->onQueue('story');
|
|
|
- }
|
|
|
-
|
|
|
- return 200;
|
|
|
- }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function react(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+ $this->validate($request, [
|
|
|
+ 'sid' => 'required',
|
|
|
+ 'reaction' => 'required|string'
|
|
|
+ ]);
|
|
|
+ $pid = $request->user()->profile_id;
|
|
|
+ $text = $request->input('reaction');
|
|
|
+ $user = $request->user();
|
|
|
+ abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action');
|
|
|
+ $story = Story::findOrFail($request->input('sid'));
|
|
|
+
|
|
|
+ abort_if(!$story->can_react, 422);
|
|
|
+ abort_if(StoryService::reactCounter($story->id, $pid) >= 5, 422, 'You have already reacted to this story');
|
|
|
+
|
|
|
+ $status = new Status;
|
|
|
+ $status->profile_id = $pid;
|
|
|
+ $status->type = 'story:reaction';
|
|
|
+ $status->caption = $text;
|
|
|
+ $status->rendered = $text;
|
|
|
+ $status->scope = 'direct';
|
|
|
+ $status->visibility = 'direct';
|
|
|
+ $status->in_reply_to_profile_id = $story->profile_id;
|
|
|
+ $status->entities = json_encode([
|
|
|
+ 'story_id' => $story->id,
|
|
|
+ 'reaction' => $text
|
|
|
+ ]);
|
|
|
+ $status->save();
|
|
|
+
|
|
|
+ $dm = new DirectMessage;
|
|
|
+ $dm->to_id = $story->profile_id;
|
|
|
+ $dm->from_id = $pid;
|
|
|
+ $dm->type = 'story:react';
|
|
|
+ $dm->status_id = $status->id;
|
|
|
+ $dm->meta = json_encode([
|
|
|
+ 'story_username' => $story->profile->username,
|
|
|
+ 'story_actor_username' => $request->user()->username,
|
|
|
+ 'story_id' => $story->id,
|
|
|
+ 'story_media_url' => url(Storage::url($story->path)),
|
|
|
+ 'reaction' => $text
|
|
|
+ ]);
|
|
|
+ $dm->save();
|
|
|
+
|
|
|
+ Conversation::updateOrInsert(
|
|
|
+ [
|
|
|
+ 'to_id' => $story->profile_id,
|
|
|
+ 'from_id' => $pid
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ 'type' => 'story:react',
|
|
|
+ 'status_id' => $status->id,
|
|
|
+ 'dm_id' => $dm->id,
|
|
|
+ 'is_hidden' => false
|
|
|
+ ]
|
|
|
+ );
|
|
|
+
|
|
|
+ if($story->local) {
|
|
|
+ // generate notification
|
|
|
+ $n = new Notification;
|
|
|
+ $n->profile_id = $dm->to_id;
|
|
|
+ $n->actor_id = $dm->from_id;
|
|
|
+ $n->item_id = $dm->id;
|
|
|
+ $n->item_type = 'App\DirectMessage';
|
|
|
+ $n->action = 'story:react';
|
|
|
+ $n->save();
|
|
|
+ } else {
|
|
|
+ StoryReactionDeliver::dispatch($story, $status)->onQueue('story');
|
|
|
+ }
|
|
|
+
|
|
|
+ StoryService::reactIncrement($story->id, $pid);
|
|
|
+
|
|
|
+ return 200;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function comment(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
+ $this->validate($request, [
|
|
|
+ 'sid' => 'required',
|
|
|
+ 'caption' => 'required|string'
|
|
|
+ ]);
|
|
|
+ $pid = $request->user()->profile_id;
|
|
|
+ $text = $request->input('caption');
|
|
|
+ $user = $request->user();
|
|
|
+ abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action');
|
|
|
+ $story = Story::findOrFail($request->input('sid'));
|
|
|
+
|
|
|
+ abort_if(!$story->can_reply, 422);
|
|
|
+
|
|
|
+ $status = new Status;
|
|
|
+ $status->type = 'story:reply';
|
|
|
+ $status->profile_id = $pid;
|
|
|
+ $status->caption = $text;
|
|
|
+ $status->rendered = $text;
|
|
|
+ $status->scope = 'direct';
|
|
|
+ $status->visibility = 'direct';
|
|
|
+ $status->in_reply_to_profile_id = $story->profile_id;
|
|
|
+ $status->entities = json_encode([
|
|
|
+ 'story_id' => $story->id
|
|
|
+ ]);
|
|
|
+ $status->save();
|
|
|
+
|
|
|
+ $dm = new DirectMessage;
|
|
|
+ $dm->to_id = $story->profile_id;
|
|
|
+ $dm->from_id = $pid;
|
|
|
+ $dm->type = 'story:comment';
|
|
|
+ $dm->status_id = $status->id;
|
|
|
+ $dm->meta = json_encode([
|
|
|
+ 'story_username' => $story->profile->username,
|
|
|
+ 'story_actor_username' => $request->user()->username,
|
|
|
+ 'story_id' => $story->id,
|
|
|
+ 'story_media_url' => url(Storage::url($story->path)),
|
|
|
+ 'caption' => $text
|
|
|
+ ]);
|
|
|
+ $dm->save();
|
|
|
+
|
|
|
+ Conversation::updateOrInsert(
|
|
|
+ [
|
|
|
+ 'to_id' => $story->profile_id,
|
|
|
+ 'from_id' => $pid
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ 'type' => 'story:comment',
|
|
|
+ 'status_id' => $status->id,
|
|
|
+ 'dm_id' => $dm->id,
|
|
|
+ 'is_hidden' => false
|
|
|
+ ]
|
|
|
+ );
|
|
|
+
|
|
|
+ if($story->local) {
|
|
|
+ // generate notification
|
|
|
+ $n = new Notification;
|
|
|
+ $n->profile_id = $dm->to_id;
|
|
|
+ $n->actor_id = $dm->from_id;
|
|
|
+ $n->item_id = $dm->id;
|
|
|
+ $n->item_type = 'App\DirectMessage';
|
|
|
+ $n->action = 'story:comment';
|
|
|
+ $n->save();
|
|
|
+ } else {
|
|
|
+ StoryReplyDeliver::dispatch($story, $status)->onQueue('story');
|
|
|
+ }
|
|
|
+
|
|
|
+ return 200;
|
|
|
+ }
|
|
|
}
|