Skip to content

Commit

Permalink
Merge branch 'development' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
ssddanbrown committed Oct 9, 2024
2 parents b0dda6e + a58102d commit a5f48e3
Show file tree
Hide file tree
Showing 596 changed files with 70,534 additions and 4,211 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
20 changes: 19 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,21 @@ 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
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
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
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;
}
}
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
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 . '"';
}
}
37 changes: 37 additions & 0 deletions app/Search/Options/FilterSearchOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace BookStack\Search\Options;

class FilterSearchOption extends SearchOption
{
protected string $name;

public function __construct(
string $value,
string $name,
bool $negated = false,
) {
parent::__construct($value, $negated);
$this->name = $name;
}

public function toString(): string
{
$valueText = ($this->value ? ':' . $this->value : '');
$filterBrace = '{' . $this->name . $valueText . '}';
return ($this->negated ? '-' : '') . $filterBrace;
}

public function getKey(): string
{
return $this->name;
}

public static function fromContentString(string $value, bool $negated = false): self
{
$explodedFilter = explode(':', $value, 2);
$filterValue = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
$filterName = $explodedFilter[0];
return new self($filterValue, $filterName, $negated);
}
}
Loading

0 comments on commit a5f48e3

Please sign in to comment.