diff --git a/app/Http/Controllers/CollectionController.php b/app/Http/Controllers/CollectionController.php
index 6cd4bda57..85f3f30cf 100644
--- a/app/Http/Controllers/CollectionController.php
+++ b/app/Http/Controllers/CollectionController.php
@@ -153,7 +153,7 @@ class CollectionController extends Controller
abort(400, 'You can only add '.$max.' posts per collection');
}
- $status = Status::whereScope('public')
+ $status = Status::whereIn('scope', ['public', 'unlisted'])
->whereProfileId($profileId)
->whereIn('type', ['photo', 'photo:album', 'video'])
->findOrFail($postId);
@@ -166,17 +166,13 @@ class CollectionController extends Controller
'order' => $count,
]);
- CollectionService::addItem(
- $collection->id,
- $status->id,
- $count
- );
+ CollectionService::deleteCollection($collection->id);
$collection->updated_at = now();
$collection->save();
CollectionService::setCollection($collection->id, $collection);
- return StatusService::get($status->id);
+ return StatusService::get($status->id, false);
}
public function getCollection(Request $request, $id)
@@ -226,10 +222,10 @@ class CollectionController extends Controller
return collect($items)
->map(function($id) {
- return StatusService::get($id);
+ return StatusService::get($id, false);
})
->filter(function($item) {
- return $item && isset($item['account'], $item['media_attachments']);
+ return $item && ($item['visibility'] == 'public' || $item['visibility'] == 'unlisted') && isset($item['account'], $item['media_attachments']);
})
->values();
}
@@ -298,7 +294,7 @@ class CollectionController extends Controller
abort(400, 'You cannot delete the only post of a collection!');
}
- $status = Status::whereScope('public')
+ $status = Status::whereIn('scope', ['public', 'unlisted'])
->whereIn('type', ['photo', 'photo:album', 'video'])
->findOrFail($postId);
diff --git a/app/Http/Controllers/FederationController.php b/app/Http/Controllers/FederationController.php
index c4b5e86bf..0cf33d43e 100644
--- a/app/Http/Controllers/FederationController.php
+++ b/app/Http/Controllers/FederationController.php
@@ -3,17 +3,17 @@
namespace App\Http\Controllers;
use App\Jobs\InboxPipeline\{
- DeleteWorker,
- InboxWorker,
- InboxValidator
+ DeleteWorker,
+ InboxWorker,
+ InboxValidator
};
use App\Jobs\RemoteFollowPipeline\RemoteFollowPipeline;
use App\{
- AccountLog,
- Like,
- Profile,
- Status,
- User
+ AccountLog,
+ Like,
+ Profile,
+ Status,
+ User
};
use App\Util\Lexer\Nickname;
use App\Util\Webfinger\Webfinger;
@@ -24,243 +24,248 @@ use Illuminate\Http\Request;
use League\Fractal;
use App\Util\Site\Nodeinfo;
use App\Util\ActivityPub\{
- Helpers,
- HttpSignature,
- Outbox
+ Helpers,
+ HttpSignature,
+ Outbox
};
use Zttp\Zttp;
use App\Services\InstanceService;
+use App\Services\AccountService;
class FederationController extends Controller
{
- public function nodeinfoWellKnown()
- {
- abort_if(!config('federation.nodeinfo.enabled'), 404);
- return response()->json(Nodeinfo::wellKnown(), 200, [], JSON_UNESCAPED_SLASHES)
- ->header('Access-Control-Allow-Origin','*');
- }
+ public function nodeinfoWellKnown()
+ {
+ abort_if(!config('federation.nodeinfo.enabled'), 404);
+ return response()->json(Nodeinfo::wellKnown(), 200, [], JSON_UNESCAPED_SLASHES)
+ ->header('Access-Control-Allow-Origin','*');
+ }
- public function nodeinfo()
- {
- abort_if(!config('federation.nodeinfo.enabled'), 404);
- return response()->json(Nodeinfo::get(), 200, [], JSON_UNESCAPED_SLASHES)
- ->header('Access-Control-Allow-Origin','*');
- }
+ public function nodeinfo()
+ {
+ abort_if(!config('federation.nodeinfo.enabled'), 404);
+ return response()->json(Nodeinfo::get(), 200, [], JSON_UNESCAPED_SLASHES)
+ ->header('Access-Control-Allow-Origin','*');
+ }
- public function webfinger(Request $request)
- {
- if (!config('federation.webfinger.enabled') ||
- !$request->has('resource') ||
- !$request->filled('resource')
- ) {
- return response('', 400);
- }
+ public function webfinger(Request $request)
+ {
+ if (!config('federation.webfinger.enabled') ||
+ !$request->has('resource') ||
+ !$request->filled('resource')
+ ) {
+ return response('', 400);
+ }
- $resource = $request->input('resource');
- $domain = config('pixelfed.domain.app');
+ $resource = $request->input('resource');
+ $domain = config('pixelfed.domain.app');
- if(config('federation.activitypub.sharedInbox') &&
- $resource == 'acct:' . $domain . '@' . $domain) {
- $res = [
- 'subject' => 'acct:' . $domain . '@' . $domain,
- 'aliases' => [
- 'https://' . $domain . '/i/actor'
- ],
- 'links' => [
- [
- 'rel' => 'http://webfinger.net/rel/profile-page',
- 'type' => 'text/html',
- 'href' => 'https://' . $domain . '/site/kb/instance-actor'
- ],
- [
- 'rel' => 'self',
- 'type' => 'application/activity+json',
- 'href' => 'https://' . $domain . '/i/actor'
- ]
- ]
- ];
- return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
- }
- $hash = hash('sha256', $resource);
- $key = 'federation:webfinger:sha256:' . $hash;
- if($cached = Cache::get($key)) {
- return response()->json($cached, 200, [], JSON_UNESCAPED_SLASHES);
- }
- if(strpos($resource, $domain) == false) {
- return response('', 400);
- }
- $parsed = Nickname::normalizeProfileUrl($resource);
- if(empty($parsed) || $parsed['domain'] !== $domain) {
- return response('', 400);
- }
- $username = $parsed['username'];
- $profile = Profile::whereNull('domain')->whereUsername($username)->first();
- if(!$profile || $profile->status !== null) {
- return response('', 400);
- }
- $webfinger = (new Webfinger($profile))->generate();
- Cache::put($key, $webfinger, 1209600);
+ if(config('federation.activitypub.sharedInbox') &&
+ $resource == 'acct:' . $domain . '@' . $domain) {
+ $res = [
+ 'subject' => 'acct:' . $domain . '@' . $domain,
+ 'aliases' => [
+ 'https://' . $domain . '/i/actor'
+ ],
+ 'links' => [
+ [
+ 'rel' => 'http://webfinger.net/rel/profile-page',
+ 'type' => 'text/html',
+ 'href' => 'https://' . $domain . '/site/kb/instance-actor'
+ ],
+ [
+ 'rel' => 'self',
+ 'type' => 'application/activity+json',
+ 'href' => 'https://' . $domain . '/i/actor'
+ ]
+ ]
+ ];
+ return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
+ }
+ $hash = hash('sha256', $resource);
+ $key = 'federation:webfinger:sha256:' . $hash;
+ if($cached = Cache::get($key)) {
+ return response()->json($cached, 200, [], JSON_UNESCAPED_SLASHES);
+ }
+ if(strpos($resource, $domain) == false) {
+ return response('', 400);
+ }
+ $parsed = Nickname::normalizeProfileUrl($resource);
+ if(empty($parsed) || $parsed['domain'] !== $domain) {
+ return response('', 400);
+ }
+ $username = $parsed['username'];
+ $profile = Profile::whereNull('domain')->whereUsername($username)->first();
+ if(!$profile || $profile->status !== null) {
+ return response('', 400);
+ }
+ $webfinger = (new Webfinger($profile))->generate();
+ Cache::put($key, $webfinger, 1209600);
- return response()->json($webfinger, 200, [], JSON_UNESCAPED_SLASHES)
- ->header('Access-Control-Allow-Origin','*');
- }
+ return response()->json($webfinger, 200, [], JSON_UNESCAPED_SLASHES)
+ ->header('Access-Control-Allow-Origin','*');
+ }
- public function hostMeta(Request $request)
- {
- abort_if(!config('federation.webfinger.enabled'), 404);
+ public function hostMeta(Request $request)
+ {
+ abort_if(!config('federation.webfinger.enabled'), 404);
- $path = route('well-known.webfinger');
- $xml = '';
+ $path = route('well-known.webfinger');
+ $xml = '';
- return response($xml)->header('Content-Type', 'application/xrd+xml');
- }
+ return response($xml)->header('Content-Type', 'application/xrd+xml');
+ }
- public function userOutbox(Request $request, $username)
- {
- abort_if(!config_cache('federation.activitypub.enabled'), 404);
+ public function userOutbox(Request $request, $username)
+ {
+ abort_if(!config_cache('federation.activitypub.enabled'), 404);
- if(!$request->wantsJson()) {
- return redirect('/' . $username);
- }
+ if(!$request->wantsJson()) {
+ return redirect('/' . $username);
+ }
- $res = [
- '@context' => 'https://www.w3.org/ns/activitystreams',
- 'id' => 'https://' . config('pixelfed.domain.app') . '/users/' . $username . '/outbox',
- 'type' => 'OrderedCollection',
- 'totalItems' => 0,
- 'orderedItems' => []
- ];
+ $res = [
+ '@context' => 'https://www.w3.org/ns/activitystreams',
+ 'id' => 'https://' . config('pixelfed.domain.app') . '/users/' . $username . '/outbox',
+ 'type' => 'OrderedCollection',
+ 'totalItems' => 0,
+ 'orderedItems' => []
+ ];
- return response(json_encode($res, JSON_UNESCAPED_SLASHES))->header('Content-Type', 'application/activity+json');
- }
+ return response(json_encode($res, JSON_UNESCAPED_SLASHES))->header('Content-Type', 'application/activity+json');
+ }
- public function userInbox(Request $request, $username)
- {
- abort_if(!config_cache('federation.activitypub.enabled'), 404);
- abort_if(!config('federation.activitypub.inbox'), 404);
+ public function userInbox(Request $request, $username)
+ {
+ abort_if(!config_cache('federation.activitypub.enabled'), 404);
+ abort_if(!config('federation.activitypub.inbox'), 404);
- $headers = $request->headers->all();
- $payload = $request->getContent();
- if(!$payload || empty($payload)) {
- return;
- }
- $obj = json_decode($payload, true, 8);
- if(!isset($obj['id'])) {
- return;
- }
- $domain = parse_url($obj['id'], PHP_URL_HOST);
- if(in_array($domain, InstanceService::getBannedDomains())) {
- return;
- }
+ $headers = $request->headers->all();
+ $payload = $request->getContent();
+ if(!$payload || empty($payload)) {
+ return;
+ }
+ $obj = json_decode($payload, true, 8);
+ if(!isset($obj['id'])) {
+ return;
+ }
+ $domain = parse_url($obj['id'], PHP_URL_HOST);
+ if(in_array($domain, InstanceService::getBannedDomains())) {
+ return;
+ }
- if(isset($obj['type']) && $obj['type'] === 'Delete') {
- if(isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) {
- if($obj['object']['type'] === 'Person') {
- if(Profile::whereRemoteUrl($obj['object']['id'])->exists()) {
- dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox');
- return;
- }
- }
+ if(isset($obj['type']) && $obj['type'] === 'Delete') {
+ if(isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) {
+ if($obj['object']['type'] === 'Person') {
+ if(Profile::whereRemoteUrl($obj['object']['id'])->exists()) {
+ dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox');
+ return;
+ }
+ }
- if($obj['object']['type'] === 'Tombstone') {
- if(Status::whereObjectUrl($obj['object']['id'])->exists()) {
- dispatch(new DeleteWorker($headers, $payload))->onQueue('delete');
- return;
- }
- }
+ if($obj['object']['type'] === 'Tombstone') {
+ if(Status::whereObjectUrl($obj['object']['id'])->exists()) {
+ dispatch(new DeleteWorker($headers, $payload))->onQueue('delete');
+ return;
+ }
+ }
- if($obj['object']['type'] === 'Story') {
- dispatch(new DeleteWorker($headers, $payload))->onQueue('story');
- return;
- }
- }
- return;
- } else if( isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) {
- dispatch(new InboxValidator($username, $headers, $payload))->onQueue('follow');
- } else {
- dispatch(new InboxValidator($username, $headers, $payload))->onQueue('high');
- }
- return;
- }
+ if($obj['object']['type'] === 'Story') {
+ dispatch(new DeleteWorker($headers, $payload))->onQueue('story');
+ return;
+ }
+ }
+ return;
+ } else if( isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) {
+ dispatch(new InboxValidator($username, $headers, $payload))->onQueue('follow');
+ } else {
+ dispatch(new InboxValidator($username, $headers, $payload))->onQueue('high');
+ }
+ return;
+ }
- public function sharedInbox(Request $request)
- {
- abort_if(!config_cache('federation.activitypub.enabled'), 404);
- abort_if(!config('federation.activitypub.sharedInbox'), 404);
+ public function sharedInbox(Request $request)
+ {
+ abort_if(!config_cache('federation.activitypub.enabled'), 404);
+ abort_if(!config('federation.activitypub.sharedInbox'), 404);
- $headers = $request->headers->all();
- $payload = $request->getContent();
+ $headers = $request->headers->all();
+ $payload = $request->getContent();
- if(!$payload || empty($payload)) {
- return;
- }
+ if(!$payload || empty($payload)) {
+ return;
+ }
- $obj = json_decode($payload, true, 8);
- if(!isset($obj['id'])) {
- return;
- }
+ $obj = json_decode($payload, true, 8);
+ if(!isset($obj['id'])) {
+ return;
+ }
- $domain = parse_url($obj['id'], PHP_URL_HOST);
- if(in_array($domain, InstanceService::getBannedDomains())) {
- return;
- }
+ $domain = parse_url($obj['id'], PHP_URL_HOST);
+ if(in_array($domain, InstanceService::getBannedDomains())) {
+ return;
+ }
- if(isset($obj['type']) && $obj['type'] === 'Delete') {
- if(isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) {
- if($obj['object']['type'] === 'Person') {
- if(Profile::whereRemoteUrl($obj['object']['id'])->exists()) {
- dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox');
- return;
- }
- }
+ if(isset($obj['type']) && $obj['type'] === 'Delete') {
+ if(isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) {
+ if($obj['object']['type'] === 'Person') {
+ if(Profile::whereRemoteUrl($obj['object']['id'])->exists()) {
+ dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox');
+ return;
+ }
+ }
- if($obj['object']['type'] === 'Tombstone') {
- if(Status::whereObjectUrl($obj['object']['id'])->exists()) {
- dispatch(new DeleteWorker($headers, $payload))->onQueue('delete');
- return;
- }
- }
+ if($obj['object']['type'] === 'Tombstone') {
+ if(Status::whereObjectUrl($obj['object']['id'])->exists()) {
+ dispatch(new DeleteWorker($headers, $payload))->onQueue('delete');
+ return;
+ }
+ }
- if($obj['object']['type'] === 'Story') {
- dispatch(new DeleteWorker($headers, $payload))->onQueue('story');
- return;
- }
- }
- return;
- } else if( isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) {
- dispatch(new InboxWorker($headers, $payload))->onQueue('follow');
- } else {
- dispatch(new InboxWorker($headers, $payload))->onQueue('shared');
- }
- return;
- }
+ if($obj['object']['type'] === 'Story') {
+ dispatch(new DeleteWorker($headers, $payload))->onQueue('story');
+ return;
+ }
+ }
+ return;
+ } else if( isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) {
+ dispatch(new InboxWorker($headers, $payload))->onQueue('follow');
+ } else {
+ dispatch(new InboxWorker($headers, $payload))->onQueue('shared');
+ }
+ return;
+ }
- public function userFollowing(Request $request, $username)
- {
- abort_if(!config_cache('federation.activitypub.enabled'), 404);
+ public function userFollowing(Request $request, $username)
+ {
+ abort_if(!config_cache('federation.activitypub.enabled'), 404);
- $obj = [
- '@context' => 'https://www.w3.org/ns/activitystreams',
- 'id' => $request->getUri(),
- 'type' => 'OrderedCollectionPage',
- 'totalItems' => 0,
- 'orderedItems' => []
- ];
- return response()->json($obj);
- }
+ $id = AccountService::usernameToId($username);
+ abort_if(!$id, 404);
+ $account = AccountService::get($id);
+ abort_if(!$account || !isset($account['following_count']), 404);
+ $obj = [
+ '@context' => 'https://www.w3.org/ns/activitystreams',
+ 'id' => $request->getUri(),
+ 'type' => 'OrderedCollection',
+ 'totalItems' => $account['following_count'] ?? 0,
+ ];
+ return response()->json($obj);
+ }
- public function userFollowers(Request $request, $username)
- {
- abort_if(!config_cache('federation.activitypub.enabled'), 404);
-
- $obj = [
- '@context' => 'https://www.w3.org/ns/activitystreams',
- 'id' => $request->getUri(),
- 'type' => 'OrderedCollectionPage',
- 'totalItems' => 0,
- 'orderedItems' => []
- ];
-
- return response()->json($obj);
- }
+ public function userFollowers(Request $request, $username)
+ {
+ abort_if(!config_cache('federation.activitypub.enabled'), 404);
+ $id = AccountService::usernameToId($username);
+ abort_if(!$id, 404);
+ $account = AccountService::get($id);
+ abort_if(!$account || !isset($account['followers_count']), 404);
+ $obj = [
+ '@context' => 'https://www.w3.org/ns/activitystreams',
+ 'id' => $request->getUri(),
+ 'type' => 'OrderedCollection',
+ 'totalItems' => $account['followers_count'] ?? 0,
+ ];
+ return response()->json($obj);
+ }
}
diff --git a/contrib/docker/Dockerfile.apache b/contrib/docker/Dockerfile.apache
index 9c33aee17..a400f8797 100644
--- a/contrib/docker/Dockerfile.apache
+++ b/contrib/docker/Dockerfile.apache
@@ -33,7 +33,7 @@ RUN apt-get update \
# Required for GD
libxpm4 \
libxpm-dev \
- libwebp6 \
+ libwebp7 \
libwebp-dev \
## Video Processing
ffmpeg \
diff --git a/contrib/docker/Dockerfile.fpm b/contrib/docker/Dockerfile.fpm
index 0b8e5c113..1bb0a15f7 100644
--- a/contrib/docker/Dockerfile.fpm
+++ b/contrib/docker/Dockerfile.fpm
@@ -33,7 +33,7 @@ RUN apt-get update \
# Required for GD
libxpm4 \
libxpm-dev \
- libwebp6 \
+ libwebp7 \
libwebp-dev \
## Video Processing
ffmpeg \
diff --git a/resources/assets/js/components/CollectionComponent.vue b/resources/assets/js/components/CollectionComponent.vue
index 3f77cfc13..ea5e21525 100644
--- a/resources/assets/js/components/CollectionComponent.vue
+++ b/resources/assets/js/components/CollectionComponent.vue
@@ -460,7 +460,7 @@ export default {
})
.then(res => {
self.postsList = res.data.filter(l => {
- return self.ids.indexOf(l.id) == -1;
+ return (l.visibility == 'public' || l.visibility == 'unlisted') && l.sensitive == false && self.ids.indexOf(l.id) == -1;
});
self.loadingPostList = false;
self.$refs.addPhotoModal.show();
@@ -619,6 +619,9 @@ export default {
this.posts = this.posts.filter(post => {
return post.id != id;
});
+ this.ids = this.ids.filter(post_id => {
+ return post_id != id;
+ });
},
addRecentId(post) {
@@ -630,6 +633,7 @@ export default {
// window.location.reload();
this.closeModals();
this.posts.push(res.data);
+ this.ids.push(post.id);
this.collection.post_count++;
}).catch(err => {
swal('Oops!', 'An error occured, please try selecting another post.', 'error');
diff --git a/resources/assets/js/components/CollectionCompose.vue b/resources/assets/js/components/CollectionCompose.vue
index 84444cf1d..2920e1f76 100644
--- a/resources/assets/js/components/CollectionCompose.vue
+++ b/resources/assets/js/components/CollectionCompose.vue
@@ -194,7 +194,6 @@ export default {
swal('Invalid URL', 'You can only add posts from this instance', 'error');
this.id = '';
}
-
if(url.includes('/i/web/post/') || url.includes('/p/')) {
let id = split[split.length - 1];
console.log('adding ' + id);
@@ -228,7 +227,7 @@ export default {
let ids = this.posts.map(s => {
return s.id;
});
- return s.visibility == 'public' && s.sensitive == false && ids.indexOf(s.id) == -1;
+ return (s.visibility == 'public' || s.visibility == 'unlisted') && s.sensitive == false && ids.indexOf(s.id) == -1;
});
});
},