Merge pull request #3295 from pixelfed/staging

Staging
pull/3308/head
daniel 3 years ago committed by GitHub
commit 7ab581f700
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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)

@ -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);
}

@ -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);

@ -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);
}
}
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
];
$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 $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);
}
$res = CollectionItem::whereCollectionId($id)
->pluck('object_id')
$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);
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($pid) {
$follows = FollowerService::follows($pid, $profile->id);
}
if($profile->is_private) {
abort_if(!$pid, 404);
abort_if(!$profile->id != $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'];
}
$visibility = $pid == $profile->id ? ['public', 'private'] : ['public'];
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)

@ -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');
}
}

@ -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'];
}
}

@ -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

@ -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",

@ -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>

@ -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');

Loading…
Cancel
Save