change gitignore

This commit is contained in:
2025-09-02 13:57:30 +02:00
parent d8b89fc4fe
commit 1bcb7eb180
9363 changed files with 1088355 additions and 608 deletions
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace Vanguard\Announcements;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\HtmlString;
use League\CommonMark\CommonMarkConverter;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\Table\TableExtension;
use Vanguard\Announcements\Database\Factories\AnnouncementFactory;
use Vanguard\User;
class Announcement extends Model
{
use HasFactory;
protected $table = 'announcements';
protected $guarded = [];
public function creator()
{
return $this->belongsTo(User::class, 'user_id');
}
public function wasReadBy(User $user)
{
return $user->announcements_last_read_at < $this->created_at;
}
public function getParsedBodyAttribute()
{
$environment = Environment::createCommonMarkEnvironment();
$environment->addExtension(new TableExtension);
$converter = new CommonMarkConverter([
'html_input' => 'escape',
'allow_unsafe_links' => false,
], $environment);
return new HtmlString(
$converter->convertToHtml($this->attributes['body'])
);
}
/**
* {@inheritDoc}
*/
protected static function newFactory()
{
return new AnnouncementFactory;
}
}
+160
View File
@@ -0,0 +1,160 @@
<?php
namespace Vanguard\Announcements;
use Event;
use Route;
use Vanguard\Announcements\Events\EmailNotificationRequested;
use Vanguard\Announcements\Hooks\NavbarItemsHook;
use Vanguard\Announcements\Hooks\ScriptsHook;
use Vanguard\Announcements\Hooks\StylesHook;
use Vanguard\Announcements\Listeners\SendEmailNotification;
use Vanguard\Announcements\Repositories\AnnouncementsRepository;
use Vanguard\Announcements\Repositories\EloquentAnnouncements;
use Vanguard\Plugins\Plugin;
use Vanguard\Support\Sidebar\Item;
use Vanguard\Announcements\Listeners\ActivityLogSubscriber;
use Vanguard\Plugins\Vanguard;
class Announcements extends Plugin
{
/**
* A sidebar item for the plugin.
* @return Item|null
*/
public function sidebar()
{
return Item::create(__('Announcements'))
->icon('fas fa-bullhorn')
->route('announcements.index')
->permissions('announcements.manage')
->active('announcements*');
}
/**
* Register plugin services.
*/
public function register()
{
$this->app->singleton(AnnouncementsRepository::class, EloquentAnnouncements::class);
}
/**
* Bootstrap services.
*
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function boot()
{
$this->registerViews();
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
$this->loadViewsFrom(__DIR__ . '/../resources/views', 'announcements');
$this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'announcements');
$this->loadJsonTranslationsFrom(__DIR__.'/../resources/lang');
$this->publishes([
__DIR__ . '/../database/migrations' => database_path('migrations')
], 'migrations');
$this->mapRoutes();
$this->registerHooks();
$this->registerEventListeners();
$this->publishAssets();
}
/**
* Register plugin views.
*
* @return void
*/
protected function registerViews()
{
$viewsPath = __DIR__.'/../resources/views';
$this->publishes([
$viewsPath => resource_path('views/vendor/plugins/announcements')
], 'views');
$this->loadViewsFrom($viewsPath, 'announcements');
}
/**
* Map all plugin related routes.
*/
protected function mapRoutes()
{
$this->mapWebRoutes();
if ($this->app['config']->get('auth.expose_api')) {
$this->mapApiRoutes();
}
}
/**
* Map web plugin related routes.
*/
protected function mapWebRoutes()
{
Route::group([
'namespace' => 'Vanguard\Announcements\Http\Controllers\Web',
'middleware' => 'web',
], function () {
$this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
});
}
/**
* Map API plugin related routes.
*/
protected function mapApiRoutes()
{
Route::group([
'namespace' => 'Vanguard\Announcements\Http\Controllers\Api',
'middleware' => 'api',
'prefix' => 'api',
], function () {
$this->loadRoutesFrom(__DIR__ . '/../routes/api.php');
});
}
/**
* Register plugin event listeners.
*/
private function registerEventListeners()
{
// Register activity log subscriber only if
// UserActivity plugin is installed.
if ($this->app->bound('Vanguard\UserActivity\Repositories\Activity\ActivityRepository')) {
Event::subscribe(ActivityLogSubscriber::class);
}
Event::listen(EmailNotificationRequested::class, SendEmailNotification::class);
}
/**
* Register all necessary view hooks for the plugin.
*/
private function registerHooks()
{
Vanguard::hook('navbar:items', NavbarItemsHook::class);
Vanguard::hook('app:styles', StylesHook::class);
Vanguard::hook('app:scripts', ScriptsHook::class);
}
/**
* Publish public assets.
*
* @return void
*/
protected function publishAssets()
{
$this->publishes([
realpath(__DIR__.'/../dist') => $this->app['path.public'].'/vendor/plugins/announcements',
], 'public');
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Vanguard\Announcements\Events;
use Illuminate\Foundation\Events\Dispatchable;
use Vanguard\Announcements\Announcement;
class Created
{
use Dispatchable;
/**
* @var Announcement
*/
public $announcement;
/**
* @var bool
*/
public $shouldSendEmailNotification;
public function __construct(Announcement $announcement, $sendEmailNotification = false)
{
$this->announcement = $announcement;
$this->shouldSendEmailNotification = $sendEmailNotification;
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace Vanguard\Announcements\Events;
use Illuminate\Foundation\Events\Dispatchable;
use Vanguard\Announcements\Announcement;
class Deleted
{
use Dispatchable;
public $announcement;
public function __construct(Announcement $announcement)
{
$this->announcement = $announcement;
}
}
@@ -0,0 +1,21 @@
<?php
namespace Vanguard\Announcements\Events;
use Illuminate\Foundation\Events\Dispatchable;
use Vanguard\Announcements\Announcement;
class EmailNotificationRequested
{
use Dispatchable;
/**
* @var Announcement
*/
public $announcement;
public function __construct(Announcement $announcement)
{
$this->announcement = $announcement;
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace Vanguard\Announcements\Events;
use Illuminate\Foundation\Events\Dispatchable;
use Vanguard\Announcements\Announcement;
class Updated
{
use Dispatchable;
public $announcement;
public function __construct(Announcement $announcement)
{
$this->announcement = $announcement;
}
}
@@ -0,0 +1,32 @@
<?php
namespace Vanguard\Announcements\Hooks;
use Vanguard\Announcements\Repositories\AnnouncementsRepository;
use Vanguard\Plugins\Contracts\Hook;
class NavbarItemsHook implements Hook
{
/**
* @var AnnouncementsRepository
*/
private $announcements;
public function __construct(AnnouncementsRepository $announcements)
{
$this->announcements = $announcements;
}
/**
* Execute the hook action.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function handle()
{
$announcements = $this->announcements->latest(5);
$announcements->load('creator');
return view('announcements::partials.navbar.list', compact('announcements'));
}
}
@@ -0,0 +1,18 @@
<?php
namespace Vanguard\Announcements\Hooks;
use Vanguard\Plugins\Contracts\Hook;
class ScriptsHook implements Hook
{
/**
* Execute the hook action.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function handle()
{
return view('announcements::partials.scripts');
}
}
@@ -0,0 +1,18 @@
<?php
namespace Vanguard\Announcements\Hooks;
use Vanguard\Plugins\Contracts\Hook;
class StylesHook implements Hook
{
/**
* Execute the hook action.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function handle()
{
return view('announcements::partials.styles');
}
}
@@ -0,0 +1,133 @@
<?php
namespace Vanguard\Announcements\Http\Controllers\Api;
use Illuminate\Http\Request;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\AllowedInclude;
use Spatie\QueryBuilder\QueryBuilder;
use Vanguard\Announcements\Announcement;
use Vanguard\Announcements\Events\EmailNotificationRequested;
use Vanguard\Announcements\Http\Requests\AnnouncementRequest;
use Vanguard\Announcements\Http\Resources\AnnouncementResource;
use Vanguard\Announcements\Repositories\AnnouncementsRepository;
use Vanguard\Http\Controllers\Api\ApiController;
/**
* Class AnnouncementsController
* @package Vanguard\Announcements\Http\Controllers\Web
*/
class AnnouncementsController extends ApiController
{
/**
* @var AnnouncementsRepository
*/
private $announcements;
/**
* AnnouncementsController constructor.
* @param AnnouncementsRepository $announcements
*/
public function __construct(AnnouncementsRepository $announcements)
{
$this->announcements = $announcements;
$this->middleware('permission:announcements.manage')->except('index', 'show');
}
/**
* Returns a paginated list of announcements.
*
* @param Request $request
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
* @throws \Illuminate\Validation\ValidationException
*/
public function index(Request $request)
{
$this->validate($request, ['per_page' => 'numeric|max:50']);
$announcements = QueryBuilder::for(Announcement::class)
->allowedIncludes([
AllowedInclude::relationship('user', 'creator')
])
->allowedFilters([
AllowedFilter::partial('title'),
AllowedFilter::partial('body'),
AllowedFilter::exact('user', 'user_id'),
])
->allowedSorts('title', 'created_at')
->defaultSort('-created_at')
->paginate($request->per_page);
return AnnouncementResource::collection($announcements);
}
/**
* Stores the announcement inside the database.
*
* @param AnnouncementRequest $request
* @return mixed
*/
public function store(AnnouncementRequest $request)
{
$announcement = $this->announcements->createFor(
auth()->user(),
$request->title,
$request->body
);
if ($request->email_notification) {
EmailNotificationRequested::dispatch($announcement);
}
return new AnnouncementResource($announcement);
}
/**
* Returns a single announcement resource.
*
* @param $announcementId
* @return AnnouncementResource
*/
public function show($announcementId)
{
$announcement = QueryBuilder::for(Announcement::where('id', $announcementId))
->allowedIncludes([
AllowedInclude::relationship('user', 'creator')
])
->first();
return new AnnouncementResource($announcement);
}
/**
* Updates announcement details.
*
* @param Announcement $announcement
* @param AnnouncementRequest $request
* @return mixed
*/
public function update(Announcement $announcement, AnnouncementRequest $request)
{
$announcement = $this->announcements->update(
$announcement,
$request->title,
$request->body
);
return new AnnouncementResource($announcement);
}
/**
* Removes announcement from the system.
*
* @param Announcement $announcement
* @return mixed
*/
public function destroy(Announcement $announcement)
{
$this->announcements->delete($announcement);
return $this->respondWithSuccess();
}
}
@@ -0,0 +1,23 @@
<?php
namespace Vanguard\Announcements\Http\Controllers\Api;
use Vanguard\Http\Controllers\Api\ApiController;
class ReadAnnouncementsController extends ApiController
{
/**
* Update the timestamp when announcements were last read
* by the currently authenticated user.
*
* @return \Illuminate\Http\JsonResponse
*/
public function index()
{
auth()->user()->forceFill([
'announcements_last_read_at' => now()
])->save();
return $this->respondWithSuccess();
}
}
@@ -0,0 +1,32 @@
<?php
namespace Vanguard\Announcements\Http\Controllers\Web;
use Vanguard\Announcements\Repositories\AnnouncementsRepository;
use Vanguard\Http\Controllers\Controller;
class AnnouncementListController extends Controller
{
/**
* @var AnnouncementsRepository
*/
private $announcements;
public function __construct(AnnouncementsRepository $announcements)
{
$this->announcements = $announcements;
}
/**
* Displays the plugin index page.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index()
{
$announcements = $this->announcements->paginate(7);
$announcements->load('creator');
return view('announcements::list', compact('announcements'));
}
}
@@ -0,0 +1,135 @@
<?php
namespace Vanguard\Announcements\Http\Controllers\Web;
use Vanguard\Announcements\Announcement;
use Vanguard\Announcements\Events\EmailNotificationRequested;
use Vanguard\Announcements\Http\Requests\AnnouncementRequest;
use Vanguard\Announcements\Repositories\AnnouncementsRepository;
use Vanguard\Http\Controllers\Controller;
/**
* Class AnnouncementsController
* @package Vanguard\Announcements\Http\Controllers\Web
*/
class AnnouncementsController extends Controller
{
/**
* @var AnnouncementsRepository
*/
private $announcements;
/**
* AnnouncementsController constructor.
* @param AnnouncementsRepository $announcements
*/
public function __construct(AnnouncementsRepository $announcements)
{
$this->announcements = $announcements;
$this->middleware('permission:announcements.manage')->except('show');
}
/**
* Displays the plugin index page.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index()
{
$announcements = $this->announcements->paginate();
$announcements->load('creator');
return view('announcements::index', compact('announcements'));
}
/**
* Shows the create announcement form.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function create()
{
return view('announcements::add-edit', ['edit' => false]);
}
/**
* Stores the announcement inside the database.
*
* @param AnnouncementRequest $request
* @return mixed
*/
public function store(AnnouncementRequest $request)
{
$announcement = $this->announcements->createFor(
auth()->user(),
$request->title,
$request->body
);
if ($request->email_notification) {
EmailNotificationRequested::dispatch($announcement);
}
return redirect()->route('announcements.index')
->withSuccess(__('Announcement created successfully.'));
}
/**
* Renders "view announcement" page.
*
* @param Announcement $announcement
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function show(Announcement $announcement)
{
return view('announcements::show', compact('announcement'));
}
/**
* Renders the form for editing the announcement.
*
* @param Announcement $announcement
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function edit(Announcement $announcement)
{
return view('announcements::add-edit', [
'edit' => true,
'announcement' => $announcement
]);
}
/**
* Updates announcement details.
*
* @param Announcement $announcement
* @param AnnouncementRequest $request
* @return mixed
*/
public function update(Announcement $announcement, AnnouncementRequest $request)
{
$this->announcements->update(
$announcement,
$request->title,
$request->body
);
return redirect()->route('announcements.index')
->withSuccess(__('Announcement updated successfully.'));
}
/**
* Removes announcement from the system.
*
* @param Announcement $announcement
* @return mixed
*/
public function destroy(Announcement $announcement)
{
$this->announcements->delete($announcement);
return redirect()->route('announcements.index')
->withSuccess(__('Announcement deleted successfully.'));
}
}
@@ -0,0 +1,20 @@
<?php
namespace Vanguard\Announcements\Http\Controllers\Web;
use Vanguard\Http\Controllers\Controller;
class ReadAnnouncementsController extends Controller
{
/**
* Update the timestamp when announcements were last read
* by the currently authenticated user.
*/
public function index()
{
auth()->user()->forceFill([
'announcements_last_read_at' => now()
])->save();
}
}
@@ -0,0 +1,22 @@
<?php
namespace Vanguard\Announcements\Http\Requests;
use Vanguard\Http\Requests\Request;
class AnnouncementRequest extends Request
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|max:150',
'body' => 'required|max:1500',
'email_notifications' => 'boolean'
];
}
}
@@ -0,0 +1,29 @@
<?php
namespace Vanguard\Announcements\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use Vanguard\Http\Resources\UserResource;
class AnnouncementResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => (int) $this->id,
'user_id' => (int) $this->user_id,
'title' => $this->title,
'body' => $this->body,
'parsed_body' => (string) $this->parsed_body,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at,
'user' => new UserResource($this->whenLoaded('creator')),
];
}
}
@@ -0,0 +1,58 @@
<?php
namespace Vanguard\Announcements\Listeners;
use Illuminate\Support\Str;
use Vanguard\Announcements\Events\Created;
use Vanguard\Announcements\Events\Deleted;
use Vanguard\Announcements\Events\Updated;
use Vanguard\UserActivity\Logger;
class ActivityLogSubscriber
{
/**
* @var Logger
*/
private $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function onCreate(Created $event)
{
$this->logger->log(__('announcements::log.created_announcement', [
'id' => $event->announcement->id,
'title' => Str::limit($event->announcement->title, 50)
]));
}
public function onUpdate(Updated $event)
{
$this->logger->log(__('announcements::log.created_announcement', [
'id' => $event->announcement->id
]));
}
public function onDelete(Deleted $event)
{
$this->logger->log(__('announcements::log.deleted_announcement', [
'id' => $event->announcement->id
]));
}
/**
* Register the listeners for the subscriber.
*
* @param \Illuminate\Events\Dispatcher $events
*/
public function subscribe($events)
{
$class = self::class;
$events->listen(Created::class, "{$class}@onCreate");
$events->listen(Updated::class, "{$class}@onUpdate");
$events->listen(Deleted::class, "{$class}@onDelete");
}
}
@@ -0,0 +1,32 @@
<?php
namespace Vanguard\Announcements\Listeners;
use Mail;
use Vanguard\Announcements\Announcement;
use Vanguard\Announcements\Events\EmailNotificationRequested;
use Vanguard\Announcements\Mail\AnnouncementEmail;
use Vanguard\User;
class SendEmailNotification
{
/**
* Handle the event.
*
* @param EmailNotificationRequested $event
* @return void
*/
public function handle(EmailNotificationRequested $event)
{
User::chunk(200, function ($users) use ($event) {
foreach ($users as $user) {
$this->sendEmailTo($user, $event->announcement);
}
});
}
private function sendEmailTo(User $user, Announcement $announcement)
{
Mail::to($user)->send(new AnnouncementEmail($announcement));
}
}
@@ -0,0 +1,42 @@
<?php
namespace Vanguard\Announcements\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use Vanguard\Announcements\Announcement;
class AnnouncementEmail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
/**
* @var Announcement
*/
public $announcement;
/**
* Create a new message instance.
*
* @param Announcement $announcement
*/
public function __construct(Announcement $announcement)
{
$this->announcement = $announcement;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$subject = sprintf("[%s] %s", __('Announcement'), $this->announcement->title);
return $this->subject($subject)
->markdown('announcements::mail.notification');
}
}
@@ -0,0 +1,61 @@
<?php
namespace Vanguard\Announcements\Repositories;
use Vanguard\Announcements\Announcement;
use Vanguard\User;
interface AnnouncementsRepository
{
/**
* Get latest announcements.
*
* @param int $count
* @return mixed
*/
public function latest($count = 5);
/**
* Paginate announcements in descending order.
*
* @param int $perPage
* @return mixed
*/
public function paginate($perPage = 10);
/**
* Create an announcement for user.
*
* @param User $user
* @param $title
* @param $body
* @return mixed
*/
public function createFor(User $user, $title, $body);
/**
* Find announcement by ID.
*
* @param $id
* @return mixed
*/
public function find($id);
/**
* Update announcement.
*
* @param Announcement $announcement
* @param $title
* @param $body
* @return mixed
*/
public function update(Announcement $announcement, $title, $body);
/**
* Remove announcement from the system.
*
* @param Announcement $announcement
* @return mixed
*/
public function delete(Announcement $announcement);
}
@@ -0,0 +1,104 @@
<?php
namespace Vanguard\Announcements\Repositories;
use Vanguard\Announcements\Announcement;
use Vanguard\Announcements\Events\Created;
use Vanguard\Announcements\Events\Deleted;
use Vanguard\Announcements\Events\Updated;
use Vanguard\User;
class EloquentAnnouncements implements AnnouncementsRepository
{
/**
* Get latest announcements.
*
* @param int $count
* @return mixed
*/
public function latest($count = 5)
{
return Announcement::latest()->take($count)->get();
}
/**
* Paginate announcements in descending order.
*
* @param int $perPage
* @return mixed
*/
public function paginate($perPage = 10)
{
return Announcement::latest()->paginate($perPage);
}
/**
* Create an announcement for user.
*
* @param User $user
* @param $title
* @param $body
* @return mixed
*/
public function createFor(User $user, $title, $body)
{
$announcement = Announcement::create([
'title' => $title,
'body' => $body,
'user_id' => $user->id
]);
Created::dispatch($announcement);
return $announcement;
}
/**
* Find announcement by ID.
*
* @param $id
* @return mixed
*/
public function find($id)
{
return Announcement::find($id);
}
/**
* Update announcement.
*
* @param Announcement $announcement
* @param $title
* @param $body
* @return mixed
*/
public function update(Announcement $announcement, $title, $body)
{
$announcement->update([
'title' => $title,
'body' => $body
]);
Updated::dispatch($announcement);
return $announcement;
}
/**
* Remove announcement from the system.
*
* @param Announcement $announcement
* @return bool
* @throws \Exception
*/
public function delete(Announcement $announcement)
{
if ($announcement->delete()) {
Deleted::dispatch($announcement);
return true;
}
return false;
}
}