Compare commits

..

14 Commits

Author SHA1 Message Date
Dan Brown
926abbe776 Updated version and assets for release v21.05.4 2021-08-04 21:29:10 +01:00
Dan Brown
4fabef3a57 Merge branch 'v21.05.x' into release 2021-08-04 21:28:45 +01:00
Dan Brown
65ebffa002 Updated when github actions run 2021-08-04 21:22:53 +01:00
Dan Brown
a04064f981 Updated php dependancies up minor versions 2021-08-04 21:10:55 +01:00
Dan Brown
7d19057e68 Fixed issue where user id still used on profile pages
Updated to use slugs and added testing to cover.
2021-08-04 21:08:51 +01:00
Dan Brown
0de0507137 Added vb.net code language option
Related to #2869
2021-08-04 20:56:34 +01:00
Dan Brown
7a8954ee65 Fixed audit log user dropdown usability issue
User search input blur would trigger the submission of the search
filters which would cause strange thing where you'd click on a search
filtered user which would blur the input hence submit, but the user
would think they've clicked the user and the page would reload but the
input had not updated at that point.

Related to #2863
2021-08-04 20:48:23 +01:00
Dan Brown
5ef4cd80c3 Updated version and assets for release v21.05.3 2021-07-03 11:59:52 +01:00
Dan Brown
e01f23583f Merge branch 'v21.05.x' into release 2021-07-03 11:59:21 +01:00
Dan Brown
b1ee1a856f Updated php dependancies for minor release 2021-07-03 11:57:32 +01:00
Dan Brown
4da72aa267 Fixed issue with translation loading without theme
System was using the empty state return from theme_path,
when no theme was configured, for loading in languages
which would result in the root path being looked up upon.

This changes the theme_path helper to return null in cases a theme
is not configured instead of empty string to help prevent assumed
return path will be legitimate, and to help enforce error case
handling.

For #2836
2021-07-03 11:53:46 +01:00
Dan Brown
3dda622f0a Added a "skip to content" link.
Closes #2810
2021-06-15 20:58:45 +01:00
Dan Brown
7d951b842c Made social account detach a POST request
Closes #2808
2021-06-14 22:37:58 +01:00
Dan Brown
94bf5b8fbb Added test for social account detach 2021-06-14 22:30:53 +01:00
29 changed files with 468 additions and 371 deletions

View File

@@ -2,15 +2,11 @@ name: phpunit
on:
push:
branches:
- master
- release
- gh_actions_update
branches-ignore:
- l10n_master
pull_request:
branches:
- '*'
- '*/*'
- '!l10n_master'
branches-ignore:
- l10n_master
jobs:
build:

View File

@@ -2,15 +2,11 @@ name: test-migrations
on:
push:
branches:
- master
- release
- gh_actions_update
branches-ignore:
- l10n_master
pull_request:
branches:
- '*'
- '*/*'
- '!l10n_master'
branches-ignore:
- l10n_master
jobs:
build:

View File

@@ -45,7 +45,7 @@ class ThemeService
public function readThemeActions()
{
$themeActionsFile = theme_path('functions.php');
if (file_exists($themeActionsFile)) {
if ($themeActionsFile && file_exists($themeActionsFile)) {
require $themeActionsFile;
}
}

View File

@@ -20,7 +20,8 @@ class FileLoader extends BaseLoader
}
if (is_null($namespace) || $namespace === '*') {
$themeTranslations = $this->loadPath(theme_path('lang'), $locale, $group);
$themePath = theme_path('lang');
$themeTranslations = $themePath ? $this->loadPath($themePath, $locale, $group) : [];
$originalTranslations = $this->loadPath($this->path, $locale, $group);
return array_merge($originalTranslations, $themeTranslations);
}

View File

@@ -94,13 +94,15 @@ function setting(string $key = null, $default = null)
/**
* Get a path to a theme resource.
* Returns null if a theme is not configured and
* therefore a full path is not available for use.
*/
function theme_path(string $path = ''): string
function theme_path(string $path = ''): ?string
{
$theme = config('view.theme');
if (!$theme) {
return '';
return null;
}
return base_path('themes/' . $theme .($path ? DIRECTORY_SEPARATOR.$path : $path));

621
composer.lock generated

File diff suppressed because it is too large Load Diff

68
public/dist/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -27,6 +27,7 @@ class DropdownSearch {
this.runLocalSearch(input);
} else {
this.toggleLoading(true);
this.listContainerElem.innerHTML = '';
this.runAjaxSearch(input);
}
}

View File

@@ -6,7 +6,14 @@
class SubmitOnChange {
setup() {
this.$el.addEventListener('change', () => {
this.filter = this.$opts.filter;
this.$el.addEventListener('change', (event) => {
if (this.filter && !event.target.matches(this.filter)) {
return;
}
const form = this.$el.closest('form');
if (form) {
form.submit();

View File

@@ -3,7 +3,6 @@ import {onChildEvent} from "../services/dom";
class UserSelect {
setup() {
this.input = this.$refs.input;
this.userInfoContainer = this.$refs.userInfo;

View File

@@ -26,6 +26,7 @@ import 'codemirror/mode/rust/rust';
import 'codemirror/mode/shell/shell';
import 'codemirror/mode/sql/sql';
import 'codemirror/mode/toml/toml';
import 'codemirror/mode/vb/vb';
import 'codemirror/mode/vbscript/vbscript';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/yaml/yaml';
@@ -87,6 +88,8 @@ const modeMap = {
sql: 'text/x-sql',
vbs: 'vbscript',
vbscript: 'vbscript',
'vb.net': 'text/x-vb',
vbnet: 'text/x-vb',
xml: 'xml',
yaml: 'yaml',
yml: 'yaml',

View File

@@ -60,6 +60,7 @@ return [
'no_activity' => 'No activity to show',
'no_items' => 'No items available',
'back_to_top' => 'Back to top',
'skip_to_main_content' => 'Skip to main content',
'toggle_details' => 'Toggle Details',
'toggle_thumbnails' => 'Toggle Thumbnails',
'details' => 'Details',

View File

@@ -629,7 +629,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
}
.code-editor .lang-options {
max-width: 480px;
max-width: 540px;
margin-bottom: $-s;
a {
margin-inline-end: $-xs;

View File

@@ -136,6 +136,23 @@ $btt-size: 40px;
}
}
.skip-to-content-link {
position: fixed;
top: -$-xxl;
left: 0;
background-color: #FFF;
z-index: 15;
border-radius: 0 4px 4px 0;
display: block;
box-shadow: $bs-dark;
font-weight: bold;
&:focus {
top: $-xl;
outline-offset: -10px;
outline: 2px dotted var(--color-primary);
}
}
.contained-search-box {
display: flex;
height: 38px;

View File

@@ -28,6 +28,7 @@
</head>
<body class="@yield('body-class')">
@include('common.parts.skip-to-content')
@include('partials.notifications')
@include('common.header')

View File

@@ -0,0 +1 @@
<a class="px-m py-s skip-to-content-link" href="#main-content">{{ trans('common.skip_to_main_content') }}</a>

View File

@@ -35,6 +35,7 @@
<a refs="code-editor@languageLink" data-lang="shell">Shell/Bash</a>
<a refs="code-editor@languageLink" data-lang="SQL">SQL</a>
<a refs="code-editor@languageLink" data-lang="VBScript">VBScript</a>
<a refs="code-editor@languageLink" data-lang="VB.NET">VB.NET</a>
<a refs="code-editor@languageLink" data-lang="XML">XML</a>
<a refs="code-editor@languageLink" data-lang="YAML">YAML</a>
</small>

View File

@@ -5,7 +5,7 @@
<div class="container small py-xl">
<main class="card content-wrap auto-height">
<div class="body">
<div id="main-content" class="body">
<h3>{{ trans('errors.error_occurred') }}</h3>
<h5 class="mb-m">{{ $message ?? 'An unknown error occurred' }}</h5>
<p><a href="{{ url('/') }}" class="button outline">{{ trans('errors.return_home') }}</a></p>

View File

@@ -8,7 +8,7 @@
@section('content')
<div class="flex-fill flex fill-height">
<div id="main-content" class="flex-fill flex fill-height">
<form action="{{ $page->getUrl() }}" autocomplete="off" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
{{ csrf_field() }}

View File

@@ -41,7 +41,9 @@
</div>
@endforeach
<div class="form-group ml-auto" component="submit-on-change">
<div class="form-group ml-auto"
component="submit-on-change"
option:submit-on-change:filter='[name="user"]'>
<label for="owner">{{ trans('settings.audit_table_user') }}</label>
@include('components.user-select', ['user' => $listDetails['user'] ? \BookStack\Auth\User::query()->find($listDetails['user']) : null, 'name' => 'user', 'compact' => true])
</div>

View File

@@ -4,7 +4,7 @@
<div class="flex-fill flex">
<div class="content flex">
<div class="scroll-body">
<div id="main-content" class="scroll-body">
@yield('body')
</div>
</div>

View File

@@ -34,7 +34,7 @@
</div>
<div class="@yield('body-wrap-classes') tri-layout-middle">
<div class="tri-layout-middle-contents">
<div id="main-content" class="tri-layout-middle-contents">
@yield('body')
</div>
</div>

View File

@@ -74,8 +74,11 @@
<div role="presentation">@icon('auth/'. $driver, ['style' => 'width: 56px;height: 56px;'])</div>
<div>
@if($user->hasSocialAccount($driver))
<a href="{{ url("/login/service/{$driver}/detach") }}" aria-label="{{ trans('settings.users_social_disconnect') }} - {{ $driver }}"
class="button small outline">{{ trans('settings.users_social_disconnect') }}</a>
<form action="{{ url("/login/service/{$driver}/detach") }}" method="POST">
{{ csrf_field() }}
<button aria-label="{{ trans('settings.users_social_disconnect') }} - {{ $driver }}"
class="button small outline">{{ trans('settings.users_social_disconnect') }}</button>
</form>
@else
<a href="{{ url("/login/service/{$driver}") }}" aria-label="{{ trans('settings.users_social_connect') }} - {{ $driver }}"
class="button small outline">{{ trans('settings.users_social_connect') }}</a>

View File

@@ -60,7 +60,7 @@
<h2 id="recent-pages" class="list-heading">
{{ trans('entities.recently_created_pages') }}
@if (count($recentlyCreated['pages']) > 0)
<a href="{{ url('/search?term=' . urlencode('{created_by:'.$user->id.'} {type:page}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
<a href="{{ url('/search?term=' . urlencode('{created_by:'.$user->slug.'} {type:page}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
@endif
</h2>
@if (count($recentlyCreated['pages']) > 0)
@@ -74,7 +74,7 @@
<h2 id="recent-chapters" class="list-heading">
{{ trans('entities.recently_created_chapters') }}
@if (count($recentlyCreated['chapters']) > 0)
<a href="{{ url('/search?term=' . urlencode('{created_by:'.$user->id.'} {type:chapter}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
<a href="{{ url('/search?term=' . urlencode('{created_by:'.$user->slug.'} {type:chapter}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
@endif
</h2>
@if (count($recentlyCreated['chapters']) > 0)
@@ -88,7 +88,7 @@
<h2 id="recent-books" class="list-heading">
{{ trans('entities.recently_created_books') }}
@if (count($recentlyCreated['books']) > 0)
<a href="{{ url('/search?term=' . urlencode('{created_by:'.$user->id.'} {type:book}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
<a href="{{ url('/search?term=' . urlencode('{created_by:'.$user->slug.'} {type:book}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
@endif
</h2>
@if (count($recentlyCreated['books']) > 0)
@@ -102,7 +102,7 @@
<h2 id="recent-shelves" class="list-heading">
{{ trans('entities.recently_created_shelves') }}
@if (count($recentlyCreated['shelves']) > 0)
<a href="{{ url('/search?term=' . urlencode('{created_by:'.$user->id.'} {type:bookshelf}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
<a href="{{ url('/search?term=' . urlencode('{created_by:'.$user->slug.'} {type:bookshelf}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
@endif
</h2>
@if (count($recentlyCreated['shelves']) > 0)

View File

@@ -14,7 +14,7 @@ Route::group(['middleware' => 'auth'], function () {
// 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');
@@ -226,7 +226,7 @@ Route::group(['middleware' => 'auth'], function () {
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@detach');
Route::post('/login/service/{socialDriver}/detach', 'Auth\SocialController@detach');
});
Route::get('/register/service/{socialDriver}', 'Auth\SocialController@register');

View File

@@ -1,5 +1,6 @@
<?php namespace Tests\Auth;
use BookStack\Auth\SocialAccount;
use BookStack\Auth\User;
use DB;
use Laravel\Socialite\Contracts\Factory;
@@ -83,6 +84,31 @@ class SocialAuthTest extends TestCase
$resp->assertDontSee("login-form");
}
public function test_social_account_detach()
{
$editor = $this->getEditor();
config([
'GITHUB_APP_ID' => 'abc123', 'GITHUB_APP_SECRET' => '123abc',
'APP_URL' => 'http://localhost'
]);
$socialAccount = SocialAccount::query()->forceCreate([
'user_id' => $editor->id,
'driver' => 'github',
'driver_id' => 'logintest123',
]);
$resp = $this->actingAs($editor)->get($editor->getEditUrl());
$resp->assertElementContains('form[action$="/login/service/github/detach"]', 'Disconnect Account');
$resp = $this->post('/login/service/github/detach');
$resp->assertRedirect($editor->getEditUrl());
$resp = $this->followRedirects($resp);
$resp->assertSee('Github account was successfully disconnected from your profile.');
$this->assertDatabaseMissing('social_accounts', ['id' => $socialAccount->id]);
}
public function test_social_autoregister()
{
config([

View File

@@ -83,6 +83,23 @@ class UserProfileTest extends BrowserKitTest
->see($newUser->name);
}
public function test_profile_has_search_links_in_created_entity_lists()
{
$user = $this->getEditor();
$resp = $this->actingAs($this->getAdmin())->visit('/user/' . $user->slug);
$expectedLinks = [
'/search?term=%7Bcreated_by%3A' . $user->slug . '%7D+%7Btype%3Apage%7D',
'/search?term=%7Bcreated_by%3A' . $user->slug . '%7D+%7Btype%3Achapter%7D',
'/search?term=%7Bcreated_by%3A' . $user->slug . '%7D+%7Btype%3Abook%7D',
'/search?term=%7Bcreated_by%3A' . $user->slug . '%7D+%7Btype%3Abookshelf%7D',
];
foreach ($expectedLinks as $link) {
$resp->seeInElement('[href$="' . $link . '"]', 'View All');
}
}
public function test_guest_profile_shows_limited_form()
{
$this->asAdmin()

View File

@@ -1 +1 @@
v21.05.2
v21.05.4