From 2452ccebedcf879958f1396c03001a86c37b124b Mon Sep 17 00:00:00 2001 From: My Random Thoughts Date: Thu, 27 Apr 2023 19:31:23 +0100 Subject: [PATCH 1/8] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e4189f1..928fa17 100644 --- a/README.md +++ b/README.md @@ -24,4 +24,8 @@ A Python wrapper for the BookStack API. ### [Szwendacz99/BookStack-Python-exporter](https://github.com/Szwendacz99/BookStack-Python-exporter) [Python] -A customizable Python script for exporting notes from BookStack through API. \ No newline at end of file +A customizable Python script for exporting notes from BookStack through API. + +### [My-Random-Thoughts/psBookStack](https://github.com/My-Random-Thoughts/psBookStack) [PowerShell] + +A PowerShell module for interacting with BookStack via the API. From 0dc645ff7bcd46e21f2806697d440e648b69bd08 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sat, 9 Sep 2023 12:26:22 +0100 Subject: [PATCH 2/8] Updated php-export-all-books script - Added better support for markdown via extension. - Removed old fix for old 2020 bookstack bug. Related to #11 --- php-export-all-books/export-books.php | 16 +++++++++------- php-export-all-books/readme.md | 5 ++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/php-export-all-books/export-books.php b/php-export-all-books/export-books.php index a0f9ff9..d2561b7 100644 --- a/php-export-all-books/export-books.php +++ b/php-export-all-books/export-books.php @@ -17,15 +17,22 @@ // Script logic //////////////// +// Get all list of all books in the system $books = getAllBooks(); +// Get a reference to our output location $outDir = realpath($exportLocation); +// Mapping for export formats to the resulting export file extensions $extensionByFormat = [ 'pdf' => 'pdf', 'html' => 'html', 'plaintext' => 'txt', + 'markdown' => 'md', ]; +// Loop over each book, exporting each one-by-one and saving its +// contents into the output location, using the books slug as +// the file name. foreach ($books as $book) { $id = $book['id']; $extension = $extensionByFormat[$exportFormat] ?? $exportFormat; @@ -37,7 +44,7 @@ /** * Get all books from the system API. */ -function getAllBooks() { +function getAllBooks(): array { $count = 100; $offset = 0; $total = 0; @@ -47,12 +54,7 @@ function getAllBooks() { $endpoint = 'api/books?' . http_build_query(['count' => $count, 'offset' => $offset]); $resp = apiGetJson($endpoint); - // Only set total on first request, due to API bug: - // https://github.com/BookStackApp/BookStack/issues/2043 - if ($offset == 0) { - $total = $resp['total'] ?? 0; - } - + $total = $resp['total'] ?? 0; $newBooks = $resp['data'] ?? []; array_push($allBooks, ...$newBooks); $offset += $count; diff --git a/php-export-all-books/readme.md b/php-export-all-books/readme.md index b076a39..fbb0cf5 100644 --- a/php-export-all-books/readme.md +++ b/php-export-all-books/readme.md @@ -1,6 +1,6 @@ # Export All Books -This script will export all books in your preferred format (PDF, HTML or TXT). +This script will export all books in your preferred format (PDF, HTML, Markdown or TXT). ## Requirements @@ -34,4 +34,7 @@ php export-books.php pdf ./ # Export as HTML to an existing "html" directory php export-books.php html ./html + +# Export as Markdown to an existing "md-files" directory +php export-books.php markdown ./md-files ``` \ No newline at end of file From d926c758cd8d9deb1e2471397ee47534f769fb89 Mon Sep 17 00:00:00 2001 From: Skiddybison5924 <101336067+chris-devel0per@users.noreply.github.com> Date: Tue, 31 Oct 2023 17:40:40 +0100 Subject: [PATCH 3/8] Added the Flutter/Dart example (#12) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 928fa17..2a94b99 100644 --- a/README.md +++ b/README.md @@ -29,3 +29,7 @@ A customizable Python script for exporting notes from BookStack through API. ### [My-Random-Thoughts/psBookStack](https://github.com/My-Random-Thoughts/psBookStack) [PowerShell] A PowerShell module for interacting with BookStack via the API. + +### [chris-devel0per/FlutterBookstackApiExample](https://github.com/chris-devel0per/FlutterBookstackApiExample) [Flutter/Dart] + +Flutter/Dart script showcases BookStackAPI for tasks like fetching, updating content in BookStack instances. From 7effa6bcd3bbd922a60faf89dd16def609afbbf4 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Tue, 31 Oct 2023 16:44:59 +0000 Subject: [PATCH 4/8] Updated wording for FlutterBookstackApiExample in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a94b99..46b8ded 100644 --- a/README.md +++ b/README.md @@ -32,4 +32,4 @@ A PowerShell module for interacting with BookStack via the API. ### [chris-devel0per/FlutterBookstackApiExample](https://github.com/chris-devel0per/FlutterBookstackApiExample) [Flutter/Dart] -Flutter/Dart script showcases BookStackAPI for tasks like fetching, updating content in BookStack instances. +A Flutter & Dart example project which showcases the use of many BookStack API endpoints. From b7b0f9226d7bf8e1ef596a4086ef596d9d7ebebf Mon Sep 17 00:00:00 2001 From: Anchit Bajaj Date: Sat, 11 Nov 2023 20:04:48 +0100 Subject: [PATCH 5/8] Add bookstack2site --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 46b8ded..3c9e911 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,7 @@ A PowerShell module for interacting with BookStack via the API. ### [chris-devel0per/FlutterBookstackApiExample](https://github.com/chris-devel0per/FlutterBookstackApiExample) [Flutter/Dart] A Flutter & Dart example project which showcases the use of many BookStack API endpoints. + +### [IceWreck/BookStack2Site](https://github.com/IceWreck/BookStack2Site) [Go] + +CLI tool which generates static sites (using Mdbook) from Bookstack Wikis. From 2236e695dafd3232926b6b437ee64d61c143ef42 Mon Sep 17 00:00:00 2001 From: Jayden Date: Sat, 23 Dec 2023 14:06:47 -0600 Subject: [PATCH 6/8] docs: add jaypyles/obsidian_to_bookstack --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c9e911..4d6a10c 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,9 @@ This repository contains different examples of BookStack API scripts that you might find useful to use or modify. - Each folder within this repo is a different script. Each script has it's own readme. Click into a folder to see the readme for detail about the script. - -*These scripts are not part an officially supported part of the BookStack project itself and therefore may be outdated or more likely to have bugs.* +_These scripts are not part an officially supported part of the BookStack project itself and therefore may be outdated or more likely to have bugs._ ## Community Projects & Scripts @@ -37,3 +35,7 @@ A Flutter & Dart example project which showcases the use of many BookStack API e ### [IceWreck/BookStack2Site](https://github.com/IceWreck/BookStack2Site) [Go] CLI tool which generates static sites (using Mdbook) from Bookstack Wikis. + +### [jaypyles/obisidan_to_bookstack](https://github.com/jaypyles/obsidian-to-bookstack) [Python] + +CLI tool to update and sync a Bookstack instance with an Obsidian Vault From 8e94a530b0dfb5caebab8b410a893f960f79e089 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Fri, 29 Mar 2024 12:04:04 +0000 Subject: [PATCH 7/8] Added contributing section to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4d6a10c..48b6849 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ Each folder within this repo is a different script. Each script has it's own rea _These scripts are not part an officially supported part of the BookStack project itself and therefore may be outdated or more likely to have bugs._ +## Contributing + +This repository is only meant to provide starting-off points as examples. Feel free to contribute fixes for existing scripts where needed, but we're relatively strict about keeping scope limited to ease maintenance so we're not looking to expand features of the scripts directly contained in this repository. If you build upon a script and want to share that back, feel free to open a PR to add it to the "Community Projects & Scripts" section below. We may be open to new scripts but only where the scope is fixed & narrow, and where there's value in something new it's demonstrating, otherwise PRs may be rejected. If you're unsure feel free to open an issue first to query a new addition before spending time on building out a script. + ## Community Projects & Scripts Here we showcase the awesome projects and scripts built by members of the community. These may reflect more full-featured examples of API use, or may provide a more complete basis for your own work. From 28f1407c82b92d297ac6f322b296bdde51cf97b3 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Wed, 24 Jul 2024 16:42:00 +0100 Subject: [PATCH 8/8] Added php-generate-tree script --- php-generate-tree/example.txt | 9 ++ php-generate-tree/generate-tree.php | 189 ++++++++++++++++++++++++++++ php-generate-tree/readme.md | 47 +++++++ 3 files changed, 245 insertions(+) create mode 100644 php-generate-tree/example.txt create mode 100755 php-generate-tree/generate-tree.php create mode 100644 php-generate-tree/readme.md diff --git a/php-generate-tree/example.txt b/php-generate-tree/example.txt new file mode 100644 index 0000000..aabf7ac --- /dev/null +++ b/php-generate-tree/example.txt @@ -0,0 +1,9 @@ +├── BOOKSHELF 1: My wonderful shelf of notes +│ └── BOOK 39: My lovely book in my notes +│ ├── PAGE 2745: A page within the book +│ ├── CHAPTER 643: A lone chapter +│ └── CHAPTER 644: My chapter with page +│ └── PAGE 47830: My new great page +├── BOOK 239: Scratch notes +│ ├── PAGE 47870: Note A +│ └── PAGE 47872: Note B diff --git a/php-generate-tree/generate-tree.php b/php-generate-tree/generate-tree.php new file mode 100755 index 0000000..76dc316 --- /dev/null +++ b/php-generate-tree/generate-tree.php @@ -0,0 +1,189 @@ +#!/usr/bin/env php + $shelf) { + $shelvesById[$id]['books'] = getBooksForShelf($id); + usleep($apiPauseMicrosecs); +} + +// For each book, fetch its contents list +foreach ($booksById as $id => $book) { + $booksById[$id]['contents'] = apiGetJson("api/books/{$id}")['contents'] ?? []; + usleep($apiPauseMicrosecs); +} + +// Cycle through the shelves and display their contents +$isBookShownById = []; +foreach ($shelvesById as $id => $shelf) { + output($shelf, 'bookshelf', [false]); + $bookCount = count($shelf['books']); + for ($i=0; $i < $bookCount; $i++) { + $bookId = $shelf['books'][$i]; + $book = $booksById[$bookId] ?? null; + if ($book) { + outputBookAndContents($book, [false, $i === $bookCount - 1]); + $isBookShownById[strval($book['id'])] = true; + } + } +} + +// Cycle through books and display any that have not been +// part of a shelve's output +foreach ($booksById as $id => $book) { + if (isset($isBookShownById[$id])) { + continue; + } + + outputBookAndContents($book, [false]); +} + +/** + * Output a book for display, along with its contents. + */ +function outputBookAndContents(array $book, array $depthPath): void +{ + output($book, 'book', $depthPath); + $childCount = count($book['contents']); + for ($i=0; $i < $childCount; $i++) { + $child = $book['contents'][$i]; + $childPath = array_merge($depthPath, [($i === $childCount - 1)]); + output($child, $child['type'], $childPath); + $pages = $child['pages'] ?? []; + $pageCount = count($pages); + for ($j=0; $j < count($pages); $j++) { + $page = $pages[$j]; + $innerPath = array_merge($childPath, [($j === $pageCount - 1)]); + output($page, 'page', $innerPath); + } + } +} + +/** + * Output a single item for display. + */ +function output(array $item, string $type, array $depthPath): void +{ + $upperType = strtoupper($type); + $prefix = ''; + $depth = count($depthPath); + for ($i=0; $i < $depth; $i++) { + $isLastAtDepth = $depthPath[$i]; + $end = ($i === $depth - 1); + if ($end) { + $prefix .= $isLastAtDepth ? '└' : '├'; + } else { + $prefix .= $isLastAtDepth ? ' ' : '│ '; + } + } + echo $prefix . "── {$upperType} {$item['id']}: {$item['name']}\n"; +} + +/** + * Key an array of array-based data objects by 'id' value. + */ +function keyById(array $data): array +{ + $byId = []; + foreach ($data as $item) { + $id = $item['id']; + $byId[$id] = $item; + } + return $byId; +} + +/** + * Get the books for the given shelf ID. + * Returns an array of the book IDs. + */ +function getBooksForShelf(int $shelfId): array +{ + $resp = apiGetJson("api/shelves/{$shelfId}"); + return array_map(function ($bookData) { + return $bookData['id']; + }, $resp['books'] ?? []); +} + +/** + * Consume all items from the given API listing endpoint. + */ +function getAllOfAtListEndpoint(string $endpoint, array $params): array +{ + global $apiPauseMicrosecs; + $count = 100; + $offset = 0; + $all = []; + + do { + $endpoint = $endpoint . '?' . http_build_query(array_merge($params, ['count' => $count, 'offset' => $offset])); + $resp = apiGetJson($endpoint); + + $total = $resp['total'] ?? 0; + $new = $resp['data'] ?? []; + array_push($all, ...$new); + $offset += $count; + usleep($apiPauseMicrosecs); + } while ($offset < $total); + + return $all; +} + +/** + * Make a simple GET HTTP request to the API. + */ +function apiGet(string $endpoint): string +{ + global $baseUrl, $clientId, $clientSecret; + $url = rtrim($baseUrl, '/') . '/' . ltrim($endpoint, '/'); + $opts = ['http' => ['header' => "Authorization: Token {$clientId}:{$clientSecret}"]]; + $context = stream_context_create($opts); + return @file_get_contents($url, false, $context); +} + +/** + * Make a simple GET HTTP request to the API & + * decode the JSON response to an array. + */ +function apiGetJson(string $endpoint): array +{ + $data = apiGet($endpoint); + $array = json_decode($data, true); + + if (!is_array($array)) { + dd("Failed request to {$endpoint}", $data); + } + + return $array; +} + +/** + * DEBUG: Dump out the given variables and exit. + */ +function dd(...$args) +{ + foreach ($args as $arg) { + var_dump($arg); + } + exit(1); +} \ No newline at end of file diff --git a/php-generate-tree/readme.md b/php-generate-tree/readme.md new file mode 100644 index 0000000..a054297 --- /dev/null +++ b/php-generate-tree/readme.md @@ -0,0 +1,47 @@ +# Generate Tree + +This script will scan through all pages, chapters books and shelves via the API to generate a big tree structure list in plaintext. + +**This is a very simplistic single-script-file example of using the endpoints API together** +, it is not a fully-featured & validated script, it error handling is very limited. + +Keep in mind, The tree generated will reflect content visible to the API user used when running the script. + +This script follows a `((Shelves > Books > (Chapters > Pages | Pages)) | Books)` structure so books and their contents may be repeated if on multiple shelves. Books not on any shelves will be shown at the end. + +## Requirements + +You will need php (~8.1+) installed on the machine you want to run this script on. +You will also need BookStack API credentials (TOKEN_ID & TOKEN_SECRET) at the ready. + +## Running + +```bash +# Downloading the script +# ALTERNATIVELY: Clone the project from GitHub and run locally. +curl https://raw.githubusercontent.com/BookStackApp/api-scripts/main/php-generate-tree/generate-tree.php > generate-tree.php + +# Setup +# ALTERNATIVELY: Open the script and edit the variables at the top. +export BS_URL=https://bookstack.example.com # Set to be your BookStack base URL +export BS_TOKEN_ID=abc123 # Set to be your API token_id +export BS_TOKEN_SECRET=123abc # Set to be your API token_secret + +# Running the script +php generate-tree.php +``` + +## Examples + +```bash +# Generate out the tree to the command line +php generate-tree.php + +# Generate & redirect output to a file +php generate-tree.php > bookstack-tree.txt + +# Generate with the output shown on the command line and write to a file +php generate-tree.php | tee bookstack-tree.txt +``` + +An example of the output can be seen in the [example.txt](./example.txt) file within the directory of this readme. \ No newline at end of file