Jelajahi Sumber

Merge pull request #6066 from pixelfed/staging

Update ComposeController, prioritize followed users and follower_coun…
(dan)iel (sup)ernault 1 Minggu lalu
induk
melakukan
5bcade1d1a

+ 38 - 14
app/Http/Controllers/ComposeController.php

@@ -408,38 +408,62 @@ class ComposeController extends Controller
         abort_if(! $request->user(), 403);
 
         $this->validate($request, [
-            'q' => 'required|string|min:2|max:50',
+            'q' => [
+                'required',
+                'string',
+                'min:2',
+                'max:50',
+                'regex:/^[@]?[a-zA-Z0-9._-]+$/',
+            ],
         ]);
 
         abort_if($request->user()->has_roles && ! UserRoleService::can('can-post', $request->user()->id), 403, 'Invalid permissions for this action');
 
         $q = $request->input('q');
 
-        if (Str::of($q)->startsWith('@')) {
-            if (strlen($q) < 3) {
-                return [];
-            }
+        $cleanQuery = Str::of($q)->startsWith('@') ? Str::substr($q, 1) : $q;
+
+        if (strlen($cleanQuery) < 2) {
+            return [];
         }
 
         $blocked = UserFilter::whereFilterableType('App\Profile')
             ->whereFilterType('block')
             ->whereFilterableId($request->user()->profile_id)
-            ->pluck('user_id');
-
-        $blocked->push($request->user()->profile_id);
-
-        $results = Profile::select('id', 'domain', 'username')
-            ->whereNotIn('id', $blocked)
-            ->where('username', 'like', '%'.$q.'%')
-            ->groupBy('id', 'domain')
+            ->pluck('user_id')
+            ->push($request->user()->profile_id);
+
+        $currentUserId = $request->user()->profile_id;
+
+        $results = Profile::select([
+            'profiles.id',
+            'profiles.domain',
+            'profiles.username',
+            'profiles.followers_count',
+        ])
+            ->selectRaw('MAX(CASE WHEN followers.following_id IS NOT NULL THEN 1 ELSE 0 END) as is_followed')
+            ->leftJoin('followers', function ($join) use ($currentUserId) {
+                $join->on('followers.following_id', '=', 'profiles.id')
+                    ->where('followers.profile_id', '=', $currentUserId);
+            })
+            ->whereNotIn('profiles.id', $blocked)
+            ->where(function ($query) use ($cleanQuery) {
+                $query->where('profiles.username', 'like', $cleanQuery.'%')
+                    ->orWhere('profiles.username', 'like', '%'.$cleanQuery.'%');
+            })
+            ->groupBy('profiles.id', 'profiles.domain', 'profiles.username', 'profiles.followers_count')
+            ->orderByDesc('is_followed')
+            ->orderByDesc('profiles.followers_count')
+            ->orderBy('profiles.username')
             ->limit(15)
             ->get()
             ->map(function ($profile) {
                 $username = $profile->domain ? substr($profile->username, 1) : $profile->username;
 
                 return [
-                    'key' => '@'.str_limit($username, 30),
+                    'key' => '@'.Str::limit($username, 30),
                     'value' => $username,
+                    'is_followed' => (bool) $profile->is_followed,
                 ];
             });
 

+ 172 - 155
app/Jobs/DeletePipeline/DeleteAccountPipeline.php

@@ -2,197 +2,214 @@
 
 namespace App\Jobs\DeletePipeline;
 
-use Illuminate\Bus\Queueable;
-use Illuminate\Queue\SerializesModels;
-use Illuminate\Queue\InteractsWithQueue;
-use Illuminate\Contracts\Queue\ShouldQueue;
-use Illuminate\Foundation\Bus\Dispatchable;
-use DB;
-use Storage;
-use Illuminate\Support\Str;
-use App\Services\AccountService;
-use App\Services\FollowerService;
-use App\Services\PublicTimelineService;
-use App\{
-	AccountInterstitial,
-	AccountLog,
-	Avatar,
-	Bookmark,
-	Collection,
-	CollectionItem,
-	Contact,
-	DirectMessage,
-	EmailVerification,
-	Follower,
-	FollowRequest,
-	Hashtag,
-	HashtagFollow,
-	ImportData,
-	ImportJob,
-	Like,
-	Media,
-	MediaTag,
-	Mention,
-	Notification,
-	OauthClient,
-	Profile,
-	ProfileSponsor,
-	Report,
-	ReportComment,
-	ReportLog,
-	StatusHashtag,
-	StatusArchived,
-	Status,
-	Story,
-	StoryView,
-	User,
-	UserDevice,
-	UserFilter,
-	UserSetting,
-};
+use App\AccountInterstitial;
+use App\AccountLog;
+use App\Bookmark;
+use App\Collection;
+use App\Contact;
+use App\DirectMessage;
+use App\EmailVerification;
+use App\Follower;
+use App\FollowRequest;
+use App\HashtagFollow;
+use App\Jobs\StatusPipeline\StatusDelete;
+use App\Like;
+use App\MediaTag;
+use App\Mention;
 use App\Models\Conversation;
+use App\Models\CustomFilter;
+use App\Models\ImportPost;
 use App\Models\Poll;
 use App\Models\PollVote;
 use App\Models\Portfolio;
+use App\Models\ProfileAlias;
+use App\Models\ProfileMigration;
+use App\Models\RemoteAuth;
+use App\Models\RemoteReport;
 use App\Models\UserPronoun;
-use App\Jobs\StatusPipeline\StatusDelete;
+use App\Notification;
+use App\OauthClient;
+use App\Profile;
+use App\ProfileSponsor;
+use App\Report;
+use App\Services\AccountService;
+use App\Services\FollowerService;
+use App\Services\PublicTimelineService;
+use App\Status;
+use App\StatusArchived;
+use App\StatusHashtag;
+use App\StatusView;
+use App\Story;
+use App\StoryView;
+use App\User;
+use App\UserDevice;
+use App\UserFilter;
+use App\UserSetting;
+use DB;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use Storage;
 
 class DeleteAccountPipeline implements ShouldQueue
 {
-	use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+    protected $user;
 
-	protected $user;
+    public $timeout = 1800;
 
-	public $timeout = 900;
     public $tries = 3;
+
     public $maxExceptions = 1;
+
     public $deleteWhenMissingModels = true;
 
-	public function __construct(User $user)
-	{
-		$this->user = $user;
-	}
+    public function __construct(User $user)
+    {
+        $this->user = $user;
+    }
 
-	public function handle()
-	{
-		$user = $this->user;
+    public function handle()
+    {
+        $user = $this->user;
         $profile = $user->profile;
-		$id = $user->profile_id;
-		Status::whereProfileId($id)->chunk(50, function($statuses) {
-            foreach($statuses as $status) {
+        $id = $user->profile_id;
+        $cloudStorageEnabled = (bool) config_cache('pixelfed.cloud_storage');
+        $cloudDisk = config('filesystems.cloud');
+
+        if ($user && $user->id && is_numeric($user->id)) {
+            $directory = 'imports/'.$user->id;
+
+            if (Storage::exists($directory)) {
+                Storage::deleteDirectory($directory);
+            }
+        }
+
+        if ($id && is_numeric($id)) {
+            $mediaDir = 'public/m/_v2/'.$id;
+
+            if (Storage::exists($mediaDir)) {
+                Storage::deleteDirectory($mediaDir);
+            }
+
+            if ($cloudStorageEnabled && $cloudDisk) {
+                if (Storage::disk($cloudDisk)->exists($mediaDir)) {
+                    Storage::disk($cloudDisk)->deleteDirectory($mediaDir);
+                }
+            }
+        }
+
+        Status::whereProfileId($id)->chunk(50, function ($statuses) {
+            foreach ($statuses as $status) {
                 StatusDelete::dispatch($status);
             }
         });
 
-		AccountLog::whereItemType('App\User')->whereItemId($user->id)->forceDelete();
+        DB::table('user_oidc_mappings')->whereUserId($user->id)->delete();
 
-		AccountInterstitial::whereUserId($user->id)->delete();
+        CustomFilter::whereProfileId($id)->delete();
+
+        StatusView::whereProfileId($id)->delete();
+
+        ProfileAlias::whereProfileId($id)->delete();
+
+        ProfileMigration::whereProfileId($id)->delete();
+
+        RemoteReport::whereAccountId($id)->delete();
+
+        RemoteAuth::whereUserId($user->id)->delete();
+
+        AccountLog::whereItemType('App\User')->whereItemId($user->id)->forceDelete();
+
+        AccountInterstitial::whereUserId($user->id)->delete();
 
-		// Delete Avatar
         $profile->avatar->forceDelete();
 
-        // Delete Poll Votes
         PollVote::whereProfileId($id)->delete();
 
-        // Delete Polls
         Poll::whereProfileId($id)->delete();
 
-        // Delete Portfolio
         Portfolio::whereProfileId($id)->delete();
 
-		ImportData::whereProfileId($id)
-			->cursor()
-			->each(function($data) {
-				$path = storage_path('app/'.$data->path);
-				if(is_file($path)) {
-					unlink($path);
-				}
-				$data->delete();
-		});
-
-		ImportJob::whereProfileId($id)
-			->cursor()
-			->each(function($data) {
-				$path = storage_path('app/'.$data->media_json);
-				if(is_file($path)) {
-					unlink($path);
-				}
-				$data->delete();
-		});
-
-		MediaTag::whereProfileId($id)->delete();
-		Bookmark::whereProfileId($id)->forceDelete();
-		EmailVerification::whereUserId($user->id)->forceDelete();
-		StatusHashtag::whereProfileId($id)->delete();
-		DirectMessage::whereFromId($id)->orWhere('to_id', $id)->delete();
+        ImportPost::whereProfileId($id)->delete();
+
+        MediaTag::whereProfileId($id)->delete();
+        Bookmark::whereProfileId($id)->forceDelete();
+        EmailVerification::whereUserId($user->id)->forceDelete();
+        StatusHashtag::whereProfileId($id)->delete();
+        DirectMessage::whereFromId($id)->orWhere('to_id', $id)->delete();
         Conversation::whereFromId($id)->orWhere('to_id', $id)->delete();
-		StatusArchived::whereProfileId($id)->delete();
-		UserPronoun::whereProfileId($id)->delete();
-		FollowRequest::whereFollowingId($id)
-			->orWhere('follower_id', $id)
-			->forceDelete();
-		Follower::whereProfileId($id)
-			->orWhere('following_id', $id)
-			->each(function($follow) {
-				FollowerService::remove($follow->profile_id, $follow->following_id);
-				$follow->delete();
-			});
-		FollowerService::delCache($id);
-		Like::whereProfileId($id)->forceDelete();
+        StatusArchived::whereProfileId($id)->delete();
+        UserPronoun::whereProfileId($id)->delete();
+        FollowRequest::whereFollowingId($id)
+            ->orWhere('follower_id', $id)
+            ->forceDelete();
+        Follower::whereProfileId($id)
+            ->orWhere('following_id', $id)
+            ->each(function ($follow) {
+                FollowerService::remove($follow->profile_id, $follow->following_id);
+                $follow->delete();
+            });
+        FollowerService::delCache($id);
+        Like::whereProfileId($id)->forceDelete();
         Mention::whereProfileId($id)->forceDelete();
 
-		StoryView::whereProfileId($id)->delete();
-		$stories = Story::whereProfileId($id)->get();
-		foreach($stories as $story) {
-			$path = storage_path('app/'.$story->path);
-			if(is_file($path)) {
-				unlink($path);
-			}
-			$story->forceDelete();
-		}
+        StoryView::whereProfileId($id)->delete();
+        $stories = Story::whereProfileId($id)->get();
+        foreach ($stories as $story) {
+            $path = storage_path('app/'.$story->path);
+            if (is_file($path)) {
+                unlink($path);
+            }
+            $story->forceDelete();
+        }
 
         UserDevice::whereUserId($user->id)->forceDelete();
         UserFilter::whereUserId($user->id)->forceDelete();
         UserSetting::whereUserId($user->id)->forceDelete();
 
-		Mention::whereProfileId($id)->forceDelete();
-		Notification::whereProfileId($id)
-			->orWhere('actor_id', $id)
-			->forceDelete();
-
-		$collections = Collection::whereProfileId($id)->get();
-		foreach ($collections as $collection) {
-			$collection->items()->delete();
-			$collection->delete();
-		}
-		Contact::whereUserId($user->id)->delete();
-		HashtagFollow::whereUserId($user->id)->delete();
-		OauthClient::whereUserId($user->id)->delete();
-		DB::table('oauth_access_tokens')->whereUserId($user->id)->delete();
-		DB::table('oauth_auth_codes')->whereUserId($user->id)->delete();
-		ProfileSponsor::whereProfileId($id)->delete();
-
-		Report::whereUserId($user->id)->forceDelete();
-		PublicTimelineService::warmCache(true, 400);
-		$this->deleteUserColumns($user);
-		AccountService::del($user->profile_id);
-		Profile::whereUserId($user->id)->delete();
-	}
-
-	protected function deleteUserColumns($user)
-	{
-		DB::transaction(function() use ($user) {
-			$user->status = 'deleted';
-			$user->name = 'deleted';
-			$user->email = $user->id;
-			$user->password = '';
-			$user->remember_token = null;
-			$user->is_admin = false;
-			$user->{'2fa_enabled'} = false;
-			$user->{'2fa_secret'} = null;
-			$user->{'2fa_backup_codes'} = null;
-			$user->{'2fa_setup_at'} = null;
-			$user->save();
-		});
-	}
+        Mention::whereProfileId($id)->forceDelete();
+        Notification::whereProfileId($id)
+            ->orWhere('actor_id', $id)
+            ->forceDelete();
+
+        $collections = Collection::whereProfileId($id)->get();
+        foreach ($collections as $collection) {
+            $collection->items()->delete();
+            $collection->delete();
+        }
+        Contact::whereUserId($user->id)->delete();
+        HashtagFollow::whereUserId($user->id)->delete();
+        OauthClient::whereUserId($user->id)->delete();
+        DB::table('oauth_access_tokens')->whereUserId($user->id)->delete();
+        DB::table('oauth_auth_codes')->whereUserId($user->id)->delete();
+        ProfileSponsor::whereProfileId($id)->delete();
+
+        Report::whereUserId($user->id)->forceDelete();
+        PublicTimelineService::warmCache(true, 400);
+        $this->deleteUserColumns($user);
+        AccountService::del($user->profile_id);
+        Profile::whereUserId($user->id)->delete();
+    }
+
+    protected function deleteUserColumns($user)
+    {
+        DB::transaction(function () use ($user) {
+            $user->status = 'deleted';
+            $user->name = 'deleted';
+            $user->email = $user->id;
+            $user->password = '';
+            $user->remember_token = null;
+            $user->is_admin = false;
+            $user->expo_token = null;
+            $user->{'2fa_enabled'} = false;
+            $user->{'2fa_secret'} = null;
+            $user->{'2fa_backup_codes'} = null;
+            $user->{'2fa_setup_at'} = null;
+            $user->save();
+        });
+    }
 }