Also aligned query naming to start with model in use.
Also added created/updated sort options to roles.
--- /dev/null
+<?php
+
+namespace BookStack\Actions\Queries;
+
+use BookStack\Actions\Webhook;
+use Illuminate\Pagination\LengthAwarePaginator;
+
+/**
+ * Get all the webhooks in the system in a paginated format.
+ */
+class WebhooksAllPaginatedAndSorted
+{
+ /**
+ * @param array{sort: string, order: string, search: string} $sortData
+ */
+ public function run(int $count, array $sortData): LengthAwarePaginator
+ {
+ $sort = $sortData['sort'];
+
+ $query = Webhook::query()->select(['*'])
+ ->withCount(['trackedEvents'])
+ ->orderBy($sort, $sortData['order']);
+
+ if ($sortData['search']) {
+ $term = '%' . $sortData['search'] . '%';
+ $query->where(function ($query) use ($term) {
+ $query->where('name', 'like', $term)
+ ->orWhere('endpoint', 'like', $term);
+ });
+ }
+
+ return $query->paginate($count);
+ }
+}
/**
* Get all the roles in the system in a paginated format.
*/
-class AllRolesPaginatedAndSorted
+class RolesAllPaginatedAndSorted
{
/**
* @param array{sort: string, order: string, search: string} $sortData
* user is assumed to be trusted. (Admin users).
* Email search can be abused to extract email addresses.
*/
-class AllUsersPaginatedAndSorted
+class UsersAllPaginatedAndSorted
{
/**
* @param array{sort: string, order: string, search: string} $sortData
namespace BookStack\Http\Controllers;
use BookStack\Auth\Permissions\PermissionsRepo;
-use BookStack\Auth\Queries\AllRolesPaginatedAndSorted;
+use BookStack\Auth\Queries\RolesAllPaginatedAndSorted;
use BookStack\Auth\Role;
use BookStack\Exceptions\PermissionsException;
use Exception;
'order' => setting()->getForCurrentUser('roles_sort_order', 'asc'),
];
- $roles = (new AllRolesPaginatedAndSorted())->run(20, $listDetails);
+ $roles = (new RolesAllPaginatedAndSorted())->run(20, $listDetails);
$roles->appends(['search' => $listDetails['search']]);
$this->setPageTitle(trans('settings.roles'));
namespace BookStack\Http\Controllers;
use BookStack\Auth\Access\SocialAuthService;
-use BookStack\Auth\Queries\AllUsersPaginatedAndSorted;
+use BookStack\Auth\Queries\UsersAllPaginatedAndSorted;
use BookStack\Auth\Role;
use BookStack\Auth\User;
use BookStack\Auth\UserRepo;
'order' => setting()->getForCurrentUser('users_sort_order', 'asc'),
];
- $users = (new AllUsersPaginatedAndSorted())->run(20, $listDetails);
+ $users = (new UsersAllPaginatedAndSorted())->run(20, $listDetails);
$this->setPageTitle(trans('settings.users'));
$users->appends(['search' => $listDetails['search']]);
*/
public function changeSort(Request $request, string $id, string $type)
{
- $validSortTypes = ['books', 'bookshelves', 'shelf_books', 'users', 'roles'];
+ $validSortTypes = ['books', 'bookshelves', 'shelf_books', 'users', 'roles', 'webhooks'];
if (!in_array($type, $validSortTypes)) {
return redirect()->back(500);
}
// Probably better to do a simple validation here then validate at usage.
$validSorts = [
'name', 'created_at', 'updated_at', 'default', 'email', 'last_activity_at', 'display_name',
- 'users_count', 'permissions_count',
+ 'users_count', 'permissions_count', 'endpoint', 'active',
];
if (!in_array($sort, $validSorts)) {
$sort = 'name';
namespace BookStack\Http\Controllers;
use BookStack\Actions\ActivityType;
+use BookStack\Actions\Queries\WebhooksAllPaginatedAndSorted;
use BookStack\Actions\Webhook;
use Illuminate\Http\Request;
/**
* Show all webhooks configured in the system.
*/
- public function index()
+ public function index(Request $request)
{
- $webhooks = Webhook::query()
- ->orderBy('name', 'desc')
- ->with('trackedEvents')
- ->get();
+ $listDetails = [
+ 'search' => $request->get('search', ''),
+ 'sort' => setting()->getForCurrentUser('webhooks_sort', 'name'),
+ 'order' => setting()->getForCurrentUser('webhooks_sort_order', 'asc'),
+ ];
+
+ $webhooks = (new WebhooksAllPaginatedAndSorted())->run(20, $listDetails);
+ $webhooks->appends(['search' => $listDetails['search']]);
$this->setPageTitle(trans('settings.webhooks'));
- return view('settings.webhooks.index', ['webhooks' => $webhooks]);
+ return view('settings.webhooks.index', [
+ 'webhooks' => $webhooks,
+ 'listDetails' => $listDetails,
+ ]);
}
/**
// Webhooks
'webhooks' => 'Webhooks',
+ 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.',
+ 'webhooks_x_trigger_events' => '1 trigger event|:count trigger events',
'webhooks_create' => 'Create New Webhook',
'webhooks_none_created' => 'No webhooks have yet been created.',
'webhooks_edit' => 'Edit Webhook',
}
.item-list-row:hover .item-list-row-toggle-all {
visibility: visible;
+}
+
+.status-indicator-active, .status-indicator-inactive {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ display: inline-block;
+}
+.status-indicator-active {
+ background-color: $positive;
+}
+.status-indicator-inactive {
+ background-color: $negative;
}
\ No newline at end of file
--- /dev/null
+<span title="{{ trans('common.status_' . ($status ? 'active' : 'inactive')) }}"
+ class="status-indicator-{{ $status ? 'active' : 'inactive' }}"
+></span>
\ No newline at end of file
'display_name' => trans('common.sort_name'),
'users_count' => trans('settings.roles_assigned_users'),
'permissions_count' => trans('settings.roles_permissions_provided'),
+ 'created_at' => trans('common.sort_created_at'),
+ 'updated_at' => trans('common.sort_updated_at'),
], 'order' => $listDetails['order'], 'sort' => $listDetails['sort'], 'type' => 'roles'])
</div>
</div>
<div class="card content-wrap auto-height">
- <div class="grid half v-center">
+ <div class="flex-container-row items-center justify-space-between wrap">
<h1 class="list-heading">{{ trans('settings.webhooks') }}</h1>
- <div class="text-right">
+ <div>
<a href="{{ url("/settings/webhooks/create") }}"
class="button outline">{{ trans('settings.webhooks_create') }}</a>
</div>
</div>
- @if(count($webhooks) > 0)
+ <p class="text-muted">{{ trans('settings.webhooks_index_desc') }}</p>
+
+ <div class="flex-container-row items-center justify-space-between gap-m mt-m mb-l wrap">
+ <div>
+ <div class="block inline mr-xs">
+ <form method="get" action="{{ url("/settings/webhooks") }}">
+ <input type="text" name="search" placeholder="{{ trans('common.search') }}" @if($listDetails['search']) value="{{$listDetails['search']}}" @endif>
+ </form>
+ </div>
+ </div>
+ <div class="justify-flex-end">
+ @include('common.sort', ['options' => [
+ 'name' => trans('common.sort_name'),
+ 'endpoint' => trans('settings.webhooks_endpoint'),
+ 'created_at' => trans('common.sort_created_at'),
+ 'updated_at' => trans('common.sort_updated_at'),
+ 'active' => trans('common.status'),
+ ], 'order' => $listDetails['order'], 'sort' => $listDetails['sort'], 'type' => 'webhooks'])
+ </div>
+ </div>
- <table class="table">
- <tr>
- <th>{{ trans('common.name') }}</th>
- <th width="100">{{ trans('settings.webhook_events_table_header') }}</th>
- <th width="100">{{ trans('common.status') }}</th>
- </tr>
+ @if(count($webhooks) > 0)
+ <div class="item-list">
@foreach($webhooks as $webhook)
- <tr>
- <td>
- <a href="{{ $webhook->getUrl() }}">{{ $webhook->name }}</a> <br>
- <span class="small text-muted italic">{{ $webhook->endpoint }}</span>
- </td>
- <td>
- @if($webhook->tracksEvent('all'))
- {{ trans('settings.webhooks_events_all') }}
- @else
- {{ $webhook->trackedEvents->count() }}
- @endif
- </td>
- <td>
- {{ trans('common.status_' . ($webhook->active ? 'active' : 'inactive')) }}
- </td>
- </tr>
+ @include('settings.webhooks.parts.webhooks-list-item', ['webhook' => $webhook])
@endforeach
- </table>
+ </div>
@else
<p class="text-muted empty-text px-none">
{{ trans('settings.webhooks_none_created') }}
</p>
@endif
+ <div class="my-m">
+ {{ $webhooks->links() }}
+ </div>
</div>
</div>
--- /dev/null
+<div class="item-list-row py-s">
+ <div class="flex-container-row">
+ <div class="flex-2 py-xxs px-m flex-container-row items-center gap-s">
+ @include('common.status-indicator', ['status' => $webhook->active])
+ <a href="{{ $webhook->getUrl() }}">{{ $webhook->name }}</a>
+ </div>
+ <div class="flex py-xxs px-m text-right text-muted">
+ @if($webhook->tracksEvent('all'))
+ {{ trans('settings.webhooks_events_all') }}
+ @else
+ {{ trans_choice('settings.webhooks_x_trigger_events', $webhook->tracked_events_count, ['count' => $webhook->tracked_events_count]) }}
+ @endif
+ </div>
+ </div>
+ <div class="px-m py-xxs text-muted italic text-limit-lines-1">
+ <small>{{ $webhook->endpoint }}</small>
+ </div>
+</div>
\ No newline at end of file