Profile.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <?php
  2. namespace App;
  3. use Auth, Cache, Storage;
  4. use App\Util\Lexer\PrettyNumber;
  5. use Illuminate\Database\Eloquent\{Model, SoftDeletes};
  6. class Profile extends Model
  7. {
  8. use SoftDeletes;
  9. protected $dates = ['deleted_at'];
  10. protected $hidden = ['private_key'];
  11. protected $visible = ['id', 'user_id', 'username', 'name'];
  12. public function user()
  13. {
  14. return $this->belongsTo(User::class);
  15. }
  16. public function url($suffix = null)
  17. {
  18. return $this->remote_url ?? url($this->username . $suffix);
  19. }
  20. public function localUrl($suffix = null)
  21. {
  22. return url($this->username . $suffix);
  23. }
  24. public function permalink($suffix = null)
  25. {
  26. return $this->remote_url ?? url('users/' . $this->username . $suffix);
  27. }
  28. public function emailUrl()
  29. {
  30. if($this->domain) {
  31. return $this->username;
  32. }
  33. $domain = parse_url(config('app.url'), PHP_URL_HOST);
  34. return $this->username.'@'.$domain;
  35. }
  36. public function statuses()
  37. {
  38. return $this->hasMany(Status::class);
  39. }
  40. public function followingCount($short = false)
  41. {
  42. $count = $this->following()->count();
  43. if ($short) {
  44. return PrettyNumber::convert($count);
  45. } else {
  46. return $count;
  47. }
  48. }
  49. public function followerCount($short = false)
  50. {
  51. $count = $this->followers()->count();
  52. if ($short) {
  53. return PrettyNumber::convert($count);
  54. } else {
  55. return $count;
  56. }
  57. }
  58. public function following()
  59. {
  60. return $this->belongsToMany(
  61. self::class,
  62. 'followers',
  63. 'profile_id',
  64. 'following_id'
  65. );
  66. }
  67. public function followers()
  68. {
  69. return $this->belongsToMany(
  70. self::class,
  71. 'followers',
  72. 'following_id',
  73. 'profile_id'
  74. );
  75. }
  76. public function follows($profile) : bool
  77. {
  78. return Follower::whereProfileId($this->id)->whereFollowingId($profile->id)->exists();
  79. }
  80. public function followedBy($profile) : bool
  81. {
  82. return Follower::whereProfileId($profile->id)->whereFollowingId($this->id)->exists();
  83. }
  84. public function bookmarks()
  85. {
  86. return $this->belongsToMany(
  87. Status::class,
  88. 'bookmarks',
  89. 'profile_id',
  90. 'status_id'
  91. );
  92. }
  93. public function likes()
  94. {
  95. return $this->hasMany(Like::class);
  96. }
  97. public function avatar()
  98. {
  99. return $this->hasOne(Avatar::class)->withDefault([
  100. 'media_path' => 'public/avatars/default.png',
  101. ]);
  102. }
  103. public function avatarUrl()
  104. {
  105. $url = Cache::remember("avatar:{$this->id}", 1440, function () {
  106. $path = optional($this->avatar)->media_path;
  107. $version = hash('sha1', $this->avatar->updated_at);
  108. $path = "{$path}?v={$version}";
  109. return url(Storage::url($path));
  110. });
  111. return $url;
  112. }
  113. public function statusCount()
  114. {
  115. return $this->statuses()
  116. ->getQuery()
  117. ->whereHas('media')
  118. ->whereNull('in_reply_to_id')
  119. ->whereNull('reblog_of_id')
  120. ->count();
  121. }
  122. public function recommendFollowers()
  123. {
  124. $follows = $this->following()->pluck('followers.id');
  125. $following = $this->following()
  126. ->orderByRaw('rand()')
  127. ->take(3)
  128. ->pluck('following_id');
  129. $following->push(Auth::id());
  130. $following = Follower::whereNotIn('profile_id', $follows)
  131. ->whereNotIn('following_id', $following)
  132. ->whereNotIn('following_id', $follows)
  133. ->whereIn('profile_id', $following)
  134. ->orderByRaw('rand()')
  135. ->distinct('id')
  136. ->limit(3)
  137. ->pluck('following_id');
  138. $recommended = [];
  139. foreach ($following as $follow) {
  140. $recommended[] = self::findOrFail($follow);
  141. }
  142. return $recommended;
  143. }
  144. public function keyId()
  145. {
  146. if ($this->remote_url) {
  147. return;
  148. }
  149. return $this->permalink('#main-key');
  150. }
  151. public function mutedIds()
  152. {
  153. return UserFilter::whereUserId($this->id)
  154. ->whereFilterableType('App\Profile')
  155. ->whereFilterType('mute')
  156. ->pluck('filterable_id');
  157. }
  158. public function blockedIds()
  159. {
  160. return UserFilter::whereUserId($this->id)
  161. ->whereFilterableType('App\Profile')
  162. ->whereFilterType('block')
  163. ->pluck('filterable_id');
  164. }
  165. public function mutedProfileUrls()
  166. {
  167. $ids = $this->mutedIds();
  168. return $this->whereIn('id', $ids)->get()->map(function($i) {
  169. return $i->url();
  170. });
  171. }
  172. public function blockedProfileUrls()
  173. {
  174. $ids = $this->blockedIds();
  175. return $this->whereIn('id', $ids)->get()->map(function($i) {
  176. return $i->url();
  177. });
  178. }
  179. public function reports()
  180. {
  181. return $this->hasMany(Report::class, 'profile_id');
  182. }
  183. public function media()
  184. {
  185. return $this->hasMany(Media::class, 'profile_id');
  186. }
  187. public function inboxUrl()
  188. {
  189. return $this->inbox_url ?? $this->permalink('/inbox');
  190. }
  191. public function outboxUrl()
  192. {
  193. return $this->outbox_url ?? $this->permalink('/outbox');
  194. }
  195. public function sharedInbox()
  196. {
  197. return $this->sharedInbox ?? $this->inboxUrl();
  198. }
  199. public function getDefaultScope()
  200. {
  201. return $this->is_private == true ? 'private' : 'public';
  202. }
  203. public function getAudience($scope = false)
  204. {
  205. if($this->remote_url) {
  206. return [];
  207. }
  208. $scope = $scope ?? $this->getDefaultScope();
  209. $audience = [];
  210. switch ($scope) {
  211. case 'public':
  212. $audience = [
  213. 'to' => [
  214. 'https://www.w3.org/ns/activitystreams#Public'
  215. ],
  216. 'cc' => [
  217. $this->permalink('/followers')
  218. ]
  219. ];
  220. break;
  221. }
  222. return $audience;
  223. }
  224. public function getAudienceInbox($scope = 'public')
  225. {
  226. return $this
  227. ->followers()
  228. ->whereLocalProfile(false)
  229. ->get()
  230. ->map(function($follow) {
  231. return $follow->sharedInbox ?? $follow->inbox_url;
  232. })
  233. ->unique()
  234. ->toArray();
  235. }
  236. public function circles()
  237. {
  238. return $this->hasMany(Circle::class);
  239. }
  240. }