diff --git a/app/Entities/Models/Page.php b/app/Entities/Models/Page.php index a3a04f403..aeee50d0f 100644 --- a/app/Entities/Models/Page.php +++ b/app/Entities/Models/Page.php @@ -25,8 +25,8 @@ use Permissions; */ class Page extends BookChild { - public static $listAttributes = ['name', 'id', 'slug', 'book_id', 'text', 'created_at', 'updated_at']; - public static $contentAttributes = ['name', 'id', 'slug', 'book_id', 'html', 'text', 'created_at', 'updated_at']; + public static $listAttributes = ['name', 'id', 'slug', 'book_id', 'chapter_id', 'draft', 'template', 'text', 'created_at', 'updated_at']; + public static $contentAttributes = ['name', 'id', 'slug', 'book_id', 'chapter_id', 'draft', 'template', 'html', 'text', 'created_at', 'updated_at']; protected $fillable = ['name', 'priority', 'markdown']; diff --git a/app/Entities/Tools/BookContents.php b/app/Entities/Tools/BookContents.php index 0b8e714fc..8622d5e12 100644 --- a/app/Entities/Tools/BookContents.php +++ b/app/Entities/Tools/BookContents.php @@ -45,7 +45,7 @@ class BookContents */ public function getTree(bool $showDrafts = false, bool $renderPages = false): Collection { - $pages = $this->getPages($showDrafts); + $pages = $this->getPages($showDrafts, $renderPages); $chapters = Chapter::visible()->where('book_id', '=', $this->book->id)->get(); $all = collect()->concat($pages)->concat($chapters); $chapterMap = $chapters->keyBy('id'); diff --git a/app/Entities/Tools/PageContent.php b/app/Entities/Tools/PageContent.php index 9a53e2d24..7a7dd407b 100644 --- a/app/Entities/Tools/PageContent.php +++ b/app/Entities/Tools/PageContent.php @@ -223,7 +223,7 @@ class PageContent */ public function render(bool $blankIncludes = false): string { - $content = $this->page->html; + $content = $this->page->html ?? ''; if (!config('app.allow_content_scripts')) { $content = HtmlContentFilter::removeScripts($content); diff --git a/app/Http/Controllers/BookSortController.php b/app/Http/Controllers/BookSortController.php index bc67a8e1c..0bd394778 100644 --- a/app/Http/Controllers/BookSortController.php +++ b/app/Http/Controllers/BookSortController.php @@ -43,7 +43,7 @@ class BookSortController extends Controller $book = $this->bookRepo->getBySlug($bookSlug); $bookChildren = (new BookContents($book))->getTree(); - return view('books.sort-box', ['book' => $book, 'bookChildren' => $bookChildren]); + return view('books.parts.sort-box', ['book' => $book, 'bookChildren' => $bookChildren]); } /** diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index c2ee826c3..6706de575 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -82,7 +82,7 @@ class HomeController extends Controller $shelves = app(BookshelfRepo::class)->getAllPaginated(18, $commonData['sort'], $commonData['order']); $data = array_merge($commonData, ['shelves' => $shelves]); - return view('common.home-shelves', $data); + return view('home.shelves', $data); } if ($homepageOption === 'books') { @@ -90,7 +90,7 @@ class HomeController extends Controller $books = $bookRepo->getAllPaginated(18, $commonData['sort'], $commonData['order']); $data = array_merge($commonData, ['books' => $books]); - return view('common.home-book', $data); + return view('home.books', $data); } if ($homepageOption === 'page') { @@ -100,26 +100,24 @@ class HomeController extends Controller $pageContent = new PageContent($customHomepage); $customHomepage->html = $pageContent->render(true); - return view('common.home-custom', array_merge($commonData, ['customHomepage' => $customHomepage])); + return view('home.specific-page', array_merge($commonData, ['customHomepage' => $customHomepage])); } - return view('common.home', $commonData); + return view('home.default', $commonData); } /** * Get custom head HTML, Used in ajax calls to show in editor. - * - * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function customHeadContent() { - return view('partials.custom-head'); + return view('common.custom-head'); } /** * Show the view for /robots.txt. */ - public function getRobots() + public function robots() { $sitePublic = setting('app-public', false); $allowRobots = config('app.allow_robots'); @@ -129,14 +127,14 @@ class HomeController extends Controller } return response() - ->view('common.robots', ['allowRobots' => $allowRobots]) + ->view('misc.robots', ['allowRobots' => $allowRobots]) ->header('Content-Type', 'text/plain'); } /** * Show the route for 404 responses. */ - public function getNotFound() + public function notFound() { return response()->view('errors.404', [], 404); } diff --git a/app/Http/Controllers/Images/DrawioImageController.php b/app/Http/Controllers/Images/DrawioImageController.php index 2a4576df1..d99bb8e6f 100644 --- a/app/Http/Controllers/Images/DrawioImageController.php +++ b/app/Http/Controllers/Images/DrawioImageController.php @@ -30,7 +30,7 @@ class DrawioImageController extends Controller $imgData = $this->imageRepo->getEntityFiltered('drawio', $parentTypeFilter, $page, 24, $uploadedToFilter, $searchTerm); - return view('components.image-manager-list', [ + return view('pages.parts.image-manager-list', [ 'images' => $imgData['images'], 'hasMore' => $imgData['has_more'], ]); diff --git a/app/Http/Controllers/Images/GalleryImageController.php b/app/Http/Controllers/Images/GalleryImageController.php index 0f0b330ee..5484411d3 100644 --- a/app/Http/Controllers/Images/GalleryImageController.php +++ b/app/Http/Controllers/Images/GalleryImageController.php @@ -33,7 +33,7 @@ class GalleryImageController extends Controller $imgData = $this->imageRepo->getEntityFiltered('gallery', $parentTypeFilter, $page, 24, $uploadedToFilter, $searchTerm); - return view('components.image-manager-list', [ + return view('pages.parts.image-manager-list', [ 'images' => $imgData['images'], 'hasMore' => $imgData['has_more'], ]); diff --git a/app/Http/Controllers/Images/ImageController.php b/app/Http/Controllers/Images/ImageController.php index 4c9737887..4070a0e2f 100644 --- a/app/Http/Controllers/Images/ImageController.php +++ b/app/Http/Controllers/Images/ImageController.php @@ -65,7 +65,7 @@ class ImageController extends Controller $this->imageRepo->loadThumbs($image); - return view('components.image-manager-form', [ + return view('pages.parts.image-manager-form', [ 'image' => $image, 'dependantPages' => null, ]); @@ -87,7 +87,7 @@ class ImageController extends Controller $this->imageRepo->loadThumbs($image); - return view('components.image-manager-form', [ + return view('pages.parts.image-manager-form', [ 'image' => $image, 'dependantPages' => $dependantPages ?? null, ]); diff --git a/app/Http/Controllers/PageTemplateController.php b/app/Http/Controllers/PageTemplateController.php index 232d0d442..1e24c29ee 100644 --- a/app/Http/Controllers/PageTemplateController.php +++ b/app/Http/Controllers/PageTemplateController.php @@ -31,7 +31,7 @@ class PageTemplateController extends Controller $templates->appends(['search' => $search]); } - return view('pages.template-manager-list', [ + return view('pages.parts.template-manager-list', [ 'templates' => $templates, ]); } diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index d4431e166..d12c23b5a 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -54,7 +54,7 @@ class SearchController extends Controller $term = $request->get('term', ''); $results = $this->searchRunner->searchBook($bookId, $term); - return view('partials.entity-list', ['entities' => $results]); + return view('entities.list', ['entities' => $results]); } /** @@ -65,7 +65,7 @@ class SearchController extends Controller $term = $request->get('term', ''); $results = $this->searchRunner->searchChapter($chapterId, $term); - return view('partials.entity-list', ['entities' => $results]); + return view('entities.list', ['entities' => $results]); } /** @@ -86,7 +86,7 @@ class SearchController extends Controller $entities = (new Popular())->run(20, 0, $entityTypes, $permission); } - return view('search.entity-ajax-list', ['entities' => $entities]); + return view('search.parts.entity-ajax-list', ['entities' => $entities]); } /** @@ -99,6 +99,6 @@ class SearchController extends Controller $entities = (new SiblingFetcher())->fetch($type, $id); - return view('partials.entity-list-basic', ['entities' => $entities, 'style' => 'compact']); + return view('entities.list-basic', ['entities' => $entities, 'style' => 'compact']); } } diff --git a/app/Http/Controllers/UserSearchController.php b/app/Http/Controllers/UserSearchController.php index 4150caf04..f7ed9e57a 100644 --- a/app/Http/Controllers/UserSearchController.php +++ b/app/Http/Controllers/UserSearchController.php @@ -27,6 +27,6 @@ class UserSearchController extends Controller $users = $query->get(); - return view('components.user-select-list', compact('users')); + return view('form.user-select-list', compact('users')); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index f7c8fe492..145a7645b 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -54,7 +54,7 @@ class AppServiceProvider extends ServiceProvider ]); // View Composers - View::composer('partials.breadcrumbs', BreadcrumbsViewComposer::class); + View::composer('entities.breadcrumbs', BreadcrumbsViewComposer::class); } /** diff --git a/resources/views/api-docs/index.blade.php b/resources/views/api-docs/index.blade.php index 56f7135c3..5bec265e8 100644 --- a/resources/views/api-docs/index.blade.php +++ b/resources/views/api-docs/index.blade.php @@ -1,4 +1,4 @@ -@extends('simple-layout') +@extends('layouts.simple') @section('body') @@ -37,148 +37,7 @@
-

Getting Started

- -
Authentication
-

- To access the API a user has to have the "Access System API" permission enabled on one of their assigned roles. - Permissions to content accessed via the API is limited by the roles & permissions assigned to the user that's used to access the API. -

-

Authentication to use the API is primarily done using API Tokens. Once the "Access System API" permission has been assigned to a user, a "API Tokens" section should be visible when editing their user profile. Choose "Create Token" and enter an appropriate name and expiry date, relevant for your API usage then press "Save". A "Token ID" and "Token Secret" will be immediately displayed. These values should be used as a header in API HTTP requests in the following format:

-
Authorization: Token <token_id>:<token_secret>
-

Here's an example of an authorized cURL request to list books in the system:

-
curl --request GET \
-  --url https://example.com/api/books \
-  --header 'Authorization: Token C6mdvEQTGnebsmVn3sFNeeuelGEBjyQp:NOvD3VlzuSVuBPNaf1xWHmy7nIRlaj22'
-

If already logged into the system within the browser, via a user account with permission to access the API, the system will also accept an existing session meaning you can browse API endpoints directly in the browser or use the browser devtools to play with the API.

- -
- -
Request Format
-

The API is primarily design to be interfaced using JSON so the majority of API endpoints, that accept data, will read JSON request data although application/x-www-form-urlencoded request data is also accepted. Endpoints that receive file data will need data sent in a multipart/form-data format although this will be highlighted in the documentation for such endpoints.

-

For endpoints in this documentation that accept data, a "Body Parameters" table will be available showing the parameters that will accepted in the request. Any rules for the values of such parameters, such as the data-type or if they're required, will be shown alongside the parameter name.

- -
- -
Listing Endpoints
-

Some endpoints will return a list of data models. These endpoints will return an array of the model data under a data property along with a numeric total property to indicate the total number of records found for the query within the system. Here's an example of a listing response:

-
{
-  "data": [
-    {
-      "id": 1,
-      "name": "BookStack User Guide",
-      "slug": "bookstack-user-guide",
-      "description": "This is a general guide on using BookStack on a day-to-day basis.",
-      "created_at": "2019-05-05 21:48:46",
-      "updated_at": "2019-12-11 20:57:31",
-      "created_by": 1,
-      "updated_by": 1,
-      "image_id": 3
-    }
-  ],
-  "total": 16
-}
-

- There are a number of standard URL parameters that can be supplied to manipulate and page through the results returned from a listing endpoint: -

- - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterDetailsExamples
count - Specify how many records will be returned in the response.
- (Default: {{ config('api.default_item_count') }}, Max: {{ config('api.max_item_count') }}) -
Limit the count to 50
?count=50
offset - Specify how many records to skip over in the response.
- (Default: 0) -
Skip over the first 100 records
?offset=100
sort - Specify what field is used to sort the data and the direction of the sort (Ascending or Descending).
- Value is the name of a field, A + or - prefix dictates ordering.
- Direction defaults to ascending.
- Can use most fields shown in the response. -
- Sort by name ascending
?sort=+name

- Sort by "Created At" date descending
?sort=-created_at -
filter[<field>] - Specify a filter to be applied to the query. Can use most fields shown in the response.
- By default a filter will apply a "where equals" query but the below operations are available using the format filter[<field>:<operation>]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
eqWhere <field> equals the filter value.
neWhere <field> does not equal the filter value.
gtWhere <field> is greater than the filter value.
ltWhere <field> is less than the filter value.
gteWhere <field> is greater than or equal to the filter value.
lteWhere <field> is less than or equal to the filter value.
like - Where <field> is "like" the filter value.
- % symbols can be used as wildcards. -
-
- Filter where id is 5:
?filter[id]=5

- Filter where id is not 5:
?filter[id:ne]=5

- Filter where name contains "cat":
?filter[name:like]=%cat%

- Filter where created after 2020-01-01:
?filter[created_at:gt]=2020-01-01 -
- -
- -
Error Handling
-

- Successful responses will return a 200 or 204 HTTP response code. Errors will return a 4xx or a 5xx HTTP response code depending on the type of error. Errors follow a standard format as shown below. The message provided may be translated depending on the configured language of the system in addition to the API users' language preference. The code provided in the JSON response will match the HTTP response code. -

- -
{
-	"error": {
-		"code": 401,
-		"message": "No authorization token found on the request"
-	}
-}
-
- + @include('api-docs.parts.getting-started')
@foreach($docs as $model => $endpoints) @@ -186,52 +45,7 @@

{{ $model }}

@foreach($endpoints as $endpoint) -
{{ $endpoint['controller_method_kebab'] }}
-
- {{ $endpoint['method'] }} - @if($endpoint['controller_method_kebab'] === 'list') - {{ url($endpoint['uri']) }} - @else - {{ url($endpoint['uri']) }} - @endif -
-

{{ $endpoint['description'] ?? '' }}

- @if($endpoint['body_params'] ?? false) -
- Body Parameters - - - - - - @foreach($endpoint['body_params'] as $paramName => $rules) - - - - - @endforeach -
Param NameValue Rules
{{ $paramName }} - @foreach($rules as $rule) - {{ $rule }} - @endforeach -
-
- @endif - @if($endpoint['example_request'] ?? false) -
- Example Request -
{{ $endpoint['example_request'] }}
-
- @endif - @if($endpoint['example_response'] ?? false) -
- Example Response -
{{ $endpoint['example_response'] }}
-
- @endif - @if(!$loop->last) -
- @endif + @include('api-docs.parts.endpoint', ['endpoint' => $endpoint, 'loop' => $loop]) @endforeach @endforeach diff --git a/resources/views/api-docs/parts/endpoint.blade.php b/resources/views/api-docs/parts/endpoint.blade.php new file mode 100644 index 000000000..c1bce805b --- /dev/null +++ b/resources/views/api-docs/parts/endpoint.blade.php @@ -0,0 +1,52 @@ +
{{ $endpoint['controller_method_kebab'] }}
+ +
+ {{ $endpoint['method'] }} + @if($endpoint['controller_method_kebab'] === 'list') + {{ url($endpoint['uri']) }} + @else + {{ url($endpoint['uri']) }} + @endif +
+ +

{{ $endpoint['description'] ?? '' }}

+ +@if($endpoint['body_params'] ?? false) +
+ Body Parameters + + + + + + @foreach($endpoint['body_params'] as $paramName => $rules) + + + + + @endforeach +
Param NameValue Rules
{{ $paramName }} + @foreach($rules as $rule) + {{ $rule }} + @endforeach +
+
+@endif + +@if($endpoint['example_request'] ?? false) +
+ Example Request +
{{ $endpoint['example_request'] }}
+
+@endif + +@if($endpoint['example_response'] ?? false) +
+ Example Response +
{{ $endpoint['example_response'] }}
+
+@endif + +@if(!$loop->last) +
+@endif \ No newline at end of file diff --git a/resources/views/api-docs/parts/getting-started.blade.php b/resources/views/api-docs/parts/getting-started.blade.php new file mode 100644 index 000000000..ba0f85fc7 --- /dev/null +++ b/resources/views/api-docs/parts/getting-started.blade.php @@ -0,0 +1,141 @@ +

Getting Started

+ +
Authentication
+

+ To access the API a user has to have the "Access System API" permission enabled on one of their assigned roles. + Permissions to content accessed via the API is limited by the roles & permissions assigned to the user that's used to access the API. +

+

Authentication to use the API is primarily done using API Tokens. Once the "Access System API" permission has been assigned to a user, a "API Tokens" section should be visible when editing their user profile. Choose "Create Token" and enter an appropriate name and expiry date, relevant for your API usage then press "Save". A "Token ID" and "Token Secret" will be immediately displayed. These values should be used as a header in API HTTP requests in the following format:

+
Authorization: Token <token_id>:<token_secret>
+

Here's an example of an authorized cURL request to list books in the system:

+
curl --request GET \
+  --url https://example.com/api/books \
+  --header 'Authorization: Token C6mdvEQTGnebsmVn3sFNeeuelGEBjyQp:NOvD3VlzuSVuBPNaf1xWHmy7nIRlaj22'
+

If already logged into the system within the browser, via a user account with permission to access the API, the system will also accept an existing session meaning you can browse API endpoints directly in the browser or use the browser devtools to play with the API.

+ +
+ +
Request Format
+

The API is primarily design to be interfaced using JSON so the majority of API endpoints, that accept data, will read JSON request data although application/x-www-form-urlencoded request data is also accepted. Endpoints that receive file data will need data sent in a multipart/form-data format although this will be highlighted in the documentation for such endpoints.

+

For endpoints in this documentation that accept data, a "Body Parameters" table will be available showing the parameters that will accepted in the request. Any rules for the values of such parameters, such as the data-type or if they're required, will be shown alongside the parameter name.

+ +
+ +
Listing Endpoints
+

Some endpoints will return a list of data models. These endpoints will return an array of the model data under a data property along with a numeric total property to indicate the total number of records found for the query within the system. Here's an example of a listing response:

+
{
+  "data": [
+    {
+      "id": 1,
+      "name": "BookStack User Guide",
+      "slug": "bookstack-user-guide",
+      "description": "This is a general guide on using BookStack on a day-to-day basis.",
+      "created_at": "2019-05-05 21:48:46",
+      "updated_at": "2019-12-11 20:57:31",
+      "created_by": 1,
+      "updated_by": 1,
+      "image_id": 3
+    }
+  ],
+  "total": 16
+}
+

+ There are a number of standard URL parameters that can be supplied to manipulate and page through the results returned from a listing endpoint: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterDetailsExamples
count + Specify how many records will be returned in the response.
+ (Default: {{ config('api.default_item_count') }}, Max: {{ config('api.max_item_count') }}) +
Limit the count to 50
?count=50
offset + Specify how many records to skip over in the response.
+ (Default: 0) +
Skip over the first 100 records
?offset=100
sort + Specify what field is used to sort the data and the direction of the sort (Ascending or Descending).
+ Value is the name of a field, A + or - prefix dictates ordering.
+ Direction defaults to ascending.
+ Can use most fields shown in the response. +
+ Sort by name ascending
?sort=+name

+ Sort by "Created At" date descending
?sort=-created_at +
filter[<field>] + Specify a filter to be applied to the query. Can use most fields shown in the response.
+ By default a filter will apply a "where equals" query but the below operations are available using the format filter[<field>:<operation>]
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
eqWhere <field> equals the filter value.
neWhere <field> does not equal the filter value.
gtWhere <field> is greater than the filter value.
ltWhere <field> is less than the filter value.
gteWhere <field> is greater than or equal to the filter value.
lteWhere <field> is less than or equal to the filter value.
like + Where <field> is "like" the filter value.
+ % symbols can be used as wildcards. +
+
+ Filter where id is 5:
?filter[id]=5

+ Filter where id is not 5:
?filter[id:ne]=5

+ Filter where name contains "cat":
?filter[name:like]=%cat%

+ Filter where created after 2020-01-01:
?filter[created_at:gt]=2020-01-01 +
+ +
+ +
Error Handling
+

+ Successful responses will return a 200 or 204 HTTP response code. Errors will return a 4xx or a 5xx HTTP response code depending on the type of error. Errors follow a standard format as shown below. The message provided may be translated depending on the configured language of the system in addition to the API users' language preference. The code provided in the JSON response will match the HTTP response code. +

+ +
{
+	"error": {
+		"code": 401,
+		"message": "No authorization token found on the request"
+	}
+}
+
\ No newline at end of file diff --git a/resources/views/attachments/manager-edit-form.blade.php b/resources/views/attachments/manager-edit-form.blade.php index ee86dc240..15837448a 100644 --- a/resources/views/attachments/manager-edit-form.blade.php +++ b/resources/views/attachments/manager-edit-form.blade.php @@ -22,7 +22,7 @@
- @include('components.dropzone', [ + @include('form.dropzone', [ 'placeholder' => trans('entities.attachments_edit_drop_upload'), 'url' => url('/attachments/upload/' . $attachment->id), 'successMessage' => trans('entities.attachments_file_updated'), diff --git a/resources/views/attachments/manager.blade.php b/resources/views/attachments/manager.blade.php index 4628f7495..024cb583c 100644 --- a/resources/views/attachments/manager.blade.php +++ b/resources/views/attachments/manager.blade.php @@ -18,7 +18,7 @@ @include('attachments.manager-list', ['attachments' => $page->attachments->all()])