now showing external profiles as well

This commit is contained in:
Ghostie 2024-12-28 16:20:34 -05:00
parent 96a7c5fefa
commit 809980786f
13 changed files with 243 additions and 136 deletions

View File

@ -16,7 +16,7 @@ Notice that the styles were taken from [AnySpace](https://anyspace.3to.moe/about
- [ ] Posts - [ ] Posts
- [ ] Local posts should be federated - [ ] Local posts should be federated
- [ ] Remote posts should be fetched - [ ] Remote posts should be fetched
- [ ] Follows - [x] Follows
- [ ] Likes - [ ] Likes
- [ ] Comments - [ ] Comments

View File

@ -59,9 +59,6 @@ class APInboxController extends Controller
"error" => "Error posting activity", "error" => "Error posting activity",
], 500); ], 500);
} }
$target->user->friends += 1;
$target->user->save ();
} }
public function handle_undo (User $user, $activity) public function handle_undo (User $user, $activity)

View File

@ -2,6 +2,8 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Types\TypeActor;
use App\Models\User; use App\Models\User;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@ -13,4 +15,27 @@ class HomeController extends Controller
return view ("home", compact ("latest_users")); return view ("home", compact ("latest_users"));
} }
public function search ()
{
$query = request ()->get ("query");
// check if the query is empty
if (empty ($query)) {
return redirect ()->route ("home");
}
// check if the search is a federated user
$user_handle = array_slice (explode ("@", $query), 1);
if (count ($user_handle) > 1) {
$username = $user_handle[0];
$domain = $user_handle[1];
$actor = TypeActor::actor_exists_or_obtain_from_handle ($username, $domain);
if (!$actor)
return redirect ()->route ("home");
return redirect ()->route ("users.show", "@$actor->preferredUsername@$domain");
}
}
} }

View File

@ -9,12 +9,27 @@ use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Drivers\Gd\Driver;
use App\Models\User; use App\Models\User;
use App\Models\Actor;
class ProfileController extends Controller class ProfileController extends Controller
{ {
public function show (User $user) public function show ($user_name)
{ {
return view ("users.profile", compact ("user")); $actor = null;
$user = null;
if (str_starts_with ($user_name, "@")) {
$actor = Actor::where ("local_actor_id", $user_name)->first ();
}
else
{
$user = User::where ("name", $user_name)->first ();
if (!$user)
return redirect ()->route ("home");
$actor = $user->actor;
}
return view ("users.profile", compact ("actor", "user"));
} }
public function edit () public function edit ()

View File

@ -14,6 +14,7 @@ class Actor extends Model
"type", "type",
"actor_id", "actor_id",
"local_actor_id",
"following", "following",
"followers", "followers",

View File

@ -111,6 +111,7 @@ class TypeActor {
// Use null coalescing operator `??` for safety // Use null coalescing operator `??` for safety
$actor->actor_id = $request['id'] ?? ''; $actor->actor_id = $request['id'] ?? '';
$actor->local_actor_id = TypeActor::actor_build_private_id ($actor->actor_id) ?? '';
$actor->type = $request['type'] ?? ''; $actor->type = $request['type'] ?? '';
$actor->following = $request['following'] ?? ''; $actor->following = $request['following'] ?? '';
@ -141,25 +142,21 @@ class TypeActor {
public static function obtain_actor_info ($actor_id) public static function obtain_actor_info ($actor_id)
{ {
$client = new Client ();
$parsed_url = parse_url ($actor_id); $parsed_url = parse_url ($actor_id);
$url_instance = $parsed_url["scheme"] . "://" . $parsed_url["host"]; $url_instance = $parsed_url["scheme"] . "://" . $parsed_url["host"];
$url_path = explode ("/", $parsed_url["path"]); $url_path = explode ("/", $parsed_url["path"]);
$actor_name = end ($url_path); $actor_name = end ($url_path);
$well_known_url = $url_instance . "/.well-known/webfinger?resource=acct:" . $actor_name . "@" . $parsed_url["host"]; $well_known = TypeActor::query_wellknown ($actor_name, $parsed_url ["host"]);
$res = $client->get ($well_known_url);
$response = json_decode ($res->getBody ()->getContents ()); foreach ($well_known->links as $link)
foreach ($response->links as $link)
{ {
if ($link->rel == "self") if ($link->rel == "self")
{ {
$client = new Client ();
$res = $client->request ("GET", $link->href, [ $res = $client->request ("GET", $link->href, [
"headers" => [ "headers" => [
"Accept" => "application/activity+json" "Accept" => "application/json"
] ]
]); ]);
$actor = json_decode ($res->getBody ()->getContents (), true); $actor = json_decode ($res->getBody ()->getContents (), true);
@ -172,6 +169,25 @@ class TypeActor {
return null; return null;
} }
public static function query_wellknown ($name, $domain)
{
$client = new Client ();
$well_known_url = "https://" . $domain . "/.well-known/webfinger?resource=acct:" . $name . "@" . $domain;
try {
$res = $client->get ($well_known_url, [
"headers" => [
"Accept" => "application/json"
]
]);
} catch (\Exception $e) {
return json_encode (["error" => "Actor not found"]);
}
return json_decode ($res->getBody ()->getContents ());
}
// some little functions // some little functions
public static function actor_exists ($actor_id) public static function actor_exists ($actor_id)
{ {
@ -190,6 +206,33 @@ class TypeActor {
return $actor; return $actor;
} }
public static function actor_exists_or_obtain_from_handle ($name, $domain)
{
$well_known = TypeActor::query_wellknown ($name, $domain);
if (!$well_known)
return null;
foreach ($well_known->links as $link)
{
if ($link->rel == "self")
{
return TypeActor::actor_exists_or_obtain ($link->href);
}
}
return null;
}
public static function actor_build_private_id ($actor_id)
{
$parsed_url = parse_url ($actor_id);
$split_path = explode ("/", $parsed_url ["path"]);
$username = end ($split_path);
$domain = $parsed_url ["host"];
return "@" . $username . "@" . $domain;
}
public static function actor_get_local ($actor_id) public static function actor_get_local ($actor_id)
{ {
$actor = Actor::where ("actor_id", $actor_id)->first (); $actor = Actor::where ("actor_id", $actor_id)->first ();

View File

@ -18,6 +18,7 @@ return new class extends Migration
$table->string ("type")->nullable (); $table->string ("type")->nullable ();
$table->string ("actor_id")->unique (); $table->string ("actor_id")->unique ();
$table->string ("local_actor_id")->unique ()->nullable ();
$table->string ("following")->nullable (); $table->string ("following")->nullable ();
$table->string ("followers")->nullable (); $table->string ("followers")->nullable ();

View File

@ -15,7 +15,7 @@
<div class="inner"> <div class="inner">
@foreach ($latest_users as $user) @foreach ($latest_users as $user)
<div class="person"> <div class="person">
<a href="{{ route ('users.show', [ 'user' => $user ]) }}"> <a href="{{ route ('users.show', [ 'user_name' => $user ]) }}">
<p>{{ $user->name }}</p> <p>{{ $user->name }}</p>
</a> </a>
<a href="#"> <a href="#">

View File

@ -23,7 +23,7 @@
<div class="more-options"> <div class="more-options">
<p> <p>
View My View My
<a href="{{ route('users.show', ['user' => auth()->user()]) }}">Profile</a> <a href="{{ route('users.show', ['user_name' => auth()->user()->name]) }}">Profile</a>
| |
<a href="#">Blog</a> <a href="#">Blog</a>
| |
@ -34,7 +34,7 @@
<p> <p>
My URL: My URL:
<a <a
href="{{ route('users.show', ['user' => auth()->user()]) }}">{{ route('users.show', ['user' => auth()->user()]) }}</a> href="{{ route('users.show', ['user_name' => auth()->user()->name]) }}">{{ route('users.show', ['user_name' => auth()->user()->name]) }}</a>
</p> </p>
</div> </div>
</div> </div>
@ -42,7 +42,7 @@
<div class="url-info view-full-profile"> <div class="url-info view-full-profile">
<p> <p>
<a href="{{ route('users.show', ['user' => auth()->user()]) }}"><b>View Your Profile</b></a> <a href="{{ route('users.show', ['user_name' => auth()->user()->name]) }}"><b>View Your Profile</b></a>
</p> </p>
</div> </div>

View File

@ -12,7 +12,7 @@
</div> </div>
<div class="center"> <div class="center">
<form action="#"> <form action="{{ route('search') }}" method="get">
<label>OurSpace</label> <label>OurSpace</label>
<input type="text" placeholder="Search OurSpace" name="query"> <input type="text" placeholder="Search OurSpace" name="query">
<input type="submit" value="Search" class="submit-btn"> <input type="submit" value="Search" class="submit-btn">
@ -67,7 +67,7 @@
</li> </li>
<li> <li>
<a href="#">&nbsp;Source </a> <a href="https://github.com/0xd011f4ce/OurSpace">&nbsp;Source </a>
</li> </li>
<li> <li>

View File

@ -9,7 +9,7 @@
<div class="col right"> <div class="col right">
<h1>Edit profile</h1> <h1>Edit profile</h1>
<p>All fields are optional and can be left empty</p> <p>All fields are optional and can be left empty</p>
<a href="{{ route ('users.show', [ 'user' => $user ]) }}">« View Profile</a> <a href="{{ route ('users.show', [ 'user_name' => $user->name ]) }}">« View Profile</a>
<div class="profile-pic"> <div class="profile-pic">
<h1>{{ $user->name }}</h1> <h1>{{ $user->name }}</h1>

View File

@ -1,41 +1,49 @@
@extends ("partials.layout") @extends ("partials.layout")
@section('title', "$user->name's Profile") @section('title', "$actor->preferredUsername's Profile")
@section('content') @section('content')
<div class="row profile"> <div class="row profile">
<div class="col w-40 left"> <div class="col w-40 left">
<span> <span>
<h1>{{ $user->name }}</h1> <h1>{{ $actor->preferredUsername }}</h1>
</span> </span>
<div class="general-about"> <div class="general-about">
<div class="profile-pic"> <div class="profile-pic">
<img loading="lazy" src="{{ $user->avatar }}" alt="{{ $user->name }}'s pfp" class="pfp-fa" style="width: 235px; height: auto"> @if ($user == null)
<img loading="lazy" src="{{ $actor->icon }}" alt="{{ $actor->preferredUsername }}'s pfp" class="pfp-fa" style="width: 235px; height: auto">
@else
<img loading="lazy" src="{{ $user->avatar }}" alt="{{ $actor->preferredUsername }}'s pfp" class="pfp-fa" style="width: 235px; height: auto">
@endif
</div> </div>
<div class="details"> @if ($user != null)
<p>{{ $user->status }}</p> <div class="details">
<p>{{ $user->about_you }}</p> <p>{{ $user->status }}</p>
<p class="online"> <p>{{ $user->about_you }}</p>
<img loading="lazy" src="/resources/img/green_person.png" alt="online"> ONLINE! <p class="online">
</p> <img loading="lazy" src="/resources/img/green_person.png" alt="online"> ONLINE!
</div> </p>
</div>
@endif
</div> </div>
<audio src="#" id="music" autoplay loop controls></audio> <audio src="#" id="music" autoplay loop controls></audio>
<div class="mood"> <div class="mood">
<p><b>Mood:</b> {{ $user->mood }}</p> @if ($user != null)
<p><b>View my: <a href="#">Blog</a> | <a href="#">Bulletins</a></b></p> <p><b>Mood:</b> {{ $user->mood }}</p>
<p><b>View my: <a href="#">Blog</a> | <a href="#">Bulletins</a></b></p>
@endif
</div> </div>
<div class="contact"> <div class="contact">
<div class="heading"> <div class="heading">
<h4>Contacting {{ $user->name }}</h4> <h4>Contacting {{ $actor->preferredUsername }}</h4>
</div> </div>
<div class="inner"> <div class="inner">
@ -101,80 +109,87 @@
<p> <p>
<b>Federation handle:</b> <b>Federation handle:</b>
</p> </p>
<p>@php echo "@" . $user->name . "@" . explode ("/", env ("APP_URL"))[2] @endphp</p> @if ($user != null)
<p>@php echo "@" . $user->name . "@" . explode ("/", env ("APP_URL"))[2] @endphp</p>
@else
<p>{{ $actor->local_actor_id }}</p>
@endif
</div> </div>
<div class="table-section"> @if ($user != null)
<div class="heading"> <div class="table-section">
<h4>{{ $user->name }}'s Interests</h4> <div class="heading">
<h4>{{ $user->name }}'s Interests</h4>
</div>
<div class="inner">
<table class="details-table" cellspacing="3" cellpadding="3">
<tbody>
<tr>
<td>
<p>General</p>
</td>
<td>
<p>{{ $user->interests_general }}</p>
</td>
</tr>
<tr>
<td>
<p>Music</p>
</td>
<td>
<p>{{ $user->interests_music }}</p>
</td>
</tr>
<tr>
<td>
<p>Movies</p>
</td>
<td>
<p>{{ $user->interests_movies }}</p>
</td>
</tr>
<tr>
<td>
<p>Television</p>
</td>
<td>
<p>{{ $user->interests_television }}</p>
</td>
</tr>
<tr>
<td>
<p>Books</p>
</td>
<td>
<p>{{ $user->interests_books }}</p>
</td>
</tr>
<tr>
<td>
<p>Heroes</p>
</td>
<td>
<p>{{ $user->interests_heroes }}</p>
</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
<div class="inner"> @endif
<table class="details-table" cellspacing="3" cellpadding="3">
<tbody>
<tr>
<td>
<p>General</p>
</td>
<td>
<p>{{ $user->interests_general }}</p>
</td>
</tr>
<tr>
<td>
<p>Music</p>
</td>
<td>
<p>{{ $user->interests_music }}</p>
</td>
</tr>
<tr>
<td>
<p>Movies</p>
</td>
<td>
<p>{{ $user->interests_movies }}</p>
</td>
</tr>
<tr>
<td>
<p>Television</p>
</td>
<td>
<p>{{ $user->interests_television }}</p>
</td>
</tr>
<tr>
<td>
<p>Books</p>
</td>
<td>
<p>{{ $user->interests_books }}</p>
</td>
</tr>
<tr>
<td>
<p>Heroes</p>
</td>
<td>
<p>{{ $user->interests_heroes }}</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div> </div>
<div class="col right"> <div class="col right">
@auth @auth
@if (auth()->user()->is($user)) @if ($user != null && auth()->user()->is($user))
<div class="profile-info"> <div class="profile-info">
<h3> <h3>
<a href="{{ route ('users.edit') }}">Edit Your Profile</a> <a href="{{ route ('users.edit') }}">Edit Your Profile</a>
@ -183,6 +198,7 @@
@endif @endif
@endauth @endauth
@if ($user != null)
<div class="blog-preview"> <div class="blog-preview">
<h4> <h4>
{{ $user->name }}'s Latest Blog Entries [<a href="#">View Blog</a>] {{ $user->name }}'s Latest Blog Entries [<a href="#">View Blog</a>]
@ -191,57 +207,62 @@
<i>There are no Blog Entries yet.</i> <i>There are no Blog Entries yet.</i>
</p> </p>
</div> </div>
@endif
<div class="blurbs"> <div class="blurbs">
<div class="heading"> <div class="heading">
<h4> <h4>
{{ $user->name }}'s Bio {{ $actor->preferredUsername }}'s Bio
</h4> </h4>
</div> </div>
<div class="inner"> <div class="inner">
<div class="section"> <div class="section">
<p>{{ $user->bio }}</p> <p>{!! $actor->summary !!}</p>
</div> </div>
</div> </div>
</div> </div>
<div class="friends"> @if ($user != null)
<div class="heading"> <div class="friends">
<h4> <div class="heading">
{{ $user->name }}'s Friend Space <h4>
</h4> {{ $actor->preferredUsername }}'s Friend Space
<a href="#" class="more">[view all]</a> </h4>
<a href="#" class="more">[view all]</a>
</div>
<div class="inner">
<p>
<b>
{{ $actor->preferredUsername }} has <span class="count">{{ count ($user->mutual_friends ()) }}</span> friends.
</b>
</p>
<div class="friends-grid"></div>
</div>
</div> </div>
@endif
<div class="inner"> @if ($user != null)
<div id="comments" class="friends">
<p> <div class="heading">
<b> <h4>{{ $actor->preferredUsername }}'s Friends Comments</h4>
{{ $user->name }} has <span class="count">{{ count ($user->mutual_friends ()) }}</span> friends. </div>
</b> <div class="inner">
</p> <p>
<b>
<div class="friends-grid"></div> Displaying <span class="count">0</span> of <span class="count">0</span> comments (<a href="#">View all</a> | <a href="#">Add Comment</a>)
</b>
</p>
<table class="comments-table" cellspacing="0" cellpadding="3" bordercollor="#ffffff" border="1">
<tbody></tbody>
</table>
</div>
</div> </div>
</div> @endif
<div id="comments" class="friends">
<div class="heading">
<h4>{{ $user->name }}'s Friends Comments</h4>
</div>
<div class="inner">
<p>
<b>
Displaying <span class="count">0</span> of <span class="count">0</span> comments (<a href="#">View all</a> | <a href="#">Add Comment</a>)
</b>
</p>
<table class="comments-table" cellspacing="0" cellpadding="3" bordercollor="#ffffff" border="1">
<tbody></tbody>
</table>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -15,8 +15,12 @@ Route::get ("/auth/logout", [ UserController::class, "logout" ])->name ("logout"
Route::post ("/auth/signup", [ UserController::class, "do_signup" ])->middleware ("guest"); Route::post ("/auth/signup", [ UserController::class, "do_signup" ])->middleware ("guest");
Route::post ("/auth/login", [ UserController::class, "do_login" ])->middleware ("guest"); Route::post ("/auth/login", [ UserController::class, "do_login" ])->middleware ("guest");
// user routes
Route::get ("/user/edit", [ ProfileController::class, "edit" ])->name ("users.edit")->middleware ("auth"); Route::get ("/user/edit", [ ProfileController::class, "edit" ])->name ("users.edit")->middleware ("auth");
Route::post ("/user/edit", [ ProfileController::class, "update" ])->middleware ("auth"); Route::post ("/user/edit", [ ProfileController::class, "update" ])->middleware ("auth");
Route::get ("/user/{user:name}", [ ProfileController::class, "show" ])->name ("users.show"); Route::get ("/user/{user_name}", [ ProfileController::class, "show" ])->name ("users.show");
// other routes
Route::get ("/search", [ HomeController::class, "search" ])->name ("search");
require __DIR__ . "/api.php"; require __DIR__ . "/api.php";