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;
use Illuminate\Support\Str;
use Pterodactyl\Models\User;
use Pterodactyl\Models\ActivityLog;
@ -22,7 +23,7 @@ class ActivityLogTransformer extends BaseClientTransformer
'is_api' => !is_null($model->api_key_id),
'ip' => $model->ip,
'description' => $model->description,
'properties' => $model->properties ? $model->properties->toArray() : [],
'properties' => $this->properties($model),
'has_additional_metadata' => $this->hasAdditionalMetadata($model),
'timestamp' => $model->timestamp->toIso8601String(),
];
@ -37,6 +38,33 @@ class ActivityLogTransformer extends BaseClientTransformer
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

View file

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

View file

@ -27,6 +27,13 @@ export default ({ activity, children }: Props) => {
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 (
<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'}>
@ -60,10 +67,8 @@ export default ({ activity, children }: Props) => {
{children}
</div>
</div>
<p className={'mt-1 text-sm break-words line-clamp-2 pr-4'}>
<Translate ns={'activity'} values={activity.properties}>
{activity.event.replace(':', '.')}
</Translate>
<p className={style.description}>
<Translate ns={'activity'} values={properties} i18nKey={activity.event.replace(':', '.')}/>
</p>
<div className={'mt-1 flex items-center text-sm'}>
<Link

View file

@ -9,3 +9,11 @@
@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;
}
}