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 Dec 23, 2024
2 parents 07e45a2 + 980a684 commit b0c5743
Show file tree
Hide file tree
Showing 619 changed files with 16,849 additions and 5,887 deletions.
6 changes: 6 additions & 0 deletions .github/translators.txt
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,9 @@ Rivo Zängov (Eraser) :: Estonian
Francisco Rafael Fonseca (chicoraf) :: Portuguese, Brazilian
ИEØ_ΙΙØZ (NEO_IIOZ) :: Chinese Traditional
madnjpn (madnjpn.) :: Georgian
Ásgeir Shiny Ásgeirsson (AsgeirShiny) :: Icelandic
Mohammad Aftab Uddin (chirohorit) :: Bengali
Yannis Karlaftis (meliseus) :: Greek
felixxx :: German Informal
randi (randi65535) :: Korean
test65428 :: Greek
4 changes: 2 additions & 2 deletions .github/workflows/analyse-php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,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: Setup PHP
uses: shivammathur/setup-php@v2
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/lint-php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ 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: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
php-version: 8.3
tools: phpcs

- name: Run formatting check
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test-migrations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ on:
jobs:
build:
if: ${{ github.ref != 'refs/heads/l10n_development' }}
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
php: ['8.1', '8.2', '8.3']
php: ['8.1', '8.2', '8.3', '8.4']
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
php: ['8.1', '8.2', '8.3']
php: ['8.1', '8.2', '8.3', '8.4']
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
Expand Down
26 changes: 23 additions & 3 deletions app/Access/LdapService.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,26 @@ private function getUserWithAttributes(string $userName, array $attributes): ?ar
return $users[0];
}

/**
* Build the user display name from the (potentially multiple) attributes defined by the configuration.
*/
protected function getUserDisplayName(array $userDetails, array $displayNameAttrs, string $defaultValue): string
{
$displayNameParts = [];
foreach ($displayNameAttrs as $dnAttr) {
$dnComponent = $this->getUserResponseProperty($userDetails, $dnAttr, null);
if ($dnComponent) {
$displayNameParts[] = $dnComponent;
}
}

if (empty($displayNameParts)) {
return $defaultValue;
}

return implode(' ', $displayNameParts);
}

/**
* Get the details of a user from LDAP using the given username.
* User found via configurable user filter.
Expand All @@ -81,11 +101,11 @@ public function getUserDetails(string $userName): ?array
{
$idAttr = $this->config['id_attribute'];
$emailAttr = $this->config['email_attribute'];
$displayNameAttr = $this->config['display_name_attribute'];
$displayNameAttrs = explode('|', $this->config['display_name_attribute']);
$thumbnailAttr = $this->config['thumbnail_attribute'];

$user = $this->getUserWithAttributes($userName, array_filter([
'cn', 'dn', $idAttr, $emailAttr, $displayNameAttr, $thumbnailAttr,
'cn', 'dn', $idAttr, $emailAttr, ...$displayNameAttrs, $thumbnailAttr,
]));

if (is_null($user)) {
Expand All @@ -95,7 +115,7 @@ public function getUserDetails(string $userName): ?array
$userCn = $this->getUserResponseProperty($user, 'cn', null);
$formatted = [
'uid' => $this->getUserResponseProperty($user, $idAttr, $user['dn']),
'name' => $this->getUserResponseProperty($user, $displayNameAttr, $userCn),
'name' => $this->getUserDisplayName($user, $displayNameAttrs, $userCn),
'dn' => $user['dn'],
'email' => $this->getUserResponseProperty($user, $emailAttr, null),
'avatar' => $thumbnailAttr ? $this->getUserResponseProperty($user, $thumbnailAttr, null) : null,
Expand Down
35 changes: 32 additions & 3 deletions app/Access/LoginService.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use BookStack\Access\Mfa\MfaSession;
use BookStack\Activity\ActivityType;
use BookStack\Exceptions\LoginAttemptException;
use BookStack\Exceptions\LoginAttemptInvalidUserException;
use BookStack\Exceptions\StoppedAuthenticationException;
use BookStack\Facades\Activity;
use BookStack\Facades\Theme;
Expand All @@ -29,10 +30,14 @@ public function __construct(
* a reason to (MFA or Unconfirmed Email).
* Returns a boolean to indicate the current login result.
*
* @throws StoppedAuthenticationException
* @throws StoppedAuthenticationException|LoginAttemptInvalidUserException
*/
public function login(User $user, string $method, bool $remember = false): void
{
if ($user->isGuest()) {
throw new LoginAttemptInvalidUserException('Login not allowed for guest user');
}

if ($this->awaitingEmailConfirmation($user) || $this->needsMfaVerification($user)) {
$this->setLastLoginAttemptedForUser($user, $method, $remember);

Expand All @@ -58,7 +63,7 @@ public function login(User $user, string $method, bool $remember = false): void
*
* @throws Exception
*/
public function reattemptLoginFor(User $user)
public function reattemptLoginFor(User $user): void
{
if ($user->id !== ($this->getLastLoginAttemptUser()->id ?? null)) {
throw new Exception('Login reattempt user does align with current session state');
Expand Down Expand Up @@ -152,16 +157,40 @@ public function awaitingEmailConfirmation(User $user): bool
*/
public function attempt(array $credentials, string $method, bool $remember = false): bool
{
if ($this->areCredentialsForGuest($credentials)) {
return false;
}

$result = auth()->attempt($credentials, $remember);
if ($result) {
$user = auth()->user();
auth()->logout();
$this->login($user, $method, $remember);
try {
$this->login($user, $method, $remember);
} catch (LoginAttemptInvalidUserException $e) {
// Catch and return false for non-login accounts
// so it looks like a normal invalid login.
return false;
}
}

return $result;
}

/**
* Check if the given credentials are likely for the system guest account.
*/
protected function areCredentialsForGuest(array $credentials): bool
{
if (isset($credentials['email'])) {
return User::query()->where('email', '=', $credentials['email'])
->where('system_name', '=', 'public')
->exists();
}

return false;
}

/**
* Logs the current user out of the application.
* Returns an app post-redirect path.
Expand Down
4 changes: 4 additions & 0 deletions app/Activity/ActivityType.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class ActivityType
const WEBHOOK_UPDATE = 'webhook_update';
const WEBHOOK_DELETE = 'webhook_delete';

const IMPORT_CREATE = 'import_create';
const IMPORT_RUN = 'import_run';
const IMPORT_DELETE = 'import_delete';

/**
* Get all the possible values.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use BookStack\Entities\Models\Entity;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Users\Models\User;
use Illuminate\Support\Facades\Log;

abstract class BaseNotificationHandler implements NotificationHandler
{
Expand Down Expand Up @@ -36,7 +37,11 @@ protected function sendNotificationToUserIds(string $notification, array $userId
}

// Send the notification
$user->notify(new $notification($detail, $initiator));
try {
$user->notify(new $notification($detail, $initiator));
} catch (\Exception $exception) {
Log::error("Failed to send email notification to user [id:{$user->id}] with error: {$exception->getMessage()}");
}
}
}
}
36 changes: 34 additions & 2 deletions app/Api/ApiEntityListFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace BookStack\Api;

use BookStack\Entities\Models\BookChild;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;

class ApiEntityListFormatter
{
Expand All @@ -20,8 +22,16 @@ class ApiEntityListFormatter
* @var array<string|int, string|callable>
*/
protected array $fields = [
'id', 'name', 'slug', 'book_id', 'chapter_id', 'draft',
'template', 'priority', 'created_at', 'updated_at',
'id',
'name',
'slug',
'book_id',
'chapter_id',
'draft',
'template',
'priority',
'created_at',
'updated_at',
];

public function __construct(array $list)
Expand Down Expand Up @@ -62,6 +72,28 @@ public function withTags(): self
return $this;
}

/**
* Include parent book/chapter info in the formatted data.
*/
public function withParents(): self
{
$this->withField('book', function (Entity $entity) {
if ($entity instanceof BookChild && $entity->book) {
return $entity->book->only(['id', 'name', 'slug']);
}
return null;
});

$this->withField('chapter', function (Entity $entity) {
if ($entity instanceof Page && $entity->chapter) {
return $entity->chapter->only(['id', 'name', 'slug']);
}
return null;
});

return $this;
}

/**
* Format the data and return an array of formatted content.
* @return array[]
Expand Down
1 change: 1 addition & 0 deletions app/Entities/Controllers/BookApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function list()
{
$books = $this->queries
->visibleForList()
->with(['cover:id,name,url'])
->addSelect(['created_by', 'updated_by']);

return $this->apiListingResponse($books, [
Expand Down
1 change: 1 addition & 0 deletions app/Entities/Controllers/BookshelfApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function list()
{
$shelves = $this->queries
->visibleForList()
->with(['cover:id,name,url'])
->addSelect(['created_by', 'updated_by']);

return $this->apiListingResponse($shelves, [
Expand Down
1 change: 1 addition & 0 deletions app/Entities/Models/Chapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public function defaultTemplate(): BelongsTo

/**
* Get the visible pages in this chapter.
* @returns Collection<Page>
*/
public function getVisiblePages(): Collection
{
Expand Down
13 changes: 12 additions & 1 deletion app/Entities/Repos/PageRepo.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ public function publishDraft(Page $draft, array $input): Page
return $draft;
}

/**
* Directly update the content for the given page from the provided input.
* Used for direct content access in a way that performs required changes
* (Search index & reference regen) without performing an official update.
*/
public function setContentFromInput(Page $page, array $input): void
{
$this->updateTemplateStatusAndContentFromInput($page, $input);
$this->baseRepo->update($page, []);
}

/**
* Update a page in the system.
*/
Expand Down Expand Up @@ -121,7 +132,7 @@ public function update(Page $page, array $input): Page
return $page;
}

protected function updateTemplateStatusAndContentFromInput(Page $page, array $input)
protected function updateTemplateStatusAndContentFromInput(Page $page, array $input): void
{
if (isset($input['template']) && userCan('templates-manage')) {
$page->template = ($input['template'] === 'true');
Expand Down
17 changes: 6 additions & 11 deletions app/Entities/Tools/Cloner.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,12 @@

class Cloner
{
protected PageRepo $pageRepo;
protected ChapterRepo $chapterRepo;
protected BookRepo $bookRepo;
protected ImageService $imageService;

public function __construct(PageRepo $pageRepo, ChapterRepo $chapterRepo, BookRepo $bookRepo, ImageService $imageService)
{
$this->pageRepo = $pageRepo;
$this->chapterRepo = $chapterRepo;
$this->bookRepo = $bookRepo;
$this->imageService = $imageService;
public function __construct(
protected PageRepo $pageRepo,
protected ChapterRepo $chapterRepo,
protected BookRepo $bookRepo,
protected ImageService $imageService,
) {
}

/**
Expand Down
7 changes: 7 additions & 0 deletions app/Exceptions/LoginAttemptInvalidUserException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace BookStack\Exceptions;

class LoginAttemptInvalidUserException extends LoginAttemptException
{
}
7 changes: 7 additions & 0 deletions app/Exceptions/ZipExportException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace BookStack\Exceptions;

class ZipExportException extends \Exception
{
}
13 changes: 13 additions & 0 deletions app/Exceptions/ZipImportException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace BookStack\Exceptions;

class ZipImportException extends \Exception
{
public function __construct(
public array $errors
) {
$message = "Import failed with errors:" . implode("\n", $this->errors);
parent::__construct($message);
}
}
Loading

0 comments on commit b0c5743

Please sign in to comment.