Abuse the translation engine to handle more of the formatting for us

This commit is contained in:
DaneEveritt 2022-06-18 15:28:42 -04:00
parent b052d29a5f
commit 95de4c30fc
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
4 changed files with 94 additions and 51 deletions

View file

@ -2,6 +2,7 @@
namespace Pterodactyl\Transformers\Api\Client; namespace Pterodactyl\Transformers\Api\Client;
use Illuminate\Support\Str;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Pterodactyl\Models\ActivityLog; use Pterodactyl\Models\ActivityLog;
@ -22,7 +23,7 @@ class ActivityLogTransformer extends BaseClientTransformer
'is_api' => !is_null($model->api_key_id), 'is_api' => !is_null($model->api_key_id),
'ip' => $model->ip, 'ip' => $model->ip,
'description' => $model->description, 'description' => $model->description,
'properties' => $model->properties ? $model->properties->toArray() : [], 'properties' => $this->properties($model),
'has_additional_metadata' => $this->hasAdditionalMetadata($model), 'has_additional_metadata' => $this->hasAdditionalMetadata($model),
'timestamp' => $model->timestamp->toIso8601String(), 'timestamp' => $model->timestamp->toIso8601String(),
]; ];
@ -37,6 +38,33 @@ class ActivityLogTransformer extends BaseClientTransformer
return $this->item($model->actor, $this->makeTransformer(UserTransformer::class), User::RESOURCE_NAME); 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 * 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 * in the response language string and that are not just the IP address or

View file

@ -15,20 +15,20 @@ return [
'checkpoint' => 'Two-factor authentication requested', 'checkpoint' => 'Two-factor authentication requested',
'recovery-token' => 'Used two-factor recovery token', 'recovery-token' => 'Used two-factor recovery token',
'token' => 'Solved two-factor challenge', 'token' => 'Solved two-factor challenge',
'ip-blocked' => 'Blocked request from unlisted IP address for <strong>:identifier</strong>', 'ip-blocked' => 'Blocked request from unlisted IP address for :identifier',
], ],
'user' => [ 'user' => [
'account' => [ 'account' => [
'email-changed' => 'Changed email from <strong>:old</strong> to <strong>:new</strong>', 'email-changed' => 'Changed email from :old to :new',
'password-changed' => 'Changed password', 'password-changed' => 'Changed password',
], ],
'api-key' => [ 'api-key' => [
'create' => 'Created new API key <strong>:identifier</strong>', 'create' => 'Created new API key :identifier',
'delete' => 'Deleted API key <strong>:identifier</strong>', 'delete' => 'Deleted API key :identifier',
], ],
'ssh-key' => [ 'ssh-key' => [
'create' => 'Added SSH key <strong>:fingerprint</strong> to account', 'create' => 'Added SSH key :fingerprint to account',
'delete' => 'Removed SSH key <strong>:fingerprint</strong> from account', 'delete' => 'Removed SSH key :fingerprint from account',
], ],
'two-factor' => [ 'two-factor' => [
'create' => 'Enabled two-factor auth', 'create' => 'Enabled two-factor auth',
@ -37,64 +37,66 @@ return [
], ],
'server' => [ 'server' => [
'backup' => [ 'backup' => [
'download' => 'Downloaded the <strong>:name</strong> backup', 'download' => 'Downloaded the :name backup',
'delete' => 'Deleted the <strong>:name</strong> backup', 'delete' => 'Deleted the :name backup',
'restore' => 'Restored the <strong>:name</strong> backup (deleted files: :truncate)', 'restore' => 'Restored the :name backup (deleted files: :truncate)',
'restore-complete' => 'Completed restoration of the <strong>:name</strong> backup', 'restore-complete' => 'Completed restoration of the :name backup',
'restore-failed' => 'Failed to complete restoration of the <strong>:name</strong> backup', 'restore-failed' => 'Failed to complete restoration of the :name backup',
'start' => 'Started a new backup <strong>:name</strong>', 'start' => 'Started a new backup :name',
'complete' => 'Marked the <strong>:name</strong> backup as complete', 'complete' => 'Marked the :name backup as complete',
'fail' => 'Marked the <strong>:name</strong> backup as failed', 'fail' => 'Marked the :name backup as failed',
'lock' => 'Locked the <strong>:name</strong> backup', 'lock' => 'Locked the :name backup',
'unlock' => 'Unlocked the <strong>:name</strong> backup', 'unlock' => 'Unlocked the :name backup',
], ],
'database' => [ 'database' => [
'create' => 'Created new database <strong>:name</strong>', 'create' => 'Created new database :name',
'rotate-password' => 'Password rotated for database <strong>:name</strong>', 'rotate-password' => 'Password rotated for database :name',
'delete' => 'Deleted database <strong>:name</strong>', 'delete' => 'Deleted database :name',
], ],
'file' => [ 'file' => [
'compress' => 'Created new file archive of files in <strong>:directory</strong>', 'compress_one' => 'Compressed :directory/:file',
'read' => 'Viewed the contents of <strong>:file</strong>', 'compress_other' => 'Compressed :count files in :directory',
'copy' => 'Created a copy of <strong>:file</strong>', 'read' => 'Viewed the contents of :file',
'create-directory' => 'Created a new directory <strong>:name</strong> in <strong>:directory</strong>', 'copy' => 'Created a copy of :file',
'decompress' => 'Decompressed a file archive in <strong>:directory</strong>', 'create-directory' => 'Created a new directory :name in :directory',
'delete' => 'Deleted files in <strong>:directory</strong>', 'decompress' => 'Decompressed :files in :directory',
'download' => 'Downloaded <strong>:file</strong>', 'delete_one' => 'Deleted :directory/:files',
'pull' => 'Downloaded a remote file from :url to <strong>:directory</strong>', 'delete_other' => 'Deleted :count files in :directory',
'rename' => 'Renamed files in <strong>:directory</strong>', 'download' => 'Downloaded :file',
'write' => 'Wrote new content to <strong>:file</strong>', 'pull' => 'Downloaded a remote file from :url to :directory',
'rename' => 'Renamed files in :directory',
'write' => 'Wrote new content to :file',
'upload' => 'Began a file upload', 'upload' => 'Began a file upload',
], ],
'allocation' => [ 'allocation' => [
'create' => 'Added <strong>:allocation</strong> to the server', 'create' => 'Added :allocation to the server',
'notes' => 'Updated the notes for <strong>:allocation</strong> from ":old" to ":new"', 'notes' => 'Updated the notes for :allocation from ":old" to ":new"',
'primary' => 'Set <strong>:allocation</strong> as the primary server allocation', 'primary' => 'Set :allocation as the primary server allocation',
'delete' => 'Deleted the <strong>:allocation</strong> allocation', 'delete' => 'Deleted the :allocation allocation',
], ],
'schedule' => [ 'schedule' => [
'store' => 'Created the <strong>:name</strong> schedule', 'store' => 'Created the :name schedule',
'update' => 'Updated the <strong>:name</strong> schedule', 'update' => 'Updated the :name schedule',
'execute' => 'Manually executed the <strong>:name</strong> schedule', 'execute' => 'Manually executed the :name schedule',
'delete' => 'Deleted the <strong>:name</strong> schedule', 'delete' => 'Deleted the :name schedule',
], ],
'task' => [ 'task' => [
'create' => 'Created a new ":action" task for the <strong>:name</strong> schedule', 'create' => 'Created a new ":action" task for the :name schedule',
'update' => 'Updated the ":action" task for the <strong>:name</strong> schedule', 'update' => 'Updated the ":action" task for the :name schedule',
'delete' => 'Deleted a task for the <strong>:name</strong> schedule', 'delete' => 'Deleted a task for the :name schedule',
], ],
'settings' => [ 'settings' => [
'rename' => 'Renamed the server from <strong>:old</strong> to <strong>:new</strong>', 'rename' => 'Renamed the server from :old to :new',
'reinstall' => 'Triggered a server reinstall', 'reinstall' => 'Triggered a server reinstall',
], ],
'startup' => [ 'startup' => [
'edit' => 'Edited the <strong>:variable</strong> startup variable for the server from ":old" to ":new"', 'edit' => 'Changed the :variable variable from ":old" to ":new"',
'image' => 'Updated the Docker Image for the server from <strong>:old</strong> to <strong>:new</strong>', 'image' => 'Updated the Docker Image for the server from :old to :new',
], ],
'subuser' => [ 'subuser' => [
'create' => 'Added <strong>:email</strong> as a subuser', 'create' => 'Added :email as a subuser',
'update' => 'Updated the subuser permissions for <strong>:email</strong>', 'update' => 'Updated the subuser permissions for :email',
'delete' => 'Removed <strong>:email</strong> as a subuser', 'delete' => 'Removed :email as a subuser',
], ],
], ],
]; ];

View file

@ -27,6 +27,13 @@ export default ({ activity, children }: Props) => {
return current.toString(); return current.toString();
}; };
const properties = Object.keys(activity.properties).reduce((obj, key) => ({
...obj,
[key]: key === 'count' || key.endsWith('_count')
? activity.properties[key]
: `<strong>${activity.properties[key]}</strong>`,
}), {});
return ( return (
<div className={'grid grid-cols-10 py-4 border-b-2 border-gray-800 last:rounded-b last:border-0 group'}> <div className={'grid grid-cols-10 py-4 border-b-2 border-gray-800 last:rounded-b last:border-0 group'}>
<div className={'hidden sm:flex sm:col-span-1 items-center justify-center select-none'}> <div className={'hidden sm:flex sm:col-span-1 items-center justify-center select-none'}>
@ -60,10 +67,8 @@ export default ({ activity, children }: Props) => {
{children} {children}
</div> </div>
</div> </div>
<p className={'mt-1 text-sm break-words line-clamp-2 pr-4'}> <p className={style.description}>
<Translate ns={'activity'} values={activity.properties}> <Translate ns={'activity'} values={properties} i18nKey={activity.event.replace(':', '.')}/>
{activity.event.replace(':', '.')}
</Translate>
</p> </p>
<div className={'mt-1 flex items-center text-sm'}> <div className={'mt-1 flex items-center text-sm'}>
<Link <Link

View file

@ -9,3 +9,11 @@
@apply w-4 h-4; @apply w-4 h-4;
} }
} }
.description {
@apply mt-1 text-sm break-words line-clamp-2 pr-4;
& strong {
@apply text-gray-50 font-semibold;
}
}