-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Tools;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Managers\PageContent;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
use BookStack\Uploads\ImageService;
use DomPDF;
use Exception;
use SnappyPDF;
+ use League\HTMLToMarkdown\HtmlConverter;
use Throwable;
+ use ZipArchive;
-class ExportService
+class ExportFormatter
{
protected $imageService;
protected function containHtml(string $htmlContent): string
{
$imageTagsOutput = [];
- preg_match_all("/\<img.*src\=(\'|\")(.*?)(\'|\").*?\>/i", $htmlContent, $imageTagsOutput);
+ preg_match_all("/\<img.*?src\=(\'|\")(.*?)(\'|\").*?\>/i", $htmlContent, $imageTagsOutput);
// Replace image src with base64 encoded image strings
if (isset($imageTagsOutput[0]) && count($imageTagsOutput[0]) > 0) {
{
$text = $chapter->name . "\n\n";
$text .= $chapter->description . "\n\n";
- foreach ($chapter->pages as $page) {
+ foreach ($chapter->getVisiblePages() as $page) {
$text .= $this->pageToPlainText($page);
}
return $text;
*/
public function bookToPlainText(Book $book): string
{
- $bookTree = (new BookContents($book))->getTree(false, true);
+ $bookTree = (new BookContents($book))->getTree(false, false);
$text = $book->name . "\n\n";
foreach ($bookTree as $bookChild) {
if ($bookChild->isA('chapter')) {
}
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";
+ }
}
namespace BookStack\Http\Controllers;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\ExportService;
+use BookStack\Entities\Tools\ExportFormatter;
use BookStack\Entities\Repos\BookRepo;
use Throwable;
{
protected $bookRepo;
- protected $exportService;
+ protected $exportFormatter;
/**
* BookExportController constructor.
*/
- public function __construct(BookRepo $bookRepo, ExportService $exportService)
+ public function __construct(BookRepo $bookRepo, ExportFormatter $exportFormatter)
{
$this->bookRepo = $bookRepo;
- $this->exportService = $exportService;
- parent::__construct();
+ $this->exportFormatter = $exportFormatter;
}
/**
public function pdf(string $bookSlug)
{
$book = $this->bookRepo->getBySlug($bookSlug);
- $pdfContent = $this->exportService->bookToPdf($book);
+ $pdfContent = $this->exportFormatter->bookToPdf($book);
return $this->downloadResponse($pdfContent, $bookSlug . '.pdf');
}
public function html(string $bookSlug)
{
$book = $this->bookRepo->getBySlug($bookSlug);
- $htmlContent = $this->exportService->bookToContainedHtml($book);
+ $htmlContent = $this->exportFormatter->bookToContainedHtml($book);
return $this->downloadResponse($htmlContent, $bookSlug . '.html');
}
public function plainText(string $bookSlug)
{
$book = $this->bookRepo->getBySlug($bookSlug);
- $textContent = $this->exportService->bookToPlainText($book);
+ $textContent = $this->exportFormatter->bookToPlainText($book);
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);
+ }
}
<?php namespace BookStack\Http\Controllers;
-use BookStack\Entities\ExportService;
+use BookStack\Entities\Tools\ExportFormatter;
use BookStack\Entities\Repos\ChapterRepo;
use BookStack\Exceptions\NotFoundException;
use Throwable;
{
protected $chapterRepo;
- protected $exportService;
+ protected $exportFormatter;
/**
* ChapterExportController constructor.
*/
- public function __construct(ChapterRepo $chapterRepo, ExportService $exportService)
+ public function __construct(ChapterRepo $chapterRepo, ExportFormatter $exportFormatter)
{
$this->chapterRepo = $chapterRepo;
- $this->exportService = $exportService;
- parent::__construct();
+ $this->exportFormatter = $exportFormatter;
}
/**
public function pdf(string $bookSlug, string $chapterSlug)
{
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
- $pdfContent = $this->exportService->chapterToPdf($chapter);
+ $pdfContent = $this->exportFormatter->chapterToPdf($chapter);
return $this->downloadResponse($pdfContent, $chapterSlug . '.pdf');
}
public function html(string $bookSlug, string $chapterSlug)
{
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
- $containedHtml = $this->exportService->chapterToContainedHtml($chapter);
+ $containedHtml = $this->exportFormatter->chapterToContainedHtml($chapter);
return $this->downloadResponse($containedHtml, $chapterSlug . '.html');
}
public function plainText(string $bookSlug, string $chapterSlug)
{
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
- $chapterText = $this->exportService->chapterToPlainText($chapter);
+ $chapterText = $this->exportFormatter->chapterToPlainText($chapter);
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');
+ }
}
namespace BookStack\Http\Controllers;
-use BookStack\Entities\ExportService;
-use BookStack\Entities\Managers\PageContent;
+use BookStack\Entities\Tools\ExportFormatter;
+use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Exceptions\NotFoundException;
use Throwable;
{
protected $pageRepo;
- protected $exportService;
+ protected $exportFormatter;
/**
* PageExportController constructor.
- * @param PageRepo $pageRepo
- * @param ExportService $exportService
*/
- public function __construct(PageRepo $pageRepo, ExportService $exportService)
+ public function __construct(PageRepo $pageRepo, ExportFormatter $exportFormatter)
{
$this->pageRepo = $pageRepo;
- $this->exportService = $exportService;
- parent::__construct();
+ $this->exportFormatter = $exportFormatter;
}
/**
{
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
$page->html = (new PageContent($page))->render();
- $pdfContent = $this->exportService->pageToPdf($page);
+ $pdfContent = $this->exportFormatter->pageToPdf($page);
return $this->downloadResponse($pdfContent, $pageSlug . '.pdf');
}
{
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
$page->html = (new PageContent($page))->render();
- $containedHtml = $this->exportService->pageToContainedHtml($page);
+ $containedHtml = $this->exportFormatter->pageToContainedHtml($page);
return $this->downloadResponse($containedHtml, $pageSlug . '.html');
}
public function plainText(string $bookSlug, string $pageSlug)
{
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
- $pageText = $this->exportService->pageToPlainText($page);
+ $pageText = $this->exportFormatter->pageToPlainText($page);
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');
+ }
}
"license": "MIT",
"type": "project",
"require": {
- "php": "^7.2",
+ "php": "^7.3|^8.0",
"ext-curl": "*",
"ext-dom": "*",
+ "ext-fileinfo": "*",
"ext-gd": "*",
"ext-json": "*",
"ext-mbstring": "*",
- "ext-tidy": "*",
"ext-xml": "*",
- "barryvdh/laravel-dompdf": "^0.8.5",
- "barryvdh/laravel-snappy": "^0.4.5",
- "doctrine/dbal": "^2.9",
- "facade/ignition": "^1.4",
- "fideloper/proxy": "^4.0",
- "gathercontent/htmldiff": "^0.2.1",
- "intervention/image": "^2.5",
- "laravel/framework": "^6.12",
- "laravel/socialite": "^4.3.2",
- "league/commonmark": "^1.4",
- "league/flysystem-aws-s3-v3": "^1.0",
+ "barryvdh/laravel-dompdf": "^0.9.0",
+ "barryvdh/laravel-snappy": "^0.4.8",
+ "doctrine/dbal": "^2.12.1",
+ "facade/ignition": "^1.16.4",
+ "fideloper/proxy": "^4.4.1",
+ "intervention/image": "^2.5.1",
+ "laravel/framework": "^6.20.16",
+ "laravel/socialite": "^5.1",
+ "league/commonmark": "^1.5",
+ "league/flysystem-aws-s3-v3": "^1.0.29",
+ "league/html-to-markdown": "^4.9",
- "nunomaduro/collision": "^3.0",
- "onelogin/php-saml": "^3.3",
- "predis/predis": "^1.1",
- "socialiteproviders/discord": "^2.0",
- "socialiteproviders/gitlab": "^3.0",
- "socialiteproviders/microsoft-azure": "^3.0",
- "socialiteproviders/okta": "^1.0",
- "socialiteproviders/slack": "^3.0",
- "socialiteproviders/twitch": "^5.0"
+ "nunomaduro/collision": "^3.1",
+ "onelogin/php-saml": "^4.0",
+ "predis/predis": "^1.1.6",
+ "socialiteproviders/discord": "^4.1",
+ "socialiteproviders/gitlab": "^4.1",
+ "socialiteproviders/microsoft-azure": "^4.1",
+ "socialiteproviders/okta": "^4.1",
+ "socialiteproviders/slack": "^4.1",
+ "socialiteproviders/twitch": "^5.3",
+ "ssddanbrown/htmldiff": "^v1.0.1"
},
"require-dev": {
- "barryvdh/laravel-debugbar": "^3.2.8",
- "barryvdh/laravel-ide-helper": "^2.6.4",
- "fzaninotto/faker": "^1.4",
- "laravel/browser-kit-testing": "^5.1",
- "mockery/mockery": "^1.0",
- "phpunit/phpunit": "^8.0",
- "squizlabs/php_codesniffer": "^3.4",
- "wnx/laravel-stats": "^2.0"
+ "barryvdh/laravel-debugbar": "^3.5.1",
+ "barryvdh/laravel-ide-helper": "^2.8.2",
+ "fakerphp/faker": "^1.13.0",
+ "laravel/browser-kit-testing": "^5.2",
+ "mockery/mockery": "^1.3.3",
+ "phpunit/phpunit": "^9.5.3",
+ "squizlabs/php_codesniffer": "^3.5.8"
},
"autoload": {
"classmap": [
],
"psr-4": {
"BookStack\\": "app/"
- }
+ },
+ "files": [
+ "app/helpers.php"
+ ]
},
"autoload-dev": {
"psr-4": {
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
],
- "pre-update-cmd": [
- "@php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"",
- "@php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\""
- ],
"pre-install-cmd": [
- "@php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"",
- "@php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\""
+ "@php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\""
],
"post-install-cmd": [
"@php artisan cache:clear",
"preferred-install": "dist",
"sort-packages": true,
"platform": {
- "php": "7.2.0"
+ "php": "7.3.0"
}
},
"extra": {
-FROM php:7.3-apache
+FROM php:7.4-apache
ENV APACHE_DOCUMENT_ROOT /app/public
WORKDIR /app
+# Install additional dependacnies and configure apache
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 libtidy-dev libpng-dev libldap2-dev libxml++2.6-dev wait-for-it libzip-dev \
++ && 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-install pdo_mysql gd ldap \
- && docker-php-ext-install pdo pdo_mysql tidy dom xml mbstring gd ldap zip \
++ && docker-php-ext-install pdo_mysql gd ldap zip \
&& a2enmod rewrite \
&& 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 \
- && php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
+ && sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
+
+# Install composer
+RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php composer-setup.php \
&& mv composer.phar /usr/bin/composer \
&& php -r "unlink('composer-setup.php');"
- && sed -i 's/memory_limit = 128M/memory_limit = 512M/g' "$PHP_INI_DIR/php.ini"
+
+# Use the default production configuration and update it as required
+RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
++ && sed -i 's/memory_limit = 128M/memory_limit = 512M/g' "$PHP_INI_DIR/php.ini"
'meta_created_name' => 'Created :timeLength by :user',
'meta_updated' => 'Updated :timeLength',
'meta_updated_name' => 'Updated :timeLength by :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Entity Select',
'images' => 'Images',
'my_recent_drafts' => 'My Recent Drafts',
'my_recently_viewed' => 'My Recently Viewed',
+ 'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+ 'my_favourites' => 'My Favourites',
'no_pages_viewed' => 'You have not viewed any pages',
'no_pages_recently_created' => 'No pages have been recently created',
'no_pages_recently_updated' => 'No pages have been recently updated',
'export_html' => 'Contained Web File',
'export_pdf' => 'PDF File',
'export_text' => 'Plain Text File',
+ 'export_md' => 'Markdown File',
// Permissions and restrictions
'permissions' => 'Permissions',
'permissions_intro' => 'Once enabled, These permissions will take priority over any set role permissions.',
'permissions_enable' => 'Enable Custom Permissions',
'permissions_save' => 'Save Permissions',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Search Results',
'search_no_pages' => 'No pages matched this search',
'search_for_term' => 'Search for :term',
'search_more' => 'More Results',
- 'search_filters' => 'Search Filters',
+ 'search_advanced' => 'Advanced Search',
+ 'search_terms' => 'Search Terms',
'search_content_type' => 'Content Type',
'search_exact_matches' => 'Exact Matches',
'search_tags' => 'Tag Searches',
'search_permissions_set' => 'Permissions set',
'search_created_by_me' => 'Created by me',
'search_updated_by_me' => 'Updated by me',
+ 'search_owned_by_me' => 'Owned by me',
'search_date_options' => 'Date Options',
'search_updated_before' => 'Updated before',
'search_updated_after' => 'Updated after',
'chapters_create' => 'Create New Chapter',
'chapters_delete' => 'Delete Chapter',
'chapters_delete_named' => 'Delete Chapter :chapterName',
- 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages will be removed and added directly to the parent book.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?',
'chapters_edit' => 'Edit Chapter',
'chapters_edit_named' => 'Edit Chapter :chapterName',
'pages_revisions' => 'Page Revisions',
'pages_revisions_named' => 'Page Revisions for :pageName',
'pages_revision_named' => 'Page Revision for :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Created By',
'pages_revisions_date' => 'Revision Date',
'pages_revisions_number' => '#',
'attachments_upload' => 'Upload File',
'attachments_link' => 'Attach Link',
'attachments_set_link' => 'Set Link',
- 'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
+ 'attachments_delete' => 'Are you sure you want to delete this attachment?',
'attachments_dropzone' => 'Drop files or click here to attach a file',
'attachments_no_files' => 'No files have been uploaded',
'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
'attachments_link_url' => 'Link to file',
'attachments_link_url_hint' => 'Url of site or file',
'attach' => 'Attach',
+ 'attachments_insert_link' => 'Add Attachment Link to Page',
'attachments_edit_file' => 'Edit File',
'attachments_edit_file_name' => 'File Name',
'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
'revision_delete_success' => 'Revision deleted',
'revision_cannot_delete_latest' => 'Cannot delete the latest revision.'
-];
+];
-<div dropdown class="dropdown-container" id="export-menu">
- <div dropdown-toggle class="icon-list-item"
+<div component="dropdown" class="dropdown-container" id="export-menu">
+ <div refs="dropdown@toggle" class="icon-list-item"
aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('entities.export') }}" tabindex="0">
<span>@icon('export')</span>
<span>{{ trans('entities.export') }}</span>
</div>
- <ul class="wide dropdown-menu" role="menu">
- <li><a href="{{ $entity->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
- <li><a href="{{ $entity->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
- <li><a href="{{ $entity->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
- <li><a href="{{ $entity->getUrl('/export/markdown') }}" target="_blank">{{ trans('entities.export_md') }} <span class="text-muted float right">.md</span></a></li>
+ <ul refs="dropdown@menu" class="wide dropdown-menu" role="menu">
+ <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/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>
--</div>
++</div>
<?php
+Route::get('/status', 'StatusController@show');
Route::get('/robots.txt', 'HomeController@getRobots');
// Authenticated routes...
// Shelves
Route::get('/create-shelf', 'BookshelfController@create');
- Route::group(['prefix' => 'shelves'], function() {
+ Route::group(['prefix' => 'shelves'], function () {
Route::get('/', 'BookshelfController@index');
Route::post('/', 'BookshelfController@store');
Route::get('/{slug}/edit', 'BookshelfController@edit');
Route::put('/{bookSlug}/sort', 'BookSortController@update');
Route::get('/{bookSlug}/export/html', 'BookExportController@html');
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');
// Pages
Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
Route::get('/{bookSlug}/page/{pageSlug}/export/pdf', 'PageExportController@pdf');
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}/edit', 'PageController@edit');
Route::get('/{bookSlug}/page/{pageSlug}/move', 'PageController@showMove');
Route::get('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@showPermissions');
Route::get('/{bookSlug}/chapter/{chapterSlug}/export/pdf', 'ChapterExportController@pdf');
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::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@permissions');
Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete');
});
// User Profile routes
- Route::get('/user/{userId}', 'UserController@showProfilePage');
+ Route::get('/user/{slug}', 'UserProfileController@show');
// Image routes
- Route::group(['prefix' => 'images'], function () {
-
- // Gallery
- Route::get('/gallery', 'Images\GalleryImageController@list');
- Route::post('/gallery', 'Images\GalleryImageController@create');
-
- // Drawio
- Route::get('/drawio', 'Images\DrawioImageController@list');
- Route::get('/drawio/base64/{id}', 'Images\DrawioImageController@getAsBase64');
- Route::post('/drawio', 'Images\DrawioImageController@create');
-
- // Shared gallery & draw.io endpoint
- Route::get('/usage/{id}', 'Images\ImageController@usage');
- Route::put('/{id}', 'Images\ImageController@update');
- Route::delete('/{id}', 'Images\ImageController@destroy');
- });
+ Route::get('/images/gallery', 'Images\GalleryImageController@list');
+ Route::post('/images/gallery', 'Images\GalleryImageController@create');
+ Route::get('/images/drawio', 'Images\DrawioImageController@list');
+ Route::get('/images/drawio/base64/{id}', 'Images\DrawioImageController@getAsBase64');
+ Route::post('/images/drawio', 'Images\DrawioImageController@create');
+ Route::get('/images/edit/{id}', 'Images\ImageController@edit');
+ Route::put('/images/{id}', 'Images\ImageController@update');
+ Route::delete('/images/{id}', 'Images\ImageController@destroy');
// Attachments routes
Route::get('/attachments/{id}', 'AttachmentController@get');
Route::post('/attachments/upload/{id}', 'AttachmentController@uploadUpdate');
Route::post('/attachments/link', 'AttachmentController@attachLink');
Route::put('/attachments/{id}', 'AttachmentController@update');
+ Route::get('/attachments/edit/{id}', 'AttachmentController@getUpdateForm');
Route::get('/attachments/get/page/{pageId}', 'AttachmentController@listForPage');
Route::put('/attachments/sort/page/{pageId}', 'AttachmentController@sortForPage');
Route::delete('/attachments/{id}', 'AttachmentController@delete');
Route::delete('/ajax/page/{id}', 'PageController@ajaxDestroy');
// Tag routes (AJAX)
- Route::group(['prefix' => 'ajax/tags'], function() {
- Route::get('/get/{entityType}/{entityId}', 'TagController@getForEntity');
+ Route::group(['prefix' => 'ajax/tags'], function () {
Route::get('/suggest/names', 'TagController@getNameSuggestions');
Route::get('/suggest/values', 'TagController@getValueSuggestions');
});
Route::get('/ajax/search/entities', 'SearchController@searchEntitiesAjax');
// Comments
- Route::post('/ajax/page/{pageId}/comment', 'CommentController@savePageComment');
- Route::put('/ajax/comment/{id}', 'CommentController@update');
- Route::delete('/ajax/comment/{id}', 'CommentController@destroy');
+ Route::post('/comment/{pageId}', 'CommentController@savePageComment');
+ Route::put('/comment/{id}', 'CommentController@update');
+ Route::delete('/comment/{id}', 'CommentController@destroy');
// Links
Route::get('/link/{id}', 'PageController@redirectFromLink');
Route::get('/search/chapter/{bookId}', 'SearchController@searchChapter');
Route::get('/search/entity/siblings', 'SearchController@searchSiblings');
+ // User Search
+ Route::get('/search/users/select', 'UserSearchController@forSelect');
+
+ // Template System
Route::get('/templates', 'PageTemplateController@list');
Route::get('/templates/{templateId}', 'PageTemplateController@get');
+ // Favourites
+ Route::get('/favourites', 'FavouriteController@index');
+ Route::post('/favourites/add', 'FavouriteController@add');
+ Route::post('/favourites/remove', 'FavouriteController@remove');
+
// Other Pages
Route::get('/', 'HomeController@index');
Route::get('/home', 'HomeController@index');
Route::post('/', 'SettingController@update');
// Maintenance
- Route::get('/maintenance', 'SettingController@showMaintenance');
- Route::delete('/maintenance/cleanup-images', 'SettingController@cleanupImages');
- Route::post('/maintenance/send-test-email', 'SettingController@sendTestEmail');
+ Route::get('/maintenance', 'MaintenanceController@index');
+ Route::delete('/maintenance/cleanup-images', 'MaintenanceController@cleanupImages');
+ Route::post('/maintenance/send-test-email', 'MaintenanceController@sendTestEmail');
+
+ // Recycle Bin
+ Route::get('/recycle-bin', 'RecycleBinController@index');
+ Route::post('/recycle-bin/empty', 'RecycleBinController@empty');
+ Route::get('/recycle-bin/{id}/destroy', 'RecycleBinController@showDestroy');
+ Route::delete('/recycle-bin/{id}', 'RecycleBinController@destroy');
+ Route::get('/recycle-bin/{id}/restore', 'RecycleBinController@showRestore');
+ Route::post('/recycle-bin/{id}/restore', 'RecycleBinController@restore');
+
+ // Audit Log
+ Route::get('/audit', 'AuditLogController@index');
// Users
Route::get('/users', 'UserController@index');
Route::delete('/users/{userId}/api-tokens/{tokenId}', 'UserApiTokenController@destroy');
// Roles
- Route::get('/roles', 'PermissionController@listRoles');
- Route::get('/roles/new', 'PermissionController@createRole');
- Route::post('/roles/new', 'PermissionController@storeRole');
- Route::get('/roles/delete/{id}', 'PermissionController@showDeleteRole');
- Route::delete('/roles/delete/{id}', 'PermissionController@deleteRole');
- Route::get('/roles/{id}', 'PermissionController@editRole');
- Route::put('/roles/{id}', 'PermissionController@updateRole');
+ Route::get('/roles', 'RoleController@list');
+ Route::get('/roles/new', 'RoleController@create');
+ Route::post('/roles/new', 'RoleController@store');
+ Route::get('/roles/delete/{id}', 'RoleController@showDelete');
+ Route::delete('/roles/delete/{id}', 'RoleController@delete');
+ Route::get('/roles/{id}', 'RoleController@edit');
+ Route::put('/roles/{id}', 'RoleController@update');
});
});
// Social auth routes
-Route::get('/login/service/{socialDriver}', 'Auth\SocialController@getSocialLogin');
-Route::get('/login/service/{socialDriver}/callback', 'Auth\SocialController@socialCallback');
+Route::get('/login/service/{socialDriver}', 'Auth\SocialController@login');
+Route::get('/login/service/{socialDriver}/callback', 'Auth\SocialController@callback');
Route::group(['middleware' => 'auth'], function () {
- Route::get('/login/service/{socialDriver}/detach', 'Auth\SocialController@detachSocialAccount');
+ Route::post('/login/service/{socialDriver}/detach', 'Auth\SocialController@detach');
});
-Route::get('/register/service/{socialDriver}', 'Auth\SocialController@socialRegister');
+Route::get('/register/service/{socialDriver}', 'Auth\SocialController@register');
// Login/Logout routes
Route::get('/login', 'Auth\LoginController@getLogin');
Route::get('/password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');
Route::post('/password/reset', 'Auth\ResetPasswordController@reset');
-Route::fallback('HomeController@getNotFound');
+Route::fallback('HomeController@getNotFound')->name('fallback');