]> BookStack Code Mirror - bookstack/commitdiff
Added chapter export options
authorDan Brown <redacted>
Sun, 26 Feb 2017 14:25:02 +0000 (14:25 +0000)
committerDan Brown <redacted>
Sun, 26 Feb 2017 14:25:02 +0000 (14:25 +0000)
Closes #177

app/Http/Controllers/ChapterController.php
app/Http/Controllers/PageController.php
app/Services/ExportService.php
resources/views/chapters/export.blade.php [new file with mode: 0644]
resources/views/chapters/show.blade.php
routes/web.php
tests/Entity/ExportTest.php

index 1760ee5c6a3ee0502c6b69e1202ea0c4c742d48c..ceeb2a3ef8ba50b8837355f03981e67a4447d6b8 100644 (file)
@@ -3,6 +3,7 @@
 use Activity;
 use BookStack\Repos\EntityRepo;
 use BookStack\Repos\UserRepo;
+use BookStack\Services\ExportService;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
 use Views;
@@ -12,16 +13,19 @@ class ChapterController extends Controller
 
     protected $userRepo;
     protected $entityRepo;
+    protected $exportService;
 
     /**
      * ChapterController constructor.
      * @param EntityRepo $entityRepo
      * @param UserRepo $userRepo
+     * @param ExportService $exportService
      */
-    public function __construct(EntityRepo $entityRepo, UserRepo $userRepo)
+    public function __construct(EntityRepo $entityRepo, UserRepo $userRepo, ExportService $exportService)
     {
         $this->entityRepo = $entityRepo;
         $this->userRepo = $userRepo;
+        $this->exportService = $exportService;
         parent::__construct();
     }
 
@@ -236,4 +240,52 @@ class ChapterController extends Controller
         session()->flash('success', trans('entities.chapters_permissions_success'));
         return redirect($chapter->getUrl());
     }
+
+    /**
+     * Exports a chapter to pdf .
+     * @param string $bookSlug
+     * @param string $chapterSlug
+     * @return \Illuminate\Http\Response
+     */
+    public function exportPdf($bookSlug, $chapterSlug)
+    {
+        $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
+        $pdfContent = $this->exportService->chapterToPdf($chapter);
+        return response()->make($pdfContent, 200, [
+            'Content-Type'        => 'application/octet-stream',
+            'Content-Disposition' => 'attachment; filename="' . $chapterSlug . '.pdf'
+        ]);
+    }
+
+    /**
+     * Export a chapter to a self-contained HTML file.
+     * @param string $bookSlug
+     * @param string $chapterSlug
+     * @return \Illuminate\Http\Response
+     */
+    public function exportHtml($bookSlug, $chapterSlug)
+    {
+        $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
+        $containedHtml = $this->exportService->chapterToContainedHtml($chapter);
+        return response()->make($containedHtml, 200, [
+            'Content-Type'        => 'application/octet-stream',
+            'Content-Disposition' => 'attachment; filename="' . $chapterSlug . '.html'
+        ]);
+    }
+
+    /**
+     * Export a chapter to a simple plaintext .txt file.
+     * @param string $bookSlug
+     * @param string $chapterSlug
+     * @return \Illuminate\Http\Response
+     */
+    public function exportPlainText($bookSlug, $chapterSlug)
+    {
+        $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
+        $containedHtml = $this->exportService->chapterToPlainText($chapter);
+        return response()->make($containedHtml, 200, [
+            'Content-Type'        => 'application/octet-stream',
+            'Content-Disposition' => 'attachment; filename="' . $chapterSlug . '.txt'
+        ]);
+    }
 }
index 4a29c20d62f7dc6db65c49ba1541254755560c31..c97597bc4834822b5947eec2a3f0cc8964e3510c 100644 (file)
@@ -429,7 +429,7 @@ class PageController extends Controller
     }
 
     /**
-     * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper.
+     * Exports a page to a PDF.
      * https://github.com/barryvdh/laravel-dompdf
      * @param string $bookSlug
      * @param string $pageSlug
index 3ac69871885853f166d5fa2cd67034d0d0225c0d..78cef41a4d333270c0af29b7b5e7fba07ec03514 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Services;
 
 use BookStack\Book;
+use BookStack\Chapter;
 use BookStack\Page;
 use BookStack\Repos\EntityRepo;
 
@@ -33,6 +34,24 @@ class ExportService
         return $this->containHtml($pageHtml);
     }
 
+    /**
+     * Convert a chapter to a self-contained HTML file.
+     * @param Chapter $chapter
+     * @return mixed|string
+     */
+    public function chapterToContainedHtml(Chapter $chapter)
+    {
+        $pages = $this->entityRepo->getChapterChildren($chapter);
+        $pages->each(function($page) {
+            $page->html = $this->entityRepo->renderPage($page);
+        });
+        $html = view('chapters/export', [
+            'chapter' => $chapter,
+            'pages' => $pages
+        ])->render();
+        return $this->containHtml($html);
+    }
+
     /**
      * Convert a book to a self-contained HTML file.
      * @param Book $book
@@ -62,6 +81,24 @@ class ExportService
         return $this->htmlToPdf($html);
     }
 
+    /**
+     * Convert a chapter to a PDF file.
+     * @param Chapter $chapter
+     * @return mixed|string
+     */
+    public function chapterToPdf(Chapter $chapter)
+    {
+        $pages = $this->entityRepo->getChapterChildren($chapter);
+        $pages->each(function($page) {
+            $page->html = $this->entityRepo->renderPage($page);
+        });
+        $html = view('chapters/export', [
+            'chapter' => $chapter,
+            'pages' => $pages
+        ])->render();
+        return $this->htmlToPdf($html);
+    }
+
     /**
      * Convert a book to a PDF file
      * @param Book $book
@@ -168,6 +205,21 @@ class ExportService
         return $text;
     }
 
+    /**
+     * Convert a chapter into a plain text string.
+     * @param Chapter $chapter
+     * @return string
+     */
+    public function chapterToPlainText(Chapter $chapter)
+    {
+        $text = $chapter->name . "\n\n";
+        $text .= $chapter->description . "\n\n";
+        foreach ($chapter->pages as $page) {
+            $text .= $this->pageToPlainText($page);
+        }
+        return $text;
+    }
+
     /**
      * Convert a book into a plain text string.
      * @param Book $book
@@ -179,11 +231,7 @@ class ExportService
         $text = $book->name . "\n\n";
         foreach ($bookTree as $bookChild) {
             if ($bookChild->isA('chapter')) {
-                $text .= $bookChild->name . "\n\n";
-                $text .= $bookChild->description . "\n\n";
-                foreach ($bookChild->pages as $page) {
-                    $text .= $this->pageToPlainText($page);
-                }
+                $text .= $this->chapterToPlainText($bookChild);
             } else {
                 $text .= $this->pageToPlainText($bookChild);
             }
diff --git a/resources/views/chapters/export.blade.php b/resources/views/chapters/export.blade.php
new file mode 100644 (file)
index 0000000..57fcd16
--- /dev/null
@@ -0,0 +1,52 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+    <title>{{ $chapter->name }}</title>
+
+    <style>
+        {!! file_get_contents(public_path('/css/export-styles.css')) !!}
+        .page-break {
+            page-break-after: always;
+        }
+        ul.contents ul li {
+            list-style: circle;
+        }
+        @media screen {
+            .page-break {
+                border-top: 1px solid #DDD;
+            }
+        }
+    </style>
+    @yield('head')
+</head>
+<body>
+<div class="container">
+    <div class="row">
+        <div class="col-md-8 col-md-offset-2">
+            <div class="page-content">
+
+                <h1 style="font-size: 4.8em">{{$chapter->name}}</h1>
+
+                <p>{{ $chapter->description }}</p>
+
+                @if(count($pages) > 0)
+                <ul class="contents">
+                    @foreach($pages as $page)
+                        <li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
+                    @endforeach
+                </ul>
+                @endif
+
+                @foreach($pages as $page)
+                    <div class="page-break"></div>
+                    <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
+                    {!! $page->html !!}
+                @endforeach
+
+            </div>
+        </div>
+    </div>
+</div>
+</body>
+</html>
index 93eee6424c055bab1ec53dc4263310df215b631e..47a1d9ddfd0ab443be1f68ad6561e2cca237b8a2 100644 (file)
                 </div>
                 <div class="col-sm-4 faded">
                     <div class="action-buttons">
+                        <span dropdown class="dropdown-container">
+                            <div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.pages_export') }}</div>
+                            <ul class="wide">
+                                <li><a href="{{ $chapter->getUrl('/export/html') }}" target="_blank">{{ trans('entities.pages_export_html') }} <span class="text-muted float right">.html</span></a></li>
+                                <li><a href="{{ $chapter->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.pages_export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                                <li><a href="{{ $chapter->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.pages_export_text') }} <span class="text-muted float right">.txt</span></a></li>
+                            </ul>
+                        </span>
                         @if(userCan('page-create', $chapter))
                             <a href="{{ $chapter->getUrl('/create-page') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.pages_new') }}</a>
                         @endif
index 670439a6659065a8ffe3068828536dc72f3bb05a..4bd2b4a069f6962c0504d5396c8f4f7f6f4388be 100644 (file)
@@ -67,6 +67,9 @@ Route::group(['middleware' => 'auth'], function () {
         Route::put('/{bookSlug}/chapter/{chapterSlug}/move', 'ChapterController@move');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/edit', 'ChapterController@edit');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@showRestrict');
+        Route::get('/{bookSlug}/chapter/{chapterSlug}/export/pdf', 'ChapterController@exportPdf');
+        Route::get('/{bookSlug}/chapter/{chapterSlug}/export/html', 'ChapterController@exportHtml');
+        Route::get('/{bookSlug}/chapter/{chapterSlug}/export/plaintext', 'ChapterController@exportPlainText');
         Route::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@restrict');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete');
         Route::delete('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@destroy');
index b1dab094f73ac1b59b1b38e102c27941799cccec..7fa485f2052ffb0214a737abd51c8b9788fbf8e2 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace Tests;
 
 
+use BookStack\Chapter;
 use BookStack\Page;
 
 class ExportTest extends TestCase
@@ -75,4 +76,40 @@ class ExportTest extends TestCase
         $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html');
     }
 
+    public function test_chapter_text_export()
+    {
+        $chapter = Chapter::first();
+        $page = $chapter->pages[0];
+        $this->asEditor();
+
+        $resp = $this->get($chapter->getUrl('/export/plaintext'));
+        $resp->assertStatus(200);
+        $resp->assertSee($chapter->name);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.txt');
+    }
+
+    public function test_chapter_pdf_export()
+    {
+        $chapter = Chapter::first();
+        $this->asEditor();
+
+        $resp = $this->get($chapter->getUrl('/export/pdf'));
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf');
+    }
+
+    public function test_chapter_html_export()
+    {
+        $chapter = Chapter::first();
+        $page = $chapter->pages[0];
+        $this->asEditor();
+
+        $resp = $this->get($chapter->getUrl('/export/html'));
+        $resp->assertStatus(200);
+        $resp->assertSee($chapter->name);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html');
+    }
+
 }
\ No newline at end of file