mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 05:36:00 +00:00
Merge branch 'markdown-export' of https://github.com/nikhiljha/BookStack-1 into nikhiljha-markdown-export
This commit is contained in:
commit
9af636bd48
@ -7,7 +7,9 @@ use BookStack\Uploads\ImageService;
|
|||||||
use DomPDF;
|
use DomPDF;
|
||||||
use Exception;
|
use Exception;
|
||||||
use SnappyPDF;
|
use SnappyPDF;
|
||||||
|
use League\HTMLToMarkdown\HtmlConverter;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
use ZipArchive;
|
||||||
|
|
||||||
class ExportFormatter
|
class ExportFormatter
|
||||||
{
|
{
|
||||||
@ -226,4 +228,72 @@ class ExportFormatter
|
|||||||
}
|
}
|
||||||
return $text;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a page to a Markdown file.
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function pageToMarkdown(Page $page)
|
||||||
|
{
|
||||||
|
if (property_exists($page, 'markdown') && $page->markdown != '') {
|
||||||
|
return "# " . $page->name . "\n\n" . $page->markdown;
|
||||||
|
} else {
|
||||||
|
$converter = new HtmlConverter();
|
||||||
|
return "# " . $page->name . "\n\n" . $converter->convert($page->html);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a chapter to a Markdown file.
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function chapterToMarkdown(Chapter $chapter)
|
||||||
|
{
|
||||||
|
$text = "# " . $chapter->name . "\n\n";
|
||||||
|
$text .= $chapter->description . "\n\n";
|
||||||
|
foreach ($chapter->pages as $page) {
|
||||||
|
$text .= $this->pageToMarkdown($page);
|
||||||
|
}
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a book into a plain text string.
|
||||||
|
*/
|
||||||
|
public function bookToMarkdown(Book $book): string
|
||||||
|
{
|
||||||
|
$bookTree = (new BookContents($book))->getTree(false, true);
|
||||||
|
$text = "# " . $book->name . "\n\n";
|
||||||
|
foreach ($bookTree as $bookChild) {
|
||||||
|
if ($bookChild->isA('chapter')) {
|
||||||
|
$text .= $this->chapterToMarkdown($bookChild);
|
||||||
|
} else {
|
||||||
|
$text .= $this->pageToMarkdown($bookChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a book into a zip file.
|
||||||
|
*/
|
||||||
|
public function bookToZip(Book $book): string
|
||||||
|
{
|
||||||
|
// TODO: Is not unlinking the file a security risk?
|
||||||
|
$z = new ZipArchive();
|
||||||
|
$z->open("book.zip", \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
|
||||||
|
$bookTree = (new BookContents($book))->getTree(false, true);
|
||||||
|
foreach ($bookTree as $bookChild) {
|
||||||
|
if ($bookChild->isA('chapter')) {
|
||||||
|
$z->addEmptyDir($bookChild->name);
|
||||||
|
foreach ($bookChild->pages as $page) {
|
||||||
|
$filename = $bookChild->name . "/" . $page->name . ".md";
|
||||||
|
$z->addFromString($filename, $this->pageToMarkdown($page));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$z->addFromString($bookChild->name . ".md", $this->pageToMarkdown($bookChild));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "book.zip";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,4 +52,24 @@ class BookExportController extends Controller
|
|||||||
$textContent = $this->exportFormatter->bookToPlainText($book);
|
$textContent = $this->exportFormatter->bookToPlainText($book);
|
||||||
return $this->downloadResponse($textContent, $bookSlug . '.txt');
|
return $this->downloadResponse($textContent, $bookSlug . '.txt');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a book as a markdown file.
|
||||||
|
*/
|
||||||
|
public function markdown(string $bookSlug)
|
||||||
|
{
|
||||||
|
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||||
|
$textContent = $this->exportService->bookToMarkdown($book);
|
||||||
|
return $this->downloadResponse($textContent, $bookSlug . '.md');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a book as a zip file, made of markdown files.
|
||||||
|
*/
|
||||||
|
public function zip(string $bookSlug)
|
||||||
|
{
|
||||||
|
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||||
|
$filename = $this->exportService->bookToZip($book);
|
||||||
|
return response()->download($filename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,4 +54,16 @@ class ChapterExportController extends Controller
|
|||||||
$chapterText = $this->exportFormatter->chapterToPlainText($chapter);
|
$chapterText = $this->exportFormatter->chapterToPlainText($chapter);
|
||||||
return $this->downloadResponse($chapterText, $chapterSlug . '.txt');
|
return $this->downloadResponse($chapterText, $chapterSlug . '.txt');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a chapter to a simple markdown file.
|
||||||
|
* @throws NotFoundException
|
||||||
|
*/
|
||||||
|
public function markdown(string $bookSlug, string $chapterSlug)
|
||||||
|
{
|
||||||
|
// TODO: This should probably export to a zip file.
|
||||||
|
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
|
||||||
|
$chapterText = $this->exportService->chapterToMarkdown($chapter);
|
||||||
|
return $this->downloadResponse($chapterText, $chapterSlug . '.md');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,4 +60,15 @@ class PageExportController extends Controller
|
|||||||
$pageText = $this->exportFormatter->pageToPlainText($page);
|
$pageText = $this->exportFormatter->pageToPlainText($page);
|
||||||
return $this->downloadResponse($pageText, $pageSlug . '.txt');
|
return $this->downloadResponse($pageText, $pageSlug . '.txt');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a page to a simple markdown .md file.
|
||||||
|
* @throws NotFoundException
|
||||||
|
*/
|
||||||
|
public function markdown(string $bookSlug, string $pageSlug)
|
||||||
|
{
|
||||||
|
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
|
||||||
|
$pageText = $this->exportService->pageToMarkdown($page);
|
||||||
|
return $this->downloadResponse($pageText, $pageSlug . '.md');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
"laravel/socialite": "^5.1",
|
"laravel/socialite": "^5.1",
|
||||||
"league/commonmark": "^1.5",
|
"league/commonmark": "^1.5",
|
||||||
"league/flysystem-aws-s3-v3": "^1.0.29",
|
"league/flysystem-aws-s3-v3": "^1.0.29",
|
||||||
|
"league/html-to-markdown": "^4.9",
|
||||||
"nunomaduro/collision": "^3.1",
|
"nunomaduro/collision": "^3.1",
|
||||||
"onelogin/php-saml": "^4.0",
|
"onelogin/php-saml": "^4.0",
|
||||||
"predis/predis": "^1.1.6",
|
"predis/predis": "^1.1.6",
|
||||||
|
@ -5,9 +5,9 @@ WORKDIR /app
|
|||||||
|
|
||||||
# Install additional dependacnies and configure apache
|
# Install additional dependacnies and configure apache
|
||||||
RUN apt-get update -y \
|
RUN apt-get update -y \
|
||||||
&& apt-get install -y git zip unzip libpng-dev libldap2-dev wait-for-it \
|
&& apt-get install -y git zip unzip libpng-dev libldap2-dev libzip-dev wait-for-it \
|
||||||
&& docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu \
|
&& docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu \
|
||||||
&& docker-php-ext-install pdo_mysql gd ldap \
|
&& docker-php-ext-install pdo_mysql gd ldap zip \
|
||||||
&& a2enmod rewrite \
|
&& a2enmod rewrite \
|
||||||
&& sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
|
&& sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
|
||||||
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
|
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
|
||||||
|
@ -36,6 +36,7 @@ return [
|
|||||||
'export_html' => 'Contained Web File',
|
'export_html' => 'Contained Web File',
|
||||||
'export_pdf' => 'PDF File',
|
'export_pdf' => 'PDF File',
|
||||||
'export_text' => 'Plain Text File',
|
'export_text' => 'Plain Text File',
|
||||||
|
'export_md' => 'Markdown File',
|
||||||
|
|
||||||
// Permissions and restrictions
|
// Permissions and restrictions
|
||||||
'permissions' => 'Permissions',
|
'permissions' => 'Permissions',
|
||||||
|
@ -8,5 +8,6 @@
|
|||||||
<li><a href="{{ $entity->getUrl('/export/html') }}" target="_blank" rel="noopener">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
|
<li><a href="{{ $entity->getUrl('/export/html') }}" target="_blank" rel="noopener">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
|
||||||
<li><a href="{{ $entity->getUrl('/export/pdf') }}" target="_blank" rel="noopener">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
|
<li><a href="{{ $entity->getUrl('/export/pdf') }}" target="_blank" rel="noopener">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
|
||||||
<li><a href="{{ $entity->getUrl('/export/plaintext') }}" target="_blank" rel="noopener">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
|
<li><a href="{{ $entity->getUrl('/export/plaintext') }}" target="_blank" rel="noopener">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
|
||||||
|
<li><a href="{{ $entity->getUrl('/export/markdown') }}" target="_blank" rel="noopener">{{ trans('entities.export_md') }} <span class="text-muted float right">.md</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
@ -48,6 +48,8 @@ Route::group(['middleware' => 'auth'], function () {
|
|||||||
Route::put('/{bookSlug}/sort', 'BookSortController@update');
|
Route::put('/{bookSlug}/sort', 'BookSortController@update');
|
||||||
Route::get('/{bookSlug}/export/html', 'BookExportController@html');
|
Route::get('/{bookSlug}/export/html', 'BookExportController@html');
|
||||||
Route::get('/{bookSlug}/export/pdf', 'BookExportController@pdf');
|
Route::get('/{bookSlug}/export/pdf', 'BookExportController@pdf');
|
||||||
|
Route::get('/{bookSlug}/export/markdown', 'BookExportController@markdown');
|
||||||
|
Route::get('/{bookSlug}/export/zip', 'BookExportController@zip');
|
||||||
Route::get('/{bookSlug}/export/plaintext', 'BookExportController@plainText');
|
Route::get('/{bookSlug}/export/plaintext', 'BookExportController@plainText');
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
@ -58,6 +60,7 @@ Route::group(['middleware' => 'auth'], function () {
|
|||||||
Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
|
Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
|
||||||
Route::get('/{bookSlug}/page/{pageSlug}/export/pdf', 'PageExportController@pdf');
|
Route::get('/{bookSlug}/page/{pageSlug}/export/pdf', 'PageExportController@pdf');
|
||||||
Route::get('/{bookSlug}/page/{pageSlug}/export/html', 'PageExportController@html');
|
Route::get('/{bookSlug}/page/{pageSlug}/export/html', 'PageExportController@html');
|
||||||
|
Route::get('/{bookSlug}/page/{pageSlug}/export/markdown', 'PageExportController@markdown');
|
||||||
Route::get('/{bookSlug}/page/{pageSlug}/export/plaintext', 'PageExportController@plainText');
|
Route::get('/{bookSlug}/page/{pageSlug}/export/plaintext', 'PageExportController@plainText');
|
||||||
Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit');
|
Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit');
|
||||||
Route::get('/{bookSlug}/page/{pageSlug}/move', 'PageController@showMove');
|
Route::get('/{bookSlug}/page/{pageSlug}/move', 'PageController@showMove');
|
||||||
@ -92,6 +95,7 @@ Route::group(['middleware' => 'auth'], function () {
|
|||||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@showPermissions');
|
Route::get('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@showPermissions');
|
||||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/export/pdf', 'ChapterExportController@pdf');
|
Route::get('/{bookSlug}/chapter/{chapterSlug}/export/pdf', 'ChapterExportController@pdf');
|
||||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/export/html', 'ChapterExportController@html');
|
Route::get('/{bookSlug}/chapter/{chapterSlug}/export/html', 'ChapterExportController@html');
|
||||||
|
Route::get('/{bookSlug}/chapter/{chapterSlug}/export/markdown', 'ChapterExportController@markdown');
|
||||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/export/plaintext', 'ChapterExportController@plainText');
|
Route::get('/{bookSlug}/chapter/{chapterSlug}/export/plaintext', 'ChapterExportController@plainText');
|
||||||
Route::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@permissions');
|
Route::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@permissions');
|
||||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete');
|
Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete');
|
||||||
|
Loading…
Reference in New Issue
Block a user