GroupsCommentController.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <?php
  2. namespace App\Http\Controllers\Groups;
  3. use Illuminate\Support\Facades\Cache;
  4. use Illuminate\Support\Facades\RateLimiter;
  5. use App\Http\Controllers\Controller;
  6. use Illuminate\Http\Request;
  7. use App\Services\AccountService;
  8. use App\Services\GroupService;
  9. use App\Services\Groups\GroupCommentService;
  10. use App\Services\Groups\GroupMediaService;
  11. use App\Services\Groups\GroupPostService;
  12. use App\Services\Groups\GroupsLikeService;
  13. use App\Models\Group;
  14. use App\Models\GroupLike;
  15. use App\Models\GroupMedia;
  16. use App\Models\GroupPost;
  17. use App\Models\GroupComment;
  18. use Purify;
  19. use App\Util\Lexer\Autolink;
  20. use App\Jobs\GroupsPipeline\ImageResizePipeline;
  21. use App\Jobs\GroupsPipeline\ImageS3UploadPipeline;
  22. use App\Jobs\GroupsPipeline\NewPostPipeline;
  23. use App\Jobs\GroupsPipeline\NewCommentPipeline;
  24. use App\Jobs\GroupsPipeline\DeleteCommentPipeline;
  25. class GroupsCommentController extends Controller
  26. {
  27. public function getComments(Request $request)
  28. {
  29. $this->validate($request, [
  30. 'gid' => 'required',
  31. 'sid' => 'required',
  32. 'cid' => 'sometimes',
  33. 'limit' => 'nullable|integer|min:3|max:10'
  34. ]);
  35. $pid = optional($request->user())->profile_id;
  36. $gid = $request->input('gid');
  37. $sid = $request->input('sid');
  38. $cid = $request->has('cid') && $request->input('cid') == 1;
  39. $limit = $request->input('limit', 3);
  40. $maxId = $request->input('max_id', 0);
  41. $group = Group::findOrFail($gid);
  42. abort_if($group->is_private && !$group->isMember($pid), 403, 'Not a member of group.');
  43. $status = $cid ? GroupComment::findOrFail($sid) : GroupPost::findOrFail($sid);
  44. abort_if($status->group_id != $group->id, 400, 'Invalid group');
  45. $replies = GroupComment::whereGroupId($group->id)
  46. ->whereStatusId($status->id)
  47. ->orderByDesc('id')
  48. ->when($maxId, function($query, $maxId) {
  49. return $query->where('id', '<', $maxId);
  50. })
  51. ->take($limit)
  52. ->get()
  53. ->map(function($gp) use($pid) {
  54. $status = GroupCommentService::get($gp['group_id'], $gp['id']);
  55. $status['reply_count'] = $gp['reply_count'];
  56. $status['url'] = $gp->url();
  57. $status['favourited'] = (bool) GroupsLikeService::liked($pid, $gp['id']);
  58. $status['account']['url'] = url("/groups/{$gp['group_id']}/user/{$gp['profile_id']}");
  59. return $status;
  60. });
  61. return $replies->toArray();
  62. }
  63. public function storeComment(Request $request)
  64. {
  65. $this->validate($request, [
  66. 'gid' => 'required|exists:groups,id',
  67. 'sid' => 'required|exists:group_posts,id',
  68. 'cid' => 'sometimes',
  69. 'content' => 'required|string|min:1|max:1500'
  70. ]);
  71. $pid = $request->user()->profile_id;
  72. $gid = $request->input('gid');
  73. $sid = $request->input('sid');
  74. $cid = $request->input('cid');
  75. $limit = $request->input('limit', 3);
  76. $caption = e($request->input('content'));
  77. $group = Group::findOrFail($gid);
  78. abort_if(!$group->isMember($pid), 403, 'Not a member of group.');
  79. abort_if(!GroupService::canComment($gid, $pid), 422, 'You cannot interact with this content at this time');
  80. $parent = $cid == 1 ?
  81. GroupComment::findOrFail($sid) :
  82. GroupPost::whereGroupId($gid)->findOrFail($sid);
  83. // $autolink = Purify::clean(Autolink::create()->autolink($caption));
  84. // $autolink = str_replace('/discover/tags/', '/groups/' . $gid . '/topics/', $autolink);
  85. $status = new GroupComment;
  86. $status->group_id = $group->id;
  87. $status->profile_id = $pid;
  88. $status->status_id = $parent->id;
  89. $status->caption = Purify::clean($caption);
  90. $status->visibility = 'public';
  91. $status->is_nsfw = false;
  92. $status->local = true;
  93. $status->save();
  94. NewCommentPipeline::dispatch($parent, $status)->onQueue('groups');
  95. // todo: perform in job
  96. $parent->reply_count = $parent->reply_count ? $parent->reply_count + $parent->reply_count : 1;
  97. $parent->save();
  98. GroupPostService::del($parent->group_id, $parent->id);
  99. GroupService::log(
  100. $group->id,
  101. $pid,
  102. 'group:comment:created',
  103. [
  104. 'type' => 'group:post:comment',
  105. 'status_id' => $status->id
  106. ],
  107. GroupPost::class,
  108. $status->id
  109. );
  110. //GroupCommentPipeline::dispatch($parent, $status, $gp);
  111. //NewStatusPipeline::dispatch($status, $gp);
  112. //GroupPostService::del($group->id, GroupService::sidToGid($group->id, $parent->id));
  113. // todo: perform in job
  114. $s = GroupCommentService::get($status->group_id, $status->id);
  115. $s['pf_type'] = 'text';
  116. $s['visibility'] = 'public';
  117. $s['url'] = $status->url();
  118. return $s;
  119. }
  120. public function storeCommentPhoto(Request $request)
  121. {
  122. $this->validate($request, [
  123. 'gid' => 'required|exists:groups,id',
  124. 'sid' => 'required|exists:group_posts,id',
  125. 'photo' => 'required|image'
  126. ]);
  127. $pid = $request->user()->profile_id;
  128. $gid = $request->input('gid');
  129. $sid = $request->input('sid');
  130. $limit = $request->input('limit', 3);
  131. $caption = $request->input('content');
  132. $group = Group::findOrFail($gid);
  133. abort_if(!$group->isMember($pid), 403, 'Not a member of group.');
  134. abort_if(!GroupService::canComment($gid, $pid), 422, 'You cannot interact with this content at this time');
  135. $parent = GroupPost::whereGroupId($gid)->findOrFail($sid);
  136. $status = new GroupComment;
  137. $status->status_id = $parent->id;
  138. $status->group_id = $group->id;
  139. $status->profile_id = $pid;
  140. $status->caption = Purify::clean($caption);
  141. $status->visibility = 'draft';
  142. $status->is_nsfw = false;
  143. $status->save();
  144. $photo = $request->file('photo');
  145. $storagePath = GroupMediaService::path($group->id, $pid, $status->id);
  146. $storagePath = 'public/g/' . $group->id . '/p/' . $parent->id;
  147. $path = $photo->storePublicly($storagePath);
  148. $media = new GroupMedia();
  149. $media->group_id = $group->id;
  150. $media->status_id = $status->id;
  151. $media->profile_id = $request->user()->profile_id;
  152. $media->media_path = $path;
  153. $media->size = $photo->getSize();
  154. $media->mime = $photo->getMimeType();
  155. $media->save();
  156. ImageResizePipeline::dispatchSync($media);
  157. ImageS3UploadPipeline::dispatchSync($media);
  158. // $gp = new GroupPost;
  159. // $gp->group_id = $group->id;
  160. // $gp->profile_id = $pid;
  161. // $gp->type = 'reply:photo';
  162. // $gp->status_id = $status->id;
  163. // $gp->in_reply_to_id = $parent->id;
  164. // $gp->save();
  165. // GroupService::log(
  166. // $group->id,
  167. // $pid,
  168. // 'group:comment:created',
  169. // [
  170. // 'type' => $gp->type,
  171. // 'status_id' => $status->id
  172. // ],
  173. // GroupPost::class,
  174. // $gp->id
  175. // );
  176. // todo: perform in job
  177. // $parent->reply_count = Status::whereInReplyToId($parent->id)->count();
  178. // $parent->save();
  179. // StatusService::del($parent->id);
  180. // GroupPostService::del($group->id, GroupService::sidToGid($group->id, $parent->id));
  181. // delay response while background job optimizes media
  182. // sleep(5);
  183. // todo: perform in job
  184. $s = GroupCommentService::get($status->group_id, $status->id);
  185. // $s['pf_type'] = 'text';
  186. // $s['visibility'] = 'public';
  187. // $s['url'] = $gp->url();
  188. return $s;
  189. }
  190. public function deleteComment(Request $request)
  191. {
  192. abort_if(!$request->user(), 403);
  193. $this->validate($request, [
  194. 'id' => 'required|integer|min:1',
  195. 'gid' => 'required|integer|min:1'
  196. ]);
  197. $pid = $request->user()->profile_id;
  198. $gid = $request->input('gid');
  199. $group = Group::findOrFail($gid);
  200. abort_if(!$group->isMember($pid), 403, 'Not a member of group.');
  201. $gp = GroupComment::whereGroupId($group->id)->findOrFail($request->input('id'));
  202. abort_if($gp->profile_id != $pid && $group->profile_id != $pid, 403);
  203. $parent = GroupPost::find($gp->status_id);
  204. abort_if(!$parent, 422, 'Invalid parent');
  205. DeleteCommentPipeline::dispatch($parent, $gp)->onQueue('groups');
  206. GroupService::log(
  207. $group->id,
  208. $pid,
  209. 'group:status:deleted',
  210. [
  211. 'type' => $gp->type,
  212. 'status_id' => $gp->id,
  213. ],
  214. GroupComment::class,
  215. $gp->id
  216. );
  217. $gp->delete();
  218. if($request->wantsJson()) {
  219. return response()->json(['Status successfully deleted.']);
  220. } else {
  221. return redirect('/groups/feed');
  222. }
  223. }
  224. public function likePost(Request $request)
  225. {
  226. $this->validate($request, [
  227. 'gid' => 'required',
  228. 'sid' => 'required'
  229. ]);
  230. $pid = $request->user()->profile_id;
  231. $gid = $request->input('gid');
  232. $sid = $request->input('sid');
  233. $group = GroupService::get($gid);
  234. abort_if(!$group || $gid != $group['id'], 422, 'Invalid group');
  235. abort_if(!GroupService::canLike($gid, $pid), 422, 'You cannot interact with this content at this time');
  236. abort_if(!GroupService::isMember($gid, $pid), 403, 'Not a member of group');
  237. $gp = GroupCommentService::get($gid, $sid);
  238. abort_if(!$gp, 422, 'Invalid status');
  239. $count = $gp['favourites_count'] ?? 0;
  240. $like = GroupLike::firstOrCreate([
  241. 'group_id' => $gid,
  242. 'profile_id' => $pid,
  243. 'comment_id' => $sid,
  244. ]);
  245. if($like->wasRecentlyCreated) {
  246. // update parent post like count
  247. $parent = GroupComment::find($sid);
  248. abort_if(!$parent || $parent->group_id != $gid, 422, 'Invalid status');
  249. $parent->likes_count = $parent->likes_count + 1;
  250. $parent->save();
  251. GroupsLikeService::add($pid, $sid);
  252. // invalidate cache
  253. GroupCommentService::del($gid, $sid);
  254. $count++;
  255. GroupService::log(
  256. $gid,
  257. $pid,
  258. 'group:like',
  259. null,
  260. GroupLike::class,
  261. $like->id
  262. );
  263. }
  264. $response = ['code' => 200, 'msg' => 'Like saved', 'count' => $count];
  265. return $response;
  266. }
  267. public function unlikePost(Request $request)
  268. {
  269. $this->validate($request, [
  270. 'gid' => 'required',
  271. 'sid' => 'required'
  272. ]);
  273. $pid = $request->user()->profile_id;
  274. $gid = $request->input('gid');
  275. $sid = $request->input('sid');
  276. $group = GroupService::get($gid);
  277. abort_if(!$group || $gid != $group['id'], 422, 'Invalid group');
  278. abort_if(!GroupService::canLike($gid, $pid), 422, 'You cannot interact with this content at this time');
  279. abort_if(!GroupService::isMember($gid, $pid), 403, 'Not a member of group');
  280. $gp = GroupCommentService::get($gid, $sid);
  281. abort_if(!$gp, 422, 'Invalid status');
  282. $count = $gp['favourites_count'] ?? 0;
  283. $like = GroupLike::where([
  284. 'group_id' => $gid,
  285. 'profile_id' => $pid,
  286. 'comment_id' => $sid,
  287. ])->first();
  288. if($like) {
  289. $like->delete();
  290. $parent = GroupComment::find($sid);
  291. abort_if(!$parent || $parent->group_id != $gid, 422, 'Invalid status');
  292. $parent->likes_count = $parent->likes_count - 1;
  293. $parent->save();
  294. GroupsLikeService::remove($pid, $sid);
  295. // invalidate cache
  296. GroupCommentService::del($gid, $sid);
  297. $count--;
  298. }
  299. $response = ['code' => 200, 'msg' => 'Unliked post', 'count' => $count];
  300. return $response;
  301. }
  302. }