Skip to content

Commit

Permalink
Merge pull request #8 from vatger/BookStackApp-release
Browse files Browse the repository at this point in the history
Book stack app release
  • Loading branch information
paulhollmann authored Dec 17, 2024
2 parents d9eb54a + c401952 commit 197918d
Show file tree
Hide file tree
Showing 653 changed files with 73,166 additions and 5,037 deletions.
5 changes: 5 additions & 0 deletions .env.example.complete
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ EXPORT_PAGE_SIZE=a4
# Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
EXPORT_PDF_COMMAND=false

# Export PDF Command Timeout
# The number of seconds that the export PDF command will run before a timeout occurs.
# Only applies for the EXPORT_PDF_COMMAND option, not for DomPDF or wkhtmltopdf.
EXPORT_PDF_COMMAND_TIMEOUT=15

# Set path to wkhtmltopdf binary for PDF generation.
# Can be 'false' or a path path like: '/home/bins/wkhtmltopdf'
# When false, BookStack will attempt to find a wkhtmltopdf in the application
Expand Down
26 changes: 25 additions & 1 deletion .github/translators.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ Kauê Sena (kaue.sena.ks) :: Portuguese, Brazilian
MatthieuParis :: French
Douradinho :: Portuguese, Brazilian; Portuguese
Gaku Yaguchi (tama11) :: Japanese
johnroyer :: Chinese Traditional
Zero Huang (johnroyer) :: Chinese Traditional
jackaaa :: Chinese Traditional
Irfan Hukama Arsyad (IrfanArsyad) :: Indonesian
Jeff Huang (s8321414) :: Chinese Traditional
Expand Down Expand Up @@ -431,3 +431,27 @@ Michal Melich (michalmelich) :: Czech
David (david-prv) :: German; German Informal
Larry (lahoje) :: Swedish
Marcia dos Santos (marciab80) :: Portuguese
Ricard López Torres (richilpez.torres) :: Catalan
sarahalves7 :: Portuguese, Brazilian
petr.husak :: Czech
javadataherian :: Persian
Ludo-code :: French
hollsten :: Swedish
Ngoc Lan Phung (lanpncz) :: Vietnamese
Worive :: Catalan
Илья Скаба (skabailya) :: Russian
Irjan Olsen (Irch) :: Norwegian Bokmal
Aleksandar Jovanovic (jovanoviczaleksandar) :: Serbian (Cyrillic)
Red (RedVortex) :: Hebrew
xgrug :: Chinese Simplified
HrCalmar :: Danish
Avishay Rapp (AvishayRapp) :: Hebrew
matthias4217 :: French
Berke BOYLU2 (berkeboylu2) :: Turkish
etwas7B :: German
Mohammed srhiri (m.sghiri20) :: Arabic
YongMin Kim (kym0118) :: Korean
Rivo Zängov (Eraser) :: Estonian
Francisco Rafael Fonseca (chicoraf) :: Portuguese, Brazilian
ИEØ_ΙΙØZ (NEO_IIOZ) :: Chinese Traditional
madnjpn (madnjpn.) :: Georgian
4 changes: 2 additions & 2 deletions .github/workflows/lint-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ on:
jobs:
build:
if: ${{ github.ref != 'refs/heads/l10n_development' }}
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Install NPM deps
run: npm ci
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/test-js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: test-js

on:
push:
paths:
- '**.js'
- '**.ts'
- '**.json'
pull_request:
paths:
- '**.js'
- '**.ts'
- '**.json'

jobs:
build:
if: ${{ github.ref != 'refs/heads/l10n_development' }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Install NPM deps
run: npm ci

- name: Run TypeScript type checking
run: npm run ts:lint

- name: Run JavaScript tests
run: npm run test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/node_modules
/.vscode
/composer
/coverage
Homestead.yaml
.env
.idea
Expand Down
4 changes: 3 additions & 1 deletion app/Access/Oidc/OidcUserinfoResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ class OidcUserinfoResponse implements ProvidesClaims

public function __construct(ResponseInterface $response, string $issuer, array $keys)
{
$contentType = $response->getHeader('Content-Type')[0];
$contentTypeHeaderValue = $response->getHeader('Content-Type')[0] ?? '';
$contentType = strtolower(trim(explode(';', $contentTypeHeaderValue, 2)[0]));

if ($contentType === 'application/json') {
$this->claims = json_decode($response->getBody()->getContents(), true);
}
Expand Down
10 changes: 10 additions & 0 deletions app/Access/UserInviteException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace BookStack\Access;

use Exception;

class UserInviteException extends Exception
{
//
}
8 changes: 7 additions & 1 deletion app/Access/UserInviteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ class UserInviteService extends UserTokenService
/**
* Send an invitation to a user to sign into BookStack
* Removes existing invitation tokens.
* @throws UserInviteException
*/
public function sendInvitation(User $user)
{
$this->deleteByUser($user);
$token = $this->createTokenForUser($user);
$user->notify(new UserInviteNotification($token));

try {
$user->notify(new UserInviteNotification($token));
} catch (\Exception $exception) {
throw new UserInviteException($exception->getMessage(), $exception->getCode(), $exception);
}
}
}
10 changes: 10 additions & 0 deletions app/App/MetaController.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,14 @@ public function licenses()
'jsLibData' => file_get_contents(base_path('dev/licensing/js-library-licenses.txt')),
]);
}

/**
* Show the view for /opensearch.xml.
*/
public function opensearch()
{
return response()
->view('misc.opensearch')
->header('Content-Type', 'application/opensearchdescription+xml');
}
}
4 changes: 4 additions & 0 deletions app/Config/exports.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
// Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
'pdf_command' => env('EXPORT_PDF_COMMAND', false),

// The amount of time allowed for PDF generation command to run
// before the process times out and is stopped.
'pdf_command_timeout' => env('EXPORT_PDF_COMMAND_TIMEOUT', 15),

// 2024-04: Snappy/WKHTMLtoPDF now considered deprecated in regard to BookStack support.
'snappy' => [
'pdf_binary' => env('WKHTMLTOPDF', false),
Expand Down
7 changes: 7 additions & 0 deletions app/Console/Commands/UpdateUrlCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public function handle(Connection $db): int
'chapters' => ['description_html'],
'books' => ['description_html'],
'bookshelves' => ['description_html'],
'page_revisions' => ['html', 'text', 'markdown'],
'images' => ['url'],
'settings' => ['value'],
'comments' => ['html', 'text'],
Expand Down Expand Up @@ -77,6 +78,12 @@ public function handle(Connection $db): int
$this->info('URL update procedure complete.');
$this->info('============================================================================');
$this->info('Be sure to run "php artisan cache:clear" to clear any old URLs in the cache.');

if (!str_starts_with($newUrl, url('/'))) {
$this->warn('You still need to update your APP_URL env value. This is currently set to:');
$this->warn(url('/'));
}

$this->info('============================================================================');

return 0;
Expand Down
1 change: 1 addition & 0 deletions app/Entities/Models/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace BookStack\Entities\Models;

use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Tools\PageEditorType;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Uploads\Attachment;
use Illuminate\Database\Eloquent\Builder;
Expand Down
17 changes: 11 additions & 6 deletions app/Entities/Repos/PageRepo.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use BookStack\Entities\Queries\EntityQueries;
use BookStack\Entities\Tools\BookContents;
use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Tools\PageEditorData;
use BookStack\Entities\Tools\PageEditorType;
use BookStack\Entities\Tools\TrashCan;
use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\PermissionsException;
Expand Down Expand Up @@ -43,6 +43,7 @@ public function getNewDraftPage(Entity $parent)
'owned_by' => user()->id,
'updated_by' => user()->id,
'draft' => true,
'editor' => PageEditorType::getSystemDefault()->value,
]);

if ($parent instanceof Chapter) {
Expand Down Expand Up @@ -127,7 +128,9 @@ protected function updateTemplateStatusAndContentFromInput(Page $page, array $in
}

$pageContent = new PageContent($page);
$currentEditor = $page->editor ?: PageEditorData::getSystemDefaultEditor();
$defaultEditor = PageEditorType::getSystemDefault();
$currentEditor = PageEditorType::forPage($page) ?: $defaultEditor;
$inputEditor = PageEditorType::fromRequestValue($input['editor'] ?? '') ?? $currentEditor;
$newEditor = $currentEditor;

$haveInput = isset($input['markdown']) || isset($input['html']);
Expand All @@ -136,15 +139,17 @@ protected function updateTemplateStatusAndContentFromInput(Page $page, array $in
if ($haveInput && $inputEmpty) {
$pageContent->setNewHTML('', user());
} elseif (!empty($input['markdown']) && is_string($input['markdown'])) {
$newEditor = 'markdown';
$newEditor = PageEditorType::Markdown;
$pageContent->setNewMarkdown($input['markdown'], user());
} elseif (isset($input['html'])) {
$newEditor = 'wysiwyg';
$newEditor = ($inputEditor->isHtmlBased() ? $inputEditor : null) ?? ($defaultEditor->isHtmlBased() ? $defaultEditor : null) ?? PageEditorType::WysiwygTinymce;
$pageContent->setNewHTML($input['html'], user());
}

if ($newEditor !== $currentEditor && userCan('editor-change')) {
$page->editor = $newEditor;
if (($newEditor !== $currentEditor || empty($page->editor)) && userCan('editor-change')) {
$page->editor = $newEditor->value;
} elseif (empty($page->editor)) {
$page->editor = $defaultEditor->value;
}
}

Expand Down
22 changes: 7 additions & 15 deletions app/Entities/Tools/PageEditorData.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,17 @@ protected function build(): array
];
}

protected function updateContentForEditor(Page $page, string $editorType): void
protected function updateContentForEditor(Page $page, PageEditorType $editorType): void
{
$isHtml = !empty($page->html) && empty($page->markdown);

// HTML to markdown-clean conversion
if ($editorType === 'markdown' && $isHtml && $this->requestedEditor === 'markdown-clean') {
if ($editorType === PageEditorType::Markdown && $isHtml && $this->requestedEditor === 'markdown-clean') {
$page->markdown = (new HtmlToMarkdown($page->html))->convert();
}

// Markdown to HTML conversion if we don't have HTML
if ($editorType === 'wysiwyg' && !$isHtml) {
if ($editorType->isHtmlBased() && !$isHtml) {
$page->html = (new MarkdownToHtml($page->markdown))->convert();
}
}
Expand All @@ -94,24 +94,16 @@ protected function updateContentForEditor(Page $page, string $editorType): void
* Defaults based upon the current content of the page otherwise will fall back
* to system default but will take a requested type (if provided) if permissions allow.
*/
protected function getEditorType(Page $page): string
protected function getEditorType(Page $page): PageEditorType
{
$editorType = $page->editor ?: self::getSystemDefaultEditor();
$editorType = PageEditorType::forPage($page) ?: PageEditorType::getSystemDefault();

// Use requested editor if valid and if we have permission
$requestedType = explode('-', $this->requestedEditor)[0];
if (($requestedType === 'markdown' || $requestedType === 'wysiwyg') && userCan('editor-change')) {
$requestedType = PageEditorType::fromRequestValue($this->requestedEditor);
if ($requestedType && userCan('editor-change')) {
$editorType = $requestedType;
}

return $editorType;
}

/**
* Get the configured system default editor.
*/
public static function getSystemDefaultEditor(): string
{
return setting('app-editor') === 'markdown' ? 'markdown' : 'wysiwyg';
}
}
37 changes: 37 additions & 0 deletions app/Entities/Tools/PageEditorType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace BookStack\Entities\Tools;

use BookStack\Entities\Models\Page;

enum PageEditorType: string
{
case WysiwygTinymce = 'wysiwyg';
case WysiwygLexical = 'wysiwyg2024';
case Markdown = 'markdown';

public function isHtmlBased(): bool
{
return match ($this) {
self::WysiwygTinymce, self::WysiwygLexical => true,
self::Markdown => false,
};
}

public static function fromRequestValue(string $value): static|null
{
$editor = explode('-', $value)[0];
return static::tryFrom($editor);
}

public static function forPage(Page $page): static|null
{
return static::tryFrom($page->editor);
}

public static function getSystemDefault(): static
{
$setting = setting('app-editor');
return static::tryFrom($setting) ?? static::WysiwygTinymce;
}
}
4 changes: 2 additions & 2 deletions app/Entities/Tools/PageIncludeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ protected function splitTextNodesAtTags(DOMNode $textNode): array

if ($currentOffset < $tagStartOffset) {
$previousText = substr($text, $currentOffset, $tagStartOffset - $currentOffset);
$textNode->parentNode->insertBefore(new DOMText($previousText), $textNode);
$textNode->parentNode->insertBefore($this->doc->createTextNode($previousText), $textNode);
}

$node = $textNode->parentNode->insertBefore(new DOMText($tagOuterContent), $textNode);
$node = $textNode->parentNode->insertBefore($this->doc->createTextNode($tagOuterContent), $textNode);
$includeTags[] = new PageIncludeTag($tagInnerContent, $node);
$currentOffset = $tagStartOffset + strlen($tagOuterContent);
}
Expand Down
11 changes: 9 additions & 2 deletions app/Entities/Tools/PdfGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use BookStack\Exceptions\PdfExportException;
use Knp\Snappy\Pdf as SnappyPdf;
use Dompdf\Dompdf;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Process;

class PdfGenerator
Expand Down Expand Up @@ -85,9 +86,15 @@ protected function renderUsingCommand(string $html): string

file_put_contents($inputHtml, $html);

$timeout = intval(config('exports.pdf_command_timeout'));
$process = Process::fromShellCommandline($command);
$process->setTimeout(15);
$process->run();
$process->setTimeout($timeout);

try {
$process->run();
} catch (ProcessTimedOutException $e) {
throw new PdfExportException("PDF Export via command failed due to timeout at {$timeout} second(s)");
}

if (!$process->isSuccessful()) {
throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}");
Expand Down
2 changes: 1 addition & 1 deletion app/Http/RangeSupportedStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ protected function parseRequest(Request $request): void
if ($start < 0 || $start > $end) {
$this->responseStatus = 416;
$this->responseHeaders['Content-Range'] = sprintf('bytes */%s', $this->fileSize);
} elseif ($end - $start < $this->fileSize - 1) {
} else {
$this->responseLength = $end < $this->fileSize ? $end - $start + 1 : -1;
$this->responseOffset = $start;
$this->responseStatus = 206;
Expand Down
13 changes: 13 additions & 0 deletions app/Search/Options/ExactSearchOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace BookStack\Search\Options;

class ExactSearchOption extends SearchOption
{
public function toString(): string
{
$escaped = str_replace('\\', '\\\\', $this->value);
$escaped = str_replace('"', '\"', $escaped);
return ($this->negated ? '-' : '') . '"' . $escaped . '"';
}
}
Loading

0 comments on commit 197918d

Please sign in to comment.