1 <?php namespace BookStack\Actions;
3 use BookStack\Auth\Permissions\PermissionService;
4 use BookStack\Auth\User;
5 use BookStack\Entities\Entity;
6 use Illuminate\Database\Eloquent\Relations\Relation;
7 use Illuminate\Support\Collection;
8 use Illuminate\Support\Facades\Log;
14 protected $permissionService;
17 * ActivityService constructor.
19 public function __construct(Activity $activity, PermissionService $permissionService)
21 $this->activity = $activity;
22 $this->permissionService = $permissionService;
27 * Add activity data to database.
29 public function add(Entity $entity, string $activityKey, ?int $bookId = null)
31 $activity = $this->newActivityForUser($activityKey, $bookId);
32 $entity->activity()->save($activity);
33 $this->setNotification($activityKey);
37 * Adds a activity history with a message, without binding to a entity.
39 public function addMessage(string $activityKey, string $message, ?int $bookId = null)
41 $this->newActivityForUser($activityKey, $bookId)->forceFill([
45 $this->setNotification($activityKey);
49 * Get a new activity instance for the current user.
51 protected function newActivityForUser(string $key, ?int $bookId = null): Activity
53 return $this->activity->newInstance()->forceFill([
54 'key' => strtolower($key),
55 'user_id' => $this->user->id,
56 'book_id' => $bookId ?? 0,
61 * Removes the entity attachment from each of its activities
62 * and instead uses the 'extra' field with the entities name.
63 * Used when an entity is deleted.
65 public function removeEntity(Entity $entity): Collection
67 $activities = $entity->activity()->get();
68 $entity->activity()->update([
69 'extra' => $entity->name,
77 * Gets the latest activity.
79 public function latest(int $count = 20, int $page = 0): array
81 $activityList = $this->permissionService
82 ->filterRestrictedEntityRelations($this->activity, 'activities', 'entity_id', 'entity_type')
83 ->orderBy('created_at', 'desc')
84 ->with(['user', 'entity'])
85 ->skip($count * $page)
89 return $this->filterSimilar($activityList);
93 * Gets the latest activity for an entity, Filtering out similar
94 * items to prevent a message activity list.
96 public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array
98 if ($entity->isA('book')) {
99 $query = $this->activity->newQuery()->where('book_id', '=', $entity->id);
101 $query = $this->activity->newQuery()->where('entity_type', '=', $entity->getMorphClass())
102 ->where('entity_id', '=', $entity->id);
105 $activity = $this->permissionService
106 ->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
107 ->orderBy('created_at', 'desc')
108 ->with(['entity' => function (Relation $query) {
109 $query->withTrashed();
111 ->skip($count * ($page - 1))
115 return $this->filterSimilar($activity);
119 * Get latest activity for a user, Filtering out similar items.
121 public function userActivity(User $user, int $count = 20, int $page = 0): array
123 $activityList = $this->permissionService
124 ->filterRestrictedEntityRelations($this->activity, 'activities', 'entity_id', 'entity_type')
125 ->orderBy('created_at', 'desc')
126 ->where('user_id', '=', $user->id)
127 ->skip($count * $page)
131 return $this->filterSimilar($activityList);
135 * Filters out similar activity.
136 * @param Activity[] $activities
139 protected function filterSimilar(iterable $activities): array
142 $previousItem = null;
144 foreach ($activities as $activityItem) {
145 if (!$previousItem || !$activityItem->isSimilarTo($previousItem)) {
146 $newActivity[] = $activityItem;
149 $previousItem = $activityItem;
156 * Flashes a notification message to the session if an appropriate message is available.
158 protected function setNotification(string $activityKey)
160 $notificationTextKey = 'activities.' . $activityKey . '_notification';
161 if (trans()->has($notificationTextKey)) {
162 $message = trans($notificationTextKey);
163 session()->flash('success', $message);
168 * Log out a failed login attempt, Providing the given username
169 * as part of the message if the '%u' string is used.
171 public function logFailedLogin(string $username)
173 $message = config('logging.failed_login.message');
178 $message = str_replace("%u", $username, $message);
179 $channel = config('logging.failed_login.channel');
180 Log::channel($channel)->warning($message);