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
- [ ] Local posts should be federated
- [ ] Remote posts should be fetched
- [ ] Follows
- [x] Follows
- [ ] Likes
- [ ] Comments

View File

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

View File

@ -2,6 +2,8 @@
namespace App\Http\Controllers;
use App\Types\TypeActor;
use App\Models\User;
use Illuminate\Http\Request;
@ -13,4 +15,27 @@ class HomeController extends Controller
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 App\Models\User;
use App\Models\Actor;
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 ()

View File

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

View File

@ -111,6 +111,7 @@ class TypeActor {
// Use null coalescing operator `??` for safety
$actor->actor_id = $request['id'] ?? '';
$actor->local_actor_id = TypeActor::actor_build_private_id ($actor->actor_id) ?? '';
$actor->type = $request['type'] ?? '';
$actor->following = $request['following'] ?? '';
@ -141,25 +142,21 @@ class TypeActor {
public static function obtain_actor_info ($actor_id)
{
$client = new Client ();
$parsed_url = parse_url ($actor_id);
$url_instance = $parsed_url["scheme"] . "://" . $parsed_url["host"];
$url_path = explode ("/", $parsed_url["path"]);
$actor_name = end ($url_path);
$well_known_url = $url_instance . "/.well-known/webfinger?resource=acct:" . $actor_name . "@" . $parsed_url["host"];
$res = $client->get ($well_known_url);
$well_known = TypeActor::query_wellknown ($actor_name, $parsed_url ["host"]);
$response = json_decode ($res->getBody ()->getContents ());
foreach ($response->links as $link)
foreach ($well_known->links as $link)
{
if ($link->rel == "self")
{
$client = new Client ();
$res = $client->request ("GET", $link->href, [
"headers" => [
"Accept" => "application/activity+json"
"Accept" => "application/json"
]
]);
$actor = json_decode ($res->getBody ()->getContents (), true);
@ -172,6 +169,25 @@ class TypeActor {
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
public static function actor_exists ($actor_id)
{
@ -190,6 +206,33 @@ class TypeActor {
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)
{
$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 ("actor_id")->unique ();
$table->string ("local_actor_id")->unique ()->nullable ();
$table->string ("following")->nullable ();
$table->string ("followers")->nullable ();

View File

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

View File

@ -23,7 +23,7 @@
<div class="more-options">
<p>
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>
|
@ -34,7 +34,7 @@
<p>
My URL:
<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>
</div>
</div>
@ -42,7 +42,7 @@
<div class="url-info view-full-profile">
<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>
</div>

View File

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

View File

@ -9,7 +9,7 @@
<div class="col right">
<h1>Edit profile</h1>
<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">
<h1>{{ $user->name }}</h1>

View File

@ -1,41 +1,49 @@
@extends ("partials.layout")
@section('title', "$user->name's Profile")
@section('title', "$actor->preferredUsername's Profile")
@section('content')
<div class="row profile">
<div class="col w-40 left">
<span>
<h1>{{ $user->name }}</h1>
<h1>{{ $actor->preferredUsername }}</h1>
</span>
<div class="general-about">
<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 class="details">
<p>{{ $user->status }}</p>
<p>{{ $user->about_you }}</p>
<p class="online">
<img loading="lazy" src="/resources/img/green_person.png" alt="online"> ONLINE!
</p>
</div>
@if ($user != null)
<div class="details">
<p>{{ $user->status }}</p>
<p>{{ $user->about_you }}</p>
<p class="online">
<img loading="lazy" src="/resources/img/green_person.png" alt="online"> ONLINE!
</p>
</div>
@endif
</div>
<audio src="#" id="music" autoplay loop controls></audio>
<div class="mood">
<p><b>Mood:</b> {{ $user->mood }}</p>
<p><b>View my: <a href="#">Blog</a> | <a href="#">Bulletins</a></b></p>
@if ($user != null)
<p><b>Mood:</b> {{ $user->mood }}</p>
<p><b>View my: <a href="#">Blog</a> | <a href="#">Bulletins</a></b></p>
@endif
</div>
<div class="contact">
<div class="heading">
<h4>Contacting {{ $user->name }}</h4>
<h4>Contacting {{ $actor->preferredUsername }}</h4>
</div>
<div class="inner">
@ -101,80 +109,87 @@
<p>
<b>Federation handle:</b>
</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 class="table-section">
<div class="heading">
<h4>{{ $user->name }}'s Interests</h4>
@if ($user != null)
<div class="table-section">
<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 class="inner">
<table class="details-table" cellspacing="3" cellpadding="3">
<tbody>
@endif
<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="col right">
@auth
@if (auth()->user()->is($user))
@if ($user != null && auth()->user()->is($user))
<div class="profile-info">
<h3>
<a href="{{ route ('users.edit') }}">Edit Your Profile</a>
@ -183,6 +198,7 @@
@endif
@endauth
@if ($user != null)
<div class="blog-preview">
<h4>
{{ $user->name }}'s Latest Blog Entries [<a href="#">View Blog</a>]
@ -191,57 +207,62 @@
<i>There are no Blog Entries yet.</i>
</p>
</div>
@endif
<div class="blurbs">
<div class="heading">
<h4>
{{ $user->name }}'s Bio
{{ $actor->preferredUsername }}'s Bio
</h4>
</div>
<div class="inner">
<div class="section">
<p>{{ $user->bio }}</p>
<p>{!! $actor->summary !!}</p>
</div>
</div>
</div>
<div class="friends">
<div class="heading">
<h4>
{{ $user->name }}'s Friend Space
</h4>
<a href="#" class="more">[view all]</a>
@if ($user != null)
<div class="friends">
<div class="heading">
<h4>
{{ $actor->preferredUsername }}'s Friend Space
</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>
@endif
<div class="inner">
<p>
<b>
{{ $user->name }} has <span class="count">{{ count ($user->mutual_friends ()) }}</span> friends.
</b>
</p>
<div class="friends-grid"></div>
@if ($user != null)
<div id="comments" class="friends">
<div class="heading">
<h4>{{ $actor->preferredUsername }}'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 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>
@endif
</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/login", [ UserController::class, "do_login" ])->middleware ("guest");
// user routes
Route::get ("/user/edit", [ ProfileController::class, "edit" ])->name ("users.edit")->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";