1
0

StatusService.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <?php
  2. namespace App\Services;
  3. use App\Status;
  4. use App\Transformer\Api\StatusStatelessTransformer;
  5. use Illuminate\Support\Facades\Cache;
  6. use League\Fractal;
  7. use League\Fractal\Serializer\ArraySerializer;
  8. class StatusService
  9. {
  10. const CACHE_KEY = 'pf:services:status:v1.1:';
  11. const MAX_PINNED = 3;
  12. public static function key($id, $publicOnly = true)
  13. {
  14. $p = $publicOnly ? 'pub:' : 'all:';
  15. return self::CACHE_KEY.$p.$id;
  16. }
  17. public static function get($id, $publicOnly = true, $mastodonMode = false)
  18. {
  19. $res = Cache::remember(self::key($id, $publicOnly), 21600, function () use ($id, $publicOnly) {
  20. if ($publicOnly) {
  21. $status = Status::whereScope('public')->find($id);
  22. } else {
  23. $status = Status::whereIn('scope', ['public', 'private', 'unlisted', 'group'])->find($id);
  24. }
  25. if (! $status) {
  26. return null;
  27. }
  28. $fractal = new Fractal\Manager;
  29. $fractal->setSerializer(new ArraySerializer);
  30. $resource = new Fractal\Resource\Item($status, new StatusStatelessTransformer);
  31. $res = $fractal->createData($resource)->toArray();
  32. $res['_pid'] = isset($res['account']) && isset($res['account']['id']) ? $res['account']['id'] : null;
  33. if (isset($res['_pid'])) {
  34. unset($res['account']);
  35. }
  36. return $res;
  37. });
  38. if ($res && isset($res['_pid'])) {
  39. $res['account'] = $mastodonMode === true ? AccountService::getMastodon($res['_pid'], true) : AccountService::get($res['_pid'], true);
  40. unset($res['_pid']);
  41. }
  42. return $res;
  43. }
  44. public static function getMastodon($id, $publicOnly = true)
  45. {
  46. $status = self::get($id, $publicOnly, true);
  47. if (! $status) {
  48. return null;
  49. }
  50. if (! isset($status['account'])) {
  51. return null;
  52. }
  53. $status['replies_count'] = $status['reply_count'];
  54. if (config('exp.emc') == false) {
  55. return $status;
  56. }
  57. unset(
  58. $status['_v'],
  59. $status['comments_disabled'],
  60. $status['content_text'],
  61. $status['gid'],
  62. $status['label'],
  63. $status['liked_by'],
  64. $status['local'],
  65. $status['parent'],
  66. $status['pf_type'],
  67. $status['place'],
  68. $status['replies'],
  69. $status['reply_count'],
  70. $status['shortcode'],
  71. $status['taggedPeople'],
  72. $status['thread'],
  73. $status['account']['header_bg'],
  74. $status['account']['is_admin'],
  75. $status['account']['last_fetched_at'],
  76. $status['account']['local'],
  77. $status['account']['location'],
  78. $status['account']['note_text'],
  79. $status['account']['pronouns'],
  80. $status['account']['website'],
  81. $status['media_attachments'],
  82. );
  83. $status['account']['avatar_static'] = $status['account']['avatar'];
  84. $status['account']['bot'] = false;
  85. $status['account']['emojis'] = [];
  86. $status['account']['fields'] = [];
  87. $status['account']['header'] = url('/storage/headers/missing.png');
  88. $status['account']['header_static'] = url('/storage/headers/missing.png');
  89. $status['account']['last_status_at'] = null;
  90. $status['media_attachments'] = array_values(MediaService::getMastodon($status['id']));
  91. $status['muted'] = false;
  92. $status['reblogged'] = false;
  93. return $status;
  94. }
  95. public static function getState($id, $pid)
  96. {
  97. $status = self::get($id, false);
  98. if (! $status || ! $pid) {
  99. return [
  100. 'liked' => false,
  101. 'shared' => false,
  102. 'bookmarked' => false,
  103. ];
  104. }
  105. return [
  106. 'liked' => LikeService::liked($pid, $id),
  107. 'shared' => self::isShared($id, $pid),
  108. 'bookmarked' => self::isBookmarked($id, $pid),
  109. ];
  110. }
  111. public static function getFull($id, $pid, $publicOnly = true)
  112. {
  113. $res = self::get($id, $publicOnly);
  114. if (! $res || ! isset($res['account']) || ! isset($res['account']['id'])) {
  115. return $res;
  116. }
  117. $res['relationship'] = RelationshipService::get($pid, $res['account']['id']);
  118. return $res;
  119. }
  120. public static function getDirectMessage($id)
  121. {
  122. $status = Status::whereScope('direct')->find($id);
  123. if (! $status) {
  124. return null;
  125. }
  126. $fractal = new Fractal\Manager;
  127. $fractal->setSerializer(new ArraySerializer);
  128. $resource = new Fractal\Resource\Item($status, new StatusStatelessTransformer);
  129. return $fractal->createData($resource)->toArray();
  130. }
  131. public static function del($id, $purge = false)
  132. {
  133. if ($purge) {
  134. $status = self::get($id);
  135. if ($status && isset($status['account']) && isset($status['account']['id'])) {
  136. Cache::forget('profile:embed:'.$status['account']['id']);
  137. }
  138. Cache::forget('status:transformer:media:attachments:'.$id);
  139. MediaService::del($id);
  140. Cache::forget('pf:services:sh:id:'.$id);
  141. PublicTimelineService::rem($id);
  142. NetworkTimelineService::rem($id);
  143. }
  144. Cache::forget(self::key($id, false));
  145. return Cache::forget(self::key($id));
  146. }
  147. public static function refresh($id)
  148. {
  149. Cache::forget(self::key($id, false));
  150. Cache::forget(self::key($id, true));
  151. self::get($id, false);
  152. self::get($id, true);
  153. }
  154. public static function isShared($id, $pid = null)
  155. {
  156. return $pid ?
  157. ReblogService::get($pid, $id) :
  158. false;
  159. }
  160. public static function isBookmarked($id, $pid = null)
  161. {
  162. return $pid ?
  163. BookmarkService::get($pid, $id) :
  164. false;
  165. }
  166. public static function totalLocalStatuses()
  167. {
  168. return InstanceService::totalLocalStatuses();
  169. }
  170. public static function isPinned($id)
  171. {
  172. return Status::whereId($id)->whereNotNull('pinned_order')->exists();
  173. }
  174. public static function totalPins($pid)
  175. {
  176. return Status::whereProfileId($pid)->whereNotNull('pinned_order')->count();
  177. }
  178. public static function markPin($id)
  179. {
  180. $status = Status::find($id);
  181. if (! $status) {
  182. return [
  183. 'success' => false,
  184. 'error' => 'Record not found',
  185. ];
  186. }
  187. if ($status->scope != 'public') {
  188. return [
  189. 'success' => false,
  190. 'error' => 'Validation failed: you can only pin public posts',
  191. ];
  192. }
  193. if (self::isPinned($id)) {
  194. return [
  195. 'success' => false,
  196. 'error' => 'This post is already pinned',
  197. ];
  198. }
  199. $totalPins = self::totalPins($status->profile_id);
  200. if ($totalPins >= self::MAX_PINNED) {
  201. return [
  202. 'success' => false,
  203. 'error' => 'Validation failed: You have already pinned the max number of posts',
  204. ];
  205. }
  206. $status->pinned_order = $totalPins + 1;
  207. $status->save();
  208. self::refresh($id);
  209. return [
  210. 'success' => true,
  211. 'error' => null,
  212. ];
  213. }
  214. public static function unmarkPin($id)
  215. {
  216. $status = Status::find($id);
  217. if (! $status || is_null($status->pinned_order)) {
  218. return false;
  219. }
  220. $removedOrder = $status->pinned_order;
  221. $profileId = $status->profile_id;
  222. $status->pinned_order = null;
  223. $status->save();
  224. Status::where('profile_id', $profileId)
  225. ->whereNotNull('pinned_order')
  226. ->where('pinned_order', '>', $removedOrder)
  227. ->orderBy('pinned_order', 'asc')
  228. ->chunk(10, function ($statuses) {
  229. foreach ($statuses as $s) {
  230. $s->pinned_order = $s->pinned_order - 1;
  231. $s->save();
  232. }
  233. });
  234. self::refresh($id);
  235. return true;
  236. }
  237. }