added the blogs feature
This commit is contained in:
parent
25f00b1dc7
commit
81f0d8818f
@ -195,11 +195,12 @@ Restart nginx:
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
Now link the storage to the public folder and install the dependencies for reverb:
|
||||
Now link the storage to the public folder, install the dependencies for reverb and seed the database:
|
||||
|
||||
```bash
|
||||
php artisan storage:link
|
||||
php artisan install:broadcasting
|
||||
php artisan db:seed
|
||||
```
|
||||
|
||||
Now, we need to create three services to handle the jobs that OurSpace needs to run, another to handle the notifications' queue and another one to run Laravel Reverb. So run something `emacs /lib/systemd/system/ourspace-queue.service`, `emacs /lib/systemd/system/ourspace-notifications.service`, `emacs /lib/systemd/system/ourspace-ws.service` and add the following content:
|
||||
|
@ -115,6 +115,10 @@ class ActionsPost
|
||||
|
||||
$processed = ActionsPost::process_content_and_attachments ($request);
|
||||
|
||||
$actor = null;
|
||||
if ($request ["blog_id"])
|
||||
$actor = Actor::where ("blog_id", $request ["blog_id"])->first ();
|
||||
else
|
||||
$actor = auth ()->user ()->actor ()->first ();
|
||||
|
||||
try {
|
||||
|
31
app/Events/BlogCreatedEvent.php
Normal file
31
app/Events/BlogCreatedEvent.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Blog;
|
||||
use App\Models\User;
|
||||
|
||||
use Illuminate\Broadcasting\Channel;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PresenceChannel;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class BlogCreatedEvent
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
public Blog $blog;
|
||||
public User $user;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(Blog $blog, User $user)
|
||||
{
|
||||
$this->blog = $blog;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@ -17,25 +17,36 @@ use App\Http\Controllers\Controller;
|
||||
|
||||
class APActorController extends Controller
|
||||
{
|
||||
public function user (User $user)
|
||||
public function user ($name)
|
||||
{
|
||||
$actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
|
||||
if (!$actor)
|
||||
return response ()->json (["error" => "Actor not found"], 404)->header ("Content-Type", "application/activity+json");
|
||||
|
||||
if (str_contains (request ()->header ("Accept"), "text/html")) {
|
||||
return redirect (route ("users.show", ["user_name" => $user->name]));
|
||||
if ($actor->blog_id) {
|
||||
return redirect (route ("blogs.show", ["blog" => $actor->preferredUsername]));
|
||||
}
|
||||
|
||||
$actor = $user->actor ()->get ();
|
||||
$response = Actor::build_response ($actor->first ());
|
||||
return redirect (route ("users.show", ["user_name" => $actor->preferredUsername]));
|
||||
}
|
||||
|
||||
$response = Actor::build_response ($actor);
|
||||
return response ()->json ($response)->header ("Content-Type", "application/activity+json");
|
||||
}
|
||||
|
||||
public function followers (User $user)
|
||||
public function followers ($name)
|
||||
{
|
||||
$follower_ids = Follow::where ("object", $user->actor->id)->get ();
|
||||
$actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
|
||||
if (!$actor)
|
||||
return response ()->json (["error" => "Actor not found"], 404)->header ("Content-Type", "application/activity+json");
|
||||
|
||||
$follower_ids = Follow::where ("object", $actor->id)->get ();
|
||||
$followers = Actor::whereIn ("id", $follower_ids->pluck ("actor")->toArray ());
|
||||
|
||||
$ordered_collection = new TypeOrderedCollection ();
|
||||
$ordered_collection->collection = $followers->get ()->pluck ("actor_id")->toArray ();
|
||||
$ordered_collection->url = route ("ap.followers", $user->name);
|
||||
$ordered_collection->url = route ("ap.followers", $actor->name);
|
||||
$ordered_collection->page_size = 10;
|
||||
|
||||
if (request ()->has ("page")) {
|
||||
@ -46,14 +57,18 @@ class APActorController extends Controller
|
||||
return response ()->json ($ordered_collection->build_response_main ())->header ("Content-Type", "application/activity+json");
|
||||
}
|
||||
|
||||
public function following (User $user)
|
||||
public function following ($name)
|
||||
{
|
||||
$following_ids = Follow::where ("actor", $user->actor->id)->get ();
|
||||
$actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
|
||||
if (!$actor)
|
||||
return response ()->json (["error" => "Actor not found"], 404)->header ("Content-Type", "application/activity+json");
|
||||
|
||||
$following_ids = Follow::where ("actor", $actor->id)->get ();
|
||||
$following = Actor::whereIn ("id", $following_ids->pluck ("object")->toArray ());
|
||||
|
||||
$ordered_collection = new TypeOrderedCollection ();
|
||||
$ordered_collection->collection = $following->get ()->pluck ("actor_id")->toArray ();
|
||||
$ordered_collection->url = route ("ap.following", $user->name);
|
||||
$ordered_collection->url = route ("ap.following", $actor->name);
|
||||
$ordered_collection->page_size = 10;
|
||||
|
||||
if (request ()->has ("page")) {
|
||||
@ -64,9 +79,13 @@ class APActorController extends Controller
|
||||
return response ()->json ($ordered_collection->build_response_main ())->header ("Content-Type", "application/activity+json");
|
||||
}
|
||||
|
||||
public function featured (User $user)
|
||||
public function featured ($name)
|
||||
{
|
||||
$featured_ids = ProfilePin::where ("actor_id", $user->actor->id)->pluck ("note_id")->toArray ();
|
||||
$actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
|
||||
if (!$actor)
|
||||
return response ()->json (["error" => "Actor not found"], 404)->header ("Content-Type", "application/activity+json");
|
||||
|
||||
$featured_ids = ProfilePin::where ("actor_id", $actor->id)->pluck ("note_id")->toArray ();
|
||||
$notes = Note::whereIn ("id", $featured_ids)->get ();
|
||||
|
||||
$collection = [];
|
||||
@ -77,7 +96,7 @@ class APActorController extends Controller
|
||||
|
||||
$ordered_collection = new TypeOrderedCollection ();
|
||||
$ordered_collection->collection = $collection;
|
||||
$ordered_collection->url = route ("ap.featured", $user->name);
|
||||
$ordered_collection->url = route ("ap.featured", $actor->preferredUsername);
|
||||
$ordered_collection->page_size = 10;
|
||||
|
||||
if (request ()->has ("page")) {
|
||||
|
@ -25,8 +25,12 @@ use App\Http\Controllers\Controller;
|
||||
|
||||
class APInboxController extends Controller
|
||||
{
|
||||
public function inbox (User $user)
|
||||
public function inbox ($name)
|
||||
{
|
||||
$actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
|
||||
if (!$actor)
|
||||
return response ()->json ([ "error" => "Actor not found" ], 404);
|
||||
|
||||
$request = request ();
|
||||
$type = $request->get ("type");
|
||||
|
||||
@ -35,15 +39,15 @@ class APInboxController extends Controller
|
||||
|
||||
switch ($type) {
|
||||
case "Follow":
|
||||
$this->handle_follow ($user, $request->all ());
|
||||
$this->handle_follow ($actor, $request->all ());
|
||||
break;
|
||||
|
||||
case "Undo":
|
||||
$this->handle_undo ($user, $request->all ());
|
||||
$this->handle_undo ($actor, $request->all ());
|
||||
break;
|
||||
|
||||
case "Like":
|
||||
$this->handle_like ($user, $request->all ());
|
||||
$this->handle_like ($actor, $request->all ());
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -53,17 +57,17 @@ class APInboxController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
private function handle_follow (User $user, $activity)
|
||||
private function handle_follow (Actor $actor, $activity)
|
||||
{
|
||||
ActivityFollowEvent::dispatch ($activity);
|
||||
}
|
||||
|
||||
public function handle_undo (User $user, $activity)
|
||||
public function handle_undo (Actor $actor, $activity)
|
||||
{
|
||||
ActivityUndoEvent::dispatch ($activity);
|
||||
}
|
||||
|
||||
public function handle_like (User $user, $activity)
|
||||
public function handle_like (Actor $actor, $activity)
|
||||
{
|
||||
ActivityLikeEvent::dispatch ($activity);
|
||||
}
|
||||
|
@ -29,45 +29,47 @@ use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class APOutboxController extends Controller
|
||||
{
|
||||
public function outbox (User $user, Request $request)
|
||||
public function outbox ($name, Request $request)
|
||||
{
|
||||
$actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
|
||||
|
||||
// TODO: check we are logged in and we are the logged in user
|
||||
switch ($request->get ("type"))
|
||||
{
|
||||
case "UpdateProfile":
|
||||
return $this->handle_update_profile ($user);
|
||||
return $this->handle_update_profile ($actor);
|
||||
break;
|
||||
|
||||
case "UpdateNote":
|
||||
return $this->handle_update_note ($user, $request);
|
||||
return $this->handle_update_note ($actor, $request);
|
||||
break;
|
||||
|
||||
case "DeleteNote":
|
||||
return $this->handle_delete_note ($user, $request);
|
||||
return $this->handle_delete_note ($actor, $request);
|
||||
break;
|
||||
|
||||
case "Follow":
|
||||
return $this->handle_follow ($user, $request->get ("object"));
|
||||
return $this->handle_follow ($actor, $request->get ("object"));
|
||||
break;
|
||||
|
||||
case "Unfollow":
|
||||
return $this->handle_unfollow ($user, $request->get ("object"));
|
||||
return $this->handle_unfollow ($actor, $request->get ("object"));
|
||||
break;
|
||||
|
||||
case "Like":
|
||||
return $this->handle_like ($user, $request->get ("object"));
|
||||
return $this->handle_like ($actor, $request->get ("object"));
|
||||
break;
|
||||
|
||||
case "Boost":
|
||||
return $this->handle_boost ($user, $request->get ("object"));
|
||||
return $this->handle_boost ($actor, $request->get ("object"));
|
||||
break;
|
||||
|
||||
case "Pin":
|
||||
return $this->handle_pin ($user, $request->get ("object"));
|
||||
return $this->handle_pin ($actor, $request->get ("object"));
|
||||
break;
|
||||
|
||||
case "Post":
|
||||
return $this->handle_post ($user, $request);
|
||||
return $this->handle_post ($actor, $request);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -77,9 +79,8 @@ class APOutboxController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function handle_update_profile (User $user)
|
||||
public function handle_update_profile (Actor $actor)
|
||||
{
|
||||
$actor = $user->actor ()->first ();
|
||||
$actor_response = TypeActor::build_response ($actor);
|
||||
|
||||
$update_activity = TypeActivity::craft_update ($actor, $actor_response);
|
||||
@ -87,10 +88,8 @@ class APOutboxController extends Controller
|
||||
return response ()->json ("success", 200);
|
||||
}
|
||||
|
||||
public function handle_update_note (User $user, $request)
|
||||
public function handle_update_note (Actor $actor, $request)
|
||||
{
|
||||
$actor = $user->actor ()->first ();
|
||||
|
||||
// first check if there are new attachments
|
||||
if ($request ["attachments"])
|
||||
{
|
||||
@ -135,9 +134,8 @@ class APOutboxController extends Controller
|
||||
return response ()->json ("success", 200);
|
||||
}
|
||||
|
||||
public function handle_delete_note (User $user, $request)
|
||||
public function handle_delete_note (Actor $actor, $request)
|
||||
{
|
||||
$actor = $user->actor ()->first ();
|
||||
$note = Note::where ("id", $request ["note"])->first ();
|
||||
if (!$note)
|
||||
return response ()->json ([ "error" => "note not found" ], 404);
|
||||
@ -154,29 +152,29 @@ class APOutboxController extends Controller
|
||||
return response ()->json ("success", 200);
|
||||
}
|
||||
|
||||
public function handle_follow (User $user, string $object)
|
||||
public function handle_follow (Actor $actor, string $object)
|
||||
{
|
||||
$object_actor = Actor::where ("actor_id", $object)->first ();
|
||||
if (!$object_actor)
|
||||
return response ()->json ([ "error" => "object not found" ], 404);
|
||||
|
||||
if ($user->actor ()->first ()->actor_id == $object_actor->actor_id)
|
||||
if ($actor->actor_id == $object_actor->actor_id)
|
||||
return response ()->json ([ "error" => "cannot follow self" ], 400);
|
||||
|
||||
// check we are not following already
|
||||
$following_activity = Activity::where ("actor", $user->actor ()->first ()->actor_id)
|
||||
$following_activity = Activity::where ("actor", $actor->actor_id)
|
||||
->where ("object", '"' . str_replace ("/", "\/", $object_actor->actor_id) . '"')
|
||||
->where ("type", "Follow")
|
||||
->first ();
|
||||
if ($following_activity)
|
||||
return response ()->json ([ "error" => "already following" ], 400);
|
||||
|
||||
$follow_activity = TypeActivity::craft_follow ($user->actor ()->first (), $object_actor);
|
||||
$response = TypeActivity::post_activity ($follow_activity, $user->actor ()->first (), $object_actor);
|
||||
$follow_activity = TypeActivity::craft_follow ($actor, $object_actor);
|
||||
$response = TypeActivity::post_activity ($follow_activity, $actor, $object_actor);
|
||||
|
||||
$follow = Follow::create ([
|
||||
"activity_id" => $follow_activity->id,
|
||||
"actor" => $user->actor ()->first ()->id,
|
||||
"actor" => $actor->id,
|
||||
"object" => $object_actor->id,
|
||||
]);
|
||||
|
||||
@ -189,21 +187,21 @@ class APOutboxController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
public function handle_unfollow (User $user, string $object)
|
||||
public function handle_unfollow (Actor $actor, string $object)
|
||||
{
|
||||
$object_actor = Actor::where ("actor_id", $object)->first ();
|
||||
if (!$object_actor)
|
||||
return response ()->json ([ "error" => "object not found" ], 404);
|
||||
|
||||
$follow_activity = Activity::where ("actor", $user->actor ()->first ()->actor_id)
|
||||
$follow_activity = Activity::where ("actor", $actor->actor_id)
|
||||
->where ("object", json_encode ($object_actor->actor_id, JSON_UNESCAPED_SLASHES))
|
||||
->where ("type", "Follow")
|
||||
->first ();
|
||||
if (!$follow_activity)
|
||||
return response ()->json ([ "error" => "no follow activity found. " . $user->actor ()->first ()->actor_id . " unfollowing " . $object_actor->actor_id ], 404);
|
||||
return response ()->json ([ "error" => "no follow activity found. " . $actor->actor_id . " unfollowing " . $object_actor->actor_id ], 404);
|
||||
|
||||
$unfollow_activity = TypeActivity::craft_undo ($follow_activity, $user->actor ()->first ());
|
||||
$response = TypeActivity::post_activity ($unfollow_activity, $user->actor ()->first (), $object_actor);
|
||||
$unfollow_activity = TypeActivity::craft_undo ($follow_activity, $actor);
|
||||
$response = TypeActivity::post_activity ($unfollow_activity, $actor, $object_actor);
|
||||
|
||||
// TODO: Check if it was successfully sent
|
||||
/* if (!$response || $response->getStatusCode () < 200 || $response->getStatusCode () >= 300)
|
||||
@ -216,13 +214,12 @@ class APOutboxController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
public function handle_like (User $user, $request)
|
||||
public function handle_like (Actor $actor, $request)
|
||||
{
|
||||
$object = Note::where ("note_id", $request)->first ();
|
||||
if (!$object)
|
||||
return response ()->json ([ "error" => "object not found" ], 404);
|
||||
|
||||
$actor = $user->actor ()->first ();
|
||||
$already_liked = $actor->liked_note ($object);
|
||||
if ($already_liked)
|
||||
{
|
||||
@ -262,13 +259,12 @@ class APOutboxController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
public function handle_boost (User $user, $object)
|
||||
public function handle_boost (Actor $actor, $object)
|
||||
{
|
||||
$object = Note::where ("note_id", $object)->first ();
|
||||
if (!$object)
|
||||
return response ()->json ([ "error" => "object not found" ], 404);
|
||||
|
||||
$actor = $user->actor ()->first ();
|
||||
$already_boosted = $actor->boosted_note ($object);
|
||||
if ($already_boosted)
|
||||
{
|
||||
@ -302,13 +298,12 @@ class APOutboxController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
public function handle_pin (User $user, $object)
|
||||
public function handle_pin (Actor $actor, $object)
|
||||
{
|
||||
$object = Note::where ("note_id", $object)->first ();
|
||||
if (!$object)
|
||||
return response ()->json ([ "error" => "object not found" ], 404);
|
||||
|
||||
$actor = $user->actor ()->first ();
|
||||
$already_pinned = $object->is_pinned ($actor);
|
||||
if ($already_pinned)
|
||||
{
|
||||
@ -342,9 +337,8 @@ class APOutboxController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
public function handle_post (User $user, $request)
|
||||
public function handle_post (Actor $actor, $request)
|
||||
{
|
||||
$actor = $user->actor ()->first ();
|
||||
$note = TypeNote::craft_from_outbox ($actor, $request);
|
||||
|
||||
if (isset ($request ["attachments"]))
|
||||
@ -417,6 +411,18 @@ class APOutboxController extends Controller
|
||||
}
|
||||
|
||||
$note->visibility = $request ["visibility"];
|
||||
|
||||
// if the parent note is not public, responses shouldn't be either
|
||||
if ($request ["inReplyTo"])
|
||||
{
|
||||
$parent_note = TypeNote::note_exists($request ["inReplyTo"]);
|
||||
if ($parent_note)
|
||||
{
|
||||
$note->to = $parent_note->to;
|
||||
$note->cc = $parent_note->cc;
|
||||
$note->visibility = $parent_note->visibility;
|
||||
}
|
||||
}
|
||||
$note->save ();
|
||||
|
||||
$create_activity = TypeActivity::craft_create ($actor, $note);
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\AP;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Blog;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@ -32,6 +33,8 @@ class APWebfingerController extends Controller
|
||||
$user = $user[0];
|
||||
$actual_user = User::where ("name", $user)->first ();
|
||||
if (!isset ($actual_user)) {
|
||||
$actual_user = Blog::where ("slug", $user)->first ();
|
||||
if (!$actual_user)
|
||||
return response ()->json ([ "error" => "user not found" ], 404);
|
||||
}
|
||||
|
||||
|
94
app/Http/Controllers/BlogController.php
Normal file
94
app/Http/Controllers/BlogController.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Blog;
|
||||
use App\Events\BlogCreatedEvent;
|
||||
|
||||
use App\Models\BlogCategory;
|
||||
|
||||
use App\Helpers\PaginationHelper;
|
||||
|
||||
use Intervention\Image\ImageManager;
|
||||
use Intervention\Image\Drivers\Gd\Driver;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BlogController extends Controller
|
||||
{
|
||||
public function index ()
|
||||
{
|
||||
$categories = BlogCategory::all ();
|
||||
$user = null;
|
||||
|
||||
if (auth ()->check ())
|
||||
$user = auth ()->user ();
|
||||
|
||||
$blogs = Blog::orderBy ("created_at", "desc")->paginate (10);
|
||||
|
||||
return view ("blogs", compact ("user", "blogs", "categories"));
|
||||
}
|
||||
|
||||
public function create ()
|
||||
{
|
||||
$categories = BlogCategory::all ();
|
||||
|
||||
return view ("blogs.create", compact ("categories"));
|
||||
}
|
||||
|
||||
public function store (Request $request)
|
||||
{
|
||||
if (!auth ()->check ())
|
||||
return redirect ()->route ("login")->with ("error", "You must be logged in to create a blog.");
|
||||
|
||||
$request->validate ([
|
||||
"name" => "required|unique:users|unique:blogs",
|
||||
"description" => "required",
|
||||
"icon" => "required|image|max:4096",
|
||||
"category" => "required"
|
||||
]);
|
||||
|
||||
$user = auth ()->user ();
|
||||
|
||||
$category = BlogCategory::find ($request->category);
|
||||
if (!$category)
|
||||
return redirect ()->route ("blogs.create")->with ("error", "Invalid category selected.");
|
||||
|
||||
$icon = null;
|
||||
$fname = $user->id . "-" . uniqid();
|
||||
if ($request->icon)
|
||||
{
|
||||
$manager = new ImageManager (new Driver ());
|
||||
$image = $manager->read ($request->file ("icon"));
|
||||
$image_data = $image->cover (256, 256)->toJpeg ();
|
||||
Storage::disk ("public")->put ("blog_icons/" . $fname . ".jpg", $image_data);
|
||||
}
|
||||
|
||||
$blog = Blog::create ([
|
||||
"name" => $request ["name"],
|
||||
"slug" => Str::slug ($request ["name"]),
|
||||
"description" => Str::markdown($request ["description"]),
|
||||
"icon" => $fname . ".jpg",
|
||||
"user_id" => $user->id,
|
||||
"blog_category_id" => $category->id
|
||||
]);
|
||||
|
||||
BlogCreatedEvent::dispatch ($blog, $user);
|
||||
|
||||
return redirect ()->route ("blogs.show", [ 'blog' => $blog->slug ])->with ("success", "Blog created successfully!");
|
||||
}
|
||||
|
||||
public function show (Blog $blog)
|
||||
{
|
||||
$notes = PaginationHelper::paginate ($blog->notes ()->orderBy ("created_at", "desc")->get (), 10);
|
||||
|
||||
return view ("blogs.show", compact ("blog", "notes"));
|
||||
}
|
||||
|
||||
public function new_entry (Blog $blog)
|
||||
{
|
||||
return view ("blogs.new_entry", compact ("blog"));
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ use App\Models\User;
|
||||
use App\Models\Actor;
|
||||
use App\Models\Note;
|
||||
use App\Models\Hashtag;
|
||||
use App\Models\BlogCategory;
|
||||
|
||||
use App\Helpers\PaginationHelper;
|
||||
|
||||
|
@ -106,8 +106,7 @@ class PostController extends Controller
|
||||
if (!auth ()->check ())
|
||||
return back ()->with ("error", "You need to be logged in to pin a post.");
|
||||
|
||||
$user = auth ()->user ();
|
||||
$actor = $user->actor ()->first ();
|
||||
$actor = $note->get_actor ()->first ();
|
||||
|
||||
$response = ActionsPost::pin_post ($actor, $note);
|
||||
|
||||
|
@ -11,6 +11,7 @@ use Intervention\Image\Drivers\Gd\Driver;
|
||||
use App\Models\User;
|
||||
use App\Models\Actor;
|
||||
use App\Models\Note;
|
||||
use App\Models\Blog;
|
||||
|
||||
use App\Actions\ActionsUser;
|
||||
use App\Helpers\PaginationHelper;
|
||||
@ -168,8 +169,9 @@ class ProfileController extends Controller
|
||||
$ids = $user->mutual_friends ();
|
||||
if (request ()->get ("query"))
|
||||
{
|
||||
$query = request ()->get ("query");
|
||||
$friends = Actor::whereIn ("actor_id", $ids)
|
||||
->where ("preferredUsername", "like", "%" . request ()->get ("query") . "%")
|
||||
->where ("preferredUsername", "like", "%" . $query . "%")
|
||||
->get ();
|
||||
}
|
||||
else
|
||||
@ -231,4 +233,20 @@ class ProfileController extends Controller
|
||||
|
||||
return view ("users.notifications", compact ("user", "notifications", "processed_notifications", "unread_notifications"));
|
||||
}
|
||||
|
||||
public function blogs ($user_name)
|
||||
{
|
||||
if (str_starts_with ($user_name, "@"))
|
||||
{
|
||||
return redirect ()->route ("users.show", [ "user_name" => $user_name ]);
|
||||
}
|
||||
|
||||
$user = User::where ("name", $user_name)->first ();
|
||||
if (!$user)
|
||||
return redirect ()->route ("home");
|
||||
|
||||
$blogs = Blog::where ("user_id", $user->id)->orderBy ("created_at", "desc")->get ();
|
||||
|
||||
return view ("users.blogs", compact ("user", "blogs"));
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class UserController extends Controller
|
||||
public function do_signup (Request $request)
|
||||
{
|
||||
$incoming_fields = $request->validate ([
|
||||
"name" => "required|alpha_dash",
|
||||
"name" => "required|alpha_dash|unique:users|unique:blogs",
|
||||
"email" => "required|email|unique:users",
|
||||
"password" => "required|confirmed"
|
||||
]);
|
||||
|
36
app/Listeners/BlogCreatedListener.php
Normal file
36
app/Listeners/BlogCreatedListener.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Models\Actor;
|
||||
use App\Types\TypeActor;
|
||||
|
||||
use App\Events\BlogCreatedEvent;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
class BlogCreatedListener
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*/
|
||||
public function handle(BlogCreatedEvent $event): void
|
||||
{
|
||||
$actor = new Actor ();
|
||||
$actor = $actor->create_from_blog ($event->blog);
|
||||
$actor->blog_id = $event->blog->id;
|
||||
$actor->user_id = $event->user->id;
|
||||
$actor->save ();
|
||||
|
||||
$event->blog->actor_id = $actor->id;
|
||||
$event->blog->save ();
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App\Models\User;
|
||||
use App\Models\Announcement;
|
||||
use App\Models\Note;
|
||||
use App\Models\Blog;
|
||||
|
||||
use App\Helpers\PaginationHelper;
|
||||
|
||||
@ -19,6 +20,7 @@ class Actor extends Model
|
||||
|
||||
"type",
|
||||
"actor_id",
|
||||
"blog_id",
|
||||
"local_actor_id",
|
||||
|
||||
"following",
|
||||
@ -57,6 +59,11 @@ class Actor extends Model
|
||||
return $this->belongsTo (User::class);
|
||||
}
|
||||
|
||||
public function blog ()
|
||||
{
|
||||
return $this->belongsTo (Blog::class);
|
||||
}
|
||||
|
||||
public function profile_attachment ()
|
||||
{
|
||||
return $this->hasMany (ProfileAttachment::class);
|
||||
@ -84,6 +91,12 @@ class Actor extends Model
|
||||
return $this->create ($data);
|
||||
}
|
||||
|
||||
public function create_from_blog (Blog $blog)
|
||||
{
|
||||
$data = TypeActor::create_from_blog ($blog);
|
||||
return $this->create ($data);
|
||||
}
|
||||
|
||||
public static function build_response (Actor $actor)
|
||||
{
|
||||
return TypeActor::build_response ($actor);
|
||||
|
38
app/Models/Blog.php
Normal file
38
app/Models/Blog.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Blog extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
"name",
|
||||
"slug",
|
||||
"description",
|
||||
"icon",
|
||||
"user_id",
|
||||
"actor_id",
|
||||
"blog_category_id"
|
||||
];
|
||||
|
||||
public function user ()
|
||||
{
|
||||
return $this->belongsTo (User::class);
|
||||
}
|
||||
|
||||
public function actor ()
|
||||
{
|
||||
return $this->belongsTo (Actor::class);
|
||||
}
|
||||
|
||||
public function notes ()
|
||||
{
|
||||
return $this->hasMany (Note::class, "actor_id", "actor_id");
|
||||
}
|
||||
|
||||
public function pinned_notes ()
|
||||
{
|
||||
return $this->hasMany (ProfilePin::class, "actor_id", "actor_id");
|
||||
}
|
||||
}
|
13
app/Models/BlogCategory.php
Normal file
13
app/Models/BlogCategory.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class BlogCategory extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
"name",
|
||||
"slug"
|
||||
];
|
||||
}
|
@ -11,4 +11,19 @@ class ProfilePin extends Model
|
||||
"note_id",
|
||||
"actor_id"
|
||||
];
|
||||
|
||||
public function activity ()
|
||||
{
|
||||
return $this->belongsTo (Activity::class);
|
||||
}
|
||||
|
||||
public function note ()
|
||||
{
|
||||
return $this->belongsTo (Note::class);
|
||||
}
|
||||
|
||||
public function actor ()
|
||||
{
|
||||
return $this->belongsTo (Actor::class);
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,11 @@ class User extends Authenticatable
|
||||
return Cache::has ("user-online-" . $this->id);
|
||||
}
|
||||
|
||||
public function blogs ()
|
||||
{
|
||||
return $this->hasMany (Blog::class);
|
||||
}
|
||||
|
||||
public function mutual_friends ()
|
||||
{
|
||||
$followers = Follow::where ("actor", $this->actor->id)->pluck ("object")->toArray ();
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Types;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Blog;
|
||||
use App\Models\Actor;
|
||||
use App\Models\ProfileAttachment;
|
||||
use App\Models\Instance;
|
||||
@ -62,6 +63,40 @@ class TypeActor {
|
||||
];
|
||||
}
|
||||
|
||||
public static function create_from_blog (Blog $blog)
|
||||
{
|
||||
$keys = TypeActor::gen_keys ();
|
||||
$app_url = env ("APP_URL");
|
||||
|
||||
return [
|
||||
"blog_id" => $blog->id,
|
||||
|
||||
"type" => "Person",
|
||||
"actor_id" => $app_url . "/ap/v1/user/" . $blog->slug,
|
||||
|
||||
"inbox" => $app_url . "/ap/v1/user/" . $blog->slug . "/inbox",
|
||||
"outbox" => $app_url . "/ap/v1/user/" . $blog->slug . "/outbox",
|
||||
|
||||
"following" => $app_url . "/ap/v1/user/" . $blog->slug . "/following",
|
||||
"followers" => $app_url . "/ap/v1/user/" . $blog->slug . "/followers",
|
||||
|
||||
"liked" => $app_url . "/ap/v1/user/" . $blog->slug . "/liked",
|
||||
"featured" => $app_url . "/ap/v1/user/" . $blog->slug . "/collections/featured",
|
||||
"featured_tags" => $app_url . "/ap/v1/user/" . $blog->slug . "/collections/featured/tags",
|
||||
|
||||
"sharedInbox" => $app_url . "/ap/v1/inbox",
|
||||
|
||||
"preferredUsername" => $blog->slug,
|
||||
"name" => $blog->name,
|
||||
"summary" => $blog->description,
|
||||
|
||||
"icon" => $app_url . "/storage/blog_icons/" . $blog->icon,
|
||||
|
||||
"public_key" => $keys["public_key"]["key"],
|
||||
"private_key" => $keys["private_key"]
|
||||
];
|
||||
}
|
||||
|
||||
public static function build_response (Actor $actor)
|
||||
{
|
||||
$response = [
|
||||
@ -122,7 +157,7 @@ class TypeActor {
|
||||
]
|
||||
];
|
||||
|
||||
if ($actor->user)
|
||||
if ($actor->user && !$actor->blog_id)
|
||||
{
|
||||
// appent to @context
|
||||
$response ["@context"][] = [
|
||||
|
39
database/migrations/2025_01_12_225005_create_blogs_table.php
Normal file
39
database/migrations/2025_01_12_225005_create_blogs_table.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('blogs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string ("name")->unique ();
|
||||
$table->string ("slug")->unique ();
|
||||
|
||||
$table->text ("description")->nullable ();
|
||||
|
||||
$table->string ("icon")->nullable ();
|
||||
|
||||
$table->foreignId ("user_id")->nullable ()->constrained ()->onDelete ("cascade");
|
||||
$table->foreignId ("actor_id")->nullable ()->constrained ()->onDelete ("cascade");
|
||||
$table->foreignId ("blog_category_id")->nullable ()->constrained ();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('blogs');
|
||||
}
|
||||
};
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('blog_categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string ("name")->unique ();
|
||||
$table->string ("slug")->unique ();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('blog_categories');
|
||||
}
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('actors', function (Blueprint $table) {
|
||||
$table->foreignId ("blog_id")->nullable ()->after ("user_id")->constrained ()->onDelete ("cascade");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('actors', function (Blueprint $table) {
|
||||
$table->dropForeign (["blog_id"]);
|
||||
$table->dropColumn ("blog_id");
|
||||
});
|
||||
}
|
||||
};
|
95
database/seeders/BlogCategorySeeder.php
Normal file
95
database/seeders/BlogCategorySeeder.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
|
||||
class BlogCategorySeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$categories = [
|
||||
[
|
||||
"name" => "Art",
|
||||
"slug" => "art"
|
||||
],
|
||||
[
|
||||
"name" => "Automotive",
|
||||
"slug" => "automotive"
|
||||
],
|
||||
[
|
||||
"name" => "Fashion",
|
||||
"slug" => "fashion"
|
||||
],
|
||||
[
|
||||
"name" => "Financial",
|
||||
"slug" => "financial"
|
||||
],
|
||||
[
|
||||
"name" => "Food",
|
||||
"slug" => "food"
|
||||
],
|
||||
[
|
||||
"name" => "Games",
|
||||
"slug" => "games"
|
||||
],
|
||||
[
|
||||
"name" => "Life",
|
||||
"slug" => "life"
|
||||
],
|
||||
[
|
||||
"name" => "Literature",
|
||||
"slug" => "literature"
|
||||
],
|
||||
[
|
||||
"name" => "Math & Science",
|
||||
"slug" => "math-science"
|
||||
],
|
||||
[
|
||||
"name" => "Movies & TV",
|
||||
"slug" => "movies-tv"
|
||||
],
|
||||
[
|
||||
"name" => "Music",
|
||||
"slug" => "music"
|
||||
],
|
||||
[
|
||||
"name" => "Paranormal",
|
||||
"slug" => "paranormal"
|
||||
],
|
||||
[
|
||||
"name" => "Politics",
|
||||
"slug" => "politics"
|
||||
],
|
||||
[
|
||||
"name" => "Humanity",
|
||||
"slug" => "humanity"
|
||||
],
|
||||
[
|
||||
"name" => "Romance",
|
||||
"slug" => "romance"
|
||||
],
|
||||
[
|
||||
"name" => "Sports",
|
||||
"slug" => "sports"
|
||||
],
|
||||
[
|
||||
"name" => "Technology",
|
||||
"slug" => "technology"
|
||||
],
|
||||
[
|
||||
"name" => "Travel",
|
||||
"slug" => "travel"
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($categories as $category) {
|
||||
DB::table("blog_categories")->insert($category);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,9 +15,8 @@ class DatabaseSeeder extends Seeder
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
$this->call ([
|
||||
BlogCategorySeeder::class
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
85
resources/views/blogs.blade.php
Normal file
85
resources/views/blogs.blade.php
Normal file
@ -0,0 +1,85 @@
|
||||
@extends ("partials.layout")
|
||||
|
||||
@section ("title", "Blogs")
|
||||
|
||||
@section ("content")
|
||||
<div class="row blog-category">
|
||||
<div class="col w-20 left">
|
||||
<div class="category-list">
|
||||
<b>View:</b>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{{ route ('blogs') }}">
|
||||
<img loading="lazy" src="/resources/icons/clock.png" class="icon">
|
||||
<b>Recent Blogs</b>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{{-- TODO: Top entries and blogs I'm following --}}
|
||||
</ul>
|
||||
|
||||
<b>Categories:</b>
|
||||
<ul>
|
||||
@foreach ($categories as $category)
|
||||
<li>
|
||||
<a href="#">{{ $category->name }}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col right">
|
||||
<h1>Blogs</h1>
|
||||
|
||||
@auth
|
||||
<div class="blog-preview">
|
||||
<h3>
|
||||
[
|
||||
<a href="{{ route ('blogs.create') }}">Create a blog</a>
|
||||
]
|
||||
</h3>
|
||||
<h3>
|
||||
[
|
||||
<a href="#">View your blogs</a>
|
||||
]
|
||||
</h3>
|
||||
</div>
|
||||
@endauth
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>Latest Blogs</h3>
|
||||
<div class="blog-entries">
|
||||
@foreach ($blogs as $blog)
|
||||
<div class="entry">
|
||||
<p class="publish-date">
|
||||
<time class="ago">{{ $blog->created_at->diffForHumans () }}</time>
|
||||
— by <a href="{{ route ('users.show', [ 'user_name' => $blog->user->name ]) }}">{{ $blog->user->name }}</a>
|
||||
— <a>{{ count ($blog->notes) }} Posts</a>
|
||||
</p>
|
||||
|
||||
<div class="inner">
|
||||
<h3 class="title">
|
||||
<a href="{{ route ('blogs.show', [ 'blog' => $blog ]) }}">
|
||||
{{ $blog->name }}
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
{!! $blog->description !!}
|
||||
</p>
|
||||
|
||||
<a href="{{ route ('blogs.show', [ 'blog' => $blog ]) }}">
|
||||
» Read more
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
{{ $blogs->links ("pagination::default") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
52
resources/views/blogs/create.blade.php
Normal file
52
resources/views/blogs/create.blade.php
Normal file
@ -0,0 +1,52 @@
|
||||
@extends ("partials.layout")
|
||||
|
||||
@section ("title", "Create a new blog")
|
||||
|
||||
@section ("content")
|
||||
<div class="row edit-blog-entry">
|
||||
<div class="col w-20 left">
|
||||
<div class="edit-info">
|
||||
<p>You can use markdown in the description of your blog!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col right">
|
||||
<h1>Create Blog</h1>
|
||||
<br>
|
||||
|
||||
<form method="POST" class="ctrl-enter-submit" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" name="name" id="name" value="{{ old ('name') }}" required>
|
||||
@error("name")
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
<label for="description">Description:</label>
|
||||
<textarea name="description" id="description">{{ old ("description") }}</textarea>
|
||||
@error("description")
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
<label for="icon">Logo:</label>
|
||||
<input type="file" name="icon" id="icon" accept="image/*" required>
|
||||
@error("icon")
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
<br>
|
||||
<label for="category">Category:</label>
|
||||
<select name="category" id="category">
|
||||
@foreach ($categories as $category)
|
||||
<option value="{{ $category->id }}">{{ $category->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<div class="publish">
|
||||
<button type="submit" name="submit">Create Blog!</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
56
resources/views/blogs/new_entry.blade.php
Normal file
56
resources/views/blogs/new_entry.blade.php
Normal file
@ -0,0 +1,56 @@
|
||||
@extends ("partials.layout")
|
||||
|
||||
@section ("title", "Create New Entry")
|
||||
|
||||
@section ("content")
|
||||
<div class="row edit-blog-entry">
|
||||
<div class="col w-20 left">
|
||||
<div class="edit-info">
|
||||
<p>You can use Markdown in the content of your entry!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col right">
|
||||
<h1>New Entry</h1>
|
||||
<br>
|
||||
|
||||
<form method="POST" class="ctrl-enter-submit" enctype="multipart/form-data" action="{{ route ('user.post.new') }}">
|
||||
@csrf
|
||||
|
||||
<input type="hidden" name="blog_id" value="{{ $blog->id }}">
|
||||
|
||||
<label for="summary">Title:</label>
|
||||
<input type="text" name="summary" id="summary" value="{{ old ('summary') }}" placeholder="A really cool post!" required>
|
||||
@error("summary")
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
<br>
|
||||
|
||||
<textarea name="content" placeholder="What's on your mind?">{{ old ('content') }}</textarea>
|
||||
<small>Markdown is supported</small>
|
||||
@error("content")
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
<br>
|
||||
|
||||
<label for="files">Attachments:</label><br>
|
||||
<input type="file" name="files[]" accept="image/*" id="files" multiple><br>
|
||||
@error("files.*")
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
<br>
|
||||
|
||||
<label for="visibility">Visibility</label>
|
||||
<select name="visibility">
|
||||
<option value="public">Public</option>
|
||||
<option value="followers">Friends only</option>
|
||||
<option value="private">Private</option>
|
||||
</select>
|
||||
<br><br>
|
||||
|
||||
<button type="submit">Post!</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
88
resources/views/blogs/show.blade.php
Normal file
88
resources/views/blogs/show.blade.php
Normal file
@ -0,0 +1,88 @@
|
||||
@extends ("partials.layout")
|
||||
|
||||
@section ("title", $blog->name)
|
||||
|
||||
@section ("content")
|
||||
<div class="row profile">
|
||||
<div class="col w-30 left">
|
||||
<h1>
|
||||
{{ $blog->name }}
|
||||
</h1>
|
||||
|
||||
<div class="general-about">
|
||||
<div class="profile-pic">
|
||||
<img loading="lazy" src="{{ $blog->actor->icon }}" alt="{{ $blog->name }}" class="pfp-fallback">
|
||||
</div>
|
||||
|
||||
<div class="details below">
|
||||
@if ($blog->user->is_online ())
|
||||
<p class="online">
|
||||
<img loading="lazy" src="/resources/img/green_person.png"> ONLINE!
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mood">
|
||||
<p><b>Mood:</b> {{ $blog->user->mood }}</p>
|
||||
<br>
|
||||
<p>
|
||||
<b>View my: <a href="{{ route ('users.show', [ 'user_name' => $blog->user->name ]) }}">Profile</a></b>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="url-info">
|
||||
<p>
|
||||
<b>
|
||||
Federation Handle:
|
||||
</b>
|
||||
</p>
|
||||
<p>@php echo "@" . $blog->slug . "@" . explode ("/", env ("APP_URL"))[2] @endphp</p>
|
||||
</div>
|
||||
|
||||
<div class="url-info view-full-profile">
|
||||
<p>
|
||||
<a href="{{ route ('users.show', [ 'user_name' => $blog->user->name ]) }}">
|
||||
<b>View Full Profile</b>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col right">
|
||||
<div class="blog-preview">
|
||||
<h1>
|
||||
{{ $blog->name }}'s Blog Entries
|
||||
</h1>
|
||||
|
||||
<div class="blog-preview">
|
||||
<h3>
|
||||
[
|
||||
<a href="{{ route ('blogs.new_entry', [ 'blog' => $blog->slug ]) }}">
|
||||
New Entry
|
||||
</a>
|
||||
]
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="blog-entries">
|
||||
<h3>Pinned</h3>
|
||||
|
||||
@foreach ($blog->pinned_notes as $note)
|
||||
<x-blog_entry_block :note="$note->note" />
|
||||
@endforeach
|
||||
|
||||
<hr>
|
||||
|
||||
@foreach ($notes as $note)
|
||||
<x-blog_entry_block :note="$note" />
|
||||
@endforeach
|
||||
|
||||
{{ $notes->links () }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
20
resources/views/components/blog_entry_block.blade.php
Normal file
20
resources/views/components/blog_entry_block.blade.php
Normal file
@ -0,0 +1,20 @@
|
||||
<div class="entry">
|
||||
<p class="publish-date">
|
||||
<time class="ago">
|
||||
{{ $note->created_at->diffForHumans () }}
|
||||
</time>
|
||||
</p>
|
||||
|
||||
<div class="inner">
|
||||
<h3 class="title">
|
||||
<a href="{{ route ('posts.show', [ 'note' => $note ]) }}">
|
||||
{{ $note->summary }}
|
||||
</a>
|
||||
</h3>
|
||||
<p>
|
||||
<a href="{{ route ('posts.show', [ 'note' => $note ]) }}">
|
||||
» View Blog Entry
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
@ -17,7 +17,9 @@ if (!$actor)
|
||||
return;
|
||||
}
|
||||
|
||||
if ($actor->user_id)
|
||||
if ($actor->blog_id)
|
||||
$actor_url = route ('blogs.show', [ 'blog' => $actor->blog->slug ]);
|
||||
else if ($actor->user_id)
|
||||
$actor_url = route ('users.show', [ 'user_name' => $actor->user->name ]);
|
||||
else
|
||||
$actor_url = route ('users.show', [ 'user_name' => $actor->local_actor_id ]);
|
||||
@ -35,11 +37,7 @@ if (!$display_post->can_view ())
|
||||
</a>
|
||||
<a href="{{ $actor_url }}">
|
||||
<p>
|
||||
@if ($actor->user)
|
||||
<img loading="lazy" src="{{ $actor->user->avatar }}" class="pfp-fallback" width="50">
|
||||
@else
|
||||
<img loading="lazy" src="{{ $actor->icon }}" class="pfp-fallback" width="50">
|
||||
@endif
|
||||
</p>
|
||||
</a>
|
||||
</td>
|
||||
|
@ -13,14 +13,14 @@
|
||||
<small>Markdown is supported</small>
|
||||
<br>
|
||||
<input type="file" name="files[]" accept="image/*" multiple><br>
|
||||
<p>
|
||||
<div>
|
||||
<b>Visibility:</b>
|
||||
<select name="visibility">
|
||||
<option value="public">Public</option>
|
||||
<option value="followers">Friends only</option>
|
||||
<option value="private">Mentioned Only</option>
|
||||
</select>
|
||||
</p>
|
||||
</div>
|
||||
<button type="submit">Post</button>
|
||||
|
||||
@error ("content")
|
||||
|
@ -25,7 +25,7 @@
|
||||
View My
|
||||
<a href="{{ route('users.show', ['user_name' => auth()->user()->name]) }}">Profile</a>
|
||||
|
|
||||
<a href="#">Blog</a>
|
||||
<a href="{{ route ('users.blogs', [ 'user_name' => auth ()->user ()->name ]) }}">Blog</a>
|
||||
|
|
||||
<a href="#">Bulletins</a>
|
||||
|
|
||||
|
@ -47,7 +47,11 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#"> Blog </a>
|
||||
@auth
|
||||
<a href="{{ route ('blogs') }}"> Blog </a>
|
||||
@else
|
||||
<a href="{{ route ('login') }}"> Blog</a>
|
||||
@endauth
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
@ -1,3 +1,20 @@
|
||||
@php
|
||||
$user_url = null;
|
||||
|
||||
if ($actor->blog_id)
|
||||
{
|
||||
$user_url = route ('blogs.show', [ 'blog' => $actor->blog->slug ]);
|
||||
}
|
||||
else if ($actor->user_id)
|
||||
{
|
||||
$user_url = route ('users.show', [ 'user_name' => $actor->user->name ]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$user_url = route ('users.show', [ 'user_name' => $actor->local_actor_id ]);
|
||||
}
|
||||
@endphp
|
||||
|
||||
@extends("partials.layout")
|
||||
|
||||
@section ("title", "View Post")
|
||||
@ -8,14 +25,14 @@
|
||||
<div class="col w-20 left">
|
||||
<div class="edit-info">
|
||||
<div class="profile-pic">
|
||||
<img loading="lazy" src="{{ $actor->user ? $actor->user->avatar : $actor->icon }}" class="pfp-fallback">
|
||||
<img loading="lazy" src="{{ $actor->icon }}" class="pfp-fallback">
|
||||
</div>
|
||||
|
||||
<div class="author-details">
|
||||
<h4>
|
||||
Published by
|
||||
<span>
|
||||
<a href="{{ route ('users.show', [ 'user_name' => $actor->user ? $actor->user->name : $actor->local_actor_id ]) }}">
|
||||
<a href="{{ $user_url }}">
|
||||
{{ $actor->name }}
|
||||
</a>
|
||||
</span>
|
||||
@ -29,9 +46,9 @@
|
||||
</p>
|
||||
|
||||
<p class="links">
|
||||
<a href="{{ route ('users.show', [ 'user_name' => $actor->user ? $actor->user->name : $actor->local_actor_id ]) }}">
|
||||
<a href="{{ $user_url }}">
|
||||
<img loading="lazy" src="/resources/icons/user.png" class="icon">
|
||||
<span class="m-hide">View</span> Profile
|
||||
<span class="m-hide">View</span> {{ $actor->blog_id ? "Blog" : "Profile" }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
42
resources/views/users/blogs.blade.php
Normal file
42
resources/views/users/blogs.blade.php
Normal file
@ -0,0 +1,42 @@
|
||||
@extends ("partials.layout")
|
||||
|
||||
@section ("title", $user->name . "'s Blogs")
|
||||
|
||||
@section ("content")
|
||||
<div class="simple-container">
|
||||
<h1>{{ $user->name }}'s Blogs</h1>
|
||||
<p>
|
||||
<a href="{{ route ('users.show', [ 'user_name' => $user->name ]) }}">« Back to profile</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="blog-entries">
|
||||
@foreach ($blogs as $blog)
|
||||
<div class="entry">
|
||||
<p class="publish-date">
|
||||
<time class="ago">{{ $blog->created_at->diffForHumans () }}</time>
|
||||
— by <a href="{{ route ('users.show', [ 'user_name' => $blog->user->name ]) }}">{{ $blog->user->name }}</a>
|
||||
— <a>{{ count ($blog->notes) }} Posts</a>
|
||||
</p>
|
||||
|
||||
<div class="inner">
|
||||
<h3 class="title">
|
||||
<a href="{{ route ('blogs.show', [ 'blog' => $blog ]) }}">
|
||||
{{ $blog->name }}
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
{!! $blog->description !!}
|
||||
</p>
|
||||
|
||||
<a href="{{ route ('blogs.show', [ 'blog' => $blog ]) }}">
|
||||
» Read more
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
@ -39,7 +39,11 @@
|
||||
@elseif ($notification ['type'] == 'Follow')
|
||||
<p>Followed you</p>
|
||||
@elseif ($notification ['type'] == 'Unfollow')
|
||||
<p>Unfollowed you</p>
|
||||
@if ($notification ["object"]->id == auth ()->user ()->id)
|
||||
<p>You unfollowed you</p>
|
||||
@else
|
||||
<p>Unfollowed <b>{{ $notification ['object']->name }}</b></p>
|
||||
@endif
|
||||
@elseif ($notification ['type'] == 'Boost')
|
||||
<p>Boosted this <b><a href="{{ route ('posts.show', ['note' => $notification['object']->id]) }}">post</a></b></p>
|
||||
@elseif ($notification ['type'] == 'Like')
|
||||
|
@ -50,7 +50,7 @@
|
||||
<div class="mood">
|
||||
@if ($user != null)
|
||||
<p><b>Mood:</b> {{ $user->mood }}</p>
|
||||
<p><b>View my: <a href="#">Blog</a> | <a href="#">Bulletins</a></b></p>
|
||||
<p><b>View my: <a href="{{ route ('users.blogs', [ 'user_name' => $user->name ]) }}">Blog</a> | <a href="#">Bulletins</a></b></p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@ -280,10 +280,9 @@
|
||||
@if ($user != null)
|
||||
<div class="blog-preview">
|
||||
<h4>
|
||||
{{ $user->name }}'s Latest Blog Entries [<a href="#">View Blog</a>]
|
||||
{{ $user->name }}'s Latest Blog Entries [<a href="{{ route ('users.blogs', [ 'user_name' => $user->name ]) }}">View Blog</a>]
|
||||
</h4>
|
||||
<p>
|
||||
<i>There are no Blog Entries yet.</i>
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
@ -19,15 +19,16 @@ Route::get ("/.well-known/nodeinfo/2.1", [ APNodeInfoController::class, "nodeinf
|
||||
|
||||
Route::prefix ("/ap/v1")->group (function () {
|
||||
// users
|
||||
Route::post ("/user/{user:name}/inbox", [ APInboxController::class, "inbox" ])->name ("ap.inbox");
|
||||
Route::post ("/user/{user:name}/outbox", [ APOutboxController::class, "outbox" ])->name ("ap.outbox");
|
||||
Route::get ("/user/{user:name}/followers", [ APActorController::class, "followers" ])->name ("ap.followers");
|
||||
Route::get ("/user/{user:name}/following", [ APActorController::class, "following" ])->name ("ap.following");
|
||||
Route::get ("/user/{user:name}/collections/featured", [ APActorController::class, "featured" ])->name ("ap.featured");
|
||||
Route::get ("/user/{user:name}", [ APActorController::class, "user" ])->name ("ap.user");
|
||||
Route::post ("/user/{name}/inbox", [ APInboxController::class, "inbox" ])->name ("ap.inbox");
|
||||
Route::post ("/user/{name}/outbox", [ APOutboxController::class, "outbox" ])->name ("ap.outbox");
|
||||
Route::get ("/user/{name}/followers", [ APActorController::class, "followers" ])->name ("ap.followers");
|
||||
Route::get ("/user/{name}/following", [ APActorController::class, "following" ])->name ("ap.following");
|
||||
Route::get ("/user/{name}/collections/featured", [ APActorController::class, "featured" ])->name ("ap.featured");
|
||||
Route::get ("/user/{name}", [ APActorController::class, "user" ])->name ("ap.user");
|
||||
|
||||
// notes
|
||||
Route::get ("/note/{note:private_id}", [ APGeneralController::class, "note" ])->name ("ap.note");
|
||||
|
||||
// instance
|
||||
Route::post ("/inbox", [ APInstanceInboxController::class, "inbox" ])->name ("ap.inbox");
|
||||
});
|
||||
|
@ -7,6 +7,7 @@ use App\Http\Controllers\PostController;
|
||||
use App\Http\Controllers\UserController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\UserActionController;
|
||||
use App\Http\Controllers\BlogController;
|
||||
|
||||
// auth related
|
||||
Route::get ("/auth/login", [ UserController::class, "login" ])->name ("login")->middleware ("guest");
|
||||
@ -28,6 +29,7 @@ Route::middleware ("update_online")->group (function () {
|
||||
Route::post ("/user/edit", [ ProfileController::class, "update" ])->middleware ("auth");
|
||||
Route::get ("/user/notifications", [ ProfileController::class, "notifications" ])->name ("users.notifications")->middleware ("auth");
|
||||
Route::get ("/user/{user_name}/friends", [ ProfileController::class, "friends" ])->name ("users.friends");
|
||||
Route::get ("/user/{user_name}/blogs", [ ProfileController::class, "blogs" ])->name ("users.blogs");
|
||||
Route::get ("/user/{user_name}", [ ProfileController::class, "show" ])->name ("users.show");
|
||||
|
||||
// posts routes
|
||||
@ -42,10 +44,17 @@ Route::middleware ("update_online")->group (function () {
|
||||
// other routes
|
||||
Route::get ("/browse", [ HomeController::class, "browse" ])->name ("browse");
|
||||
Route::get ("/search", [ HomeController::class, "search" ])->name ("search");
|
||||
Route::get ("/tags/{tag}", [ HomeController::class, "tag" ])->name ("tags"); // TODO: This
|
||||
Route::get ("/tags/{tag}", [ HomeController::class, "tag" ])->name ("tags");
|
||||
Route::get ("/search", [ HomeController::class, "search" ])->name ("search");
|
||||
Route::get ("/requests", [ HomeController::class, "requests" ])->name ("requests")->middleware ("auth");
|
||||
Route::post ("/requests", [ HomeController::class, "requests_accept" ])->middleware ("auth");
|
||||
|
||||
// blog routes
|
||||
Route::get ("/blogs/create", [ BlogController::class, "create" ])->name ("blogs.create")->middleware ("auth");
|
||||
Route::post ("/blogs/create", [ BlogController::class, "store" ])->middleware ("auth");
|
||||
Route::get ("/blogs/{blog:slug}/entry/new", [ BlogController::class, "new_entry" ])->name ("blogs.new_entry")->middleware("auth");
|
||||
Route::get ("/blogs/{blog:slug}", [ BlogController::class, "show" ])->name ("blogs.show");
|
||||
Route::get ("/blogs", [ BlogController::class, "index" ])->name ("blogs");
|
||||
});
|
||||
|
||||
require __DIR__ . "/api.php";
|
||||
|
Loading…
x
Reference in New Issue
Block a user