diff --git a/.env.example b/.env.example
index 2a8604cc7..f5ed15a93 100644
--- a/.env.example
+++ b/.env.example
@@ -56,9 +56,9 @@ ACTIVITYPUB_SHAREDINBOX=false
# php artisan optimize:clear
# php artisan optimize
-PF_COSTAR_ENABLED=false
-CS_BLOCKED_DOMAINS='example.org,example.net,example.com'
-CS_CW_DOMAINS='example.org,example.net,example.com'
+PF_COSTAR_ENABLED=true
+CS_BLOCKED_DOMAINS='gab.com,gab.ai,develop.gab.com'
+CS_CW_DOMAINS='switter.at'
CS_UNLISTED_DOMAINS='example.org,example.net,example.com'
## Optional
diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php
index 13d5dc030..8ddc88751 100644
--- a/app/Http/Controllers/AccountController.php
+++ b/app/Http/Controllers/AccountController.php
@@ -191,7 +191,6 @@ class AccountController extends Controller
$pid = $user->id;
Cache::forget("user:filter:list:$pid");
- Cache::forget("feature:discover:people:$pid");
Cache::forget("feature:discover:posts:$pid");
Cache::forget("api:local:exp:rec:$pid");
@@ -242,7 +241,6 @@ class AccountController extends Controller
$pid = $user->id;
Cache::forget("user:filter:list:$pid");
- Cache::forget("feature:discover:people:$pid");
Cache::forget("feature:discover:posts:$pid");
Cache::forget("api:local:exp:rec:$pid");
@@ -296,7 +294,6 @@ class AccountController extends Controller
$pid = $user->id;
Cache::forget("user:filter:list:$pid");
- Cache::forget("feature:discover:people:$pid");
Cache::forget("feature:discover:posts:$pid");
Cache::forget("api:local:exp:rec:$pid");
@@ -348,7 +345,6 @@ class AccountController extends Controller
$pid = $user->id;
Cache::forget("user:filter:list:$pid");
- Cache::forget("feature:discover:people:$pid");
Cache::forget("feature:discover:posts:$pid");
Cache::forget("api:local:exp:rec:$pid");
diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php
index b3d8869ac..94d149410 100644
--- a/app/Http/Controllers/PageController.php
+++ b/app/Http/Controllers/PageController.php
@@ -18,6 +18,7 @@ class PageController extends Controller
'/site/about' => 'site:about',
'/site/privacy' => 'site:privacy',
'/site/terms' => 'site:terms',
+ '/site/kb/community-guidelines' => 'site:help:community-guidelines'
];
}
@@ -81,7 +82,7 @@ class PageController extends Controller
public function generatePage(Request $request)
{
$this->validate($request, [
- 'page' => 'required|string|in:about,terms,privacy',
+ 'page' => 'required|string|in:about,terms,privacy,community_guidelines',
]);
$page = $request->input('page');
@@ -98,6 +99,10 @@ class PageController extends Controller
case 'terms':
Page::firstOrCreate(['slug' => '/site/terms']);
break;
+
+ case 'community_guidelines':
+ Page::firstOrCreate(['slug' => '/site/kb/community-guidelines']);
+ break;
}
return redirect(route('admin.settings.pages'));
diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php
index d8d13d477..6c9d4b83c 100644
--- a/app/Http/Controllers/SearchController.php
+++ b/app/Http/Controllers/SearchController.php
@@ -56,7 +56,7 @@ class SearchController extends Controller
]
]];
} else if ($type == 'Note') {
- $item = Helpers::statusFirstOrFetch($tag, false);
+ $item = Helpers::statusFetch($tag);
$tokens['posts'] = [[
'count' => 0,
'url' => $item->url(),
diff --git a/app/Http/Controllers/Settings/PrivacySettings.php b/app/Http/Controllers/Settings/PrivacySettings.php
index d3283c921..99f7d4100 100644
--- a/app/Http/Controllers/Settings/PrivacySettings.php
+++ b/app/Http/Controllers/Settings/PrivacySettings.php
@@ -5,11 +5,13 @@ namespace App\Http\Controllers\Settings;
use App\AccountLog;
use App\EmailVerification;
use App\Instance;
+use App\Follower;
use App\Media;
use App\Profile;
use App\User;
use App\UserFilter;
use App\Util\Lexer\PrettyNumber;
+use App\Util\ActivityPub\Helpers;
use Auth, Cache, DB;
use Illuminate\Http\Request;
@@ -134,9 +136,13 @@ trait PrivacySettings
public function blockedInstanceStore(Request $request)
{
$this->validate($request, [
- 'domain' => 'required|active_url'
+ 'domain' => 'required|url|min:1|max:120'
]);
$domain = $request->input('domain');
+ if(Helpers::validateUrl($domain) == false) {
+ return abort(400, 'Invalid domain');
+ }
+ $domain = parse_url($domain, PHP_URL_HOST);
$instance = Instance::firstOrCreate(['domain' => $domain]);
$filter = new UserFilter;
$filter->user_id = Auth::user()->profile->id;
@@ -165,4 +171,47 @@ trait PrivacySettings
{
return view('settings.privacy.blocked-keywords');
}
+
+ public function privateAccountOptions(Request $request)
+ {
+ $this->validate($request, [
+ 'mode' => 'required|string|in:keep-all,mutual-only,only-followers,remove-all',
+ 'duration' => 'required|integer|min:60|max:525600',
+ ]);
+ $mode = $request->input('mode');
+ $duration = $request->input('duration');
+ // $newRequests = $request->input('newrequests');
+
+ $profile = Auth::user()->profile;
+ $settings = Auth::user()->settings;
+
+ if($mode !== 'keep-all') {
+ switch ($mode) {
+ case 'mutual-only':
+ $following = $profile->following()->pluck('profiles.id');
+ Follower::whereFollowingId($profile->id)->whereNotIn('profile_id', $following)->delete();
+ break;
+
+ case 'only-followers':
+ $ts = now()->subMinutes($duration);
+ Follower::whereFollowingId($profile->id)->where('created_at', '>', $ts)->delete();
+ break;
+
+ case 'remove-all':
+ Follower::whereFollowingId($profile->id)->delete();
+ break;
+
+ default:
+ # code...
+ break;
+ }
+ }
+ $profile->is_private = true;
+ $settings->show_guests = false;
+ $settings->show_discover = false;
+ $settings->save();
+ $profile->save();
+ Cache::forget('profiles:private');
+ return [200];
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/SiteController.php b/app/Http/Controllers/SiteController.php
index c14dc1ddb..58b092b83 100644
--- a/app/Http/Controllers/SiteController.php
+++ b/app/Http/Controllers/SiteController.php
@@ -42,7 +42,7 @@ class SiteController extends Controller
public function about()
{
- return Cache::remember('site:about', now()->addMinutes(120), function() {
+ return Cache::remember('site:about', now()->addHours(12), function() {
$page = Page::whereSlug('/site/about')->whereActive(true)->first();
$stats = [
'posts' => Status::whereLocal(true)->count(),
@@ -64,24 +64,25 @@ class SiteController extends Controller
public function communityGuidelines(Request $request)
{
- $slug = '/site/kb/community-guidelines';
- $page = Page::whereSlug($slug)->whereActive(true)->first();
- return view('site.help.community-guidelines', compact('page'));
+ return Cache::remember('site:help:community-guidelines', now()->addDays(120), function() {
+ $slug = '/site/kb/community-guidelines';
+ $page = Page::whereSlug($slug)->whereActive(true)->first();
+ return View::make('site.help.community-guidelines')->with(compact('page'))->render();
+ });
}
public function privacy(Request $request)
{
- return Cache::remember('site:privacy', now()->addMinutes(120), function() {
+ return Cache::remember('site:privacy', now()->addDays(120), function() {
$slug = '/site/privacy';
$page = Page::whereSlug($slug)->whereActive(true)->first();
return View::make('site.privacy')->with(compact('page'))->render();
});
}
-
public function terms(Request $request)
{
- return Cache::remember('site:terms', now()->addMinutes(120), function() {
+ return Cache::remember('site:terms', now()->addDays(120), function() {
$slug = '/site/terms';
$page = Page::whereSlug($slug)->whereActive(true)->first();
return View::make('site.terms')->with(compact('page'))->render();
diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php
index 65d6a2da5..c5a1c9975 100644
--- a/app/Http/Controllers/StatusController.php
+++ b/app/Http/Controllers/StatusController.php
@@ -30,11 +30,12 @@ class StatusController extends Controller
}
$status = Status::whereProfileId($user->id)
+ ->whereNull('reblog_of_id')
->whereNotIn('visibility',['draft','direct'])
->findOrFail($id);
- if($status->uri) {
- $url = $status->uri;
+ if($status->uri || $status->url) {
+ $url = $status->uri ?? $status->url;
if(ends_with($url, '/activity')) {
$url = str_replace('/activity', '', $url);
}
@@ -59,6 +60,11 @@ class StatusController extends Controller
return view($template, compact('user', 'status'));
}
+ public function showEmbed(Request $request, $username, int $id)
+ {
+ return;
+ }
+
public function showObject(Request $request, $username, int $id)
{
$user = Profile::whereNull('domain')->whereUsername($username)->firstOrFail();
@@ -102,109 +108,6 @@ class StatusController extends Controller
public function store(Request $request)
{
return;
-
- $this->authCheck();
- $user = Auth::user();
-
- $size = Media::whereUserId($user->id)->sum('size') / 1000;
- $limit = (int) config('pixelfed.max_account_size');
- if ($size >= $limit) {
- return redirect()->back()->with('error', 'You have exceeded your storage limit. Please click here for more info.');
- }
-
- $this->validate($request, [
- 'photo.*' => 'required|mimetypes:' . config('pixelfed.media_types').'|max:' . config('pixelfed.max_photo_size'),
- 'caption' => 'nullable|string|max:'.config('pixelfed.max_caption_length'),
- 'cw' => 'nullable|string',
- 'filter_class' => 'nullable|alpha_dash|max:30',
- 'filter_name' => 'nullable|string',
- 'visibility' => 'required|string|min:5|max:10',
- ]);
-
- if (count($request->file('photo')) > config('pixelfed.max_album_length')) {
- return redirect()->back()->with('error', 'Too many files, max limit per post: '.config('pixelfed.max_album_length'));
- }
- $cw = $request->filled('cw') && $request->cw == 'on' ? true : false;
- $monthHash = hash('sha1', date('Y').date('m'));
- $userHash = hash('sha1', $user->id.(string) $user->created_at);
- $profile = $user->profile;
- $visibility = $this->validateVisibility($request->visibility);
-
- $cw = $profile->cw == true ? true : $cw;
- $visibility = $profile->unlisted == true && $visibility == 'public' ? 'unlisted' : $visibility;
-
- if(config('costar.enabled') == true) {
- $blockedKeywords = config('costar.keyword.block');
- if($blockedKeywords !== null) {
- $keywords = config('costar.keyword.block');
- foreach($keywords as $kw) {
- if(Str::contains($request->caption, $kw) == true) {
- abort(400, 'Invalid object');
- }
- }
- }
- }
-
- $status = new Status();
- $status->profile_id = $profile->id;
- $status->caption = strip_tags($request->caption);
- $status->is_nsfw = $cw;
-
- // TODO: remove deprecated visibility in favor of scope
- $status->visibility = $visibility;
- $status->scope = $visibility;
-
- $status->save();
-
- $photos = $request->file('photo');
- $order = 1;
- $mimes = [];
- $medias = 0;
-
- foreach ($photos as $k => $v) {
-
- $allowedMimes = explode(',', config('pixelfed.media_types'));
- if(in_array($v->getMimeType(), $allowedMimes) == false) {
- continue;
- }
- $filter_class = $request->input('filter_class');
- $filter_name = $request->input('filter_name');
-
- $storagePath = "public/m/{$monthHash}/{$userHash}";
- $path = $v->store($storagePath);
- $hash = \hash_file('sha256', $v);
- $media = new Media();
- $media->status_id = $status->id;
- $media->profile_id = $profile->id;
- $media->user_id = $user->id;
- $media->media_path = $path;
- $media->original_sha256 = $hash;
- $media->size = $v->getSize();
- $media->mime = $v->getMimeType();
-
- $media->filter_class = in_array($filter_class, Filter::classes()) ? $filter_class : null;
- $media->filter_name = in_array($filter_name, Filter::names()) ? $filter_name : null;
- $media->order = $order;
- $media->save();
- array_push($mimes, $media->mime);
- ImageOptimize::dispatch($media);
- $order++;
- $medias++;
- }
-
- if($medias == 0) {
- $status->delete();
- return;
- }
- $status->type = (new self)::mimeTypeCheck($mimes);
- $status->save();
-
- Cache::forget('profile:status_count:'.$profile->id);
- NewStatusPipeline::dispatch($status);
-
- // TODO: Send to subscribers
-
- return redirect($status->url());
}
public function delete(Request $request)
@@ -238,7 +141,9 @@ class StatusController extends Controller
$user = Auth::user();
$profile = $user->profile;
- $status = Status::withCount('shares')->findOrFail($request->input('item'));
+ $status = Status::withCount('shares')
+ ->whereIn('scope', ['public', 'unlisted'])
+ ->findOrFail($request->input('item'));
$count = $status->shares_count;
diff --git a/app/Jobs/FollowPipeline/FollowPipeline.php b/app/Jobs/FollowPipeline/FollowPipeline.php
index 17ad47889..57ed1d2e4 100644
--- a/app/Jobs/FollowPipeline/FollowPipeline.php
+++ b/app/Jobs/FollowPipeline/FollowPipeline.php
@@ -61,8 +61,6 @@ class FollowPipeline implements ShouldQueue
$notification->item_type = "App\Profile";
$notification->save();
- Cache::forever('notification.'.$notification->id, $notification);
- Cache::forget('feature:discover:people:'.$actor->id);
$redis = Redis::connection();
$nkey = config('cache.prefix').':user.'.$target->id.'.notifications';
diff --git a/app/Jobs/LikePipeline/LikePipeline.php b/app/Jobs/LikePipeline/LikePipeline.php
index f8aeb5c0a..896827917 100644
--- a/app/Jobs/LikePipeline/LikePipeline.php
+++ b/app/Jobs/LikePipeline/LikePipeline.php
@@ -2,16 +2,17 @@
namespace App\Jobs\LikePipeline;
-use App\Like;
-use App\Notification;
-use Cache;
+use Cache, Log, Redis;
+use App\{Like, Notification};
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
-use Log;
-use Redis;
+use App\Util\ActivityPub\Helpers;
+use League\Fractal;
+use League\Fractal\Serializer\ArraySerializer;
+use App\Transformer\ActivityPub\Verb\Like as LikeTransformer;
class LikePipeline implements ShouldQueue
{
@@ -48,11 +49,15 @@ class LikePipeline implements ShouldQueue
$status = $this->like->status;
$actor = $this->like->actor;
- if (!$status || $status->url !== null) {
- // Ignore notifications to remote statuses, or deleted statuses
+ if (!$status) {
+ // Ignore notifications to deleted statuses
return;
}
+ if($status->url && $actor->domain == null) {
+ return $this->remoteLikeDeliver();
+ }
+
$exists = Notification::whereProfileId($status->profile_id)
->whereActorId($actor->id)
->whereAction('like')
@@ -78,4 +83,20 @@ class LikePipeline implements ShouldQueue
} catch (Exception $e) {
}
}
+
+ public function remoteLikeDeliver()
+ {
+ $like = $this->like;
+ $status = $this->like->status;
+ $actor = $this->like->actor;
+
+ $fractal = new Fractal\Manager();
+ $fractal->setSerializer(new ArraySerializer());
+ $resource = new Fractal\Resource\Item($like, new LikeTransformer());
+ $activity = $fractal->createData($resource)->toArray();
+
+ $url = $status->profile->sharedInbox ?? $status->profile->inbox_url;
+
+ Helpers::sendSignedObject($actor, $url, $activity);
+ }
}
diff --git a/app/Jobs/MentionPipeline/MentionPipeline.php b/app/Jobs/MentionPipeline/MentionPipeline.php
index e75ede04f..ea833345d 100644
--- a/app/Jobs/MentionPipeline/MentionPipeline.php
+++ b/app/Jobs/MentionPipeline/MentionPipeline.php
@@ -50,7 +50,7 @@ class MentionPipeline implements ShouldQueue
$exists = Notification::whereProfileId($target)
->whereActorId($actor->id)
- ->whereAction('mention')
+ ->whereIn('action', ['mention', 'comment'])
->whereItemId($status->id)
->whereItemType('App\Status')
->count();
diff --git a/app/Jobs/SharePipeline/SharePipeline.php b/app/Jobs/SharePipeline/SharePipeline.php
index 6d581eb73..b62c51268 100644
--- a/app/Jobs/SharePipeline/SharePipeline.php
+++ b/app/Jobs/SharePipeline/SharePipeline.php
@@ -2,16 +2,18 @@
namespace App\Jobs\SharePipeline;
-use App\Status;
-use App\Notification;
-use Cache;
+use Cache, Log, Redis;
+use App\{Status, Notification};
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
-use Log;
-use Redis;
+use League\Fractal;
+use League\Fractal\Serializer\ArraySerializer;
+use App\Transformer\ActivityPub\Verb\Announce;
+use GuzzleHttp\{Pool, Client, Promise};
+use App\Util\ActivityPub\HttpSignature;
class SharePipeline implements ShouldQueue
{
@@ -63,6 +65,8 @@ class SharePipeline implements ShouldQueue
return true;
}
+ $this->remoteAnnounceDeliver();
+
try {
$notification = new Notification;
$notification->profile_id = $target->id;
@@ -74,8 +78,6 @@ class SharePipeline implements ShouldQueue
$notification->item_type = "App\Status";
$notification->save();
- Cache::forever('notification.'.$notification->id, $notification);
-
$redis = Redis::connection();
$key = config('cache.prefix').':user.'.$status->profile_id.'.notifications';
$redis->lpush($key, $notification->id);
@@ -83,4 +85,56 @@ class SharePipeline implements ShouldQueue
Log::error($e);
}
}
+
+ public function remoteAnnounceDeliver()
+ {
+ $status = $this->status;
+ $profile = $status->profile;
+
+ $fractal = new Fractal\Manager();
+ $fractal->setSerializer(new ArraySerializer());
+ $resource = new Fractal\Resource\Item($status, new Announce());
+ $activity = $fractal->createData($resource)->toArray();
+
+ $audience = $status->profile->getAudienceInbox();
+
+ if(empty($audience) || $status->scope != 'public') {
+ // Return on profiles with no remote followers
+ return;
+ }
+
+ $payload = json_encode($activity);
+
+ $client = new Client([
+ 'timeout' => config('federation.activitypub.delivery.timeout')
+ ]);
+
+ $requests = function($audience) use ($client, $activity, $profile, $payload) {
+ foreach($audience as $url) {
+ $headers = HttpSignature::sign($profile, $url, $activity);
+ yield function() use ($client, $url, $headers, $payload) {
+ return $client->postAsync($url, [
+ 'curl' => [
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => $payload,
+ CURLOPT_HEADER => true
+ ]
+ ]);
+ };
+ }
+ };
+
+ $pool = new Pool($client, $requests($audience), [
+ 'concurrency' => config('federation.activitypub.delivery.concurrency'),
+ 'fulfilled' => function ($response, $index) {
+ },
+ 'rejected' => function ($reason, $index) {
+ }
+ ]);
+
+ $promise = $pool->promise();
+
+ $promise->wait();
+
+ }
}
diff --git a/app/Jobs/StatusPipeline/StatusActivityPubDeliver.php b/app/Jobs/StatusPipeline/StatusActivityPubDeliver.php
index a2b6d6f93..ec4243107 100644
--- a/app/Jobs/StatusPipeline/StatusActivityPubDeliver.php
+++ b/app/Jobs/StatusPipeline/StatusActivityPubDeliver.php
@@ -49,6 +49,7 @@ class StatusActivityPubDeliver implements ShouldQueue
public function handle()
{
$status = $this->status;
+ $profile = $status->profile;
if($status->local == false || $status->url || $status->uri) {
return;
@@ -56,12 +57,11 @@ class StatusActivityPubDeliver implements ShouldQueue
$audience = $status->profile->getAudienceInbox();
- if(empty($audience) || $status->visibility != 'public') {
+ if(empty($audience) || $status->scope != 'public') {
// Return on profiles with no remote followers
return;
}
- $profile = $status->profile;
$fractal = new Fractal\Manager();
$fractal->setSerializer(new ArraySerializer());
diff --git a/app/Transformer/ActivityPub/Verb/Announce.php b/app/Transformer/ActivityPub/Verb/Announce.php
index b6acb31d1..682885b77 100644
--- a/app/Transformer/ActivityPub/Verb/Announce.php
+++ b/app/Transformer/ActivityPub/Verb/Announce.php
@@ -11,9 +11,16 @@ class Announce extends Fractal\TransformerAbstract
{
return [
'@context' => 'https://www.w3.org/ns/activitystreams',
+ 'id' => $status->permalink(),
'type' => 'Announce',
'actor' => $status->profile->permalink(),
- 'object' => $status->parent()->url()
+ 'to' => ['https://www.w3.org/ns/activitystreams#Public'],
+ 'cc' => [
+ $status->profile->permalink(),
+ $status->profile->follower_url ?? $status->profile->permalink('/followers')
+ ],
+ 'published' => $status->created_at->format(DATE_ISO8601),
+ 'object' => $status->parent()->url(),
];
}
}
\ No newline at end of file
diff --git a/app/Transformer/ActivityPub/Verb/Like.php b/app/Transformer/ActivityPub/Verb/Like.php
index 5662ab758..b6f699158 100644
--- a/app/Transformer/ActivityPub/Verb/Like.php
+++ b/app/Transformer/ActivityPub/Verb/Like.php
@@ -11,6 +11,7 @@ class Like extends Fractal\TransformerAbstract
{
return [
'@context' => 'https://www.w3.org/ns/activitystreams',
+ 'id' => $like->actor->permalink('#likes/'.$like->id),
'type' => 'Like',
'actor' => $like->actor->permalink(),
'object' => $like->status->url()
diff --git a/app/Transformer/Api/StatusTransformer.php b/app/Transformer/Api/StatusTransformer.php
index 1c033c1f1..8a16e4dfd 100644
--- a/app/Transformer/Api/StatusTransformer.php
+++ b/app/Transformer/Api/StatusTransformer.php
@@ -34,7 +34,7 @@ class StatusTransformer extends Fractal\TransformerAbstract
'muted' => null,
'sensitive' => (bool) $status->is_nsfw,
'spoiler_text' => $status->cw_summary,
- 'visibility' => $status->visibility,
+ 'visibility' => $status->visibility ?? $status->scope,
'application' => [
'name' => 'web',
'website' => null
diff --git a/app/Util/ActivityPub/Helpers.php b/app/Util/ActivityPub/Helpers.php
index f2c1169db..49a881740 100644
--- a/app/Util/ActivityPub/Helpers.php
+++ b/app/Util/ActivityPub/Helpers.php
@@ -146,9 +146,13 @@ class Helpers {
$host = parse_url($valid, PHP_URL_HOST);
+ if(count(dns_get_record($host, DNS_A | DNS_AAAA)) == 0) {
+ return false;
+ }
+
if(config('costar.enabled') == true) {
if(
- (config('costar.domain.block') != null && in_array($host, config('costar.domain.block')) == true) ||
+ (config('costar.domain.block') != null && Str::contains($host, config('costar.domain.block')) == true) ||
(config('costar.actor.block') != null && in_array($url, config('costar.actor.block')) == true)
) {
return false;
@@ -202,7 +206,7 @@ class Helpers {
return self::fetchFromUrl($url);
}
- public static function statusFirstOrFetch($url, $replyTo = true)
+ public static function statusFirstOrFetch($url, $replyTo = false)
{
$url = self::validateUrl($url);
if($url == false) {
@@ -333,6 +337,11 @@ class Helpers {
}
}
+ public static function statusFetch($url)
+ {
+ return self::statusFirstOrFetch($url);
+ }
+
public static function importNoteAttachment($data, Status $status)
{
if(self::verifyAttachments($data) == false) {
@@ -399,7 +408,10 @@ class Helpers {
return;
}
$domain = parse_url($res['id'], PHP_URL_HOST);
- $username = Purify::clean($res['preferredUsername']);
+ $username = (string) Purify::clean($res['preferredUsername']);
+ if(empty($username)) {
+ return;
+ }
$remoteUsername = "@{$username}@{$domain}";
abort_if(!self::validateUrl($res['inbox']), 400);
@@ -408,9 +420,9 @@ class Helpers {
$profile = Profile::whereRemoteUrl($res['id'])->first();
if(!$profile) {
- $profile = new Profile;
+ $profile = new Profile();
$profile->domain = $domain;
- $profile->username = Purify::clean($remoteUsername);
+ $profile->username = (string) Purify::clean($remoteUsername);
$profile->name = Purify::clean($res['name']) ?? 'user';
$profile->bio = Purify::clean($res['summary']);
$profile->sharedInbox = isset($res['endpoints']) && isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : null;
@@ -428,6 +440,11 @@ class Helpers {
return $profile;
}
+ public static function profileFetch($url)
+ {
+ return self::profileFirstOrNew($url);
+ }
+
public static function sendSignedObject($senderProfile, $url, $body)
{
abort_if(!self::validateUrl($url), 400);
diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php
index 4731811f9..54aaa4af4 100644
--- a/app/Util/ActivityPub/Inbox.php
+++ b/app/Util/ActivityPub/Inbox.php
@@ -151,7 +151,7 @@ class Inbox
if(Status::whereUrl($url)->exists()) {
return;
}
- Helpers::statusFirstOrFetch($url, false);
+ Helpers::statusFetch($url);
return;
}
@@ -205,21 +205,27 @@ class Inbox
{
$actor = $this->actorFirstOrCreate($this->payload['actor']);
$activity = $this->payload['object'];
+
if(!$actor || $actor->domain == null) {
return;
}
+
if(Helpers::validateLocalUrl($activity) == false) {
return;
}
- $parent = Helpers::statusFirstOrFetch($activity, true);
- if(!$parent) {
+
+ $parent = Helpers::statusFetch($activity);
+
+ if(empty($parent)) {
return;
}
+
$status = Status::firstOrCreate([
'profile_id' => $actor->id,
'reblog_of_id' => $parent->id,
- 'type' => 'reply'
+ 'type' => 'share'
]);
+
Notification::firstOrCreate([
'profile_id' => $parent->profile->id,
'actor_id' => $actor->id,
@@ -229,6 +235,7 @@ class Inbox
'item_id' => $parent->id,
'item_type' => 'App\Status'
]);
+
$parent->reblogs_count = $parent->shares()->count();
$parent->save();
}
@@ -267,8 +274,10 @@ class Inbox
if(is_string($obj) && Helpers::validateUrl($obj)) {
// actor object detected
// todo delete actor
+ return;
} else if (Helpers::validateUrl($obj['id']) && Helpers::validateObject($obj) && $obj['type'] == 'Tombstone') {
// todo delete status or object
+ return;
}
}
@@ -316,6 +325,21 @@ class Inbox
break;
case 'Announce':
+ $obj = $obj['object'];
+ abort_if(!Helpers::validateLocalUrl($obj), 400);
+ $status = Helpers::statusFetch($obj);
+ if(!$status) {
+ return;
+ }
+ Status::whereProfileId($profile->id)
+ ->whereReblogOfId($status->id)
+ ->forceDelete();
+ Notification::whereProfileId($status->profile->id)
+ ->whereActorId($profile->id)
+ ->whereAction('share')
+ ->whereItemId($status->reblog_of_id)
+ ->whereItemType('App\Status')
+ ->forceDelete();
break;
case 'Block':
@@ -347,6 +371,6 @@ class Inbox
->forceDelete();
break;
}
-
+ return;
}
}
diff --git a/app/Util/Lexer/Autolink.php b/app/Util/Lexer/Autolink.php
index 4ec8c2ef7..4aa38d7f1 100755
--- a/app/Util/Lexer/Autolink.php
+++ b/app/Util/Lexer/Autolink.php
@@ -718,7 +718,7 @@ class Autolink extends Regex
// Replace the username
$linkText = Str::startsWith($screen_name, '@') ? $screen_name : '@'.$screen_name;
$class = $this->class_user;
- $url = $this->url_base_user.$screen_name;;
+ $url = $this->url_base_user . $screen_name;
}
if (!empty($class)) {
$attributes['class'] = $class;
diff --git a/app/Util/RateLimit/User.php b/app/Util/RateLimit/User.php
index 75e4b1c6e..c93aa6c4f 100644
--- a/app/Util/RateLimit/User.php
+++ b/app/Util/RateLimit/User.php
@@ -48,4 +48,9 @@ trait User {
{
return 500;
}
+
+ public function getMaxInstanceBansPerDayAttribute()
+ {
+ return 100;
+ }
}
\ No newline at end of file
diff --git a/app/Util/Webfinger/Webfinger.php b/app/Util/Webfinger/Webfinger.php
index e8f10c31c..879103332 100644
--- a/app/Util/Webfinger/Webfinger.php
+++ b/app/Util/Webfinger/Webfinger.php
@@ -4,74 +4,43 @@ namespace App\Util\Webfinger;
class Webfinger
{
- public $user;
- public $subject;
- public $aliases;
- public $links;
+ protected $user;
+ protected $subject;
+ protected $aliases;
+ protected $links;
- public function __construct($user)
- {
- $this->user = $user;
- $this->subject = '';
- $this->aliases = [];
- $this->links = [];
- }
+ public function __construct($user)
+ {
+ $this->subject = 'acct:'.$user->username.'@'.parse_url(config('app.url'), PHP_URL_HOST);
+ $this->aliases = [
+ $user->url(),
+ $user->permalink(),
+ ];
+ $this->links = [
+ [
+ 'rel' => 'http://webfinger.net/rel/profile-page',
+ 'type' => 'text/html',
+ 'href' => $user->url(),
+ ],
+ [
+ 'rel' => 'http://schemas.google.com/g/2010#updates-from',
+ 'type' => 'application/atom+xml',
+ 'href' => $user->permalink('.atom'),
+ ],
+ [
+ 'rel' => 'self',
+ 'type' => 'application/activity+json',
+ 'href' => $user->permalink(),
+ ],
+ ];
+ }
- public function setSubject()
- {
- $host = parse_url(config('app.url'), PHP_URL_HOST);
- $username = $this->user->username;
-
- $this->subject = 'acct:'.$username.'@'.$host;
-
- return $this;
- }
-
- public function generateAliases()
- {
- $this->aliases = [
- $this->user->url(),
- $this->user->permalink(),
- ];
-
- return $this;
- }
-
- public function generateLinks()
- {
- $user = $this->user;
-
- $this->links = [
- [
- 'rel' => 'http://webfinger.net/rel/profile-page',
- 'type' => 'text/html',
- 'href' => $user->url(),
- ],
- [
- 'rel' => 'http://schemas.google.com/g/2010#updates-from',
- 'type' => 'application/atom+xml',
- 'href' => $user->permalink('.atom'),
- ],
- [
- 'rel' => 'self',
- 'type' => 'application/activity+json',
- 'href' => $user->permalink(),
- ],
- ];
-
- return $this;
- }
-
- public function generate()
- {
- $this->setSubject();
- $this->generateAliases();
- $this->generateLinks();
-
- return [
- 'subject' => $this->subject,
- 'aliases' => $this->aliases,
- 'links' => $this->links,
- ];
- }
+ public function generate()
+ {
+ return [
+ 'subject' => $this->subject,
+ 'aliases' => $this->aliases,
+ 'links' => $this->links,
+ ];
+ }
}
diff --git a/config/instance.php b/config/instance.php
index 3842de358..6fc04c902 100644
--- a/config/instance.php
+++ b/config/instance.php
@@ -7,4 +7,9 @@ return [
'enabled' => env('INSTANCE_CONTACT_FORM', false),
'max_per_day' => env('INSTANCE_CONTACT_MAX_PER_DAY', 1),
],
+
+ 'announcement' => [
+ 'enabled' => env('INSTANCE_ANNOUNCEMENT_ENABLED', true),
+ 'message' => env('INSTANCE_ANNOUNCEMENT_MESSAGE', 'Example announcement message.
Something else here')
+ ]
];
\ No newline at end of file
diff --git a/public/js/profile.js b/public/js/profile.js
index 338f6bd7d..0ddcd0afe 100644
Binary files a/public/js/profile.js and b/public/js/profile.js differ
diff --git a/public/js/status.js b/public/js/status.js
index b2c231a7c..ba4af018e 100644
Binary files a/public/js/status.js and b/public/js/status.js differ
diff --git a/public/js/timeline.js b/public/js/timeline.js
index 4ec4f9432..f4c3175df 100644
Binary files a/public/js/timeline.js and b/public/js/timeline.js differ
diff --git a/public/mix-manifest.json b/public/mix-manifest.json
index 4512a2b1b..cbe51aa43 100644
Binary files a/public/mix-manifest.json and b/public/mix-manifest.json differ
diff --git a/resources/assets/js/components/PostComponent.vue b/resources/assets/js/components/PostComponent.vue
index 7902fd6a7..f0be53b1c 100644
--- a/resources/assets/js/components/PostComponent.vue
+++ b/resources/assets/js/components/PostComponent.vue
@@ -179,14 +179,14 @@
Muted Users Blocked Users + Blocked keywords + Blocked instances