namespace BookStack\Activity\Controllers;
use BookStack\Activity\Models\Watch;
-use BookStack\Activity\Tools\UserWatchOptions;
+use BookStack\Activity\Tools\UserEntityWatchOptions;
use BookStack\App\Model;
use BookStack\Entities\Models\Entity;
use BookStack\Http\Controller;
]);
$watchable = $this->getValidatedModelFromRequest($request);
- $watchOptions = new UserWatchOptions(user());
- $watchOptions->updateEntityWatchLevel($watchable, $requestData['level']);
+ $watchOptions = new UserEntityWatchOptions(user(), $watchable);
+ $watchOptions->updateWatchLevel($requestData['level']);
$this->showSuccessNotification(trans('activities.watch_update_level_notification'));
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Tools;
+
+use BookStack\Activity\Models\Watch;
+use BookStack\Activity\WatchLevels;
+use BookStack\Entities\Models\BookChild;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use BookStack\Users\Models\User;
+use Illuminate\Database\Eloquent\Builder;
+
+class UserEntityWatchOptions
+{
+ protected ?array $watchMap = null;
+
+ public function __construct(
+ protected User $user,
+ protected Entity $entity,
+ ) {
+ }
+
+ public function canWatch(): bool
+ {
+ return $this->user->can('receive-notifications') && !$this->user->isDefault();
+ }
+
+ public function getWatchLevel(): string
+ {
+ return WatchLevels::levelValueToName($this->getWatchLevelValue());
+ }
+
+ public function isWatching(): bool
+ {
+ return $this->getWatchLevelValue() !== WatchLevels::DEFAULT;
+ }
+
+ public function getWatchedParent(): ?WatchedParentDetails
+ {
+ $watchMap = $this->getWatchMap();
+ unset($watchMap[$this->entity->getMorphClass()]);
+
+ if (isset($watchMap['chapter'])) {
+ return new WatchedParentDetails('chapter', $watchMap['chapter']);
+ }
+
+ if (isset($watchMap['book'])) {
+ return new WatchedParentDetails('book', $watchMap['book']);
+ }
+
+ return null;
+ }
+
+ public function updateWatchLevel(string $level): void
+ {
+ $levelValue = WatchLevels::levelNameToValue($level);
+ if ($levelValue < 0) {
+ $this->remove();
+ return;
+ }
+
+ $this->updateLevel($levelValue);
+ }
+
+ public function getWatchMap(): array
+ {
+ if (!is_null($this->watchMap)) {
+ return $this->watchMap;
+ }
+
+ $entities = [$this->entity];
+ if ($this->entity instanceof BookChild) {
+ $entities[] = $this->entity->book;
+ }
+ if ($this->entity instanceof Page && $this->entity->chapter) {
+ $entities[] = $this->entity->chapter;
+ }
+
+ $query = Watch::query()->where(function (Builder $subQuery) use ($entities) {
+ foreach ($entities as $entity) {
+ $subQuery->orWhere(function (Builder $whereQuery) use ($entity) {
+ $whereQuery->where('watchable_type', '=', $entity->getMorphClass())
+ ->where('watchable_id', '=', $entity->id);
+ });
+ }
+ });
+
+ $this->watchMap = $query->get(['watchable_type', 'level'])
+ ->pluck('level', 'watchable_type')
+ ->toArray();
+
+ return $this->watchMap;
+ }
+
+ protected function getWatchLevelValue()
+ {
+ return $this->getWatchMap()[$this->entity->getMorphClass()] ?? WatchLevels::DEFAULT;
+ }
+
+ protected function updateLevel(int $levelValue): void
+ {
+ Watch::query()->updateOrCreate([
+ 'watchable_id' => $this->entity->id,
+ 'watchable_type' => $this->entity->getMorphClass(),
+ 'user_id' => $this->user->id,
+ ], [
+ 'level' => $levelValue,
+ ]);
+ $this->watchMap = null;
+ }
+
+ protected function remove(): void
+ {
+ $this->entityQuery()->delete();
+ $this->watchMap = null;
+ }
+
+ protected function entityQuery(): Builder
+ {
+ return Watch::query()->where('watchable_id', '=', $this->entity->id)
+ ->where('watchable_type', '=', $this->entity->getMorphClass())
+ ->where('user_id', '=', $this->user->id);
+ }
+}
+++ /dev/null
-<?php
-
-namespace BookStack\Activity\Tools;
-
-use BookStack\Activity\Models\Watch;
-use BookStack\Activity\WatchLevels;
-use BookStack\Entities\Models\Entity;
-use BookStack\Users\Models\User;
-use Illuminate\Database\Eloquent\Builder;
-
-class UserWatchOptions
-{
- public function __construct(
- protected User $user,
- ) {
- }
-
- public function canWatch(): bool
- {
- return $this->user->can('receive-notifications') && !$this->user->isDefault();
- }
-
- public function getEntityWatchLevel(Entity $entity): string
- {
- $levelValue = $this->entityQuery($entity)->first(['level'])->level ?? -1;
- return WatchLevels::levelValueToName($levelValue);
- }
-
- public function isWatching(Entity $entity): bool
- {
- return $this->entityQuery($entity)->exists();
- }
-
- public function updateEntityWatchLevel(Entity $entity, string $level): void
- {
- $levelValue = WatchLevels::levelNameToValue($level);
- if ($levelValue < 0) {
- $this->removeForEntity($entity);
- return;
- }
-
- $this->updateForEntity($entity, $levelValue);
- }
-
- protected function updateForEntity(Entity $entity, int $levelValue): void
- {
- Watch::query()->updateOrCreate([
- 'watchable_id' => $entity->id,
- 'watchable_type' => $entity->getMorphClass(),
- 'user_id' => $this->user->id,
- ], [
- 'level' => $levelValue,
- ]);
- }
-
- protected function removeForEntity(Entity $entity): void
- {
- $this->entityQuery($entity)->delete();
- }
-
- protected function entityQuery(Entity $entity): Builder
- {
- return Watch::query()->where('watchable_id', '=', $entity->id)
- ->where('watchable_type', '=', $entity->getMorphClass())
- ->where('user_id', '=', $this->user->id);
- }
-}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Tools;
+
+use BookStack\Activity\WatchLevels;
+
+class WatchedParentDetails
+{
+ public function __construct(
+ public string $type,
+ public int $level,
+ ) {
+ }
+
+ public function ignoring(): bool
+ {
+ return $this->level === WatchLevels::IGNORE;
+ }
+}
use BookStack\Activity\ActivityQueries;
use BookStack\Activity\ActivityType;
use BookStack\Activity\Models\View;
-use BookStack\Activity\Tools\UserWatchOptions;
+use BookStack\Activity\Tools\UserEntityWatchOptions;
use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Entities\Tools\BookContents;
'current' => $book,
'bookChildren' => $bookChildren,
'bookParentShelves' => $bookParentShelves,
- 'watchOptions' => new UserWatchOptions(user()),
+ 'watchOptions' => new UserEntityWatchOptions(user(), $book),
'activity' => $activities->entityActivity($book, 20, 1),
'referenceCount' => $this->referenceFetcher->getPageReferenceCountToEntity($book),
]);
namespace BookStack\Entities\Controllers;
use BookStack\Activity\Models\View;
-use BookStack\Activity\Tools\UserWatchOptions;
+use BookStack\Activity\Tools\UserEntityWatchOptions;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Repos\ChapterRepo;
use BookStack\Entities\Tools\BookContents;
'chapter' => $chapter,
'current' => $chapter,
'sidebarTree' => $sidebarTree,
- 'watchOptions' => new UserWatchOptions(user()),
+ 'watchOptions' => new UserEntityWatchOptions(user(), $chapter),
'pages' => $pages,
'next' => $nextPreviousLocator->getNext(),
'previous' => $nextPreviousLocator->getPrevious(),
use BookStack\Activity\Models\View;
use BookStack\Activity\Tools\CommentTree;
-use BookStack\Activity\Tools\UserWatchOptions;
+use BookStack\Activity\Tools\UserEntityWatchOptions;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Entities\Tools\BookContents;
'sidebarTree' => $sidebarTree,
'commentTree' => $commentTree,
'pageNav' => $pageNav,
- 'watchOptions' => new UserWatchOptions(user()),
+ 'watchOptions' => new UserEntityWatchOptions(user(), $page),
'next' => $nextPreviousLocator->getNext(),
'previous' => $nextPreviousLocator->getPrevious(),
'referenceCount' => $this->referenceFetcher->getPageReferenceCountToEntity($page),
'watch_detail_new' => 'Watching for new pages',
'watch_detail_updates' => 'Watching new pages and updates',
'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
<hr class="primary-background">
- @if($watchOptions->canWatch() && !$watchOptions->isWatching($book))
+ @if($watchOptions->canWatch() && !$watchOptions->isWatching())
@include('entities.watch-action', ['entity' => $book])
@endif
@if(signedInUser())
<hr class="primary-background"/>
- @if($watchOptions->canWatch() && !$watchOptions->isWatching($chapter))
+ @if($watchOptions->canWatch() && !$watchOptions->isWatching())
@include('entities.watch-action', ['entity' => $chapter])
@endif
@if(signedInUser())
</a>
@endif
- @if($watchOptions?->canWatch() && $watchOptions->isWatching($entity))
- @php
- $watchLevel = $watchOptions->getEntityWatchLevel($entity);
- @endphp
- <div component="dropdown"
- class="dropdown-container block my-xxs">
- <a refs="dropdown@toggle" href="#" class="entity-meta-item my-none">
- @icon(($watchLevel === 'ignore' ? 'watch-ignore' : 'watch'))
- <span>{{ trans('entities.watch_detail_' . $watchLevel) }}</span>
- </a>
- @include('entities.watch-controls', ['entity' => $entity, 'watchLevel' => $watchLevel])
- </div>
+ @if($watchOptions?->canWatch())
+ @if($watchOptions->isWatching())
+ @include('entities.watch-controls', [
+ 'entity' => $entity,
+ 'watchLevel' => $watchOptions->getWatchLevel(),
+ 'label' => trans('entities.watch_detail_' . $watchOptions->getWatchLevel()),
+ 'ignoring' => $watchOptions->getWatchLevel() === 'ignore',
+ ])
+ @elseif($watchedParent = $watchOptions->getWatchedParent())
+ @include('entities.watch-controls', [
+ 'entity' => $entity,
+ 'watchLevel' => $watchOptions->getWatchLevel(),
+ 'label' => trans('entities.watch_detail_parent_' . $watchedParent->type . ($watchedParent->ignoring() ? '_ignore' : '')),
+ 'ignoring' => $watchedParent->ignoring(),
+ ])
+ @endif
@endif
</div>
\ No newline at end of file
-<form action="{{ url('/watching/update') }}" method="POST">
- {{ method_field('PUT') }}
- {{ csrf_field() }}
- <input type="hidden" name="type" value="{{ get_class($entity) }}">
- <input type="hidden" name="id" value="{{ $entity->id }}">
+<div component="dropdown"
+ class="dropdown-container block my-xxs">
+ <a refs="dropdown@toggle" href="#" class="entity-meta-item my-none">
+ @icon(($ignoring ? 'watch-ignore' : 'watch'))
+ <span>{{ $label }}</span>
+ </a>
+ <form action="{{ url('/watching/update') }}" method="POST">
+ {{ method_field('PUT') }}
+ {{ csrf_field() }}
+ <input type="hidden" name="type" value="{{ get_class($entity) }}">
+ <input type="hidden" name="id" value="{{ $entity->id }}">
- <ul refs="dropdown@menu" class="dropdown-menu xl-limited anchor-left pb-none">
- @foreach(\BookStack\Activity\WatchLevels::all() as $option => $value)
- <li>
- <button name="level" value="{{ $option }}" class="icon-item">
- @if($watchLevel === $option)
- <span class="text-pos pt-m"
- title="{{ trans('common.status_active') }}">@icon('check-circle')</span>
- @else
- <span title="{{ trans('common.status_inactive') }}"></span>
- @endif
- <div class="break-text">
- <div class="mb-xxs"><strong>{{ trans('entities.watch_title_' . $option) }}</strong></div>
- <div class="text-muted text-small">
- {{ trans('entities.watch_desc_' . $option) }}
+ <ul refs="dropdown@menu" class="dropdown-menu xl-limited anchor-left pb-none">
+ @foreach(\BookStack\Activity\WatchLevels::all() as $option => $value)
+ <li>
+ <button name="level" value="{{ $option }}" class="icon-item">
+ @if($watchLevel === $option)
+ <span class="text-pos pt-m"
+ title="{{ trans('common.status_active') }}">@icon('check-circle')</span>
+ @else
+ <span title="{{ trans('common.status_inactive') }}"></span>
+ @endif
+ <div class="break-text">
+ <div class="mb-xxs"><strong>{{ trans('entities.watch_title_' . $option) }}</strong></div>
+ <div class="text-muted text-small">
+ {{ trans('entities.watch_desc_' . $option) }}
+ </div>
</div>
- </div>
- </button>
- </li>
+ </button>
+ </li>
+ <li>
+ <hr class="my-none">
+ </li>
+ @endforeach
<li>
- <hr class="my-none">
+ <a href="{{ url('/preferences/notifications') }}"
+ target="_blank"
+ class="text-item text-muted text-small break-text">{{ trans('entities.watch_change_default') }}</a>
</li>
- @endforeach
- <li>
- <a href="{{ url('/preferences/notifications') }}"
- target="_blank"
- class="text-item text-muted text-small break-text">{{ trans('entities.watch_change_default') }}</a>
- </li>
- </ul>
-</form>
\ No newline at end of file
+ </ul>
+ </form>
+</div>
\ No newline at end of file
<hr class="primary-background"/>
- @if($watchOptions->canWatch() && !$watchOptions->isWatching($page))
+ @if($watchOptions->canWatch() && !$watchOptions->isWatching())
@include('entities.watch-action', ['entity' => $page])
@endif
@if(signedInUser())