From: Dan Brown Date: Sat, 28 Jun 2025 21:27:28 +0000 (+0100) Subject: Customization: Added parent tag classes X-Git-Url: http://source.bookstackapp.com/bookstack/commitdiff_plain/dca9765d5d432333630d12a62445dca2e863600a?hp=-c Customization: Added parent tag classes For #5217 --- dca9765d5d432333630d12a62445dca2e863600a diff --git a/app/Activity/Models/Tag.php b/app/Activity/Models/Tag.php index 0af0a65ac..0e7c68a27 100644 --- a/app/Activity/Models/Tag.php +++ b/app/Activity/Models/Tag.php @@ -12,6 +12,8 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; * @property int $id * @property string $name * @property string $value + * @property int $entity_id + * @property string $entity_type * @property int $order */ class Tag extends Model diff --git a/app/Activity/Tools/TagClassGenerator.php b/app/Activity/Tools/TagClassGenerator.php index 1a1bd16c8..5bcb44113 100644 --- a/app/Activity/Tools/TagClassGenerator.php +++ b/app/Activity/Tools/TagClassGenerator.php @@ -3,17 +3,15 @@ namespace BookStack\Activity\Tools; use BookStack\Activity\Models\Tag; +use BookStack\Entities\Models\BookChild; +use BookStack\Entities\Models\Entity; +use BookStack\Entities\Models\Page; class TagClassGenerator { - protected array $tags; - - /** - * @param Tag[] $tags - */ - public function __construct(array $tags) - { - $this->tags = $tags; + public function __construct( + protected Entity $entity + ) { } /** @@ -22,14 +20,23 @@ class TagClassGenerator public function generate(): array { $classes = []; + $tags = $this->entity->tags->all(); + + foreach ($tags as $tag) { + array_push($classes, ...$this->generateClassesForTag($tag)); + } + + if ($this->entity instanceof BookChild && userCan('view', $this->entity->book)) { + $bookTags = $this->entity->book->tags; + foreach ($bookTags as $bookTag) { + array_push($classes, ...$this->generateClassesForTag($bookTag, 'book-')); + } + } - 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; + if ($this->entity instanceof Page && $this->entity->chapter && userCan('view', $this->entity->chapter)) { + $chapterTags = $this->entity->chapter->tags; + foreach ($chapterTags as $chapterTag) { + array_push($classes, ...$this->generateClassesForTag($chapterTag, 'chapter-')); } } @@ -41,6 +48,22 @@ class TagClassGenerator return implode(' ', $this->generate()); } + /** + * @return string[] + */ + protected function generateClassesForTag(Tag $tag, string $prefix = ''): array + { + $classes = []; + $name = $this->normalizeTagClassString($tag->name); + $value = $this->normalizeTagClassString($tag->value); + $classes[] = "{$prefix}tag-name-{$name}"; + if ($value) { + $classes[] = "{$prefix}tag-value-{$value}"; + $classes[] = "{$prefix}tag-pair-{$name}-{$value}"; + } + return $classes; + } + protected function normalizeTagClassString(string $value): string { $value = str_replace(' ', '', strtolower($value)); diff --git a/resources/views/entities/body-tag-classes.blade.php b/resources/views/entities/body-tag-classes.blade.php index 08427f1a5..f9ba023c3 100644 --- a/resources/views/entities/body-tag-classes.blade.php +++ b/resources/views/entities/body-tag-classes.blade.php @@ -1 +1 @@ -@push('body-class', e((new \BookStack\Activity\Tools\TagClassGenerator($entity->tags->all()))->generateAsString() . ' ')) \ No newline at end of file +@push('body-class', e((new \BookStack\Activity\Tools\TagClassGenerator($entity))->generateAsString() . ' ')) \ No newline at end of file diff --git a/tests/Entity/TagTest.php b/tests/Entity/TagTest.php index 729f93903..63f037d9c 100644 --- a/tests/Entity/TagTest.php +++ b/tests/Entity/TagTest.php @@ -230,4 +230,39 @@ class TagTest extends TestCase $resp->assertDontSee('tag-name-<>', false); $resp->assertSee('tag-name-<>', false); } + + public function test_parent_tag_classes_visible() + { + $page = $this->entities->pageWithinChapter(); + $page->chapter->tags()->create(['name' => 'My Chapter Tag', 'value' => 'abc123']); + $page->book->tags()->create(['name' => 'My Book Tag', 'value' => 'def456']); + $this->asEditor(); + + $html = $this->withHtml($this->get($page->getUrl())); + $html->assertElementExists('body.chapter-tag-pair-mychaptertag-abc123'); + $html->assertElementExists('body.book-tag-pair-mybooktag-def456'); + + $html = $this->withHtml($this->get($page->chapter->getUrl())); + $html->assertElementExists('body.book-tag-pair-mybooktag-def456'); + } + + public function test_parent_tag_classes_not_visible_if_cannot_see_parent() + { + $page = $this->entities->pageWithinChapter(); + $page->chapter->tags()->create(['name' => 'My Chapter Tag', 'value' => 'abc123']); + $page->book->tags()->create(['name' => 'My Book Tag', 'value' => 'def456']); + $editor = $this->users->editor(); + $this->actingAs($editor); + + $this->permissions->setEntityPermissions($page, ['view'], [$editor->roles()->first()]); + $this->permissions->disableEntityInheritedPermissions($page->chapter); + + $html = $this->withHtml($this->get($page->getUrl())); + $html->assertElementNotExists('body.chapter-tag-pair-mychaptertag-abc123'); + $html->assertElementExists('body.book-tag-pair-mybooktag-def456'); + + $this->permissions->disableEntityInheritedPermissions($page->book); + $html = $this->withHtml($this->get($page->getUrl())); + $html->assertElementNotExists('body.book-tag-pair-mybooktag-def456'); + } }