mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-01-19 08:42:48 +08:00
Initial controller/views for webhooks management
This commit is contained in:
parent
a3a3055695
commit
4621d8bcc5
|
@ -53,4 +53,16 @@ class ActivityType
|
|||
|
||||
const MFA_SETUP_METHOD = 'mfa_setup_method';
|
||||
const MFA_REMOVE_METHOD = 'mfa_remove_method';
|
||||
|
||||
const WEBHOOK_CREATE = 'webhook_create';
|
||||
const WEBHOOK_UPDATE = 'webhook_update';
|
||||
const WEBHOOK_DELETE = 'webhook_delete';
|
||||
|
||||
/**
|
||||
* Get all the possible values.
|
||||
*/
|
||||
public static function all(): array
|
||||
{
|
||||
return (new \ReflectionClass(static::class))->getConstants();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,24 @@
|
|||
|
||||
namespace BookStack\Actions;
|
||||
|
||||
use BookStack\Interfaces\Loggable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Webhook extends Model
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $endpoint
|
||||
*/
|
||||
class Webhook extends Model implements Loggable
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* Get the string descriptor for this item.
|
||||
*/
|
||||
public function logDescriptor(): string
|
||||
{
|
||||
return "({$this->id}) {$this->name}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,93 @@
|
|||
|
||||
namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Actions\ActivityType;
|
||||
use BookStack\Actions\Webhook;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class WebhookController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware([
|
||||
'can:settings-manage',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all webhooks configured in the system.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// TODO - Get and pass webhooks
|
||||
return view('settings.webhooks.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the view for creating a new webhook in the system.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('settings.webhooks.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new webhook in the system.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// TODO - Create webhook
|
||||
$this->logActivity(ActivityType::WEBHOOK_CREATE, $webhook);
|
||||
return redirect('/settings/webhooks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the view to edit an existing webhook.
|
||||
*/
|
||||
public function edit(string $id)
|
||||
{
|
||||
/** @var Webhook $webhook */
|
||||
$webhook = Webhook::query()->findOrFail($id);
|
||||
|
||||
return view('settings.webhooks.edit', ['webhook' => $webhook]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing webhook with the provided request data.
|
||||
*/
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
/** @var Webhook $webhook */
|
||||
$webhook = Webhook::query()->findOrFail($id);
|
||||
|
||||
// TODO - Update
|
||||
|
||||
$this->logActivity(ActivityType::WEBHOOK_UPDATE, $webhook);
|
||||
return redirect('/settings/webhooks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the view to delete a webhook.
|
||||
*/
|
||||
public function delete(string $id)
|
||||
{
|
||||
/** @var Webhook $webhook */
|
||||
$webhook = Webhook::query()->findOrFail($id);
|
||||
return view('settings.webhooks.delete', ['webhook' => $webhook]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a webhook from the system.
|
||||
*/
|
||||
public function destroy(string $id)
|
||||
{
|
||||
/** @var Webhook $webhook */
|
||||
$webhook = Webhook::query()->findOrFail($id);
|
||||
|
||||
// TODO - Delete event type relations
|
||||
$webhook->delete();
|
||||
|
||||
$this->logActivity(ActivityType::WEBHOOK_DELETE, $webhook);
|
||||
return redirect('/settings/webhooks');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ import templateManager from "./template-manager.js"
|
|||
import toggleSwitch from "./toggle-switch.js"
|
||||
import triLayout from "./tri-layout.js"
|
||||
import userSelect from "./user-select.js"
|
||||
import webhookEvents from "./webhook-events";
|
||||
import wysiwygEditor from "./wysiwyg-editor.js"
|
||||
|
||||
const componentMapping = {
|
||||
|
@ -105,6 +106,7 @@ const componentMapping = {
|
|||
"toggle-switch": toggleSwitch,
|
||||
"tri-layout": triLayout,
|
||||
"user-select": userSelect,
|
||||
"webhook-events": webhookEvents,
|
||||
"wysiwyg-editor": wysiwygEditor,
|
||||
};
|
||||
|
||||
|
|
32
resources/js/components/webhook-events.js
Normal file
32
resources/js/components/webhook-events.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
|
||||
/**
|
||||
* Webhook Events
|
||||
* Manages dynamic selection control in the webhook form interface.
|
||||
* @extends {Component}
|
||||
*/
|
||||
class WebhookEvents {
|
||||
|
||||
setup() {
|
||||
this.checkboxes = this.$el.querySelectorAll('input[type="checkbox"]');
|
||||
this.allCheckbox = this.$refs.all;
|
||||
|
||||
this.$el.addEventListener('change', event => {
|
||||
if (event.target.checked && event.target === this.allCheckbox) {
|
||||
this.deselectIndividualEvents();
|
||||
} else if (event.target.checked) {
|
||||
this.allCheckbox.checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deselectIndividualEvents() {
|
||||
for (const checkbox of this.checkboxes) {
|
||||
if (checkbox !== this.allCheckbox) {
|
||||
checkbox.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebhookEvents;
|
|
@ -51,6 +51,14 @@ return [
|
|||
'mfa_setup_method_notification' => 'Multi-factor method successfully configured',
|
||||
'mfa_remove_method_notification' => 'Multi-factor method successfully removed',
|
||||
|
||||
// Webhooks
|
||||
'webhook_create' => 'created webhook',
|
||||
'webhook_create_notification' => 'Webhook successfully created',
|
||||
'webhook_update' => 'updated webhook',
|
||||
'webhook_update_notification' => 'Webhook successfully updated',
|
||||
'webhook_delete' => 'deleted webhook',
|
||||
'webhook_delete_notification' => 'Webhook successfully deleted',
|
||||
|
||||
// Other
|
||||
'commented_on' => 'commented on',
|
||||
'permissions_update' => 'updated permissions',
|
||||
|
|
|
@ -236,6 +236,19 @@ return [
|
|||
// Webhooks
|
||||
'webhooks' => 'Webhooks',
|
||||
'webhooks_create' => 'Create New Webhook',
|
||||
'webhooks_edit' => 'Edit Webhook',
|
||||
'webhooks_save' => 'Save Webhook',
|
||||
'webhooks_details' => 'Webhook Details',
|
||||
'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.',
|
||||
'webhooks_events' => 'Webhook Events',
|
||||
'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.',
|
||||
'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.',
|
||||
'webhooks_events_all' => 'All system events',
|
||||
'webhooks_name' => 'Webhook Name',
|
||||
'webhooks_endpoint' => 'Webhook Endpoint',
|
||||
'webhooks_delete' => 'Delete Webhook',
|
||||
'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.',
|
||||
'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?',
|
||||
|
||||
//! If editing translations files directly please ignore this in all
|
||||
//! languages apart from en. Content will be auto-copied from en.
|
||||
|
|
16
resources/views/settings/webhooks/create.blade.php
Normal file
16
resources/views/settings/webhooks/create.blade.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
@extends('layouts.simple')
|
||||
|
||||
@section('body')
|
||||
|
||||
<div class="container small">
|
||||
|
||||
<div class="py-m">
|
||||
@include('settings.parts.navbar', ['selected' => 'webhooks'])
|
||||
</div>
|
||||
|
||||
<form action="{{ url("/settings/webhooks/new") }}" method="POST">
|
||||
@include('settings.webhooks.parts.form', ['title' => trans('settings.webhooks_create')])
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@stop
|
39
resources/views/settings/webhooks/delete.blade.php
Normal file
39
resources/views/settings/webhooks/delete.blade.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
@extends('layouts.simple')
|
||||
|
||||
@section('body')
|
||||
<div class="container small">
|
||||
|
||||
<div class="py-m">
|
||||
@include('settings.parts.navbar', ['selected' => 'webhooks'])
|
||||
</div>
|
||||
|
||||
<div class="card content-wrap auto-height">
|
||||
<h1 class="list-heading"> {{ trans('settings.webhooks_delete') }}</h1>
|
||||
|
||||
<p>{{ trans('settings.webhooks_delete_warning', ['webhookName' => $webhook->name]) }}</p>
|
||||
|
||||
|
||||
<form action="{{ url("/settings/webhooks/{$role->id}") }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
{!! method_field('DELETE') !!}
|
||||
|
||||
<div class="grid half v-center">
|
||||
<div>
|
||||
<p class="text-neg">
|
||||
<strong>{{ trans('settings.webhooks_delete_confirm') }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="form-group text-right">
|
||||
<a href="{{ url("/settings/webhooks/{$role->id}") }}" class="button outline">{{ trans('common.cancel') }}</a>
|
||||
<button type="submit" class="button">{{ trans('common.confirm') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@stop
|
16
resources/views/settings/webhooks/edit.blade.php
Normal file
16
resources/views/settings/webhooks/edit.blade.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
@extends('layouts.simple')
|
||||
|
||||
@section('body')
|
||||
|
||||
<div class="container small">
|
||||
<div class="py-m">
|
||||
@include('settings.parts.navbar', ['selected' => 'webhooks'])
|
||||
</div>
|
||||
|
||||
<form action="{{ url("/settings/webhooks/{$webhook->id}") }}" method="POST">
|
||||
{!! method_field('PUT') !!}
|
||||
@include('settings.webhooks.parts.form', ['model' => $webhook, 'title' => trans('settings.webhooks_edit')])
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@stop
|
|
@ -14,7 +14,7 @@
|
|||
<h1 class="list-heading">{{ trans('settings.webhooks') }}</h1>
|
||||
|
||||
<div class="text-right">
|
||||
<a href="{{ url("/settings/webhooks/new") }}" class="button outline">{{ trans('settings.webhooks_create') }}</a>
|
||||
<a href="{{ url("/settings/webhooks/create") }}" class="button outline">{{ trans('settings.webhooks_create') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
57
resources/views/settings/webhooks/parts/form.blade.php
Normal file
57
resources/views/settings/webhooks/parts/form.blade.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
{!! csrf_field() !!}
|
||||
|
||||
<div class="card content-wrap auto-height">
|
||||
<h1 class="list-heading">{{ $title }}</h1>
|
||||
|
||||
<div class="setting-list">
|
||||
|
||||
<div class="grid half">
|
||||
<div>
|
||||
<label class="setting-list-label">{{ trans('settings.webhooks_details') }}</label>
|
||||
<p class="small">{{ trans('settings.webhooks_details_desc') }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<label for="name">{{ trans('settings.webhooks_name') }}</label>
|
||||
@include('form.text', ['name' => 'name'])
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="endpoint">{{ trans('settings.webhooks_endpoint') }}</label>
|
||||
@include('form.text', ['name' => 'endpoint'])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div component="webhook-events">
|
||||
<label class="setting-list-label">{{ trans('settings.webhooks_events') }}</label>
|
||||
<p class="small">{{ trans('settings.webhooks_events_desc') }}</p>
|
||||
<p class="text-warn small">{{ trans('settings.webhooks_events_warning') }}</p>
|
||||
|
||||
<div>
|
||||
<label><input type="checkbox"
|
||||
name="events[]"
|
||||
value="all"
|
||||
refs="webhook-events@all">
|
||||
{{ trans('settings.webhooks_events_all') }}</label>
|
||||
</div>
|
||||
|
||||
<hr class="my-m">
|
||||
|
||||
<div class="dual-column-content">
|
||||
@foreach(\BookStack\Actions\ActivityType::all() as $activityType)
|
||||
<label><input type="checkbox" name="events[]" value="{{ $activityType }}">{{ $activityType }}</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group text-right">
|
||||
<a href="{{ url("/settings/webhooks") }}" class="button outline">{{ trans('common.cancel') }}</a>
|
||||
@if ($webhook->id ?? false)
|
||||
<a href="{{ url("/settings/roles/delete/{$webhook->id}") }}" class="button outline">{{ trans('settings.webhooks_delete') }}</a>
|
||||
@endif
|
||||
<button type="submit" class="button">{{ trans('settings.webhooks_save') }}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -258,6 +258,12 @@ Route::middleware('auth')->group(function () {
|
|||
|
||||
// Webhooks
|
||||
Route::get('/settings/webhooks', [WebhookController::class, 'index']);
|
||||
Route::get('/settings/webhooks/create', [WebhookController::class, 'create']);
|
||||
Route::post('/settings/webhooks/create', [WebhookController::class, 'store']);
|
||||
Route::get('/settings/webhooks/{id}', [WebhookController::class, 'edit']);
|
||||
Route::put('/settings/webhooks/{id}', [WebhookController::class, 'update']);
|
||||
Route::get('/settings/webhooks/{id}/delete', [WebhookController::class, 'delete']);
|
||||
Route::delete('/settings/webhooks/{id}', [WebhookController::class, 'destroy']);
|
||||
});
|
||||
|
||||
// MFA routes
|
||||
|
|
Loading…
Reference in New Issue
Block a user