Skip to content
This repository was archived by the owner on Jul 27, 2024. It is now read-only.

Commit e45930b

Browse files
committed
Added static site script
1 parent a62c7f1 commit e45930b

File tree

3 files changed

+256
-0
lines changed

3 files changed

+256
-0
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea/
2+
node_modules/

Diff for: php-book-to-static-site/book-to-static.php

+217
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
// API Credentials
5+
// You can either provide them as environment variables
6+
// or hard-code them in the empty strings below.
7+
$apiUrl = getenv('BS_URL') ?: '';
8+
$clientId = getenv('BS_TOKEN_ID') ?: '';
9+
$clientSecret = getenv('BS_TOKEN_SECRET') ?: '';
10+
11+
// Output Folder
12+
// Can be provided as a arguments when calling the script
13+
// or be hard-coded as strings below.
14+
$bookSlug = $argv[1] ?? '';
15+
$outFolder = $argv[2] ?? './out';
16+
17+
// Script logic
18+
////////////////
19+
20+
if (empty($bookSlug) || empty($outFolder)) {
21+
errorOut("Both a book slug and output folder must be provided");
22+
}
23+
24+
if (!is_dir($outFolder)) {
25+
mkdir($outFolder, 0777, true);
26+
}
27+
28+
$outDir = realpath($outFolder);
29+
$book = getBookBySlug($bookSlug);
30+
31+
if (is_null($book)) {
32+
errorOut("Could not find book with the URL slug: {$bookSlug}");
33+
}
34+
35+
$chapters = getAllOfAtListEndpoint("api/chapters", ['filter[book_id]' => $book['id']]);
36+
$pages = getAllOfAtListEndpoint("api/pages", ['filter[book_id]' => $book['id']]);
37+
38+
foreach ($pages as $index => $page) {
39+
$pages[$index] = apiGetJson("api/pages/{$page['id']}");
40+
}
41+
42+
if (!is_dir($outDir . "/images")) {
43+
mkdir($outDir . "/images", 0777, true);
44+
}
45+
46+
$directBookPages = array_filter($pages, function($page) {
47+
return empty($page['chapter_id']);
48+
});
49+
50+
// Create book index file
51+
$bookIndex = getBookContent($book, $chapters, $directBookPages);
52+
file_put_contents($outDir . "/index.html", $bookIndex);
53+
54+
foreach ($chapters as $chapter) {
55+
$childPages = array_filter($pages, function($page) use ($chapter) {
56+
return $page['chapter_id'] == $chapter['id'];
57+
});
58+
$chapterPage = getChapterContent($chapter, $childPages);
59+
file_put_contents($outDir . "/chapter-{$chapter['slug']}.html", $chapterPage);
60+
61+
foreach ($childPages as $childPage) {
62+
$childPageContent = getPageContent($childPage, $chapter);
63+
$childPageContent = extractImagesFromHtml($childPageContent);
64+
file_put_contents($outDir . "/page-{$childPage['slug']}.html", $childPageContent);
65+
}
66+
}
67+
68+
foreach ($directBookPages as $directPage) {
69+
$directPageContent = getPageContent($directPage, null);
70+
$directPageContent = extractImagesFromHtml($directPageContent);
71+
file_put_contents($outDir . "/page-{$directPage['slug']}.html", $directPageContent);
72+
}
73+
74+
function extractImagesFromHtml(string $html): string {
75+
global $outDir;
76+
$matches = [];
77+
preg_match_all('/<img.*?src=["\'](.*?)[\'"].*?>/i', $html, $matches);
78+
foreach (array_unique($matches[1] ?? []) as $url) {
79+
$image = file_get_contents($url);
80+
$name = basename($url);
81+
$fileName = $name;
82+
$count = 1;
83+
while (file_exists($outDir . "/images/" . $fileName)) {
84+
$fileName = $count . '-' . $name;
85+
}
86+
file_put_contents($outDir . "/images/" . $fileName, $image);
87+
$html = str_replace($url, "./images/" . $fileName, $html);
88+
}
89+
return $html;
90+
}
91+
92+
function getImageFile($url): string {
93+
global $apiUrl;
94+
if (strpos(strtolower($url), strtolower($apiUrl)) === 0) {
95+
$url = substr($url, strlen($apiUrl));
96+
return apiGet($url);
97+
}
98+
return file_get_contents($url);
99+
}
100+
101+
function getBookContent(array $book, array $chapters, array $pages): string {
102+
$content = "<h1>{$book['name']}</h1>";
103+
$content .= "<p>{$book['description']}</p>";
104+
$content .= "<hr>";
105+
if (count($chapters) > 0) {
106+
$content .= "<h3>Chapters</h3><ul>";
107+
foreach ($chapters as $chapter) {
108+
$content .= "<li><a href='./chapter-{$chapter['slug']}.html'>{$chapter['name']}</a></li>";
109+
}
110+
$content .= "</ul>";
111+
}
112+
if (count($pages) > 0) {
113+
$content .= "<h3>Pages</h3><ul>";
114+
foreach ($pages as $page) {
115+
$content .= "<li><a href='./page-{$page['slug']}.html'>{$page['name']}</a></li>";
116+
}
117+
$content .= "</ul>";
118+
}
119+
return $content;
120+
}
121+
122+
function getChapterContent(array $chapter, array $pages): string {
123+
$content = "<p><a href='./index.html'>Back to book</a></p>";
124+
$content .= "<h1>{$chapter['name']}</h1>";
125+
$content .= "<p>{$chapter['description']}</p>";
126+
$content .= "<hr>";
127+
if (count($pages) > 0) {
128+
$content .= "<h3>Pages</h3><ul>";
129+
foreach ($pages as $page) {
130+
$content .= "<li><a href='./page-{$page['slug']}.html'>{$page['name']}</a></li>";
131+
}
132+
$content .= "</ul>";
133+
}
134+
return $content;
135+
}
136+
137+
function getPageContent(array $page, ?array $parentChapter): string {
138+
if (is_null($parentChapter)) {
139+
$content = "<p><a href='./index.html'>Back to book</a></p>";
140+
} else {
141+
$content = "<p><a href='./chapter-{$parentChapter['slug']}.html'>Back to chapter</a></p>";
142+
}
143+
$content .= "<h1>{$page['name']}</h1>";
144+
$content .= "<div>{$page['html']}</div>";
145+
return $content;
146+
}
147+
148+
/**
149+
* Get a single book by the slug or return null if not exists.
150+
*/
151+
function getBookBySlug(string $slug): ?array {
152+
$endpoint = 'api/books?' . http_build_query(['filter[slug]' => $slug]);
153+
$resp = apiGetJson($endpoint);
154+
$book = $resp['data'][0] ?? null;
155+
156+
if (!is_null($book)) {
157+
$book = apiGetJson("api/books/{$book['id']}") ?? null;
158+
}
159+
return $book;
160+
}
161+
162+
/**
163+
* Get all books from the system API.
164+
*/
165+
function getAllOfAtListEndpoint(string $endpoint, array $params): array {
166+
$count = 100;
167+
$offset = 0;
168+
$total = 0;
169+
$all = [];
170+
171+
do {
172+
$endpoint = $endpoint . '?' . http_build_query(array_merge($params, ['count' => $count, 'offset' => $offset]));
173+
$resp = apiGetJson($endpoint);
174+
175+
$total = $resp['total'] ?? 0;
176+
$new = $resp['data'] ?? [];
177+
array_push($all, ...$new);
178+
$offset += $count;
179+
} while ($offset < $total);
180+
181+
return $all;
182+
}
183+
184+
/**
185+
* Make a simple GET HTTP request to the API.
186+
*/
187+
function apiGet(string $endpoint): string {
188+
global $apiUrl, $clientId, $clientSecret;
189+
$url = rtrim($apiUrl, '/') . '/' . ltrim($endpoint, '/');
190+
$opts = ['http' => ['header' => "Authorization: Token {$clientId}:{$clientSecret}"]];
191+
$context = stream_context_create($opts);
192+
return file_get_contents($url, false, $context);
193+
}
194+
195+
/**
196+
* Make a simple GET HTTP request to the API &
197+
* decode the JSON response to an array.
198+
*/
199+
function apiGetJson(string $endpoint): array {
200+
$data = apiGet($endpoint);
201+
return json_decode($data, true);
202+
}
203+
204+
/**
205+
* DEBUG: Dump out the given variables and exit.
206+
*/
207+
function dd(...$args) {
208+
foreach ($args as $arg) {
209+
var_dump($arg);
210+
}
211+
exit(1);
212+
}
213+
214+
function errorOut(string $text) {
215+
echo "ERROR: " . $text;
216+
exit(1);
217+
}

Diff for: php-book-to-static-site/readme.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Book to Static Site
2+
3+
This script will export all chapters and pages within a given book to a folder
4+
of basic HTML and image files which could act as a static site.
5+
6+
Once ran, you should be able to open `index.html` within the output folder to start browsing.
7+
8+
**This is a very simplistic single-script-file example of using the book, chapter and pages
9+
API together**, the output lacks a lot of detail including styling and inter-content link transforming.
10+
11+
## Requirements
12+
13+
You will need php (~7.2+) installed on the machine you want to run this script on.
14+
You will also need BookStack API credentials (TOKEN_ID & TOKEN_SECRET) at the ready.
15+
16+
## Running
17+
18+
```bash
19+
# Downloading the script
20+
curl https://raw.githubusercontent.com/BookStackApp/api-scripts/main/php-book-to-static-site/book-to-static.php > book-to-static.php
21+
22+
# Setup
23+
# ALTERNATIVELY: Open the script and edit the variables at the top.
24+
export BS_URL=https://bookstack.example.com # Set to be your BookStack base URL
25+
export BS_TOKEN_ID=abc123 # Set to be your API token_id
26+
export BS_TOKEN_SECRET=123abc # Set to be your API token_secret
27+
28+
# Running the script
29+
php book-to-static.php <book_url_slug> <output_dir>
30+
```
31+
32+
## Examples
33+
34+
```bash
35+
# Export a book with URL slug of my_book to an "out" directory
36+
php book-to-static.php my_book ./out
37+
```

0 commit comments

Comments
 (0)