pixelfed/app/Status.php

416 lines
10 KiB
PHP
Raw Normal View History

<?php
namespace App;
2019-02-25 18:56:24 +00:00
use Auth, Cache, Hashids, Storage;
2018-06-14 00:52:42 +00:00
use Illuminate\Database\Eloquent\Model;
2019-03-12 05:38:21 +00:00
use Pixelfed\Snowflake\HasSnowflakePrimary;
use App\Http\Controllers\StatusController;
2018-06-14 00:52:42 +00:00
use Illuminate\Database\Eloquent\SoftDeletes;
class Status extends Model
{
2019-03-12 05:38:21 +00:00
use HasSnowflakePrimary, SoftDeletes;
/**
* Indicates if the IDs are auto-incrementing.
*
* @var bool
*/
public $incrementing = false;
2018-06-14 00:52:42 +00:00
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['deleted_at'];
2018-08-28 03:07:36 +00:00
2018-12-24 02:07:49 +00:00
protected $fillable = ['profile_id', 'visibility', 'in_reply_to_id', 'reblog_of_id', 'type'];
2018-10-10 01:26:20 +00:00
2018-12-02 04:02:26 +00:00
const STATUS_TYPES = [
2018-12-12 07:16:44 +00:00
'text',
2018-12-02 04:02:26 +00:00
'photo',
'photo:album',
'video',
'video:album',
2018-12-02 06:04:42 +00:00
'photo:video:album',
2018-12-02 04:02:26 +00:00
'share',
'reply',
'story',
'story:reply',
'story:reaction',
2019-05-29 02:58:38 +00:00
'story:live',
'loop'
2018-12-02 04:02:26 +00:00
];
2019-06-02 03:31:03 +00:00
const MAX_MENTIONS = 5;
const MAX_HASHTAGS = 30;
2019-11-12 06:50:25 +00:00
const MAX_LINKS = 0;
2019-06-02 03:31:03 +00:00
2018-04-17 01:24:18 +00:00
public function profile()
{
2018-08-28 03:07:36 +00:00
return $this->belongsTo(Profile::class);
2018-04-17 01:24:18 +00:00
}
public function media()
{
2018-08-28 03:07:36 +00:00
return $this->hasMany(Media::class);
2018-04-17 01:24:18 +00:00
}
public function firstMedia()
{
2018-08-28 03:07:36 +00:00
return $this->hasMany(Media::class)->orderBy('order', 'asc')->first();
2018-04-17 01:24:18 +00:00
}
2018-08-27 03:31:56 +00:00
public function viewType()
{
if($this->type) {
return $this->type;
}
return $this->setType();
2018-08-27 03:31:56 +00:00
}
2018-08-28 03:07:36 +00:00
2018-12-02 07:11:07 +00:00
public function setType()
{
if(in_array($this->type, self::STATUS_TYPES)) {
return $this->type;
2018-12-02 07:11:07 +00:00
}
$mimes = $this->media->pluck('mime')->toArray();
$type = StatusController::mimeTypeCheck($mimes);
if($type) {
$this->type = $type;
$this->save();
return $type;
2018-12-02 07:11:07 +00:00
}
}
2018-08-27 03:31:56 +00:00
public function thumb($showNsfw = false)
{
2019-02-25 18:56:24 +00:00
return Cache::remember('status:thumb:'.$this->id, now()->addMinutes(15), function() use ($showNsfw) {
2018-12-11 04:37:16 +00:00
$type = $this->type ?? $this->setType();
$is_nsfw = !$showNsfw ? $this->is_nsfw : false;
2019-05-27 18:58:54 +00:00
if ($this->media->count() == 0 || $is_nsfw || !in_array($type,['photo', 'photo:album', 'video'])) {
2019-03-04 05:39:52 +00:00
return url(Storage::url('public/no-preview.png'));
}
return url(Storage::url($this->firstMedia()->thumbnail_path));
});
}
2018-04-17 01:24:18 +00:00
public function url()
{
2018-12-24 02:07:49 +00:00
if($this->uri) {
return $this->uri;
2019-05-27 18:58:54 +00:00
} else {
$id = $this->id;
$username = $this->profile->username;
$path = url(config('app.url')."/p/{$username}/{$id}");
return $path;
2018-12-18 06:40:35 +00:00
}
2018-04-17 01:24:18 +00:00
}
2018-08-10 02:57:06 +00:00
public function permalink($suffix = '/activity')
{
2018-08-28 03:07:36 +00:00
$id = $this->id;
$username = $this->profile->username;
$path = config('app.url')."/p/{$username}/{$id}{$suffix}";
return url($path);
2018-08-10 02:57:06 +00:00
}
2018-06-14 00:52:42 +00:00
public function editUrl()
{
2018-08-28 03:07:36 +00:00
return $this->url().'/edit';
2018-06-14 00:52:42 +00:00
}
2018-04-17 01:24:18 +00:00
public function mediaUrl()
{
2018-08-28 03:07:36 +00:00
$media = $this->firstMedia();
$path = $media->media_path;
$hash = is_null($media->processed_at) ? md5('unprocessed') : md5($media->created_at);
2019-12-31 09:33:54 +00:00
$url = $media->cdn_url ? $media->cdn_url . "?v={$hash}" : url(Storage::url($path)."?v={$hash}");
2018-08-28 03:07:36 +00:00
2019-12-31 09:33:54 +00:00
return $url;
2018-04-17 01:24:18 +00:00
}
public function likes()
{
2018-08-28 03:07:36 +00:00
return $this->hasMany(Like::class);
}
2018-07-12 16:41:14 +00:00
public function liked() : bool
{
2018-11-09 01:28:07 +00:00
if(Auth::check() == false) {
return false;
}
2019-06-18 07:15:47 +00:00
$user = Auth::user();
$id = $this->id;
return Cache::remember('status:'.$this->id.':likedby:userid:'.$user->id, now()->addHours(30), function() use($user, $id) {
$profile = $user->profile;
return Like::whereProfileId($profile->id)->whereStatusId($id)->count();
2019-06-18 06:48:40 +00:00
});
2018-07-12 16:41:14 +00:00
}
2018-12-02 04:02:26 +00:00
public function likedBy()
{
return $this->hasManyThrough(
Profile::class,
Like::class,
'status_id',
'id',
'id',
'profile_id'
);
}
2018-04-19 05:57:24 +00:00
public function comments()
{
2018-08-28 03:07:36 +00:00
return $this->hasMany(self::class, 'in_reply_to_id');
}
2018-07-12 16:41:14 +00:00
public function bookmarked()
{
2018-08-28 03:07:36 +00:00
if (!Auth::check()) {
2018-11-14 00:28:45 +00:00
return false;
2018-08-28 03:07:36 +00:00
}
$profile = Auth::user()->profile;
return Bookmark::whereProfileId($profile->id)->whereStatusId($this->id)->count();
2018-07-12 16:41:14 +00:00
}
public function shares()
{
2018-08-28 03:07:36 +00:00
return $this->hasMany(self::class, 'reblog_of_id');
2018-07-12 16:41:14 +00:00
}
public function shared() : bool
{
2018-11-09 01:28:07 +00:00
if(Auth::check() == false) {
return false;
}
2019-06-18 07:15:47 +00:00
$user = Auth::user();
$id = $this->id;
return Cache::remember('status:'.$this->id.':sharedby:userid:'.$user->id, now()->addHours(30), function() use($user, $id) {
$profile = $user->profile;
return self::whereProfileId($profile->id)->whereReblogOfId($id)->count();
});
2018-07-12 16:41:14 +00:00
}
2018-12-02 04:02:26 +00:00
public function sharedBy()
{
return $this->hasManyThrough(
Profile::class,
Status::class,
'reblog_of_id',
'id',
'id',
'profile_id'
);
}
public function parent()
{
2018-08-28 03:07:36 +00:00
$parent = $this->in_reply_to_id ?? $this->reblog_of_id;
if (!empty($parent)) {
2018-11-14 00:28:45 +00:00
return $this->findOrFail($parent);
2019-05-27 18:58:54 +00:00
} else {
return false;
2018-08-28 03:07:36 +00:00
}
}
public function conversation()
{
2018-08-28 03:07:36 +00:00
return $this->hasOne(Conversation::class);
}
public function hashtags()
{
2018-08-28 03:07:36 +00:00
return $this->hasManyThrough(
Hashtag::class,
StatusHashtag::class,
'status_id',
'id',
'id',
'hashtag_id'
);
}
2018-07-12 16:41:14 +00:00
public function mentions()
{
2018-08-28 03:07:36 +00:00
return $this->hasManyThrough(
2018-07-12 16:41:14 +00:00
Profile::class,
Mention::class,
'status_id',
'id',
'id',
'profile_id'
);
}
public function reportUrl()
{
2018-08-28 03:07:36 +00:00
return route('report.form')."?type=post&id={$this->id}";
2018-07-12 16:41:14 +00:00
}
public function toActivityStream()
{
2018-08-28 03:07:36 +00:00
$media = $this->media;
$mediaCollection = [];
foreach ($media as $image) {
$mediaCollection[] = [
'type' => 'Link',
'href' => $image->url(),
'mediaType' => $image->mime,
];
2018-08-28 03:07:36 +00:00
}
$obj = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'type' => 'Image',
'name' => null,
'url' => $mediaCollection,
];
2018-08-28 03:07:36 +00:00
return $obj;
2018-04-19 05:57:24 +00:00
}
2018-06-04 08:16:33 +00:00
public function replyToText()
{
2018-08-28 03:07:36 +00:00
$actorName = $this->profile->username;
return "{$actorName} ".__('notification.commented');
2018-06-04 08:16:33 +00:00
}
public function replyToHtml()
{
2018-08-28 03:07:36 +00:00
$actorName = $this->profile->username;
$actorUrl = $this->profile->url();
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
2018-06-04 08:16:33 +00:00
__('notification.commented');
}
2018-08-10 02:57:06 +00:00
2019-01-15 05:34:50 +00:00
public function shareToText()
{
$actorName = $this->profile->username;
return "{$actorName} ".__('notification.shared');
}
public function shareToHtml()
{
$actorName = $this->profile->username;
$actorUrl = $this->profile->url();
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
__('notification.shared');
}
2018-08-10 02:57:06 +00:00
public function recentComments()
{
2018-08-28 03:07:36 +00:00
return $this->comments()->orderBy('created_at', 'desc')->take(3);
2018-08-10 02:57:06 +00:00
}
2018-10-18 01:05:02 +00:00
public function toActivityPubObject()
{
if($this->local == false) {
return;
}
$profile = $this->profile;
$to = $this->scopeToAudience('to');
$cc = $this->scopeToAudience('cc');
return [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => $this->permalink(),
'type' => 'Create',
'actor' => $profile->permalink(),
'published' => $this->created_at->format('c'),
'to' => $to,
'cc' => $cc,
'object' => [
'id' => $this->url(),
'type' => 'Note',
'summary' => null,
'inReplyTo' => null,
'published' => $this->created_at->format('c'),
'url' => $this->url(),
'attributedTo' => $this->profile->url(),
'to' => $to,
'cc' => $cc,
'sensitive' => (bool) $this->is_nsfw,
'content' => $this->rendered,
'attachment' => $this->media->map(function($media) {
return [
'type' => 'Document',
'mediaType' => $media->mime,
'url' => $media->url(),
'name' => null
];
2018-11-14 00:28:45 +00:00
})->toArray()
2018-10-18 01:05:02 +00:00
]
];
}
public function scopeToAudience($audience)
{
if(!in_array($audience, ['to', 'cc']) || $this->local == false) {
return;
}
$res = [];
$res['to'] = [];
$res['cc'] = [];
$scope = $this->scope;
2018-11-14 00:28:45 +00:00
$mentions = $this->mentions->map(function ($mention) {
return $mention->permalink();
})->toArray();
2019-10-07 08:49:23 +00:00
if($this->in_reply_to_id != null) {
$parent = $this->parent();
2019-10-07 08:46:21 +00:00
if($parent) {
$mentions = array_merge([$parent->profile->permalink()], $mentions);
}
}
2018-10-18 01:05:02 +00:00
switch ($scope) {
case 'public':
$res['to'] = [
"https://www.w3.org/ns/activitystreams#Public"
];
2018-11-14 00:28:45 +00:00
$res['cc'] = array_merge([$this->profile->permalink('/followers')], $mentions);
2018-10-18 01:05:02 +00:00
break;
2018-11-19 04:52:21 +00:00
case 'unlisted':
2019-10-07 08:46:21 +00:00
$res['to'] = array_merge([$this->profile->permalink('/followers')], $mentions);
$res['cc'] = [
"https://www.w3.org/ns/activitystreams#Public"
];
2018-11-19 04:52:21 +00:00
break;
case 'private':
2019-10-07 08:46:21 +00:00
$res['to'] = array_merge([$this->profile->permalink('/followers')], $mentions);
$res['cc'] = [];
2018-11-19 04:52:21 +00:00
break;
// TODO: Update scope when DMs are supported
2018-11-19 04:52:21 +00:00
case 'direct':
$res['to'] = [];
$res['cc'] = [];
2018-10-18 01:05:02 +00:00
break;
}
return $res[$audience];
}
public function place()
{
return $this->belongsTo(Place::class);
}
2020-11-19 02:19:07 +00:00
public function directMessage()
{
return $this->hasOne(DirectMessage::class);
}
}