1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/pixelfed_ynh.git synced 2024-09-03 20:06:04 +02:00

Merge pull request #3295 from pixelfed/staging

Staging
This commit is contained in:
daniel 2022-03-08 21:02:27 -07:00 committed by GitHub
commit 7ab581f700
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 313 additions and 80 deletions

View file

@ -67,6 +67,12 @@
- Updated admin diagnostics, add more configuration data to help diagnose potential issues. ([eab96fc3](https://github.com/pixelfed/pixelfed/commit/eab96fc3))
- Updated ConfigCacheService, fix discover features. ([ad48521a](https://github.com/pixelfed/pixelfed/commit/ad48521a))
- Updated MediaTransformer, fix type case bug. Fixes #3281. ([c1669253](https://github.com/pixelfed/pixelfed/commit/c1669253))
- Updated SpaController, redirect web ui hashtags to legacy page for unauthenticated users. ([a44b812b](https://github.com/pixelfed/pixelfed/commit/a44b812b))
- Updated ApiV1Controller, fixes #3288. ([3e670774](https://github.com/pixelfed/pixelfed/commit/3e670774))
- Updated AP Helpers, fixes #3287. ([b78bff72](https://github.com/pixelfed/pixelfed/commit/b78bff72))
- Updated AP Helpers, fixes #3290. ([53975206](https://github.com/pixelfed/pixelfed/commit/53975206))
- Updated AccountController, refresh relationship after handling follow request. ([fe768785](https://github.com/pixelfed/pixelfed/commit/fe768785))
- Updated CollectionController, fixes #3289. ([c7e1e473](https://github.com/pixelfed/pixelfed/commit/c7e1e473))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.2 (2022-01-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.1...v0.11.2)

View file

@ -406,6 +406,7 @@ class AccountController extends Controller
Cache::forget('profile:follower_count:'.$pid);
Cache::forget('profile:following_count:'.$pid);
RelationshipService::refresh($pid, $follower->id);
return response()->json(['msg' => 'success'], 200);
}

View file

@ -2547,7 +2547,7 @@ class ApiV1Controller extends Controller
$sortBy = $request->input('sort', 'all');
if($sortBy == 'all' && $status['replies_count'] && $request->has('refresh_cache')) {
if($sortBy == 'all' && isset($status['replies_count']) && $status['replies_count'] && $request->has('refresh_cache')) {
if(!Cache::has('status:replies:all-rc:' . $id)) {
Cache::forget('status:replies:all:' . $id);
Cache::put('status:replies:all-rc:' . $id, true, 300);

View file

@ -17,6 +17,8 @@ use App\Transformer\Api\{
};
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use App\Services\CollectionService;
use App\Services\FollowerService;
use App\Services\StatusService;
class CollectionController extends Controller
@ -30,18 +32,25 @@ class CollectionController extends Controller
'profile_id' => $profile->id,
'published_at' => null
]);
$collection->visibility = 'draft';
$collection->save();
return view('collection.create', compact('collection'));
}
public function show(Request $request, int $id)
{
$user = $request->user();
$collection = Collection::findOrFail($id);
if($collection->published_at == null || $collection->visibility != 'public') {
if(!$user || $user->profile_id != $collection->profile_id) {
abort_unless($user && $user->is_admin, 404);
}
}
$collection = CollectionService::getCollection($id);
abort_if(!$collection, 404);
if($collection['published_at'] == null || $collection['visibility'] != 'public') {
abort_if(!$user, 404);
if($user->profile_id != $collection['pid']) {
if(!$user->is_admin) {
abort_if($collection['visibility'] != 'private', 404);
abort_if(!FollowerService::follows($user->profile_id, $collection['pid']), 404);
}
}
}
return view('collection.show', compact('collection'));
}
@ -57,7 +66,7 @@ class CollectionController extends Controller
$this->validate($request, [
'title' => 'nullable',
'description' => 'nullable',
'visibility' => 'nullable|string|in:public,private'
'visibility' => 'nullable|string|in:public,private,draft'
]);
$profile = Auth::user()->profile;
@ -67,7 +76,7 @@ class CollectionController extends Controller
$collection->visibility = e($request->input('visibility'));
$collection->save();
return 200;
return CollectionService::setCollection($collection->id, $collection);
}
public function publish(Request $request, int $id)
@ -76,7 +85,7 @@ class CollectionController extends Controller
$this->validate($request, [
'title' => 'nullable',
'description' => 'nullable',
'visibility' => 'required|alpha|in:public,private'
'visibility' => 'required|alpha|in:public,private,draft'
]);
$profile = Auth::user()->profile;
$collection = Collection::whereProfileId($profile->id)->findOrFail($id);
@ -88,8 +97,7 @@ class CollectionController extends Controller
$collection->visibility = e($request->input('visibility'));
$collection->published_at = now();
$collection->save();
return $collection->url();
return CollectionService::setCollection($collection->id, $collection);
}
public function delete(Request $request, int $id)
@ -105,6 +113,8 @@ class CollectionController extends Controller
return 200;
}
CollectionService::deleteCollection($id);
return redirect('/');
}
@ -139,82 +149,98 @@ class CollectionController extends Controller
'order' => $count,
]);
return 200;
CollectionService::addItem(
$collection->id,
$status->id,
$count
);
return StatusService::get($status->id);
}
public function get(Request $request, $id)
public function getCollection(Request $request, $id)
{
$user = $request->user();
$collection = Collection::findOrFail($id);
if($collection->published_at == null || $collection->visibility != 'public') {
if(!$user || $user->profile_id != $collection->profile_id) {
abort_unless($user && $user->is_admin, 404);
}
}
$user = $request->user();
$collection = CollectionService::getCollection($id);
if($collection['published_at'] == null || $collection['visibility'] != 'public') {
abort_unless($user, 404);
if($user->profile_id != $collection['pid']) {
if(!$user->is_admin) {
abort_if($collection['visibility'] != 'private', 404);
abort_if(!FollowerService::follows($user->profile_id, $collection['pid']), 404);
}
}
}
return [
'id' => (string) $collection->id,
'visibility' => $collection->visibility,
'title' => $collection->title,
'description' => $collection->description,
'thumb' => $collection->posts()->first()->thumb(),
'url' => $collection->url(),
'post_count' => $collection->posts()->count(),
'published_at' => $collection->published_at
];
return $collection;
}
public function getItems(Request $request, int $id)
{
$collection = Collection::findOrFail($id);
if($collection->visibility !== 'public') {
abort_if(!Auth::check() || Auth::user()->profile_id != $collection->profile_id, 404);
}
$user = $request->user();
$collection = CollectionService::getCollection($id);
if($collection['published_at'] == null || $collection['visibility'] != 'public') {
abort_unless($user, 404);
if($user->profile_id != $collection['pid']) {
if(!$user->is_admin) {
abort_if($collection['visibility'] != 'private', 404);
abort_if(!FollowerService::follows($user->profile_id, $collection['pid']), 404);
}
}
}
$page = $request->input('page') ?? 1;
$start = $page == 1 ? 0 : ($page * 10 - 10);
$end = $start + 10;
$items = CollectionService::getItems($id, $start, $end);
$res = CollectionItem::whereCollectionId($id)
->pluck('object_id')
return collect($items)
->map(function($id) {
return StatusService::get($id);
})
->filter(function($post) {
return $post && isset($post['account']);
->filter(function($item) {
return $item && isset($item['account'], $item['media_attachments']);
})
->values();
return response()->json($res);
}
public function getUserCollections(Request $request, int $id)
{
$user = $request->user();
$pid = $user ? $user->profile_id : null;
$follows = false;
$visibility = ['public'];
$profile = Profile::whereNull('status')
->whereNull('domain')
->findOrFail($id);
if($profile->is_private) {
abort_if(!$pid, 404);
abort_if(!$profile->id != $pid, 404);
if($pid) {
$follows = FollowerService::follows($pid, $profile->id);
}
$visibility = $pid == $profile->id ? ['public', 'private'] : ['public'];
if($profile->is_private) {
abort_if(!$pid, 404);
if(!$user->is_admin) {
abort_if($profile->id != $pid && $follows == false, 404);
}
}
$owner = $pid ? $pid == $profile->id : false;
if($follows) {
$visibility = ['public', 'private'];
}
if($pid && $pid == $profile->id) {
$visibility = ['public', 'private', 'draft'];
}
return Collection::whereProfileId($profile->id)
->whereIn('visibility', $visibility)
->orderByDesc('id')
->paginate(9)
->map(function($collection) {
return [
'id' => (string) $collection->id,
'visibility' => $collection->visibility,
'title' => $collection->title,
'description' => $collection->description,
'thumb' => $collection->posts()->first()->thumb(),
'url' => $collection->url(),
'post_count' => $collection->posts()->count(),
'published_at' => $collection->published_at
];
return CollectionService::getCollection($collection->id);
});
}
@ -240,6 +266,11 @@ class CollectionController extends Controller
->whereIn('type', ['photo', 'photo:album', 'video'])
->findOrFail($postId);
CollectionService::removeItem(
$collection->id,
$status->id
);
$item = CollectionItem::whereCollectionId($collection->id)
->whereObjectType('App\Status')
->whereObjectId($status->id)

View file

@ -126,4 +126,13 @@ class SpaController extends Controller
}
return redirect('/i/web/profile/' . $id);
}
public function hashtagRedirect(Request $request, $tag)
{
if(!$request->user()) {
return redirect('/discover/tags/' . $tag);
}
return view('layouts.spa');
}
}

View file

@ -0,0 +1,141 @@
<?php
namespace App\Services;
use App\Collection;
use App\CollectionItem;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
class CollectionService
{
const CACHE_KEY = 'pf:services:collections:';
public static function getItems($id, $start = 0, $stop = 10)
{
if(self::count($id)) {
return Redis::zrangebyscore(self::CACHE_KEY . 'items:' . $id, $start, $stop);
}
return self::coldBootItems($id);
}
public static function addItem($id, $sid, $score)
{
Redis::zadd(self::CACHE_KEY . 'items:' . $id, $score, $sid);
}
public static function removeItem($id, $sid)
{
Redis::zrem(self::CACHE_KEY . 'items:' . $id, $sid);
}
public static function clearItems($id)
{
Redis::del(self::CACHE_KEY . 'items:' . $id);
}
public static function coldBootItems($id)
{
return Cache::remember(self::CACHE_KEY . 'items:' . $id, 86400, function() use($id) {
return CollectionItem::whereCollectionId($id)
->orderBy('order')
->get()
->each(function($item) use ($id) {
self::addItem($id, $item->object_id, $item->order);
})
->map(function($item) {
return (string) $item->object_id;
})
->values()
->toArray();
});
}
public static function count($id)
{
$count = Redis::zcard(self::CACHE_KEY . 'items:' . $id);
if(!$count) {
self::coldBootItems($id);
$count = Redis::zcard(self::CACHE_KEY . 'items:' . $id);
}
return $count;
}
public static function getCollection($id)
{
$collection = Cache::remember(self::CACHE_KEY . 'get:' . $id, 86400, function() use($id) {
$collection = Collection::find($id);
if(!$collection) {
return false;
}
$account = AccountService::get($collection->profile_id);
if(!$account) {
return false;
}
return [
'id' => (string) $collection->id,
'pid' => (string) $collection->profile_id,
'username' => $account['username'],
'visibility' => $collection->visibility,
'title' => $collection->title,
'description' => $collection->description,
'thumb' => self::getThumb($id),
'url' => $collection->url(),
'published_at' => $collection->published_at
];
});
if($collection) {
$collection['post_count'] = self::count($id);
}
return $collection;
}
public static function setCollection($id, $collection)
{
$account = AccountService::get($collection->profile_id);
if(!$account) {
return false;
}
$res = [
'id' => (string) $collection->id,
'pid' => (string) $collection->profile_id,
'username' => $account['username'],
'visibility' => $collection->visibility,
'title' => $collection->title,
'description' => $collection->description,
'thumb' => self::getThumb($id),
'url' => $collection->url(),
'published_at' => $collection->published_at
];
Cache::put(self::CACHE_KEY . 'get:' . $id, $res, 86400);
$res['post_count'] = self::count($id);
return $res;
}
public static function deleteCollection($id)
{
Redis::del(self::CACHE_KEY . 'items:' . $id);
Cache::forget(self::CACHE_KEY . 'get:' . $id);
}
public static function getThumb($id)
{
$item = self::getItems($id, 0, 1);
if(!$item || empty($item)) {
return '/storage/no-preview.png';
}
$status = StatusService::get($item[0]);
if(!$status) {
return '/storage/no-preview.png';
}
if(!isset($status['media_attachments']) || empty($status['media_attachments'])) {
return '/storage/no-preview.png';
}
return $status['media_attachments'][0]['url'];
}
}

View file

@ -71,11 +71,25 @@ class Helpers {
$mimeTypes = explode(',', config_cache('pixelfed.media_types'));
$mediaTypes = in_array('video/mp4', $mimeTypes) ? ['Document', 'Image', 'Video'] : ['Document', 'Image'];
// Peertube
// $mediaTypes = in_array('video/mp4', $mimeTypes) ? ['Document', 'Image', 'Video', 'Link'] : ['Document', 'Image'];
if(!isset($activity['attachment']) || empty($activity['attachment'])) {
return false;
}
// peertube
// $attachment = is_array($activity['url']) ?
// collect($activity['url'])
// ->filter(function($media) {
// return $media['type'] == 'Link' && $media['mediaType'] == 'video/mp4';
// })
// ->take(1)
// ->values()
// ->toArray()[0] : $activity['attachment'];
$attachment = $activity['attachment'];
$valid = Validator::make($attachment, [
'*.type' => [
'required',
@ -88,7 +102,7 @@ class Helpers {
'string',
Rule::in($mimeTypes)
],
'*.name' => 'nullable|string|max:255'
'*.name' => 'sometimes|nullable|string|max:255'
])->passes();
return $valid;
@ -247,6 +261,19 @@ class Helpers {
return self::fetchFromUrl($url);
}
public static function pluckval($val)
{
if(is_string($val)) {
return $val;
}
if(is_array($val)) {
return !empty($val) ? $val[0] : null;
}
return null;
}
public static function statusFirstOrFetch($url, $replyTo = false)
{
$url = self::validateUrl($url);
@ -330,7 +357,7 @@ class Helpers {
}
}
$id = isset($res['id']) ? $res['id'] : $url;
$id = isset($res['id']) ? self::pluckval($res['id']) : self::pluckval($url);
$idDomain = parse_url($id, PHP_URL_HOST);
$urlDomain = parse_url($url, PHP_URL_HOST);
@ -338,9 +365,20 @@ class Helpers {
return;
}
if(isset($activity['object']['attributedTo'])) {
$actorDomain = parse_url($activity['object']['attributedTo'], PHP_URL_HOST);
if(!self::validateUrl($activity['object']['attributedTo']) ||
$attributedTo = is_string($activity['object']['attributedTo']) ?
$activity['object']['attributedTo'] :
(is_array($activity['object']['attributedTo']) ?
collect($activity['object']['attributedTo'])
->filter(function($o) {
return $o && isset($o['type']) && $o['type'] == 'Person';
})
->pluck('id')
->first() : null
);
if($attributedTo) {
$actorDomain = parse_url($attributedTo, PHP_URL_HOST);
if(!self::validateUrl($attributedTo) ||
$idDomain !== $actorDomain ||
$actorDomain !== $urlDomain
)
@ -353,14 +391,14 @@ class Helpers {
return;
}
$profile = self::profileFirstOrNew($activity['object']['attributedTo']);
$profile = self::profileFirstOrNew($attributedTo);
if(isset($activity['object']['inReplyTo']) && !empty($activity['object']['inReplyTo']) || $replyTo == true) {
$reply_to = self::statusFirstOrFetch($activity['object']['inReplyTo'], false);
$reply_to = self::statusFirstOrFetch(self::pluckval($activity['object']['inReplyTo']), false);
$reply_to = optional($reply_to)->id;
} else {
$reply_to = null;
}
$ts = is_array($res['published']) ? $res['published'][0] : $res['published'];
$ts = self::pluckval($res['published']);
if($scope == 'public' && in_array($urlDomain, InstanceService::getUnlistedDomains())) {
$scope = 'unlisted';
@ -399,8 +437,8 @@ class Helpers {
return DB::transaction(function() use($profile, $res, $url, $ts, $reply_to, $cw, $scope, $id) {
$status = new Status;
$status->profile_id = $profile->id;
$status->url = isset($res['url']) ? $res['url'] : $url;
$status->uri = isset($res['url']) ? $res['url'] : $url;
$status->url = isset($res['url']) && is_string($res['url']) ? $res['url'] : $url;
$status->uri = isset($res['url']) && is_string($res['url']) ? $res['url'] : $url;
$status->object_url = $id;
$status->caption = strip_tags($res['content']);
$status->rendered = Purify::clean($res['content']);
@ -486,10 +524,16 @@ class Helpers {
public static function importNoteAttachment($data, Status $status)
{
if(self::verifyAttachments($data) == false) {
// \Log::info('importNoteAttachment::failedVerification.', [$data['id']]);
$status->viewType();
return;
}
$attachments = isset($data['object']) ? $data['object']['attachment'] : $data['attachment'];
// peertube
// if(!$attachments) {
// $obj = isset($data['object']) ? $data['object'] : $data;
// $attachments = is_array($obj['url']) ? $obj['url'] : null;
// }
$user = $status->profile;
$storagePath = MediaPathService::get($user, 2);
$allowed = explode(',', config_cache('pixelfed.media_types'));
@ -585,7 +629,7 @@ class Helpers {
$profile->bio = isset($res['summary']) ? Purify::clean($res['summary']) : null;
$profile->sharedInbox = isset($res['endpoints']) && isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : null;
$profile->inbox_url = $res['inbox'];
$profile->outbox_url = $res['outbox'];
$profile->outbox_url = isset($res['outbox']) ? $res['outbox'] : null;
$profile->remote_url = $res['id'];
$profile->public_key = $res['publicKey']['publicKeyPem'];
$profile->key_id = $res['publicKey']['id'];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -12,8 +12,8 @@
"/js/developers.js": "/js/developers.js?id=5789400f559a5329ce547f5eed0de929",
"/js/loops.js": "/js/loops.js?id=decb6597d9ac0c4b91e4f4f9cd2114cd",
"/js/hashtag.js": "/js/hashtag.js?id=7889520ab31a351a41410f52194ab905",
"/js/collectioncompose.js": "/js/collectioncompose.js?id=69e0a11d0aad0f0cf18ebc319c242db0",
"/js/collections.js": "/js/collections.js?id=a43e5c78ade1aa6111663d08df67256e",
"/js/collectioncompose.js": "/js/collectioncompose.js?id=d7acc36deab8f6a1f67361f05f88e2d8",
"/js/collections.js": "/js/collections.js?id=74bc440f4fee5063a5fe830cf8fa1c09",
"/js/profile-directory.js": "/js/profile-directory.js?id=04ec970031e6bf15de5ade019147d53e",
"/js/story-compose.js": "/js/story-compose.js?id=b9757f1d5a146f0d5c06c62b83c983fb",
"/js/direct.js": "/js/direct.js?id=9366e39e3bab1f1139eac9bad1037c42",
@ -26,7 +26,7 @@
"/js/home-chunk-uopy3z.js": "/js/home-chunk-uopy3z.js?id=2b40c3a64ecbc71b7ae7ee3b6d12d181",
"/js/compose-chunk-uopy3z.js": "/js/compose-chunk-uopy3z.js?id=eeb20fcf99043412b638812ba6246cf1",
"/js/post-chunk-uopy3z.js": "/js/post-chunk-uopy3z.js?id=577f96a6689f6d061a4dde249dc906f9",
"/js/profile-chunk-uopy3z.js": "/js/profile-chunk-uopy3z.js?id=d4b7aa21ab61b40d654e64a96539de4e",
"/js/profile-chunk-uopy3z.js": "/js/profile-chunk-uopy3z.js?id=81c4a251b880455113c3f5652f26b59c",
"/js/dmym-chunk-uopy3z.js": "/js/dmym-chunk-uopy3z.js?id=1226d7b5818bea35f1b01b275594111b",
"/js/dmyh-chunk-uopy3z.js": "/js/dmyh-chunk-uopy3z.js?id=649ee66f6ae1ff3380e7a2e72e14616d",
"/js/daci-chunk-uopy3z.js": "/js/daci-chunk-uopy3z.js?id=64147223e82af20862191bb234a873ef",

View file

@ -4,12 +4,12 @@
<div class="container">
<collection-component
collection-id="{{$collection->id}}"
collection-title="{{$collection->title}}"
collection-description="{{$collection->description}}"
collection-visibility="{{$collection->visibility}}"
profile-id="{{$collection->profile_id}}"
profile-username="{{$collection->profile->username}}"
collection-id="{{$collection['id']}}"
collection-title="{{$collection['title']}}"
collection-description="{{$collection['description']}}"
collection-visibility="{{$collection['visibility']}}"
profile-id="{{$collection['pid']}}"
profile-username="{{$collection['username']}}"
></collection-component>
</div>
@ -30,4 +30,4 @@
<script type="text/javascript" src="{{mix('js/compose.js')}}" async></script>
<script type="text/javascript" src="{{mix('js/collections.js')}}"></script>
<script type="text/javascript">App.boot()</script>
@endpush
@endpush

View file

@ -237,7 +237,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::get('collection/items/{id}', 'CollectionController@getItems');
Route::post('collection/item', 'CollectionController@storeId');
Route::delete('collection/item', 'CollectionController@deleteId');
Route::get('collection/{id}', 'CollectionController@get');
Route::get('collection/{id}', 'CollectionController@getCollection');
Route::post('collection/{id}', 'CollectionController@store');
Route::delete('collection/{id}', 'CollectionController@delete');
Route::post('collection/{id}/publish', 'CollectionController@publish');
@ -351,6 +351,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::post('warning', 'AccountInterstitialController@read');
Route::get('my2020', 'SeasonalController@yearInReview');
Route::get('web/hashtag/{tag}', 'SpaController@hashtagRedirect');
Route::get('web/username/{id}', 'SpaController@usernameRedirect');
Route::get('web/post/{id}', 'SpaController@webPost');
Route::get('web/profile/{id}', 'SpaController@webProfile');