$model->batch, 'event' => $model->event, 'is_api' => !is_null($model->api_key_id), 'ip' => $model->ip, 'description' => $model->description, 'properties' => $this->properties($model), 'has_additional_metadata' => $this->hasAdditionalMetadata($model), 'timestamp' => $model->timestamp->toIso8601String(), ]; } public function includeActor(ActivityLog $model) { if (!$model->actor instanceof User) { return $this->null(); } return $this->item($model->actor, $this->makeTransformer(UserTransformer::class), User::RESOURCE_NAME); } /** * Transforms any array values in the properties into a countable field for easier * use within the translation outputs. */ protected function properties(ActivityLog $model): array { if (!$model->properties || $model->properties->isEmpty()) { return []; } $properties = $model->properties ->mapWithKeys(function ($value, $key) { if (!is_array($value)) { return [$key => $value]; } return [$key => $value, "{$key}_count" => count($value)]; }); $keys = $properties->keys()->filter(fn ($key) => Str::endsWith($key, '_count'))->values(); if ($keys->containsOneItem()) { $properties = $properties->merge(['count' => $properties->get($keys[0])])->except($keys[0]); } return $properties->toArray(); } /** * Determines if there are any log properties that we've not already exposed * in the response language string and that are not just the IP address or * the browser useragent. * * This is used by the front-end to selectively display an "additional metadata" * button that is pointless if there is nothing the user can't already see from * the event description. */ protected function hasAdditionalMetadata(ActivityLog $model): bool { if (is_null($model->properties) || $model->properties->isEmpty()) { return false; } $str = trans('activity.' . str_replace(':', '.', $model->event)); preg_match_all('/:(?[\w.-]+\w)(?:[^\w:]?|$)/', $str, $matches); $exclude = array_merge($matches['key'], ['ip', 'useragent']); foreach ($model->properties->keys() as $key) { if (!in_array($key, $exclude, true)) { return true; } } return false; } }