GroupController.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. <?php
  2. namespace App\Http\Controllers;
  3. use DB;
  4. use Illuminate\Http\Request;
  5. use Illuminate\Support\Str;
  6. use App\Models\Group;
  7. use App\Models\GroupActivityGraph;
  8. use App\Models\GroupBlock;
  9. use App\Models\GroupCategory;
  10. use App\Models\GroupComment;
  11. use App\Models\GroupEvent;
  12. use App\Models\GroupInteraction;
  13. use App\Models\GroupInvitation;
  14. use App\Models\GroupLimit;
  15. use App\Models\GroupLike;
  16. use App\Models\GroupMember;
  17. use App\Models\GroupPost;
  18. use App\Models\GroupPostHashtag;
  19. use App\Models\GroupReport;
  20. use App\Models\GroupRole;
  21. use App\Models\GroupStore;
  22. use App\Models\Poll;
  23. use App\Follower;
  24. use App\Instance;
  25. use App\Hashtag;
  26. use App\StatusHashtag;
  27. use App\Like;
  28. use App\Media;
  29. use App\Notification;
  30. use App\Profile;
  31. use App\Status;
  32. use App\User;
  33. use App\Util\Lexer\Autolink;
  34. use App\Services\AccountService;
  35. use App\Services\FollowerService;
  36. use App\Services\HashidService;
  37. use App\Services\LikeService;
  38. use App\Services\Groups\GroupCommentService;
  39. use App\Services\Groups\GroupsLikeService;
  40. use App\Services\HashtagService;
  41. use App\Services\GroupService;
  42. use App\Services\GroupFeedService;
  43. use App\Services\GroupPostService;
  44. use App\Services\PollService;
  45. use App\Services\RelationshipService;
  46. use App\Services\StatusService;
  47. use App\Services\UserFilterService;
  48. use Cache;
  49. use Storage;
  50. use Purify;
  51. use App\Jobs\GroupPipeline\LikePipeline;
  52. use App\Jobs\GroupPipeline\UnlikePipeline;
  53. use App\Jobs\ImageOptimizePipeline\ImageOptimize;
  54. use App\Jobs\VideoPipeline\VideoThumbnail;
  55. use App\Jobs\StatusPipeline\StatusDelete;
  56. use App\Jobs\GroupPipeline\GroupCommentPipeline;
  57. use App\Jobs\GroupPipeline\GroupMemberInvite;
  58. use App\Jobs\GroupPipeline\NewStatusPipeline;
  59. use App\Jobs\GroupPipeline\JoinApproved;
  60. use App\Jobs\GroupPipeline\JoinRejected;
  61. use Illuminate\Support\Facades\RateLimiter;
  62. class GroupController extends GroupFederationController
  63. {
  64. public function __construct()
  65. {
  66. // $this->middleware('auth');
  67. }
  68. public function index(Request $request)
  69. {
  70. abort_if(!$request->user(), 404);
  71. return view('layouts.spa');
  72. }
  73. public function home(Request $request)
  74. {
  75. abort_if(!$request->user(), 404);
  76. return view('layouts.spa');
  77. }
  78. public function show(Request $request, $id, $path = false)
  79. {
  80. $group = Group::find($id);
  81. if(!$group || $group->status) {
  82. return response()->view('groups.unavailable')->setStatusCode(404);
  83. }
  84. if($request->wantsJson()) {
  85. return $this->showGroupObject($group);
  86. }
  87. return view('layouts.spa', compact('id', 'path'));
  88. }
  89. public function showStatus(Request $request, $gid, $sid)
  90. {
  91. $group = Group::find($gid);
  92. $pid = optional($request->user())->profile_id ?? false;
  93. if(!$group || $group->status) {
  94. return response()->view('groups.unavailable')->setStatusCode(404);
  95. }
  96. if($group->is_private) {
  97. abort_if(!$request->user(), 404);
  98. abort_if(!$group->isMember($pid), 404);
  99. }
  100. $gp = GroupPost::whereGroupId($gid)
  101. ->findOrFail($sid);
  102. return view('layouts.spa', compact('group', 'gp'));
  103. }
  104. public function getGroup(Request $request, $id)
  105. {
  106. $group = Group::whereNull('status')->findOrFail($id);
  107. $pid = optional($request->user())->profile_id ?? false;
  108. $group = $this->toJson($group, $pid);
  109. return response()->json($group, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
  110. }
  111. public function showStatusLikes(Request $request, $id, $sid)
  112. {
  113. $group = Group::findOrFail($id);
  114. $user = $request->user();
  115. $pid = $user->profile_id;
  116. abort_if(!$group->isMember($pid), 404);
  117. $status = GroupPost::whereGroupId($id)->findOrFail($sid);
  118. $likes = GroupLike::whereStatusId($sid)
  119. ->cursorPaginate(10)
  120. ->map(function($l) use($group) {
  121. $account = AccountService::get($l->profile_id);
  122. $account['url'] = "/groups/{$group->id}/user/{$account['id']}";
  123. return $account;
  124. })
  125. ->filter(function($l) {
  126. return $l && isset($l['id']);
  127. })
  128. ->values();
  129. return $likes;
  130. }
  131. public function groupSettings(Request $request, $id)
  132. {
  133. abort_if(!$request->user(), 404);
  134. $group = Group::findOrFail($id);
  135. $pid = $request->user()->profile_id;
  136. abort_if(!$group->isMember($pid), 404);
  137. abort_if(!in_array($group->selfRole($pid), ['founder', 'admin']), 404);
  138. return view('groups.settings', compact('group'));
  139. }
  140. public function joinGroup(Request $request, $id)
  141. {
  142. $group = Group::findOrFail($id);
  143. $pid = $request->user()->profile_id;
  144. abort_if($group->isMember($pid), 404);
  145. if(!$request->user()->is_admin) {
  146. abort_if(GroupService::getRejoinTimeout($group->id, $pid), 422, 'Cannot re-join this group for 24 hours after leaving or cancelling a request to join');
  147. }
  148. $member = new GroupMember;
  149. $member->group_id = $group->id;
  150. $member->profile_id = $pid;
  151. $member->role = 'member';
  152. $member->local_group = true;
  153. $member->local_profile = true;
  154. $member->join_request = $group->is_private;
  155. $member->save();
  156. GroupService::delSelf($group->id, $pid);
  157. GroupService::log(
  158. $group->id,
  159. $pid,
  160. 'group:joined',
  161. null,
  162. GroupMember::class,
  163. $member->id
  164. );
  165. $group = $this->toJson($group, $pid);
  166. return $group;
  167. }
  168. public function updateGroup(Request $request, $id)
  169. {
  170. $this->validate($request, [
  171. 'description' => 'nullable|max:500',
  172. 'membership' => 'required|in:all,local,private',
  173. 'avatar' => 'nullable',
  174. 'header' => 'nullable',
  175. 'discoverable' => 'required',
  176. 'activitypub' => 'required',
  177. 'is_nsfw' => 'required',
  178. 'category' => 'required|string|in:' . implode(',',GroupService::categories())
  179. ]);
  180. $pid = $request->user()->profile_id;
  181. $group = Group::whereProfileId($pid)->findOrFail($id);
  182. $member = GroupMember::whereGroupId($group->id)->whereProfileId($pid)->firstOrFail();
  183. abort_if($member->role != 'founder', 403, 'Invalid group permission');
  184. $metadata = $group->metadata;
  185. $len = $group->is_private ? 12 : 4;
  186. if($request->hasFile('avatar')) {
  187. $avatar = $request->file('avatar');
  188. if($avatar) {
  189. if( isset($metadata['avatar']) &&
  190. isset($metadata['avatar']['path']) &&
  191. Storage::exists($metadata['avatar']['path'])
  192. ) {
  193. Storage::delete($metadata['avatar']['path']);
  194. }
  195. $fileName = 'avatar_' . strtolower(str_random($len)) . '.' . $avatar->extension();
  196. $path = $avatar->storePubliclyAs('public/g/'.$group->id.'/meta', $fileName);
  197. $url = url(Storage::url($path));
  198. $metadata['avatar'] = [
  199. 'path' => $path,
  200. 'url' => $url,
  201. 'updated_at' => now()
  202. ];
  203. }
  204. }
  205. if($request->hasFile('header')) {
  206. $header = $request->file('header');
  207. if($header) {
  208. if( isset($metadata['header']) &&
  209. isset($metadata['header']['path']) &&
  210. Storage::exists($metadata['header']['path'])
  211. ) {
  212. Storage::delete($metadata['header']['path']);
  213. }
  214. $fileName = 'header_' . strtolower(str_random($len)) . '.' . $header->extension();
  215. $path = $header->storePubliclyAs('public/g/'.$group->id.'/meta', $fileName);
  216. $url = url(Storage::url($path));
  217. $metadata['header'] = [
  218. 'path' => $path,
  219. 'url' => $url,
  220. 'updated_at' => now()
  221. ];
  222. }
  223. }
  224. $cat = GroupService::categoryById($group->category_id);
  225. if($request->category !== $cat['name']) {
  226. $group->category_id = GroupCategory::whereName($request->category)->first()->id;
  227. }
  228. $changes = null;
  229. $group->description = e($request->input('description', null));
  230. $group->is_private = $request->input('membership') == 'private';
  231. $group->local_only = $request->input('membership') == 'local';
  232. $group->activitypub = $request->input('activitypub') == "true";
  233. $group->discoverable = $request->input('discoverable') == "true";
  234. $group->is_nsfw = $request->input('is_nsfw') == "true";
  235. $group->metadata = $metadata;
  236. if($group->isDirty()) {
  237. $changes = $group->getDirty();
  238. }
  239. $group->save();
  240. GroupService::log(
  241. $group->id,
  242. $pid,
  243. 'group:settings:updated',
  244. $changes
  245. );
  246. GroupService::del($group->id);
  247. $res = $this->toJson($group, $pid);
  248. return $res;
  249. }
  250. protected function toJson($group, $pid = false)
  251. {
  252. return GroupService::get($group->id, $pid);
  253. }
  254. // public function likePost(Request $request)
  255. // {
  256. // $this->validate($request, [
  257. // 'gid' => 'required|exists:groups,id',
  258. // 'sid' => 'required|exists:group_posts,id'
  259. // ]);
  260. // $pid = $request->user()->profile_id;
  261. // $gid = $request->input('gid');
  262. // $sid = $request->input('sid');
  263. // $group = Group::findOrFail($gid);
  264. // abort_if(!GroupService::canLike($gid, $pid), 422, 'You cannot interact with this content at this time');
  265. // abort_if(!$group->isMember($pid), 403, 'Not a member of group.');
  266. // $gp = GroupPost::whereGroupId($group->id)->findOrFail($sid);
  267. // $action = false;
  268. // if (GroupLike::whereGroupId($gid)->whereStatusId($sid)->whereProfileId($pid)->exists()) {
  269. // $like = GroupLike::whereProfileId($pid)->whereStatusId($sid)->firstOrFail();
  270. // // UnlikePipeline::dispatch($like);
  271. // $count = $gp->likes_count - 1;
  272. // $action = 'group:unlike';
  273. // } else {
  274. // $count = $gp->likes_count;
  275. // $like = GroupLike::firstOrCreate([
  276. // 'group_id' => $gid,
  277. // 'profile_id' => $pid,
  278. // 'status_id' => $sid
  279. // ]);
  280. // if($like->wasRecentlyCreated == true) {
  281. // $count++;
  282. // $gp->likes_count = $count;
  283. // $like->save();
  284. // $gp->save();
  285. // // LikePipeline::dispatch($like);
  286. // $action = 'group:like';
  287. // }
  288. // }
  289. // if($action) {
  290. // GroupService::log(
  291. // $group->id,
  292. // $pid,
  293. // $action,
  294. // [
  295. // 'type' => $gp->type,
  296. // 'status_id' => $gp->id
  297. // ],
  298. // GroupPost::class,
  299. // $gp->id
  300. // );
  301. // }
  302. // // Cache::forget('status:'.$status->id.':likedby:userid:'.$request->user()->id);
  303. // // StatusService::del($status->id);
  304. // $response = ['code' => 200, 'msg' => 'Like saved', 'count' => $count];
  305. // return $response;
  306. // }
  307. public function groupLeave(Request $request, $id)
  308. {
  309. abort_if(!$request->user(), 404);
  310. $pid = $request->user()->profile_id;
  311. $group = Group::findOrFail($id);
  312. abort_if($pid == $group->profile_id, 422, 'Cannot leave a group you created');
  313. abort_if(!$group->isMember($pid), 403, 'Not a member of group.');
  314. GroupMember::whereGroupId($group->id)->whereProfileId($pid)->delete();
  315. GroupService::del($group->id);
  316. GroupService::delSelf($group->id, $pid);
  317. GroupService::setRejoinTimeout($group->id, $pid);
  318. return [200];
  319. }
  320. public function cancelJoinRequest(Request $request, $id)
  321. {
  322. abort_if(!$request->user(), 404);
  323. $pid = $request->user()->profile_id;
  324. $group = Group::findOrFail($id);
  325. abort_if($pid == $group->profile_id, 422, 'Cannot leave a group you created');
  326. abort_if($group->isMember($pid), 422, 'Cannot cancel approved join request, please leave group instead.');
  327. GroupMember::whereGroupId($group->id)->whereProfileId($pid)->delete();
  328. GroupService::del($group->id);
  329. GroupService::delSelf($group->id, $pid);
  330. GroupService::setRejoinTimeout($group->id, $pid);
  331. return [200];
  332. }
  333. public function metaBlockSearch(Request $request, $id)
  334. {
  335. abort_if(!$request->user(), 404);
  336. $group = Group::findOrFail($id);
  337. $pid = $request->user()->profile_id;
  338. abort_if(!$group->isMember($pid), 404);
  339. abort_if(!in_array($group->selfRole($pid), ['founder', 'admin']), 404);
  340. $type = $request->input('type');
  341. $item = $request->input('item');
  342. switch($type) {
  343. case 'instance':
  344. $res = Instance::whereDomain($item)->first();
  345. if($res) {
  346. abort_if(GroupBlock::whereGroupId($group->id)->whereInstanceId($res->id)->exists(), 400);
  347. }
  348. break;
  349. case 'user':
  350. $res = Profile::whereUsername($item)->first();
  351. if($res) {
  352. abort_if(GroupBlock::whereGroupId($group->id)->whereProfileId($res->id)->exists(), 400);
  353. }
  354. if($res->user_id != null) {
  355. abort_if(User::whereIsAdmin(true)->whereId($res->user_id)->exists(), 400);
  356. }
  357. break;
  358. }
  359. return response()->json((bool) $res, ($res ? 200 : 404));
  360. }
  361. public function reportCreate(Request $request, $id)
  362. {
  363. abort_if(!$request->user(), 404);
  364. $group = Group::findOrFail($id);
  365. $pid = $request->user()->profile_id;
  366. abort_if(!$group->isMember($pid), 404);
  367. $id = $request->input('id');
  368. $type = $request->input('type');
  369. $types = [
  370. // original 3
  371. 'spam',
  372. 'sensitive',
  373. 'abusive',
  374. // new
  375. 'underage',
  376. 'violence',
  377. 'copyright',
  378. 'impersonation',
  379. 'scam',
  380. 'terrorism'
  381. ];
  382. $gp = GroupPost::whereGroupId($group->id)->find($id);
  383. abort_if(!$gp, 422, 'Cannot report an invalid or deleted post');
  384. abort_if(!in_array($type, $types), 422, 'Invalid report type');
  385. abort_if($gp->profile_id === $pid, 422, 'Cannot report your own post');
  386. abort_if(
  387. GroupReport::whereGroupId($group->id)
  388. ->whereProfileId($pid)
  389. ->whereItemType(GroupPost::class)
  390. ->whereItemId($id)
  391. ->exists(),
  392. 422,
  393. 'You already reported this'
  394. );
  395. $report = new GroupReport();
  396. $report->group_id = $group->id;
  397. $report->profile_id = $pid;
  398. $report->type = $type;
  399. $report->item_type = GroupPost::class;
  400. $report->item_id = $id;
  401. $report->open = true;
  402. $report->save();
  403. GroupService::log(
  404. $group->id,
  405. $pid,
  406. 'group:report:create',
  407. [
  408. 'type' => $type,
  409. 'report_id' => $report->id,
  410. 'status_id' => $gp->status_id,
  411. 'profile_id' => $gp->profile_id,
  412. 'username' => optional(AccountService::get($gp->profile_id))['acct'],
  413. 'gpid' => $gp->id,
  414. 'url' => $gp->url()
  415. ],
  416. GroupReport::class,
  417. $report->id
  418. );
  419. return response([200]);
  420. }
  421. public function reportAction(Request $request, $id)
  422. {
  423. abort_if(!$request->user(), 404);
  424. $group = Group::findOrFail($id);
  425. $pid = $request->user()->profile_id;
  426. abort_if(!$group->isMember($pid), 404);
  427. abort_if(!in_array($group->selfRole($pid), ['founder', 'admin']), 404);
  428. $this->validate($request, [
  429. 'action' => 'required|in:cw,delete,ignore',
  430. 'id' => 'required|string'
  431. ]);
  432. $action = $request->input('action');
  433. $id = $request->input('id');
  434. $report = GroupReport::whereGroupId($group->id)
  435. ->findOrFail($id);
  436. $status = Status::findOrFail($report->item_id);
  437. $gp = GroupPost::whereGroupId($group->id)
  438. ->whereStatusId($status->id)
  439. ->firstOrFail();
  440. switch ($action) {
  441. case 'cw':
  442. $status->is_nsfw = true;
  443. $status->save();
  444. StatusService::del($status->id);
  445. GroupReport::whereGroupId($group->id)
  446. ->whereItemType($report->item_type)
  447. ->whereItemId($report->item_id)
  448. ->update(['open' => false]);
  449. GroupService::log(
  450. $group->id,
  451. $pid,
  452. 'group:moderation:action',
  453. [
  454. 'type' => 'cw',
  455. 'report_id' => $report->id,
  456. 'status_id' => $status->id,
  457. 'profile_id' => $status->profile_id,
  458. 'status_url' => $gp->url()
  459. ],
  460. GroupReport::class,
  461. $report->id
  462. );
  463. return response()->json([200]);
  464. break;
  465. case 'ignore':
  466. GroupReport::whereGroupId($group->id)
  467. ->whereItemType($report->item_type)
  468. ->whereItemId($report->item_id)
  469. ->update(['open' => false]);
  470. GroupService::log(
  471. $group->id,
  472. $pid,
  473. 'group:moderation:action',
  474. [
  475. 'type' => 'ignore',
  476. 'report_id' => $report->id,
  477. 'status_id' => $status->id,
  478. 'profile_id' => $status->profile_id,
  479. 'status_url' => $gp->url()
  480. ],
  481. GroupReport::class,
  482. $report->id
  483. );
  484. return response()->json([200]);
  485. break;
  486. }
  487. }
  488. public function getMemberInteractionLimits(Request $request, $id)
  489. {
  490. abort_if(!$request->user(), 404);
  491. $group = Group::findOrFail($id);
  492. $pid = $request->user()->profile_id;
  493. abort_if(!$group->isMember($pid), 404);
  494. abort_if(!in_array($group->selfRole($pid), ['founder', 'admin']), 404);
  495. $profile_id = $request->input('profile_id');
  496. abort_if(!$group->isMember($profile_id), 404);
  497. $limits = GroupService::getInteractionLimits($group->id, $profile_id);
  498. return response()->json($limits);
  499. }
  500. public function updateMemberInteractionLimits(Request $request, $id)
  501. {
  502. abort_if(!$request->user(), 404);
  503. $group = Group::findOrFail($id);
  504. $pid = $request->user()->profile_id;
  505. abort_if(!$group->isMember($pid), 404);
  506. abort_if(!in_array($group->selfRole($pid), ['founder', 'admin']), 404);
  507. $this->validate($request, [
  508. 'profile_id' => 'required|exists:profiles,id',
  509. 'can_post' => 'required',
  510. 'can_comment' => 'required',
  511. 'can_like' => 'required'
  512. ]);
  513. $member = $request->input('profile_id');
  514. $can_post = $request->input('can_post');
  515. $can_comment = $request->input('can_comment');
  516. $can_like = $request->input('can_like');
  517. $account = AccountService::get($member);
  518. abort_if(!$account, 422, 'Invalid profile');
  519. abort_if(!$group->isMember($member), 422, 'Invalid profile');
  520. $limit = GroupLimit::firstOrCreate([
  521. 'profile_id' => $member,
  522. 'group_id' => $group->id
  523. ]);
  524. if($limit->wasRecentlyCreated) {
  525. abort_if(GroupLimit::whereGroupId($group->id)->count() >= 25, 422, 'limit_reached');
  526. }
  527. $previousLimits = $limit->limits;
  528. $limit->limits = [
  529. 'can_post' => $can_post,
  530. 'can_comment' => $can_comment,
  531. 'can_like' => $can_like
  532. ];
  533. $limit->save();
  534. GroupService::clearInteractionLimits($group->id, $member);
  535. GroupService::log(
  536. $group->id,
  537. $pid,
  538. 'group:member-limits:updated',
  539. [
  540. 'profile_id' => $account['id'],
  541. 'username' => $account['username'],
  542. 'previousLimits' => $previousLimits,
  543. 'newLimits' => $limit->limits
  544. ],
  545. GroupLimit::class,
  546. $limit->id
  547. );
  548. return $request->all();
  549. }
  550. public function showProfile(Request $request, $id, $pid)
  551. {
  552. $group = Group::find($id);
  553. if(!$group || $group->status) {
  554. return response()->view('groups.unavailable')->setStatusCode(404);
  555. }
  556. // $gm = GroupMember::whereGroupId($id)
  557. // ->whereProfileId($pid)
  558. // ->firstOrFail();
  559. // $group = json_encode(GroupService::get($id));
  560. // $profile = AccountService::get($pid);
  561. // $profile['group'] = [
  562. // 'joined' => $gm->created_at->format('M d, Y'),
  563. // 'role' => $gm->role
  564. // ];
  565. // $profile['relationship'] = RelationshipService::get($cid, $pid);
  566. // $profile = json_encode($profile);
  567. return view('layouts.spa');
  568. }
  569. public function showProfileByUsername(Request $request, $id, $pid)
  570. {
  571. // abort_if(!$request->user(), 404);
  572. if(!$request->user()) {
  573. return redirect("/{$pid}");
  574. }
  575. $group = Group::find($id);
  576. $cid = $request->user()->profile_id;
  577. if(!$group || $group->status) {
  578. return response()->view('groups.unavailable')->setStatusCode(404);
  579. }
  580. if(!$group->isMember($cid)) {
  581. return redirect("/{$pid}");
  582. }
  583. $profile = Profile::whereUsername($pid)->first();
  584. if(!$group->isMember($profile->id)) {
  585. return redirect("/{$pid}");
  586. }
  587. if($profile) {
  588. $url = url("/groups/{$id}/user/{$profile->id}");
  589. return redirect($url);
  590. }
  591. abort(404, 'Invalid username');
  592. }
  593. public function groupInviteLanding(Request $request, $id)
  594. {
  595. abort(404, 'Not yet implemented');
  596. $group = Group::findOrFail($id);
  597. return view('groups.invite', compact('group'));
  598. }
  599. public function groupShortLinkRedirect(Request $request, $hid)
  600. {
  601. $gid = HashidService::decode($hid);
  602. $group = Group::findOrFail($gid);
  603. return redirect($group->url());
  604. }
  605. public function groupInviteClaim(Request $request, $id)
  606. {
  607. $group = GroupService::get($id);
  608. abort_if(!$group || empty($group), 404);
  609. return view('groups.invite-claim', compact('group'));
  610. }
  611. public function groupMemberInviteCheck(Request $request, $id)
  612. {
  613. abort_if(!$request->user(), 404);
  614. $pid = $request->user()->profile_id;
  615. $group = Group::findOrFail($id);
  616. abort_if($group->isMember($pid), 422, 'Already a member');
  617. $exists = GroupInvitation::whereGroupId($id)->whereToProfileId($pid)->exists();
  618. return response()->json([
  619. 'gid' => $id,
  620. 'can_join' => (bool) $exists
  621. ]);
  622. }
  623. public function groupMemberInviteAccept(Request $request, $id)
  624. {
  625. abort_if(!$request->user(), 404);
  626. $pid = $request->user()->profile_id;
  627. $group = Group::findOrFail($id);
  628. abort_if($group->isMember($pid), 422, 'Already a member');
  629. abort_if(!GroupInvitation::whereGroupId($id)->whereToProfileId($pid)->exists(), 422);
  630. $gm = new GroupMember;
  631. $gm->group_id = $id;
  632. $gm->profile_id = $pid;
  633. $gm->role = 'member';
  634. $gm->local_group = $group->local;
  635. $gm->local_profile = true;
  636. $gm->join_request = false;
  637. $gm->save();
  638. GroupInvitation::whereGroupId($id)->whereToProfileId($pid)->delete();
  639. GroupService::del($id);
  640. GroupService::delSelf($id, $pid);
  641. return ['next_url' => $group->url()];
  642. }
  643. public function groupMemberInviteDecline(Request $request, $id)
  644. {
  645. abort_if(!$request->user(), 404);
  646. $pid = $request->user()->profile_id;
  647. $group = Group::findOrFail($id);
  648. abort_if($group->isMember($pid), 422, 'Already a member');
  649. return ['next_url' => '/'];
  650. }
  651. }