mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Added content-view body classes generated from tags
Included tests to cover. Closes #3583
This commit is contained in:
parent
840a1ea011
commit
975ba4f8d8
50
app/Actions/TagClassGenerator.php
Normal file
50
app/Actions/TagClassGenerator.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Actions;
|
||||||
|
|
||||||
|
class TagClassGenerator
|
||||||
|
{
|
||||||
|
|
||||||
|
protected array $tags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Tag[] $tags
|
||||||
|
*/
|
||||||
|
public function __construct(array $tags)
|
||||||
|
{
|
||||||
|
$this->tags = $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function generate(): array
|
||||||
|
{
|
||||||
|
$classes = [];
|
||||||
|
|
||||||
|
foreach ($this->tags as $tag) {
|
||||||
|
$name = $this->normalizeTagClassString($tag->name);
|
||||||
|
$value = $this->normalizeTagClassString($tag->value);
|
||||||
|
$classes[] = 'tag-name-' . $name;
|
||||||
|
if ($value) {
|
||||||
|
$classes[] = 'tag-value-' . $value;
|
||||||
|
$classes[] = 'tag-pair-' . $name . '-' . $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_unique($classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateAsString(): string
|
||||||
|
{
|
||||||
|
return implode(' ', $this->generate());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function normalizeTagClassString(string $value): string
|
||||||
|
{
|
||||||
|
$value = str_replace(' ', '', strtolower($value));
|
||||||
|
$value = str_replace('-', '', strtolower($value));
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,8 @@
|
|||||||
@endif
|
@endif
|
||||||
@endpush
|
@endpush
|
||||||
|
|
||||||
|
@include('entities.body-tag-classes', ['entity' => $book])
|
||||||
|
|
||||||
@section('body')
|
@section('body')
|
||||||
|
|
||||||
<div class="mb-s">
|
<div class="mb-s">
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
<meta property="og:description" content="{{ Str::limit($chapter->description, 100, '...') }}">
|
<meta property="og:description" content="{{ Str::limit($chapter->description, 100, '...') }}">
|
||||||
@endpush
|
@endpush
|
||||||
|
|
||||||
|
@include('entities.body-tag-classes', ['entity' => $chapter])
|
||||||
|
|
||||||
@section('body')
|
@section('body')
|
||||||
|
|
||||||
<div class="mb-m print-hidden">
|
<div class="mb-m print-hidden">
|
||||||
|
1
resources/views/entities/body-tag-classes.blade.php
Normal file
1
resources/views/entities/body-tag-classes.blade.php
Normal file
@ -0,0 +1 @@
|
|||||||
|
@push('body-class', e((new \BookStack\Actions\TagClassGenerator($entity->tags->all()))->generateAsString() . ' '))
|
@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ config('app.lang') }}"
|
<html lang="{{ config('app.lang') }}"
|
||||||
dir="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
|
dir="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
|
||||||
class="{{ setting()->getForCurrentUser('dark-mode-enabled') ? 'dark-mode ' : '' }}@yield('body-class')">
|
class="{{ setting()->getForCurrentUser('dark-mode-enabled') ? 'dark-mode ' : '' }}">
|
||||||
<head>
|
<head>
|
||||||
<title>{{ isset($pageTitle) ? $pageTitle . ' | ' : '' }}{{ setting('app-name') }}</title>
|
<title>{{ isset($pageTitle) ? $pageTitle . ' | ' : '' }}{{ setting('app-name') }}</title>
|
||||||
|
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<!-- Translations for JS -->
|
<!-- Translations for JS -->
|
||||||
@stack('translations')
|
@stack('translations')
|
||||||
</head>
|
</head>
|
||||||
<body class="@yield('body-class')">
|
<body class="@stack('body-class')">
|
||||||
|
|
||||||
@include('layouts.parts.base-body-start')
|
@include('layouts.parts.base-body-start')
|
||||||
@include('common.skip-to-content')
|
@include('common.skip-to-content')
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@extends('layouts.base')
|
@extends('layouts.base')
|
||||||
|
|
||||||
@section('body-class', 'tri-layout')
|
@push('body-class', 'tri-layout ')
|
||||||
@section('content-components', 'tri-layout')
|
@section('content-components', 'tri-layout')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@extends('layouts.base')
|
@extends('layouts.base')
|
||||||
|
|
||||||
@section('body-class', 'flexbox')
|
@push('body-class', 'flexbox ')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
<meta property="og:description" content="{{ Str::limit($page->text, 100, '...') }}">
|
<meta property="og:description" content="{{ Str::limit($page->text, 100, '...') }}">
|
||||||
@endpush
|
@endpush
|
||||||
|
|
||||||
|
@include('entities.body-tag-classes', ['entity' => $page])
|
||||||
|
|
||||||
@section('body')
|
@section('body')
|
||||||
|
|
||||||
<div class="mb-m print-hidden">
|
<div class="mb-m print-hidden">
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
@endif
|
@endif
|
||||||
@endpush
|
@endpush
|
||||||
|
|
||||||
|
@include('entities.body-tag-classes', ['entity' => $shelf])
|
||||||
|
|
||||||
@section('body')
|
@section('body')
|
||||||
|
|
||||||
<div class="mb-s">
|
<div class="mb-s">
|
||||||
|
@ -198,9 +198,28 @@ class TagTest extends TestCase
|
|||||||
|
|
||||||
public function test_tag_index_shows_message_on_no_results()
|
public function test_tag_index_shows_message_on_no_results()
|
||||||
{
|
{
|
||||||
/** @var Page $page */
|
|
||||||
$resp = $this->asEditor()->get('/tags?search=testingval');
|
$resp = $this->asEditor()->get('/tags?search=testingval');
|
||||||
$resp->assertSee('No items available');
|
$resp->assertSee('No items available');
|
||||||
$resp->assertSee('Tags can be assigned via the page editor sidebar');
|
$resp->assertSee('Tags can be assigned via the page editor sidebar');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_tag_classes_visible_on_entities()
|
||||||
|
{
|
||||||
|
$this->asEditor();
|
||||||
|
|
||||||
|
foreach ($this->getEachEntityType() as $entity) {
|
||||||
|
$entity->tags()->create(['name' => 'My Super Tag Name', 'value' => 'An-awesome-value']);
|
||||||
|
$html = $this->withHtml($this->get($entity->getUrl()));
|
||||||
|
$html->assertElementExists('body.tag-name-mysupertagname.tag-value-anawesomevalue.tag-pair-mysupertagname-anawesomevalue');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_tag_classes_are_escaped()
|
||||||
|
{
|
||||||
|
$page = Page::query()->first();
|
||||||
|
$page->tags()->create(['name' => '<>']);
|
||||||
|
$resp = $this->asEditor()->get($page->getUrl());
|
||||||
|
$resp->assertDontSee('tag-name-<>', false);
|
||||||
|
$resp->assertSee('tag-name-<>', false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,4 +428,17 @@ abstract class TestCase extends BaseTestCase
|
|||||||
|
|
||||||
$this->assertDatabaseHas('activities', $detailsToCheck);
|
$this->assertDatabaseHas('activities', $detailsToCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Entity[]
|
||||||
|
*/
|
||||||
|
protected function getEachEntityType(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'page' => Page::query()->first(),
|
||||||
|
'chapter' => Chapter::query()->first(),
|
||||||
|
'book' => Book::query()->first(),
|
||||||
|
'bookshelf' => Bookshelf::query()->first(),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user